Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / dom / base / Selection.h
blob101bada49f98abb7227ab6b1616b18fc8986fbc2
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 CaretAssociationHint;
47 enum class TableSelectionMode : uint32_t;
48 struct AutoPrepareFocusRange;
49 struct PrimaryFrameData;
50 namespace dom {
51 class DocGroup;
52 } // namespace dom
53 } // namespace mozilla
55 namespace mozilla {
57 namespace dom {
59 // Note, the ownership of mozilla::dom::Selection depends on which way the
60 // object is created. When nsFrameSelection has created Selection,
61 // addreffing/releasing the Selection object is aggregated to nsFrameSelection.
62 // Otherwise normal addref/release is used. This ensures that nsFrameSelection
63 // is never deleted before its Selections.
64 class Selection final : public nsSupportsWeakReference,
65 public nsWrapperCache,
66 public SupportsWeakPtr {
67 using AllowRangeCrossShadowBoundary =
68 mozilla::dom::AllowRangeCrossShadowBoundary;
70 protected:
71 virtual ~Selection();
73 public:
74 /**
75 * @param aFrameSelection can be nullptr.
77 explicit Selection(SelectionType aSelectionType,
78 nsFrameSelection* aFrameSelection);
80 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
81 NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(Selection)
83 /**
84 * Match this up with EndbatchChanges. will stop ui updates while multiple
85 * selection methods are called
87 * @param aDetails string to explian why this is called. This won't be
88 * stored nor exposed to selection listeners etc. Just for logging.
90 void StartBatchChanges(const char* aDetails);
92 /**
93 * Match this up with StartBatchChanges
95 * @param aDetails string to explian why this is called. This won't be
96 * stored nor exposed to selection listeners etc. Just for logging.
97 * @param aReasons potentially multiple of the reasons defined in
98 * nsISelectionListener.idl
100 void EndBatchChanges(const char* aDetails,
101 int16_t aReason = nsISelectionListener::NO_REASON);
104 * NotifyAutoCopy() starts to notify AutoCopyListener of selection changes.
106 void NotifyAutoCopy() {
107 MOZ_ASSERT(mSelectionType == SelectionType::eNormal);
109 mNotifyAutoCopy = true;
113 * MaybeNotifyAccessibleCaretEventHub() starts to notify
114 * AccessibleCaretEventHub of selection change if aPresShell has it.
116 void MaybeNotifyAccessibleCaretEventHub(PresShell* aPresShell);
119 * StopNotifyingAccessibleCaretEventHub() stops notifying
120 * AccessibleCaretEventHub of selection change.
122 void StopNotifyingAccessibleCaretEventHub();
125 * EnableSelectionChangeEvent() starts to notify
126 * SelectionChangeEventDispatcher of selection change to dispatch a
127 * selectionchange event at every selection change.
129 void EnableSelectionChangeEvent() {
130 if (!mSelectionChangeEventDispatcher) {
131 mSelectionChangeEventDispatcher = new SelectionChangeEventDispatcher();
135 // Required for WebIDL bindings, see
136 // https://developer.mozilla.org/en-US/docs/Mozilla/WebIDL_bindings#Adding_WebIDL_bindings_to_a_class.
137 Document* GetParentObject() const;
139 DocGroup* GetDocGroup() const;
141 // utility methods for scrolling the selection into view
142 nsPresContext* GetPresContext() const;
143 PresShell* GetPresShell() const;
144 nsFrameSelection* GetFrameSelection() const { return mFrameSelection; }
145 // Returns a rect containing the selection region, and frame that that
146 // position is relative to. For SELECTION_ANCHOR_REGION or
147 // SELECTION_FOCUS_REGION the rect is a zero-width rectangle. For
148 // SELECTION_WHOLE_SELECTION the rect contains both the anchor and focus
149 // region rects.
150 nsIFrame* GetSelectionAnchorGeometry(SelectionRegion aRegion, nsRect* aRect);
151 // Returns the position of the region (SELECTION_ANCHOR_REGION or
152 // SELECTION_FOCUS_REGION only), and frame that that position is relative to.
153 // The 'position' is a zero-width rectangle.
154 nsIFrame* GetSelectionEndPointGeometry(SelectionRegion aRegion,
155 nsRect* aRect);
157 nsresult PostScrollSelectionIntoViewEvent(SelectionRegion aRegion,
158 int32_t aFlags,
159 ScrollAxis aVertical,
160 ScrollAxis aHorizontal);
161 enum {
162 SCROLL_SYNCHRONOUS = 1 << 1,
163 SCROLL_FIRST_ANCESTOR_ONLY = 1 << 2,
164 SCROLL_DO_FLUSH =
165 1 << 3, // only matters if SCROLL_SYNCHRONOUS is passed too
166 SCROLL_OVERFLOW_HIDDEN = 1 << 5,
167 SCROLL_FOR_CARET_MOVE = 1 << 6
169 // If aFlags doesn't contain SCROLL_SYNCHRONOUS, then we'll flush when
170 // the scroll event fires so we make sure to scroll to the right place.
171 // Otherwise, if SCROLL_DO_FLUSH is also in aFlags, then this method will
172 // flush layout and you MUST hold a strong ref on 'this' for the duration
173 // of this call. This might destroy arbitrary layout objects.
174 MOZ_CAN_RUN_SCRIPT nsresult
175 ScrollIntoView(SelectionRegion aRegion, ScrollAxis aVertical = ScrollAxis(),
176 ScrollAxis aHorizontal = ScrollAxis(), int32_t aFlags = 0);
178 private:
179 static bool IsUserSelectionCollapsed(
180 const nsRange& aRange, nsTArray<RefPtr<nsRange>>& aTempRangesToAdd);
182 * https://w3c.github.io/selection-api/#selectstart-event.
184 enum class DispatchSelectstartEvent {
186 Maybe,
190 * See `AddRangesForSelectableNodes`.
192 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult AddRangesForUserSelectableNodes(
193 nsRange* aRange, Maybe<size_t>* aOutIndex,
194 const DispatchSelectstartEvent aDispatchSelectstartEvent);
197 * Adds aRange to this Selection. If mUserInitiated is true,
198 * then aRange is first scanned for -moz-user-select:none nodes and split up
199 * into multiple ranges to exclude those before adding the resulting ranges
200 * to this Selection.
202 * @param aOutIndex points to the range last added, if at least one was added.
203 * If aRange is already contained, it points to the range
204 * containing it. Nothing() if mStyledRanges.mRanges was
205 * empty and no range was added.
207 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult AddRangesForSelectableNodes(
208 nsRange* aRange, Maybe<size_t>* aOutIndex,
209 DispatchSelectstartEvent aDispatchSelectstartEvent);
211 already_AddRefed<StaticRange> GetComposedRange(
212 const AbstractRange* aRange,
213 const Sequence<OwningNonNull<ShadowRoot>>& aShadowRoots) const;
215 public:
216 nsresult RemoveCollapsedRanges();
217 void Clear(nsPresContext* aPresContext);
218 MOZ_CAN_RUN_SCRIPT nsresult CollapseInLimiter(nsINode* aContainer,
219 uint32_t aOffset) {
220 if (!aContainer) {
221 return NS_ERROR_INVALID_ARG;
223 return CollapseInLimiter(RawRangeBoundary(aContainer, aOffset));
225 MOZ_CAN_RUN_SCRIPT nsresult
226 CollapseInLimiter(const RawRangeBoundary& aPoint) {
227 ErrorResult result;
228 CollapseInLimiter(aPoint, result);
229 return result.StealNSResult();
231 MOZ_CAN_RUN_SCRIPT void CollapseInLimiter(const RawRangeBoundary& aPoint,
232 ErrorResult& aRv);
234 MOZ_CAN_RUN_SCRIPT nsresult Extend(nsINode* aContainer, uint32_t aOffset);
237 * See mStyledRanges.mRanges.
239 nsRange* GetRangeAt(uint32_t aIndex) const;
242 * @brief Get the |AbstractRange| at |aIndex|.
244 * This method is safe to be called for every selection type.
245 * However, |StaticRange|s only occur for |SelectionType::eHighlight|.
246 * If the SelectionType may be eHighlight, this method must be called instead
247 * of |GetRangeAt()|.
249 * Returns null if |aIndex| is out of bounds.
251 AbstractRange* GetAbstractRangeAt(uint32_t aIndex) const;
252 // Get the anchor-to-focus range if we don't care which end is
253 // anchor and which end is focus.
254 const nsRange* GetAnchorFocusRange() const { return mAnchorFocusRange; }
256 void GetDirection(nsAString& aDirection) const;
258 nsDirection GetDirection() const { return mDirection; }
260 void SetDirection(nsDirection aDir) { mDirection = aDir; }
261 MOZ_CAN_RUN_SCRIPT nsresult SetAnchorFocusToRange(nsRange* aRange);
263 MOZ_CAN_RUN_SCRIPT void ReplaceAnchorFocusRange(nsRange* aRange);
265 void AdjustAnchorFocusForMultiRange(nsDirection aDirection);
267 nsIFrame* GetPrimaryFrameForAnchorNode() const;
270 * Get primary frame and some other data for putting caret or extending
271 * selection at the focus point.
273 PrimaryFrameData GetPrimaryFrameForCaretAtFocusNode(bool aVisual) const;
275 UniquePtr<SelectionDetails> LookUpSelection(
276 nsIContent* aContent, uint32_t aContentOffset, uint32_t aContentLength,
277 UniquePtr<SelectionDetails> aDetailsHead, SelectionType aSelectionType,
278 bool aSlowCheck);
280 NS_IMETHOD Repaint(nsPresContext* aPresContext);
282 MOZ_CAN_RUN_SCRIPT
283 nsresult StartAutoScrollTimer(nsIFrame* aFrame, const nsPoint& aPoint,
284 uint32_t aDelayInMs);
286 nsresult StopAutoScrollTimer();
288 JSObject* WrapObject(JSContext* aCx,
289 JS::Handle<JSObject*> aGivenProto) override;
291 // WebIDL methods
292 nsINode* GetAnchorNode(CallerType aCallerType = CallerType::System) const {
293 const RangeBoundary& anchor = AnchorRef();
294 nsINode* anchorNode = anchor.IsSet() ? anchor.Container() : nullptr;
295 if (!anchorNode || aCallerType == CallerType::System ||
296 !anchorNode->ChromeOnlyAccess()) {
297 return anchorNode;
299 // anchor is nsIContent as ChromeOnlyAccess is nsIContent-only
300 return anchorNode->AsContent()->FindFirstNonChromeOnlyAccessContent();
302 uint32_t AnchorOffset(CallerType aCallerType = CallerType::System) const {
303 const RangeBoundary& anchor = AnchorRef();
304 if (aCallerType != CallerType::System && anchor.IsSet() &&
305 anchor.Container()->ChromeOnlyAccess()) {
306 return 0;
308 const Maybe<uint32_t> offset =
309 anchor.Offset(RangeBoundary::OffsetFilter::kValidOffsets);
310 return offset ? *offset : 0;
312 nsINode* GetFocusNode(CallerType aCallerType = CallerType::System) const {
313 const RangeBoundary& focus = FocusRef();
314 nsINode* focusNode = focus.IsSet() ? focus.Container() : nullptr;
315 if (!focusNode || aCallerType == CallerType::System ||
316 !focusNode->ChromeOnlyAccess()) {
317 return focusNode;
319 // focus is nsIContent as ChromeOnlyAccess is nsIContent-only
320 return focusNode->AsContent()->FindFirstNonChromeOnlyAccessContent();
322 uint32_t FocusOffset(CallerType aCallerType = CallerType::System) const {
323 const RangeBoundary& focus = FocusRef();
324 if (aCallerType != CallerType::System && focus.IsSet() &&
325 focus.Container()->ChromeOnlyAccess()) {
326 return 0;
328 const Maybe<uint32_t> offset =
329 focus.Offset(RangeBoundary::OffsetFilter::kValidOffsets);
330 return offset ? *offset : 0;
333 nsINode* GetMayCrossShadowBoundaryAnchorNode() const {
334 const RangeBoundary& anchor = AnchorRef(AllowRangeCrossShadowBoundary::Yes);
335 return anchor.IsSet() ? anchor.Container() : nullptr;
338 uint32_t MayCrossShadowBoundaryAnchorOffset() const {
339 const RangeBoundary& anchor = AnchorRef(AllowRangeCrossShadowBoundary::Yes);
340 const Maybe<uint32_t> offset =
341 anchor.Offset(RangeBoundary::OffsetFilter::kValidOffsets);
342 return offset ? *offset : 0;
345 nsINode* GetMayCrossShadowBoundaryFocusNode() const {
346 const RangeBoundary& focus = FocusRef(AllowRangeCrossShadowBoundary::Yes);
347 return focus.IsSet() ? focus.Container() : nullptr;
350 uint32_t MayCrossShadowBoundaryFocusOffset() const {
351 const RangeBoundary& focus = FocusRef(AllowRangeCrossShadowBoundary::Yes);
352 const Maybe<uint32_t> offset =
353 focus.Offset(RangeBoundary::OffsetFilter::kValidOffsets);
354 return offset ? *offset : 0;
357 nsIContent* GetChildAtAnchorOffset() {
358 const RangeBoundary& anchor = AnchorRef();
359 return anchor.IsSet() ? anchor.GetChildAtOffset() : nullptr;
361 nsIContent* GetChildAtFocusOffset() {
362 const RangeBoundary& focus = FocusRef();
363 return focus.IsSet() ? focus.GetChildAtOffset() : nullptr;
366 const RangeBoundary& AnchorRef(
367 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary =
368 AllowRangeCrossShadowBoundary::No) const;
369 const RangeBoundary& FocusRef(
370 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary =
371 AllowRangeCrossShadowBoundary::No) const;
374 * IsCollapsed -- is the whole selection just one point, or unset?
376 bool IsCollapsed() const {
377 size_t cnt = mStyledRanges.Length();
378 if (cnt == 0) {
379 return true;
382 if (cnt != 1) {
383 return false;
386 return mStyledRanges.mRanges[0].mRange->Collapsed();
389 // Returns whether both normal range and cross-shadow-boundary
390 // range are collapsed.
392 // If StaticPrefs::dom_shadowdom_selection_across_boundary_enabled is
393 // disabled, this method always returns result as nsRange::IsCollapsed.
394 bool AreNormalAndCrossShadowBoundaryRangesCollapsed() const {
395 if (!IsCollapsed()) {
396 return false;
399 size_t cnt = mStyledRanges.Length();
400 if (cnt == 0) {
401 return true;
404 AbstractRange* range = mStyledRanges.mRanges[0].mRange;
405 MOZ_ASSERT_IF(
406 range->MayCrossShadowBoundary(),
407 !range->AsDynamicRange()->CrossShadowBoundaryRangeCollapsed());
408 // Returns false if nsRange::mCrossBoundaryRange exists,
409 // true otherwise.
410 return !range->MayCrossShadowBoundary();
413 // *JS() methods are mapped to Selection.*().
414 // They may move focus only when the range represents normal selection.
415 // These methods shouldn't be used by non-JS callers.
416 MOZ_CAN_RUN_SCRIPT void CollapseJS(nsINode* aContainer, uint32_t aOffset,
417 mozilla::ErrorResult& aRv);
418 MOZ_CAN_RUN_SCRIPT void CollapseToStartJS(mozilla::ErrorResult& aRv);
419 MOZ_CAN_RUN_SCRIPT void CollapseToEndJS(mozilla::ErrorResult& aRv);
421 MOZ_CAN_RUN_SCRIPT void ExtendJS(nsINode& aContainer, uint32_t aOffset,
422 mozilla::ErrorResult& aRv);
424 MOZ_CAN_RUN_SCRIPT void SelectAllChildrenJS(nsINode& aNode,
425 mozilla::ErrorResult& aRv);
428 * Deletes this selection from document the nodes belong to.
429 * Only if this has `SelectionType::eNormal`.
431 MOZ_CAN_RUN_SCRIPT void DeleteFromDocument(mozilla::ErrorResult& aRv);
433 uint32_t RangeCount() const { return mStyledRanges.Length(); }
435 void GetType(nsAString& aOutType) const;
437 nsRange* GetRangeAt(uint32_t aIndex, mozilla::ErrorResult& aRv);
438 MOZ_CAN_RUN_SCRIPT void AddRangeJS(nsRange& aRange,
439 mozilla::ErrorResult& aRv);
442 * Callers need to keep `aRange` alive.
444 MOZ_CAN_RUN_SCRIPT void RemoveRangeAndUnselectFramesAndNotifyListeners(
445 AbstractRange& aRange, mozilla::ErrorResult& aRv);
447 MOZ_CAN_RUN_SCRIPT void RemoveAllRanges(mozilla::ErrorResult& aRv);
449 void GetComposedRanges(
450 const Sequence<OwningNonNull<ShadowRoot>>& aShadowRoots,
451 nsTArray<RefPtr<StaticRange>>& aComposedRanges);
454 * Whether Stringify should flush layout or not.
456 enum class FlushFrames { No, Yes };
457 MOZ_CAN_RUN_SCRIPT
458 void Stringify(nsAString& aResult, FlushFrames = FlushFrames::Yes);
461 * Indicates whether the node is part of the selection. If partlyContained
462 * is true, the function returns true when some part of the node
463 * is part of the selection. If partlyContained is false, the
464 * function only returns true when the entire node is part of the selection.
466 bool ContainsNode(nsINode& aNode, bool aPartlyContained,
467 mozilla::ErrorResult& aRv);
470 * Check to see if the given point is contained within the selection area. In
471 * particular, this iterates through all the rects that make up the selection,
472 * not just the bounding box, and checks to see if the given point is
473 * contained in any one of them.
474 * @param aPoint The point to check, relative to the root frame.
476 bool ContainsPoint(const nsPoint& aPoint);
479 * Modifies the selection. Note that the parameters are case-insensitive.
481 * @param alter can be one of { "move", "extend" }
482 * - "move" collapses the selection to the end of the selection and
483 * applies the movement direction/granularity to the collapsed
484 * selection.
485 * - "extend" leaves the start of the selection unchanged, and applies
486 * movement direction/granularity to the end of the selection.
487 * @param direction can be one of { "forward", "backward", "left", "right" }
488 * @param granularity can be one of { "character", "word",
489 * "line", "lineboundary" }
491 * @throws NS_ERROR_NOT_IMPLEMENTED if the granularity is "sentence",
492 * "sentenceboundary", "paragraph", "paragraphboundary", or
493 * "documentboundary". Throws NS_ERROR_INVALID_ARG if alter, direction,
494 * or granularity has an unrecognized value.
496 MOZ_CAN_RUN_SCRIPT void Modify(const nsAString& aAlter,
497 const nsAString& aDirection,
498 const nsAString& aGranularity,
499 mozilla::ErrorResult& aRv);
501 MOZ_CAN_RUN_SCRIPT
502 void SetBaseAndExtentJS(nsINode& aAnchorNode, uint32_t aAnchorOffset,
503 nsINode& aFocusNode, uint32_t aFocusOffset,
504 mozilla::ErrorResult& aRv);
506 bool GetInterlinePositionJS(mozilla::ErrorResult& aRv) const;
507 void SetInterlinePositionJS(bool aHintRight, mozilla::ErrorResult& aRv);
509 enum class InterlinePosition : uint8_t {
510 // Caret should be put at end of line (i.e., before the line break)
511 EndOfLine,
512 // Caret should be put at start of next line (i.e., after the line break)
513 StartOfNextLine,
514 // Undefined means only what is not EndOfLine nor StartOfNextLine.
515 // `SetInterlinePosition` should never be called with this value, and
516 // if `GetInterlinePosition` returns this, it means that the instance has
517 // not been initialized or cleared by the cycle collector or something.
518 // If a method needs to consider whether to call `SetInterlinePosition` or
519 // not call, this value can be used for the latter.
520 Undefined,
522 InterlinePosition GetInterlinePosition() const;
523 nsresult SetInterlinePosition(InterlinePosition aInterlinePosition);
525 Nullable<int16_t> GetCaretBidiLevel(mozilla::ErrorResult& aRv) const;
526 void SetCaretBidiLevel(const Nullable<int16_t>& aCaretBidiLevel,
527 mozilla::ErrorResult& aRv);
529 void ToStringWithFormat(const nsAString& aFormatType, uint32_t aFlags,
530 int32_t aWrapColumn, nsAString& aReturn,
531 mozilla::ErrorResult& aRv);
532 void AddSelectionListener(nsISelectionListener* aListener);
533 void RemoveSelectionListener(nsISelectionListener* aListener);
535 RawSelectionType RawType() const {
536 return ToRawSelectionType(mSelectionType);
538 SelectionType Type() const { return mSelectionType; }
541 * @brief Sets highlight selection properties.
543 * This includes the highlight name as well as its priority and type.
545 void SetHighlightSelectionData(
546 HighlightSelectionData aHighlightSelectionData);
549 * See documentation of `GetRangesForInterval` in Selection.webidl.
551 * @param aReturn references, not copies, of the internal ranges.
553 void GetRangesForInterval(nsINode& aBeginNode, uint32_t aBeginOffset,
554 nsINode& aEndNode, uint32_t aEndOffset,
555 bool aAllowAdjacent,
556 nsTArray<RefPtr<nsRange>>& aReturn,
557 ErrorResult& aRv);
559 MOZ_CAN_RUN_SCRIPT void ScrollIntoView(int16_t aRegion, bool aIsSynchronous,
560 int16_t aVPercent, int16_t aHPercent,
561 ErrorResult& aRv);
563 void SetColors(const nsAString& aForeColor, const nsAString& aBackColor,
564 const nsAString& aAltForeColor, const nsAString& aAltBackColor,
565 ErrorResult& aRv);
567 void ResetColors();
570 * Non-JS callers should use the following
571 * collapse/collapseToStart/extend/etc methods, instead of the *JS
572 * versions that bindings call.
576 * Collapses the selection to a single point, at the specified offset
577 * in the given node. When the selection is collapsed, and the content
578 * is focused and editable, the caret will blink there.
579 * @param aContainer The given node where the selection will be set
580 * @param aOffset Where in given dom node to place the selection (the
581 * offset into the given node)
583 MOZ_CAN_RUN_SCRIPT void CollapseInLimiter(nsINode& aContainer,
584 uint32_t aOffset,
585 ErrorResult& aRv) {
586 CollapseInLimiter(RawRangeBoundary(&aContainer, aOffset), aRv);
589 private:
590 enum class InLimiter {
591 // If eYes, the method may reset selection limiter and move focus if the
592 // given range is out of the limiter.
593 eYes,
594 // If eNo, the method won't reset selection limiter. So, if given range
595 // is out of bounds, the method may return error.
596 eNo,
598 MOZ_CAN_RUN_SCRIPT
599 void CollapseInternal(InLimiter aInLimiter, const RawRangeBoundary& aPoint,
600 ErrorResult& aRv);
602 public:
604 * Collapses the whole selection to a single point at the start
605 * of the current selection (irrespective of direction). If content
606 * is focused and editable, the caret will blink there.
608 MOZ_CAN_RUN_SCRIPT void CollapseToStart(mozilla::ErrorResult& aRv);
611 * Collapses the whole selection to a single point at the end
612 * of the current selection (irrespective of direction). If content
613 * is focused and editable, the caret will blink there.
615 MOZ_CAN_RUN_SCRIPT void CollapseToEnd(mozilla::ErrorResult& aRv);
618 * Extends the selection by moving the selection end to the specified node and
619 * offset, preserving the selection begin position. The new selection end
620 * result will always be from the anchorNode to the new focusNode, regardless
621 * of direction.
623 * @param aContainer The node where the selection will be extended to
624 * @param aOffset Where in aContainer to place the offset of the new
625 * selection end.
627 MOZ_CAN_RUN_SCRIPT void Extend(nsINode& aContainer, uint32_t aOffset,
628 ErrorResult& aRv);
630 MOZ_CAN_RUN_SCRIPT void AddRangeAndSelectFramesAndNotifyListeners(
631 nsRange& aRange, mozilla::ErrorResult& aRv);
633 MOZ_CAN_RUN_SCRIPT void AddHighlightRangeAndSelectFramesAndNotifyListeners(
634 AbstractRange& aRange);
637 * Adds all children of the specified node to the selection.
638 * @param aNode the parent of the children to be added to the selection.
640 MOZ_CAN_RUN_SCRIPT void SelectAllChildren(nsINode& aNode,
641 mozilla::ErrorResult& aRv);
644 * SetStartAndEnd() removes all ranges and sets new range as given range.
645 * Different from SetBaseAndExtent(), this won't compare the DOM points of
646 * aStartRef and aEndRef for performance nor set direction to eDirPrevious.
647 * Note that this may reset the limiter and move focus. If you don't want
648 * that, use SetStartAndEndInLimiter() instead.
650 MOZ_CAN_RUN_SCRIPT
651 void SetStartAndEnd(const RawRangeBoundary& aStartRef,
652 const RawRangeBoundary& aEndRef, ErrorResult& aRv);
653 MOZ_CAN_RUN_SCRIPT
654 void SetStartAndEnd(nsINode& aStartContainer, uint32_t aStartOffset,
655 nsINode& aEndContainer, uint32_t aEndOffset,
656 ErrorResult& aRv) {
657 SetStartAndEnd(RawRangeBoundary(&aStartContainer, aStartOffset),
658 RawRangeBoundary(&aEndContainer, aEndOffset), aRv);
662 * SetStartAndEndInLimiter() is similar to SetStartAndEnd(), but this respects
663 * the selection limiter. If all or part of given range is not in the
664 * limiter, this returns error.
666 MOZ_CAN_RUN_SCRIPT
667 void SetStartAndEndInLimiter(const RawRangeBoundary& aStartRef,
668 const RawRangeBoundary& aEndRef,
669 ErrorResult& aRv);
670 MOZ_CAN_RUN_SCRIPT
671 void SetStartAndEndInLimiter(nsINode& aStartContainer, uint32_t aStartOffset,
672 nsINode& aEndContainer, uint32_t aEndOffset,
673 ErrorResult& aRv) {
674 SetStartAndEndInLimiter(RawRangeBoundary(&aStartContainer, aStartOffset),
675 RawRangeBoundary(&aEndContainer, aEndOffset), aRv);
677 MOZ_CAN_RUN_SCRIPT
678 Result<Ok, nsresult> SetStartAndEndInLimiter(
679 nsINode& aStartContainer, uint32_t aStartOffset, nsINode& aEndContainer,
680 uint32_t aEndOffset, nsDirection aDirection, int16_t aReason);
683 * SetBaseAndExtent() is alternative of the JS API for internal use.
684 * Different from SetStartAndEnd(), this sets anchor and focus points as
685 * specified, then if anchor point is after focus node, this sets the
686 * direction to eDirPrevious.
687 * Note that this may reset the limiter and move focus. If you don't want
688 * that, use SetBaseAndExtentInLimier() instead.
690 MOZ_CAN_RUN_SCRIPT
691 void SetBaseAndExtent(nsINode& aAnchorNode, uint32_t aAnchorOffset,
692 nsINode& aFocusNode, uint32_t aFocusOffset,
693 ErrorResult& aRv);
694 MOZ_CAN_RUN_SCRIPT
695 void SetBaseAndExtent(const RawRangeBoundary& aAnchorRef,
696 const RawRangeBoundary& aFocusRef, ErrorResult& aRv);
699 * SetBaseAndExtentInLimiter() is similar to SetBaseAndExtent(), but this
700 * respects the selection limiter. If all or part of given range is not in
701 * the limiter, this returns error.
703 MOZ_CAN_RUN_SCRIPT
704 void SetBaseAndExtentInLimiter(nsINode& aAnchorNode, uint32_t aAnchorOffset,
705 nsINode& aFocusNode, uint32_t aFocusOffset,
706 ErrorResult& aRv) {
707 SetBaseAndExtentInLimiter(RawRangeBoundary(&aAnchorNode, aAnchorOffset),
708 RawRangeBoundary(&aFocusNode, aFocusOffset), aRv);
710 MOZ_CAN_RUN_SCRIPT
711 void SetBaseAndExtentInLimiter(const RawRangeBoundary& aAnchorRef,
712 const RawRangeBoundary& aFocusRef,
713 ErrorResult& aRv);
715 void AddSelectionChangeBlocker();
716 void RemoveSelectionChangeBlocker();
717 bool IsBlockingSelectionChangeEvents() const;
719 // Whether this selection is focused in an editable element.
720 bool IsEditorSelection() const;
723 * Set the painting style for the range. The range must be a range in
724 * the selection. The textRangeStyle will be used by text frame
725 * when it is painting the selection.
727 nsresult SetTextRangeStyle(nsRange* aRange,
728 const TextRangeStyle& aTextRangeStyle);
730 // Methods to manipulate our mFrameSelection's ancestor limiter.
731 nsIContent* GetAncestorLimiter() const;
732 void SetAncestorLimiter(nsIContent* aLimiter);
735 * Frame Offset cache can be used just during calling
736 * nsEditor::EndPlaceHolderTransaction. EndPlaceHolderTransaction will give
737 * rise to reflow/refreshing view/scroll, and call times of
738 * nsTextFrame::GetPointFromOffset whose return value is to be cached. see
739 * bugs 35296 and 199412
741 void SetCanCacheFrameOffset(bool aCanCacheFrameOffset);
743 // Selection::GetAbstractRangesForIntervalArray
745 // Fills a nsTArray with the ranges overlapping the range specified by
746 // the given endpoints. Ranges in the selection exactly adjacent to the
747 // input range are not returned unless aAllowAdjacent is set.
749 // For example, if the following ranges were in the selection
750 // (assume everything is within the same node)
752 // Start Offset: 0 2 7 9
753 // End Offset: 2 5 9 10
755 // and passed aBeginOffset of 2 and aEndOffset of 9, then with
756 // aAllowAdjacent set, all the ranges should be returned. If
757 // aAllowAdjacent was false, the ranges [2, 5] and [7, 9] only
758 // should be returned
760 // Now that overlapping ranges are disallowed, there can be a maximum of
761 // 2 adjacent ranges
762 nsresult GetAbstractRangesForIntervalArray(nsINode* aBeginNode,
763 uint32_t aBeginOffset,
764 nsINode* aEndNode,
765 uint32_t aEndOffset,
766 bool aAllowAdjacent,
767 nsTArray<AbstractRange*>* aRanges);
770 * Converts the results of |GetAbstractRangesForIntervalArray()| to |nsRange|.
772 * |StaticRange|s can only occur in Selections of type |eHighlight|.
773 * Therefore, this method must not be called for this selection type
774 * as not every |AbstractRange| can be cast to |nsRange|.
776 nsresult GetDynamicRangesForIntervalArray(
777 nsINode* aBeginNode, uint32_t aBeginOffset, nsINode* aEndNode,
778 uint32_t aEndOffset, bool aAllowAdjacent, nsTArray<nsRange*>* aRanges);
781 * Modifies the cursor Bidi level after a change in keyboard direction
782 * @param langRTL is true if the new language is right-to-left or
783 * false if the new language is left-to-right.
785 nsresult SelectionLanguageChange(bool aLangRTL);
787 private:
788 bool HasSameRootOrSameComposedDoc(const nsINode& aNode);
790 // XXX Please don't add additional uses of this method, it's only for
791 // XXX supporting broken code (bug 1245883) in the following classes:
792 friend class ::nsCopySupport;
793 friend class ::nsHTMLCopyEncoder;
794 MOZ_CAN_RUN_SCRIPT
795 void AddRangeAndSelectFramesAndNotifyListenersInternal(nsRange& aRange,
796 Document* aDocument,
797 ErrorResult&);
799 // Get the cached value for nsTextFrame::GetPointFromOffset.
800 nsresult GetCachedFrameOffset(nsIFrame* aFrame, int32_t inOffset,
801 nsPoint& aPoint);
803 MOZ_CAN_RUN_SCRIPT
804 void SetStartAndEndInternal(InLimiter aInLimiter,
805 const RawRangeBoundary& aStartRef,
806 const RawRangeBoundary& aEndRef,
807 nsDirection aDirection, ErrorResult& aRv);
808 MOZ_CAN_RUN_SCRIPT
809 void SetBaseAndExtentInternal(InLimiter aInLimiter,
810 const RawRangeBoundary& aAnchorRef,
811 const RawRangeBoundary& aFocusRef,
812 ErrorResult& aRv);
814 public:
815 SelectionType GetType() const { return mSelectionType; }
817 SelectionCustomColors* GetCustomColors() const { return mCustomColors.get(); }
819 MOZ_CAN_RUN_SCRIPT void NotifySelectionListeners(bool aCalledByJS);
820 MOZ_CAN_RUN_SCRIPT void NotifySelectionListeners();
822 friend struct AutoUserInitiated;
823 struct MOZ_RAII AutoUserInitiated {
824 explicit AutoUserInitiated(Selection& aSelectionRef)
825 : AutoUserInitiated(&aSelectionRef) {}
826 explicit AutoUserInitiated(Selection* aSelection)
827 : mSavedValue(aSelection->mUserInitiated) {
828 aSelection->mUserInitiated = true;
830 AutoRestore<bool> mSavedValue;
833 private:
834 friend struct mozilla::AutoPrepareFocusRange;
835 class ScrollSelectionIntoViewEvent;
836 friend class ScrollSelectionIntoViewEvent;
838 class ScrollSelectionIntoViewEvent : public Runnable {
839 public:
840 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_DECL_NSIRUNNABLE
842 ScrollSelectionIntoViewEvent(Selection* aSelection, SelectionRegion aRegion,
843 ScrollAxis aVertical, ScrollAxis aHorizontal,
844 int32_t aFlags)
845 : Runnable("dom::Selection::ScrollSelectionIntoViewEvent"),
846 mSelection(aSelection),
847 mRegion(aRegion),
848 mVerticalScroll(aVertical),
849 mHorizontalScroll(aHorizontal),
850 mFlags(aFlags) {
851 NS_ASSERTION(aSelection, "null parameter");
853 void Revoke() { mSelection = nullptr; }
855 private:
856 Selection* mSelection;
857 SelectionRegion mRegion;
858 ScrollAxis mVerticalScroll;
859 ScrollAxis mHorizontalScroll;
860 int32_t mFlags;
864 * Set mAnchorFocusRange to mStyledRanges.mRanges[aIndex] if aIndex is a valid
865 * index.
867 void SetAnchorFocusRange(size_t aIndex);
868 void RemoveAnchorFocusRange() { mAnchorFocusRange = nullptr; }
869 void SelectFramesOf(nsIContent* aContent, bool aSelected) const;
872 * https://dom.spec.whatwg.org/#concept-tree-inclusive-descendant.
874 nsresult SelectFramesOfInclusiveDescendantsOfContent(
875 PostContentIterator& aPostOrderIter, nsIContent* aContent,
876 bool aSelected) const;
879 * https://dom.spec.whatwg.org/#concept-shadow-including-descendant
881 void SelectFramesOfShadowIncludingDescendantsOfContent(nsIContent* aContent,
882 bool aSelected) const;
884 nsresult SelectFrames(nsPresContext* aPresContext, AbstractRange& aRange,
885 bool aSelect) const;
888 * SelectFramesInAllRanges() calls SelectFrames() for all current
889 * ranges.
891 void SelectFramesInAllRanges(nsPresContext* aPresContext);
894 * @param aOutIndex If some, points to the index of the range in
895 * mStyledRanges.mRanges so that it's always in [0, mStyledRanges.Length()].
896 * Otherwise, if nothing, this didn't add the range to mStyledRanges.
898 MOZ_CAN_RUN_SCRIPT nsresult MaybeAddTableCellRange(nsRange& aRange,
899 Maybe<size_t>* aOutIndex);
901 Document* GetDocument() const;
903 MOZ_CAN_RUN_SCRIPT void RemoveAllRangesInternal(mozilla::ErrorResult& aRv);
905 void Disconnect();
907 struct StyledRanges {
908 explicit StyledRanges(Selection& aSelection) : mSelection(aSelection) {}
909 void Clear();
911 StyledRange* FindRangeData(AbstractRange* aRange);
913 using StyledRangeArray = AutoTArray<StyledRange, 1>;
915 StyledRangeArray::size_type Length() const;
917 nsresult RemoveCollapsedRanges();
919 nsresult RemoveRangeAndUnregisterSelection(AbstractRange& aRange);
922 * Binary searches the given sorted array of ranges for the insertion point
923 * for the given node/offset. The given comparator is used, and the index
924 * where the point should appear in the array is returned.
926 * If there is an item in the array equal to the input point (aPointNode,
927 * aPointOffset), we will return the index of this item.
929 * @return the index where the point should appear in the array. In
930 * [0, `aElementArray->Length()`].
932 static size_t FindInsertionPoint(
933 const nsTArray<StyledRange>* aElementArray, const nsINode& aPointNode,
934 uint32_t aPointOffset,
935 int32_t (*aComparator)(const nsINode&, uint32_t, const AbstractRange&));
938 * Works on the same principle as GetRangesForIntervalArray, however
939 * instead this returns the indices into mRanges between which
940 * the overlapping ranges lie.
942 * @param aStartIndex If some, aEndIndex will also be some and the value of
943 * aStartIndex will be less or equal than aEndIndex. If
944 * nothing, aEndIndex will also be nothing and it means
945 * that there is no range which in the range.
946 * @param aEndIndex If some, the value is less than mRanges.Length().
948 nsresult GetIndicesForInterval(const nsINode* aBeginNode,
949 uint32_t aBeginOffset,
950 const nsINode* aEndNode, uint32_t aEndOffset,
951 bool aAllowAdjacent,
952 Maybe<size_t>& aStartIndex,
953 Maybe<size_t>& aEndIndex);
955 bool HasEqualRangeBoundariesAt(const AbstractRange& aRange,
956 size_t aRangeIndex) const;
959 * Preserves the sorting and disjunctiveness of mRanges.
961 * @param aOutIndex If some, will point to the index of the added range, or
962 * if aRange is already contained, to the one containing
963 * it. Hence it'll always be in [0, mRanges.Length()).
964 * This is nothing only when the method returns an error.
966 MOZ_CAN_RUN_SCRIPT nsresult
967 MaybeAddRangeAndTruncateOverlaps(nsRange* aRange, Maybe<size_t>* aOutIndex);
970 * Adds the range even if there are overlaps.
972 MOZ_CAN_RUN_SCRIPT nsresult
973 AddRangeAndIgnoreOverlaps(AbstractRange* aRange);
976 * GetCommonEditingHost() returns common editing host of all
977 * ranges if there is. If at least one of the ranges is in non-editable
978 * element, returns nullptr. See following examples for the detail:
980 * <div id="a" contenteditable>
981 * an[cestor
982 * <div id="b" contenteditable="false">
983 * non-editable
984 * <div id="c" contenteditable>
985 * desc]endant
986 * in this case, this returns div#a because div#c is also in div#a.
988 * <div id="a" contenteditable>
989 * an[ce]stor
990 * <div id="b" contenteditable="false">
991 * non-editable
992 * <div id="c" contenteditable>
993 * de[sc]endant
994 * in this case, this returns div#a because second range is also in div#a
995 * and common ancestor of the range (i.e., div#c) is editable.
997 * <div id="a" contenteditable>
998 * an[ce]stor
999 * <div id="b" contenteditable="false">
1000 * [non]-editable
1001 * <div id="c" contenteditable>
1002 * de[sc]endant
1003 * in this case, this returns nullptr because the second range is in
1004 * non-editable area.
1006 Element* GetCommonEditingHost() const;
1008 MOZ_CAN_RUN_SCRIPT void MaybeFocusCommonEditingHost(
1009 PresShell* aPresShell) const;
1011 static nsresult SubtractRange(StyledRange& aRange, nsRange& aSubtract,
1012 nsTArray<StyledRange>* aOutput);
1014 void UnregisterSelection();
1016 // `mRanges` always needs to be sorted by the Range's start point.
1017 // Especially when dealing with `StaticRange`s this is not guaranteed
1018 // automatically. Therefore this method should be called before paint to
1019 // ensure that any potential DOM mutations are incorporated in `mRanges`
1020 // order. This method will also move invalid `StaticRange`s into
1021 // `mInvalidStaticRanges` (and previously-invalid-now-valid-again
1022 // `StaticRange`s back into `mRanges`).
1023 void ReorderRangesIfNecessary();
1025 // These are the ranges inside this selection. They are kept sorted in order
1026 // of DOM start position.
1028 // This data structure is sorted by the range beginnings. As the ranges are
1029 // disjoint, it is also implicitly sorted by the range endings. This allows
1030 // us to perform binary searches when searching for existence of a range,
1031 // giving us O(log n) search time.
1033 // Inserting a new range requires finding the overlapping interval,
1034 // requiring two binary searches plus up to an additional 6 DOM comparisons.
1035 // If this proves to be a performance concern, then an interval tree may be
1036 // a possible solution, allowing the calculation of the overlap interval in
1037 // O(log n) time, though this would require rebalancing and other overhead.
1038 StyledRangeArray mRanges;
1040 // With introduction of the custom highlight API, Selection must be able to
1041 // hold `StaticRange`s as well. If they become invalid (eg. end is before
1042 // start), they must be excluded from painting, but still kept.
1043 // mRanges needs to contain valid ranges sorted correctly only. Therefore,
1044 // invalid static ranges are being stored in this array, which is being kept
1045 // up to date in `ReorderRangesIfNecessary()`.
1046 StyledRangeArray mInvalidStaticRanges;
1048 Selection& mSelection;
1050 // The Document's generation for which `mRanges` have been ordered.
1051 int32_t mDocumentGeneration{0};
1052 // This flag indicates that ranges may have changed. It is set to true in
1053 // `Selection::NotifySelectionListeners().`
1054 bool mRangesMightHaveChanged{false};
1057 StyledRanges mStyledRanges{*this};
1059 RefPtr<nsRange> mAnchorFocusRange;
1060 RefPtr<nsFrameSelection> mFrameSelection;
1061 RefPtr<AccessibleCaretEventHub> mAccessibleCaretEventHub;
1062 RefPtr<SelectionChangeEventDispatcher> mSelectionChangeEventDispatcher;
1063 RefPtr<AutoScroller> mAutoScroller;
1064 nsTArray<nsCOMPtr<nsISelectionListener>> mSelectionListeners;
1065 nsRevocableEventPtr<ScrollSelectionIntoViewEvent> mScrollEvent;
1066 CachedOffsetForFrame* mCachedOffsetForFrame;
1067 nsDirection mDirection;
1068 const SelectionType mSelectionType;
1069 HighlightSelectionData mHighlightData;
1070 UniquePtr<SelectionCustomColors> mCustomColors;
1072 // Non-zero if we don't want any changes we make to the selection to be
1073 // visible to content. If non-zero, content won't be notified about changes.
1074 uint32_t mSelectionChangeBlockerCount;
1077 * True if the current selection operation was initiated by user action.
1078 * It determines whether we exclude -moz-user-select:none nodes or not,
1079 * as well as whether selectstart events will be fired.
1081 bool mUserInitiated;
1084 * When the selection change is caused by a call of Selection API,
1085 * mCalledByJS is true. Otherwise, false.
1087 bool mCalledByJS;
1090 * true if AutoCopyListner::OnSelectionChange() should be called.
1092 bool mNotifyAutoCopy;
1095 // Stack-class to turn on/off selection batching.
1096 class MOZ_STACK_CLASS SelectionBatcher final {
1097 private:
1098 const RefPtr<Selection> mSelection;
1099 const int16_t mReasons;
1100 const char* const mRequesterFuncName;
1102 public:
1104 * @param aRequesterFuncName function name which wants the selection batch.
1105 * This won't be stored nor exposed to selection listeners etc, used only for
1106 * logging. This MUST be living when the destructor runs.
1108 // TODO: Mark these constructors `MOZ_CAN_RUN_SCRIPT` because the destructor
1109 // may run script via nsISelectionListener.
1110 explicit SelectionBatcher(Selection& aSelectionRef,
1111 const char* aRequesterFuncName,
1112 int16_t aReasons = nsISelectionListener::NO_REASON)
1113 : SelectionBatcher(&aSelectionRef, aRequesterFuncName, aReasons) {}
1114 explicit SelectionBatcher(Selection* aSelection,
1115 const char* aRequesterFuncName,
1116 int16_t aReasons = nsISelectionListener::NO_REASON)
1117 : mSelection(aSelection),
1118 mReasons(aReasons),
1119 mRequesterFuncName(aRequesterFuncName) {
1120 if (mSelection) {
1121 mSelection->StartBatchChanges(mRequesterFuncName);
1125 ~SelectionBatcher() {
1126 if (mSelection) {
1127 mSelection->EndBatchChanges(mRequesterFuncName, mReasons);
1132 class MOZ_RAII AutoHideSelectionChanges final {
1133 public:
1134 explicit AutoHideSelectionChanges(const nsFrameSelection* aFrame);
1136 explicit AutoHideSelectionChanges(Selection& aSelectionRef)
1137 : AutoHideSelectionChanges(&aSelectionRef) {}
1139 ~AutoHideSelectionChanges() {
1140 if (mSelection) {
1141 mSelection->RemoveSelectionChangeBlocker();
1145 private:
1146 explicit AutoHideSelectionChanges(Selection* aSelection)
1147 : mSelection(aSelection) {
1148 if (mSelection) {
1149 mSelection->AddSelectionChangeBlocker();
1153 RefPtr<Selection> mSelection;
1156 } // namespace dom
1158 inline bool IsValidRawSelectionType(RawSelectionType aRawSelectionType) {
1159 return aRawSelectionType >= nsISelectionController::SELECTION_NONE &&
1160 aRawSelectionType <= nsISelectionController::SELECTION_URLSTRIKEOUT;
1163 inline SelectionType ToSelectionType(RawSelectionType aRawSelectionType) {
1164 if (!IsValidRawSelectionType(aRawSelectionType)) {
1165 return SelectionType::eInvalid;
1167 return static_cast<SelectionType>(aRawSelectionType);
1170 inline RawSelectionType ToRawSelectionType(SelectionType aSelectionType) {
1171 MOZ_ASSERT(aSelectionType != SelectionType::eInvalid);
1172 return static_cast<RawSelectionType>(aSelectionType);
1175 inline RawSelectionType ToRawSelectionType(TextRangeType aTextRangeType) {
1176 return ToRawSelectionType(ToSelectionType(aTextRangeType));
1179 inline SelectionTypeMask ToSelectionTypeMask(SelectionType aSelectionType) {
1180 MOZ_ASSERT(aSelectionType != SelectionType::eInvalid);
1181 return aSelectionType == SelectionType::eNone
1183 : static_cast<SelectionTypeMask>(
1184 1 << (static_cast<uint8_t>(aSelectionType) - 1));
1187 inline std::ostream& operator<<(
1188 std::ostream& aStream, const dom::Selection::InterlinePosition& aPosition) {
1189 using InterlinePosition = dom::Selection::InterlinePosition;
1190 switch (aPosition) {
1191 case InterlinePosition::EndOfLine:
1192 return aStream << "InterlinePosition::EndOfLine";
1193 case InterlinePosition::StartOfNextLine:
1194 return aStream << "InterlinePosition::StartOfNextLine";
1195 case InterlinePosition::Undefined:
1196 return aStream << "InterlinePosition::Undefined";
1197 default:
1198 MOZ_ASSERT_UNREACHABLE("Illegal value");
1199 return aStream << "<Illegal value>";
1203 } // namespace mozilla
1205 #endif // mozilla_Selection_h__