/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef AutoRangeArray_h #define AutoRangeArray_h #include "EditAction.h" // for EditSubAction #include "EditorBase.h" // for EditorBase #include "EditorDOMPoint.h" // for EditorDOMPoint, EditorDOMRange, etc #include "EditorForwards.h" #include "SelectionState.h" // for SelectionState #include "mozilla/ErrorResult.h" // for ErrorResult #include "mozilla/IntegerRange.h" // for IntegerRange #include "mozilla/Maybe.h" // for Maybe #include "mozilla/RangeBoundary.h" // for RangeBoundary #include "mozilla/Result.h" // for Result<> #include "mozilla/dom/Element.h" // for dom::Element #include "mozilla/dom/HTMLBRElement.h" // for dom::HTMLBRElement #include "mozilla/dom/Selection.h" // for dom::Selection #include "mozilla/dom/Text.h" // for dom::Text #include "nsDebug.h" // for NS_WARNING, etc #include "nsDirection.h" // for nsDirection #include "nsError.h" // for NS_SUCCESS_* and NS_ERROR_* #include "nsRange.h" // for nsRange namespace mozilla { /****************************************************************************** * AutoRangeArray stores ranges which do no belong any `Selection`. * So, different from `AutoSelectionRangeArray`, this can be used for * ranges which may need to be modified before touching the DOM tree, * but does not want to modify `Selection` for the performance. *****************************************************************************/ class MOZ_STACK_CLASS AutoRangeArray final { public: explicit AutoRangeArray(const dom::Selection& aSelection); template explicit AutoRangeArray(const EditorDOMRangeBase& aRange); template explicit AutoRangeArray(const EditorDOMPointBase& aPoint); // The copy constructor copies everything except saved ranges. explicit AutoRangeArray(const AutoRangeArray& aOther); ~AutoRangeArray(); void Initialize(const dom::Selection& aSelection) { ClearSavedRanges(); mDirection = aSelection.GetDirection(); mRanges.Clear(); for (const uint32_t i : IntegerRange(aSelection.RangeCount())) { MOZ_ASSERT(aSelection.GetRangeAt(i)); mRanges.AppendElement(aSelection.GetRangeAt(i)->CloneRange()); if (aSelection.GetRangeAt(i) == aSelection.GetAnchorFocusRange()) { mAnchorFocusRange = mRanges.LastElement(); } } } /** * Check whether all ranges in content nodes or not. If the ranges is empty, * this returns false. */ [[nodiscard]] bool IsInContent() const { if (mRanges.IsEmpty()) { return false; } for (const OwningNonNull& range : mRanges) { if (MOZ_UNLIKELY(!range->IsPositioned() || !range->GetStartContainer() || !range->GetStartContainer()->IsContent() || !range->GetEndContainer() || !range->GetEndContainer()->IsContent())) { return false; } } return true; } /** * EnsureOnlyEditableRanges() removes ranges which cannot modify. * Note that this is designed only for `HTMLEditor` because this must not * be required by `TextEditor`. */ void EnsureOnlyEditableRanges(const dom::Element& aEditingHost); /** * EnsureRangesInTextNode() is designed for TextEditor to guarantee that * all ranges are in its text node which is first child of the anonymous
* element and is first child. */ void EnsureRangesInTextNode(const dom::Text& aTextNode); /** * Extend ranges to wrap lines to handle block level edit actions such as * updating the block parent or indent/outdent around the selection. */ void ExtendRangesToWrapLinesToHandleBlockLevelEditAction( EditSubAction aEditSubAction, const dom::Element& aEditingHost); /** * Check whether the range is in aEditingHost and both containers of start and * end boundaries of the range are editable. */ [[nodiscard]] static bool IsEditableRange(const dom::AbstractRange& aRange, const dom::Element& aEditingHost); /** * Check whether the first range is in aEditingHost and both containers of * start and end boundaries of the first range are editable. */ [[nodiscard]] bool IsFirstRangeEditable( const dom::Element& aEditingHost) const { return IsEditableRange(FirstRangeRef(), aEditingHost); } /** * IsAtLeastOneContainerOfRangeBoundariesInclusiveDescendantOf() returns true * if at least one of the containers of the range boundaries is an inclusive * descendant of aContent. */ [[nodiscard]] bool IsAtLeastOneContainerOfRangeBoundariesInclusiveDescendantOf( const nsIContent& aContent) const { for (const OwningNonNull& range : mRanges) { nsINode* startContainer = range->GetStartContainer(); if (startContainer && startContainer->IsInclusiveDescendantOf(&aContent)) { return true; } nsINode* endContainer = range->GetEndContainer(); if (startContainer == endContainer) { continue; } if (endContainer && endContainer->IsInclusiveDescendantOf(&aContent)) { return true; } } return false; } [[nodiscard]] auto& Ranges() { return mRanges; } [[nodiscard]] const auto& Ranges() const { return mRanges; } [[nodiscard]] OwningNonNull& FirstRangeRef() { return mRanges[0]; } [[nodiscard]] const OwningNonNull& FirstRangeRef() const { return mRanges[0]; } template