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
This commit is contained in:
Masayuki Nakano 2023-03-17 06:26:02 +00:00
parent 585fe519f1
commit 458f69ffc8
3 changed files with 46 additions and 1 deletions

View File

@ -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();
};

View File

@ -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

View File

@ -0,0 +1,24 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<style>
div {
border: medium solid red;
border-width: 32em;
}
</style>
<script>
document.addEventListener("DOMContentLoaded", () => {
document.execCommand("selectAll");
getSelection().modify("move", "backward", "character");
getSelection().collapseToStart();
}, {once: true});
</script>
</head>
<body>
<button contenteditable></button>
<input value="a">
<div></div>
</body>
</html>