/* -*- 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/. */
#include"EditAction.h"// for EditSubAction #include"EditorBase.h"// for EditorBase #include"EditorDOMPoint.h"// for EditorDOMPoint, EditorDOMRange, etc #include"EditorForwards.h" #include"HTMLEditHelpers.h"// for BlockInlineCheck #include"HTMLEditor.h"// for HTMLEditor #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"mozilla/intl/BidiEmbeddingLevel.h"// for BidiEmbeddingLevel
#include"nsDebug.h"// for NS_WARNING, etc #include"nsDirection.h"// for nsDirection #include"nsError.h"// for NS_SUCCESS_* and NS_ERROR_* #include"nsFrameSelection.h"// for nsFrameSelection #include"nsRange.h"// for nsRange
namespace mozilla {
enumclass CaretAssociationHint;
/****************************************************************************** * AutoClonedRangeArray stores closed ranges and has similar API to Selection.
*****************************************************************************/
/** * Check whether all ranges in content nodes or not. If the ranges is empty, * this returns false.
*/
[[nodiscard]] bool IsInContent() const { if (mRanges.IsEmpty()) { returnfalse;
} for (const OwningNonNull<nsRange>& range : mRanges) { if (MOZ_UNLIKELY(!range->IsPositioned() || !range->GetStartContainer() ||
!range->GetStartContainer()->IsContent() ||
!range->GetEndContainer() ||
!range->GetEndContainer()->IsContent())) { returnfalse;
}
} returntrue;
}
/** * 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 <div> * element and is first child.
*/ void EnsureRangesInTextNode(const dom::Text& aTextNode);
/** * Extend ranges to make each range select starting from a line start edge and * ending after a line end edge to handle per line edit sub-actions.
*/ void ExtendRangesToWrapLines(EditSubAction aEditSubAction,
BlockInlineCheck aBlockInlineCheck, const dom::Element& aAncestorLimiter);
/** * Check whether the range is in aEditingHost and both containers of start and * end boundaries of the range are editable.
*/
[[nodiscard]] staticbool 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<nsRange>& range : mRanges) {
nsINode* startContainer = range->GetStartContainer(); if (startContainer &&
startContainer->IsInclusiveDescendantOf(&aContent)) { returntrue;
}
nsINode* endContainer = range->GetEndContainer(); if (startContainer == endContainer) { continue;
} if (endContainer && endContainer->IsInclusiveDescendantOf(&aContent)) { returntrue;
}
} returnfalse;
}
/** * For compatiblity with the other browsers, we should shrink ranges to * start from an atomic content and/or end after one instead of start * from end of a preceding text node and end by start of a follwing text * node. Returns true if this modifies a range.
*/ enumclass IfSelectingOnlyOneAtomicContent {
Collapse, // Collapse to the range selecting only one atomic content to // start or after of it. Whether to collapse start or after // it depends on aDirectionAndAmount. This is ignored if // there are multiple ranges.
KeepSelecting, // Won't collapse the range.
};
Result<bool, nsresult> ShrinkRangesIfStartFromOrEndAfterAtomicContent( const HTMLEditor& aHTMLEditor, nsIEditor::EDirection aDirectionAndAmount,
IfSelectingOnlyOneAtomicContent aIfSelectingOnlyOneAtomicContent);
/** * If the points are same (i.e., mean a collapsed range) and in an empty block * element except the padding <br> element, this makes aStartPoint and * aEndPoint contain the padding <br> element.
*/ staticvoid UpdatePointsToSelectAllChildrenIfCollapsedInEmptyBlockElement(
EditorDOMPoint& aStartPoint, EditorDOMPoint& aEndPoint, const dom::Element& aEditingHost);
/** * CreateRangeExtendedToHardLineStartAndEnd() creates an nsRange instance * which may be expanded to start/end of hard line at both edges of the given * range. If this fails handling something, returns nullptr.
*/ static already_AddRefed<nsRange>
CreateRangeWrappingStartAndEndLinesContainingBoundaries( const EditorDOMRange& aRange, EditSubAction aEditSubAction,
BlockInlineCheck aBlockInlineCheck, const dom::Element& aEditingHost) { if (!aRange.IsPositioned()) { return nullptr;
} return CreateRangeWrappingStartAndEndLinesContainingBoundaries(
aRange.StartRef(), aRange.EndRef(), aEditSubAction, aBlockInlineCheck,
aEditingHost);
} static already_AddRefed<nsRange>
CreateRangeWrappingStartAndEndLinesContainingBoundaries( const EditorDOMPoint& aStartPoint, const EditorDOMPoint& aEndPoint,
EditSubAction aEditSubAction, BlockInlineCheck aBlockInlineCheck, const dom::Element& aEditingHost) {
RefPtr<nsRange> range =
nsRange::Create(aStartPoint.ToRawRangeBoundary(),
aEndPoint.ToRawRangeBoundary(), IgnoreErrors()); if (MOZ_UNLIKELY(!range)) { return nullptr;
} if (NS_FAILED(ExtendRangeToWrapStartAndEndLinesContainingBoundaries(
*range, aEditSubAction, aBlockInlineCheck, aEditingHost)) ||
MOZ_UNLIKELY(!range->IsPositioned())) { return nullptr;
} return range.forget();
}
/** * Splits text nodes if each range end is in middle of a text node, then, * calls HTMLEditor::SplitParentInlineElementsAtRangeBoundaries() for each * range. Finally, updates ranges to keep edit target ranges as expected. * * @param aHTMLEditor The HTMLEditor which will handle the splittings. * @param aBlockInlineCheck Considering block vs inline with whether the * computed style or the HTML default style. * @param aElement The editing host. * @param aAncestorLimiter A content node which you don't want this to * split it. * @return A suggest point to put caret if succeeded, but * it may be unset.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult>
SplitTextAtEndBoundariesAndInlineAncestorsAtBothBoundaries(
HTMLEditor& aHTMLEditor, BlockInlineCheck aBlockInlineCheck, const dom::Element& aEditingHost, const nsIContent* aAncestorLimiter = nullptr);
/** * CollectEditTargetNodes() collects edit target nodes the ranges. * First, this collects all nodes in given ranges, then, modifies the * result for specific edit sub-actions.
*/ enumclass CollectNonEditableNodes { No, Yes };
nsresult CollectEditTargetNodes( const HTMLEditor& aHTMLEditor,
nsTArray<OwningNonNull<nsIContent>>& aOutArrayOfContents,
EditSubAction aEditSubAction,
CollectNonEditableNodes aCollectNonEditableNodes) const;
/** * Retrieve a closest ancestor list element of a common ancestor of _A_ range * of the ranges. This tries to retrieve it from the first range to the last * range.
*/
dom::Element* GetClosestAncestorAnyListElementOfRange() const;
/****************************************************************************** * AutoClonedSelectionRangeArray stores closed ranges and have similar API of * 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 AutoClonedSelectionRangeArray final
: public AutoClonedRangeArray { using Selection = dom::Selection;
~AutoClonedSelectionRangeArray() override { if (HasSavedRanges()) {
ClearSavedRanges();
}
}
void Initialize(const Selection& aSelection) {
ClearSavedRanges();
mDirection = aSelection.GetDirection();
mRanges.Clear(); if (nsFrameSelection* frameSelection = aSelection.GetFrameSelection()) {
mLimitersAndCaretData = LimitersAndCaretData(*frameSelection);
} for (const uint32_t i : IntegerRange(aSelection.RangeCount())) {
MOZ_ASSERT(aSelection.GetRangeAt(i)); const nsRange* const range = aSelection.GetRangeAt(i); if (!RangeIsInLimiters(*range)) { continue;
}
mRanges.AppendElement(range->CloneRange()); if (range == aSelection.GetAnchorFocusRange()) {
mAnchorFocusRange = mRanges.LastElement();
}
}
}
/** * APIs to store ranges with only container node and offset in it, and track * them with RangeUpdater.
*/
[[nodiscard]] bool SaveAndTrackRanges(HTMLEditor& aHTMLEditor);
[[nodiscard]] bool HasSavedRanges() const override { return mSavedRanges.isSome();
} void ClearSavedRanges(); void RestoreFromSavedRanges() {
MOZ_DIAGNOSTIC_ASSERT(mSavedRanges.isSome()); if (MOZ_UNLIKELY(mSavedRanges.isNothing())) { return;
}
mSavedRanges->ApplyTo(*this);
ClearSavedRanges();
}
/** * Apply mRanges and mDirection to aSelection.
*/
MOZ_CAN_RUN_SCRIPT nsresult ApplyTo(dom::Selection& aSelection) {
dom::SelectionBatcher selectionBatcher(aSelection, __FUNCTION__);
aSelection.RemoveAllRanges(IgnoreErrors());
MOZ_ASSERT(!aSelection.RangeCount());
aSelection.SetDirection(mDirection);
IgnoredErrorResult error; for (const OwningNonNull<nsRange>& range : mRanges) { // MOZ_KnownLive(range) due to bug 1622253
aSelection.AddRangeAndSelectFramesAndNotifyListeners(MOZ_KnownLive(range),
error); if (MOZ_UNLIKELY(error.Failed())) { return error.StealNSResult();
}
} // FIXME: If and only if the interline position is set explicitly, we need // to call `Selection::SetInterlinePosition` here with the specified value. // However, currently nobody does it. return NS_OK;
}
/** * Equivalent to nsFrameSelection::GetLimiter(). * NOTE: This should be called only when IsForSelection() returns true.
*/
[[nodiscard]] dom::Element* GetLimiter() const { return mLimitersAndCaretData.mLimiter;
} /** * Equivalent to nsFrameSelection::GetAncestorLimiter() * NOTE: This should be called only when IsForSelection() returns true.
*/
[[nodiscard]] dom::Element* GetAncestorLimiter() const { return mLimitersAndCaretData.mAncestorLimiter;
} /** * Equivalent to nsFrameSelection::GetHint(), this may be updated when * Collapse() is called. Otherwise, this value may mismatch with what * nsFrameSelection returns if you worked with nsFrameSelection and * Selection directly.
*/
[[nodiscard]] CaretAssociationHint GetHint() const { return mLimitersAndCaretData.mCaretAssociationHint;
} /** * Equivalent to nsFrameSelection::GetCaretBidiLevel(), this returns the value * when this is initialized with nsFrameSelection or inherits another * instance. Therefore, the value may be invalid once you already modified * the ranges.
*/
[[nodiscard]] intl::BidiEmbeddingLevel GetCaretBidiLevel() const { return mLimitersAndCaretData.mCaretBidiLevel;
}
/** * ExtendAnchorFocusRangeFor() extends the anchor-focus range for deleting * content for aDirectionAndAmount. The range won't be extended to outer of * selection limiter. Note that if a range is extened, the range is * recreated. Therefore, caller cannot cache pointer of any ranges before * calling this.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<nsIEditor::EDirection, nsresult>
ExtendAnchorFocusRangeFor(const EditorBase& aEditorBase,
nsIEditor::EDirection aDirectionAndAmount);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.