From 12294302219b470f1f4893992533aa06cef613f1 Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Tue, 9 Mar 2021 23:57:54 +0000 Subject: [PATCH] Bug 1677566 - part 2: Make `HTMLEditUtils` treat a found non-editable element as a leaf node even if it has children r=m_kato Blink treats each non-editable node as an atomic object. E.g., deleting or forward-deleting from next to a non-editable element, it deletes only one non-editable element. Unfortunately, our layout treat adjacent non-editable nodes as a node. Therefore, the adding WPTs do not work, but they are not new regression of this patch. Differential Revision: https://phabricator.services.mozilla.com/D107587 --- editor/libeditor/EditorBase.cpp | 26 ++--- editor/libeditor/HTMLEditSubActionHandler.cpp | 16 ++-- editor/libeditor/HTMLEditUtils.h | 95 +++++++++++++------ editor/libeditor/HTMLEditor.cpp | 9 +- editor/libeditor/HTMLStyleEditor.cpp | 7 +- editor/libeditor/TextEditor.cpp | 5 +- editor/libeditor/WSRunObject.cpp | 31 ++++-- .../meta/editing/run/delete.html.ini | 2 + .../meta/editing/run/forwarddelete.html.ini | 5 +- ...et-ranges-forwarddelete.tentative.html.ini | 3 - .../web-platform/tests/editing/data/delete.js | 5 + .../tests/editing/data/forwarddelete.js | 5 + 12 files changed, 139 insertions(+), 70 deletions(-) diff --git a/editor/libeditor/EditorBase.cpp b/editor/libeditor/EditorBase.cpp index 38d20e21d5f4..f688561fcbf3 100644 --- a/editor/libeditor/EditorBase.cpp +++ b/editor/libeditor/EditorBase.cpp @@ -126,7 +126,8 @@ namespace mozilla { using namespace dom; using namespace widget; -using ChildBlockBoundary = HTMLEditUtils::ChildBlockBoundary; +using LeafNodeType = HTMLEditUtils::LeafNodeType; +using LeafNodeTypes = HTMLEditUtils::LeafNodeTypes; /***************************************************************************** * mozilla::EditorBase @@ -2585,7 +2586,7 @@ nsINode* EditorBase::GetFirstEditableNode(nsINode* aRoot) { EditorType editorType = GetEditorType(); nsIContent* content = - HTMLEditUtils::GetFirstLeafChild(*aRoot, ChildBlockBoundary::TreatAsLeaf); + HTMLEditUtils::GetFirstLeafChild(*aRoot, {LeafNodeType::OnlyLeafNode}); if (content && !EditorUtils::IsEditableContent(*content, editorType)) { content = GetNextEditableNode(*content); } @@ -2830,8 +2831,9 @@ nsIContent* EditorBase::GetPreviousNodeInternal(const EditorRawDOMPoint& aPoint, // unless there isn't one, in which case we are at the end of the node // and want the deep-right child. nsIContent* lastLeafContent = HTMLEditUtils::GetLastLeafChild( - *aPoint.GetContainer(), aNoBlockCrossing ? ChildBlockBoundary::TreatAsLeaf - : ChildBlockBoundary::Ignore); + *aPoint.GetContainer(), + {aNoBlockCrossing ? LeafNodeType::LeafNodeOrChildBlock + : LeafNodeType::OnlyLeafNode}); if (!lastLeafContent) { return nullptr; } @@ -2884,8 +2886,9 @@ nsIContent* EditorBase::GetNextNodeInternal(const EditorRawDOMPoint& aPoint, } nsIContent* firstLeafContent = HTMLEditUtils::GetFirstLeafChild( - *point.GetChild(), aNoBlockCrossing ? ChildBlockBoundary::TreatAsLeaf - : ChildBlockBoundary::Ignore); + *point.GetChild(), + {aNoBlockCrossing ? LeafNodeType::LeafNodeOrChildBlock + : LeafNodeType::OnlyLeafNode}); if (!firstLeafContent) { return point.GetChild(); } @@ -2936,13 +2939,12 @@ nsIContent* EditorBase::FindNextLeafNode(const nsINode* aCurrentNode, // don't look inside prevsib, since it is a block return sibling; } - ChildBlockBoundary childBlockBoundary = - bNoBlockCrossing ? ChildBlockBoundary::TreatAsLeaf - : ChildBlockBoundary::Ignore; + const LeafNodeTypes leafNodeTypes = { + bNoBlockCrossing ? LeafNodeType::LeafNodeOrChildBlock + : LeafNodeType::OnlyLeafNode}; nsIContent* leafContent = - aGoForward - ? HTMLEditUtils::GetFirstLeafChild(*sibling, childBlockBoundary) - : HTMLEditUtils::GetLastLeafChild(*sibling, childBlockBoundary); + aGoForward ? HTMLEditUtils::GetFirstLeafChild(*sibling, leafNodeTypes) + : HTMLEditUtils::GetLastLeafChild(*sibling, leafNodeTypes); return leafContent ? leafContent : sibling; } diff --git a/editor/libeditor/HTMLEditSubActionHandler.cpp b/editor/libeditor/HTMLEditSubActionHandler.cpp index 195cb4a92f70..b79c8e0716a1 100644 --- a/editor/libeditor/HTMLEditSubActionHandler.cpp +++ b/editor/libeditor/HTMLEditSubActionHandler.cpp @@ -71,7 +71,8 @@ class nsISupports; namespace mozilla { using namespace dom; -using ChildBlockBoundary = HTMLEditUtils::ChildBlockBoundary; +using LeafNodeType = HTMLEditUtils::LeafNodeType; +using LeafNodeTypes = HTMLEditUtils::LeafNodeTypes; using StyleDifference = HTMLEditUtils::StyleDifference; /******************************************************** @@ -2195,7 +2196,8 @@ HTMLEditor::DeleteTextAndNormalizeSurroundingWhiteSpaces( // Try to put caret next to immediately after previous editable leaf. nsIContent* previousContent = HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement( - newCaretPosition, *currentBlock, editingHost); + newCaretPosition, *currentBlock, + {LeafNodeType::LeafNodeOrNonEditableNode}, editingHost); if (previousContent && !HTMLEditUtils::IsBlockElement(*previousContent)) { newCaretPosition = previousContent->IsText() || @@ -2207,7 +2209,9 @@ HTMLEditor::DeleteTextAndNormalizeSurroundingWhiteSpaces( // a child block, look for next editable leaf instead. else if (nsIContent* nextContent = HTMLEditUtils::GetNextLeafContentOrNextBlockElement( - newCaretPosition, *currentBlock, editingHost)) { + newCaretPosition, *currentBlock, + {LeafNodeType::LeafNodeOrNonEditableNode}, + editingHost)) { newCaretPosition = nextContent->IsText() || HTMLEditUtils::IsContainerNode(*nextContent) ? EditorDOMPoint(nextContent, 0) @@ -5344,7 +5348,7 @@ nsresult HTMLEditor::MaybeExtendSelectionToHardLineEdgesForBlockEditAction() { // endpoint is just after the close of a block. nsIContent* child = HTMLEditUtils::GetLastLeafChild( *wsScannerAtEnd.StartReasonOtherBlockElementPtr(), - ChildBlockBoundary::TreatAsLeaf); + {LeafNodeType::LeafNodeOrChildBlock}); if (child) { newEndPoint.SetAfter(child); } @@ -5378,7 +5382,7 @@ nsresult HTMLEditor::MaybeExtendSelectionToHardLineEdgesForBlockEditAction() { // startpoint is just before the start of a block. nsINode* child = HTMLEditUtils::GetFirstLeafChild( *wsScannerAtStart.EndReasonOtherBlockElementPtr(), - ChildBlockBoundary::TreatAsLeaf); + {LeafNodeType::LeafNodeOrChildBlock}); if (child) { newStartPoint.Set(child); } @@ -6766,7 +6770,7 @@ nsresult HTMLEditor::SplitParagraph( // selection to beginning of right hand para; // look inside any containers that are up front. nsCOMPtr child = HTMLEditUtils::GetFirstLeafChild( - aParentDivOrP, ChildBlockBoundary::TreatAsLeaf); + aParentDivOrP, {LeafNodeType::LeafNodeOrChildBlock}); if (child && (child->IsText() || HTMLEditUtils::IsContainerNode(*child))) { nsresult rv = CollapseSelectionToStartOf(*child); if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) { diff --git a/editor/libeditor/HTMLEditUtils.h b/editor/libeditor/HTMLEditUtils.h index fefc06b62160..614afd402c68 100644 --- a/editor/libeditor/HTMLEditUtils.h +++ b/editor/libeditor/HTMLEditUtils.h @@ -9,6 +9,7 @@ #include "mozilla/Attributes.h" #include "mozilla/EditorDOMPoint.h" #include "mozilla/EditorUtils.h" +#include "mozilla/EnumSet.h" #include "mozilla/Maybe.h" #include "mozilla/dom/AbstractRange.h" #include "mozilla/dom/AncestorIterator.h" @@ -307,46 +308,58 @@ class HTMLEditUtils final { /** * GetLastLeafChild() returns rightmost leaf content in aNode. It depends on - * aChildBlockBoundary whether this scans into a block child or treat - * block as a leaf. + * aLeafNodeTypes whether this which types of nodes are treated as leaf nodes. */ - enum class ChildBlockBoundary { + enum class LeafNodeType { // Even if there is a child block, keep scanning a leaf content in it. - Ignore, - // If there is a child block, return it. - TreatAsLeaf, + OnlyLeafNode, + // If there is a child block, return it too. Note that this does not + // mean that block siblings are not treated as leaf nodes. + LeafNodeOrChildBlock, + // If there is a non-editable element if and only if scanning from editable + // node, return it too. + LeafNodeOrNonEditableNode, }; + using LeafNodeTypes = EnumSet; static nsIContent* GetLastLeafChild(nsINode& aNode, - ChildBlockBoundary aChildBlockBoundary) { + const LeafNodeTypes& aLeafNodeTypes) { for (nsIContent* content = aNode.GetLastChild(); content; content = content->GetLastChild()) { - if (aChildBlockBoundary == ChildBlockBoundary::TreatAsLeaf && + if (aLeafNodeTypes.contains(LeafNodeType::LeafNodeOrChildBlock) && HTMLEditUtils::IsBlockElement(*content)) { return content; } if (!content->HasChildren()) { return content; } + if (aLeafNodeTypes.contains(LeafNodeType::LeafNodeOrNonEditableNode) && + aNode.IsEditable() && !content->IsEditable()) { + return content; + } } return nullptr; } /** * GetFirstLeafChild() returns leftmost leaf content in aNode. It depends on - * aChildBlockBoundary whether this scans into a block child or treat + * aLeafNodeTypes whether this scans into a block child or treat * block as a leaf. */ static nsIContent* GetFirstLeafChild(nsINode& aNode, - ChildBlockBoundary aChildBlockBoundary) { + const LeafNodeTypes& aLeafNodeTypes) { for (nsIContent* content = aNode.GetFirstChild(); content; content = content->GetFirstChild()) { - if (aChildBlockBoundary == ChildBlockBoundary::TreatAsLeaf && + if (aLeafNodeTypes.contains(LeafNodeType::LeafNodeOrChildBlock) && HTMLEditUtils::IsBlockElement(*content)) { return content; } if (!content->HasChildren()) { return content; } + if (aLeafNodeTypes.contains(LeafNodeType::LeafNodeOrNonEditableNode) && + aNode.IsEditable() && !content->IsEditable()) { + return content; + } } return nullptr; } @@ -361,12 +374,14 @@ class HTMLEditUtils final { * @param aCurrentBlock Must be ancestor of aStartContent. Dispite * the name, inline content is allowed if * aStartContent is in an inline editing host. + * @param aLeafNodeTypes See LeafNodeType. * @param aAncestorLimiter Optional, setting this guarantees the * result is in aAncestorLimiter unless * aStartContent is not a descendant of this. */ static nsIContent* GetNextLeafContentOrNextBlockElement( const nsIContent& aStartContent, const nsIContent& aCurrentBlock, + const LeafNodeTypes& aLeafNodeTypes, const Element* aAncestorLimiter = nullptr) { if (&aStartContent == aAncestorLimiter) { return nullptr; @@ -402,10 +417,14 @@ class HTMLEditUtils final { if (HTMLEditUtils::IsBlockElement(*nextContent)) { return nextContent; } + if (aLeafNodeTypes.contains(LeafNodeType::LeafNodeOrNonEditableNode) && + aStartContent.IsEditable() && !nextContent->IsEditable()) { + return nextContent; + } if (HTMLEditUtils::IsContainerNode(*nextContent)) { // Else if it's a container, get deep leftmost child - if (nsIContent* child = HTMLEditUtils::GetFirstLeafChild( - *nextContent, ChildBlockBoundary::Ignore)) { + if (nsIContent* child = + HTMLEditUtils::GetFirstLeafChild(*nextContent, aLeafNodeTypes)) { return child; } } @@ -420,7 +439,7 @@ class HTMLEditUtils final { template static nsIContent* GetNextLeafContentOrNextBlockElement( const EditorDOMPointBase& aStartPoint, - const nsIContent& aCurrentBlock, + const nsIContent& aCurrentBlock, const LeafNodeTypes& aLeafNodeTypes, const Element* aAncestorLimiter = nullptr) { MOZ_ASSERT(aStartPoint.IsSet()); @@ -429,11 +448,13 @@ class HTMLEditUtils final { } if (aStartPoint.IsInTextNode()) { return HTMLEditUtils::GetNextLeafContentOrNextBlockElement( - *aStartPoint.ContainerAsText(), aCurrentBlock, aAncestorLimiter); + *aStartPoint.ContainerAsText(), aCurrentBlock, aLeafNodeTypes, + aAncestorLimiter); } if (!HTMLEditUtils::IsContainerNode(*aStartPoint.ContainerAsContent())) { return HTMLEditUtils::GetNextLeafContentOrNextBlockElement( - *aStartPoint.ContainerAsContent(), aCurrentBlock, aAncestorLimiter); + *aStartPoint.ContainerAsContent(), aCurrentBlock, aLeafNodeTypes, + aAncestorLimiter); } nsCOMPtr nextContent = aStartPoint.GetChild(); @@ -445,17 +466,23 @@ class HTMLEditUtils final { // We are at end of non-block container return HTMLEditUtils::GetNextLeafContentOrNextBlockElement( - *aStartPoint.ContainerAsContent(), aCurrentBlock, aAncestorLimiter); + *aStartPoint.ContainerAsContent(), aCurrentBlock, aLeafNodeTypes, + aAncestorLimiter); } // We have a next node. If it's a block, return it. if (HTMLEditUtils::IsBlockElement(*nextContent)) { return nextContent; } + if (aLeafNodeTypes.contains(LeafNodeType::LeafNodeOrNonEditableNode) && + aStartPoint.GetContainer()->IsEditable() && + !nextContent->IsEditable()) { + return nextContent; + } if (HTMLEditUtils::IsContainerNode(*nextContent)) { // else if it's a container, get deep leftmost child - if (nsIContent* child = HTMLEditUtils::GetFirstLeafChild( - *nextContent, ChildBlockBoundary::Ignore)) { + if (nsIContent* child = + HTMLEditUtils::GetFirstLeafChild(*nextContent, aLeafNodeTypes)) { return child; } } @@ -474,12 +501,14 @@ class HTMLEditUtils final { * @param aCurrentBlock Must be ancestor of aStartContent. Dispite * the name, inline content is allowed if * aStartContent is in an inline editing host. + * @param aLeafNodeTypes See LeafNodeType. * @param aAncestorLimiter Optional, setting this guarantees the * result is in aAncestorLimiter unless * aStartContent is not a descendant of this. */ static nsIContent* GetPreviousLeafContentOrPreviousBlockElement( const nsIContent& aStartContent, const nsIContent& aCurrentBlock, + const LeafNodeTypes& aLeafNodeTypes, const Element* aAncestorLimiter = nullptr) { if (&aStartContent == aAncestorLimiter) { return nullptr; @@ -515,10 +544,14 @@ class HTMLEditUtils final { if (HTMLEditUtils::IsBlockElement(*previousContent)) { return previousContent; } + if (aLeafNodeTypes.contains(LeafNodeType::LeafNodeOrNonEditableNode) && + aStartContent.IsEditable() && !previousContent->IsEditable()) { + return previousContent; + } if (HTMLEditUtils::IsContainerNode(*previousContent)) { // Else if it's a container, get deep rightmost child - if (nsIContent* child = HTMLEditUtils::GetLastLeafChild( - *previousContent, ChildBlockBoundary::Ignore)) { + if (nsIContent* child = HTMLEditUtils::GetLastLeafChild(*previousContent, + aLeafNodeTypes)) { return child; } } @@ -533,7 +566,7 @@ class HTMLEditUtils final { template static nsIContent* GetPreviousLeafContentOrPreviousBlockElement( const EditorDOMPointBase& aStartPoint, - const nsIContent& aCurrentBlock, + const nsIContent& aCurrentBlock, const LeafNodeTypes& aLeafNodeTypes, const Element* aAncestorLimiter = nullptr) { MOZ_ASSERT(aStartPoint.IsSet()); @@ -542,11 +575,13 @@ class HTMLEditUtils final { } if (aStartPoint.IsInTextNode()) { return HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement( - *aStartPoint.ContainerAsText(), aCurrentBlock, aAncestorLimiter); + *aStartPoint.ContainerAsText(), aCurrentBlock, aLeafNodeTypes, + aAncestorLimiter); } if (!HTMLEditUtils::IsContainerNode(*aStartPoint.ContainerAsContent())) { return HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement( - *aStartPoint.ContainerAsContent(), aCurrentBlock, aAncestorLimiter); + *aStartPoint.ContainerAsContent(), aCurrentBlock, aLeafNodeTypes, + aAncestorLimiter); } if (aStartPoint.IsStartOfContainer()) { @@ -557,7 +592,8 @@ class HTMLEditUtils final { // We are at start of non-block container return HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement( - *aStartPoint.ContainerAsContent(), aCurrentBlock, aAncestorLimiter); + *aStartPoint.ContainerAsContent(), aCurrentBlock, aLeafNodeTypes, + aAncestorLimiter); } nsCOMPtr previousContent = @@ -570,10 +606,15 @@ class HTMLEditUtils final { if (HTMLEditUtils::IsBlockElement(*previousContent)) { return previousContent; } + if (aLeafNodeTypes.contains(LeafNodeType::LeafNodeOrNonEditableNode) && + aStartPoint.GetContainer()->IsEditable() && + !previousContent->IsEditable()) { + return previousContent; + } if (HTMLEditUtils::IsContainerNode(*previousContent)) { // Else if it's a container, get deep rightmost child - if (nsIContent* child = HTMLEditUtils::GetLastLeafChild( - *previousContent, ChildBlockBoundary::Ignore)) { + if (nsIContent* child = HTMLEditUtils::GetLastLeafChild(*previousContent, + aLeafNodeTypes)) { return child; } } diff --git a/editor/libeditor/HTMLEditor.cpp b/editor/libeditor/HTMLEditor.cpp index 4b5842a93fc5..4953181c35f8 100644 --- a/editor/libeditor/HTMLEditor.cpp +++ b/editor/libeditor/HTMLEditor.cpp @@ -69,7 +69,8 @@ namespace mozilla { using namespace dom; using namespace widget; -using ChildBlockBoundary = HTMLEditUtils::ChildBlockBoundary; +using LeafNodeType = HTMLEditUtils::LeafNodeType; +using LeafNodeTypes = HTMLEditUtils::LeafNodeTypes; const char16_t kNBSP = 160; @@ -2856,7 +2857,7 @@ already_AddRefed HTMLEditor::GetSelectedElement(const nsAtom* aTagName, return nullptr; } nsIContent* firstEditableLeaf = HTMLEditUtils::GetFirstLeafChild( - *nextSibling, ChildBlockBoundary::Ignore); + *nextSibling, {LeafNodeType::OnlyLeafNode}); if (firstEditableLeaf && firstEditableLeaf->IsHTMLElement(nsGkAtoms::br)) { return nullptr; @@ -5116,7 +5117,7 @@ nsIContent* HTMLEditor::GetLastEditableChild(nsINode& aNode) const { nsIContent* HTMLEditor::GetFirstEditableLeaf(nsINode& aNode) const { nsIContent* child = - HTMLEditUtils::GetFirstLeafChild(aNode, ChildBlockBoundary::Ignore); + HTMLEditUtils::GetFirstLeafChild(aNode, {LeafNodeType::OnlyLeafNode}); while (child && (!EditorUtils::IsEditableContent(*child, EditorType::HTML) || child->HasChildren())) { child = GetNextEditableHTMLNode(*child); @@ -5132,7 +5133,7 @@ nsIContent* HTMLEditor::GetFirstEditableLeaf(nsINode& aNode) const { nsIContent* HTMLEditor::GetLastEditableLeaf(nsINode& aNode) const { nsIContent* child = - HTMLEditUtils::GetLastLeafChild(aNode, ChildBlockBoundary::Ignore); + HTMLEditUtils::GetLastLeafChild(aNode, {LeafNodeType::OnlyLeafNode}); while (child && (!EditorUtils::IsEditableContent(*child, EditorType::HTML) || child->HasChildren())) { child = GetPreviousEditableHTMLNode(*child); diff --git a/editor/libeditor/HTMLStyleEditor.cpp b/editor/libeditor/HTMLStyleEditor.cpp index 5a62978a635f..e090f5e899be 100644 --- a/editor/libeditor/HTMLStyleEditor.cpp +++ b/editor/libeditor/HTMLStyleEditor.cpp @@ -47,7 +47,8 @@ namespace mozilla { using namespace dom; -using ChildBlockBoundary = HTMLEditUtils::ChildBlockBoundary; +using LeafNodeType = HTMLEditUtils::LeafNodeType; +using LeafNodeTypes = HTMLEditUtils::LeafNodeTypes; nsresult HTMLEditor::SetInlinePropertyAsAction(nsAtom& aProperty, nsAtom* aAttribute, @@ -991,7 +992,7 @@ EditResult HTMLEditor::ClearStyleAt(const EditorDOMPoint& aPoint, // `

