Skip to content

Prevent infinite loop in revealing algorithms #11457

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Aug 1, 2025
Merged

Conversation

josepharhar
Copy link
Contributor

@josepharhar josepharhar commented Jul 15, 2025

The ancestor details revealing algorithm and the hidden until found revealing algorithm may both get stuck in infinite loops because they iterate the flat tree while firing synchronous events. If the page were to listen to these events and change the flat tree in response, then the algorithm could theoretically never end.

Fixes #11436

(See WHATWG Working Mode: Changes for more details.)


/browsing-the-web.html ( diff )
/interaction.html ( diff )
/interactive-elements.html ( diff )

The ancestor details revealing algorithm and the hidden until found
revealing algorithm may both get stuck in infinite loops because they
iterate the flat tree while firing synchronous events. If the page were
to listen to these events and change the flat tree in response, then the
algorithm could theoretically never end.

Fixes whatwg#11436
@nt1m nt1m requested a review from annevk July 15, 2025 20:32
Copy link
Member

@domenic domenic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Editorially LGTM, although some implementer review from @nt1m and/or @jnjaeschke would be appreciated.

Copy link
Member

@nt1m nt1m left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think going through every single ancestor twice is very unefficient. The second loop should only go through relevant elements, rather than all of the ancestors again. I know it may result in some behavior difference in some very specific edge cases, but I'd rather have an efficient algorithm here.

@annevk
Copy link
Member

annevk commented Jul 16, 2025

Yeah, this seems like the right time to combine these algorithms so we only do a single tree walk. That's an observable change in behavior due to the events, but I think it's very much an acceptable one. And if we ever add more cases we care about we don't end up with ever more ancestor walks.

@nt1m
Copy link
Member

nt1m commented Jul 16, 2025

Just to be clear, I was pointing out that the current PR collects all the ancestors into a list for both algorithms, instead of only the relevant ones for each algorithm, which is different from what Anne mentioned in the previous comment.

(But having 1 reveal algorithm instead of 2 separate ones as Anne suggests wouldn't be something I'm opposed to, if the change in order of events/revealing makes sense for web developers)

@annevk
Copy link
Member

annevk commented Jul 16, 2025

@nt1m and I discussed this some more and while in theory you could do a single ancestor walk and collect the nodes to dispatch events on in separate lists depending on the feature, my suspicion is that it would be more useful for web developers if the events are also collapsed into a single reverse-tree-ordered list.

@josepharhar
Copy link
Contributor Author

Thanks, I combined the algorithms and only stored relevant nodes for the second traversal. How does it look?

Copy link
Member

@annevk annevk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After we fire the first event, do we want to check for each node we go through that it's still connected?

And maybe check that the pre-conditions are still valid?

And maybe that if any of that is no longer true, we return early as we're no longer sure what we're dealing with.

@josepharhar
Copy link
Contributor Author

After we fire the first event, do we want to check for each node we go through that it's still connected?

And maybe check that the pre-conditions are still valid?

And maybe that if any of that is no longer true, we return early as we're no longer sure what we're dealing with.

I added checks to make sure that each ancestor to reveal is still connected and still has the open attribute if its a details or still has hidden=until-found otherwise, and stop the algorithm if any of those are not true

Copy link
Member

@annevk annevk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks pretty good. Couple of nits.

Copy link
Member

@annevk annevk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me, modulo a couple of nits.

@annevk
Copy link
Member

annevk commented Jul 30, 2025

@jnjaeschke @nt1m do we already have bugs tracking this change for Gecko and WebKit?

@jnjaeschke
Copy link

There's no Gecko bug that I know of.

@josepharhar
Copy link
Contributor Author

I filed a gecko bug and put it in the OP

Comment on lines +80576 to +80581
<li><p>If <var>ancestorToReveal</var> is not <span>connected</span>, then return.</p></li>

<li><p>If <var>ancestorToReveal</var>'s <code data-x="attr-hidden">hidden</code> attribute is
not in the <span data-x="attr-hidden-until-found-state">Hidden Until Found</span> state, then
return.</p></li>

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm currently discussing this with @annevk but I'm not convinced these checks are good for component based websites. The fact that child components can break parent components behaviors seems pretty unexpected to me. E.g. a parent component that is expecting revealing behavior on find-in-page should not have that be unintentionally prevented by a child component getting removed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this PR can be merged as is for now, I'll raise a follow up issue if needed.

@annevk annevk merged commit 55baf05 into whatwg:main Aug 1, 2025
2 checks passed
nt1m added a commit to nt1m/WebKit that referenced this pull request Aug 1, 2025
https://bugs.webkit.org/show_bug.cgi?id=296797
rdar://157280008

