tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 05c50f4921e367bd4b0f673b7c38cbad20542ecf
parent 566d1009e7ce14808adf985ac141fea8d7918348
Author: Masayuki Nakano <masayuki@d-toybox.com>
Date:   Fri, 26 Dec 2025 04:39:47 +0000

Bug 2007618 - Make `AutoEmptyBlockAncestorDeleter::GetNewCaretPosition` ignore invisible `Text` nodes r=m_kato

The remaining case of bug 2005895 is, there is a invisible `Text`
between paragraphs like:
```html
<p>abc</p> <p>{}<br></p>
```
The reason is, `HTMLEditUtils::GetPreviousContent` is not called with
`WalkTreeOption::IgnoreWhiteSpaceOnlyText`.  Therefore, I tried to
add the option in `AutoEmptyBlockAncestorDeleter::GetNewCaretPosition`.
However, the handling is odd [1]:
```cpp
  static bool IsContentIgnored(const nsIContent& aContent,
                               const WalkTreeOptions& aOptions) {
```
<snip>
```cpp
    if (aOptions.contains(WalkTreeOption::IgnoreWhiteSpaceOnlyText) &&
        aContent.IsText() &&
        const_cast<Text*>(aContent.AsText())->TextIsOnlyWhitespace()) {
      return true;
    }
```
So, whether the `Text` is actually visible or not is not tested.
Therefore, this does not work with preformatted content.  However, I'd
like to uplift this to ESR140 so that it's too risky to change the
extant `HTMLEditUtils` implementation.  Therefore, this patch makes
`GetNewCaretPosition` ignore invisible `Text` nodes directly.

1. https://searchfox.org/firefox-main/rev/20a1fb35a4d5c2f2ea6c865ecebc8e4bee6f86c9/editor/libeditor/HTMLEditUtils.h#3268-3269,3279-3282

Differential Revision: https://phabricator.services.mozilla.com/D277498

Diffstat:
Meditor/libeditor/HTMLEditorDeleteHandler.cpp | 47+++++++++++++++++++++++++++++++++++++----------
Mtesting/web-platform/tests/editing/data/delete.js | 10++++++++++
Mtesting/web-platform/tests/editing/data/forwarddelete.js | 10++++++++++
Mtesting/web-platform/tests/editing/data/multitest.js | 21+++++++++++++++++++++
4 files changed, 78 insertions(+), 10 deletions(-)

