From bf5792cbacafec1aedfb54b7278c7ae5b79ebb3a Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Sat, 18 Mar 2023 04:18:52 +0000 Subject: [PATCH] Bug 1384606 - part 1: Make `nsIFrame::GetFrameFromDirection` treat frames in different native anonymous subtree not selectable r=emilio It's called by `PeekOffsetFor*` and `GetPrevNextBidiLevels`, so it's used for considering whether to put caret or move a selection range boundary. Therefore, it should treat nodes which can be managed by `Selection` as selectable. In theory, even if a native anonymous subtree does not have an independent selection, its content nodes should not be the container of the selection range boundaries of selection outside the subtree since Selection API shouldn't expose nodes in native anonymous subtrees. Therefore, it can simply treat content nodes in different anonymous subtrees are not selectable. Note that it's not standardized that how `Selection.modify` works with various content nodes. https://w3c.github.io/selection-api/#dom-selection-modify And also Chrome cannot cross generated content like form controls with this API. This could cause web-compat issues, but it does not make sense for caret navigation, and anyway out of scope of this bug. Therefore, this patch just adds the crash test. Differential Revision: https://phabricator.services.mozilla.com/D172204 --- layout/generic/nsIFrame.cpp | 11 ++++++++- layout/generic/nsIFrame.h | 12 ++++++++++ .../selection-modify-around-input.html | 24 +++++++++++++++++++ 3 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 testing/web-platform/tests/selection/crashtests/selection-modify-around-input.html diff --git a/layout/generic/nsIFrame.cpp b/layout/generic/nsIFrame.cpp index e15ef0ef56b3..1005d58c4fd4 100644 --- a/layout/generic/nsIFrame.cpp +++ b/layout/generic/nsIFrame.cpp @@ -9536,6 +9536,8 @@ nsIFrame::SelectablePeekReport nsIFrame::GetFrameFromDirection( bool selectable = false; nsIFrame* traversedFrame = this; AutoAssertNoDomMutations guard; + const nsIContent* const nativeAnonymousSubtreeContent = + GetClosestNativeAnonymousSubtreeRoot(); while (!selectable) { auto [blockFrame, lineFrame] = traversedFrame->GetContainingBlockForLine(aScrollViewStop); @@ -9573,10 +9575,17 @@ nsIFrame::SelectablePeekReport nsIFrame::GetFrameFromDirection( return result; } - auto IsSelectable = [aForceEditableRegion](const nsIFrame* aFrame) { + auto IsSelectable = [aForceEditableRegion, nativeAnonymousSubtreeContent]( + const nsIFrame* aFrame) { if (!aFrame->IsSelectable(nullptr)) { return false; } + // If the new frame is in a native anonymous subtree, we should treat it + // as not selectable unless the frame and found frame are in same subtree. + if (aFrame->GetClosestNativeAnonymousSubtreeRoot() != + nativeAnonymousSubtreeContent) { + return false; + } return !aForceEditableRegion || aFrame->GetContent()->IsEditable(); }; diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h index bd596e05c098..075b05166969 100644 --- a/layout/generic/nsIFrame.h +++ b/layout/generic/nsIFrame.h @@ -747,6 +747,18 @@ class nsIFrame : public nsQueryFrame { */ nsIContent* GetContent() const { return mContent; } + /** + * @brief Get the closest native anonymous subtree root if the content is in a + * native anonymous subtree. + * + * @return The root of native anonymous subtree which the content belongs to. + * Otherwise, nullptr. + */ + nsIContent* GetClosestNativeAnonymousSubtreeRoot() const { + return mContent ? mContent->GetClosestNativeAnonymousSubtreeRoot() + : nullptr; + } + /** * Get the frame that should be the parent for the frames of child elements * May return nullptr during reflow diff --git a/testing/web-platform/tests/selection/crashtests/selection-modify-around-input.html b/testing/web-platform/tests/selection/crashtests/selection-modify-around-input.html new file mode 100644 index 000000000000..24b9ae7d35be --- /dev/null +++ b/testing/web-platform/tests/selection/crashtests/selection-modify-around-input.html @@ -0,0 +1,24 @@ + + + + + + + + + + +
+ +