Reviewed by NOBODY (OOPS!).

Follow whatwg/html#11457

* Source/WebCore/dom/FindRevealAlgorithms.cpp:
(WebCore::revealClosedDetailsAndHiddenUntilFoundAncestors):
(WebCore::revealClosedDetailsAncestors): Deleted.
(WebCore::revealHiddenUntilFoundAncestors): Deleted.
* Source/WebCore/dom/FindRevealAlgorithms.h:
nt1m added a commit to nt1m/WebKit that referenced this pull request Aug 1, 2025
https://bugs.webkit.org/show_bug.cgi?id=296797
rdar://157280008

Reviewed by NOBODY (OOPS!).

Follow whatwg/html#11457

* Source/WebCore/dom/FindRevealAlgorithms.cpp:
(WebCore::revealClosedDetailsAndHiddenUntilFoundAncestors):
(WebCore::revealClosedDetailsAncestors): Deleted.
(WebCore::revealHiddenUntilFoundAncestors): Deleted.
* Source/WebCore/dom/FindRevealAlgorithms.h:
nt1m added a commit to nt1m/WebKit that referenced this pull request Aug 2, 2025
https://bugs.webkit.org/show_bug.cgi?id=296797
rdar://157280008

Reviewed by NOBODY (OOPS!).

Follow whatwg/html#11457

* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-attribute-removal-001-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-attribute-removal-001.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-attribute-removal-002-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-attribute-removal-002.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-element-removal-001-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-element-removal-001.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-element-removal-002-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-element-removal-002.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/hidden-until-found-and-details-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/hidden-until-found-and-details.html: Added.
* Source/WebCore/dom/FindRevealAlgorithms.cpp:
(WebCore::revealClosedDetailsAndHiddenUntilFoundAncestors):
(WebCore::revealClosedDetailsAncestors): Deleted.
(WebCore::revealHiddenUntilFoundAncestors): Deleted.
* Source/WebCore/dom/FindRevealAlgorithms.h:
nt1m added a commit to nt1m/WebKit that referenced this pull request Aug 2, 2025
https://bugs.webkit.org/show_bug.cgi?id=296797
rdar://157280008

Reviewed by NOBODY (OOPS!).

Follow whatwg/html#11457

Also write a bunch of new WPT for ordering & state mutations during the beforematch event.

* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-attribute-removal-001-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-attribute-removal-001.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-attribute-removal-002-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-attribute-removal-002.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-element-removal-001-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-element-removal-001.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-element-removal-002-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-element-removal-002.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-infinite-loop-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/hidden-until-found-and-details-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/hidden-until-found-and-details.html: Added.
* Source/WebCore/dom/FindRevealAlgorithms.cpp:
(WebCore::revealClosedDetailsAndHiddenUntilFoundAncestors):
(WebCore::revealClosedDetailsAncestors): Deleted.
(WebCore::revealHiddenUntilFoundAncestors): Deleted.
* Source/WebCore/dom/FindRevealAlgorithms.h:
nt1m added a commit to nt1m/WebKit that referenced this pull request Aug 2, 2025
https://bugs.webkit.org/show_bug.cgi?id=296797
rdar://157280008

Reviewed by NOBODY (OOPS!).

Follow whatwg/html#11457 which:
- Prevents infinite looping due to mutations on the beforematch event, by collecting the ancestors beforehand (covered by beforematch-infinite-loop.html)
- Combines both details and hidden=until-found ancestor revealing algorithms into a single one, to avoid two separate traversals (covered by hidden-until-found-and-details.html)
- Adds checks for attribute/element removal mutations during the beforematch event (covered by removal WPTs)

Also write WPTs for previously uncovered parts.

* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-attribute-removal-001-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-attribute-removal-001.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-attribute-removal-002-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-attribute-removal-002.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-element-removal-001-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-element-removal-001.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-element-removal-002-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-element-removal-002.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-infinite-loop-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/hidden-until-found-and-details-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/hidden-until-found-and-details.html: Added.
* Source/WebCore/dom/FindRevealAlgorithms.cpp:
(WebCore::revealClosedDetailsAndHiddenUntilFoundAncestors):
(WebCore::revealClosedDetailsAncestors): Deleted.
(WebCore::revealHiddenUntilFoundAncestors): Deleted.
* Source/WebCore/dom/FindRevealAlgorithms.h:
@nt1m
Copy link
Member

nt1m commented Aug 2, 2025

@josepharhar @jnjaeschke I wrote a bunch of WPTs here: web-platform-tests/wpt#54116

nt1m added a commit to nt1m/WebKit that referenced this pull request Aug 2, 2025
https://bugs.webkit.org/show_bug.cgi?id=296797
rdar://157280008