diff --git a/editor/libeditor/HTMLEditorDeleteHandler.cpp b/editor/libeditor/HTMLEditorDeleteHandler.cpp @@ -7203,11 +7203,23 @@ Result<CaretPoint, nsresult> HTMLEditor::AutoDeleteRangesHandler:: case nsIEditor::eToEndOfLine: { // Collapse Selection to next node of after empty block element // if there is. Otherwise, to just after the empty block. - auto afterEmptyBlock( - EditorDOMPoint::After(mEmptyInclusiveAncestorBlockElement)); - MOZ_ASSERT(afterEmptyBlock.IsSet()); - if (nsIContent* nextContentOfEmptyBlock = HTMLEditUtils::GetNextContent( - afterEmptyBlock, {}, BlockInlineCheck::Unused, &aEditingHost)) { + nsIContent* const nextContentOfEmptyBlock = [&]() -> nsIContent* { + for (EditorRawDOMPoint scanStartPoint = + EditorRawDOMPoint::After(mEmptyInclusiveAncestorBlockElement); + scanStartPoint.IsInContentNode();) { + nsIContent* const nextContent = HTMLEditUtils::GetNextContent( + scanStartPoint, {}, BlockInlineCheck::Unused, &aEditingHost); + // Let's ignore invisible `Text`. + if (nextContent && nextContent->IsText() && + !HTMLEditUtils::IsVisibleTextNode(*nextContent->AsText())) { + scanStartPoint = EditorRawDOMPoint::After(*nextContent); + continue; + } + return nextContent; + } + return nullptr; + }(); + if (nextContentOfEmptyBlock) { EditorDOMPoint pt = HTMLEditUtils::GetGoodCaretPointFor<EditorDOMPoint>( *nextContentOfEmptyBlock, aDirectionAndAmount); if (!pt.IsSet()) { @@ -7216,6 +7228,8 @@ Result<CaretPoint, nsresult> HTMLEditor::AutoDeleteRangesHandler:: } return CaretPoint(std::move(pt)); } + EditorDOMPoint afterEmptyBlock = + EditorDOMPoint::After(mEmptyInclusiveAncestorBlockElement); if (NS_WARN_IF(!afterEmptyBlock.IsSet())) { return Err(NS_ERROR_FAILURE); } @@ -7226,11 +7240,24 @@ Result<CaretPoint, nsresult> HTMLEditor::AutoDeleteRangesHandler:: case nsIEditor::eToBeginningOfLine: { // Collapse Selection to previous editable node of the empty block // if there is. - EditorRawDOMPoint atEmptyBlock(mEmptyInclusiveAncestorBlockElement); - if (nsIContent* const previousContentOfEmptyBlock = - HTMLEditUtils::GetPreviousContent( - atEmptyBlock, {WalkTreeOption::IgnoreNonEditableNode}, - BlockInlineCheck::Unused, &aEditingHost)) { + nsIContent* const previousContentOfEmptyBlock = [&]() -> nsIContent* { + for (EditorRawDOMPoint scanStartPoint = + EditorRawDOMPoint(mEmptyInclusiveAncestorBlockElement); + scanStartPoint.IsInContentNode();) { + nsIContent* const previousContent = HTMLEditUtils::GetPreviousContent( + scanStartPoint, {WalkTreeOption::IgnoreNonEditableNode}, + BlockInlineCheck::Unused, &aEditingHost); + // Let's ignore invisible `Text`. + if (previousContent && previousContent->IsText() && + !HTMLEditUtils::IsVisibleTextNode(*previousContent->AsText())) { + scanStartPoint = EditorRawDOMPoint(previousContent, 0u); + continue; + } + return previousContent; + } + return nullptr; + }(); + if (previousContentOfEmptyBlock) { const EditorRawDOMPoint atEndOfPreviousContent = HTMLEditUtils::GetGoodCaretPointFor<EditorRawDOMPoint>( *previousContentOfEmptyBlock, aDirectionAndAmount); diff --git a/testing/web-platform/tests/editing/data/delete.js b/testing/web-platform/tests/editing/data/delete.js @@ -3506,6 +3506,16 @@ var browserTests = [ "<p>abc</p>", [true], {}], +["<p>abc<br> </p> <p>{}<br></p>", + [["delete",""]], + "<p>abc</p>", + [true], + {}], +["<div style=white-space:pre><p>abc</p> <p>{}<br></p></div>", + [["delete",""]], + "<div style=\"white-space:pre\"><p>abc</p> </div>", + [true], + {}], // The following tests are ported by Mozilla from their old test and the // expectations are based on Chrome's behavior unless the behavior does not diff --git a/testing/web-platform/tests/editing/data/forwarddelete.js b/testing/web-platform/tests/editing/data/forwarddelete.js @@ -3368,4 +3368,14 @@ var browserTests = [ "<p>abc</p>", [true], {}], +["<p>abc{}<br> </p> <p><br></p>", + [["forwarddelete",""]], + "<p>abc</p>", + [true], + {}], +["<div style=white-space:pre><p>abc{}</p> <p><br></p></div>", + [["forwarddelete",""]], + "<div style=\"white-space:pre\"><p>abc </p><p><br></p></div>", + [true], + {}], ] diff --git a/testing/web-platform/tests/editing/data/multitest.js b/testing/web-platform/tests/editing/data/multitest.js @@ -3260,4 +3260,25 @@ var browserTests = [ ["<p><b>abc </b>d</p>", "<p><b>abc&nbsp;</b>d</p>"], [true,true,true], {}], + +["<p>abc<br> </p> <p>{}<br></p>", + [["delete",""],["inserttext","d"]], + "<p>abcd</p>", + [true,true], + {}], +["<div style=white-space:pre><p>abc</p> <p>{}<br></p></div>", + [["delete",""],["inserttext","d"]], + "<div style=\"white-space:pre\"><p>abc</p> d</div>", + [true,true], + {}], +["<p>abc[]<br> </p> <p><br></p>", + [["forwarddelete",""],["inserttext","d"]], + "<p>abcd</p>", + [true,true], + {}], +["<div style=white-space:pre><p>abc[]</p> <p><br></p></div>", + [["forwarddelete",""],["inserttext","d"]], + "<div style=\"white-space:pre\"><p>abcd </p><p><br></p></div>", + [true,true], + {}], ]