1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef mozilla_Selection_h__
8 #define mozilla_Selection_h__
10 #include "mozilla/dom/StyledRange.h"
11 #include "mozilla/AutoRestore.h"
12 #include "mozilla/EventForwards.h"
13 #include "mozilla/PresShellForwards.h"
14 #include "mozilla/RangeBoundary.h"
15 #include "mozilla/SelectionChangeEventDispatcher.h"
16 #include "mozilla/UniquePtr.h"
17 #include "mozilla/WeakPtr.h"
18 #include "nsDirection.h"
19 #include "nsISelectionController.h"
20 #include "nsISelectionListener.h"
22 #include "nsTArrayForwardDeclare.h"
23 #include "nsThreadUtils.h"
24 #include "nsWeakReference.h"
25 #include "nsWrapperCache.h"
27 struct CachedOffsetForFrame
;
30 class nsFrameSelection
;
31 class nsPIDOMWindowOuter
;
32 struct SelectionDetails
;
33 struct SelectionCustomColors
;
35 class nsHTMLCopyEncoder
;
41 class AccessibleCaretEventHub
;
44 class PostContentIterator
;
45 enum class TableSelectionMode
: uint32_t;
46 struct AutoPrepareFocusRange
;
50 } // namespace mozilla
56 // Note, the ownership of mozilla::dom::Selection depends on which way the
57 // object is created. When nsFrameSelection has created Selection,
58 // addreffing/releasing the Selection object is aggregated to nsFrameSelection.
59 // Otherwise normal addref/release is used. This ensures that nsFrameSelection
60 // is never deleted before its Selections.
61 class Selection final
: public nsSupportsWeakReference
,
62 public nsWrapperCache
,
63 public SupportsWeakPtr
{
69 * @param aFrameSelection can be nullptr.
71 explicit Selection(SelectionType aSelectionType
,
72 nsFrameSelection
* aFrameSelection
);
74 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
75 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Selection
)
77 // match this up with EndbatchChanges. will stop ui updates while multiple
78 // selection methods are called
79 void StartBatchChanges();
81 // match this up with StartBatchChanges
82 void EndBatchChanges(int16_t aReason
= nsISelectionListener::NO_REASON
);
85 * NotifyAutoCopy() starts to notify AutoCopyListener of selection changes.
87 void NotifyAutoCopy() {
88 MOZ_ASSERT(mSelectionType
== SelectionType::eNormal
);
90 mNotifyAutoCopy
= true;
94 * MaybeNotifyAccessibleCaretEventHub() starts to notify
95 * AccessibleCaretEventHub of selection change if aPresShell has it.
97 void MaybeNotifyAccessibleCaretEventHub(PresShell
* aPresShell
);
100 * StopNotifyingAccessibleCaretEventHub() stops notifying
101 * AccessibleCaretEventHub of selection change.
103 void StopNotifyingAccessibleCaretEventHub();
106 * EnableSelectionChangeEvent() starts to notify
107 * SelectionChangeEventDispatcher of selection change to dispatch a
108 * selectionchange event at every selection change.
110 void EnableSelectionChangeEvent() {
111 if (!mSelectionChangeEventDispatcher
) {
112 mSelectionChangeEventDispatcher
= new SelectionChangeEventDispatcher();
116 // Required for WebIDL bindings, see
117 // https://developer.mozilla.org/en-US/docs/Mozilla/WebIDL_bindings#Adding_WebIDL_bindings_to_a_class.
118 Document
* GetParentObject() const;
120 DocGroup
* GetDocGroup() const;
122 // utility methods for scrolling the selection into view
123 nsPresContext
* GetPresContext() const;
124 PresShell
* GetPresShell() const;
125 nsFrameSelection
* GetFrameSelection() const { return mFrameSelection
; }
126 // Returns a rect containing the selection region, and frame that that
127 // position is relative to. For SELECTION_ANCHOR_REGION or
128 // SELECTION_FOCUS_REGION the rect is a zero-width rectangle. For
129 // SELECTION_WHOLE_SELECTION the rect contains both the anchor and focus
131 nsIFrame
* GetSelectionAnchorGeometry(SelectionRegion aRegion
, nsRect
* aRect
);
132 // Returns the position of the region (SELECTION_ANCHOR_REGION or
133 // SELECTION_FOCUS_REGION only), and frame that that position is relative to.
134 // The 'position' is a zero-width rectangle.
135 nsIFrame
* GetSelectionEndPointGeometry(SelectionRegion aRegion
,
138 nsresult
PostScrollSelectionIntoViewEvent(SelectionRegion aRegion
,
140 ScrollAxis aVertical
,
141 ScrollAxis aHorizontal
);
143 SCROLL_SYNCHRONOUS
= 1 << 1,
144 SCROLL_FIRST_ANCESTOR_ONLY
= 1 << 2,
146 1 << 3, // only matters if SCROLL_SYNCHRONOUS is passed too
147 SCROLL_OVERFLOW_HIDDEN
= 1 << 5,
148 SCROLL_FOR_CARET_MOVE
= 1 << 6
150 // If aFlags doesn't contain SCROLL_SYNCHRONOUS, then we'll flush when
151 // the scroll event fires so we make sure to scroll to the right place.
152 // Otherwise, if SCROLL_DO_FLUSH is also in aFlags, then this method will
153 // flush layout and you MUST hold a strong ref on 'this' for the duration
154 // of this call. This might destroy arbitrary layout objects.
155 MOZ_CAN_RUN_SCRIPT nsresult
156 ScrollIntoView(SelectionRegion aRegion
, ScrollAxis aVertical
= ScrollAxis(),
157 ScrollAxis aHorizontal
= ScrollAxis(), int32_t aFlags
= 0);
160 static bool IsUserSelectionCollapsed(
161 const nsRange
& aRange
, nsTArray
<RefPtr
<nsRange
>>& aTempRangesToAdd
);
163 * https://w3c.github.io/selection-api/#selectstart-event.
165 enum class DispatchSelectstartEvent
{
171 * See `AddRangesForSelectableNodes`.
173 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
AddRangesForUserSelectableNodes(
174 nsRange
* aRange
, int32_t* aOutIndex
,
175 const DispatchSelectstartEvent aDispatchSelectstartEvent
);
178 * Adds aRange to this Selection. If mUserInitiated is true,
179 * then aRange is first scanned for -moz-user-select:none nodes and split up
180 * into multiple ranges to exclude those before adding the resulting ranges
183 * @param aOutIndex points to the range last added, if at least one was added.
184 * If aRange is already contained, it points to the range
185 * containing it. -1 if mStyledRanges.mRanges was empty and
186 * no range was added.
188 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
AddRangesForSelectableNodes(
189 nsRange
* aRange
, int32_t* aOutIndex
,
190 DispatchSelectstartEvent aDispatchSelectstartEvent
);
193 nsresult
RemoveCollapsedRanges();
194 nsresult
Clear(nsPresContext
* aPresContext
);
195 MOZ_CAN_RUN_SCRIPT nsresult
CollapseInLimiter(nsINode
* aContainer
,
198 return NS_ERROR_INVALID_ARG
;
200 return CollapseInLimiter(RawRangeBoundary(aContainer
, aOffset
));
202 MOZ_CAN_RUN_SCRIPT nsresult
203 CollapseInLimiter(const RawRangeBoundary
& aPoint
) {
205 CollapseInLimiter(aPoint
, result
);
206 return result
.StealNSResult();
208 MOZ_CAN_RUN_SCRIPT
void CollapseInLimiter(const RawRangeBoundary
& aPoint
,
210 CollapseInternal(InLimiter::eYes
, aPoint
, aRv
);
213 MOZ_CAN_RUN_SCRIPT nsresult
Extend(nsINode
* aContainer
, int32_t aOffset
);
216 * See mStyledRanges.mRanges.
218 nsRange
* GetRangeAt(int32_t aIndex
) const;
220 // Get the anchor-to-focus range if we don't care which end is
221 // anchor and which end is focus.
222 const nsRange
* GetAnchorFocusRange() const { return mAnchorFocusRange
; }
224 nsDirection
GetDirection() const { return mDirection
; }
226 void SetDirection(nsDirection aDir
) { mDirection
= aDir
; }
227 MOZ_CAN_RUN_SCRIPT nsresult
SetAnchorFocusToRange(nsRange
* aRange
);
229 MOZ_CAN_RUN_SCRIPT
void ReplaceAnchorFocusRange(nsRange
* aRange
);
231 void AdjustAnchorFocusForMultiRange(nsDirection aDirection
);
233 nsIFrame
* GetPrimaryFrameForAnchorNode() const;
234 nsIFrame
* GetPrimaryFrameForFocusNode(bool aVisual
,
235 int32_t* aOffsetUsed
= nullptr) const;
237 UniquePtr
<SelectionDetails
> LookUpSelection(
238 nsIContent
* aContent
, int32_t aContentOffset
, int32_t aContentLength
,
239 UniquePtr
<SelectionDetails
> aDetailsHead
, SelectionType aSelectionType
,
242 NS_IMETHOD
Repaint(nsPresContext
* aPresContext
);
245 nsresult
StartAutoScrollTimer(nsIFrame
* aFrame
, const nsPoint
& aPoint
,
246 uint32_t aDelayInMs
);
248 nsresult
StopAutoScrollTimer();
250 JSObject
* WrapObject(JSContext
* aCx
,
251 JS::Handle
<JSObject
*> aGivenProto
) override
;
254 nsINode
* GetAnchorNode(CallerType aCallerType
= CallerType::System
) const {
255 const RangeBoundary
& anchor
= AnchorRef();
256 nsINode
* anchorNode
= anchor
.IsSet() ? anchor
.Container() : nullptr;
257 if (!anchorNode
|| aCallerType
== CallerType::System
||
258 !anchorNode
->ChromeOnlyAccess()) {
261 // anchor is nsIContent as ChromeOnlyAccess is nsIContent-only
262 return anchorNode
->AsContent()->FindFirstNonChromeOnlyAccessContent();
264 uint32_t AnchorOffset(CallerType aCallerType
= CallerType::System
) const {
265 const RangeBoundary
& anchor
= AnchorRef();
266 if (aCallerType
!= CallerType::System
&& anchor
.IsSet() &&
267 anchor
.Container()->ChromeOnlyAccess()) {
270 const Maybe
<uint32_t> offset
=
271 anchor
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
);
272 return offset
? *offset
: 0;
274 nsINode
* GetFocusNode(CallerType aCallerType
= CallerType::System
) const {
275 const RangeBoundary
& focus
= FocusRef();
276 nsINode
* focusNode
= focus
.IsSet() ? focus
.Container() : nullptr;
277 if (!focusNode
|| aCallerType
== CallerType::System
||
278 !focusNode
->ChromeOnlyAccess()) {
281 // focus is nsIContent as ChromeOnlyAccess is nsIContent-only
282 return focusNode
->AsContent()->FindFirstNonChromeOnlyAccessContent();
284 uint32_t FocusOffset(CallerType aCallerType
= CallerType::System
) const {
285 const RangeBoundary
& focus
= FocusRef();
286 if (aCallerType
!= CallerType::System
&& focus
.IsSet() &&
287 focus
.Container()->ChromeOnlyAccess()) {
290 const Maybe
<uint32_t> offset
=
291 focus
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
);
292 return offset
? *offset
: 0;
295 nsIContent
* GetChildAtAnchorOffset() {
296 const RangeBoundary
& anchor
= AnchorRef();
297 return anchor
.IsSet() ? anchor
.GetChildAtOffset() : nullptr;
299 nsIContent
* GetChildAtFocusOffset() {
300 const RangeBoundary
& focus
= FocusRef();
301 return focus
.IsSet() ? focus
.GetChildAtOffset() : nullptr;
304 const RangeBoundary
& AnchorRef() const;
305 const RangeBoundary
& FocusRef() const;
308 * IsCollapsed -- is the whole selection just one point, or unset?
310 bool IsCollapsed() const {
311 uint32_t cnt
= mStyledRanges
.Length();
320 return mStyledRanges
.mRanges
[0].mRange
->Collapsed();
323 // *JS() methods are mapped to Selection.*().
324 // They may move focus only when the range represents normal selection.
325 // These methods shouldn't be used by non-JS callers.
326 MOZ_CAN_RUN_SCRIPT
void CollapseJS(nsINode
* aContainer
, uint32_t aOffset
,
327 mozilla::ErrorResult
& aRv
);
328 MOZ_CAN_RUN_SCRIPT
void CollapseToStartJS(mozilla::ErrorResult
& aRv
);
329 MOZ_CAN_RUN_SCRIPT
void CollapseToEndJS(mozilla::ErrorResult
& aRv
);
331 MOZ_CAN_RUN_SCRIPT
void ExtendJS(nsINode
& aContainer
, uint32_t aOffset
,
332 mozilla::ErrorResult
& aRv
);
334 MOZ_CAN_RUN_SCRIPT
void SelectAllChildrenJS(nsINode
& aNode
,
335 mozilla::ErrorResult
& aRv
);
338 * Deletes this selection from document the nodes belong to.
339 * Only if this has `SelectionType::eNormal`.
341 MOZ_CAN_RUN_SCRIPT
void DeleteFromDocument(mozilla::ErrorResult
& aRv
);
343 uint32_t RangeCount() const { return mStyledRanges
.Length(); }
345 void GetType(nsAString
& aOutType
) const;
347 nsRange
* GetRangeAt(uint32_t aIndex
, mozilla::ErrorResult
& aRv
);
348 MOZ_CAN_RUN_SCRIPT
void AddRangeJS(nsRange
& aRange
,
349 mozilla::ErrorResult
& aRv
);
352 * Callers need to keep `aRange` alive.
354 MOZ_CAN_RUN_SCRIPT
void RemoveRangeAndUnselectFramesAndNotifyListeners(
355 nsRange
& aRange
, mozilla::ErrorResult
& aRv
);
357 MOZ_CAN_RUN_SCRIPT
void RemoveAllRanges(mozilla::ErrorResult
& aRv
);
360 * Whether Stringify should flush layout or not.
362 enum class FlushFrames
{ No
, Yes
};
364 void Stringify(nsAString
& aResult
, FlushFrames
= FlushFrames::Yes
);
367 * Indicates whether the node is part of the selection. If partlyContained
368 * is true, the function returns true when some part of the node
369 * is part of the selection. If partlyContained is false, the
370 * function only returns true when the entire node is part of the selection.
372 bool ContainsNode(nsINode
& aNode
, bool aPartlyContained
,
373 mozilla::ErrorResult
& aRv
);
376 * Check to see if the given point is contained within the selection area. In
377 * particular, this iterates through all the rects that make up the selection,
378 * not just the bounding box, and checks to see if the given point is
379 * contained in any one of them.
380 * @param aPoint The point to check, relative to the root frame.
382 bool ContainsPoint(const nsPoint
& aPoint
);
385 * Modifies the selection. Note that the parameters are case-insensitive.
387 * @param alter can be one of { "move", "extend" }
388 * - "move" collapses the selection to the end of the selection and
389 * applies the movement direction/granularity to the collapsed
391 * - "extend" leaves the start of the selection unchanged, and applies
392 * movement direction/granularity to the end of the selection.
393 * @param direction can be one of { "forward", "backward", "left", "right" }
394 * @param granularity can be one of { "character", "word",
395 * "line", "lineboundary" }
397 * @throws NS_ERROR_NOT_IMPLEMENTED if the granularity is "sentence",
398 * "sentenceboundary", "paragraph", "paragraphboundary", or
399 * "documentboundary". Throws NS_ERROR_INVALID_ARG if alter, direction,
400 * or granularity has an unrecognized value.
402 MOZ_CAN_RUN_SCRIPT
void Modify(const nsAString
& aAlter
,
403 const nsAString
& aDirection
,
404 const nsAString
& aGranularity
,
405 mozilla::ErrorResult
& aRv
);
408 void SetBaseAndExtentJS(nsINode
& aAnchorNode
, uint32_t aAnchorOffset
,
409 nsINode
& aFocusNode
, uint32_t aFocusOffset
,
410 mozilla::ErrorResult
& aRv
);
412 bool GetInterlinePosition(mozilla::ErrorResult
& aRv
);
413 void SetInterlinePosition(bool aValue
, mozilla::ErrorResult
& aRv
);
415 Nullable
<int16_t> GetCaretBidiLevel(mozilla::ErrorResult
& aRv
) const;
416 void SetCaretBidiLevel(const Nullable
<int16_t>& aCaretBidiLevel
,
417 mozilla::ErrorResult
& aRv
);
419 void ToStringWithFormat(const nsAString
& aFormatType
, uint32_t aFlags
,
420 int32_t aWrapColumn
, nsAString
& aReturn
,
421 mozilla::ErrorResult
& aRv
);
422 void AddSelectionListener(nsISelectionListener
* aListener
);
423 void RemoveSelectionListener(nsISelectionListener
* aListener
);
425 RawSelectionType
RawType() const {
426 return ToRawSelectionType(mSelectionType
);
428 SelectionType
Type() const { return mSelectionType
; }
430 void GetRangesForInterval(nsINode
& aBeginNode
, int32_t aBeginOffset
,
431 nsINode
& aEndNode
, int32_t aEndOffset
,
433 nsTArray
<RefPtr
<nsRange
>>& aReturn
,
434 mozilla::ErrorResult
& aRv
);
436 MOZ_CAN_RUN_SCRIPT
void ScrollIntoView(int16_t aRegion
, bool aIsSynchronous
,
437 WhereToScroll aVPercent
,
438 WhereToScroll aHPercent
,
439 mozilla::ErrorResult
& aRv
);
441 void SetColors(const nsAString
& aForeColor
, const nsAString
& aBackColor
,
442 const nsAString
& aAltForeColor
, const nsAString
& aAltBackColor
,
443 mozilla::ErrorResult
& aRv
);
445 void ResetColors(mozilla::ErrorResult
& aRv
);
448 * Non-JS callers should use the following
449 * collapse/collapseToStart/extend/etc methods, instead of the *JS
450 * versions that bindings call.
454 * Collapses the selection to a single point, at the specified offset
455 * in the given node. When the selection is collapsed, and the content
456 * is focused and editable, the caret will blink there.
457 * @param aContainer The given node where the selection will be set
458 * @param offset Where in given dom node to place the selection (the
459 * offset into the given node)
461 MOZ_CAN_RUN_SCRIPT
void CollapseInLimiter(nsINode
& aContainer
,
464 CollapseInternal(InLimiter::eYes
, RawRangeBoundary(&aContainer
, aOffset
),
469 enum class InLimiter
{
470 // If eYes, the method may reset selection limiter and move focus if the
471 // given range is out of the limiter.
473 // If eNo, the method won't reset selection limiter. So, if given range
474 // is out of bounds, the method may return error.
478 void CollapseInternal(InLimiter aInLimiter
, const RawRangeBoundary
& aPoint
,
483 * Collapses the whole selection to a single point at the start
484 * of the current selection (irrespective of direction). If content
485 * is focused and editable, the caret will blink there.
487 MOZ_CAN_RUN_SCRIPT
void CollapseToStart(mozilla::ErrorResult
& aRv
);
490 * Collapses the whole selection to a single point at the end
491 * of the current selection (irrespective of direction). If content
492 * is focused and editable, the caret will blink there.
494 MOZ_CAN_RUN_SCRIPT
void CollapseToEnd(mozilla::ErrorResult
& aRv
);
497 * Extends the selection by moving the selection end to the specified node and
498 * offset, preserving the selection begin position. The new selection end
499 * result will always be from the anchorNode to the new focusNode, regardless
502 * @param aContainer The node where the selection will be extended to
503 * @param aOffset Where in aContainer to place the offset of the new
506 MOZ_CAN_RUN_SCRIPT
void Extend(nsINode
& aContainer
, uint32_t aOffset
,
509 MOZ_CAN_RUN_SCRIPT
void AddRangeAndSelectFramesAndNotifyListeners(
510 nsRange
& aRange
, mozilla::ErrorResult
& aRv
);
513 * Adds all children of the specified node to the selection.
514 * @param aNode the parent of the children to be added to the selection.
516 MOZ_CAN_RUN_SCRIPT
void SelectAllChildren(nsINode
& aNode
,
517 mozilla::ErrorResult
& aRv
);
520 * SetStartAndEnd() removes all ranges and sets new range as given range.
521 * Different from SetBaseAndExtent(), this won't compare the DOM points of
522 * aStartRef and aEndRef for performance nor set direction to eDirPrevious.
523 * Note that this may reset the limiter and move focus. If you don't want
524 * that, use SetStartAndEndInLimiter() instead.
527 void SetStartAndEnd(const RawRangeBoundary
& aStartRef
,
528 const RawRangeBoundary
& aEndRef
, ErrorResult
& aRv
) {
529 SetStartAndEndInternal(InLimiter::eNo
, aStartRef
, aEndRef
, eDirNext
, aRv
);
532 void SetStartAndEnd(nsINode
& aStartContainer
, uint32_t aStartOffset
,
533 nsINode
& aEndContainer
, uint32_t aEndOffset
,
535 SetStartAndEnd(RawRangeBoundary(&aStartContainer
, aStartOffset
),
536 RawRangeBoundary(&aEndContainer
, aEndOffset
), aRv
);
540 * SetStartAndEndInLimiter() is similar to SetStartAndEnd(), but this respects
541 * the selection limiter. If all or part of given range is not in the
542 * limiter, this returns error.
545 void SetStartAndEndInLimiter(const RawRangeBoundary
& aStartRef
,
546 const RawRangeBoundary
& aEndRef
,
548 SetStartAndEndInternal(InLimiter::eYes
, aStartRef
, aEndRef
, eDirNext
, aRv
);
551 void SetStartAndEndInLimiter(nsINode
& aStartContainer
, uint32_t aStartOffset
,
552 nsINode
& aEndContainer
, uint32_t aEndOffset
,
554 SetStartAndEndInLimiter(RawRangeBoundary(&aStartContainer
, aStartOffset
),
555 RawRangeBoundary(&aEndContainer
, aEndOffset
), aRv
);
559 * SetBaseAndExtent() is alternative of the JS API for internal use.
560 * Different from SetStartAndEnd(), this sets anchor and focus points as
561 * specified, then if anchor point is after focus node, this sets the
562 * direction to eDirPrevious.
563 * Note that this may reset the limiter and move focus. If you don't want
564 * that, use SetBaseAndExtentInLimier() instead.
567 void SetBaseAndExtent(nsINode
& aAnchorNode
, uint32_t aAnchorOffset
,
568 nsINode
& aFocusNode
, uint32_t aFocusOffset
,
571 void SetBaseAndExtent(const RawRangeBoundary
& aAnchorRef
,
572 const RawRangeBoundary
& aFocusRef
, ErrorResult
& aRv
) {
573 SetBaseAndExtentInternal(InLimiter::eNo
, aAnchorRef
, aFocusRef
, aRv
);
577 * SetBaseAndExtentInLimiter() is similar to SetBaseAndExtent(), but this
578 * respects the selection limiter. If all or part of given range is not in
579 * the limiter, this returns error.
582 void SetBaseAndExtentInLimiter(nsINode
& aAnchorNode
, uint32_t aAnchorOffset
,
583 nsINode
& aFocusNode
, uint32_t aFocusOffset
,
585 SetBaseAndExtentInLimiter(RawRangeBoundary(&aAnchorNode
, aAnchorOffset
),
586 RawRangeBoundary(&aFocusNode
, aFocusOffset
), aRv
);
589 void SetBaseAndExtentInLimiter(const RawRangeBoundary
& aAnchorRef
,
590 const RawRangeBoundary
& aFocusRef
,
592 SetBaseAndExtentInternal(InLimiter::eYes
, aAnchorRef
, aFocusRef
, aRv
);
595 void AddSelectionChangeBlocker();
596 void RemoveSelectionChangeBlocker();
597 bool IsBlockingSelectionChangeEvents() const;
599 // Whether this selection is focused in an editable element.
600 bool IsEditorSelection() const;
603 * Set the painting style for the range. The range must be a range in
604 * the selection. The textRangeStyle will be used by text frame
605 * when it is painting the selection.
607 nsresult
SetTextRangeStyle(nsRange
* aRange
,
608 const TextRangeStyle
& aTextRangeStyle
);
610 // Methods to manipulate our mFrameSelection's ancestor limiter.
611 nsIContent
* GetAncestorLimiter() const;
612 void SetAncestorLimiter(nsIContent
* aLimiter
);
615 * Frame Offset cache can be used just during calling
616 * nsEditor::EndPlaceHolderTransaction. EndPlaceHolderTransaction will give
617 * rise to reflow/refreshing view/scroll, and call times of
618 * nsTextFrame::GetPointFromOffset whose return value is to be cached. see
619 * bugs 35296 and 199412
621 void SetCanCacheFrameOffset(bool aCanCacheFrameOffset
);
623 // Selection::GetRangesForIntervalArray
625 // Fills a nsTArray with the ranges overlapping the range specified by
626 // the given endpoints. Ranges in the selection exactly adjacent to the
627 // input range are not returned unless aAllowAdjacent is set.
629 // For example, if the following ranges were in the selection
630 // (assume everything is within the same node)
632 // Start Offset: 0 2 7 9
633 // End Offset: 2 5 9 10
635 // and passed aBeginOffset of 2 and aEndOffset of 9, then with
636 // aAllowAdjacent set, all the ranges should be returned. If
637 // aAllowAdjacent was false, the ranges [2, 5] and [7, 9] only
638 // should be returned
640 // Now that overlapping ranges are disallowed, there can be a maximum of
642 nsresult
GetRangesForIntervalArray(nsINode
* aBeginNode
, int32_t aBeginOffset
,
643 nsINode
* aEndNode
, int32_t aEndOffset
,
645 nsTArray
<nsRange
*>* aRanges
);
648 * Modifies the cursor Bidi level after a change in keyboard direction
649 * @param langRTL is true if the new language is right-to-left or
650 * false if the new language is left-to-right.
652 nsresult
SelectionLanguageChange(bool aLangRTL
);
655 bool HasSameRootOrSameComposedDoc(const nsINode
& aNode
);
657 // XXX Please don't add additional uses of this method, it's only for
658 // XXX supporting broken code (bug 1245883) in the following classes:
659 friend class ::nsCopySupport
;
660 friend class ::nsHTMLCopyEncoder
;
662 void AddRangeAndSelectFramesAndNotifyListeners(nsRange
& aRange
,
666 // This is helper method for GetPrimaryFrameForFocusNode.
667 // If aVisual is true, this returns caret frame.
668 // If false, this returns primary frame.
669 nsIFrame
* GetPrimaryOrCaretFrameForNodeOffset(nsIContent
* aContent
,
671 int32_t* aOffsetUsed
,
674 // Get the cached value for nsTextFrame::GetPointFromOffset.
675 nsresult
GetCachedFrameOffset(nsIFrame
* aFrame
, int32_t inOffset
,
679 void SetStartAndEndInternal(InLimiter aInLimiter
,
680 const RawRangeBoundary
& aStartRef
,
681 const RawRangeBoundary
& aEndRef
,
682 nsDirection aDirection
, ErrorResult
& aRv
);
684 void SetBaseAndExtentInternal(InLimiter aInLimiter
,
685 const RawRangeBoundary
& aAnchorRef
,
686 const RawRangeBoundary
& aFocusRef
,
690 SelectionType
GetType() const { return mSelectionType
; }
692 SelectionCustomColors
* GetCustomColors() const { return mCustomColors
.get(); }
694 MOZ_CAN_RUN_SCRIPT nsresult
NotifySelectionListeners(bool aCalledByJS
);
695 MOZ_CAN_RUN_SCRIPT nsresult
NotifySelectionListeners();
697 friend struct AutoUserInitiated
;
698 struct MOZ_RAII AutoUserInitiated
{
699 explicit AutoUserInitiated(Selection
* aSelection
)
700 : mSavedValue(aSelection
->mUserInitiated
) {
701 aSelection
->mUserInitiated
= true;
703 AutoRestore
<bool> mSavedValue
;
707 friend struct mozilla::AutoPrepareFocusRange
;
708 class ScrollSelectionIntoViewEvent
;
709 friend class ScrollSelectionIntoViewEvent
;
711 class ScrollSelectionIntoViewEvent
: public Runnable
{
713 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_DECL_NSIRUNNABLE
715 ScrollSelectionIntoViewEvent(Selection
* aSelection
, SelectionRegion aRegion
,
716 ScrollAxis aVertical
, ScrollAxis aHorizontal
,
718 : Runnable("dom::Selection::ScrollSelectionIntoViewEvent"),
719 mSelection(aSelection
),
721 mVerticalScroll(aVertical
),
722 mHorizontalScroll(aHorizontal
),
724 NS_ASSERTION(aSelection
, "null parameter");
726 void Revoke() { mSelection
= nullptr; }
729 Selection
* mSelection
;
730 SelectionRegion mRegion
;
731 ScrollAxis mVerticalScroll
;
732 ScrollAxis mHorizontalScroll
;
737 * Set mAnchorFocusRange to mStyledRanges.mRanges[aIndex] if aIndex is a valid
738 * index. Set mAnchorFocusRange to nullptr if aIndex is negative. Otherwise,
739 * i.e., if aIndex is positive but out of bounds of mStyledRanges.mRanges, do
742 void SetAnchorFocusRange(int32_t aIndex
);
743 void SelectFramesOf(nsIContent
* aContent
, bool aSelected
) const;
746 * https://dom.spec.whatwg.org/#concept-tree-inclusive-descendant.
748 nsresult
SelectFramesOfInclusiveDescendantsOfContent(
749 PostContentIterator
& aPostOrderIter
, nsIContent
* aContent
,
750 bool aSelected
) const;
752 nsresult
SelectFrames(nsPresContext
* aPresContext
, nsRange
* aRange
,
756 * SelectFramesInAllRanges() calls SelectFrames() for all current
759 void SelectFramesInAllRanges(nsPresContext
* aPresContext
);
762 * @param aOutIndex points to the index of the range in mStyledRanges.mRanges.
763 * If aDidAddRange is true, it is in [0, mStyledRanges.Length()).
765 MOZ_CAN_RUN_SCRIPT nsresult
MaybeAddTableCellRange(nsRange
& aRange
,
769 Document
* GetDocument() const;
773 struct StyledRanges
{
776 StyledRange
* FindRangeData(nsRange
* aRange
);
778 using Elements
= AutoTArray
<StyledRange
, 1>;
780 Elements::size_type
Length() const;
782 nsresult
RemoveCollapsedRanges();
784 nsresult
RemoveRangeAndUnregisterSelection(nsRange
& aRange
);
787 * Binary searches the given sorted array of ranges for the insertion point
788 * for the given node/offset. The given comparator is used, and the index
789 * where the point should appear in the array is returned.
791 * If there is an item in the array equal to the input point (aPointNode,
792 * aPointOffset), we will return the index of this item.
794 * @return the index where the point should appear in the array. In
795 * [0, `aElementArray->Length()`].
797 static int32_t FindInsertionPoint(
798 const nsTArray
<StyledRange
>* aElementArray
, const nsINode
& aPointNode
,
799 int32_t aPointOffset
,
800 int32_t (*aComparator
)(const nsINode
&, int32_t, const nsRange
&));
803 * Works on the same principle as GetRangesForIntervalArray, however
804 * instead this returns the indices into mRanges between which
805 * the overlapping ranges lie.
807 * @param aStartIndex will be less or equal than aEndIndex.
808 * @param aEndIndex can be in [-1, mRanges.Length()].
810 nsresult
GetIndicesForInterval(const nsINode
* aBeginNode
,
811 int32_t aBeginOffset
,
812 const nsINode
* aEndNode
, int32_t aEndOffset
,
813 bool aAllowAdjacent
, int32_t& aStartIndex
,
814 int32_t& aEndIndex
) const;
816 bool HasEqualRangeBoundariesAt(const nsRange
& aRange
,
817 int32_t aRangeIndex
) const;
820 * Preserves the sorting and disjunctiveness of mRanges.
822 * @param aOutIndex will point to the index of the added range, or if aRange
823 * is already contained, to the one containing it. Hence
824 * it'll always be in [0, mRanges.Length()).
826 MOZ_CAN_RUN_SCRIPT nsresult
MaybeAddRangeAndTruncateOverlaps(
827 nsRange
* aRange
, int32_t* aOutIndex
, Selection
& aSelection
);
830 * GetCommonEditingHost() returns common editing host of all
831 * ranges if there is. If at least one of the ranges is in non-editable
832 * element, returns nullptr. See following examples for the detail:
834 * <div id="a" contenteditable>
836 * <div id="b" contenteditable="false">
838 * <div id="c" contenteditable>
840 * in this case, this returns div#a because div#c is also in div#a.
842 * <div id="a" contenteditable>
844 * <div id="b" contenteditable="false">
846 * <div id="c" contenteditable>
848 * in this case, this returns div#a because second range is also in div#a
849 * and common ancestor of the range (i.e., div#c) is editable.
851 * <div id="a" contenteditable>
853 * <div id="b" contenteditable="false">
855 * <div id="c" contenteditable>
857 * in this case, this returns nullptr because the second range is in
860 Element
* GetCommonEditingHost() const;
862 MOZ_CAN_RUN_SCRIPT
void MaybeFocusCommonEditingHost(
863 PresShell
* aPresShell
) const;
865 static nsresult
SubtractRange(StyledRange
& aRange
, nsRange
& aSubtract
,
866 nsTArray
<StyledRange
>* aOutput
);
868 void UnregisterSelection();
870 // These are the ranges inside this selection. They are kept sorted in order
871 // of DOM start position.
873 // This data structure is sorted by the range beginnings. As the ranges are
874 // disjoint, it is also implicitly sorted by the range endings. This allows
875 // us to perform binary searches when searching for existence of a range,
876 // giving us O(log n) search time.
878 // Inserting a new range requires finding the overlapping interval,
879 // requiring two binary searches plus up to an additional 6 DOM comparisons.
880 // If this proves to be a performance concern, then an interval tree may be
881 // a possible solution, allowing the calculation of the overlap interval in
882 // O(log n) time, though this would require rebalancing and other overhead.
886 StyledRanges mStyledRanges
;
888 RefPtr
<nsRange
> mAnchorFocusRange
;
889 RefPtr
<nsFrameSelection
> mFrameSelection
;
890 RefPtr
<AccessibleCaretEventHub
> mAccessibleCaretEventHub
;
891 RefPtr
<SelectionChangeEventDispatcher
> mSelectionChangeEventDispatcher
;
892 RefPtr
<AutoScroller
> mAutoScroller
;
893 nsTArray
<nsCOMPtr
<nsISelectionListener
>> mSelectionListeners
;
894 nsRevocableEventPtr
<ScrollSelectionIntoViewEvent
> mScrollEvent
;
895 CachedOffsetForFrame
* mCachedOffsetForFrame
;
896 nsDirection mDirection
;
897 const SelectionType mSelectionType
;
898 UniquePtr
<SelectionCustomColors
> mCustomColors
;
900 // Non-zero if we don't want any changes we make to the selection to be
901 // visible to content. If non-zero, content won't be notified about changes.
902 uint32_t mSelectionChangeBlockerCount
;
905 * True if the current selection operation was initiated by user action.
906 * It determines whether we exclude -moz-user-select:none nodes or not,
907 * as well as whether selectstart events will be fired.
912 * When the selection change is caused by a call of Selection API,
913 * mCalledByJS is true. Otherwise, false.
918 * true if AutoCopyListner::OnSelectionChange() should be called.
920 bool mNotifyAutoCopy
;
923 // Stack-class to turn on/off selection batching.
924 class MOZ_STACK_CLASS SelectionBatcher final
{
926 RefPtr
<Selection
> mSelection
;
929 explicit SelectionBatcher(Selection
* aSelection
) {
930 mSelection
= aSelection
;
932 mSelection
->StartBatchChanges();
936 ~SelectionBatcher() {
938 mSelection
->EndBatchChanges();
943 class MOZ_RAII AutoHideSelectionChanges final
{
945 RefPtr
<Selection
> mSelection
;
948 explicit AutoHideSelectionChanges(const nsFrameSelection
* aFrame
);
950 explicit AutoHideSelectionChanges(Selection
* aSelection
)
951 : mSelection(aSelection
) {
952 mSelection
= aSelection
;
954 mSelection
->AddSelectionChangeBlocker();
958 ~AutoHideSelectionChanges() {
960 mSelection
->RemoveSelectionChangeBlocker();
967 inline bool IsValidRawSelectionType(RawSelectionType aRawSelectionType
) {
968 return aRawSelectionType
>= nsISelectionController::SELECTION_NONE
&&
969 aRawSelectionType
<= nsISelectionController::SELECTION_URLSTRIKEOUT
;
972 inline SelectionType
ToSelectionType(RawSelectionType aRawSelectionType
) {
973 if (!IsValidRawSelectionType(aRawSelectionType
)) {
974 return SelectionType::eInvalid
;
976 return static_cast<SelectionType
>(aRawSelectionType
);
979 inline RawSelectionType
ToRawSelectionType(SelectionType aSelectionType
) {
980 MOZ_ASSERT(aSelectionType
!= SelectionType::eInvalid
);
981 return static_cast<RawSelectionType
>(aSelectionType
);
984 inline RawSelectionType
ToRawSelectionType(TextRangeType aTextRangeType
) {
985 return ToRawSelectionType(ToSelectionType(aTextRangeType
));
988 inline SelectionTypeMask
ToSelectionTypeMask(SelectionType aSelectionType
) {
989 MOZ_ASSERT(aSelectionType
!= SelectionType::eInvalid
);
990 return aSelectionType
== SelectionType::eNone
992 : static_cast<SelectionTypeMask
>(
993 1 << (static_cast<uint8_t>(aSelectionType
) - 1));
996 } // namespace mozilla
998 #endif // mozilla_Selection_h__