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/AutoRestore.h"
11 #include "mozilla/EventForwards.h"
12 #include "mozilla/PresShellForwards.h"
13 #include "mozilla/RangeBoundary.h"
14 #include "mozilla/SelectionChangeEventDispatcher.h"
15 #include "mozilla/UniquePtr.h"
16 #include "mozilla/WeakPtr.h"
17 #include "mozilla/dom/Highlight.h"
18 #include "mozilla/dom/StyledRange.h"
19 #include "nsDirection.h"
20 #include "nsISelectionController.h"
21 #include "nsISelectionListener.h"
23 #include "nsTArrayForwardDeclare.h"
24 #include "nsThreadUtils.h"
25 #include "nsWeakReference.h"
26 #include "nsWrapperCache.h"
28 struct CachedOffsetForFrame
;
31 class nsFrameSelection
;
32 class nsPIDOMWindowOuter
;
33 struct SelectionDetails
;
34 struct SelectionCustomColors
;
36 class nsHTMLCopyEncoder
;
42 class AccessibleCaretEventHub
;
45 class PostContentIterator
;
46 enum class TableSelectionMode
: uint32_t;
47 struct AutoPrepareFocusRange
;
51 } // namespace mozilla
57 // Note, the ownership of mozilla::dom::Selection depends on which way the
58 // object is created. When nsFrameSelection has created Selection,
59 // addreffing/releasing the Selection object is aggregated to nsFrameSelection.
60 // Otherwise normal addref/release is used. This ensures that nsFrameSelection
61 // is never deleted before its Selections.
62 class Selection final
: public nsSupportsWeakReference
,
63 public nsWrapperCache
,
64 public SupportsWeakPtr
{
70 * @param aFrameSelection can be nullptr.
72 explicit Selection(SelectionType aSelectionType
,
73 nsFrameSelection
* aFrameSelection
);
75 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
76 NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(Selection
)
79 * Match this up with EndbatchChanges. will stop ui updates while multiple
80 * selection methods are called
82 * @param aDetails string to explian why this is called. This won't be
83 * stored nor exposed to selection listeners etc. Just for logging.
85 void StartBatchChanges(const char* aDetails
);
88 * Match this up with StartBatchChanges
90 * @param aDetails string to explian why this is called. This won't be
91 * stored nor exposed to selection listeners etc. Just for logging.
92 * @param aReasons potentially multiple of the reasons defined in
93 * nsISelectionListener.idl
95 void EndBatchChanges(const char* aDetails
,
96 int16_t aReason
= nsISelectionListener::NO_REASON
);
99 * NotifyAutoCopy() starts to notify AutoCopyListener of selection changes.
101 void NotifyAutoCopy() {
102 MOZ_ASSERT(mSelectionType
== SelectionType::eNormal
);
104 mNotifyAutoCopy
= true;
108 * MaybeNotifyAccessibleCaretEventHub() starts to notify
109 * AccessibleCaretEventHub of selection change if aPresShell has it.
111 void MaybeNotifyAccessibleCaretEventHub(PresShell
* aPresShell
);
114 * StopNotifyingAccessibleCaretEventHub() stops notifying
115 * AccessibleCaretEventHub of selection change.
117 void StopNotifyingAccessibleCaretEventHub();
120 * EnableSelectionChangeEvent() starts to notify
121 * SelectionChangeEventDispatcher of selection change to dispatch a
122 * selectionchange event at every selection change.
124 void EnableSelectionChangeEvent() {
125 if (!mSelectionChangeEventDispatcher
) {
126 mSelectionChangeEventDispatcher
= new SelectionChangeEventDispatcher();
130 // Required for WebIDL bindings, see
131 // https://developer.mozilla.org/en-US/docs/Mozilla/WebIDL_bindings#Adding_WebIDL_bindings_to_a_class.
132 Document
* GetParentObject() const;
134 DocGroup
* GetDocGroup() const;
136 // utility methods for scrolling the selection into view
137 nsPresContext
* GetPresContext() const;
138 PresShell
* GetPresShell() const;
139 nsFrameSelection
* GetFrameSelection() const { return mFrameSelection
; }
140 // Returns a rect containing the selection region, and frame that that
141 // position is relative to. For SELECTION_ANCHOR_REGION or
142 // SELECTION_FOCUS_REGION the rect is a zero-width rectangle. For
143 // SELECTION_WHOLE_SELECTION the rect contains both the anchor and focus
145 nsIFrame
* GetSelectionAnchorGeometry(SelectionRegion aRegion
, nsRect
* aRect
);
146 // Returns the position of the region (SELECTION_ANCHOR_REGION or
147 // SELECTION_FOCUS_REGION only), and frame that that position is relative to.
148 // The 'position' is a zero-width rectangle.
149 nsIFrame
* GetSelectionEndPointGeometry(SelectionRegion aRegion
,
152 nsresult
PostScrollSelectionIntoViewEvent(SelectionRegion aRegion
,
154 ScrollAxis aVertical
,
155 ScrollAxis aHorizontal
);
157 SCROLL_SYNCHRONOUS
= 1 << 1,
158 SCROLL_FIRST_ANCESTOR_ONLY
= 1 << 2,
160 1 << 3, // only matters if SCROLL_SYNCHRONOUS is passed too
161 SCROLL_OVERFLOW_HIDDEN
= 1 << 5,
162 SCROLL_FOR_CARET_MOVE
= 1 << 6
164 // If aFlags doesn't contain SCROLL_SYNCHRONOUS, then we'll flush when
165 // the scroll event fires so we make sure to scroll to the right place.
166 // Otherwise, if SCROLL_DO_FLUSH is also in aFlags, then this method will
167 // flush layout and you MUST hold a strong ref on 'this' for the duration
168 // of this call. This might destroy arbitrary layout objects.
169 MOZ_CAN_RUN_SCRIPT nsresult
170 ScrollIntoView(SelectionRegion aRegion
, ScrollAxis aVertical
= ScrollAxis(),
171 ScrollAxis aHorizontal
= ScrollAxis(), int32_t aFlags
= 0);
174 static bool IsUserSelectionCollapsed(
175 const nsRange
& aRange
, nsTArray
<RefPtr
<nsRange
>>& aTempRangesToAdd
);
177 * https://w3c.github.io/selection-api/#selectstart-event.
179 enum class DispatchSelectstartEvent
{
185 * See `AddRangesForSelectableNodes`.
187 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
AddRangesForUserSelectableNodes(
188 nsRange
* aRange
, Maybe
<size_t>* aOutIndex
,
189 const DispatchSelectstartEvent aDispatchSelectstartEvent
);
192 * Adds aRange to this Selection. If mUserInitiated is true,
193 * then aRange is first scanned for -moz-user-select:none nodes and split up
194 * into multiple ranges to exclude those before adding the resulting ranges
197 * @param aOutIndex points to the range last added, if at least one was added.
198 * If aRange is already contained, it points to the range
199 * containing it. Nothing() if mStyledRanges.mRanges was
200 * empty and no range was added.
202 [[nodiscard
]] MOZ_CAN_RUN_SCRIPT nsresult
AddRangesForSelectableNodes(
203 nsRange
* aRange
, Maybe
<size_t>* aOutIndex
,
204 DispatchSelectstartEvent aDispatchSelectstartEvent
);
207 nsresult
RemoveCollapsedRanges();
208 void Clear(nsPresContext
* aPresContext
);
209 MOZ_CAN_RUN_SCRIPT nsresult
CollapseInLimiter(nsINode
* aContainer
,
212 return NS_ERROR_INVALID_ARG
;
214 return CollapseInLimiter(RawRangeBoundary(aContainer
, aOffset
));
216 MOZ_CAN_RUN_SCRIPT nsresult
217 CollapseInLimiter(const RawRangeBoundary
& aPoint
) {
219 CollapseInLimiter(aPoint
, result
);
220 return result
.StealNSResult();
222 MOZ_CAN_RUN_SCRIPT
void CollapseInLimiter(const RawRangeBoundary
& aPoint
,
225 MOZ_CAN_RUN_SCRIPT nsresult
Extend(nsINode
* aContainer
, uint32_t aOffset
);
228 * See mStyledRanges.mRanges.
230 nsRange
* GetRangeAt(uint32_t aIndex
) const;
233 * @brief Get the |AbstractRange| at |aIndex|.
235 * This method is safe to be called for every selection type.
236 * However, |StaticRange|s only occur for |SelectionType::eHighlight|.
237 * If the SelectionType may be eHighlight, this method must be called instead
240 * Returns null if |aIndex| is out of bounds.
242 AbstractRange
* GetAbstractRangeAt(uint32_t aIndex
) const;
243 // Get the anchor-to-focus range if we don't care which end is
244 // anchor and which end is focus.
245 const nsRange
* GetAnchorFocusRange() const { return mAnchorFocusRange
; }
247 nsDirection
GetDirection() const { return mDirection
; }
249 void SetDirection(nsDirection aDir
) { mDirection
= aDir
; }
250 MOZ_CAN_RUN_SCRIPT nsresult
SetAnchorFocusToRange(nsRange
* aRange
);
252 MOZ_CAN_RUN_SCRIPT
void ReplaceAnchorFocusRange(nsRange
* aRange
);
254 void AdjustAnchorFocusForMultiRange(nsDirection aDirection
);
256 nsIFrame
* GetPrimaryFrameForAnchorNode() const;
257 nsIFrame
* GetPrimaryFrameForFocusNode(bool aVisual
,
258 int32_t* aOffsetUsed
= nullptr) const;
260 UniquePtr
<SelectionDetails
> LookUpSelection(
261 nsIContent
* aContent
, uint32_t aContentOffset
, uint32_t aContentLength
,
262 UniquePtr
<SelectionDetails
> aDetailsHead
, SelectionType aSelectionType
,
265 NS_IMETHOD
Repaint(nsPresContext
* aPresContext
);
268 nsresult
StartAutoScrollTimer(nsIFrame
* aFrame
, const nsPoint
& aPoint
,
269 uint32_t aDelayInMs
);
271 nsresult
StopAutoScrollTimer();
273 JSObject
* WrapObject(JSContext
* aCx
,
274 JS::Handle
<JSObject
*> aGivenProto
) override
;
277 nsINode
* GetAnchorNode(CallerType aCallerType
= CallerType::System
) const {
278 const RangeBoundary
& anchor
= AnchorRef();
279 nsINode
* anchorNode
= anchor
.IsSet() ? anchor
.Container() : nullptr;
280 if (!anchorNode
|| aCallerType
== CallerType::System
||
281 !anchorNode
->ChromeOnlyAccess()) {
284 // anchor is nsIContent as ChromeOnlyAccess is nsIContent-only
285 return anchorNode
->AsContent()->FindFirstNonChromeOnlyAccessContent();
287 uint32_t AnchorOffset(CallerType aCallerType
= CallerType::System
) const {
288 const RangeBoundary
& anchor
= AnchorRef();
289 if (aCallerType
!= CallerType::System
&& anchor
.IsSet() &&
290 anchor
.Container()->ChromeOnlyAccess()) {
293 const Maybe
<uint32_t> offset
=
294 anchor
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
);
295 return offset
? *offset
: 0;
297 nsINode
* GetFocusNode(CallerType aCallerType
= CallerType::System
) const {
298 const RangeBoundary
& focus
= FocusRef();
299 nsINode
* focusNode
= focus
.IsSet() ? focus
.Container() : nullptr;
300 if (!focusNode
|| aCallerType
== CallerType::System
||
301 !focusNode
->ChromeOnlyAccess()) {
304 // focus is nsIContent as ChromeOnlyAccess is nsIContent-only
305 return focusNode
->AsContent()->FindFirstNonChromeOnlyAccessContent();
307 uint32_t FocusOffset(CallerType aCallerType
= CallerType::System
) const {
308 const RangeBoundary
& focus
= FocusRef();
309 if (aCallerType
!= CallerType::System
&& focus
.IsSet() &&
310 focus
.Container()->ChromeOnlyAccess()) {
313 const Maybe
<uint32_t> offset
=
314 focus
.Offset(RangeBoundary::OffsetFilter::kValidOffsets
);
315 return offset
? *offset
: 0;
318 nsIContent
* GetChildAtAnchorOffset() {
319 const RangeBoundary
& anchor
= AnchorRef();
320 return anchor
.IsSet() ? anchor
.GetChildAtOffset() : nullptr;
322 nsIContent
* GetChildAtFocusOffset() {
323 const RangeBoundary
& focus
= FocusRef();
324 return focus
.IsSet() ? focus
.GetChildAtOffset() : nullptr;
327 const RangeBoundary
& AnchorRef() const;
328 const RangeBoundary
& FocusRef() const;
331 * IsCollapsed -- is the whole selection just one point, or unset?
333 bool IsCollapsed() const {
334 size_t cnt
= mStyledRanges
.Length();
343 return mStyledRanges
.mRanges
[0].mRange
->Collapsed();
346 // *JS() methods are mapped to Selection.*().
347 // They may move focus only when the range represents normal selection.
348 // These methods shouldn't be used by non-JS callers.
349 MOZ_CAN_RUN_SCRIPT
void CollapseJS(nsINode
* aContainer
, uint32_t aOffset
,
350 mozilla::ErrorResult
& aRv
);
351 MOZ_CAN_RUN_SCRIPT
void CollapseToStartJS(mozilla::ErrorResult
& aRv
);
352 MOZ_CAN_RUN_SCRIPT
void CollapseToEndJS(mozilla::ErrorResult
& aRv
);
354 MOZ_CAN_RUN_SCRIPT
void ExtendJS(nsINode
& aContainer
, uint32_t aOffset
,
355 mozilla::ErrorResult
& aRv
);
357 MOZ_CAN_RUN_SCRIPT
void SelectAllChildrenJS(nsINode
& aNode
,
358 mozilla::ErrorResult
& aRv
);
361 * Deletes this selection from document the nodes belong to.
362 * Only if this has `SelectionType::eNormal`.
364 MOZ_CAN_RUN_SCRIPT
void DeleteFromDocument(mozilla::ErrorResult
& aRv
);
366 uint32_t RangeCount() const { return mStyledRanges
.Length(); }
368 void GetType(nsAString
& aOutType
) const;
370 nsRange
* GetRangeAt(uint32_t aIndex
, mozilla::ErrorResult
& aRv
);
371 MOZ_CAN_RUN_SCRIPT
void AddRangeJS(nsRange
& aRange
,
372 mozilla::ErrorResult
& aRv
);
375 * Callers need to keep `aRange` alive.
377 MOZ_CAN_RUN_SCRIPT
void RemoveRangeAndUnselectFramesAndNotifyListeners(
378 AbstractRange
& aRange
, mozilla::ErrorResult
& aRv
);
380 MOZ_CAN_RUN_SCRIPT
void RemoveAllRanges(mozilla::ErrorResult
& aRv
);
383 * Whether Stringify should flush layout or not.
385 enum class FlushFrames
{ No
, Yes
};
387 void Stringify(nsAString
& aResult
, FlushFrames
= FlushFrames::Yes
);
390 * Indicates whether the node is part of the selection. If partlyContained
391 * is true, the function returns true when some part of the node
392 * is part of the selection. If partlyContained is false, the
393 * function only returns true when the entire node is part of the selection.
395 bool ContainsNode(nsINode
& aNode
, bool aPartlyContained
,
396 mozilla::ErrorResult
& aRv
);
399 * Check to see if the given point is contained within the selection area. In
400 * particular, this iterates through all the rects that make up the selection,
401 * not just the bounding box, and checks to see if the given point is
402 * contained in any one of them.
403 * @param aPoint The point to check, relative to the root frame.
405 bool ContainsPoint(const nsPoint
& aPoint
);
408 * Modifies the selection. Note that the parameters are case-insensitive.
410 * @param alter can be one of { "move", "extend" }
411 * - "move" collapses the selection to the end of the selection and
412 * applies the movement direction/granularity to the collapsed
414 * - "extend" leaves the start of the selection unchanged, and applies
415 * movement direction/granularity to the end of the selection.
416 * @param direction can be one of { "forward", "backward", "left", "right" }
417 * @param granularity can be one of { "character", "word",
418 * "line", "lineboundary" }
420 * @throws NS_ERROR_NOT_IMPLEMENTED if the granularity is "sentence",
421 * "sentenceboundary", "paragraph", "paragraphboundary", or
422 * "documentboundary". Throws NS_ERROR_INVALID_ARG if alter, direction,
423 * or granularity has an unrecognized value.
425 MOZ_CAN_RUN_SCRIPT
void Modify(const nsAString
& aAlter
,
426 const nsAString
& aDirection
,
427 const nsAString
& aGranularity
,
428 mozilla::ErrorResult
& aRv
);
431 void SetBaseAndExtentJS(nsINode
& aAnchorNode
, uint32_t aAnchorOffset
,
432 nsINode
& aFocusNode
, uint32_t aFocusOffset
,
433 mozilla::ErrorResult
& aRv
);
435 bool GetInterlinePositionJS(mozilla::ErrorResult
& aRv
) const;
436 void SetInterlinePositionJS(bool aHintRight
, mozilla::ErrorResult
& aRv
);
438 enum class InterlinePosition
: uint8_t {
439 // Caret should be put at end of line (i.e., before the line break)
441 // Caret should be put at start of next line (i.e., after the line break)
443 // Undefined means only what is not EndOfLine nor StartOfNextLine.
444 // `SetInterlinePosition` should never be called with this value, and
445 // if `GetInterlinePosition` returns this, it means that the instance has
446 // not been initialized or cleared by the cycle collector or something.
447 // If a method needs to consider whether to call `SetInterlinePosition` or
448 // not call, this value can be used for the latter.
451 InterlinePosition
GetInterlinePosition() const;
452 nsresult
SetInterlinePosition(InterlinePosition aInterlinePosition
);
454 Nullable
<int16_t> GetCaretBidiLevel(mozilla::ErrorResult
& aRv
) const;
455 void SetCaretBidiLevel(const Nullable
<int16_t>& aCaretBidiLevel
,
456 mozilla::ErrorResult
& aRv
);
458 void ToStringWithFormat(const nsAString
& aFormatType
, uint32_t aFlags
,
459 int32_t aWrapColumn
, nsAString
& aReturn
,
460 mozilla::ErrorResult
& aRv
);
461 void AddSelectionListener(nsISelectionListener
* aListener
);
462 void RemoveSelectionListener(nsISelectionListener
* aListener
);
464 RawSelectionType
RawType() const {
465 return ToRawSelectionType(mSelectionType
);
467 SelectionType
Type() const { return mSelectionType
; }
470 * @brief Sets highlight selection properties.
472 * This includes the highlight name as well as its priority and type.
474 void SetHighlightSelectionData(
475 HighlightSelectionData aHighlightSelectionData
);
478 * See documentation of `GetRangesForInterval` in Selection.webidl.
480 * @param aReturn references, not copies, of the internal ranges.
482 void GetRangesForInterval(nsINode
& aBeginNode
, uint32_t aBeginOffset
,
483 nsINode
& aEndNode
, uint32_t aEndOffset
,
485 nsTArray
<RefPtr
<nsRange
>>& aReturn
,
488 MOZ_CAN_RUN_SCRIPT
void ScrollIntoView(int16_t aRegion
, bool aIsSynchronous
,
489 int16_t aVPercent
, int16_t aHPercent
,
492 void SetColors(const nsAString
& aForeColor
, const nsAString
& aBackColor
,
493 const nsAString
& aAltForeColor
, const nsAString
& aAltBackColor
,
499 * Non-JS callers should use the following
500 * collapse/collapseToStart/extend/etc methods, instead of the *JS
501 * versions that bindings call.
505 * Collapses the selection to a single point, at the specified offset
506 * in the given node. When the selection is collapsed, and the content
507 * is focused and editable, the caret will blink there.
508 * @param aContainer The given node where the selection will be set
509 * @param aOffset Where in given dom node to place the selection (the
510 * offset into the given node)
512 MOZ_CAN_RUN_SCRIPT
void CollapseInLimiter(nsINode
& aContainer
,
515 CollapseInLimiter(RawRangeBoundary(&aContainer
, aOffset
), aRv
);
519 enum class InLimiter
{
520 // If eYes, the method may reset selection limiter and move focus if the
521 // given range is out of the limiter.
523 // If eNo, the method won't reset selection limiter. So, if given range
524 // is out of bounds, the method may return error.
528 void CollapseInternal(InLimiter aInLimiter
, const RawRangeBoundary
& aPoint
,
533 * Collapses the whole selection to a single point at the start
534 * of the current selection (irrespective of direction). If content
535 * is focused and editable, the caret will blink there.
537 MOZ_CAN_RUN_SCRIPT
void CollapseToStart(mozilla::ErrorResult
& aRv
);
540 * Collapses the whole selection to a single point at the end
541 * of the current selection (irrespective of direction). If content
542 * is focused and editable, the caret will blink there.
544 MOZ_CAN_RUN_SCRIPT
void CollapseToEnd(mozilla::ErrorResult
& aRv
);
547 * Extends the selection by moving the selection end to the specified node and
548 * offset, preserving the selection begin position. The new selection end
549 * result will always be from the anchorNode to the new focusNode, regardless
552 * @param aContainer The node where the selection will be extended to
553 * @param aOffset Where in aContainer to place the offset of the new
556 MOZ_CAN_RUN_SCRIPT
void Extend(nsINode
& aContainer
, uint32_t aOffset
,
559 MOZ_CAN_RUN_SCRIPT
void AddRangeAndSelectFramesAndNotifyListeners(
560 nsRange
& aRange
, mozilla::ErrorResult
& aRv
);
562 MOZ_CAN_RUN_SCRIPT
void AddHighlightRangeAndSelectFramesAndNotifyListeners(
563 AbstractRange
& aRange
);
566 * Adds all children of the specified node to the selection.
567 * @param aNode the parent of the children to be added to the selection.
569 MOZ_CAN_RUN_SCRIPT
void SelectAllChildren(nsINode
& aNode
,
570 mozilla::ErrorResult
& aRv
);
573 * SetStartAndEnd() removes all ranges and sets new range as given range.
574 * Different from SetBaseAndExtent(), this won't compare the DOM points of
575 * aStartRef and aEndRef for performance nor set direction to eDirPrevious.
576 * Note that this may reset the limiter and move focus. If you don't want
577 * that, use SetStartAndEndInLimiter() instead.
580 void SetStartAndEnd(const RawRangeBoundary
& aStartRef
,
581 const RawRangeBoundary
& aEndRef
, ErrorResult
& aRv
);
583 void SetStartAndEnd(nsINode
& aStartContainer
, uint32_t aStartOffset
,
584 nsINode
& aEndContainer
, uint32_t aEndOffset
,
586 SetStartAndEnd(RawRangeBoundary(&aStartContainer
, aStartOffset
),
587 RawRangeBoundary(&aEndContainer
, aEndOffset
), aRv
);
591 * SetStartAndEndInLimiter() is similar to SetStartAndEnd(), but this respects
592 * the selection limiter. If all or part of given range is not in the
593 * limiter, this returns error.
596 void SetStartAndEndInLimiter(const RawRangeBoundary
& aStartRef
,
597 const RawRangeBoundary
& aEndRef
,
600 void SetStartAndEndInLimiter(nsINode
& aStartContainer
, uint32_t aStartOffset
,
601 nsINode
& aEndContainer
, uint32_t aEndOffset
,
603 SetStartAndEndInLimiter(RawRangeBoundary(&aStartContainer
, aStartOffset
),
604 RawRangeBoundary(&aEndContainer
, aEndOffset
), aRv
);
607 Result
<Ok
, nsresult
> SetStartAndEndInLimiter(
608 nsINode
& aStartContainer
, uint32_t aStartOffset
, nsINode
& aEndContainer
,
609 uint32_t aEndOffset
, nsDirection aDirection
, int16_t aReason
);
612 * SetBaseAndExtent() is alternative of the JS API for internal use.
613 * Different from SetStartAndEnd(), this sets anchor and focus points as
614 * specified, then if anchor point is after focus node, this sets the
615 * direction to eDirPrevious.
616 * Note that this may reset the limiter and move focus. If you don't want
617 * that, use SetBaseAndExtentInLimier() instead.
620 void SetBaseAndExtent(nsINode
& aAnchorNode
, uint32_t aAnchorOffset
,
621 nsINode
& aFocusNode
, uint32_t aFocusOffset
,
624 void SetBaseAndExtent(const RawRangeBoundary
& aAnchorRef
,
625 const RawRangeBoundary
& aFocusRef
, ErrorResult
& aRv
);
628 * SetBaseAndExtentInLimiter() is similar to SetBaseAndExtent(), but this
629 * respects the selection limiter. If all or part of given range is not in
630 * the limiter, this returns error.
633 void SetBaseAndExtentInLimiter(nsINode
& aAnchorNode
, uint32_t aAnchorOffset
,
634 nsINode
& aFocusNode
, uint32_t aFocusOffset
,
636 SetBaseAndExtentInLimiter(RawRangeBoundary(&aAnchorNode
, aAnchorOffset
),
637 RawRangeBoundary(&aFocusNode
, aFocusOffset
), aRv
);
640 void SetBaseAndExtentInLimiter(const RawRangeBoundary
& aAnchorRef
,
641 const RawRangeBoundary
& aFocusRef
,
644 void AddSelectionChangeBlocker();
645 void RemoveSelectionChangeBlocker();
646 bool IsBlockingSelectionChangeEvents() const;
648 // Whether this selection is focused in an editable element.
649 bool IsEditorSelection() const;
652 * Set the painting style for the range. The range must be a range in
653 * the selection. The textRangeStyle will be used by text frame
654 * when it is painting the selection.
656 nsresult
SetTextRangeStyle(nsRange
* aRange
,
657 const TextRangeStyle
& aTextRangeStyle
);
659 // Methods to manipulate our mFrameSelection's ancestor limiter.
660 nsIContent
* GetAncestorLimiter() const;
661 void SetAncestorLimiter(nsIContent
* aLimiter
);
664 * Frame Offset cache can be used just during calling
665 * nsEditor::EndPlaceHolderTransaction. EndPlaceHolderTransaction will give
666 * rise to reflow/refreshing view/scroll, and call times of
667 * nsTextFrame::GetPointFromOffset whose return value is to be cached. see
668 * bugs 35296 and 199412
670 void SetCanCacheFrameOffset(bool aCanCacheFrameOffset
);
672 // Selection::GetAbstractRangesForIntervalArray
674 // Fills a nsTArray with the ranges overlapping the range specified by
675 // the given endpoints. Ranges in the selection exactly adjacent to the
676 // input range are not returned unless aAllowAdjacent is set.
678 // For example, if the following ranges were in the selection
679 // (assume everything is within the same node)
681 // Start Offset: 0 2 7 9
682 // End Offset: 2 5 9 10
684 // and passed aBeginOffset of 2 and aEndOffset of 9, then with
685 // aAllowAdjacent set, all the ranges should be returned. If
686 // aAllowAdjacent was false, the ranges [2, 5] and [7, 9] only
687 // should be returned
689 // Now that overlapping ranges are disallowed, there can be a maximum of
691 nsresult
GetAbstractRangesForIntervalArray(nsINode
* aBeginNode
,
692 uint32_t aBeginOffset
,
696 nsTArray
<AbstractRange
*>* aRanges
);
699 * Converts the results of |GetAbstractRangesForIntervalArray()| to |nsRange|.
701 * |StaticRange|s can only occur in Selections of type |eHighlight|.
702 * Therefore, this method must not be called for this selection type
703 * as not every |AbstractRange| can be cast to |nsRange|.
705 nsresult
GetDynamicRangesForIntervalArray(
706 nsINode
* aBeginNode
, uint32_t aBeginOffset
, nsINode
* aEndNode
,
707 uint32_t aEndOffset
, bool aAllowAdjacent
, nsTArray
<nsRange
*>* aRanges
);
710 * Modifies the cursor Bidi level after a change in keyboard direction
711 * @param langRTL is true if the new language is right-to-left or
712 * false if the new language is left-to-right.
714 nsresult
SelectionLanguageChange(bool aLangRTL
);
717 bool HasSameRootOrSameComposedDoc(const nsINode
& aNode
);
719 // XXX Please don't add additional uses of this method, it's only for
720 // XXX supporting broken code (bug 1245883) in the following classes:
721 friend class ::nsCopySupport
;
722 friend class ::nsHTMLCopyEncoder
;
724 void AddRangeAndSelectFramesAndNotifyListenersInternal(nsRange
& aRange
,
728 // This is helper method for GetPrimaryFrameForFocusNode.
729 // If aVisual is true, this returns caret frame.
730 // If false, this returns primary frame.
731 nsIFrame
* GetPrimaryOrCaretFrameForNodeOffset(nsIContent
* aContent
,
733 int32_t* aOffsetUsed
,
736 // Get the cached value for nsTextFrame::GetPointFromOffset.
737 nsresult
GetCachedFrameOffset(nsIFrame
* aFrame
, int32_t inOffset
,
741 void SetStartAndEndInternal(InLimiter aInLimiter
,
742 const RawRangeBoundary
& aStartRef
,
743 const RawRangeBoundary
& aEndRef
,
744 nsDirection aDirection
, ErrorResult
& aRv
);
746 void SetBaseAndExtentInternal(InLimiter aInLimiter
,
747 const RawRangeBoundary
& aAnchorRef
,
748 const RawRangeBoundary
& aFocusRef
,
752 SelectionType
GetType() const { return mSelectionType
; }
754 SelectionCustomColors
* GetCustomColors() const { return mCustomColors
.get(); }
756 MOZ_CAN_RUN_SCRIPT
void NotifySelectionListeners(bool aCalledByJS
);
757 MOZ_CAN_RUN_SCRIPT
void NotifySelectionListeners();
759 friend struct AutoUserInitiated
;
760 struct MOZ_RAII AutoUserInitiated
{
761 explicit AutoUserInitiated(Selection
& aSelectionRef
)
762 : AutoUserInitiated(&aSelectionRef
) {}
763 explicit AutoUserInitiated(Selection
* aSelection
)
764 : mSavedValue(aSelection
->mUserInitiated
) {
765 aSelection
->mUserInitiated
= true;
767 AutoRestore
<bool> mSavedValue
;
771 friend struct mozilla::AutoPrepareFocusRange
;
772 class ScrollSelectionIntoViewEvent
;
773 friend class ScrollSelectionIntoViewEvent
;
775 class ScrollSelectionIntoViewEvent
: public Runnable
{
777 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_DECL_NSIRUNNABLE
779 ScrollSelectionIntoViewEvent(Selection
* aSelection
, SelectionRegion aRegion
,
780 ScrollAxis aVertical
, ScrollAxis aHorizontal
,
782 : Runnable("dom::Selection::ScrollSelectionIntoViewEvent"),
783 mSelection(aSelection
),
785 mVerticalScroll(aVertical
),
786 mHorizontalScroll(aHorizontal
),
788 NS_ASSERTION(aSelection
, "null parameter");
790 void Revoke() { mSelection
= nullptr; }
793 Selection
* mSelection
;
794 SelectionRegion mRegion
;
795 ScrollAxis mVerticalScroll
;
796 ScrollAxis mHorizontalScroll
;
801 * Set mAnchorFocusRange to mStyledRanges.mRanges[aIndex] if aIndex is a valid
804 void SetAnchorFocusRange(size_t aIndex
);
805 void RemoveAnchorFocusRange() { mAnchorFocusRange
= nullptr; }
806 void SelectFramesOf(nsIContent
* aContent
, bool aSelected
) const;
809 * https://dom.spec.whatwg.org/#concept-tree-inclusive-descendant.
811 nsresult
SelectFramesOfInclusiveDescendantsOfContent(
812 PostContentIterator
& aPostOrderIter
, nsIContent
* aContent
,
813 bool aSelected
) const;
815 nsresult
SelectFrames(nsPresContext
* aPresContext
, AbstractRange
& aRange
,
819 * SelectFramesInAllRanges() calls SelectFrames() for all current
822 void SelectFramesInAllRanges(nsPresContext
* aPresContext
);
825 * @param aOutIndex If some, points to the index of the range in
826 * mStyledRanges.mRanges so that it's always in [0, mStyledRanges.Length()].
827 * Otherwise, if nothing, this didn't add the range to mStyledRanges.
829 MOZ_CAN_RUN_SCRIPT nsresult
MaybeAddTableCellRange(nsRange
& aRange
,
830 Maybe
<size_t>* aOutIndex
);
832 Document
* GetDocument() const;
834 MOZ_CAN_RUN_SCRIPT
void RemoveAllRangesInternal(mozilla::ErrorResult
& aRv
);
838 struct StyledRanges
{
839 explicit StyledRanges(Selection
& aSelection
) : mSelection(aSelection
) {}
842 StyledRange
* FindRangeData(AbstractRange
* aRange
);
844 using Elements
= AutoTArray
<StyledRange
, 1>;
846 Elements::size_type
Length() const;
848 nsresult
RemoveCollapsedRanges();
850 nsresult
RemoveRangeAndUnregisterSelection(AbstractRange
& aRange
);
853 * Binary searches the given sorted array of ranges for the insertion point
854 * for the given node/offset. The given comparator is used, and the index
855 * where the point should appear in the array is returned.
857 * If there is an item in the array equal to the input point (aPointNode,
858 * aPointOffset), we will return the index of this item.
860 * @return the index where the point should appear in the array. In
861 * [0, `aElementArray->Length()`].
863 static size_t FindInsertionPoint(
864 const nsTArray
<StyledRange
>* aElementArray
, const nsINode
& aPointNode
,
865 uint32_t aPointOffset
,
866 int32_t (*aComparator
)(const nsINode
&, uint32_t, const AbstractRange
&));
869 * Works on the same principle as GetRangesForIntervalArray, however
870 * instead this returns the indices into mRanges between which
871 * the overlapping ranges lie.
873 * @param aStartIndex If some, aEndIndex will also be some and the value of
874 * aStartIndex will be less or equal than aEndIndex. If
875 * nothing, aEndIndex will also be nothing and it means
876 * that there is no range which in the range.
877 * @param aEndIndex If some, the value is less than mRanges.Length().
879 nsresult
GetIndicesForInterval(const nsINode
* aBeginNode
,
880 uint32_t aBeginOffset
,
881 const nsINode
* aEndNode
, uint32_t aEndOffset
,
883 Maybe
<size_t>& aStartIndex
,
884 Maybe
<size_t>& aEndIndex
) const;
886 bool HasEqualRangeBoundariesAt(const nsRange
& aRange
,
887 size_t aRangeIndex
) const;
890 * Preserves the sorting and disjunctiveness of mRanges.
892 * @param aOutIndex If some, will point to the index of the added range, or
893 * if aRange is already contained, to the one containing
894 * it. Hence it'll always be in [0, mRanges.Length()).
895 * This is nothing only when the method returns an error.
897 MOZ_CAN_RUN_SCRIPT nsresult
898 MaybeAddRangeAndTruncateOverlaps(nsRange
* aRange
, Maybe
<size_t>* aOutIndex
);
901 * GetCommonEditingHost() returns common editing host of all
902 * ranges if there is. If at least one of the ranges is in non-editable
903 * element, returns nullptr. See following examples for the detail:
905 * <div id="a" contenteditable>
907 * <div id="b" contenteditable="false">
909 * <div id="c" contenteditable>
911 * in this case, this returns div#a because div#c is also in div#a.
913 * <div id="a" contenteditable>
915 * <div id="b" contenteditable="false">
917 * <div id="c" contenteditable>
919 * in this case, this returns div#a because second range is also in div#a
920 * and common ancestor of the range (i.e., div#c) is editable.
922 * <div id="a" contenteditable>
924 * <div id="b" contenteditable="false">
926 * <div id="c" contenteditable>
928 * in this case, this returns nullptr because the second range is in
931 Element
* GetCommonEditingHost() const;
933 MOZ_CAN_RUN_SCRIPT
void MaybeFocusCommonEditingHost(
934 PresShell
* aPresShell
) const;
936 static nsresult
SubtractRange(StyledRange
& aRange
, nsRange
& aSubtract
,
937 nsTArray
<StyledRange
>* aOutput
);
939 void UnregisterSelection();
941 // These are the ranges inside this selection. They are kept sorted in order
942 // of DOM start position.
944 // This data structure is sorted by the range beginnings. As the ranges are
945 // disjoint, it is also implicitly sorted by the range endings. This allows
946 // us to perform binary searches when searching for existence of a range,
947 // giving us O(log n) search time.
949 // Inserting a new range requires finding the overlapping interval,
950 // requiring two binary searches plus up to an additional 6 DOM comparisons.
951 // If this proves to be a performance concern, then an interval tree may be
952 // a possible solution, allowing the calculation of the overlap interval in
953 // O(log n) time, though this would require rebalancing and other overhead.
956 Selection
& mSelection
;
959 StyledRanges mStyledRanges
{*this};
961 RefPtr
<nsRange
> mAnchorFocusRange
;
962 RefPtr
<nsFrameSelection
> mFrameSelection
;
963 RefPtr
<AccessibleCaretEventHub
> mAccessibleCaretEventHub
;
964 RefPtr
<SelectionChangeEventDispatcher
> mSelectionChangeEventDispatcher
;
965 RefPtr
<AutoScroller
> mAutoScroller
;
966 nsTArray
<nsCOMPtr
<nsISelectionListener
>> mSelectionListeners
;
967 nsRevocableEventPtr
<ScrollSelectionIntoViewEvent
> mScrollEvent
;
968 CachedOffsetForFrame
* mCachedOffsetForFrame
;
969 nsDirection mDirection
;
970 const SelectionType mSelectionType
;
971 HighlightSelectionData mHighlightData
;
972 UniquePtr
<SelectionCustomColors
> mCustomColors
;
974 // Non-zero if we don't want any changes we make to the selection to be
975 // visible to content. If non-zero, content won't be notified about changes.
976 uint32_t mSelectionChangeBlockerCount
;
979 * True if the current selection operation was initiated by user action.
980 * It determines whether we exclude -moz-user-select:none nodes or not,
981 * as well as whether selectstart events will be fired.
986 * When the selection change is caused by a call of Selection API,
987 * mCalledByJS is true. Otherwise, false.
992 * true if AutoCopyListner::OnSelectionChange() should be called.
994 bool mNotifyAutoCopy
;
997 // Stack-class to turn on/off selection batching.
998 class MOZ_STACK_CLASS SelectionBatcher final
{
1000 const RefPtr
<Selection
> mSelection
;
1001 const int16_t mReasons
;
1002 const char* const mRequesterFuncName
;
1006 * @param aRequesterFuncName function name which wants the selection batch.
1007 * This won't be stored nor exposed to selection listeners etc, used only for
1008 * logging. This MUST be living when the destructor runs.
1010 // TODO: Mark these constructors `MOZ_CAN_RUN_SCRIPT` because the destructor
1011 // may run script via nsISelectionListener.
1012 explicit SelectionBatcher(Selection
& aSelectionRef
,
1013 const char* aRequesterFuncName
,
1014 int16_t aReasons
= nsISelectionListener::NO_REASON
)
1015 : SelectionBatcher(&aSelectionRef
, aRequesterFuncName
, aReasons
) {}
1016 explicit SelectionBatcher(Selection
* aSelection
,
1017 const char* aRequesterFuncName
,
1018 int16_t aReasons
= nsISelectionListener::NO_REASON
)
1019 : mSelection(aSelection
),
1021 mRequesterFuncName(aRequesterFuncName
) {
1023 mSelection
->StartBatchChanges(mRequesterFuncName
);
1027 ~SelectionBatcher() {
1029 mSelection
->EndBatchChanges(mRequesterFuncName
, mReasons
);
1034 class MOZ_RAII AutoHideSelectionChanges final
{
1036 explicit AutoHideSelectionChanges(const nsFrameSelection
* aFrame
);
1038 explicit AutoHideSelectionChanges(Selection
& aSelectionRef
)
1039 : AutoHideSelectionChanges(&aSelectionRef
) {}
1041 ~AutoHideSelectionChanges() {
1043 mSelection
->RemoveSelectionChangeBlocker();
1048 explicit AutoHideSelectionChanges(Selection
* aSelection
)
1049 : mSelection(aSelection
) {
1051 mSelection
->AddSelectionChangeBlocker();
1055 RefPtr
<Selection
> mSelection
;
1060 inline bool IsValidRawSelectionType(RawSelectionType aRawSelectionType
) {
1061 return aRawSelectionType
>= nsISelectionController::SELECTION_NONE
&&
1062 aRawSelectionType
<= nsISelectionController::SELECTION_URLSTRIKEOUT
;
1065 inline SelectionType
ToSelectionType(RawSelectionType aRawSelectionType
) {
1066 if (!IsValidRawSelectionType(aRawSelectionType
)) {
1067 return SelectionType::eInvalid
;
1069 return static_cast<SelectionType
>(aRawSelectionType
);
1072 inline RawSelectionType
ToRawSelectionType(SelectionType aSelectionType
) {
1073 MOZ_ASSERT(aSelectionType
!= SelectionType::eInvalid
);
1074 return static_cast<RawSelectionType
>(aSelectionType
);
1077 inline RawSelectionType
ToRawSelectionType(TextRangeType aTextRangeType
) {
1078 return ToRawSelectionType(ToSelectionType(aTextRangeType
));
1081 inline SelectionTypeMask
ToSelectionTypeMask(SelectionType aSelectionType
) {
1082 MOZ_ASSERT(aSelectionType
!= SelectionType::eInvalid
);
1083 return aSelectionType
== SelectionType::eNone
1085 : static_cast<SelectionTypeMask
>(
1086 1 << (static_cast<uint8_t>(aSelectionType
) - 1));
1089 inline std::ostream
& operator<<(
1090 std::ostream
& aStream
, const dom::Selection::InterlinePosition
& aPosition
) {
1091 using InterlinePosition
= dom::Selection::InterlinePosition
;
1092 switch (aPosition
) {
1093 case InterlinePosition::EndOfLine
:
1094 return aStream
<< "InterlinePosition::EndOfLine";
1095 case InterlinePosition::StartOfNextLine
:
1096 return aStream
<< "InterlinePosition::StartOfNextLine";
1097 case InterlinePosition::Undefined
:
1098 return aStream
<< "InterlinePosition::Undefined";
1100 MOZ_ASSERT_UNREACHABLE("Illegal value");
1101 return aStream
<< "<Illegal value>";
1105 } // namespace mozilla
1107 #endif // mozilla_Selection_h__