/* -*- 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/. */
/** * EditorDOMPoint and EditorRawDOMPoint are simple classes which refers * a point in the DOM tree at creating the instance or initializing the * instance with calling Set(). * * EditorDOMPoint refers container node (and child node if it's already set) * with nsCOMPtr. EditorRawDOMPoint refers them with raw pointer. * So, EditorRawDOMPoint is useful when you access the nodes only before * changing DOM tree since increasing refcount may appear in micro benchmark * if it's in a hot path. On the other hand, if you need to refer them even * after changing DOM tree, you must use EditorDOMPoint. * * When initializing an instance only with child node or offset, the instance * starts to refer the child node or offset in the container. In this case, * the other information hasn't been initialized due to performance reason. * When you retrieve the other information with calling Offset() or * GetChild(), the other information is computed with the current DOM tree. * Therefore, e.g., in the following case, the other information may be * different: * * EditorDOMPoint pointA(container1, childNode1); * EditorDOMPoint pointB(container1, childNode1); * Unused << pointA.Offset(); // The offset is computed now. * container1->RemoveChild(childNode1->GetPreviousSibling()); * Unused << pointB.Offset(); // Now, pointB.Offset() equals pointA.Offset() - 1 * * similarly: * * EditorDOMPoint pointA(container1, 5); * EditorDOMPoint pointB(container1, 5); * Unused << pointA.GetChild(); // The child is computed now. * container1->RemoveChild(childNode1->GetFirstChild()); * Unused << pointB.GetChild(); // Now, pointB.GetChild() equals * // pointA.GetChild()->GetPreviousSibling(). * * So, when you initialize an instance only with one information, you need to * be careful when you access the other information after changing the DOM tree. * When you need to lock the child node or offset and recompute the other * information with new DOM tree, you can use * AutoEditorDOMPointOffsetInvalidator and AutoEditorDOMPointChildInvalidator.
*/
// FYI: Don't make the following instantiating macros end with `;` because // using them without `;`, VSCode may be confused and cause wrong red- // wavy underlines in the following code of the macro. #define NS_INSTANTIATE_EDITOR_DOM_POINT_METHOD(aResultType, aMethodName, ...) \ template aResultType EditorDOMPoint::aMethodName(__VA_ARGS__); \ template aResultType EditorRawDOMPoint::aMethodName(__VA_ARGS__); \ template aResultType EditorDOMPointInText::aMethodName(__VA_ARGS__); \ template aResultType EditorRawDOMPointInText::aMethodName(__VA_ARGS__)
/** * Different from RangeBoundary, aPointedNode should be a child node * which you want to refer.
*/ explicit EditorDOMPointBase( const nsINode* aPointedNode,
InterlinePosition aInterlinePosition = InterlinePosition::Undefined)
: mParent(aPointedNode && aPointedNode->IsContent()
? aPointedNode->GetParentNode()
: nullptr),
mChild(aPointedNode && aPointedNode->IsContent()
? const_cast<nsIContent*>(aPointedNode->AsContent())
: nullptr),
mInterlinePosition(aInterlinePosition) {
mIsChildInitialized = aPointedNode && mChild;
NS_WARNING_ASSERTION(IsSet(), "The child is nullptr or doesn't have its parent");
NS_WARNING_ASSERTION(mChild && mChild->GetParentNode() == mParent, "Initializing RangeBoundary with invalid value");
}
/** * GetContainer() returns the container node at the point. * GetContainerAs() returns the container node as specific type.
*/
nsINode* GetContainer() const { return mParent; } template <typename ContentNodeType>
ContentNodeType* GetContainerAs() const { return ContentNodeType::FromNodeOrNull(mParent);
}
/** * ContainerAs() returns the container node with just casting to the specific * type. Therefore, callers need to guarantee that the result is not nullptr * nor wrong cast.
*/ template <typename ContentNodeType>
ContentNodeType* ContainerAs() const {
MOZ_ASSERT(mParent);
MOZ_DIAGNOSTIC_ASSERT(
ContentNodeType::FromNode(static_cast<const nsINode*>(mParent))); returnstatic_cast<ContentNodeType*>(GetContainer());
}
/** * CanContainerHaveChildren() returns true if the container node can have * child nodes. Otherwise, e.g., when the container is a text node, returns * false.
*/ bool CanContainerHaveChildren() const { return mParent && mParent->IsContainerNode();
}
/** * IsContainerEmpty() returns true if it has no children or its text is empty.
*/ bool IsContainerEmpty() const { return mParent && !mParent->Length(); }
/** * IsInContentNode() returns true if the container is a subclass of * nsIContent.
*/ bool IsInContentNode() const { return mParent && mParent->IsContent(); }
/** * IsInDataNode() returns true if the container node is a data node including * text node.
*/ bool IsInDataNode() const { return mParent && mParent->IsCharacterData(); }
/** * IsInTextNode() returns true if the container node is a text node.
*/ bool IsInTextNode() const { return mParent && mParent->IsText(); }
/** * IsInNativeAnonymousSubtree() returns true if the container is in * native anonymous subtree.
*/ bool IsInNativeAnonymousSubtree() const { return mParent && mParent->IsInNativeAnonymousSubtree();
}
/** * Returns true if the container node is an element node.
*/ bool IsContainerElement() const { return mParent && mParent->IsElement(); }
/** * Returns true if the container node is an editing host.
*/
[[nodiscard]] bool IsContainerEditableRoot() const;
/** * IsContainerHTMLElement() returns true if the container node is an HTML * element node and its node name is aTag.
*/ bool IsContainerHTMLElement(nsAtom* aTag) const { return mParent && mParent->IsHTMLElement(aTag);
}
/** * IsContainerAnyOfHTMLElements() returns true if the container node is an * HTML element node and its node name is one of the arguments.
*/ template <typename First, typename... Args> bool IsContainerAnyOfHTMLElements(First aFirst, Args... aArgs) const { return mParent && mParent->IsAnyOfHTMLElements(aFirst, aArgs...);
}
/** * GetChild() returns a child node which is pointed by the instance. * If mChild hasn't been initialized yet, this computes the child node * from mParent and mOffset with *current* DOM tree.
*/
nsIContent* GetChild() const { if (!mParent || !mParent->IsContainerNode()) { return nullptr;
} if (mIsChildInitialized) { return mChild;
} // Fix child node now. const_cast<SelfType*>(this)->EnsureChild(); return mChild;
}
/** * GetCurrentChildAtOffset() returns current child at mOffset. * I.e., mOffset needs to be fixed before calling this.
*/
nsIContent* GetCurrentChildAtOffset() const {
MOZ_ASSERT(mOffset.isSome()); if (mOffset.isNothing()) { return GetChild();
} return mParent ? mParent->GetChildAt_Deprecated(*mOffset) : nullptr;
}
/** * GetChildOrContainerIfDataNode() returns the child content node, * or container content node if the container is a data node.
*/
nsIContent* GetChildOrContainerIfDataNode() const { if (IsInDataNode()) { return ContainerAs<nsIContent>();
} return GetChild();
}
/** * GetNextSiblingOfChild() returns next sibling of the child node. * If this refers after the last child or the container cannot have children, * this returns nullptr with warning. * If mChild hasn't been initialized yet, this computes the child node * from mParent and mOffset with *current* DOM tree.
*/
nsIContent* GetNextSiblingOfChild() const { if (NS_WARN_IF(!mParent) || !mParent->IsContainerNode()) { return nullptr;
} if (mIsChildInitialized) { return mChild ? mChild->GetNextSibling() : nullptr;
}
MOZ_ASSERT(mOffset.isSome()); if (NS_WARN_IF(mOffset.value() > mParent->Length())) { // If this has been set only offset and now the offset is invalid, // let's just return nullptr. return nullptr;
} // Fix child node now. const_cast<SelfType*>(this)->EnsureChild(); return mChild ? mChild->GetNextSibling() : nullptr;
} template <typename ContentNodeType>
ContentNodeType* GetNextSiblingOfChildAs() const { return ContentNodeType::FromNodeOrNull(GetNextSiblingOfChild());
} template <typename ContentNodeType>
ContentNodeType* NextSiblingOfChildAs() const {
MOZ_ASSERT(IsSet());
MOZ_DIAGNOSTIC_ASSERT(GetNextSiblingOfChildAs<ContentNodeType>()); returnstatic_cast<ContentNodeType*>(GetNextSiblingOfChild());
}
/** * GetPreviousSiblingOfChild() returns previous sibling of a child * at offset. If this refers the first child or the container cannot have * children, this returns nullptr with warning. * If mChild hasn't been initialized yet, this computes the child node * from mParent and mOffset with *current* DOM tree.
*/
nsIContent* GetPreviousSiblingOfChild() const { if (NS_WARN_IF(!mParent) || !mParent->IsContainerNode()) { return nullptr;
} if (mIsChildInitialized) { return mChild ? mChild->GetPreviousSibling() : mParent->GetLastChild();
}
MOZ_ASSERT(mOffset.isSome()); if (NS_WARN_IF(mOffset.value() > mParent->Length())) { // If this has been set only offset and now the offset is invalid, // let's just return nullptr. return nullptr;
} // Fix child node now. const_cast<SelfType*>(this)->EnsureChild(); return mChild ? mChild->GetPreviousSibling() : mParent->GetLastChild();
} template <typename ContentNodeType>
ContentNodeType* GetPreviousSiblingOfChildAs() const { return ContentNodeType::FromNodeOrNull(GetPreviousSiblingOfChild());
} template <typename ContentNodeType>
ContentNodeType* PreviousSiblingOfChildAs() const {
MOZ_ASSERT(IsSet());
MOZ_DIAGNOSTIC_ASSERT(GetPreviousSiblingOfChildAs<ContentNodeType>()); returnstatic_cast<ContentNodeType*>(GetPreviousSiblingOfChild());
}
/** * Simple accessors of the character in dom::Text so that when you call * these methods, you need to guarantee that the container is a dom::Text.
*/
MOZ_NEVER_INLINE_DEBUG char16_t Char() const {
MOZ_ASSERT(IsSetAndValid());
MOZ_ASSERT(!IsEndOfContainer()); return ContainerAs<dom::Text>()->TextFragment().CharAt(mOffset.value());
}
MOZ_NEVER_INLINE_DEBUG bool IsCharASCIISpace() const { return nsCRT::IsAsciiSpace(Char());
}
MOZ_NEVER_INLINE_DEBUG bool IsCharNBSP() const { returnChar() == 0x00A0; }
MOZ_NEVER_INLINE_DEBUG bool IsCharASCIISpaceOrNBSP() const {
char16_t ch = Char(); return nsCRT::IsAsciiSpace(ch) || ch == 0x00A0;
}
MOZ_NEVER_INLINE_DEBUG bool IsCharNewLine() const { returnChar() == '\n'; }
MOZ_NEVER_INLINE_DEBUG bool IsCharPreformattedNewLine() const;
MOZ_NEVER_INLINE_DEBUG bool
IsCharPreformattedNewLineCollapsedWithWhiteSpaces() const; /** * IsCharCollapsibleASCIISpace(), IsCharCollapsibleNBSP() and * IsCharCollapsibleASCIISpaceOrNBSP() checks whether the white-space is * preformatted or collapsible with the style of the container text node * without flushing pending notifications.
*/ bool IsCharCollapsibleASCIISpace() const; bool IsCharCollapsibleNBSP() const; bool IsCharCollapsibleASCIISpaceOrNBSP() const;
/** * ParentPoint() returns a point whose child is the container.
*/ template <typename EditorDOMPointType = SelfType>
EditorDOMPointType ParentPoint() const {
MOZ_ASSERT(mParent); if (MOZ_UNLIKELY(!mParent) || !mParent->IsContent()) { return EditorDOMPointType();
} return EditorDOMPointType(ContainerAs<nsIContent>());
}
/** * NextPoint() and PreviousPoint() returns next/previous DOM point in * the container.
*/ template <typename EditorDOMPointType = SelfType>
EditorDOMPointType NextPoint() const {
NS_ASSERTION(!IsEndOfContainer(), "Should not be at end of the container"); auto result = this->template To<EditorDOMPointType>();
result.AdvanceOffset(); return result;
} template <typename EditorDOMPointType = SelfType>
EditorDOMPointType NextPointOrAfterContainer() const {
MOZ_ASSERT(IsInContentNode()); if (!IsEndOfContainer()) { return NextPoint<EditorDOMPointType>();
} return AfterContainer<EditorDOMPointType>();
} template <typename EditorDOMPointType = SelfType>
EditorDOMPointType AfterContainer() const {
MOZ_ASSERT(IsInContentNode()); return EditorDOMPointType::After(*ContainerAs<nsIContent>());
} template <typename EditorDOMPointType = SelfType>
EditorDOMPointType PreviousPoint() const {
NS_ASSERTION(!IsStartOfContainer(), "Should not be at start of the container");
EditorDOMPointType result = this->template To<EditorDOMPointType>();
result.RewindOffset(); return result;
}
/** * Clear() makes the instance not point anywhere.
*/ void Clear() {
mParent = nullptr;
mChild = nullptr;
mOffset.reset();
mIsChildInitialized = false;
mInterlinePosition = InterlinePosition::Undefined;
}
/** * AdvanceOffset() tries to refer next sibling of mChild and/of next offset. * If the container can have children and there is no next sibling or the * offset reached the length of the container, this outputs warning and does * nothing. So, callers need to check if there is next sibling which you * need to refer. * * @return true if there is a next DOM point to refer.
*/ bool AdvanceOffset() { if (NS_WARN_IF(!mParent)) { returnfalse;
} // If only mOffset is available, just compute the offset. if ((mOffset.isSome() && !mIsChildInitialized) ||
!mParent->IsContainerNode()) {
MOZ_ASSERT(mOffset.isSome());
MOZ_ASSERT(!mChild); if (NS_WARN_IF(mOffset.value() >= mParent->Length())) { // We're already referring the start of the container. returnfalse;
}
mOffset = mozilla::Some(mOffset.value() + 1);
mInterlinePosition = InterlinePosition::Undefined; returntrue;
}
MOZ_ASSERT(mIsChildInitialized);
MOZ_ASSERT(!mOffset.isSome() || mOffset.isSome()); if (NS_WARN_IF(!mParent->HasChildren()) || NS_WARN_IF(!mChild) ||
NS_WARN_IF(mOffset.isSome() && mOffset.value() >= mParent->Length())) { // We're already referring the end of the container (or outside). returnfalse;
}
/** * RewindOffset() tries to refer previous sibling of mChild and/or previous * offset. If the container can have children and there is no next previous * or the offset is 0, this outputs warning and does nothing. So, callers * need to check if there is previous sibling which you need to refer. * * @return true if there is a previous DOM point to refer.
*/ bool RewindOffset() { if (NS_WARN_IF(!mParent)) { returnfalse;
} // If only mOffset is available, just compute the offset. if ((mOffset.isSome() && !mIsChildInitialized) ||
!mParent->IsContainerNode()) {
MOZ_ASSERT(mOffset.isSome());
MOZ_ASSERT(!mChild); if (NS_WARN_IF(!mOffset.value()) ||
NS_WARN_IF(mOffset.value() > mParent->Length())) { // We're already referring the start of the container or // the offset is invalid since perhaps, the offset was set before // the last DOM tree change.
NS_ASSERTION(false, "Failed to rewind offset"); returnfalse;
}
mOffset = mozilla::Some(mOffset.value() - 1);
mInterlinePosition = InterlinePosition::Undefined; returntrue;
}
MOZ_ASSERT(mIsChildInitialized);
MOZ_ASSERT(!mOffset.isSome() || mOffset.isSome()); if (NS_WARN_IF(!mParent->HasChildren()) ||
NS_WARN_IF(mChild && !mChild->GetPreviousSibling()) ||
NS_WARN_IF(mOffset.isSome() && !mOffset.value())) { // We're already referring the start of the container (or the child has // been moved from the container?). returnfalse;
}
nsIContent* previousSibling =
mChild ? mChild->GetPreviousSibling() : mParent->GetLastChild(); if (NS_WARN_IF(!previousSibling)) { // We're already referring the first child of the container. returnfalse;
}
/** * GetNonAnonymousSubtreePoint() returns a DOM point which is NOT in * native-anonymous subtree. If the instance isn't in native-anonymous * subtree, this returns same point. Otherwise, climbs up until finding * non-native-anonymous parent and returns the point of it. I.e., * container is parent of the found non-anonymous-native node.
*/ template <typename EditorDOMPointType>
EditorDOMPointType GetNonAnonymousSubtreePoint() const { if (NS_WARN_IF(!IsSet())) { return EditorDOMPointType();
} if (!IsInNativeAnonymousSubtree()) { return this->template To<EditorDOMPointType>();
}
nsINode* parent; for (parent = mParent->GetParentNode();
parent && parent->IsInNativeAnonymousSubtree();
parent = parent->GetParentNode()) {
} if (!parent) { return EditorDOMPointType();
} return EditorDOMPointType(parent);
}
bool IsStartOfContainer() const { // If we're referring the first point in the container: // If mParent is not a container like a text node, mOffset is 0. // If mChild is initialized and it's first child of mParent. // If mChild isn't initialized and the offset is 0. if (NS_WARN_IF(!mParent)) { returnfalse;
} if (!mParent->IsContainerNode()) { return !mOffset.value();
} if (mIsChildInitialized) { if (mParent->GetFirstChild() == mChild) {
NS_WARNING_ASSERTION(!mOffset.isSome() || !mOffset.value(), "If mOffset was initialized, it should be 0"); returntrue;
}
NS_WARNING_ASSERTION(!mOffset.isSome() || mParent->GetChildAt_Deprecated(
mOffset.value()) == mChild, "mOffset and mChild are mismatched"); returnfalse;
}
MOZ_ASSERT(mOffset.isSome()); return !mOffset.value();
}
bool IsEndOfContainer() const { // If we're referring after the last point of the container: // If mParent is not a container like text node, mOffset is same as the // length of the container. // If mChild is initialized and it's nullptr. // If mChild isn't initialized and mOffset is same as the length of the // container. if (NS_WARN_IF(!mParent)) { returnfalse;
} if (!mParent->IsContainerNode()) { return mOffset.value() == mParent->Length();
} if (mIsChildInitialized) { if (!mChild) {
NS_WARNING_ASSERTION(
!mOffset.isSome() || mOffset.value() == mParent->Length(), "If mOffset was initialized, it should be length of the container"); returntrue;
}
NS_WARNING_ASSERTION(!mOffset.isSome() || mParent->GetChildAt_Deprecated(
mOffset.value()) == mChild, "mOffset and mChild are mismatched"); returnfalse;
}
MOZ_ASSERT(mOffset.isSome()); return mOffset.value() == mParent->Length();
}
/** * IsAtLastContent() returns true when it refers last child of the container * or last character offset of text node.
*/ bool IsAtLastContent() const { if (NS_WARN_IF(!mParent)) { returnfalse;
} if (mParent->IsContainerNode() && mOffset.isSome()) { return mOffset.value() == mParent->Length() - 1;
} if (mIsChildInitialized) { if (mChild && mChild == mParent->GetLastChild()) {
NS_WARNING_ASSERTION(
!mOffset.isSome() || mOffset.value() == mParent->Length() - 1, "If mOffset was initialized, it should be length - 1 of the " "container"); returntrue;
}
NS_WARNING_ASSERTION(!mOffset.isSome() || mParent->GetChildAt_Deprecated(
mOffset.value()) == mChild, "mOffset and mChild are mismatched"); returnfalse;
}
MOZ_ASSERT(mOffset.isSome()); return mOffset.value() == mParent->Length() - 1;
}
bool IsBRElementAtEndOfContainer() const { if (NS_WARN_IF(!mParent)) { returnfalse;
} if (!mParent->IsContainerNode()) { returnfalse;
} const_cast<SelfType*>(this)->EnsureChild(); if (!mChild || mChild->GetNextSibling()) { returnfalse;
} return mChild->IsHTMLElement(nsGkAtoms::br);
}
/** * Return a point in text node if "this" points around a text node. * EditorDOMPointType can always be EditorDOMPoint or EditorRawDOMPoint, * but EditorDOMPointInText or EditorRawDOMPointInText is also available * only when "this type" is one of them. * If the point is in the anonymous <div> of a TextEditor, use * TextEditor::FindBetterInsertionPoint() instead.
*/ template <typename EditorDOMPointType>
EditorDOMPointType GetPointInTextNodeIfPointingAroundTextNode() const { if (NS_WARN_IF(!IsSet()) || !mParent->HasChildren()) { return To<EditorDOMPointType>();
} if (IsStartOfContainer()) { if (auto* firstTextChild =
dom::Text::FromNode(mParent->GetFirstChild())) { return EditorDOMPointType(firstTextChild, 0u);
} return To<EditorDOMPointType>();
} if (auto* previousSiblingChild = dom::Text::FromNodeOrNull(
GetPreviousSiblingOfChildAs<dom::Text>())) { return EditorDOMPointType::AtEndOf(*previousSiblingChild);
} if (auto* child = dom::Text::FromNodeOrNull(GetChildAs<dom::Text>())) { return EditorDOMPointType(child, 0u);
} return To<EditorDOMPointType>();
}
template <typename EditorDOMPointType>
constexpr EditorDOMPointType To() const { // XXX Cannot specialize this method due to implicit instantiatation caused // by the inline CC functions below. if (std::is_same<SelfType, EditorDOMPointType>::value) { returnreinterpret_cast<const EditorDOMPointType&>(*this);
}
EditorDOMPointType result;
result.mParent = mParent;
result.mChild = mChild;
result.mOffset = mOffset;
result.mIsChildInitialized = mIsChildInitialized;
result.mInterlinePosition = mInterlinePosition; return result;
}
/** * Don't compare mInterlinePosition. If it's required to check, perhaps, * another compare operator like `===` should be created.
*/ template <typename A, typename B> booloperator==(const EditorDOMPointBase<A, B>& aOther) const { if (mParent != aOther.mParent) { returnfalse;
}
if (mOffset.isSome() && aOther.mOffset.isSome()) { // If both mOffset are set, we need to compare both mRef too because // the relation of mRef and mOffset have already broken by DOM tree // changes. if (mOffset != aOther.mOffset) { returnfalse;
} if (mChild == aOther.mChild) { returntrue;
} if (NS_WARN_IF(mIsChildInitialized && aOther.mIsChildInitialized)) { // In this case, relation between mChild and mOffset of one of or both // of them doesn't match with current DOM tree since the DOM tree might // have been changed after computing mChild or mOffset. returnfalse;
} // If one of mChild hasn't been computed yet, we should compare them only // with mOffset. Perhaps, we shouldn't copy mChild from non-nullptr one // to the other since if we copy it here, it may be unexpected behavior // for some callers. returntrue;
}
if (mOffset.isSome() && !mIsChildInitialized && !aOther.mOffset.isSome() &&
aOther.mIsChildInitialized) { // If this has only mOffset and the other has only mChild, this needs to // compute mChild now. const_cast<SelfType*>(this)->EnsureChild(); return mChild == aOther.mChild;
}
if (!mOffset.isSome() && mIsChildInitialized && aOther.mOffset.isSome() &&
!aOther.mIsChildInitialized) { // If this has only mChild and the other has only mOffset, the other needs // to compute mChild now. const_cast<EditorDOMPointBase<A, B>&>(aOther).EnsureChild(); return mChild == aOther.mChild;
}
// If mOffset of one of them hasn't been computed from mChild yet, we should // compare only with mChild. Perhaps, we shouldn't copy mOffset from being // some one to not being some one since if we copy it here, it may be // unexpected behavior for some callers. return mChild == aOther.mChild;
}
template <typename A, typename B> booloperator==(const RangeBoundaryBase<A, B>& aOther) const { // TODO: Optimize this with directly comparing with RangeBoundaryBase // members. return *this == SelfType(aOther);
}
/** * This operator should be used if API of other modules take RawRangeBoundary, * e.g., methods of Selection and nsRange.
*/ operatorconst RawRangeBoundary() const { return ToRawRangeBoundary(); } const RawRangeBoundary ToRawRangeBoundary() const { if (!IsSet() || NS_WARN_IF(!mIsChildInitialized && !mOffset.isSome())) { return RawRangeBoundary();
} if (!mParent->IsContainerNode()) {
MOZ_ASSERT(mOffset.value() <= mParent->Length()); // If the container is a data node like a text node, we need to create // RangeBoundaryBase instance only with mOffset because mChild is always // nullptr. return RawRangeBoundary(mParent, mOffset.value());
} if (mIsChildInitialized && mOffset.isSome()) { // If we've already set both child and offset, we should create // RangeBoundary with offset after validation. #ifdef DEBUG if (mChild) {
MOZ_ASSERT(mParent == mChild->GetParentNode());
MOZ_ASSERT(mParent->GetChildAt_Deprecated(mOffset.value()) == mChild);
} else {
MOZ_ASSERT(mParent->Length() == mOffset.value());
} #endif// #ifdef DEBUG return RawRangeBoundary(mParent, mOffset.value());
} // Otherwise, we should create RangeBoundaryBase only with available // information. if (mOffset.isSome()) { return RawRangeBoundary(mParent, mOffset.value());
} if (mChild) { return RawRangeBoundary(mParent, mChild->GetPreviousSibling());
} return RawRangeBoundary(mParent, mParent->GetLastChild());
}
/** * EditorDOMRangeBase class stores a pair of same EditorDOMPointBase type. * The instance must be created with valid DOM points and start must be * before or same as end.
*/ #define NS_INSTANTIATE_EDITOR_DOM_RANGE_METHOD(aResultType, aMethodName, ...) \ template aResultType EditorDOMRange::aMethodName(__VA_ARGS__); \ template aResultType EditorRawDOMRange::aMethodName(__VA_ARGS__); \ template aResultType EditorDOMRangeInTexts::aMethodName(__VA_ARGS__); \ template aResultType EditorRawDOMRangeInTexts::aMethodName(__VA_ARGS__)
/** * AutoEditorDOMPointOffsetInvalidator is useful if DOM tree will be changed * when EditorDOMPoint instance is available and keeps referring same child * node. * * This class automatically guarantees that given EditorDOMPoint instance * stores the child node and invalidates its offset when the instance is * destroyed. Additionally, users of this class can invalidate the offset * manually when they need.
*/ class MOZ_STACK_CLASS AutoEditorDOMPointOffsetInvalidator final { public:
AutoEditorDOMPointOffsetInvalidator() = delete;
AutoEditorDOMPointOffsetInvalidator( const AutoEditorDOMPointOffsetInvalidator&) = delete;
AutoEditorDOMPointOffsetInvalidator(AutoEditorDOMPointOffsetInvalidator&&) = delete; const AutoEditorDOMPointOffsetInvalidator& operator=( const AutoEditorDOMPointOffsetInvalidator&) = delete; explicit AutoEditorDOMPointOffsetInvalidator(EditorDOMPoint& aPoint)
: mPoint(aPoint), mCanceled(false) {
MOZ_ASSERT(aPoint.IsSetAndValid());
MOZ_ASSERT(mPoint.CanContainerHaveChildren());
mChild = mPoint.GetChild();
}
~AutoEditorDOMPointOffsetInvalidator() { if (!mCanceled) {
InvalidateOffset();
}
}
/** * Manually, invalidate offset of the given point.
*/ void InvalidateOffset() { if (mChild) {
mPoint.Set(mChild);
} else { // If the point referred after the last child, let's keep referring // after current last node of the old container.
mPoint.SetToEndOf(mPoint.GetContainer());
}
}
/** * After calling Cancel(), mPoint won't be modified by the destructor.
*/ void Cancel() { mCanceled = true; }
private:
EditorDOMPoint& mPoint; // Needs to store child node by ourselves because EditorDOMPoint stores // child node with mRef which is previous sibling of current child node. // Therefore, we cannot keep referring it if it's first child.
nsCOMPtr<nsIContent> mChild;
bool mCanceled;
};
class MOZ_STACK_CLASS AutoEditorDOMRangeOffsetsInvalidator final { public: explicit AutoEditorDOMRangeOffsetsInvalidator(EditorDOMRange& aRange)
: mStartInvalidator(const_cast<EditorDOMPoint&>(aRange.StartRef())),
mEndInvalidator(const_cast<EditorDOMPoint&>(aRange.EndRef())) {}
/** * AutoEditorDOMPointChildInvalidator is useful if DOM tree will be changed * when EditorDOMPoint instance is available and keeps referring same container * and offset in it. * * This class automatically guarantees that given EditorDOMPoint instance * stores offset and invalidates its child node when the instance is destroyed. * Additionally, users of this class can invalidate the child manually when * they need.
*/ class MOZ_STACK_CLASS AutoEditorDOMPointChildInvalidator final { public:
AutoEditorDOMPointChildInvalidator() = delete;
AutoEditorDOMPointChildInvalidator( const AutoEditorDOMPointChildInvalidator&) = delete;
AutoEditorDOMPointChildInvalidator(AutoEditorDOMPointChildInvalidator&&) = delete; const AutoEditorDOMPointChildInvalidator& operator=( const AutoEditorDOMPointChildInvalidator&) = delete; explicit AutoEditorDOMPointChildInvalidator(EditorDOMPoint& aPoint)
: mPoint(aPoint), mCanceled(false) {
MOZ_ASSERT(aPoint.IsSetAndValid());
Unused << mPoint.Offset();
}
~AutoEditorDOMPointChildInvalidator() { if (!mCanceled) {
InvalidateChild();
}
}
/** * Manually, invalidate child of the given point.
*/ void InvalidateChild() { mPoint.Set(mPoint.GetContainer(), mPoint.Offset()); }
/** * After calling Cancel(), mPoint won't be modified by the destructor.
*/ void Cancel() { mCanceled = true; }
private:
EditorDOMPoint& mPoint;
bool mCanceled;
};
class MOZ_STACK_CLASS AutoEditorDOMRangeChildrenInvalidator final { public: explicit AutoEditorDOMRangeChildrenInvalidator(EditorDOMRange& aRange)
: mStartInvalidator(const_cast<EditorDOMPoint&>(aRange.StartRef())),
mEndInvalidator(const_cast<EditorDOMPoint&>(aRange.EndRef())) {}
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.