Bug 1852754: part 9) Add tests for dynamically loading <link rel="prefetch"> elements...
[gecko.git] / dom / base / Selection.h
blob6ebddbdd2b42413ec2740cfd8dcd649fc1d92fa9
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"
22 #include "nsRange.h"
23 #include "nsTArrayForwardDeclare.h"
24 #include "nsThreadUtils.h"
25 #include "nsWeakReference.h"
26 #include "nsWrapperCache.h"
28 struct CachedOffsetForFrame;
29 class AutoScroller;
30 class nsIFrame;
31 class nsFrameSelection;
32 class nsPIDOMWindowOuter;
33 struct SelectionDetails;
34 struct SelectionCustomColors;
35 class nsCopySupport;
36 class nsHTMLCopyEncoder;
37 class nsPresContext;
38 struct nsPoint;
39 struct nsRect;
41 namespace mozilla {
42 class AccessibleCaretEventHub;
43 class ErrorResult;
44 class HTMLEditor;
45 class PostContentIterator;
46 enum class TableSelectionMode : uint32_t;
47 struct AutoPrepareFocusRange;
48 namespace dom {
49 class DocGroup;
50 } // namespace dom
51 } // namespace mozilla
53 namespace mozilla {
55 namespace dom {
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 {
65 protected:
66 virtual ~Selection();
68 public:
69 /**
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)
78 /**
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);
87 /**
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);
98 /**
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
144 // region rects.
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,
150 nsRect* aRect);
152 nsresult PostScrollSelectionIntoViewEvent(SelectionRegion aRegion,
153 int32_t aFlags,
154 ScrollAxis aVertical,
155 ScrollAxis aHorizontal);
156 enum {
157 SCROLL_SYNCHRONOUS = 1 << 1,
158 SCROLL_FIRST_ANCESTOR_ONLY = 1 << 2,
159 SCROLL_DO_FLUSH =
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);
173 private:
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 {
181 Maybe,
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
195 * to this Selection.
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);
206 public:
207 nsresult RemoveCollapsedRanges();
208 void Clear(nsPresContext* aPresContext);
209 MOZ_CAN_RUN_SCRIPT nsresult CollapseInLimiter(nsINode* aContainer,
210 uint32_t aOffset) {
211 if (!aContainer) {
212 return NS_ERROR_INVALID_ARG;
214 return CollapseInLimiter(RawRangeBoundary(aContainer, aOffset));
216 MOZ_CAN_RUN_SCRIPT nsresult
217 CollapseInLimiter(const RawRangeBoundary& aPoint) {
218 ErrorResult result;
219 CollapseInLimiter(aPoint, result);
220 return result.StealNSResult();
222 MOZ_CAN_RUN_SCRIPT void CollapseInLimiter(const RawRangeBoundary& aPoint,
223 ErrorResult& aRv);
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
238 * of |GetRangeAt()|.
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,
263 bool aSlowCheck);
265 NS_IMETHOD Repaint(nsPresContext* aPresContext);
267 MOZ_CAN_RUN_SCRIPT
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;
276 // WebIDL methods
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()) {
282 return anchorNode;
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()) {
291 return 0;
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()) {
302 return focusNode;
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()) {
311 return 0;
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();
335 if (cnt == 0) {
336 return true;
339 if (cnt != 1) {
340 return false;
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 };
386 MOZ_CAN_RUN_SCRIPT
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
413 * selection.
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);
430 MOZ_CAN_RUN_SCRIPT
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)
440 EndOfLine,
441 // Caret should be put at start of next line (i.e., after the line break)
442 StartOfNextLine,
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.
449 Undefined,
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,
484 bool aAllowAdjacent,
485 nsTArray<RefPtr<nsRange>>& aReturn,
486 ErrorResult& aRv);
488 MOZ_CAN_RUN_SCRIPT void ScrollIntoView(int16_t aRegion, bool aIsSynchronous,
489 int16_t aVPercent, int16_t aHPercent,
490 ErrorResult& aRv);
492 void SetColors(const nsAString& aForeColor, const nsAString& aBackColor,
493 const nsAString& aAltForeColor, const nsAString& aAltBackColor,
494 ErrorResult& aRv);
496 void ResetColors();
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,
513 uint32_t aOffset,
514 ErrorResult& aRv) {
515 CollapseInLimiter(RawRangeBoundary(&aContainer, aOffset), aRv);
518 private:
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.
522 eYes,
523 // If eNo, the method won't reset selection limiter. So, if given range
524 // is out of bounds, the method may return error.
525 eNo,
527 MOZ_CAN_RUN_SCRIPT
528 void CollapseInternal(InLimiter aInLimiter, const RawRangeBoundary& aPoint,
529 ErrorResult& aRv);
531 public:
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
550 * of direction.
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
554 * selection end.
556 MOZ_CAN_RUN_SCRIPT void Extend(nsINode& aContainer, uint32_t aOffset,
557 ErrorResult& aRv);
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.
579 MOZ_CAN_RUN_SCRIPT
580 void SetStartAndEnd(const RawRangeBoundary& aStartRef,
581 const RawRangeBoundary& aEndRef, ErrorResult& aRv);
582 MOZ_CAN_RUN_SCRIPT
583 void SetStartAndEnd(nsINode& aStartContainer, uint32_t aStartOffset,
584 nsINode& aEndContainer, uint32_t aEndOffset,
585 ErrorResult& aRv) {
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.
595 MOZ_CAN_RUN_SCRIPT
596 void SetStartAndEndInLimiter(const RawRangeBoundary& aStartRef,
597 const RawRangeBoundary& aEndRef,
598 ErrorResult& aRv);
599 MOZ_CAN_RUN_SCRIPT
600 void SetStartAndEndInLimiter(nsINode& aStartContainer, uint32_t aStartOffset,
601 nsINode& aEndContainer, uint32_t aEndOffset,
602 ErrorResult& aRv) {
603 SetStartAndEndInLimiter(RawRangeBoundary(&aStartContainer, aStartOffset),
604 RawRangeBoundary(&aEndContainer, aEndOffset), aRv);
606 MOZ_CAN_RUN_SCRIPT
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.
619 MOZ_CAN_RUN_SCRIPT
620 void SetBaseAndExtent(nsINode& aAnchorNode, uint32_t aAnchorOffset,
621 nsINode& aFocusNode, uint32_t aFocusOffset,
622 ErrorResult& aRv);
623 MOZ_CAN_RUN_SCRIPT
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.
632 MOZ_CAN_RUN_SCRIPT
633 void SetBaseAndExtentInLimiter(nsINode& aAnchorNode, uint32_t aAnchorOffset,
634 nsINode& aFocusNode, uint32_t aFocusOffset,
635 ErrorResult& aRv) {
636 SetBaseAndExtentInLimiter(RawRangeBoundary(&aAnchorNode, aAnchorOffset),
637 RawRangeBoundary(&aFocusNode, aFocusOffset), aRv);
639 MOZ_CAN_RUN_SCRIPT
640 void SetBaseAndExtentInLimiter(const RawRangeBoundary& aAnchorRef,
641 const RawRangeBoundary& aFocusRef,
642 ErrorResult& aRv);
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
690 // 2 adjacent ranges
691 nsresult GetAbstractRangesForIntervalArray(nsINode* aBeginNode,
692 uint32_t aBeginOffset,
693 nsINode* aEndNode,
694 uint32_t aEndOffset,
695 bool aAllowAdjacent,
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);
716 private:
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;
723 MOZ_CAN_RUN_SCRIPT
724 void AddRangeAndSelectFramesAndNotifyListenersInternal(nsRange& aRange,
725 Document* aDocument,
726 ErrorResult&);
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,
732 uint32_t aOffset,
733 int32_t* aOffsetUsed,
734 bool aVisual) const;
736 // Get the cached value for nsTextFrame::GetPointFromOffset.
737 nsresult GetCachedFrameOffset(nsIFrame* aFrame, int32_t inOffset,
738 nsPoint& aPoint);
740 MOZ_CAN_RUN_SCRIPT
741 void SetStartAndEndInternal(InLimiter aInLimiter,
742 const RawRangeBoundary& aStartRef,
743 const RawRangeBoundary& aEndRef,
744 nsDirection aDirection, ErrorResult& aRv);
745 MOZ_CAN_RUN_SCRIPT
746 void SetBaseAndExtentInternal(InLimiter aInLimiter,
747 const RawRangeBoundary& aAnchorRef,
748 const RawRangeBoundary& aFocusRef,
749 ErrorResult& aRv);
751 public:
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;
770 private:
771 friend struct mozilla::AutoPrepareFocusRange;
772 class ScrollSelectionIntoViewEvent;
773 friend class ScrollSelectionIntoViewEvent;
775 class ScrollSelectionIntoViewEvent : public Runnable {
776 public:
777 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_DECL_NSIRUNNABLE
779 ScrollSelectionIntoViewEvent(Selection* aSelection, SelectionRegion aRegion,
780 ScrollAxis aVertical, ScrollAxis aHorizontal,
781 int32_t aFlags)
782 : Runnable("dom::Selection::ScrollSelectionIntoViewEvent"),
783 mSelection(aSelection),
784 mRegion(aRegion),
785 mVerticalScroll(aVertical),
786 mHorizontalScroll(aHorizontal),
787 mFlags(aFlags) {
788 NS_ASSERTION(aSelection, "null parameter");
790 void Revoke() { mSelection = nullptr; }
792 private:
793 Selection* mSelection;
794 SelectionRegion mRegion;
795 ScrollAxis mVerticalScroll;
796 ScrollAxis mHorizontalScroll;
797 int32_t mFlags;
801 * Set mAnchorFocusRange to mStyledRanges.mRanges[aIndex] if aIndex is a valid
802 * index.
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,
816 bool aSelect) const;
819 * SelectFramesInAllRanges() calls SelectFrames() for all current
820 * ranges.
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);
836 void Disconnect();
838 struct StyledRanges {
839 explicit StyledRanges(Selection& aSelection) : mSelection(aSelection) {}
840 void Clear();
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,
882 bool aAllowAdjacent,
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>
906 * an[cestor
907 * <div id="b" contenteditable="false">
908 * non-editable
909 * <div id="c" contenteditable>
910 * desc]endant
911 * in this case, this returns div#a because div#c is also in div#a.
913 * <div id="a" contenteditable>
914 * an[ce]stor
915 * <div id="b" contenteditable="false">
916 * non-editable
917 * <div id="c" contenteditable>
918 * de[sc]endant
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>
923 * an[ce]stor
924 * <div id="b" contenteditable="false">
925 * [non]-editable
926 * <div id="c" contenteditable>
927 * de[sc]endant
928 * in this case, this returns nullptr because the second range is in
929 * non-editable area.
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.
954 Elements mRanges;
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.
983 bool mUserInitiated;
986 * When the selection change is caused by a call of Selection API,
987 * mCalledByJS is true. Otherwise, false.
989 bool mCalledByJS;
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 {
999 private:
1000 const RefPtr<Selection> mSelection;
1001 const int16_t mReasons;
1002 const char* const mRequesterFuncName;
1004 public:
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),
1020 mReasons(aReasons),
1021 mRequesterFuncName(aRequesterFuncName) {
1022 if (mSelection) {
1023 mSelection->StartBatchChanges(mRequesterFuncName);
1027 ~SelectionBatcher() {
1028 if (mSelection) {
1029 mSelection->EndBatchChanges(mRequesterFuncName, mReasons);
1034 class MOZ_RAII AutoHideSelectionChanges final {
1035 public:
1036 explicit AutoHideSelectionChanges(const nsFrameSelection* aFrame);
1038 explicit AutoHideSelectionChanges(Selection& aSelectionRef)
1039 : AutoHideSelectionChanges(&aSelectionRef) {}
1041 ~AutoHideSelectionChanges() {
1042 if (mSelection) {
1043 mSelection->RemoveSelectionChangeBlocker();
1047 private:
1048 explicit AutoHideSelectionChanges(Selection* aSelection)
1049 : mSelection(aSelection) {
1050 if (mSelection) {
1051 mSelection->AddSelectionChangeBlocker();
1055 RefPtr<Selection> mSelection;
1058 } // namespace dom
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";
1099 default:
1100 MOZ_ASSERT_UNREACHABLE("Illegal value");
1101 return aStream << "<Illegal value>";
1105 } // namespace mozilla
1107 #endif // mozilla_Selection_h__