abc

`. // ^^^^^^^^^^^^^^ nsIContent* firstLeafChildOfNextNode = HTMLEditUtils::GetFirstLeafChild( - *splitResult.GetNextNode(), ChildBlockBoundary::Ignore); + *splitResult.GetNextNode(), {LeafNodeType::OnlyLeafNode}); EditorDOMPoint atStartOfNextNode(firstLeafChildOfNextNode ? firstLeafChildOfNextNode : splitResult.GetNextNode(), @@ -1050,7 +1051,7 @@ EditResult HTMLEditor::ClearStyleAt(const EditorDOMPoint& aPoint, // E.g., `

a
bc

` nsIContent* firstLeafChildOfPreviousNode = HTMLEditUtils::GetFirstLeafChild( *splitResultAtStartOfNextNode.GetPreviousNode(), - ChildBlockBoundary::Ignore); + {LeafNodeType::OnlyLeafNode}); EditorDOMPoint pointToPutCaret( firstLeafChildOfPreviousNode ? firstLeafChildOfPreviousNode diff --git a/editor/libeditor/TextEditor.cpp b/editor/libeditor/TextEditor.cpp index 49076299bfb9..4cac42a23938 100644 --- a/editor/libeditor/TextEditor.cpp +++ b/editor/libeditor/TextEditor.cpp @@ -70,7 +70,8 @@ namespace mozilla { using namespace dom; -using ChildBlockBoundary = HTMLEditUtils::ChildBlockBoundary; +using LeafNodeType = HTMLEditUtils::LeafNodeType; +using LeafNodeTypes = HTMLEditUtils::LeafNodeTypes; TextEditor::TextEditor() : mMaxTextLength(-1), @@ -1018,7 +1019,7 @@ nsresult TextEditor::UndoAsAction(uint32_t aCount, nsIPrincipal* aPrincipal) { // and redo are relatively rare, it makes sense to take the (small) // performance hit here. nsIContent* firstLeafChild = HTMLEditUtils::GetFirstLeafChild( - *mRootElement, ChildBlockBoundary::Ignore); + *mRootElement, {LeafNodeType::OnlyLeafNode}); if (firstLeafChild && EditorUtils::IsPaddingBRElementForEmptyEditor(*firstLeafChild)) { mPaddingBRElementForEmptyEditor = diff --git a/editor/libeditor/WSRunObject.cpp b/editor/libeditor/WSRunObject.cpp index cb9d5678e87b..a789a9ae91b0 100644 --- a/editor/libeditor/WSRunObject.cpp +++ b/editor/libeditor/WSRunObject.cpp @@ -36,7 +36,8 @@ namespace mozilla { using namespace dom; -using ChildBlockBoundary = HTMLEditUtils::ChildBlockBoundary; +using LeafNodeType = HTMLEditUtils::LeafNodeType; +using LeafNodeTypes = HTMLEditUtils::LeafNodeTypes; const char16_t kNBSP = 160; @@ -1259,6 +1260,12 @@ WSScanResult WSRunScanner::ScanPreviousVisibleNodeOrBlockBoundaryFrom( TextFragmentDataAtStartRef().VisibleWhiteSpacesDataRef(); if (visibleWhiteSpaces.IsInitialized() && visibleWhiteSpaces.StartRef().IsBefore(aPoint)) { + // If the visible things are not editable, we shouldn't scan "editable" + // things now. Whether keep scanning editable things or not should be + // considered by the caller. + if (aPoint.GetChild() && !aPoint.GetChild()->IsEditable()) { + return WSScanResult(aPoint.GetChild(), WSType::SpecialContent); + } EditorDOMPointInText atPreviousChar = GetPreviousEditableCharPoint(aPoint); // When it's a non-empty text node, return it. if (atPreviousChar.IsSet() && !atPreviousChar.IsContainerEmpty()) { @@ -1297,6 +1304,12 @@ WSScanResult WSRunScanner::ScanNextVisibleNodeOrBlockBoundaryFrom( TextFragmentDataAtStartRef().VisibleWhiteSpacesDataRef(); if (visibleWhiteSpaces.IsInitialized() && aPoint.EqualsOrIsBefore(visibleWhiteSpaces.EndRef())) { + // If the visible things are not editable, we shouldn't scan "editable" + // things now. Whether keep scanning editable things or not should be + // considered by the caller. + if (aPoint.GetChild() && !aPoint.GetChild()->IsEditable()) { + return WSScanResult(aPoint.GetChild(), WSType::SpecialContent); + } EditorDOMPointInText atNextChar = GetInclusiveNextEditableCharPoint(aPoint); // When it's a non-empty text node, return it. if (atNextChar.IsSet() && !atNextChar.IsContainerEmpty()) { @@ -1448,7 +1461,7 @@ WSRunScanner::TextFragmentData::BoundaryData WSRunScanner::TextFragmentData:: nsIContent* previousLeafContentOrBlock = HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement( aPoint, aEditableBlockParentOrTopmostEditableInlineContent, - aEditingHost); + {LeafNodeType::LeafNodeOrNonEditableNode}, aEditingHost); if (!previousLeafContentOrBlock) { // no prior node means we exhausted // aEditableBlockParentOrTopmostEditableInlineContent @@ -1580,7 +1593,7 @@ WSRunScanner::TextFragmentData::BoundaryData::ScanCollapsibleWhiteSpaceEndFrom( nsIContent* nextLeafContentOrBlock = HTMLEditUtils::GetNextLeafContentOrNextBlockElement( aPoint, aEditableBlockParentOrTopmostEditableInlineElement, - aEditingHost); + {LeafNodeType::LeafNodeOrNonEditableNode}, aEditingHost); if (!nextLeafContentOrBlock) { // no next node means we exhausted // aEditableBlockParentOrTopmostEditableInlineElement @@ -2280,7 +2293,7 @@ WSRunScanner::TextFragmentData::GetInclusiveNextEditableCharPoint( aPoint.CanContainerHaveChildren() ? aPoint.GetChild() : nullptr) { nsIContent* leafContent = child->HasChildren() ? HTMLEditUtils::GetFirstLeafChild( - *child, ChildBlockBoundary::Ignore) + *child, {LeafNodeType::OnlyLeafNode}) : child; if (NS_WARN_IF(!leafContent)) { return EditorDOMPointInText(); @@ -2326,11 +2339,11 @@ WSRunScanner::TextFragmentData::GetInclusiveNextEditableCharPoint( HTMLEditUtils::GetNextLeafContentOrNextBlockElement( *point.ContainerAsContent(), *editableBlockParentOrTopmostEditableInlineContent, - mEditingHost); + {LeafNodeType::LeafNodeOrNonEditableNode}, mEditingHost); nextContent; nextContent = HTMLEditUtils::GetNextLeafContentOrNextBlockElement( *nextContent, *editableBlockParentOrTopmostEditableInlineContent, - mEditingHost)) { + {LeafNodeType::LeafNodeOrNonEditableNode}, mEditingHost)) { if (!nextContent->IsText() || !nextContent->IsEditable()) { if (nextContent == GetEndReasonContent()) { break; // Reached end of current runs. @@ -2360,7 +2373,7 @@ WSRunScanner::TextFragmentData::GetPreviousEditableCharPoint( nsIContent* leafContent = previousChild->HasChildren() ? HTMLEditUtils::GetLastLeafChild(*previousChild, - ChildBlockBoundary::Ignore) + {LeafNodeType::OnlyLeafNode}) : previousChild; if (NS_WARN_IF(!leafContent)) { return EditorDOMPointInText(); @@ -2407,13 +2420,13 @@ WSRunScanner::TextFragmentData::GetPreviousEditableCharPoint( HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement( *point.ContainerAsContent(), *editableBlockParentOrTopmostEditableInlineContent, - mEditingHost); + {LeafNodeType::LeafNodeOrNonEditableNode}, mEditingHost); previousContent; previousContent = HTMLEditUtils::GetPreviousLeafContentOrPreviousBlockElement( *previousContent, *editableBlockParentOrTopmostEditableInlineContent, - mEditingHost)) { + {LeafNodeType::LeafNodeOrNonEditableNode}, mEditingHost)) { if (!previousContent->IsText() || !previousContent->IsEditable()) { if (previousContent == GetStartReasonContent()) { break; // Reached start of current runs. diff --git a/testing/web-platform/meta/editing/run/delete.html.ini b/testing/web-platform/meta/editing/run/delete.html.ini index 305a606a332a..ce0ffa5fbff5 100644 --- a/testing/web-platform/meta/editing/run/delete.html.ini +++ b/testing/web-platform/meta/editing/run/delete.html.ini @@ -700,3 +700,5 @@ [[["delete",""\]\] "foo
bar
[\]baz" compare innerHTML] expected: FAIL + [[["delete",""\]\] "foobarbaz[\]qux" compare innerHTML] + expected: FAIL diff --git a/testing/web-platform/meta/editing/run/forwarddelete.html.ini b/testing/web-platform/meta/editing/run/forwarddelete.html.ini index e8a7d6413406..adc68b00de78 100644 --- a/testing/web-platform/meta/editing/run/forwarddelete.html.ini +++ b/testing/web-platform/meta/editing/run/forwarddelete.html.ini @@ -613,9 +613,6 @@ [[["forwarddelete",""\]\] "
abc[\]
" compare innerHTML] expected: FAIL - [[["forwarddelete",""\]\] "foo[\]barbaz": execCommand("forwarddelete", false, "") return value] - expected: FAIL - - [[["forwarddelete",""\]\] "foo[\]barbaz" compare innerHTML] + [[["forwarddelete",""\]\] "foo[\]barbazqux" compare innerHTML] expected: FAIL diff --git a/testing/web-platform/meta/input-events/input-events-get-target-ranges-forwarddelete.tentative.html.ini b/testing/web-platform/meta/input-events/input-events-get-target-ranges-forwarddelete.tentative.html.ini index 9cf084e45467..a0a36b39983d 100644 --- a/testing/web-platform/meta/input-events/input-events-get-target-ranges-forwarddelete.tentative.html.ini +++ b/testing/web-platform/meta/input-events/input-events-get-target-ranges-forwarddelete.tentative.html.ini @@ -102,6 +102,3 @@ [Delete at "
abc
  • def[\]
ghi
"] expected: FAIL - [Delete at "

abc[\]defghi

"] - expected: FAIL - diff --git a/testing/web-platform/tests/editing/data/delete.js b/testing/web-platform/tests/editing/data/delete.js index 138a749500b7..57ddb1520cfd 100644 --- a/testing/web-platform/tests/editing/data/delete.js +++ b/testing/web-platform/tests/editing/data/delete.js @@ -2565,6 +2565,11 @@ var browserTests = [ "foo{}baz", [true], {"delete":[false,false,"",false,false,""]}], +["foobarbaz[]qux", + [["delete",""]], + "foobar[]qux", + [true], + {"delete":[false,false,"",false,false,""]}], ["foobar[]baz", [["delete",""]], "foo{}baz", diff --git a/testing/web-platform/tests/editing/data/forwarddelete.js b/testing/web-platform/tests/editing/data/forwarddelete.js index cc67b35c27e3..eeee322169df 100644 --- a/testing/web-platform/tests/editing/data/forwarddelete.js +++ b/testing/web-platform/tests/editing/data/forwarddelete.js @@ -2450,6 +2450,11 @@ var browserTests = [ "foo{}baz", [true], {"forwarddelete":[false,false,"",false,false,""]}], +["foo[]barbazqux", + [["forwarddelete",""]], + "foo[]bazqux", + [true], + {"forwarddelete":[false,false,"",false,false,""]}], ["foo[]barbaz", [["forwarddelete",""]], "foo{}baz",