Selection.h (50306B)
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/. */ 6 7 #ifndef mozilla_Selection_h__ 8 #define mozilla_Selection_h__ 9 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 "mozilla/intl/BidiEmbeddingLevel.h" 20 #include "nsDirection.h" 21 #include "nsISelectionController.h" 22 #include "nsISelectionListener.h" 23 #include "nsRange.h" 24 #include "nsTArrayForwardDeclare.h" 25 #include "nsThreadUtils.h" 26 #include "nsWeakReference.h" 27 #include "nsWrapperCache.h" 28 29 struct CachedOffsetForFrame; 30 class AutoScroller; 31 class nsIFrame; 32 class nsFrameSelection; 33 class nsPIDOMWindowOuter; 34 struct SelectionDetails; 35 struct SelectionCustomColors; 36 class nsCopySupport; 37 class nsHTMLCopyEncoder; 38 class nsPresContext; 39 struct nsPoint; 40 struct nsRect; 41 42 namespace mozilla { 43 class AccessibleCaretEventHub; 44 class ErrorResult; 45 class HTMLEditor; 46 class PostContentIterator; 47 enum class CaretAssociationHint; 48 enum class TableSelectionMode : uint32_t; 49 struct AutoPrepareFocusRange; 50 struct PrimaryFrameData; 51 namespace dom { 52 class DocGroup; 53 class ShadowRootOrGetComposedRangesOptions; 54 } // namespace dom 55 } // namespace mozilla 56 57 namespace mozilla { 58 59 enum class SelectionScrollMode : uint8_t { 60 // Don't scroll synchronously. We'll flush when the scroll event fires so we 61 // make sure to scroll to the right place. 62 Async, 63 // Scroll synchronously, without flushing layout. 64 SyncNoFlush, 65 // Scroll synchronously, flushing layout. You MUST hold a strong ref on 66 // 'this' for the duration of this call. This might destroy arbitrary 67 // layout objects. 68 SyncFlush, 69 }; 70 71 namespace dom { 72 73 /** 74 * This cache allows to store all selected nodes during a reflow operation. 75 * 76 * All fully selected nodes are stored in a hash set per-selection instance. 77 * This allows fast paths in `nsINode::IsSelected()` and 78 * `Selection::LookupSelection()`. For partially selected nodes, the old 79 * mechanisms are used. This is okay, because for partially selected nodes 80 * no expensive node traversal is necessary. 81 * 82 * This cache is designed to be used in a context where no script is allowed 83 * to run. It assumes that the selection itself, or any range therein, does not 84 * change during its lifetime. 85 * 86 * By design, this class can only be instantiated in the `PresShell`. 87 */ 88 class MOZ_RAII SelectionNodeCache final { 89 public: 90 ~SelectionNodeCache(); 91 /** 92 * Returns true if `aNode` is fully selected by any of the given selections. 93 * 94 * This method will collect all fully selected nodes of `aSelections` and 95 * store them internally (therefore this method isn't const). 96 */ 97 bool MaybeCollectNodesAndCheckIfFullySelectedInAnyOf( 98 const nsINode* aNode, const nsTArray<Selection*>& aSelections); 99 100 /** 101 * Returns true if `aNode` is fully selected by any range in `aSelection`. 102 * 103 * This method collects all fully selected nodes from `aSelection` and store 104 * them internally. 105 */ 106 bool MaybeCollectNodesAndCheckIfFullySelected(const nsINode* aNode, 107 const Selection* aSelection) { 108 return MaybeCollect(aSelection).Contains(aNode); 109 } 110 111 private: 112 /** 113 * This class is supposed to be only created by the PresShell. 114 */ 115 friend PresShell; 116 explicit SelectionNodeCache(PresShell& aOwningPresShell); 117 /** 118 * Iterates all ranges in `aSelection` and collects its fully selected nodes 119 * into a hash set, which is also returned. 120 * 121 * If `aSelection` is already cached, the hash set is returned directly. 122 */ 123 const nsTHashSet<const nsINode*>& MaybeCollect(const Selection* aSelection); 124 125 nsTHashMap<const Selection*, nsTHashSet<const nsINode*>> mSelectedNodes; 126 127 PresShell& mOwningPresShell; 128 }; 129 130 // Note, the ownership of mozilla::dom::Selection depends on which way the 131 // object is created. When nsFrameSelection has created Selection, 132 // addreffing/releasing the Selection object is aggregated to nsFrameSelection. 133 // Otherwise normal addref/release is used. This ensures that nsFrameSelection 134 // is never deleted before its Selections. 135 class Selection final : public nsSupportsWeakReference, 136 public nsWrapperCache, 137 public SupportsWeakPtr { 138 using AllowRangeCrossShadowBoundary = 139 mozilla::dom::AllowRangeCrossShadowBoundary; 140 using IsUnlinking = AbstractRange::IsUnlinking; 141 142 protected: 143 virtual ~Selection(); 144 145 public: 146 /** 147 * @param aFrameSelection can be nullptr. 148 */ 149 explicit Selection(SelectionType aSelectionType, 150 nsFrameSelection* aFrameSelection); 151 152 NS_DECL_CYCLE_COLLECTING_ISUPPORTS 153 NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(Selection) 154 155 /** 156 * Match this up with EndbatchChanges. will stop ui updates while multiple 157 * selection methods are called 158 * 159 * @param aDetails string to explian why this is called. This won't be 160 * stored nor exposed to selection listeners etc. Just for logging. 161 */ 162 void StartBatchChanges(const char* aDetails); 163 164 /** 165 * Match this up with StartBatchChanges 166 * 167 * @param aDetails string to explian why this is called. This won't be 168 * stored nor exposed to selection listeners etc. Just for logging. 169 * @param aReasons potentially multiple of the reasons defined in 170 * nsISelectionListener.idl 171 */ 172 MOZ_CAN_RUN_SCRIPT void EndBatchChanges( 173 const char* aDetails, int16_t aReason = nsISelectionListener::NO_REASON); 174 175 /** 176 * NotifyAutoCopy() starts to notify AutoCopyListener of selection changes. 177 */ 178 void NotifyAutoCopy() { 179 MOZ_ASSERT(mSelectionType == SelectionType::eNormal); 180 181 mNotifyAutoCopy = true; 182 } 183 184 /** 185 * MaybeNotifyAccessibleCaretEventHub() starts to notify 186 * AccessibleCaretEventHub of selection change if aPresShell has it. 187 */ 188 void MaybeNotifyAccessibleCaretEventHub(PresShell* aPresShell); 189 190 /** 191 * StopNotifyingAccessibleCaretEventHub() stops notifying 192 * AccessibleCaretEventHub of selection change. 193 */ 194 void StopNotifyingAccessibleCaretEventHub(); 195 196 /** 197 * EnableSelectionChangeEvent() starts to notify 198 * SelectionChangeEventDispatcher of selection change to dispatch a 199 * selectionchange event at every selection change. 200 */ 201 void EnableSelectionChangeEvent() { 202 if (!mSelectionChangeEventDispatcher) { 203 mSelectionChangeEventDispatcher = new SelectionChangeEventDispatcher(); 204 } 205 } 206 207 // Required for WebIDL bindings, see 208 // https://developer.mozilla.org/en-US/docs/Mozilla/WebIDL_bindings#Adding_WebIDL_bindings_to_a_class. 209 Document* GetParentObject() const; 210 211 DocGroup* GetDocGroup() const; 212 213 // utility methods for scrolling the selection into view 214 nsPresContext* GetPresContext() const; 215 PresShell* GetPresShell() const; 216 nsFrameSelection* GetFrameSelection() const { return mFrameSelection; } 217 // Returns a rect containing the selection region, and frame that that 218 // position is relative to. For SELECTION_ANCHOR_REGION or 219 // SELECTION_FOCUS_REGION the rect is a zero-width rectangle. For 220 // SELECTION_WHOLE_SELECTION the rect contains both the anchor and focus 221 // region rects. 222 nsIFrame* GetSelectionAnchorGeometry(SelectionRegion aRegion, nsRect* aRect); 223 // Returns the position of the region (SELECTION_ANCHOR_REGION or 224 // SELECTION_FOCUS_REGION only), and frame that that position is relative to. 225 // The 'position' is a zero-width rectangle. 226 nsIFrame* GetSelectionEndPointGeometry(SelectionRegion aRegion, 227 nsRect* aRect); 228 229 nsresult PostScrollSelectionIntoViewEvent(SelectionRegion aRegion, 230 ScrollFlags aFlags, 231 ScrollAxis aVertical, 232 ScrollAxis aHorizontal); 233 234 MOZ_CAN_RUN_SCRIPT nsresult ScrollIntoView( 235 SelectionRegion, ScrollAxis aVertical = ScrollAxis(), 236 ScrollAxis aHorizontal = ScrollAxis(), ScrollFlags = ScrollFlags::None, 237 SelectionScrollMode = SelectionScrollMode::Async); 238 239 private: 240 static bool IsUserSelectionCollapsed( 241 const nsRange& aRange, nsTArray<RefPtr<nsRange>>& aTempRangesToAdd); 242 /** 243 * https://w3c.github.io/selection-api/#selectstart-event. 244 */ 245 enum class DispatchSelectstartEvent { 246 No, 247 Maybe, 248 }; 249 250 /** 251 * See `AddRangesForSelectableNodes`. 252 */ 253 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult AddRangesForUserSelectableNodes( 254 nsRange* aRange, Maybe<size_t>* aOutIndex, 255 const DispatchSelectstartEvent aDispatchSelectstartEvent); 256 257 /** 258 * Adds aRange to this Selection. If mUserInitiated is true, 259 * then aRange is first scanned for -moz-user-select:none nodes and split up 260 * into multiple ranges to exclude those before adding the resulting ranges 261 * to this Selection. 262 * 263 * @param aOutIndex points to the range last added, if at least one was added. 264 * If aRange is already contained, it points to the range 265 * containing it. Nothing() if mStyledRanges.mRanges was 266 * empty and no range was added. 267 */ 268 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult AddRangesForSelectableNodes( 269 nsRange* aRange, Maybe<size_t>* aOutIndex, 270 DispatchSelectstartEvent aDispatchSelectstartEvent); 271 272 already_AddRefed<StaticRange> GetComposedRange( 273 const AbstractRange* aRange, 274 const Sequence<OwningNonNull<ShadowRoot>>& aShadowRoots) const; 275 276 public: 277 nsresult RemoveCollapsedRanges(); 278 void Clear(nsPresContext* aPresContext, 279 IsUnlinking aIsUnlinking = IsUnlinking::No); 280 MOZ_CAN_RUN_SCRIPT nsresult CollapseInLimiter(nsINode* aContainer, 281 uint32_t aOffset) { 282 if (!aContainer) { 283 return NS_ERROR_INVALID_ARG; 284 } 285 return CollapseInLimiter(RawRangeBoundary(aContainer, aOffset)); 286 } 287 MOZ_CAN_RUN_SCRIPT nsresult 288 CollapseInLimiter(const RawRangeBoundary& aPoint) { 289 ErrorResult result; 290 CollapseInLimiter(aPoint, result); 291 return result.StealNSResult(); 292 } 293 MOZ_CAN_RUN_SCRIPT void CollapseInLimiter(const RawRangeBoundary& aPoint, 294 ErrorResult& aRv); 295 296 MOZ_CAN_RUN_SCRIPT nsresult Extend(nsINode* aContainer, uint32_t aOffset); 297 298 /** 299 * See mStyledRanges.mRanges. 300 */ 301 nsRange* GetRangeAt(uint32_t aIndex) const; 302 nsRange* GetFirstRange() const { return GetRangeAt(0); } 303 nsRange* GetLastRange() const { 304 return RangeCount() ? GetRangeAt(RangeCount() - 1u) : nullptr; 305 } 306 307 /** 308 * @brief Get the |AbstractRange| at |aIndex|. 309 * 310 * This method is safe to be called for every selection type. 311 * However, |StaticRange|s only occur for |SelectionType::eHighlight|. 312 * If the SelectionType may be eHighlight, this method must be called instead 313 * of |GetRangeAt()|. 314 * 315 * Returns null if |aIndex| is out of bounds. 316 */ 317 AbstractRange* GetAbstractRangeAt(uint32_t aIndex) const; 318 // Get the anchor-to-focus range if we don't care which end is 319 // anchor and which end is focus. 320 const nsRange* GetAnchorFocusRange() const { return mAnchorFocusRange; } 321 322 void GetDirection(nsAString& aDirection) const; 323 324 nsDirection GetDirection() const { return mDirection; } 325 326 void SetDirection(nsDirection aDir) { mDirection = aDir; } 327 MOZ_CAN_RUN_SCRIPT nsresult SetAnchorFocusToRange(nsRange* aRange); 328 329 MOZ_CAN_RUN_SCRIPT void ReplaceAnchorFocusRange(nsRange* aRange); 330 331 void AdjustAnchorFocusForMultiRange(nsDirection aDirection); 332 333 nsIFrame* GetPrimaryFrameForAnchorNode() const; 334 335 /** 336 * Get primary frame and some other data for putting caret or extending 337 * selection at the focus point. 338 */ 339 PrimaryFrameData GetPrimaryFrameForCaretAtFocusNode(bool aVisual) const; 340 341 UniquePtr<SelectionDetails> LookUpSelection( 342 nsIContent* aContent, uint32_t aContentOffset, uint32_t aContentLength, 343 UniquePtr<SelectionDetails> aDetailsHead, SelectionType aSelectionType); 344 345 NS_IMETHOD Repaint(nsPresContext* aPresContext); 346 347 MOZ_CAN_RUN_SCRIPT 348 nsresult StartAutoScrollTimer(nsIFrame* aFrame, const nsPoint& aPoint, 349 uint32_t aDelayInMs); 350 351 nsresult StopAutoScrollTimer(); 352 353 JSObject* WrapObject(JSContext* aCx, 354 JS::Handle<JSObject*> aGivenProto) override; 355 356 // WebIDL methods 357 nsINode* GetAnchorNode(CallerType aCallerType = CallerType::System) const { 358 const RangeBoundary& anchor = AnchorRef(); 359 nsINode* anchorNode = anchor.IsSet() ? anchor.GetContainer() : nullptr; 360 if (!anchorNode || aCallerType == CallerType::System || 361 !anchorNode->ChromeOnlyAccess()) { 362 return anchorNode; 363 } 364 // anchor is nsIContent as ChromeOnlyAccess is nsIContent-only 365 return anchorNode->AsContent()->FindFirstNonChromeOnlyAccessContent(); 366 } 367 uint32_t AnchorOffset(CallerType aCallerType = CallerType::System) const { 368 const RangeBoundary& anchor = AnchorRef(); 369 if (aCallerType != CallerType::System && anchor.IsSet() && 370 anchor.GetContainer()->ChromeOnlyAccess()) { 371 return 0; 372 } 373 const Maybe<uint32_t> offset = 374 anchor.Offset(RangeBoundary::OffsetFilter::kValidOffsets); 375 return offset ? *offset : 0; 376 } 377 nsINode* GetFocusNode(CallerType aCallerType = CallerType::System) const { 378 const RangeBoundary& focus = FocusRef(); 379 nsINode* focusNode = focus.IsSet() ? focus.GetContainer() : nullptr; 380 if (!focusNode || aCallerType == CallerType::System || 381 !focusNode->ChromeOnlyAccess()) { 382 return focusNode; 383 } 384 // focus is nsIContent as ChromeOnlyAccess is nsIContent-only 385 return focusNode->AsContent()->FindFirstNonChromeOnlyAccessContent(); 386 } 387 uint32_t FocusOffset(CallerType aCallerType = CallerType::System) const { 388 const RangeBoundary& focus = FocusRef(); 389 if (aCallerType != CallerType::System && focus.IsSet() && 390 focus.GetContainer()->ChromeOnlyAccess()) { 391 return 0; 392 } 393 const Maybe<uint32_t> offset = 394 focus.Offset(RangeBoundary::OffsetFilter::kValidOffsets); 395 return offset ? *offset : 0; 396 } 397 398 nsINode* GetMayCrossShadowBoundaryAnchorNode() const { 399 const RangeBoundary& anchor = AnchorRef(AllowRangeCrossShadowBoundary::Yes); 400 return anchor.IsSet() ? anchor.GetContainer() : nullptr; 401 } 402 403 uint32_t MayCrossShadowBoundaryAnchorOffset() const { 404 const RangeBoundary& anchor = AnchorRef(AllowRangeCrossShadowBoundary::Yes); 405 const Maybe<uint32_t> offset = 406 anchor.Offset(RangeBoundary::OffsetFilter::kValidOffsets); 407 return offset ? *offset : 0; 408 } 409 410 nsINode* GetMayCrossShadowBoundaryFocusNode() const { 411 const RangeBoundary& focus = FocusRef(AllowRangeCrossShadowBoundary::Yes); 412 return focus.IsSet() ? focus.GetContainer() : nullptr; 413 } 414 415 uint32_t MayCrossShadowBoundaryFocusOffset() const { 416 const RangeBoundary& focus = FocusRef(AllowRangeCrossShadowBoundary::Yes); 417 const Maybe<uint32_t> offset = 418 focus.Offset(RangeBoundary::OffsetFilter::kValidOffsets); 419 return offset ? *offset : 0; 420 } 421 422 nsIContent* GetChildAtAnchorOffset() { 423 const RangeBoundary& anchor = AnchorRef(); 424 return anchor.IsSet() ? anchor.GetChildAtOffset() : nullptr; 425 } 426 nsIContent* GetChildAtFocusOffset() { 427 const RangeBoundary& focus = FocusRef(); 428 return focus.IsSet() ? focus.GetChildAtOffset() : nullptr; 429 } 430 431 const RangeBoundary& AnchorRef( 432 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary = 433 AllowRangeCrossShadowBoundary::No) const; 434 const RangeBoundary& FocusRef( 435 AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary = 436 AllowRangeCrossShadowBoundary::No) const; 437 438 /* 439 * IsCollapsed -- is the whole selection just one point, or unset? 440 */ 441 bool IsCollapsed() const { 442 size_t cnt = mStyledRanges.Length(); 443 if (cnt == 0) { 444 return true; 445 } 446 447 if (cnt != 1) { 448 return false; 449 } 450 451 return mStyledRanges.mRanges[0].mRange->Collapsed(); 452 } 453 454 // Returns whether both normal range and cross-shadow-boundary 455 // range are collapsed. 456 // 457 // If StaticPrefs::dom_shadowdom_selection_across_boundary_enabled is 458 // disabled, this method always returns result as nsRange::IsCollapsed. 459 bool AreNormalAndCrossShadowBoundaryRangesCollapsed() const { 460 if (!IsCollapsed()) { 461 return false; 462 } 463 464 size_t cnt = mStyledRanges.Length(); 465 if (cnt == 0) { 466 return true; 467 } 468 469 AbstractRange* range = mStyledRanges.mRanges[0].mRange; 470 if (range->MayCrossShadowBoundary()) { 471 return range->AsDynamicRange()->CrossShadowBoundaryRangeCollapsed(); 472 } 473 474 return true; 475 } 476 477 // *JS() methods are mapped to Selection.*(). 478 // They may move focus only when the range represents normal selection. 479 // These methods shouldn't be used by non-JS callers. 480 MOZ_CAN_RUN_SCRIPT void CollapseJS(nsINode* aContainer, uint32_t aOffset, 481 mozilla::ErrorResult& aRv); 482 MOZ_CAN_RUN_SCRIPT void CollapseToStartJS(mozilla::ErrorResult& aRv); 483 MOZ_CAN_RUN_SCRIPT void CollapseToEndJS(mozilla::ErrorResult& aRv); 484 485 MOZ_CAN_RUN_SCRIPT void ExtendJS(nsINode& aContainer, uint32_t aOffset, 486 mozilla::ErrorResult& aRv); 487 488 MOZ_CAN_RUN_SCRIPT void SelectAllChildrenJS(nsINode& aNode, 489 mozilla::ErrorResult& aRv); 490 491 /** 492 * Deletes this selection from document the nodes belong to. 493 * Only if this has `SelectionType::eNormal`. 494 */ 495 MOZ_CAN_RUN_SCRIPT void DeleteFromDocument(mozilla::ErrorResult& aRv); 496 497 uint32_t RangeCount() const { return mStyledRanges.Length(); } 498 499 void GetType(nsAString& aOutType) const; 500 501 nsRange* GetRangeAt(uint32_t aIndex, mozilla::ErrorResult& aRv); 502 MOZ_CAN_RUN_SCRIPT void AddRangeJS(nsRange& aRange, 503 mozilla::ErrorResult& aRv); 504 505 /** 506 * Callers need to keep `aRange` alive. 507 */ 508 MOZ_CAN_RUN_SCRIPT void RemoveRangeAndUnselectFramesAndNotifyListeners( 509 AbstractRange& aRange, mozilla::ErrorResult& aRv); 510 511 MOZ_CAN_RUN_SCRIPT void RemoveAllRanges(mozilla::ErrorResult& aRv); 512 513 // https://www.w3.org/TR/selection-api/#ref-for-dom-selection-getcomposedranges-1 514 void GetComposedRanges( 515 const ShadowRootOrGetComposedRangesOptions& 516 aShadowRootOrGetComposedRangesOptions, 517 const Sequence<OwningNonNull<ShadowRoot>>& aShadowRoots, 518 nsTArray<RefPtr<StaticRange>>& aComposedRanges); 519 520 /** 521 * Whether Stringify should flush layout or not. 522 */ 523 enum class FlushFrames { No, Yes }; 524 MOZ_CAN_RUN_SCRIPT 525 void Stringify(nsAString& aResult, 526 CallerType aCallerType = CallerType::System, 527 FlushFrames = FlushFrames::Yes); 528 529 /** 530 * Indicates whether the node is part of the selection. If partlyContained 531 * is true, the function returns true when some part of the node 532 * is part of the selection. If partlyContained is false, the 533 * function only returns true when the entire node is part of the selection. 534 */ 535 bool ContainsNode(nsINode& aNode, bool aPartlyContained, 536 mozilla::ErrorResult& aRv); 537 538 /** 539 * Check to see if the given point is contained within the selection area. In 540 * particular, this iterates through all the rects that make up the selection, 541 * not just the bounding box, and checks to see if the given point is 542 * contained in any one of them. 543 * @param aPoint The point to check, relative to the root frame. 544 */ 545 bool ContainsPoint(const nsPoint& aPoint); 546 547 /** 548 * Modifies the selection. Note that the parameters are case-insensitive. 549 * 550 * @param alter can be one of { "move", "extend" } 551 * - "move" collapses the selection to the end of the selection and 552 * applies the movement direction/granularity to the collapsed 553 * selection. 554 * - "extend" leaves the start of the selection unchanged, and applies 555 * movement direction/granularity to the end of the selection. 556 * @param direction can be one of { "forward", "backward", "left", "right" } 557 * @param granularity can be one of { "character", "word", 558 * "line", "lineboundary" } 559 */ 560 MOZ_CAN_RUN_SCRIPT void Modify(const nsAString& aAlter, 561 const nsAString& aDirection, 562 const nsAString& aGranularity); 563 564 MOZ_CAN_RUN_SCRIPT 565 void SetBaseAndExtentJS(nsINode& aAnchorNode, uint32_t aAnchorOffset, 566 nsINode& aFocusNode, uint32_t aFocusOffset, 567 mozilla::ErrorResult& aRv); 568 569 bool GetInterlinePositionJS(mozilla::ErrorResult& aRv) const; 570 void SetInterlinePositionJS(bool aHintRight, mozilla::ErrorResult& aRv); 571 572 enum class InterlinePosition : uint8_t { 573 // Caret should be put at end of line (i.e., before the line break) 574 EndOfLine, 575 // Caret should be put at start of next line (i.e., after the line break) 576 StartOfNextLine, 577 // Undefined means only what is not EndOfLine nor StartOfNextLine. 578 // `SetInterlinePosition` should never be called with this value, and 579 // if `GetInterlinePosition` returns this, it means that the instance has 580 // not been initialized or cleared by the cycle collector or something. 581 // If a method needs to consider whether to call `SetInterlinePosition` or 582 // not call, this value can be used for the latter. 583 Undefined, 584 }; 585 InterlinePosition GetInterlinePosition() const; 586 nsresult SetInterlinePosition(InterlinePosition aInterlinePosition); 587 588 Nullable<int16_t> GetCaretBidiLevel(mozilla::ErrorResult& aRv) const; 589 void SetCaretBidiLevel(const Nullable<int16_t>& aCaretBidiLevel, 590 mozilla::ErrorResult& aRv); 591 592 void ToStringWithFormat(const nsAString& aFormatType, uint32_t aFlags, 593 int32_t aWrapColumn, nsAString& aReturn, 594 mozilla::ErrorResult& aRv); 595 void AddSelectionListener(nsISelectionListener* aListener); 596 void RemoveSelectionListener(nsISelectionListener* aListener); 597 598 RawSelectionType RawType() const { 599 return ToRawSelectionType(mSelectionType); 600 } 601 SelectionType Type() const { return mSelectionType; } 602 603 /** 604 * @brief Sets highlight selection properties. 605 * 606 * This includes the highlight name as well as its priority and type. 607 */ 608 void SetHighlightSelectionData( 609 dom::HighlightSelectionData aHighlightSelectionData); 610 611 const dom::HighlightSelectionData& HighlightSelectionData() const { 612 return mHighlightData; 613 } 614 615 /** 616 * See documentation of `GetRangesForInterval` in Selection.webidl. 617 * 618 * @param aReturn references, not copies, of the internal ranges. 619 */ 620 void GetRangesForInterval(nsINode& aBeginNode, uint32_t aBeginOffset, 621 nsINode& aEndNode, uint32_t aEndOffset, 622 bool aAllowAdjacent, 623 nsTArray<RefPtr<nsRange>>& aReturn, 624 ErrorResult& aRv); 625 626 void SetColors(const nsAString& aForeColor, const nsAString& aBackColor, 627 const nsAString& aAltForeColor, const nsAString& aAltBackColor, 628 ErrorResult& aRv); 629 630 void ResetColors(); 631 632 /** 633 * Non-JS callers should use the following 634 * collapse/collapseToStart/extend/etc methods, instead of the *JS 635 * versions that bindings call. 636 */ 637 638 /** 639 * Collapses the selection to a single point, at the specified offset 640 * in the given node. When the selection is collapsed, and the content 641 * is focused and editable, the caret will blink there. 642 * @param aContainer The given node where the selection will be set 643 * @param aOffset Where in given dom node to place the selection (the 644 * offset into the given node) 645 */ 646 MOZ_CAN_RUN_SCRIPT void CollapseInLimiter(nsINode& aContainer, 647 uint32_t aOffset, 648 ErrorResult& aRv) { 649 CollapseInLimiter(RawRangeBoundary(&aContainer, aOffset), aRv); 650 } 651 652 private: 653 enum class InLimiter { 654 // If eYes, the method may reset selection limiter and move focus if the 655 // given range is out of the limiter. 656 eYes, 657 // If eNo, the method won't reset selection limiter. So, if given range 658 // is out of bounds, the method may return error. 659 eNo, 660 }; 661 MOZ_CAN_RUN_SCRIPT 662 void CollapseInternal(InLimiter aInLimiter, const RawRangeBoundary& aPoint, 663 ErrorResult& aRv); 664 665 public: 666 /** 667 * Collapses the whole selection to a single point at the start 668 * of the current selection (irrespective of direction). If content 669 * is focused and editable, the caret will blink there. 670 */ 671 MOZ_CAN_RUN_SCRIPT void CollapseToStart(mozilla::ErrorResult& aRv); 672 673 /** 674 * Collapses the whole selection to a single point at the end 675 * of the current selection (irrespective of direction). If content 676 * is focused and editable, the caret will blink there. 677 */ 678 MOZ_CAN_RUN_SCRIPT void CollapseToEnd(mozilla::ErrorResult& aRv); 679 680 /** 681 * Extends the selection by moving the selection end to the specified node and 682 * offset, preserving the selection begin position. The new selection end 683 * result will always be from the anchorNode to the new focusNode, regardless 684 * of direction. 685 * 686 * @param aContainer The node where the selection will be extended to 687 * @param aOffset Where in aContainer to place the offset of the new 688 * selection end. 689 */ 690 MOZ_CAN_RUN_SCRIPT void Extend(nsINode& aContainer, uint32_t aOffset, 691 ErrorResult& aRv); 692 693 MOZ_CAN_RUN_SCRIPT void AddRangeAndSelectFramesAndNotifyListeners( 694 nsRange& aRange, mozilla::ErrorResult& aRv); 695 696 MOZ_CAN_RUN_SCRIPT void AddHighlightRangeAndSelectFramesAndNotifyListeners( 697 AbstractRange& aRange); 698 699 /** 700 * Adds all children of the specified node to the selection. 701 * @param aNode the parent of the children to be added to the selection. 702 */ 703 MOZ_CAN_RUN_SCRIPT void SelectAllChildren(nsINode& aNode, 704 mozilla::ErrorResult& aRv); 705 706 /** 707 * SetStartAndEnd() removes all ranges and sets new range as given range. 708 * Different from SetBaseAndExtent(), this won't compare the DOM points of 709 * aStartRef and aEndRef for performance nor set direction to eDirPrevious. 710 * Note that this may reset the limiter and move focus. If you don't want 711 * that, use SetStartAndEndInLimiter() instead. 712 */ 713 MOZ_CAN_RUN_SCRIPT 714 void SetStartAndEnd(const RawRangeBoundary& aStartRef, 715 const RawRangeBoundary& aEndRef, ErrorResult& aRv); 716 MOZ_CAN_RUN_SCRIPT 717 void SetStartAndEnd(nsINode& aStartContainer, uint32_t aStartOffset, 718 nsINode& aEndContainer, uint32_t aEndOffset, 719 ErrorResult& aRv) { 720 SetStartAndEnd(RawRangeBoundary(&aStartContainer, aStartOffset), 721 RawRangeBoundary(&aEndContainer, aEndOffset), aRv); 722 } 723 724 /** 725 * SetStartAndEndInLimiter() is similar to SetStartAndEnd(), but this respects 726 * the selection limiter. If all or part of given range is not in the 727 * limiter, this returns error. 728 */ 729 MOZ_CAN_RUN_SCRIPT 730 void SetStartAndEndInLimiter(const RawRangeBoundary& aStartRef, 731 const RawRangeBoundary& aEndRef, 732 ErrorResult& aRv); 733 MOZ_CAN_RUN_SCRIPT 734 void SetStartAndEndInLimiter(nsINode& aStartContainer, uint32_t aStartOffset, 735 nsINode& aEndContainer, uint32_t aEndOffset, 736 ErrorResult& aRv) { 737 SetStartAndEndInLimiter(RawRangeBoundary(&aStartContainer, aStartOffset), 738 RawRangeBoundary(&aEndContainer, aEndOffset), aRv); 739 } 740 MOZ_CAN_RUN_SCRIPT 741 Result<Ok, nsresult> SetStartAndEndInLimiter( 742 nsINode& aStartContainer, uint32_t aStartOffset, nsINode& aEndContainer, 743 uint32_t aEndOffset, nsDirection aDirection, int16_t aReason); 744 745 /** 746 * SetBaseAndExtent() is alternative of the JS API for internal use. 747 * Different from SetStartAndEnd(), this sets anchor and focus points as 748 * specified, then if anchor point is after focus node, this sets the 749 * direction to eDirPrevious. 750 * Note that this may reset the limiter and move focus. If you don't want 751 * that, use SetBaseAndExtentInLimier() instead. 752 */ 753 MOZ_CAN_RUN_SCRIPT 754 void SetBaseAndExtent(nsINode& aAnchorNode, uint32_t aAnchorOffset, 755 nsINode& aFocusNode, uint32_t aFocusOffset, 756 ErrorResult& aRv); 757 MOZ_CAN_RUN_SCRIPT 758 void SetBaseAndExtent(const RawRangeBoundary& aAnchorRef, 759 const RawRangeBoundary& aFocusRef, ErrorResult& aRv); 760 761 /** 762 * SetBaseAndExtentInLimiter() is similar to SetBaseAndExtent(), but this 763 * respects the selection limiter. If all or part of given range is not in 764 * the limiter, this returns error. 765 */ 766 MOZ_CAN_RUN_SCRIPT 767 void SetBaseAndExtentInLimiter(nsINode& aAnchorNode, uint32_t aAnchorOffset, 768 nsINode& aFocusNode, uint32_t aFocusOffset, 769 ErrorResult& aRv) { 770 SetBaseAndExtentInLimiter(RawRangeBoundary(&aAnchorNode, aAnchorOffset), 771 RawRangeBoundary(&aFocusNode, aFocusOffset), aRv); 772 } 773 MOZ_CAN_RUN_SCRIPT 774 void SetBaseAndExtentInLimiter(const RawRangeBoundary& aAnchorRef, 775 const RawRangeBoundary& aFocusRef, 776 ErrorResult& aRv); 777 778 void AddSelectionChangeBlocker(); 779 void RemoveSelectionChangeBlocker(); 780 bool IsBlockingSelectionChangeEvents() const; 781 782 // Whether this selection is focused in an editable element. 783 bool IsEditorSelection() const; 784 785 /** 786 * Set the painting style for the range. The range must be a range in 787 * the selection. The textRangeStyle will be used by text frame 788 * when it is painting the selection. 789 */ 790 nsresult SetTextRangeStyle(nsRange* aRange, 791 const TextRangeStyle& aTextRangeStyle); 792 793 // Methods to manipulate our mFrameSelection's ancestor limiter. 794 [[nodiscard]] Element* GetAncestorLimiter() const; 795 MOZ_CAN_RUN_SCRIPT void SetAncestorLimiter(Element* aLimiter); 796 797 /* 798 * Frame Offset cache can be used just during calling 799 * nsEditor::EndPlaceHolderTransaction. EndPlaceHolderTransaction will give 800 * rise to reflow/refreshing view/scroll, and call times of 801 * nsTextFrame::GetPointFromOffset whose return value is to be cached. see 802 * bugs 35296 and 199412 803 */ 804 void SetCanCacheFrameOffset(bool aCanCacheFrameOffset); 805 806 // Selection::GetAbstractRangesForIntervalArray 807 // 808 // Fills a nsTArray with the ranges overlapping the range specified by 809 // the given endpoints. Ranges in the selection exactly adjacent to the 810 // input range are not returned unless aAllowAdjacent is set. 811 // 812 // For example, if the following ranges were in the selection 813 // (assume everything is within the same node) 814 // 815 // Start Offset: 0 2 7 9 816 // End Offset: 2 5 9 10 817 // 818 // and passed aBeginOffset of 2 and aEndOffset of 9, then with 819 // aAllowAdjacent set, all the ranges should be returned. If 820 // aAllowAdjacent was false, the ranges [2, 5] and [7, 9] only 821 // should be returned 822 // 823 // Now that overlapping ranges are disallowed, there can be a maximum of 824 // 2 adjacent ranges 825 nsresult GetAbstractRangesForIntervalArray(nsINode* aBeginNode, 826 uint32_t aBeginOffset, 827 nsINode* aEndNode, 828 uint32_t aEndOffset, 829 bool aAllowAdjacent, 830 nsTArray<AbstractRange*>* aRanges); 831 832 /** 833 * Converts the results of |GetAbstractRangesForIntervalArray()| to |nsRange|. 834 * 835 * |StaticRange|s can only occur in Selections of type |eHighlight|. 836 * Therefore, this method must not be called for this selection type 837 * as not every |AbstractRange| can be cast to |nsRange|. 838 */ 839 nsresult GetDynamicRangesForIntervalArray( 840 nsINode* aBeginNode, uint32_t aBeginOffset, nsINode* aEndNode, 841 uint32_t aEndOffset, bool aAllowAdjacent, nsTArray<nsRange*>* aRanges); 842 843 /** 844 * Modifies the cursor Bidi level after a change in keyboard direction 845 * @param langRTL is true if the new language is right-to-left or 846 * false if the new language is left-to-right. 847 */ 848 nsresult SelectionLanguageChange(bool aLangRTL); 849 850 private: 851 bool HasSameRootOrSameComposedDoc(const nsINode& aNode); 852 853 // XXX Please don't add additional uses of this method, it's only for 854 // XXX supporting broken code (bug 1245883) in the following classes: 855 friend class ::nsCopySupport; 856 friend class ::nsHTMLCopyEncoder; 857 MOZ_CAN_RUN_SCRIPT 858 void AddRangeAndSelectFramesAndNotifyListenersInternal(nsRange& aRange, 859 Document* aDocument, 860 ErrorResult&); 861 862 // Get the cached value for nsTextFrame::GetPointFromOffset. 863 nsresult GetCachedFrameOffset(nsIFrame* aFrame, int32_t inOffset, 864 nsPoint& aPoint); 865 866 MOZ_CAN_RUN_SCRIPT 867 void SetStartAndEndInternal(InLimiter aInLimiter, 868 const RawRangeBoundary& aStartRef, 869 const RawRangeBoundary& aEndRef, 870 nsDirection aDirection, ErrorResult& aRv); 871 MOZ_CAN_RUN_SCRIPT 872 void SetBaseAndExtentInternal(InLimiter aInLimiter, 873 const RawRangeBoundary& aAnchorRef, 874 const RawRangeBoundary& aFocusRef, 875 ErrorResult& aRv); 876 877 public: 878 SelectionType GetType() const { return mSelectionType; } 879 880 SelectionCustomColors* GetCustomColors() const { return mCustomColors.get(); } 881 882 MOZ_CAN_RUN_SCRIPT void NotifySelectionListeners(bool aCalledByJS); 883 MOZ_CAN_RUN_SCRIPT void NotifySelectionListeners(); 884 885 bool ChangesDuringBatching() const { return mChangesDuringBatching; } 886 887 friend struct AutoUserInitiated; 888 struct MOZ_RAII AutoUserInitiated { 889 explicit AutoUserInitiated(Selection& aSelectionRef) 890 : AutoUserInitiated(&aSelectionRef) {} 891 explicit AutoUserInitiated(Selection* aSelection) 892 : mSavedValue(aSelection->mUserInitiated) { 893 aSelection->mUserInitiated = true; 894 } 895 AutoRestore<bool> mSavedValue; 896 }; 897 898 private: 899 friend struct mozilla::AutoPrepareFocusRange; 900 class ScrollSelectionIntoViewEvent; 901 friend class ScrollSelectionIntoViewEvent; 902 903 class ScrollSelectionIntoViewEvent : public Runnable { 904 public: 905 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_DECL_NSIRUNNABLE 906 907 ScrollSelectionIntoViewEvent(Selection* aSelection, SelectionRegion aRegion, 908 ScrollAxis aVertical, ScrollAxis aHorizontal, 909 ScrollFlags aFlags) 910 : Runnable("dom::Selection::ScrollSelectionIntoViewEvent"), 911 mSelection(aSelection), 912 mRegion(aRegion), 913 mVerticalScroll(aVertical), 914 mHorizontalScroll(aHorizontal), 915 mFlags(aFlags) { 916 NS_ASSERTION(aSelection, "null parameter"); 917 } 918 void Revoke() { mSelection = nullptr; } 919 920 private: 921 Selection* mSelection; 922 SelectionRegion mRegion; 923 ScrollAxis mVerticalScroll; 924 ScrollAxis mHorizontalScroll; 925 ScrollFlags mFlags; 926 }; 927 928 /** 929 * Set mAnchorFocusRange to mStyledRanges.mRanges[aIndex] if aIndex is a valid 930 * index. 931 */ 932 void SetAnchorFocusRange(size_t aIndex); 933 void RemoveAnchorFocusRange() { mAnchorFocusRange = nullptr; } 934 void SelectFramesOf(nsIContent* aContent, bool aSelected) const; 935 936 /** 937 * https://dom.spec.whatwg.org/#concept-tree-inclusive-descendant. 938 */ 939 nsresult SelectFramesOfInclusiveDescendantsOfContent( 940 PostContentIterator& aPostOrderIter, nsIContent* aContent, 941 bool aSelected) const; 942 943 void SelectFramesOfFlattenedTreeOfContent(nsIContent* aContent, 944 bool aSelected) const; 945 946 nsresult SelectFrames(nsPresContext* aPresContext, AbstractRange& aRange, 947 bool aSelect) const; 948 949 /** 950 * SelectFramesInAllRanges() calls SelectFrames() for all current 951 * ranges. 952 */ 953 void SelectFramesInAllRanges(nsPresContext* aPresContext); 954 955 /** 956 * @param aOutIndex If some, points to the index of the range in 957 * mStyledRanges.mRanges so that it's always in [0, mStyledRanges.Length()]. 958 * Otherwise, if nothing, this didn't add the range to mStyledRanges. 959 */ 960 MOZ_CAN_RUN_SCRIPT nsresult MaybeAddTableCellRange(nsRange& aRange, 961 Maybe<size_t>* aOutIndex); 962 963 Document* GetDocument() const; 964 965 MOZ_CAN_RUN_SCRIPT void RemoveAllRangesInternal( 966 mozilla::ErrorResult& aRv, IsUnlinking aIsUnlinking = IsUnlinking::No); 967 968 void Disconnect(); 969 970 struct StyledRanges { 971 explicit StyledRanges(Selection& aSelection) : mSelection(aSelection) {} 972 void Clear(); 973 974 StyledRange* FindRangeData(AbstractRange* aRange); 975 976 using StyledRangeArray = AutoTArray<StyledRange, 1>; 977 978 StyledRangeArray::size_type Length() const; 979 980 nsresult RemoveCollapsedRanges(); 981 982 nsresult RemoveRangeAndUnregisterSelection(AbstractRange& aRange); 983 984 /** 985 * Binary searches the given sorted array of ranges for the insertion point 986 * for the given aBoundary. The given comparator is used, and the index 987 * where the point should appear in the array is returned. 988 989 * If there is an item in the array equal to aBoundary, we will return the 990 index of this item. 991 * 992 * @return the index where the point should appear in the array. In 993 * [0, `aElementArray->Length()`]. 994 */ 995 template <typename PT, typename RT> 996 static size_t FindInsertionPoint( 997 const nsTArray<StyledRange>* aElementArray, 998 const RangeBoundaryBase<PT, RT>& aBoundary, 999 int32_t (*aComparator)(const RangeBoundaryBase<PT, RT>&, 1000 const AbstractRange&)); 1001 1002 /** 1003 * Works on the same principle as GetRangesForIntervalArray, however 1004 * instead this returns the indices into mRanges between which 1005 * the overlapping ranges lie. 1006 * 1007 * @param aStartIndex If some, aEndIndex will also be some and the value of 1008 * aStartIndex will be less or equal than aEndIndex. If 1009 * nothing, aEndIndex will also be nothing and it means 1010 * that there is no range which in the range. 1011 * @param aEndIndex If some, the value is less than mRanges.Length(). 1012 */ 1013 nsresult GetIndicesForInterval(const nsINode* aBeginNode, 1014 uint32_t aBeginOffset, 1015 const nsINode* aEndNode, uint32_t aEndOffset, 1016 bool aAllowAdjacent, 1017 Maybe<size_t>& aStartIndex, 1018 Maybe<size_t>& aEndIndex); 1019 1020 bool HasEqualRangeBoundariesAt(const AbstractRange& aRange, 1021 size_t aRangeIndex) const; 1022 1023 /** 1024 * Preserves the sorting and disjunctiveness of mRanges. 1025 * 1026 * @param aOutIndex If some, will point to the index of the added range, or 1027 * if aRange is already contained, to the one containing 1028 * it. Hence it'll always be in [0, mRanges.Length()). 1029 * This is nothing only when the method returns an error. 1030 */ 1031 MOZ_CAN_RUN_SCRIPT nsresult 1032 MaybeAddRangeAndTruncateOverlaps(nsRange* aRange, Maybe<size_t>* aOutIndex); 1033 1034 /** 1035 * Adds the range even if there are overlaps. 1036 */ 1037 MOZ_CAN_RUN_SCRIPT nsresult 1038 AddRangeAndIgnoreOverlaps(AbstractRange* aRange); 1039 1040 /** 1041 * GetCommonEditingHost() returns common editing host of all 1042 * ranges if there is. If at least one of the ranges is in non-editable 1043 * element, returns nullptr. See following examples for the detail: 1044 * 1045 * <div id="a" contenteditable> 1046 * an[cestor 1047 * <div id="b" contenteditable="false"> 1048 * non-editable 1049 * <div id="c" contenteditable> 1050 * desc]endant 1051 * in this case, this returns div#a because div#c is also in div#a. 1052 * 1053 * <div id="a" contenteditable> 1054 * an[ce]stor 1055 * <div id="b" contenteditable="false"> 1056 * non-editable 1057 * <div id="c" contenteditable> 1058 * de[sc]endant 1059 * in this case, this returns div#a because second range is also in div#a 1060 * and common ancestor of the range (i.e., div#c) is editable. 1061 * 1062 * <div id="a" contenteditable> 1063 * an[ce]stor 1064 * <div id="b" contenteditable="false"> 1065 * [non]-editable 1066 * <div id="c" contenteditable> 1067 * de[sc]endant 1068 * in this case, this returns nullptr because the second range is in 1069 * non-editable area. 1070 */ 1071 Element* GetCommonEditingHost() const; 1072 1073 MOZ_CAN_RUN_SCRIPT void MaybeFocusCommonEditingHost( 1074 PresShell* aPresShell) const; 1075 1076 static nsresult SubtractRange(StyledRange& aRange, nsRange& aSubtract, 1077 nsTArray<StyledRange>* aOutput); 1078 1079 void UnregisterSelection(IsUnlinking aIsUnlinking = IsUnlinking::No); 1080 1081 // `mRanges` always needs to be sorted by the Range's start point. 1082 // Especially when dealing with `StaticRange`s this is not guaranteed 1083 // automatically. Therefore this method should be called before paint to 1084 // ensure that any potential DOM mutations are incorporated in `mRanges` 1085 // order. This method will also move invalid `StaticRange`s into 1086 // `mInvalidStaticRanges` (and previously-invalid-now-valid-again 1087 // `StaticRange`s back into `mRanges`). 1088 void ReorderRangesIfNecessary(); 1089 1090 // These are the ranges inside this selection. They are kept sorted in order 1091 // of DOM start position. 1092 // 1093 // This data structure is sorted by the range beginnings. As the ranges are 1094 // disjoint, it is also implicitly sorted by the range endings. This allows 1095 // us to perform binary searches when searching for existence of a range, 1096 // giving us O(log n) search time. 1097 // 1098 // Inserting a new range requires finding the overlapping interval, 1099 // requiring two binary searches plus up to an additional 6 DOM comparisons. 1100 // If this proves to be a performance concern, then an interval tree may be 1101 // a possible solution, allowing the calculation of the overlap interval in 1102 // O(log n) time, though this would require rebalancing and other overhead. 1103 StyledRangeArray mRanges; 1104 1105 // With introduction of the custom highlight API, Selection must be able to 1106 // hold `StaticRange`s as well. If they become invalid (eg. end is before 1107 // start), they must be excluded from painting, but still kept. 1108 // mRanges needs to contain valid ranges sorted correctly only. Therefore, 1109 // invalid static ranges are being stored in this array, which is being kept 1110 // up to date in `ReorderRangesIfNecessary()`. 1111 StyledRangeArray mInvalidStaticRanges; 1112 1113 Selection& mSelection; 1114 1115 // The Document's generation for which `mRanges` have been ordered. 1116 int32_t mDocumentGeneration{0}; 1117 // This flag indicates that ranges may have changed. It is set to true in 1118 // `Selection::NotifySelectionListeners().` 1119 bool mRangesMightHaveChanged{false}; 1120 }; 1121 1122 StyledRanges mStyledRanges{*this}; 1123 1124 RefPtr<nsRange> mAnchorFocusRange; 1125 RefPtr<nsFrameSelection> mFrameSelection; 1126 RefPtr<AccessibleCaretEventHub> mAccessibleCaretEventHub; 1127 RefPtr<SelectionChangeEventDispatcher> mSelectionChangeEventDispatcher; 1128 RefPtr<AutoScroller> mAutoScroller; 1129 nsTArray<nsCOMPtr<nsISelectionListener>> mSelectionListeners; 1130 nsRevocableEventPtr<ScrollSelectionIntoViewEvent> mScrollEvent; 1131 CachedOffsetForFrame* mCachedOffsetForFrame; 1132 nsDirection mDirection; 1133 const SelectionType mSelectionType; 1134 dom::HighlightSelectionData mHighlightData; 1135 UniquePtr<SelectionCustomColors> mCustomColors; 1136 1137 // Non-zero if we don't want any changes we make to the selection to be 1138 // visible to content. If non-zero, content won't be notified about changes. 1139 uint32_t mSelectionChangeBlockerCount; 1140 1141 /** 1142 * True if the current selection operation was initiated by user action. 1143 * It determines whether we exclude -moz-user-select:none nodes or not, 1144 * as well as whether selectstart events will be fired. 1145 */ 1146 bool mUserInitiated; 1147 1148 /** 1149 * When the selection change is caused by a call of Selection API, 1150 * mCalledByJS is true. Otherwise, false. 1151 */ 1152 bool mCalledByJS; 1153 1154 /** 1155 * true if AutoCopyListner::OnSelectionChange() should be called. 1156 */ 1157 bool mNotifyAutoCopy; 1158 1159 /** 1160 * Indicates that this selection has changed during a batch change and 1161 * `NotifySelectionListener()` should be called after batching ends. 1162 * 1163 * See `nsFrameSelection::StartBatchChanges()` and `::EndBatchChanges()`. 1164 * 1165 * This flag is set and reset in `NotifySelectionListener()`. 1166 */ 1167 bool mChangesDuringBatching = false; 1168 }; 1169 1170 // Stack-class to turn on/off selection batching. 1171 class MOZ_STACK_CLASS SelectionBatcher final { 1172 private: 1173 const RefPtr<Selection> mSelection; 1174 const int16_t mReasons; 1175 const char* const mRequesterFuncName; 1176 1177 public: 1178 /** 1179 * @param aRequesterFuncName function name which wants the selection batch. 1180 * This won't be stored nor exposed to selection listeners etc, used only for 1181 * logging. This MUST be living when the destructor runs. 1182 */ 1183 MOZ_CAN_RUN_SCRIPT explicit SelectionBatcher( 1184 Selection& aSelectionRef, const char* aRequesterFuncName, 1185 int16_t aReasons = nsISelectionListener::NO_REASON) 1186 : SelectionBatcher(&aSelectionRef, aRequesterFuncName, aReasons) {} 1187 MOZ_CAN_RUN_SCRIPT explicit SelectionBatcher( 1188 Selection* aSelection, const char* aRequesterFuncName, 1189 int16_t aReasons = nsISelectionListener::NO_REASON) 1190 : mSelection(aSelection), 1191 mReasons(aReasons), 1192 mRequesterFuncName(aRequesterFuncName) { 1193 if (mSelection) { 1194 mSelection->StartBatchChanges(mRequesterFuncName); 1195 } 1196 } 1197 1198 MOZ_CAN_RUN_SCRIPT ~SelectionBatcher() { 1199 if (mSelection) { 1200 MOZ_KnownLive(mSelection)->EndBatchChanges(mRequesterFuncName, mReasons); 1201 } 1202 } 1203 }; 1204 1205 class MOZ_RAII AutoHideSelectionChanges final { 1206 public: 1207 explicit AutoHideSelectionChanges(const nsFrameSelection* aFrame); 1208 1209 explicit AutoHideSelectionChanges(Selection& aSelectionRef) 1210 : AutoHideSelectionChanges(&aSelectionRef) {} 1211 1212 ~AutoHideSelectionChanges() { 1213 if (mSelection) { 1214 mSelection->RemoveSelectionChangeBlocker(); 1215 } 1216 } 1217 1218 private: 1219 explicit AutoHideSelectionChanges(Selection* aSelection) 1220 : mSelection(aSelection) { 1221 if (mSelection) { 1222 mSelection->AddSelectionChangeBlocker(); 1223 } 1224 } 1225 1226 RefPtr<Selection> mSelection; 1227 }; 1228 1229 } // namespace dom 1230 1231 constexpr bool IsValidRawSelectionType(RawSelectionType aRawSelectionType) { 1232 return aRawSelectionType >= nsISelectionController::SELECTION_NONE && 1233 aRawSelectionType <= nsISelectionController::SELECTION_TARGET_TEXT; 1234 } 1235 1236 constexpr SelectionType ToSelectionType(RawSelectionType aRawSelectionType) { 1237 if (!IsValidRawSelectionType(aRawSelectionType)) { 1238 return SelectionType::eInvalid; 1239 } 1240 return static_cast<SelectionType>(aRawSelectionType); 1241 } 1242 1243 constexpr RawSelectionType ToRawSelectionType(SelectionType aSelectionType) { 1244 MOZ_ASSERT(aSelectionType != SelectionType::eInvalid); 1245 return static_cast<RawSelectionType>(aSelectionType); 1246 } 1247 1248 constexpr RawSelectionType ToRawSelectionType(TextRangeType aTextRangeType) { 1249 return ToRawSelectionType(ToSelectionType(aTextRangeType)); 1250 } 1251 1252 constexpr SelectionTypeMask ToSelectionTypeMask(SelectionType aSelectionType) { 1253 MOZ_ASSERT(aSelectionType != SelectionType::eInvalid); 1254 return aSelectionType == SelectionType::eNone 1255 ? 0 1256 : static_cast<SelectionTypeMask>( 1257 1 << (static_cast<uint8_t>(aSelectionType) - 1)); 1258 } 1259 1260 inline std::ostream& operator<<( 1261 std::ostream& aStream, const dom::Selection::InterlinePosition& aPosition) { 1262 using InterlinePosition = dom::Selection::InterlinePosition; 1263 switch (aPosition) { 1264 case InterlinePosition::EndOfLine: 1265 return aStream << "InterlinePosition::EndOfLine"; 1266 case InterlinePosition::StartOfNextLine: 1267 return aStream << "InterlinePosition::StartOfNextLine"; 1268 case InterlinePosition::Undefined: 1269 return aStream << "InterlinePosition::Undefined"; 1270 default: 1271 MOZ_ASSERT_UNREACHABLE("Illegal value"); 1272 return aStream << "<Illegal value>"; 1273 } 1274 } 1275 1276 } // namespace mozilla 1277 1278 inline nsresult nsISelectionController::ScrollSelectionIntoView( 1279 mozilla::SelectionType aType, SelectionRegion aRegion, 1280 const mozilla::ScrollAxis& aVertical = mozilla::ScrollAxis(), 1281 const mozilla::ScrollAxis& aHorizontal = mozilla::ScrollAxis(), 1282 mozilla::ScrollFlags aScrollFlags = mozilla::ScrollFlags::None, 1283 mozilla::SelectionScrollMode aMode = mozilla::SelectionScrollMode::Async) { 1284 RefPtr selection = GetSelection(mozilla::RawSelectionType(aType)); 1285 if (!selection) { 1286 return NS_ERROR_FAILURE; 1287 } 1288 return selection->ScrollIntoView(aRegion, aVertical, aHorizontal, 1289 aScrollFlags, aMode); 1290 } 1291 1292 inline nsresult nsISelectionController::ScrollSelectionIntoView( 1293 mozilla::SelectionType aType, SelectionRegion aRegion, 1294 mozilla::SelectionScrollMode aMode) { 1295 return ScrollSelectionIntoView(aType, aRegion, mozilla::ScrollAxis(), 1296 mozilla::ScrollAxis(), 1297 mozilla::ScrollFlags::None, aMode); 1298 } 1299 1300 #endif // mozilla_Selection_h__