/* -*- 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 WSRunScanner_h #define WSRunScanner_h
#include"EditorBase.h" #include"EditorForwards.h" #include"EditorDOMPoint.h"// for EditorDOMPoint #include"HTMLEditor.h" #include"HTMLEditUtils.h"
/** * WSScanResult is result of ScanNextVisibleNodeOrBlockBoundaryFrom(), * ScanPreviousVisibleNodeOrBlockBoundaryFrom(), and their static wrapper * methods. This will have information of found visible content (and its * position) or reached block element or topmost editable content at the * start of scanner.
*/ class MOZ_STACK_CLASS WSScanResult final { private: using Element = dom::Element; using HTMLBRElement = dom::HTMLBRElement; using Text = dom::Text;
enumclass WSType : uint8_t {
NotInitialized, // Could be the DOM tree is broken as like crash tests.
UnexpectedError, // The scanner cannot work in uncomposed tree, but tried to scan in it.
InUncomposedDoc, // The run is maybe collapsible white-spaces at start of a hard line.
LeadingWhiteSpaces, // The run is maybe collapsible white-spaces at end of a hard line.
TrailingWhiteSpaces, // Collapsible, but visible white-spaces.
CollapsibleWhiteSpaces, // Visible characters except collapsible white-spaces.
NonCollapsibleCharacters, // Special content such as `<img>`, etc.
SpecialContent, // <br> element.
BRElement, // A linefeed which is preformatted.
PreformattedLineBreak, // Other block's boundary (child block of current block, maybe).
OtherBlockBoundary, // Current block's boundary.
CurrentBlockBoundary, // Inline editing host boundary.
InlineEditingHostBoundary,
};
friend std::ostream& operator<<(std::ostream& aStream, const WSType& aType) { switch (aType) { case WSType::NotInitialized: return aStream << "WSType::NotInitialized"; case WSType::UnexpectedError: return aStream << "WSType::UnexpectedError"; case WSType::InUncomposedDoc: return aStream << "WSType::InUncomposedDoc"; case WSType::LeadingWhiteSpaces: return aStream << "WSType::LeadingWhiteSpaces"; case WSType::TrailingWhiteSpaces: return aStream << "WSType::TrailingWhiteSpaces"; case WSType::CollapsibleWhiteSpaces: return aStream << "WSType::CollapsibleWhiteSpaces"; case WSType::NonCollapsibleCharacters: return aStream << "WSType::NonCollapsibleCharacters"; case WSType::SpecialContent: return aStream << "WSType::SpecialContent"; case WSType::BRElement: return aStream << "WSType::BRElement"; case WSType::PreformattedLineBreak: return aStream << "WSType::PreformattedLineBreak"; case WSType::OtherBlockBoundary: return aStream << "WSType::OtherBlockBoundary"; case WSType::CurrentBlockBoundary: return aStream << "WSType::CurrentBlockBoundary"; case WSType::InlineEditingHostBoundary: return aStream << "WSType::InlineEditingHostBoundary";
} return aStream << "";
}
/** * GetContent() returns found visible and editable content/element. * See MOZ_ASSERT_IF()s in AssertIfInvalidData() for the detail.
*/
nsIContent* GetContent() const { return mContent; }
/** * Offset_Deprecated() returns meaningful value only when * InVisibleOrCollapsibleCharacters() returns true or the scanner reached to * start or end of its scanning range and that is same as start or end * container which are specified when the scanner is initialized. If it's * result of scanning backward, this offset means the point of the found * point. Otherwise, i.e., scanning forward, this offset means next point * of the found point. E.g., if it reaches a collapsible white-space, this * offset is at the first non-collapsible character after it.
*/
MOZ_NEVER_INLINE_DEBUG uint32_t Offset_Deprecated() const {
NS_ASSERTION(mOffset.isSome(), "Retrieved non-meaningful offset"); return mOffset.valueOr(0);
}
/** * Point_Deprecated() returns the position in found visible node or reached * block boundary. So, this returns meaningful point only when * Offset_Deprecated() returns meaningful value.
*/ template <typename EditorDOMPointType>
EditorDOMPointType Point_Deprecated() const {
NS_ASSERTION(mOffset.isSome(), "Retrieved non-meaningful point"); return EditorDOMPointType(mContent, mOffset.valueOr(0));
}
/** * PointAtReachedContent() returns the position of found visible content or * reached block element.
*/ template <typename EditorDOMPointType>
EditorDOMPointType PointAtReachedContent() const {
MOZ_ASSERT(mContent); switch (mReason) { case WSType::CollapsibleWhiteSpaces: case WSType::NonCollapsibleCharacters: case WSType::PreformattedLineBreak:
MOZ_DIAGNOSTIC_ASSERT(mOffset.isSome()); return mDirection == ScanDirection::Forward
? EditorDOMPointType(mContent, mOffset.valueOr(0))
: EditorDOMPointType(mContent,
std::max(mOffset.valueOr(1), 1u) - 1); default: return EditorDOMPointType(mContent);
}
}
/** * PointAfterReachedContent() returns the next position of found visible * content or reached block element.
*/ template <typename EditorDOMPointType>
EditorDOMPointType PointAfterReachedContent() const {
MOZ_ASSERT(mContent); return PointAtReachedContent<EditorDOMPointType>()
.template NextPointOrAfterContainer<EditorDOMPointType>();
}
/** * The scanner reached <img> or something which is inline and is not a * container.
*/ bool ReachedSpecialContent() const { return mReason == WSType::SpecialContent;
}
/** * The point is in visible characters or collapsible white-spaces.
*/ bool InVisibleOrCollapsibleCharacters() const { return mReason == WSType::CollapsibleWhiteSpaces ||
mReason == WSType::NonCollapsibleCharacters;
}
/** * The point is in collapsible white-spaces.
*/ bool InCollapsibleWhiteSpaces() const { return mReason == WSType::CollapsibleWhiteSpaces;
}
/** * The point is in visible non-collapsible characters.
*/ bool InNonCollapsibleCharacters() const { return mReason == WSType::NonCollapsibleCharacters;
}
// ScanInclusiveNextVisibleNodeOrBlockBoundaryFrom() returns the first visible // node at or after aPoint. If there is no visible nodes after aPoint, // returns topmost editable inline ancestor at end of current block. See // comments around WSScanResult for the detail. When you reach a character, // this returns WSScanResult both whose Point_Deprecated() and // PointAtReachedContent() return the found character position. template <typename PT, typename CT>
WSScanResult ScanInclusiveNextVisibleNodeOrBlockBoundaryFrom( const EditorDOMPointBase<PT, CT>& aPoint) const; template <typename PT, typename CT> static WSScanResult ScanInclusiveNextVisibleNodeOrBlockBoundary(
Scan aScanMode, const EditorDOMPointBase<PT, CT>& aPoint,
BlockInlineCheck aBlockInlineCheck, const Element* aAncestorLimiter = nullptr) { return WSRunScanner(aScanMode, aPoint, aBlockInlineCheck, aAncestorLimiter)
.ScanInclusiveNextVisibleNodeOrBlockBoundaryFrom(aPoint);
}
// ScanPreviousVisibleNodeOrBlockBoundaryFrom() returns the first visible node // before aPoint. If there is no visible nodes before aPoint, returns topmost // editable inline ancestor at start of current block. See comments around // WSScanResult for the detail. When you reach a character, this returns // WSScanResult whose Point_Deprecated() returns next point of the found // character and PointAtReachedContent() returns the point at found character. template <typename PT, typename CT>
WSScanResult ScanPreviousVisibleNodeOrBlockBoundaryFrom( const EditorDOMPointBase<PT, CT>& aPoint) const; template <typename PT, typename CT> static WSScanResult ScanPreviousVisibleNodeOrBlockBoundary(
Scan aScanMode, const EditorDOMPointBase<PT, CT>& aPoint,
BlockInlineCheck aBlockInlineCheck, const Element* aAncestorLimiter = nullptr) { return WSRunScanner(aScanMode, aPoint, aBlockInlineCheck, aAncestorLimiter)
.ScanPreviousVisibleNodeOrBlockBoundaryFrom(aPoint);
}
/** * Return a point in a `Text` node which is at current character or next * character if aPoint does not points a character or end of a `Text` node.
*/ template <typename EditorDOMPointType, typename PT, typename CT> static EditorDOMPointType GetInclusiveNextCharPoint(
Scan aScanMode, const EditorDOMPointBase<PT, CT>& aPoint,
BlockInlineCheck aBlockInlineCheck, const Element* aAncestorLimiter = nullptr) { if (aPoint.IsInTextNode() && !aPoint.IsEndOfContainer() &&
(aScanMode != Scan::EditableNodes ||
HTMLEditUtils::IsSimplyEditableNode(
*aPoint.template ContainerAs<Text>()))) { return EditorDOMPointType(aPoint.template ContainerAs<Text>(),
aPoint.Offset());
} return WSRunScanner(aScanMode, aPoint, aBlockInlineCheck, aAncestorLimiter)
.GetInclusiveNextCharPoint<EditorDOMPointType>(aPoint);
}
/** * Return a point in a `Text` node which is before aPoint.
*/ template <typename EditorDOMPointType, typename PT, typename CT> static EditorDOMPointType GetPreviousCharPoint(
Scan aScanMode, const EditorDOMPointBase<PT, CT>& aPoint,
BlockInlineCheck aBlockInlineCheck, const Element* aAncestorLimiter = nullptr) { if (aPoint.IsInTextNode() && !aPoint.IsStartOfContainer() &&
(aScanMode != Scan::EditableNodes ||
HTMLEditUtils::IsSimplyEditableNode(
*aPoint.template ContainerAs<Text>()))) { return EditorDOMPointType(aPoint.template ContainerAs<Text>(),
aPoint.Offset() - 1);
} return WSRunScanner(aScanMode, aPoint, aBlockInlineCheck, aAncestorLimiter)
.GetPreviousCharPoint<EditorDOMPointType>(aPoint);
}
/** * Scan aTextNode from end or start to find last or first visible things. * I.e., this returns a point immediately before or after invisible * white-spaces of aTextNode if aTextNode ends or begins with some invisible * white-spaces. * Note that the result may not be in different text node if aTextNode has * only invisible white-spaces and there is previous or next text node.
*/ template <typename EditorDOMPointType> static EditorDOMPointType GetAfterLastVisiblePoint(
Scan aScanMode, Text& aTextNode, const Element* aAncestorLimiter = nullptr); template <typename EditorDOMPointType> static EditorDOMPointType GetFirstVisiblePoint(
Scan aScanMode, Text& aTextNode, const Element* aAncestorLimiter = nullptr);
/** * GetRangeInTextNodesToForwardDeleteFrom() returns the range to remove * text when caret is at aPoint.
*/ static Result<EditorDOMRangeInTexts, nsresult>
GetRangeInTextNodesToForwardDeleteFrom(
Scan aScanMode, const EditorDOMPoint& aPoint, const Element* aAncestorLimiter = nullptr);
/** * GetRangeInTextNodesToBackspaceFrom() returns the range to remove text * when caret is at aPoint.
*/ static Result<EditorDOMRangeInTexts, nsresult>
GetRangeInTextNodesToBackspaceFrom(Scan aScanMode, const EditorDOMPoint& aPoint, const Element* aAncestorLimiter = nullptr);
/** * GetRangesForDeletingAtomicContent() returns the range to delete * aAtomicContent. If it's followed by invisible white-spaces, they will * be included into the range.
*/ static EditorDOMRange GetRangesForDeletingAtomicContent(
Scan aScanMode, const nsIContent& aAtomicContent, const Element* aAncestorLimiter = nullptr);
/** * GetRangeForDeleteBlockElementBoundaries() returns a range starting from end * of aLeftBlockElement to start of aRightBlockElement and extend invisible * white-spaces around them. * * @param aLeftBlockElement The block element which will be joined with * aRightBlockElement. * @param aRightBlockElement The block element which will be joined with * aLeftBlockElement. This must be an element * after aLeftBlockElement. * @param aPointContainingTheOtherBlock * When aRightBlockElement is an ancestor of * aLeftBlockElement, this must be set and the * container must be aRightBlockElement. * When aLeftBlockElement is an ancestor of * aRightBlockElement, this must be set and the * container must be aLeftBlockElement. * Otherwise, must not be set.
*/ static EditorDOMRange GetRangeForDeletingBlockElementBoundaries(
Scan aScanMode, const Element& aLeftBlockElement, const Element& aRightBlockElement, const EditorDOMPoint& aPointContainingTheOtherBlock, const Element* aAncestorLimiter = nullptr);
/** * ShrinkRangeIfStartsFromOrEndsAfterAtomicContent() may shrink aRange if it * starts and/or ends with an atomic content, but the range boundary * is in adjacent text nodes. Returns true if this modifies the range.
*/ static Result<bool, nsresult> ShrinkRangeIfStartsFromOrEndsAfterAtomicContent(
Scan aScanMode, nsRange& aRange, const Element* aAncestorLimiter = nullptr);
/** * GetRangeContainingInvisibleWhiteSpacesAtRangeBoundaries() returns * extended range if range boundaries of aRange are in invisible white-spaces.
*/ static EditorDOMRange GetRangeContainingInvisibleWhiteSpacesAtRangeBoundaries(
Scan aScanMode, const EditorDOMRange& aRange, const Element* aAncestorLimiter = nullptr);
/** * GetPrecedingBRElementUnlessVisibleContentFound() scans a `<br>` element * backward, but stops scanning it if the scanner finds visible character * or something. In other words, this method ignores only invisible * white-spaces between `<br>` element and aPoint.
*/ template <typename EditorDOMPointType>
MOZ_NEVER_INLINE_DEBUG static HTMLBRElement*
GetPrecedingBRElementUnlessVisibleContentFound(
Scan aScanMode, const EditorDOMPointType& aPoint,
BlockInlineCheck aBlockInlineCheck, const Element* aAncestorLimiter = nullptr) {
MOZ_ASSERT(aPoint.IsSetAndValid()); // XXX This method behaves differently even in similar point. // If aPoint is in a text node following `<br>` element, reaches the // `<br>` element when all characters between the `<br>` and // aPoint are ASCII whitespaces. // But if aPoint is not in a text node, e.g., at start of an inline // element which is immediately after a `<br>` element, returns the // `<br>` element even if there is no invisible white-spaces. if (aPoint.IsStartOfContainer()) { return nullptr;
} // TODO: Scan for end boundary is redundant in this case, we should optimize // it.
TextFragmentData textFragmentData(aScanMode, aPoint, aBlockInlineCheck,
aAncestorLimiter); return textFragmentData.StartsFromBRElement()
? textFragmentData.StartReasonBRElementPtr()
: nullptr;
}
/** * GetStartReasonContent() and GetEndReasonContent() return a node which * was found by scanning from mScanStartPoint backward or forward. If there * was white-spaces or text from the point, returns the text node. Otherwise, * returns an element which is explained by the following methods. Note that * when the reason is WSType::CurrentBlockBoundary, In most cases, it's * current block element which is editable, but also may be non-element and/or * non-editable. See MOZ_ASSERT_IF()s in WSScanResult::AssertIfInvalidData() * for the detail.
*/
nsIContent* GetStartReasonContent() const { return TextFragmentDataAtStartRef().GetStartReasonContent();
}
nsIContent* GetEndReasonContent() const { return TextFragmentDataAtStartRef().GetEndReasonContent();
}
private: // Initializers should be accessible only from `TextFragmentData`. friendclass WSRunScanner::TextFragmentData;
VisibleWhiteSpacesData()
: mLeftWSType(WSType::NotInitialized),
mRightWSType(WSType::NotInitialized) {}
using PointPosition = VisibleWhiteSpacesData::PointPosition;
/** * Return aPoint if it points a character in a `Text` node, or start of next * `Text` node otherwise. * FYI: For the performance, this does not check whether given container is * not after mStart.mReasonContent or not.
*/ template <typename EditorDOMPointType, typename PT, typename CT>
EditorDOMPointType GetInclusiveNextCharPoint( const EditorDOMPointBase<PT, CT>& aPoint) const { return TextFragmentDataAtStartRef()
.GetInclusiveNextCharPoint<EditorDOMPointType>(
aPoint, ShouldIgnoreNonEditableSiblingsOrDescendants(mScanMode));
}
/** * Return the previous editable point in a `Text` node. Note that this * returns the last character point when it meets non-empty text node, * otherwise, returns a point in an empty text node. * FYI: For the performance, this does not check whether given container is * not before mEnd.mReasonContent or not.
*/ template <typename EditorDOMPointType, typename PT, typename CT>
EditorDOMPointType GetPreviousCharPoint( const EditorDOMPointBase<PT, CT>& aPoint) const { return TextFragmentDataAtStartRef()
.GetPreviousCharPoint<EditorDOMPointType>(
aPoint, ShouldIgnoreNonEditableSiblingsOrDescendants(mScanMode));
}
/** * GetEndOfCollapsibleASCIIWhiteSpaces() returns the next visible char * (meaning a character except ASCII white-spaces) point or end of last text * node scanning from aPointAtASCIIWhiteSpace. * Note that this may return different text node from the container of * aPointAtASCIIWhiteSpace.
*/ template <typename EditorDOMPointType>
EditorDOMPointType GetEndOfCollapsibleASCIIWhiteSpaces( const EditorDOMPointInText& aPointAtASCIIWhiteSpace,
nsIEditor::EDirection aDirectionToDelete) const {
MOZ_ASSERT(aDirectionToDelete == nsIEditor::eNone ||
aDirectionToDelete == nsIEditor::eNext ||
aDirectionToDelete == nsIEditor::ePrevious); return TextFragmentDataAtStartRef()
.GetEndOfCollapsibleASCIIWhiteSpaces<EditorDOMPointType>(
aPointAtASCIIWhiteSpace, aDirectionToDelete);
}
/** * GetFirstASCIIWhiteSpacePointCollapsedTo() returns the first ASCII * white-space which aPointAtASCIIWhiteSpace belongs to. In other words, * the white-space at aPointAtASCIIWhiteSpace should be collapsed into * the result. * Note that this may return different text node from the container of * aPointAtASCIIWhiteSpace.
*/ template <typename EditorDOMPointType>
EditorDOMPointType GetFirstASCIIWhiteSpacePointCollapsedTo( const EditorDOMPointInText& aPointAtASCIIWhiteSpace,
nsIEditor::EDirection aDirectionToDelete) const {
MOZ_ASSERT(aDirectionToDelete == nsIEditor::eNone ||
aDirectionToDelete == nsIEditor::eNext ||
aDirectionToDelete == nsIEditor::ePrevious); return TextFragmentDataAtStartRef()
.GetFirstASCIIWhiteSpacePointCollapsedTo<EditorDOMPointType>(
aPointAtASCIIWhiteSpace, aDirectionToDelete);
}
/** * TextFragmentData stores the information of white-space sequence which * contains `aPoint` of the constructor.
*/ class MOZ_STACK_CLASS TextFragmentData final { private: class NoBreakingSpaceData; class MOZ_STACK_CLASS BoundaryData final { public: using NoBreakingSpaceData =
WSRunScanner::TextFragmentData::NoBreakingSpaceData;
/** * ScanCollapsibleWhiteSpaceStartFrom() returns start boundary data of * white-spaces containing aPoint. When aPoint is in a text node and * points a non-white-space character or the text node is preformatted, * this returns the data at aPoint. * * @param aPoint Scan start point. * @param aNBSPData Optional. If set, this recodes first and last * NBSP positions.
*/ template <typename EditorDOMPointType> static BoundaryData ScanCollapsibleWhiteSpaceStartFrom(
Scan aScanMode, const EditorDOMPointType& aPoint,
NoBreakingSpaceData* aNBSPData, BlockInlineCheck aBlockInlineCheck,
StopAtNonEditableNode aStopAtNonEditableNode, const Element& aAncestorLimiter);
/** * ScanCollapsibleWhiteSpaceEndFrom() returns end boundary data of * white-spaces containing aPoint. When aPoint is in a text node and * points a non-white-space character or the text node is preformatted, * this returns the data at aPoint. * * @param aPoint Scan start point. * @param aNBSPData Optional. If set, this recodes first and last * NBSP positions.
*/ template <typename EditorDOMPointType> static BoundaryData ScanCollapsibleWhiteSpaceEndFrom(
Scan aScanMode, const EditorDOMPointType& aPoint,
NoBreakingSpaceData* aNBSPData, BlockInlineCheck aBlockInlineCheck,
StopAtNonEditableNode aStopAtNonEditableNode, const Element& aAncestorLimiter);
private: /** * Helper methods of ScanCollapsibleWhiteSpaceStartFrom() and * ScanCollapsibleWhiteSpaceEndFrom() when they need to scan in a text * node.
*/ template <typename EditorDOMPointType> static Maybe<BoundaryData> ScanCollapsibleWhiteSpaceStartInTextNode( const EditorDOMPointType& aPoint, NoBreakingSpaceData* aNBSPData,
BlockInlineCheck aBlockInlineCheck); template <typename EditorDOMPointType> static Maybe<BoundaryData> ScanCollapsibleWhiteSpaceEndInTextNode( const EditorDOMPointType& aPoint, NoBreakingSpaceData* aNBSPData,
BlockInlineCheck aBlockInlineCheck);
nsCOMPtr<nsIContent> mReasonContent;
EditorDOMPoint mPoint; // Must be one of WSType::NotInitialized, // WSType::NonCollapsibleCharacters, WSType::SpecialContent, // WSType::BRElement, WSType::CurrentBlockBoundary, // WSType::OtherBlockBoundary or WSType::InlineEditingHostBoundary.
WSType mReason = WSType::NotInitialized;
};
/** * If aScanMode is Scan::EditableNodes and aPoint is in an editable node, * this scans only in the editing host. Therefore, it's same as that * aAncestorLimiter is specified to the editing host.
*/ template <typename EditorDOMPointType>
TextFragmentData(Scan aScanMode, const EditorDOMPointType& aPoint,
BlockInlineCheck aBlockInlineCheck, const Element* aAncestorLimiter = nullptr);
/** * Return inclusive next point in inclusive next `Text` node from aPoint. * So, it may be in a collapsed white-space or invisible white-spaces.
*/ template <typename EditorDOMPointType, typename PT, typename CT>
[[nodiscard]] static EditorDOMPointType GetInclusiveNextCharPoint( const EditorDOMPointBase<PT, CT>& aPoint,
BlockInlineCheck aBlockInlineCheck,
IgnoreNonEditableNodes aIgnoreNonEditableNodes, const nsIContent* aFollowingLimiterContent = nullptr);
/** * Return previous point in inclusive previous `Text` node from aPoint. * So, it may be in a collapsed white-space or invisible white-spaces.
*/ template <typename EditorDOMPointType, typename PT, typename CT>
[[nodiscard]] static EditorDOMPointType GetPreviousCharPoint( const EditorDOMPointBase<PT, CT>& aPoint,
BlockInlineCheck aBlockInlineCheck,
IgnoreNonEditableNodes aIgnoreNonEditableNodes, const nsIContent* aPrecedingLimiterContent = nullptr);
/** * Return end of current collapsible ASCII white-spaces. * * @param aPointAtASCIIWhiteSpace Must be in a sequence of collapsible * ASCII white-spaces. * @param aDirectionToDelete The direction to delete.
*/ template <typename EditorDOMPointType>
[[nodiscard]] static EditorDOMPointType GetEndOfCollapsibleASCIIWhiteSpaces( const EditorDOMPointInText& aPointAtASCIIWhiteSpace,
nsIEditor::EDirection aDirectionToDelete,
BlockInlineCheck aBlockInlineCheck,
IgnoreNonEditableNodes aIgnoreNonEditableNodes, const nsIContent* aFollowingLimiterContent = nullptr);
/** * GetNonCollapsedRangeInTexts() returns non-empty range in texts which * is the largest range in aRange if there is some text nodes.
*/
EditorDOMRangeInTexts GetNonCollapsedRangeInTexts( const EditorDOMRange& aRange) const;
/** * InvisibleLeadingWhiteSpaceRangeRef() retruns reference to two DOM points, * start of the line and first visible point or end of the hard line. When * this returns non-positioned range or positioned but collapsed range, * there is no invisible leading white-spaces. * Note that if there are only invisible white-spaces in a hard line, * this returns all of the white-spaces.
*/ const EditorDOMRange& InvisibleLeadingWhiteSpaceRangeRef() const;
/** * InvisibleTrailingWhiteSpaceRangeRef() returns reference to two DOM * points, first invisible white-space and end of the hard line. When this * returns non-positioned range or positioned but collapsed range, * there is no invisible trailing white-spaces. * Note that if there are only invisible white-spaces in a hard line, * this returns all of the white-spaces.
*/ const EditorDOMRange& InvisibleTrailingWhiteSpaceRangeRef() const;
/** * GetNewInvisibleLeadingWhiteSpaceRangeIfSplittingAt() returns new * invisible leading white-space range which should be removed if * splitting invisible white-space sequence at aPointToSplit creates * new invisible leading white-spaces in the new line. * Note that the result may be collapsed range if the point is around * invisible white-spaces.
*/ template <typename EditorDOMPointType>
EditorDOMRange GetNewInvisibleLeadingWhiteSpaceRangeIfSplittingAt( const EditorDOMPointType& aPointToSplit) const { // If there are invisible trailing white-spaces and some or all of them // become invisible leading white-spaces in the new line, although we // don't need to delete them, but for aesthetically and backward // compatibility, we should remove them. const EditorDOMRange& trailingWhiteSpaceRange =
InvisibleTrailingWhiteSpaceRangeRef(); // XXX Why don't we check leading white-spaces too? if (!trailingWhiteSpaceRange.IsPositioned()) { return trailingWhiteSpaceRange;
} // If the point is before the trailing white-spaces, the new line won't // start with leading white-spaces. if (aPointToSplit.IsBefore(trailingWhiteSpaceRange.StartRef())) { return EditorDOMRange();
} // If the point is in the trailing white-spaces, the new line may // start with some leading white-spaces. Returning collapsed range // is intentional because the caller may want to know whether the // point is in trailing white-spaces or not. if (aPointToSplit.EqualsOrIsBefore(trailingWhiteSpaceRange.EndRef())) { return EditorDOMRange(trailingWhiteSpaceRange.StartRef(),
aPointToSplit);
} // Otherwise, if the point is after the trailing white-spaces, it may // be just outside of the text node. E.g., end of parent element. // This is possible case but the validation cost is not worthwhile // due to the runtime cost in the worst case. Therefore, we should just // return collapsed range at the end of trailing white-spaces. Then, // callers can know the point is immediately after the trailing // white-spaces. return EditorDOMRange(trailingWhiteSpaceRange.EndRef());
}
/** * GetNewInvisibleTrailingWhiteSpaceRangeIfSplittingAt() returns new * invisible trailing white-space range which should be removed if * splitting invisible white-space sequence at aPointToSplit creates * new invisible trailing white-spaces in the new line. * Note that the result may be collapsed range if the point is around * invisible white-spaces.
*/ template <typename EditorDOMPointType>
EditorDOMRange GetNewInvisibleTrailingWhiteSpaceRangeIfSplittingAt( const EditorDOMPointType& aPointToSplit) const { // If there are invisible leading white-spaces and some or all of them // become end of current line, they will become visible. Therefore, we // need to delete the invisible leading white-spaces before insertion // point. const EditorDOMRange& leadingWhiteSpaceRange =
InvisibleLeadingWhiteSpaceRangeRef(); if (!leadingWhiteSpaceRange.IsPositioned()) { return leadingWhiteSpaceRange;
} // If the point equals or is after the leading white-spaces, the line // will end without trailing white-spaces. if (leadingWhiteSpaceRange.EndRef().IsBefore(aPointToSplit)) { return EditorDOMRange();
} // If the point is in the leading white-spaces, the line may // end with some trailing white-spaces. Returning collapsed range // is intentional because the caller may want to know whether the // point is in leading white-spaces or not. if (leadingWhiteSpaceRange.StartRef().EqualsOrIsBefore(aPointToSplit)) { return EditorDOMRange(aPointToSplit, leadingWhiteSpaceRange.EndRef());
} // Otherwise, if the point is before the leading white-spaces, it may // be just outside of the text node. E.g., start of parent element. // This is possible case but the validation cost is not worthwhile // due to the runtime cost in the worst case. Therefore, we should // just return collapsed range at start of the leading white-spaces. // Then, callers can know the point is immediately before the leading // white-spaces. return EditorDOMRange(leadingWhiteSpaceRange.StartRef());
}
/** * FollowingContentMayBecomeFirstVisibleContent() returns true if some * content may be first visible content after removing content after aPoint. * Note that it's completely broken what this does. Don't use this method * with new code.
*/ template <typename EditorDOMPointType> bool FollowingContentMayBecomeFirstVisibleContent( const EditorDOMPointType& aPoint) const {
MOZ_ASSERT(aPoint.IsSetAndValid()); if (!mStart.IsHardLineBreak() && !mStart.IsInlineEditingHostBoundary()) { returnfalse;
} // If the point is before start of text fragment, that means that the // point may be at the block boundary or inline element boundary. if (aPoint.EqualsOrIsBefore(mStart.PointRef())) { returntrue;
} // VisibleWhiteSpacesData is marked as start of line only when it // represents leading white-spaces. const EditorDOMRange& leadingWhiteSpaceRange =
InvisibleLeadingWhiteSpaceRangeRef(); if (!leadingWhiteSpaceRange.StartRef().IsSet()) { returnfalse;
} if (aPoint.EqualsOrIsBefore(leadingWhiteSpaceRange.StartRef())) { returntrue;
} if (!leadingWhiteSpaceRange.EndRef().IsSet()) { returnfalse;
} return aPoint.EqualsOrIsBefore(leadingWhiteSpaceRange.EndRef());
}
/** * PrecedingContentMayBecomeInvisible() returns true if end of preceding * content is collapsed (when ends with an ASCII white-space). * Note that it's completely broken what this does. Don't use this method * with new code.
*/ template <typename EditorDOMPointType> bool PrecedingContentMayBecomeInvisible( const EditorDOMPointType& aPoint) const {
MOZ_ASSERT(aPoint.IsSetAndValid()); // If this fragment is ends by block boundary, always the caller needs // additional check. if (mEnd.IsBlockBoundary() || mEnd.IsInlineEditingHostBoundary()) { returntrue;
}
// If the point is in visible white-spaces and ends with an ASCII // white-space, it may be collapsed even if it won't be end of line. const VisibleWhiteSpacesData& visibleWhiteSpaces =
VisibleWhiteSpacesDataRef(); if (!visibleWhiteSpaces.IsInitialized()) { returnfalse;
} // XXX Odd case, but keep traditional behavior of `FindNearestRun()`. if (!visibleWhiteSpaces.StartRef().IsSet()) { returntrue;
} if (!visibleWhiteSpaces.StartRef().EqualsOrIsBefore(aPoint)) { returnfalse;
} // XXX Odd case, but keep traditional behavior of `FindNearestRun()`. if (visibleWhiteSpaces.EndsByTrailingWhiteSpaces()) { returntrue;
} // XXX Must be a bug. This claims that the caller needs additional // check even when there is no white-spaces. if (visibleWhiteSpaces.StartRef() == visibleWhiteSpaces.EndRef()) { returntrue;
} return aPoint.IsBefore(visibleWhiteSpaces.EndRef());
}
/** * GetPreviousNBSPPointIfNeedToReplaceWithASCIIWhiteSpace() may return an * NBSP point which should be replaced with an ASCII white-space when we're * inserting text into aPointToInsert. Note that this is a helper method for * the traditional white-space normalizer. Don't use this with the new * white-space normalizer. * Must be called only when VisibleWhiteSpacesDataRef() returns initialized * instance and previous character of aPointToInsert is in the range.
*/
EditorDOMPointInText GetPreviousNBSPPointIfNeedToReplaceWithASCIIWhiteSpace( const EditorDOMPoint& aPointToInsert) const;
/** * GetInclusiveNextNBSPPointIfNeedToReplaceWithASCIIWhiteSpace() may return * an NBSP point which should be replaced with an ASCII white-space when * the caller inserts text into aPointToInsert. * Note that this is a helper method for the traditional white-space * normalizer. Don't use this with the new white-space normalizer. * Must be called only when VisibleWhiteSpacesDataRef() returns initialized * instance, and inclusive next char of aPointToInsert is in the range.
*/
EditorDOMPointInText
GetInclusiveNextNBSPPointIfNeedToReplaceWithASCIIWhiteSpace( const EditorDOMPoint& aPointToInsert) const;
/** * GetReplaceRangeDataAtEndOfDeletionRange() and * GetReplaceRangeDataAtStartOfDeletionRange() return delete range if * end or start of deleting range splits invisible trailing/leading * white-spaces and it may become visible, or return replace range if * end or start of deleting range splits visible white-spaces and it * causes some ASCII white-spaces become invisible unless replacing * with an NBSP.
*/
ReplaceRangeData GetReplaceRangeDataAtEndOfDeletionRange( const TextFragmentData& aTextFragmentDataAtStartToDelete) const;
ReplaceRangeData GetReplaceRangeDataAtStartOfDeletionRange( const TextFragmentData& aTextFragmentDataAtEndToDelete) const;
/** * VisibleWhiteSpacesDataRef() returns reference to visible white-spaces * data. That is zero or more white-spaces which are visible. * Note that when there is no visible content, it's not initialized. * Otherwise, even if there is no white-spaces, it's initialized and * the range is collapsed in such case.
*/ const VisibleWhiteSpacesData& VisibleWhiteSpacesDataRef() const;
// The node passed to our constructor.
EditorDOMPoint mScanStartPoint; // Together, the above represent the point at which we are building up ws // info.
private: /** * ComputeRangeInTextNodesContainingInvisibleWhiteSpaces() returns range * containing invisible white-spaces if deleting between aStart and aEnd * causes them become visible. * * @param aStart TextFragmentData at start of deleting range. * This must be initialized with DOM point in a text node. * @param aEnd TextFragmentData at end of deleting range. * This must be initialized with DOM point in a text node.
*/ static EditorDOMRangeInTexts
ComputeRangeInTextNodesContainingInvisibleWhiteSpaces( const TextFragmentData& aStart, const TextFragmentData& aEnd);
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.