summaryrefslogtreecommitdiffstats
path: root/editor/libeditor/WSRunObject.h
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-12 05:43:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-12 05:43:14 +0000
commit8dd16259287f58f9273002717ec4d27e97127719 (patch)
tree3863e62a53829a84037444beab3abd4ed9dfc7d0 /editor/libeditor/WSRunObject.h
parentReleasing progress-linux version 126.0.1-1~progress7.99u1. (diff)
downloadfirefox-8dd16259287f58f9273002717ec4d27e97127719.tar.xz
firefox-8dd16259287f58f9273002717ec4d27e97127719.zip
Merging upstream version 127.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--editor/libeditor/WSRunObject.h237
1 files changed, 155 insertions, 82 deletions
diff --git a/editor/libeditor/WSRunObject.h b/editor/libeditor/WSRunObject.h
index 9328b24eb2..bbc6a078d9 100644
--- a/editor/libeditor/WSRunObject.h
+++ b/editor/libeditor/WSRunObject.h
@@ -41,6 +41,8 @@ class MOZ_STACK_CLASS WSScanResult final {
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.
@@ -59,6 +61,8 @@ class MOZ_STACK_CLASS WSScanResult final {
OtherBlockBoundary,
// Current block's boundary.
CurrentBlockBoundary,
+ // Inline editing host boundary.
+ InlineEditingHostBoundary,
};
friend std::ostream& operator<<(std::ostream& aStream, const WSType& aType) {
@@ -67,6 +71,8 @@ class MOZ_STACK_CLASS WSScanResult final {
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:
@@ -85,90 +91,111 @@ class MOZ_STACK_CLASS WSScanResult final {
return aStream << "WSType::OtherBlockBoundary";
case WSType::CurrentBlockBoundary:
return aStream << "WSType::CurrentBlockBoundary";
+ case WSType::InlineEditingHostBoundary:
+ return aStream << "WSType::InlineEditingHostBoundary";
}
return aStream << "<Illegal value>";
}
friend class WSRunScanner; // Because of WSType.
+ explicit WSScanResult(WSType aReason) : mReason(aReason) {
+ MOZ_ASSERT(mReason == WSType::UnexpectedError ||
+ mReason == WSType::NotInitialized);
+ }
+
public:
WSScanResult() = delete;
- MOZ_NEVER_INLINE_DEBUG WSScanResult(nsIContent* aContent, WSType aReason,
+ enum class ScanDirection : bool { Backward, Forward };
+ MOZ_NEVER_INLINE_DEBUG WSScanResult(ScanDirection aScanDirection,
+ nsIContent& aContent, WSType aReason,
BlockInlineCheck aBlockInlineCheck)
- : mContent(aContent), mReason(aReason) {
+ : mContent(&aContent), mReason(aReason), mDirection(aScanDirection) {
+ MOZ_ASSERT(aReason != WSType::CollapsibleWhiteSpaces &&
+ aReason != WSType::NonCollapsibleCharacters &&
+ aReason != WSType::PreformattedLineBreak);
AssertIfInvalidData(aBlockInlineCheck);
}
- MOZ_NEVER_INLINE_DEBUG WSScanResult(const EditorDOMPoint& aPoint,
+ MOZ_NEVER_INLINE_DEBUG WSScanResult(ScanDirection aScanDirection,
+ const EditorDOMPoint& aPoint,
WSType aReason,
BlockInlineCheck aBlockInlineCheck)
: mContent(aPoint.GetContainerAs<nsIContent>()),
mOffset(Some(aPoint.Offset())),
- mReason(aReason) {
+ mReason(aReason),
+ mDirection(aScanDirection) {
AssertIfInvalidData(aBlockInlineCheck);
}
+ static WSScanResult Error() { return WSScanResult(WSType::UnexpectedError); }
+
MOZ_NEVER_INLINE_DEBUG void AssertIfInvalidData(
BlockInlineCheck aBlockInlineCheck) const {
#ifdef DEBUG
MOZ_ASSERT(mReason == WSType::UnexpectedError ||
+ mReason == WSType::InUncomposedDoc ||
mReason == WSType::NonCollapsibleCharacters ||
mReason == WSType::CollapsibleWhiteSpaces ||
mReason == WSType::BRElement ||
mReason == WSType::PreformattedLineBreak ||
mReason == WSType::SpecialContent ||
mReason == WSType::CurrentBlockBoundary ||
- mReason == WSType::OtherBlockBoundary);
+ mReason == WSType::OtherBlockBoundary ||
+ mReason == WSType::InlineEditingHostBoundary);
MOZ_ASSERT_IF(mReason == WSType::UnexpectedError, !mContent);
+ MOZ_ASSERT_IF(mReason != WSType::UnexpectedError, mContent);
+ MOZ_ASSERT_IF(mReason == WSType::InUncomposedDoc,
+ !mContent->IsInComposedDoc());
+ MOZ_ASSERT_IF(mContent && !mContent->IsInComposedDoc(),
+ mReason == WSType::InUncomposedDoc);
+ MOZ_ASSERT_IF(mReason == WSType::NonCollapsibleCharacters ||
+ mReason == WSType::CollapsibleWhiteSpaces ||
+ mReason == WSType::PreformattedLineBreak,
+ mContent->IsText());
+ MOZ_ASSERT_IF(mReason == WSType::NonCollapsibleCharacters ||
+ mReason == WSType::CollapsibleWhiteSpaces ||
+ mReason == WSType::PreformattedLineBreak,
+ mOffset.isSome());
MOZ_ASSERT_IF(mReason == WSType::NonCollapsibleCharacters ||
- mReason == WSType::CollapsibleWhiteSpaces,
- mContent && mContent->IsText());
+ mReason == WSType::CollapsibleWhiteSpaces ||
+ mReason == WSType::PreformattedLineBreak,
+ mContent->AsText()->TextDataLength() > 0);
+ MOZ_ASSERT_IF(mDirection == ScanDirection::Backward &&
+ (mReason == WSType::NonCollapsibleCharacters ||
+ mReason == WSType::CollapsibleWhiteSpaces ||
+ mReason == WSType::PreformattedLineBreak),
+ *mOffset > 0);
+ MOZ_ASSERT_IF(mDirection == ScanDirection::Forward &&
+ (mReason == WSType::NonCollapsibleCharacters ||
+ mReason == WSType::CollapsibleWhiteSpaces ||
+ mReason == WSType::PreformattedLineBreak),
+ *mOffset < mContent->AsText()->TextDataLength());
MOZ_ASSERT_IF(mReason == WSType::BRElement,
- mContent && mContent->IsHTMLElement(nsGkAtoms::br));
+ mContent->IsHTMLElement(nsGkAtoms::br));
MOZ_ASSERT_IF(mReason == WSType::PreformattedLineBreak,
- mContent && mContent->IsText() &&
- EditorUtils::IsNewLinePreformatted(*mContent));
+ EditorUtils::IsNewLinePreformatted(*mContent));
MOZ_ASSERT_IF(
mReason == WSType::SpecialContent,
- mContent &&
- ((mContent->IsText() && !mContent->IsEditable()) ||
- (!mContent->IsHTMLElement(nsGkAtoms::br) &&
- !HTMLEditUtils::IsBlockElement(*mContent, aBlockInlineCheck))));
+ (mContent->IsText() && !mContent->IsEditable()) ||
+ (!mContent->IsHTMLElement(nsGkAtoms::br) &&
+ !HTMLEditUtils::IsBlockElement(*mContent, aBlockInlineCheck)));
MOZ_ASSERT_IF(mReason == WSType::OtherBlockBoundary,
- mContent && HTMLEditUtils::IsBlockElement(*mContent,
- aBlockInlineCheck));
- // If mReason is WSType::CurrentBlockBoundary, mContent can be any content.
- // In most cases, it's current block element which is editable. However, if
- // there is no editable block parent, this is topmost editable inline
- // content. Additionally, if there is no editable content, this is the
- // container start of scanner and is not editable.
- if (mReason == WSType::CurrentBlockBoundary) {
- if (!mContent ||
- // Although not expected that scanning in orphan document fragment,
- // it's okay.
- !mContent->IsInComposedDoc() ||
- // This is what the most preferred result is mContent itself is a
- // block.
- HTMLEditUtils::IsBlockElement(*mContent, aBlockInlineCheck) ||
- // If mContent is not editable, we cannot check whether there is no
- // block ancestor in the limiter which we don't have. Therefore,
- // let's skip the ancestor check.
- !mContent->IsEditable()) {
- return;
- }
- const DebugOnly<Element*> closestAncestorEditableBlockElement =
- HTMLEditUtils::GetAncestorElement(
- *mContent, HTMLEditUtils::ClosestEditableBlockElement,
- aBlockInlineCheck);
- MOZ_ASSERT_IF(
- mReason == WSType::CurrentBlockBoundary,
- // There is no editable block ancestor, it's fine.
- !closestAncestorEditableBlockElement ||
- // If we found an editable block, but mContent can be inline if
- // it's an editing host (root or its parent is not editable).
- !closestAncestorEditableBlockElement->GetParentElement() ||
- !closestAncestorEditableBlockElement->GetParentElement()
- ->IsEditable());
- }
+ HTMLEditUtils::IsBlockElement(*mContent, aBlockInlineCheck));
+ MOZ_ASSERT_IF(mReason == WSType::CurrentBlockBoundary,
+ mContent->IsElement());
+ MOZ_ASSERT_IF(mReason == WSType::CurrentBlockBoundary,
+ mContent->IsEditable());
+ MOZ_ASSERT_IF(mReason == WSType::CurrentBlockBoundary,
+ HTMLEditUtils::IsBlockElement(*mContent, aBlockInlineCheck));
+ MOZ_ASSERT_IF(mReason == WSType::InlineEditingHostBoundary,
+ mContent->IsElement());
+ MOZ_ASSERT_IF(mReason == WSType::InlineEditingHostBoundary,
+ mContent->IsEditable());
+ MOZ_ASSERT_IF(mReason == WSType::InlineEditingHostBoundary,
+ !HTMLEditUtils::IsBlockElement(*mContent, aBlockInlineCheck));
+ MOZ_ASSERT_IF(mReason == WSType::InlineEditingHostBoundary,
+ !mContent->GetParentElement() ||
+ !mContent->GetParentElement()->IsEditable());
#endif // #ifdef DEBUG
}
@@ -187,6 +214,10 @@ class MOZ_STACK_CLASS WSScanResult final {
return mContent && mContent->IsElement();
}
+ [[nodiscard]] bool ContentIsText() const {
+ return mContent && mContent->IsText();
+ }
+
/**
* The following accessors makes it easier to understand each callers.
*/
@@ -204,53 +235,65 @@ class MOZ_STACK_CLASS WSScanResult final {
}
/**
- * Returns true if found or reached content is ediable.
+ * Returns true if found or reached content is editable.
*/
bool IsContentEditable() const { return mContent && mContent->IsEditable(); }
/**
- * Offset() 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 before the found point.
- * Otherwise, i.e., scanning forward, this offset means after the found point.
+ * 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() const {
+ MOZ_NEVER_INLINE_DEBUG uint32_t Offset_Deprecated() const {
NS_ASSERTION(mOffset.isSome(), "Retrieved non-meaningful offset");
return mOffset.valueOr(0);
}
/**
- * Point() and RawPoint() return the position in found visible node or
- * reached block boundary. So, they return meaningful point only when
- * Offset() returns meaningful value.
+ * 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() const {
+ EditorDOMPointType Point_Deprecated() const {
NS_ASSERTION(mOffset.isSome(), "Retrieved non-meaningful point");
return EditorDOMPointType(mContent, mOffset.valueOr(0));
}
/**
- * PointAtContent() and RawPointAtContent() return the position of found
- * visible content or reached block element.
+ * PointAtReachedContent() returns the position of found visible content or
+ * reached block element.
*/
template <typename EditorDOMPointType>
- EditorDOMPointType PointAtContent() const {
+ EditorDOMPointType PointAtReachedContent() const {
MOZ_ASSERT(mContent);
- return EditorDOMPointType(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);
+ }
}
/**
- * PointAfterContent() and RawPointAfterContent() retrun the position after
- * found visible content or reached block element.
+ * PointAfterReachedContent() returns the position after found visible content
+ * or reached block element.
*/
template <typename EditorDOMPointType>
- EditorDOMPointType PointAfterContent() const {
+ EditorDOMPointType PointAfterReachedContent() const {
MOZ_ASSERT(mContent);
- return mContent ? EditorDOMPointType::After(mContent)
- : EditorDOMPointType();
+ return PointAtReachedContent<EditorDOMPointType>().template NextPoint();
}
/**
@@ -337,6 +380,13 @@ class MOZ_STACK_CLASS WSScanResult final {
}
/**
+ * The scanner reached inline editing host boundary.
+ */
+ [[nodiscard]] bool ReachedInlineEditingHostBoundary() const {
+ return mReason == WSType::InlineEditingHostBoundary;
+ }
+
+ /**
* The scanner reached something non-text node.
*/
bool ReachedSomethingNonTextContent() const {
@@ -347,6 +397,7 @@ class MOZ_STACK_CLASS WSScanResult final {
nsCOMPtr<nsIContent> mContent;
Maybe<uint32_t> mOffset;
WSType mReason;
+ ScanDirection mDirection = ScanDirection::Backward;
};
class MOZ_STACK_CLASS WSRunScanner final {
@@ -363,25 +414,29 @@ class MOZ_STACK_CLASS WSRunScanner final {
aBlockInlineCheck),
mBlockInlineCheck(aBlockInlineCheck) {}
- // ScanNextVisibleNodeOrBlockBoundaryForwardFrom() returns the first visible
- // node 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.
+ // 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 ScanNextVisibleNodeOrBlockBoundaryFrom(
+ WSScanResult ScanInclusiveNextVisibleNodeOrBlockBoundaryFrom(
const EditorDOMPointBase<PT, CT>& aPoint) const;
template <typename PT, typename CT>
- static WSScanResult ScanNextVisibleNodeOrBlockBoundary(
+ static WSScanResult ScanInclusiveNextVisibleNodeOrBlockBoundary(
const Element* aEditingHost, const EditorDOMPointBase<PT, CT>& aPoint,
BlockInlineCheck aBlockInlineCheck) {
return WSRunScanner(aEditingHost, aPoint, aBlockInlineCheck)
- .ScanNextVisibleNodeOrBlockBoundaryFrom(aPoint);
+ .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.
+ // 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;
@@ -588,6 +643,9 @@ class MOZ_STACK_CLASS WSRunScanner final {
bool StartsFromBlockBoundary() const {
return TextFragmentDataAtStartRef().StartsFromBlockBoundary();
}
+ bool StartsFromInlineEditingHostBoundary() const {
+ return TextFragmentDataAtStartRef().StartsFromInlineEditingHostBoundary();
+ }
bool StartsFromHardLineBreak() const {
return TextFragmentDataAtStartRef().StartsFromHardLineBreak();
}
@@ -618,6 +676,9 @@ class MOZ_STACK_CLASS WSRunScanner final {
bool EndsByBlockBoundary() const {
return TextFragmentDataAtStartRef().EndsByBlockBoundary();
}
+ bool EndsByInlineEditingHostBoundary() const {
+ return TextFragmentDataAtStartRef().EndsByInlineEditingHostBoundary();
+ }
MOZ_NEVER_INLINE_DEBUG Element* StartReasonOtherBlockElementPtr() const {
return TextFragmentDataAtStartRef().StartReasonOtherBlockElementPtr();
@@ -688,6 +749,9 @@ class MOZ_STACK_CLASS WSRunScanner final {
return mRightWSType == WSType::CurrentBlockBoundary ||
mRightWSType == WSType::OtherBlockBoundary;
}
+ bool EndsByInlineEditingHostBoundary() const {
+ return mRightWSType == WSType::InlineEditingHostBoundary;
+ }
/**
* ComparePoint() compares aPoint with the white-spaces.
@@ -916,6 +980,9 @@ class MOZ_STACK_CLASS WSRunScanner final {
return mReason == WSType::CurrentBlockBoundary ||
mReason == WSType::OtherBlockBoundary;
}
+ bool IsInlineEditingHostBoundary() const {
+ return mReason == WSType::InlineEditingHostBoundary;
+ }
bool IsHardLineBreak() const {
return mReason == WSType::CurrentBlockBoundary ||
mReason == WSType::OtherBlockBoundary ||
@@ -950,8 +1017,8 @@ class MOZ_STACK_CLASS WSRunScanner final {
EditorDOMPoint mPoint;
// Must be one of WSType::NotInitialized,
// WSType::NonCollapsibleCharacters, WSType::SpecialContent,
- // WSType::BRElement, WSType::CurrentBlockBoundary or
- // WSType::OtherBlockBoundary.
+ // WSType::BRElement, WSType::CurrentBlockBoundary,
+ // WSType::OtherBlockBoundary or WSType::InlineEditingHostBoundary.
WSType mReason = WSType::NotInitialized;
};
@@ -1027,6 +1094,9 @@ class MOZ_STACK_CLASS WSRunScanner final {
return mStart.IsOtherBlockBoundary();
}
bool StartsFromBlockBoundary() const { return mStart.IsBlockBoundary(); }
+ bool StartsFromInlineEditingHostBoundary() const {
+ return mStart.IsInlineEditingHostBoundary();
+ }
bool StartsFromHardLineBreak() const { return mStart.IsHardLineBreak(); }
bool EndsByNonCollapsibleCharacters() const {
return mEnd.IsNonCollapsibleCharacters();
@@ -1053,6 +1123,9 @@ class MOZ_STACK_CLASS WSRunScanner final {
}
bool EndsByOtherBlockElement() const { return mEnd.IsOtherBlockBoundary(); }
bool EndsByBlockBoundary() const { return mEnd.IsBlockBoundary(); }
+ bool EndsByInlineEditingHostBoundary() const {
+ return mEnd.IsInlineEditingHostBoundary();
+ }
WSType StartRawReason() const { return mStart.RawReason(); }
WSType EndRawReason() const { return mEnd.RawReason(); }
@@ -1224,7 +1297,7 @@ class MOZ_STACK_CLASS WSRunScanner final {
bool FollowingContentMayBecomeFirstVisibleContent(
const EditorDOMPointType& aPoint) const {
MOZ_ASSERT(aPoint.IsSetAndValid());
- if (!mStart.IsHardLineBreak()) {
+ if (!mStart.IsHardLineBreak() && !mStart.IsInlineEditingHostBoundary()) {
return false;
}
// If the point is before start of text fragment, that means that the
@@ -1260,7 +1333,7 @@ class MOZ_STACK_CLASS WSRunScanner final {
MOZ_ASSERT(aPoint.IsSetAndValid());
// If this fragment is ends by block boundary, always the caller needs
// additional check.
- if (mEnd.IsBlockBoundary()) {
+ if (mEnd.IsBlockBoundary() || mEnd.IsInlineEditingHostBoundary()) {
return true;
}