Reviewed by NOBODY (OOPS!).

Follow whatwg/html#11457 which:
- Prevents infinite looping due to mutations on the beforematch event, by collecting the ancestors beforehand (covered by beforematch-infinite-loop.html)
- Combines both details and hidden=until-found ancestor revealing algorithms into a single one, to avoid two separate traversals (covered by hidden-until-found-and-details.html)
- Adds checks for attribute/element removal mutations during the beforematch event (covered by removal WPTs)

Also write WPTs for previously uncovered parts.

* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-attribute-removal-001-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-attribute-removal-001.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-attribute-removal-002-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-attribute-removal-002.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-element-removal-001-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-element-removal-001.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-element-removal-002-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-element-removal-002.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-infinite-loop-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/hidden-until-found-and-details-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/hidden-until-found-and-details.html: Added.
* Source/WebCore/dom/FindRevealAlgorithms.cpp:
(WebCore::revealClosedDetailsAndHiddenUntilFoundAncestors):
(WebCore::revealClosedDetailsAncestors): Deleted.
(WebCore::revealHiddenUntilFoundAncestors): Deleted.
* Source/WebCore/dom/FindRevealAlgorithms.h:
nt1m added a commit to nt1m/WebKit that referenced this pull request Aug 2, 2025
https://bugs.webkit.org/show_bug.cgi?id=296797
rdar://157280008

Reviewed by NOBODY (OOPS!).

Follow whatwg/html#11457 which:
- Prevents infinite looping due to mutations on the beforematch event, by collecting the ancestors beforehand (covered by beforematch-infinite-loop.html)
- Combines both details and hidden=until-found ancestor revealing algorithms into a single one, to avoid two separate traversals (covered by hidden-until-found-and-details.html)
- Adds checks for attribute/element removal mutations during the beforematch event (covered by removal WPTs)

Also write WPTs for previously uncovered parts.

* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-attribute-removal-001-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-attribute-removal-001.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-attribute-removal-002-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-attribute-removal-002.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-element-removal-001-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-element-removal-001.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-element-removal-002-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-element-removal-002.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-infinite-loop-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/hidden-until-found-and-details-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/hidden-until-found-and-details.html: Added.
* Source/WebCore/dom/FindRevealAlgorithms.cpp:
(WebCore::revealClosedDetailsAndHiddenUntilFoundAncestors):
(WebCore::revealClosedDetailsAncestors): Deleted.
(WebCore::revealHiddenUntilFoundAncestors): Deleted.
* Source/WebCore/dom/FindRevealAlgorithms.h:
webkit-commit-queue pushed a commit to nt1m/WebKit that referenced this pull request Aug 2, 2025
https://bugs.webkit.org/show_bug.cgi?id=296797
rdar://157280008

Reviewed by Darin Adler.

Follow whatwg/html#11457 which:
- Prevents infinite looping due to mutations on the beforematch event, by collecting the ancestors beforehand (covered by beforematch-infinite-loop.html)
- Combines both details and hidden=until-found ancestor revealing algorithms into a single one, to avoid two separate traversals (covered by hidden-until-found-and-details.html)
- Adds checks for attribute/element removal mutations during the beforematch event (covered by removal WPTs)

Also write WPTs for previously uncovered parts.

* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-attribute-removal-001-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-attribute-removal-001.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-attribute-removal-002-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-attribute-removal-002.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-element-removal-001-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-element-removal-001.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-element-removal-002-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-element-removal-002.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/beforematch-infinite-loop-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/hidden-until-found-and-details-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/editing/the-hidden-attribute/hidden-until-found-and-details.html: Added.
* Source/WebCore/dom/FindRevealAlgorithms.cpp:
(WebCore::revealClosedDetailsAndHiddenUntilFoundAncestors):
(WebCore::revealClosedDetailsAncestors): Deleted.
(WebCore::revealHiddenUntilFoundAncestors): Deleted.
* Source/WebCore/dom/FindRevealAlgorithms.h:

Canonical link: https://commits.webkit.org/298164@main
aarongable pushed a commit to chromium/chromium that referenced this pull request Aug 12, 2025
This patch implements the spec changes for the auto-expanding algorithms
for details elements and hidden=until-found:
whatwg/html#11457

I2S: https://groups.google.com/a/chromium.org/g/blink-dev/c/SeRScSVsUXY

Fixed: 433545121
Change-Id: I09d58f2c1b97c98c33d9c577369cb877198404c5
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6821516
Reviewed-by: Mason Freed <[email protected]>
Commit-Queue: Joey Arhar <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1500237}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

hidden=until-found and details revealing algorithms can loop infinitely
5 participants