AutoClonedRangeArray.h (24884B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #ifndef AutoClonedRangeArray_h 7 #define AutoClonedRangeArray_h 8 9 #include "EditAction.h" // for EditSubAction 10 #include "EditorBase.h" // for EditorBase 11 #include "EditorDOMPoint.h" // for EditorDOMPoint, EditorDOMRange, etc 12 #include "EditorForwards.h" 13 #include "HTMLEditHelpers.h" // for BlockInlineCheck 14 #include "HTMLEditor.h" // for HTMLEditor 15 #include "SelectionState.h" // for SelectionState 16 17 #include "mozilla/ErrorResult.h" // for ErrorResult 18 #include "mozilla/IntegerRange.h" // for IntegerRange 19 #include "mozilla/Maybe.h" // for Maybe 20 #include "mozilla/RangeBoundary.h" // for RangeBoundary 21 #include "mozilla/Result.h" // for Result<> 22 #include "mozilla/dom/Element.h" // for dom::Element 23 #include "mozilla/dom/HTMLBRElement.h" // for dom::HTMLBRElement 24 #include "mozilla/dom/Selection.h" // for dom::Selection 25 #include "mozilla/dom/Text.h" // for dom::Text 26 #include "mozilla/intl/BidiEmbeddingLevel.h" // for BidiEmbeddingLevel 27 28 #include "nsDebug.h" // for NS_WARNING, etc 29 #include "nsDirection.h" // for nsDirection 30 #include "nsError.h" // for NS_SUCCESS_* and NS_ERROR_* 31 #include "nsFrameSelection.h" // for nsFrameSelection 32 #include "nsRange.h" // for nsRange 33 34 namespace mozilla { 35 36 enum class CaretAssociationHint; 37 38 /****************************************************************************** 39 * AutoClonedRangeArray stores closed ranges and has similar API to Selection. 40 *****************************************************************************/ 41 42 class MOZ_STACK_CLASS AutoClonedRangeArray { 43 public: 44 template <typename PointType> 45 explicit AutoClonedRangeArray(const EditorDOMRangeBase<PointType>& aRange); 46 template <typename PT, typename CT> 47 explicit AutoClonedRangeArray(const EditorDOMPointBase<PT, CT>& aPoint); 48 explicit AutoClonedRangeArray(const nsRange& aRange); 49 // The copy constructor copies everything except saved ranges. 50 explicit AutoClonedRangeArray(const AutoClonedRangeArray& aOther); 51 52 virtual ~AutoClonedRangeArray() = default; 53 54 /** 55 * Check whether all ranges in content nodes or not. If the ranges is empty, 56 * this returns false. 57 */ 58 [[nodiscard]] bool IsInContent() const { 59 if (mRanges.IsEmpty()) { 60 return false; 61 } 62 for (const OwningNonNull<nsRange>& range : mRanges) { 63 if (MOZ_UNLIKELY(!range->IsPositioned() || !range->GetStartContainer() || 64 !range->GetStartContainer()->IsContent() || 65 !range->GetEndContainer() || 66 !range->GetEndContainer()->IsContent())) { 67 return false; 68 } 69 } 70 return true; 71 } 72 73 /** 74 * EnsureOnlyEditableRanges() removes ranges which cannot modify. 75 * Note that this is designed only for `HTMLEditor` because this must not 76 * be required by `TextEditor`. 77 */ 78 void EnsureOnlyEditableRanges(const dom::Element& aEditingHost); 79 80 enum class RangeInReplacedOrVoidElement : bool { 81 // Each range in a replaced or a void element should be collapsed before the 82 // element. 83 Collapse, 84 // Each range in a replaced or a void element should be deleted. 85 Delete, 86 }; 87 88 /** 89 * Adjust ranges if each boundary is in a replaced element or a void element. 90 * If the adjusted range is not at proper position to edit, this will remove 91 * the range. 92 * 93 * @return true if some ranges are modified. 94 */ 95 bool AdjustRangesNotInReplacedNorVoidElements( 96 RangeInReplacedOrVoidElement aRangeInReplacedOrVoidElement, 97 const dom::Element& aEditingHost); 98 99 /** 100 * EnsureRangesInTextNode() is designed for TextEditor to guarantee that 101 * all ranges are in its text node which is first child of the anonymous <div> 102 * element and is first child. 103 */ 104 void EnsureRangesInTextNode(const dom::Text& aTextNode); 105 106 /** 107 * Extend ranges to make each range select starting from a line start edge and 108 * ending after a line end edge to handle per line edit sub-actions. 109 */ 110 void ExtendRangesToWrapLines(EditSubAction aEditSubAction, 111 BlockInlineCheck aBlockInlineCheck, 112 const dom::Element& aAncestorLimiter); 113 114 /** 115 * Check whether the range is in aEditingHost and both containers of start and 116 * end boundaries of the range are editable. 117 */ 118 [[nodiscard]] static bool IsEditableRange(const dom::AbstractRange& aRange, 119 const dom::Element& aEditingHost); 120 121 /** 122 * Check whether the first range is in aEditingHost and both containers of 123 * start and end boundaries of the first range are editable. 124 */ 125 [[nodiscard]] bool IsFirstRangeEditable( 126 const dom::Element& aEditingHost) const { 127 return IsEditableRange(FirstRangeRef(), aEditingHost); 128 } 129 130 /** 131 * IsAtLeastOneContainerOfRangeBoundariesInclusiveDescendantOf() returns true 132 * if at least one of the containers of the range boundaries is an inclusive 133 * descendant of aContent. 134 */ 135 [[nodiscard]] bool 136 IsAtLeastOneContainerOfRangeBoundariesInclusiveDescendantOf( 137 const nsIContent& aContent) const { 138 for (const OwningNonNull<nsRange>& range : mRanges) { 139 nsINode* startContainer = range->GetStartContainer(); 140 if (startContainer && 141 startContainer->IsInclusiveDescendantOf(&aContent)) { 142 return true; 143 } 144 nsINode* endContainer = range->GetEndContainer(); 145 if (startContainer == endContainer) { 146 continue; 147 } 148 if (endContainer && endContainer->IsInclusiveDescendantOf(&aContent)) { 149 return true; 150 } 151 } 152 return false; 153 } 154 155 [[nodiscard]] auto& Ranges() { return mRanges; } 156 [[nodiscard]] const auto& Ranges() const { return mRanges; } 157 [[nodiscard]] OwningNonNull<nsRange>& FirstRangeRef() { return mRanges[0]; } 158 [[nodiscard]] const OwningNonNull<nsRange>& FirstRangeRef() const { 159 return mRanges[0]; 160 } 161 162 template <template <typename> typename StrongPtrType> 163 [[nodiscard]] AutoTArray<StrongPtrType<nsRange>, 8> CloneRanges() const { 164 AutoTArray<StrongPtrType<nsRange>, 8> ranges; 165 for (const auto& range : mRanges) { 166 ranges.AppendElement(range->CloneRange()); 167 } 168 return ranges; 169 } 170 171 template <typename EditorDOMPointType> 172 [[nodiscard]] EditorDOMPointType GetFirstRangeStartPoint() const { 173 if (mRanges.IsEmpty() || !mRanges[0]->IsPositioned()) { 174 return EditorDOMPointType(); 175 } 176 return EditorDOMPointType(mRanges[0]->StartRef()); 177 } 178 template <typename EditorDOMPointType> 179 [[nodiscard]] EditorDOMPointType GetFirstRangeEndPoint() const { 180 if (mRanges.IsEmpty() || !mRanges[0]->IsPositioned()) { 181 return EditorDOMPointType(); 182 } 183 return EditorDOMPointType(mRanges[0]->EndRef()); 184 } 185 186 nsresult SelectNode(nsINode& aNode) { 187 mRanges.Clear(); 188 if (!mAnchorFocusRange) { 189 mAnchorFocusRange = nsRange::Create(&aNode); 190 if (!mAnchorFocusRange) { 191 return NS_ERROR_FAILURE; 192 } 193 } 194 ErrorResult error; 195 mAnchorFocusRange->SelectNode(aNode, error); 196 if (error.Failed()) { 197 mAnchorFocusRange = nullptr; 198 return error.StealNSResult(); 199 } 200 mRanges.AppendElement(*mAnchorFocusRange); 201 return NS_OK; 202 } 203 204 /** 205 * For compatiblity with the other browsers, we should shrink ranges to 206 * start from an atomic content and/or end after one instead of start 207 * from end of a preceding text node and end by start of a follwing text 208 * node. Returns true if this modifies a range. 209 */ 210 enum class IfSelectingOnlyOneAtomicContent { 211 Collapse, // Collapse to the range selecting only one atomic content to 212 // start or after of it. Whether to collapse start or after 213 // it depends on aDirectionAndAmount. This is ignored if 214 // there are multiple ranges. 215 KeepSelecting, // Won't collapse the range. 216 }; 217 Result<bool, nsresult> ShrinkRangesIfStartFromOrEndAfterAtomicContent( 218 const HTMLEditor& aHTMLEditor, nsIEditor::EDirection aDirectionAndAmount, 219 IfSelectingOnlyOneAtomicContent aIfSelectingOnlyOneAtomicContent); 220 221 /** 222 * The following methods are same as `Selection`'s methods. 223 */ 224 [[nodiscard]] bool IsCollapsed() const { 225 return mRanges.IsEmpty() || 226 (mRanges.Length() == 1 && mRanges[0]->Collapsed()); 227 } 228 template <typename PT, typename CT> 229 nsresult Collapse(const EditorDOMPointBase<PT, CT>& aPoint) { 230 mRanges.Clear(); 231 if (!mAnchorFocusRange) { 232 ErrorResult error; 233 mAnchorFocusRange = nsRange::Create(aPoint.ToRawRangeBoundary(), 234 aPoint.ToRawRangeBoundary(), error); 235 if (error.Failed()) { 236 mAnchorFocusRange = nullptr; 237 return error.StealNSResult(); 238 } 239 } else { 240 nsresult rv = mAnchorFocusRange->CollapseTo(aPoint.ToRawRangeBoundary()); 241 if (NS_FAILED(rv)) { 242 mAnchorFocusRange = nullptr; 243 return rv; 244 } 245 } 246 mRanges.AppendElement(*mAnchorFocusRange); 247 SetNewCaretAssociationHint(aPoint.ToRawRangeBoundary(), 248 aPoint.GetInterlinePosition()); 249 return NS_OK; 250 } 251 template <typename SPT, typename SCT, typename EPT, typename ECT> 252 nsresult SetStartAndEnd(const EditorDOMPointBase<SPT, SCT>& aStart, 253 const EditorDOMPointBase<EPT, ECT>& aEnd) { 254 mRanges.Clear(); 255 if (!mAnchorFocusRange) { 256 ErrorResult error; 257 mAnchorFocusRange = nsRange::Create(aStart.ToRawRangeBoundary(), 258 aEnd.ToRawRangeBoundary(), error); 259 if (error.Failed()) { 260 mAnchorFocusRange = nullptr; 261 return error.StealNSResult(); 262 } 263 } else { 264 nsresult rv = mAnchorFocusRange->SetStartAndEnd( 265 aStart.ToRawRangeBoundary(), aEnd.ToRawRangeBoundary()); 266 if (NS_FAILED(rv)) { 267 mAnchorFocusRange = nullptr; 268 return rv; 269 } 270 } 271 mRanges.AppendElement(*mAnchorFocusRange); 272 return NS_OK; 273 } 274 template <typename SPT, typename SCT, typename EPT, typename ECT> 275 nsresult SetBaseAndExtent(const EditorDOMPointBase<SPT, SCT>& aAnchor, 276 const EditorDOMPointBase<EPT, ECT>& aFocus) { 277 if (MOZ_UNLIKELY(!aAnchor.IsSet()) || MOZ_UNLIKELY(!aFocus.IsSet())) { 278 mRanges.Clear(); 279 mAnchorFocusRange = nullptr; 280 return NS_ERROR_INVALID_ARG; 281 } 282 return aAnchor.EqualsOrIsBefore(aFocus) ? SetStartAndEnd(aAnchor, aFocus) 283 : SetStartAndEnd(aFocus, aAnchor); 284 } 285 [[nodiscard]] const nsRange* GetAnchorFocusRange() const { 286 return mAnchorFocusRange; 287 } 288 [[nodiscard]] nsDirection GetDirection() const { return mDirection; } 289 290 void SetDirection(nsDirection aDirection) { mDirection = aDirection; } 291 292 [[nodiscard]] const RangeBoundary& AnchorRef() const { 293 if (!mAnchorFocusRange) { 294 static RangeBoundary sEmptyRangeBoundary; 295 return sEmptyRangeBoundary; 296 } 297 return mDirection == nsDirection::eDirNext ? mAnchorFocusRange->StartRef() 298 : mAnchorFocusRange->EndRef(); 299 } 300 [[nodiscard]] nsINode* GetAnchorNode() const { 301 return AnchorRef().IsSet() ? AnchorRef().GetContainer() : nullptr; 302 } 303 [[nodiscard]] uint32_t GetAnchorOffset() const { 304 return AnchorRef().IsSet() 305 ? AnchorRef() 306 .Offset(RangeBoundary::OffsetFilter::kValidOffsets) 307 .valueOr(0) 308 : 0; 309 } 310 [[nodiscard]] nsIContent* GetChildAtAnchorOffset() const { 311 return AnchorRef().IsSet() ? AnchorRef().GetChildAtOffset() : nullptr; 312 } 313 314 [[nodiscard]] const RangeBoundary& FocusRef() const { 315 if (!mAnchorFocusRange) { 316 static RangeBoundary sEmptyRangeBoundary; 317 return sEmptyRangeBoundary; 318 } 319 return mDirection == nsDirection::eDirNext ? mAnchorFocusRange->EndRef() 320 : mAnchorFocusRange->StartRef(); 321 } 322 [[nodiscard]] nsINode* GetFocusNode() const { 323 return FocusRef().IsSet() ? FocusRef().GetContainer() : nullptr; 324 } 325 [[nodiscard]] uint32_t FocusOffset() const { 326 return FocusRef().IsSet() 327 ? FocusRef() 328 .Offset(RangeBoundary::OffsetFilter::kValidOffsets) 329 .valueOr(0) 330 : 0; 331 } 332 [[nodiscard]] nsIContent* GetChildAtFocusOffset() const { 333 return FocusRef().IsSet() ? FocusRef().GetChildAtOffset() : nullptr; 334 } 335 336 void RemoveAllRanges() { 337 mRanges.Clear(); 338 mAnchorFocusRange = nullptr; 339 mDirection = nsDirection::eDirNext; 340 } 341 342 void RemoveCollapsedRanges(); 343 344 /** 345 * If the points are same (i.e., mean a collapsed range) and in an empty block 346 * element except the padding <br> element, this makes aStartPoint and 347 * aEndPoint contain the padding <br> element. 348 */ 349 static void UpdatePointsToSelectAllChildrenIfCollapsedInEmptyBlockElement( 350 EditorDOMPoint& aStartPoint, EditorDOMPoint& aEndPoint, 351 const dom::Element& aEditingHost); 352 353 /** 354 * CreateRangeExtendedToHardLineStartAndEnd() creates an nsRange instance 355 * which may be expanded to start/end of hard line at both edges of the given 356 * range. If this fails handling something, returns nullptr. 357 */ 358 static already_AddRefed<nsRange> 359 CreateRangeWrappingStartAndEndLinesContainingBoundaries( 360 const EditorDOMRange& aRange, EditSubAction aEditSubAction, 361 BlockInlineCheck aBlockInlineCheck, const dom::Element& aEditingHost) { 362 if (!aRange.IsPositioned()) { 363 return nullptr; 364 } 365 return CreateRangeWrappingStartAndEndLinesContainingBoundaries( 366 aRange.StartRef(), aRange.EndRef(), aEditSubAction, aBlockInlineCheck, 367 aEditingHost); 368 } 369 static already_AddRefed<nsRange> 370 CreateRangeWrappingStartAndEndLinesContainingBoundaries( 371 const EditorDOMPoint& aStartPoint, const EditorDOMPoint& aEndPoint, 372 EditSubAction aEditSubAction, BlockInlineCheck aBlockInlineCheck, 373 const dom::Element& aEditingHost) { 374 RefPtr<nsRange> range = 375 nsRange::Create(aStartPoint.ToRawRangeBoundary(), 376 aEndPoint.ToRawRangeBoundary(), IgnoreErrors()); 377 if (MOZ_UNLIKELY(!range)) { 378 return nullptr; 379 } 380 if (NS_FAILED(ExtendRangeToWrapStartAndEndLinesContainingBoundaries( 381 *range, aEditSubAction, aBlockInlineCheck, aEditingHost)) || 382 MOZ_UNLIKELY(!range->IsPositioned())) { 383 return nullptr; 384 } 385 return range.forget(); 386 } 387 388 /** 389 * Splits text nodes if each range end is in middle of a text node, then, 390 * calls HTMLEditor::SplitParentInlineElementsAtRangeBoundaries() for each 391 * range. Finally, updates ranges to keep edit target ranges as expected. 392 * 393 * @param aHTMLEditor The HTMLEditor which will handle the splittings. 394 * @param aBlockInlineCheck Considering block vs inline with whether the 395 * computed style or the HTML default style. 396 * @param aElement The editing host. 397 * @param aAncestorLimiter A content node which you don't want this to 398 * split it. 399 * @return A suggest point to put caret if succeeded, but 400 * it may be unset. 401 */ 402 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult> 403 SplitTextAtEndBoundariesAndInlineAncestorsAtBothBoundaries( 404 HTMLEditor& aHTMLEditor, BlockInlineCheck aBlockInlineCheck, 405 const dom::Element& aEditingHost, 406 const nsIContent* aAncestorLimiter = nullptr); 407 408 /** 409 * CollectEditTargetNodes() collects edit target nodes the ranges. 410 * First, this collects all nodes in given ranges, then, modifies the 411 * result for specific edit sub-actions. 412 */ 413 enum class CollectNonEditableNodes { No, Yes }; 414 nsresult CollectEditTargetNodes( 415 const HTMLEditor& aHTMLEditor, 416 nsTArray<OwningNonNull<nsIContent>>& aOutArrayOfContents, 417 EditSubAction aEditSubAction, 418 CollectNonEditableNodes aCollectNonEditableNodes) const; 419 420 /** 421 * Retrieve a closest ancestor list element of a common ancestor of _A_ range 422 * of the ranges. This tries to retrieve it from the first range to the last 423 * range. 424 */ 425 dom::Element* GetClosestAncestorAnyListElementOfRange() const; 426 427 [[nodiscard]] virtual bool HasSavedRanges() const { return false; } 428 429 /** 430 * Extend all ranges to contain surrounding invisible white-spaces if there 431 * are. 432 * 433 * @param aStripWrappers nsIEditor::eStrip if the caller wants to delete 434 * inline ancestors too. 435 */ 436 void ExtendRangeToContainSurroundingInvisibleWhiteSpaces( 437 nsIEditor::EStripWrappers aStripWrappers); 438 439 protected: 440 AutoClonedRangeArray() = default; 441 442 static nsresult ExtendRangeToWrapStartAndEndLinesContainingBoundaries( 443 nsRange& aRange, EditSubAction aEditSubAction, 444 BlockInlineCheck aBlockInlineCheck, const dom::Element& aEditingHost); 445 446 using InterlinePosition = dom::Selection::InterlinePosition; 447 virtual void SetNewCaretAssociationHint( 448 const RawRangeBoundary& aPoint, InterlinePosition aInterlinePosition) {} 449 450 AutoTArray<mozilla::OwningNonNull<nsRange>, 8> mRanges; 451 RefPtr<nsRange> mAnchorFocusRange; 452 nsDirection mDirection = nsDirection::eDirNext; 453 }; 454 455 /****************************************************************************** 456 * AutoClonedSelectionRangeArray stores closed ranges and have similar API of 457 * Selection. So, different from `AutoSelectionRangeArray`, this can be used 458 * for ranges which may need to be modified before touching the DOM tree, but 459 * does not want to modify `Selection` for the performance. 460 *****************************************************************************/ 461 462 class MOZ_STACK_CLASS AutoClonedSelectionRangeArray final 463 : public AutoClonedRangeArray { 464 using Selection = dom::Selection; 465 466 public: 467 AutoClonedSelectionRangeArray() = delete; 468 explicit AutoClonedSelectionRangeArray(const Selection& aSelection); 469 template <typename PointType> 470 AutoClonedSelectionRangeArray( 471 const EditorDOMRangeBase<PointType>& aRange, 472 const LimitersAndCaretData& aLimitersAndCaretData); 473 template <typename PT, typename CT> 474 AutoClonedSelectionRangeArray( 475 const EditorDOMPointBase<PT, CT>& aPoint, 476 const LimitersAndCaretData& aLimitersAndCaretData); 477 AutoClonedSelectionRangeArray( 478 const nsRange& aRange, const LimitersAndCaretData& aLimitersAndCaretData); 479 // The copy constructor copies everything except saved ranges. 480 explicit AutoClonedSelectionRangeArray( 481 const AutoClonedSelectionRangeArray& aOther); 482 483 ~AutoClonedSelectionRangeArray() override { 484 if (HasSavedRanges()) { 485 ClearSavedRanges(); 486 } 487 } 488 489 void Initialize(const Selection& aSelection) { 490 ClearSavedRanges(); 491 mDirection = aSelection.GetDirection(); 492 mRanges.Clear(); 493 if (nsFrameSelection* frameSelection = aSelection.GetFrameSelection()) { 494 mLimitersAndCaretData = LimitersAndCaretData(*frameSelection); 495 } 496 for (const uint32_t i : IntegerRange(aSelection.RangeCount())) { 497 MOZ_ASSERT(aSelection.GetRangeAt(i)); 498 const nsRange* const range = aSelection.GetRangeAt(i); 499 if (!RangeIsInLimiters(*range)) { 500 continue; 501 } 502 mRanges.AppendElement(range->CloneRange()); 503 if (range == aSelection.GetAnchorFocusRange()) { 504 mAnchorFocusRange = mRanges.LastElement(); 505 } 506 } 507 } 508 509 /** 510 * APIs to store ranges with only container node and offset in it, and track 511 * them with RangeUpdater. 512 */ 513 [[nodiscard]] bool SaveAndTrackRanges(HTMLEditor& aHTMLEditor); 514 [[nodiscard]] bool HasSavedRanges() const override { 515 return mSavedRanges.isSome(); 516 } 517 void ClearSavedRanges(); 518 void RestoreFromSavedRanges() { 519 MOZ_DIAGNOSTIC_ASSERT(mSavedRanges.isSome()); 520 if (MOZ_UNLIKELY(mSavedRanges.isNothing())) { 521 return; 522 } 523 mSavedRanges->ApplyTo(*this); 524 ClearSavedRanges(); 525 } 526 527 /** 528 * Apply mRanges and mDirection to aSelection. 529 */ 530 MOZ_CAN_RUN_SCRIPT nsresult ApplyTo(dom::Selection& aSelection) { 531 dom::SelectionBatcher selectionBatcher(aSelection, __FUNCTION__); 532 aSelection.RemoveAllRanges(IgnoreErrors()); 533 MOZ_ASSERT(!aSelection.RangeCount()); 534 aSelection.SetDirection(mDirection); 535 IgnoredErrorResult error; 536 for (const OwningNonNull<nsRange>& range : mRanges) { 537 // MOZ_KnownLive(range) due to bug 1622253 538 aSelection.AddRangeAndSelectFramesAndNotifyListeners(MOZ_KnownLive(range), 539 error); 540 if (MOZ_UNLIKELY(error.Failed())) { 541 return error.StealNSResult(); 542 } 543 } 544 // FIXME: If and only if the interline position is set explicitly, we need 545 // to call `Selection::SetInterlinePosition` here with the specified value. 546 // However, currently nobody does it. 547 return NS_OK; 548 } 549 550 [[nodiscard]] const LimitersAndCaretData& LimitersAndCaretDataRef() const { 551 return mLimitersAndCaretData; 552 } 553 554 /** 555 * Equivalent to nsFrameSelection::GetIndependentSelectionRootElement(). 556 * NOTE: This should be called only when IsForSelection() returns true. 557 */ 558 [[nodiscard]] dom::Element* GetIndependentSelectionRootElement() const { 559 return mLimitersAndCaretData.mIndependentSelectionRootElement; 560 } 561 /** 562 * Equivalent to nsFrameSelection::GetAncestorLimiter() 563 * NOTE: This should be called only when IsForSelection() returns true. 564 */ 565 [[nodiscard]] dom::Element* GetAncestorLimiter() const { 566 return mLimitersAndCaretData.mAncestorLimiter; 567 } 568 /** 569 * Equivalent to nsFrameSelection::GetHint(), this may be updated when 570 * Collapse() is called. Otherwise, this value may mismatch with what 571 * nsFrameSelection returns if you worked with nsFrameSelection and 572 * Selection directly. 573 */ 574 [[nodiscard]] CaretAssociationHint GetHint() const { 575 return mLimitersAndCaretData.mCaretAssociationHint; 576 } 577 /** 578 * Equivalent to nsFrameSelection::GetCaretBidiLevel(), this returns the value 579 * when this is initialized with nsFrameSelection or inherits another 580 * instance. Therefore, the value may be invalid once you already modified 581 * the ranges. 582 */ 583 [[nodiscard]] intl::BidiEmbeddingLevel GetCaretBidiLevel() const { 584 return mLimitersAndCaretData.mCaretBidiLevel; 585 } 586 587 void SetAncestorLimiter(const dom::Element* aSelectionAncestorLimiter) { 588 if (mLimitersAndCaretData.mAncestorLimiter == aSelectionAncestorLimiter) { 589 return; 590 } 591 mLimitersAndCaretData.mAncestorLimiter = 592 const_cast<dom::Element*>(aSelectionAncestorLimiter); 593 if (NodeIsInLimiters(GetFocusNode())) { 594 return; 595 } 596 RemoveAllRanges(); 597 } 598 599 void SetInterlinePosition(Selection::InterlinePosition aInterlinePosition) { 600 switch (aInterlinePosition) { 601 case Selection::InterlinePosition::EndOfLine: 602 mLimitersAndCaretData.mCaretAssociationHint = 603 CaretAssociationHint::Before; 604 break; 605 case Selection::InterlinePosition::StartOfNextLine: 606 mLimitersAndCaretData.mCaretAssociationHint = 607 CaretAssociationHint::After; 608 break; 609 case Selection::InterlinePosition::Undefined: 610 break; 611 } 612 } 613 614 void SetCaretBidiLevel(intl::BidiEmbeddingLevel aBidiLevel) { 615 mLimitersAndCaretData.mCaretBidiLevel = aBidiLevel; 616 } 617 618 [[nodiscard]] bool NodeIsInLimiters(const nsINode* aContainerNode) const { 619 return mLimitersAndCaretData.NodeIsInLimiters(aContainerNode); 620 } 621 [[nodiscard]] bool RangeIsInLimiters(const dom::AbstractRange& aRange) const { 622 return mLimitersAndCaretData.RangeInLimiters(aRange); 623 } 624 625 /** 626 * ExtendAnchorFocusRangeFor() extends the anchor-focus range for deleting 627 * content for aDirectionAndAmount. The range won't be extended to outer of 628 * selection limiter. Note that if a range is extened, the range is 629 * recreated. Therefore, caller cannot cache pointer of any ranges before 630 * calling this. 631 */ 632 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<nsIEditor::EDirection, nsresult> 633 ExtendAnchorFocusRangeFor(const EditorBase& aEditorBase, 634 nsIEditor::EDirection aDirectionAndAmount); 635 636 private: 637 void SetNewCaretAssociationHint( 638 const RawRangeBoundary& aRawRangeBoundary, 639 InterlinePosition aInternlinePosition) override; 640 641 Maybe<SelectionState> mSavedRanges; 642 RefPtr<HTMLEditor> mTrackingHTMLEditor; 643 LimitersAndCaretData mLimitersAndCaretData; 644 }; 645 646 } // namespace mozilla 647 648 #endif // #ifndef AutoClonedRangeArray_h