tor-browser

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

commit c73776fe6995e7c9e4cc4b908fd488f7e641e6c4
parent cbd064b8c2fe2a680b46cefd4e100651726e7141
Author: Masayuki Nakano <masayuki@d-toybox.com>
Date:   Tue, 14 Oct 2025 00:22:35 +0000

Bug 1992511 - Make `WhiteSpaceVisibilityKeeper::EnsureNoInvisibleWhiteSpaces` stop initializing `TextFragmentData` with outside inline editing host r=m_kato

It seems that the method depended on the buggy behavior when the
inserting position is start of an inline editing host.

The method uses the closest block element of the insertion point as the
scanning limiter.  However, it's non-editable if the editing host is
an inline element.  In this case, the method has initialized its
`TextFragmentData` instance with the point at the editing host.  I.e.,
the container is non-editable parent of the inline editing host.
This is completely unexpected behavior for the method.  This method
needs to scan previous content of the insertion point if and only if
it's not start of an editing host.  Therefore, it should check whether
the container is start of an editing host at considering to use the
previous point.

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

Diffstat:
Meditor/libeditor/WhiteSpaceVisibilityKeeper.cpp | 12+++++++-----
Atesting/web-platform/tests/editing/other/inline-editing-host-has-placeholder-with-after-pseudo-element.html | 35+++++++++++++++++++++++++++++++++++
Atesting/web-platform/tests/editing/other/inline-editing-host-has-placeholder-with-before-pseudo-element.html | 35+++++++++++++++++++++++++++++++++++
3 files changed, 77 insertions(+), 5 deletions(-)

diff --git a/editor/libeditor/WhiteSpaceVisibilityKeeper.cpp b/editor/libeditor/WhiteSpaceVisibilityKeeper.cpp @@ -1867,20 +1867,22 @@ WhiteSpaceVisibilityKeeper::EnsureNoInvisibleWhiteSpaces( (!aPoint.IsEndOfContainer() && !aPoint.IsCharCollapsibleASCIISpace())) { return EditorDOMPoint(); } - const Element* const closestBlockElement = + const Element* const maybeNonEditableClosestBlockElement = HTMLEditUtils::GetInclusiveAncestorElement( *aPoint.ContainerAs<nsIContent>(), HTMLEditUtils::ClosestBlockElement, BlockInlineCheck::UseComputedDisplayStyle); - if (MOZ_UNLIKELY(!closestBlockElement)) { + if (MOZ_UNLIKELY(!maybeNonEditableClosestBlockElement)) { return EditorDOMPoint(); // aPoint is not in a block. } const TextFragmentData textFragmentDataForLeadingWhiteSpaces( Scan::EditableNodes, aPoint.IsStartOfContainer() && - aPoint.GetContainer() == closestBlockElement + (aPoint.GetContainer() == maybeNonEditableClosestBlockElement || + aPoint.GetContainer()->IsEditingHost()) ? aPoint : aPoint.PreviousPointOrParentPoint<EditorDOMPoint>(), - BlockInlineCheck::UseComputedDisplayStyle, closestBlockElement); + BlockInlineCheck::UseComputedDisplayStyle, + maybeNonEditableClosestBlockElement); if (NS_WARN_IF(!textFragmentDataForLeadingWhiteSpaces.IsInitialized())) { return Err(NS_ERROR_FAILURE); } @@ -1941,7 +1943,7 @@ WhiteSpaceVisibilityKeeper::EnsureNoInvisibleWhiteSpaces( ? textFragmentDataForLeadingWhiteSpaces : TextFragmentData(Scan::EditableNodes, aPoint, BlockInlineCheck::UseComputedDisplayStyle, - closestBlockElement); + maybeNonEditableClosestBlockElement); const EditorDOMRange& trailingWhiteSpaceRange = textFragmentData.InvisibleTrailingWhiteSpaceRangeRef(); if (trailingWhiteSpaceRange.IsPositioned() && diff --git a/testing/web-platform/tests/editing/other/inline-editing-host-has-placeholder-with-after-pseudo-element.html b/testing/web-platform/tests/editing/other/inline-editing-host-has-placeholder-with-after-pseudo-element.html @@ -0,0 +1,35 @@ +<!doctype html> +<html> +<head> +<meta charset="utf-8"> +<title>Basic test for inline editing host which has placeholder text with pseudo-element</title> +<style> +[contenteditable]:empty::after { + content: "Type something"; +} +</style> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script> +"use strict"; + +addEventListener("load", () => { + const editingHost = document.querySelector("span[contenteditable]"); + editingHost.focus(); + test(() => { + document.execCommand("insertText", false, "X"); + assert_equals(editingHost.innerHTML, "X"); + }, "The editing host should have inserted text"); + test(() => { + document.execCommand("delete"); + assert_equals(editingHost.innerHTML, ""); + }, "The editing host should have no text"); +}, {once: true}); +</script> +</head> +<body> + <span id="container"> + <span contenteditable="true"></span> + </span> +</body> +</html> diff --git a/testing/web-platform/tests/editing/other/inline-editing-host-has-placeholder-with-before-pseudo-element.html b/testing/web-platform/tests/editing/other/inline-editing-host-has-placeholder-with-before-pseudo-element.html @@ -0,0 +1,35 @@ +<!doctype html> +<html> +<head> +<meta charset="utf-8"> +<title>Basic test for inline editing host which has placeholder text with pseudo-element</title> +<style> +[contenteditable]:empty::before { + content: "Type something"; +} +</style> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script> +"use strict"; + +addEventListener("load", () => { + const editingHost = document.querySelector("span[contenteditable]"); + editingHost.focus(); + test(() => { + document.execCommand("insertText", false, "X"); + assert_equals(editingHost.innerHTML, "X"); + }, "The editing host should have inserted text"); + test(() => { + document.execCommand("delete"); + assert_equals(editingHost.innerHTML, ""); + }, "The editing host should have no text"); +}, {once: true}); +</script> +</head> +<body> + <span id="container"> + <span contenteditable="true"></span> + </span> +</body> +</html>