Intent to Ship: Close requests for CloseWatcher, <dialog>, and popover=""

646 views
Skip to first unread message

Domenic Denicola

unread,
Sep 27, 2023, 10:43:49 PM9/27/23
to blink-dev

Contact emails

jap...@chromium.orgdom...@chromium.orgjar...@chromium.org

Explainer

https://github.com/WICG/close-watcher/blob/main/README.md

Specification

https://github.com/whatwg/html/pull/9462

Summary

"Close requests" are a new concept that encompasses user requests to close something currently open, using the Esc key on desktop or the back gesture/button on Android. Integrating them into Chromium comes with two changes: * CloseWatcher, a new API for directly listening and responding to close requests. * Upgrades to <dialog> and popover="" to use the new close request framework, so that they respond to the Android back button.



Blink component

Blink

TAG review

https://github.com/w3ctag/design-reviews/issues/594

TAG review status

Issues addressed

Risks



Interoperability and Compatibility

This API is designed to have an interoperable surface for web developers, to help them avoid platform-specific code. So, if it were implemented across browsers, it would be a positive for interoperability. Otherwise, it has the usual risks of not getting adopted by other vendors. Compatibility: To avoid allowing CloseWatchers, dialogs, and popovers ("close watchers") to prevent the Android back gesture/button from navigating through history, how close watchers respond to close requests depends on user activation. If no user activation occurs between opening, and the user issuing a close request, this can cause a CloseWatcher/dialog's cancel event to be skipped, or cause multiple close watchers to be closed at once. Although this behavior is meant to prevent back-trapping on Android specifically, it applies to desktop as well, for interoperability reasons. This change is a compatibility risk. However, use counters show it to be an acceptable one: - 0.000015% of pages impacted by skipped cancel events - 0.000007% of pages impacted by skipped cancel events that would otherwise call preventDefault() - between 0.000000% and 0.000001% of pages impacted by multiple dialogs closed



Gecko: Positive (https://github.com/mozilla/standards-positions/issues/604)

WebKit: No signal (https://github.com/WebKit/standards-positions/issues/215)

Web developers: Positive (https://github.com/w3ctag/design-reviews/issues/594#issuecomment-890257686) See also https://bugs.chromium.org/p/chromium/issues/detail?id=1319915

Other signals:

Activation

The CloseWatcher API is meant to be usable as a progressive enhancement; if developers use it with feature detection, then their app will be able to watch for unusual close watchers in supporting browsers, while falling back to listening for the Esc key in browsers that haven't implemented the API. It would benefit from a conditional polyfill that translates the Esc key into a close signal, so that then developers don't even have to have feature detection and fallback logic, but can just use the CloseWatcher API surface. One such polyfill is available in the demo: https://close-watcher-demo.glitch.me/



Security

The main security-related concern in this API is preventing it from being usable for back-trapping, i.e. disabling the Android back gesture/button. Although this is already possible in Chromium and other browsers due to bugs, we worked to ensure CloseWatcher and close request integration to dialogs/popups does not increase the size of the problem, by gating repeated use of these behind transient user activation checks: see https://github.com/WICG/close-watcher#abuse-analysis



WebView application risks

Does this intent deprecate or change behavior of existing APIs, such that it has potentially high risk for Android WebView-based applications?

Beyond the low risks already listed in the Compat section, we do not anticipate any WebView-specific risks. A base::Feature killswitch is available just in case.



Debuggability

No special DevTools support is required.



Will this feature be supported on all six Blink platforms (Windows, Mac, Linux, Chrome OS, Android, and Android WebView)?

Yes

Is this feature fully tested by web-platform-tests?

Yes

Flag name on chrome://flags

CloseWatcher

Finch feature name

CloseWatcher

Requires code in //chrome?

False

Tracking bug

https://bugs.chromium.org/p/chromium/issues/detail?id=1171318

Non-OSS dependencies

Does the feature depend on any code or APIs outside the Chromium open source repository and its open-source dependencies to function?

No.

Sample links


https://close-watcher-demo.glitch.me

Estimated milestones

Shipping on desktop119
DevTrial on desktop97
Shipping on Android119
DevTrial on Android97
Shipping on WebView119


Anticipated spec changes

Open questions about a feature may be a source of future web compat or interop issues. Please list open issues (e.g. links to known github issues in the project for the feature specification) whose resolution may introduce web compat/interop risk (e.g., changing to naming or structure of the API in a non-backward-compatible way).

None.

Link to entry on the Chrome Platform Status

https://chromestatus.com/feature/4722261258928128

Links to previous Intent discussions

Intent to prototype: https://groups.google.com/a/chromium.org/g/blink-dev/c/NA5NC16OmsU

This intent message was generated by Chrome Platform Status.

Alex Russell

unread,
Sep 29, 2023, 4:01:50 PM9/29/23
to blink-dev, Domenic Denicola
The implicit behaviours based on construction order in this API are very strange and seem like footguns. The TAG feedback didn't touch on this very much, AFAICT, but it's somewhat surprising that the stack of close actions isn't inspectable. What's the behaviour of non-`destroy()`'d watchers; e.g. if a developer forgets to dispose of one correctly? Can users get stuck?

On Wednesday, September 27, 2023 at 7:43:49 PM UTC-7 Domenic Denicola wrote:

Domenic Denicola

unread,
Oct 2, 2023, 12:08:57 AM10/2/23
to Alex Russell, blink-dev, Domenic Denicola
On Sat, Sep 30, 2023 at 5:01 AM Alex Russell <sligh...@chromium.org> wrote:
The implicit behaviours based on construction order in this API are very strange and seem like footguns.

I don't understand why you find this strange, or a footgun. It's intended to be the opposite: it guides developers toward creating the experience the user expects, where when the user requests to close something, the last thing that was opened, is what closes.
 
The TAG feedback didn't touch on this very much, AFAICT, but it's somewhat surprising that the stack of close actions isn't inspectable.

I can't speak for the TAG, but here are the reasons why the stack of close watchers isn't inspectable:
  • We received no developer or partner feedback requesting this capability
  • This could cause potential forward-compat problems without careful design. E.g., it could make it possible for developers to write code that assumes that only CloseWatchers, dialogs, and popover="" elements are close watchers, and thus make it hard for the web platform to introduce a fourth close watcher (e.g., <selectlist>) in the future.
  • This would be somewhat of an encapsulation leak between different parts of the application, making it harder to write resilient components. (This is not a strong argument, but rather a bias toward waiting for a use case instead of just exposing the information automatically.)
 
What's the behaviour of non-`destroy()`'d watchers; e.g. if a developer forgets to dispose of one correctly? Can users get stuck?

Non-destroy()ed is the default state of a CloseWatcher, so such CloseWatchers will respond to the next close request if they are on the top of the stack. The user cannot really get stuck, as every close request will either destroy the topmost close watcher on the stack, or possibly trigger (at most once) a preventDefault()ed cancel event. See https://github.com/WICG/close-watcher/blob/main/README.md#abuse-analysis for more details.

Note that the API generally guides you away from this possibility by making the simpler code be the one that automatically calls destroy() for you: https://github.com/WICG/close-watcher/blob/main/README.md#requesting-close-yourself .
 

On Wednesday, September 27, 2023 at 7:43:49 PM UTC-7 Domenic Denicola wrote:

Alex Russell

unread,
Oct 2, 2023, 1:11:27 PM10/2/23
to blink-dev, Domenic Denicola, blink-dev, Alex Russell
On Sunday, October 1, 2023 at 9:08:57 PM UTC-7 Domenic Denicola wrote:
On Sat, Sep 30, 2023 at 5:01 AM Alex Russell <sligh...@chromium.org> wrote:
The implicit behaviours based on construction order in this API are very strange and seem like footguns.

I don't understand why you find this strange, or a footgun. It's intended to be the opposite: it guides developers toward creating the experience the user expects, where when the user requests to close something, the last thing that was opened, is what closes.

Chris Palmer covered this pretty well recently, so I'll defer to his more eloquent writeup:

https://noncombatant.org/2023/05/29/complexities-of-allocation/

Basically, this is spooky action at a distance and without _at least_ some reflection and manipulation surface (via DOM, probably), it's hard to understand how this won't turn into a footgun.

As a separate note, I'm disappointed in the proliferation of APIs that affect DOM but have no API and reflection. Import Maps spring to mind, but there are other recent examples too. If manual disposal is going to be required for this, we should at least make it possible to introspect outside the scope in which an object that defines this behaviour is allocated.
 
The TAG feedback didn't touch on this very much, AFAICT, but it's somewhat surprising that the stack of close actions isn't inspectable.

I can't speak for the TAG, but here are the reasons why the stack of close watchers isn't inspectable:
  • We received no developer or partner feedback requesting this capability
  • This could cause potential forward-compat problems without careful design. E.g., it could make it possible for developers to write code that assumes that only CloseWatchers, dialogs, and popover="" elements are close watchers, and thus make it hard for the web platform to introduce a fourth close watcher (e.g., <selectlist>) in the future.
  • This would be somewhat of an encapsulation leak between different parts of the application, making it harder to write resilient components. (This is not a strong argument, but rather a bias toward waiting for a use case instead of just exposing the information automatically.)
Thanks, I appreciate the context, and I am impressed by the thoroughness of the design artifacts.
 
What's the behaviour of non-`destroy()`'d watchers; e.g. if a developer forgets to dispose of one correctly? Can users get stuck?

Non-destroy()ed is the default state of a CloseWatcher, so such CloseWatchers will respond to the next close request if they are on the top of the stack. The user cannot really get stuck, as every close request will either destroy the topmost close watcher on the stack, or possibly trigger (at most once) a preventDefault()ed cancel event. See https://github.com/WICG/close-watcher/blob/main/README.md#abuse-analysis for more details.

Also helpful; thank you!
 
Note that the API generally guides you away from this possibility by making the simpler code be the one that automatically calls destroy() for you: https://github.com/WICG/close-watcher/blob/main/README.md#requesting-close-yourself .
On Wednesday, September 27, 2023 at 7:43:49 PM UTC-7 Domenic Denicola wrote:


Summary

"Close requests" are a new concept that encompasses user requests to close something currently open, using the Esc key on desktop or the back gesture/button on Android. Integrating them into Chromium comes with two changes: * CloseWatcher, a new API for directly listening and responding to close requests. * Upgrades to <dialog> and popover="" to use the new close request framework, so that they respond to the Android back button.



Blink componentBlink

TAG reviewhttps://github.com/w3ctag/design-reviews/issues/594

TAG review statusIssues addressed


Risks


Interoperability and Compatibility

This API is designed to have an interoperable surface for web developers, to help them avoid platform-specific code. So, if it were implemented across browsers, it would be a positive for interoperability. Otherwise, it has the usual risks of not getting adopted by other vendors. Compatibility: To avoid allowing CloseWatchers, dialogs, and popovers ("close watchers") to prevent the Android back gesture/button from navigating through history, how close watchers respond to close requests depends on user activation. If no user activation occurs between opening, and the user issuing a close request, this can cause a CloseWatcher/dialog's cancel event to be skipped, or cause multiple close watchers to be closed at once. Although this behavior is meant to prevent back-trapping on Android specifically, it applies to desktop as well, for interoperability reasons. This change is a compatibility risk. However, use counters show it to be an acceptable one: - 0.000015% of pages impacted by skipped cancel events - 0.000007% of pages impacted by skipped cancel events that would otherwise call preventDefault() - between 0.000000% and 0.000001% of pages impacted by multiple dialogs closed



Gecko: Positive (https://github.com/mozilla/standards-positions/issues/604)

WebKit: No signal (https://github.com/WebKit/standards-positions/issues/215)

Web developers: Positive (https://github.com/w3ctag/design-reviews/issues/594#issuecomment-890257686) See also https://bugs.chromium.org/p/chromium/issues/detail?id=1319915

Other signals:

Activation

The CloseWatcher API is meant to be usable as a progressive enhancement; if developers use it with feature detection, then their app will be able to watch for unusual close watchers in supporting browsers, while falling back to listening for the Esc key in browsers that haven't implemented the API. It would benefit from a conditional polyfill that translates the Esc key into a close signal, so that then developers don't even have to have feature detection and fallback logic, but can just use the CloseWatcher API surface. One such polyfill is available in the demo: https://close-watcher-demo.glitch.me/



Security

The main security-related concern in this API is preventing it from being usable for back-trapping, i.e. disabling the Android back gesture/button. Although this is already possible in Chromium and other browsers due to bugs, we worked to ensure CloseWatcher and close request integration to dialogs/popups does not increase the size of the problem, by gating repeated use of these behind transient user activation checks: see https://github.com/WICG/close-watcher#abuse-analysis



WebView application risks

Does this intent deprecate or change behavior of existing APIs, such that it has potentially high risk for Android WebView-based applications?

Beyond the low risks already listed in the Compat section, we do not anticipate any WebView-specific risks. A base::Feature killswitch is available just in case.



Debuggability

No special DevTools support is required.



Will this feature be supported on all six Blink platforms (Windows, Mac, Linux, Chrome OS, Android, and Android WebView)?Yes

Is this feature fully tested by web-platform-tests?Yes

Flag name on chrome://flagsCloseWatcher

Finch feature nameCloseWatcher

Requires code in //chrome?False



Non-OSS dependencies

Does the feature depend on any code or APIs outside the Chromium open source repository and its open-source dependencies to function?

No.

Sample links
https://close-watcher-demo.glitch.me

Estimated milestonesShipping on desktop119DevTrial on desktop97Shipping on Android119DevTrial on Android97Shipping on WebView119

Anticipated spec changes

Open questions about a feature may be a source of future web compat or interop issues. Please list open issues (e.g. links to known github issues in the project for the feature specification) whose resolution may introduce web compat/interop risk (e.g., changing to naming or structure of the API in a non-backward-compatible way).

None.

Link to entry on the Chrome Platform Statushttps://chromestatus.com/feature/4722261258928128

Links to previous Intent discussionsIntent to prototype: https://groups.google.com/a/chromium.org/g/blink-dev/c/NA5NC16OmsU

Domenic Denicola

unread,
Oct 2, 2023, 1:16:53 PM10/2/23
to Alex Russell, blink-dev, Domenic Denicola


2023年10月2日(月) 10:11 Alex Russell <sligh...@chromium.org>:


On Sunday, October 1, 2023 at 9:08:57 PM UTC-7 Domenic Denicola wrote:
On Sat, Sep 30, 2023 at 5:01 AM Alex Russell <sligh...@chromium.org> wrote:
The implicit behaviours based on construction order in this API are very strange and seem like footguns.

I don't understand why you find this strange, or a footgun. It's intended to be the opposite: it guides developers toward creating the experience the user expects, where when the user requests to close something, the last thing that was opened, is what closes.

Chris Palmer covered this pretty well recently, so I'll defer to his more eloquent writeup:

https://noncombatant.org/2023/05/29/complexities-of-allocation/

Basically, this is spooky action at a distance and without _at least_ some reflection and manipulation surface (via DOM, probably), it's hard to understand how this won't turn into a footgun.

As a separate note, I'm disappointed in the proliferation of APIs that affect DOM but have no API and reflection. Import Maps spring to mind, but there are other recent examples too. If manual disposal is going to be required for this, we should at least make it possible to introspect outside the scope in which an object that defines this behaviour is allocated.

In what way does this API affect the DOM? No parts of the DOM tree are modified by CloseWatcher. The same is true for import maps...

Alex Russell

unread,
Oct 4, 2023, 11:16:23 AM10/4/23
to blink-dev, Domenic Denicola, blink-dev, Alex Russell
On Monday, October 2, 2023 at 10:16:53 AM UTC-7 Domenic Denicola wrote:


2023年10月2日(月) 10:11 Alex Russell <sligh...@chromium.org>:


On Sunday, October 1, 2023 at 9:08:57 PM UTC-7 Domenic Denicola wrote:
On Sat, Sep 30, 2023 at 5:01 AM Alex Russell <sligh...@chromium.org> wrote:
The implicit behaviours based on construction order in this API are very strange and seem like footguns.

I don't understand why you find this strange, or a footgun. It's intended to be the opposite: it guides developers toward creating the experience the user expects, where when the user requests to close something, the last thing that was opened, is what closes.

Chris Palmer covered this pretty well recently, so I'll defer to his more eloquent writeup:

https://noncombatant.org/2023/05/29/complexities-of-allocation/

Basically, this is spooky action at a distance and without _at least_ some reflection and manipulation surface (via DOM, probably), it's hard to understand how this won't turn into a footgun.

As a separate note, I'm disappointed in the proliferation of APIs that affect DOM but have no API and reflection. Import Maps spring to mind, but there are other recent examples too. If manual disposal is going to be required for this, we should at least make it possible to introspect outside the scope in which an object that defines this behaviour is allocated.

In what way does this API affect the DOM? No parts of the DOM tree are modified by CloseWatcher. The same is true for import maps...

This is view state, which is frequently reflected via DOM. The primary concern here is that there's no way to inspect and/or modify the stack (attached to Node instances or not) independently of closure-scoped object lifetimes.

Domenic Denicola

unread,
Oct 4, 2023, 11:28:55 AM10/4/23
to Alex Russell, blink-dev, Domenic Denicola


2023年10月4日(水) 8:16 Alex Russell <sligh...@chromium.org>:


On Monday, October 2, 2023 at 10:16:53 AM UTC-7 Domenic Denicola wrote:


2023年10月2日(月) 10:11 Alex Russell <sligh...@chromium.org>:


On Sunday, October 1, 2023 at 9:08:57 PM UTC-7 Domenic Denicola wrote:
On Sat, Sep 30, 2023 at 5:01 AM Alex Russell <sligh...@chromium.org> wrote:
The implicit behaviours based on construction order in this API are very strange and seem like footguns.

I don't understand why you find this strange, or a footgun. It's intended to be the opposite: it guides developers toward creating the experience the user expects, where when the user requests to close something, the last thing that was opened, is what closes.

Chris Palmer covered this pretty well recently, so I'll defer to his more eloquent writeup:

https://noncombatant.org/2023/05/29/complexities-of-allocation/

Basically, this is spooky action at a distance and without _at least_ some reflection and manipulation surface (via DOM, probably), it's hard to understand how this won't turn into a footgun.

As a separate note, I'm disappointed in the proliferation of APIs that affect DOM but have no API and reflection. Import Maps spring to mind, but there are other recent examples too. If manual disposal is going to be required for this, we should at least make it possible to introspect outside the scope in which an object that defines this behaviour is allocated.

In what way does this API affect the DOM? No parts of the DOM tree are modified by CloseWatcher. The same is true for import maps...

This is view state, which is frequently reflected via DOM. The primary concern here is that there's no way to inspect and/or modify the stack (attached to Node instances or not) independently of closure-scoped object lifetimes.

It's not clear to me what definition of "view state" you are using, such that it encompasses things like the module specifier resolution algorithm or the routing of Android back gestures.

Maybe, if this is a principle you believe in, you could file it as a suggestion on the w3ctag/design-principles repository, ideally with a clear explanation of what the boundaries of this "view state" concept are. (Including what, in your view, would *not* quality as view state.)

Chris Harrelson

unread,
Oct 5, 2023, 6:34:21 PM10/5/23
to Domenic Denicola, Alex Russell, blink-dev
Hi,

While Alex's concerns are totally valid to consider from a feature design perspective, I think they are better to be discussed on the WHATWG issues for this feature. I chatted offline with Alex and he agreed about that point, and agreed to post comments and questions there.

So from an API owners perspective LGTM1 modulo considering and taking into account all comments and feedback from Alex on the spec (as we should for all such feedback from anyone, of course!).


--
You received this message because you are subscribed to the Google Groups "blink-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to blink-dev+...@chromium.org.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/blink-dev/CAM0wra-SULEU6D-HmDDuf%3D5T9faNVk_LcqjKxY%3Do%3Du-vqTzaag%40mail.gmail.com.

Yoav Weiss

unread,
Oct 11, 2023, 11:51:06 AM10/11/23
to blink-dev, Chris Harrelson, Alex Russell, blink-dev, Domenic Denicola
What's preventing the PR from landing?
 
To unsubscribe from this group and stop receiving emails from it, send an email to blink-dev+unsubscribe@chromium.org.

Domenic Denicola

unread,
Oct 11, 2023, 7:59:06 PM10/11/23
to Yoav Weiss, Philip Jägenstedt, blink-dev, Chris Harrelson, Alex Russell, Domenic Denicola
It needs review, and none of the other editors have made the time yet. (Maybe +Philip Jägenstedt could help?)

Philip Jägenstedt

unread,
Oct 18, 2023, 11:48:14 AM10/18/23
to Domenic Denicola, Yoav Weiss, blink-dev, Chris Harrelson, Alex Russell
The spec change has now landed, LGTM2.

More introspection could possibly be useful, but without a concrete use case, example code, or developer feedback, I think it's hard to do a good job. Having reviewed the spec change, I'm confident that exposing more information is technically straightforward if the need arises.

Yoav Weiss

unread,
Oct 18, 2023, 11:59:44 AM10/18/23
to Philip Jägenstedt, Domenic Denicola, blink-dev, Chris Harrelson, Alex Russell
LGTM3

I agree that introspection can be additive on top of what we want to ship here.

Domenic Denicola

unread,
Jan 10, 2024, 9:23:28 PMJan 10
to Yoav Weiss, Philip Jägenstedt, Domenic Denicola, blink-dev, Chris Harrelson, Alex Russell
Hi all,

Over the holidays, some developers experienced the 0.000015% of pages impacted by skipped cancel events on <dialog>s, and filed a regression bug. Out of an abundance of caution, we disabled this feature remotely using Finch for M120 and M121, and then disabled it in the codebase for M122.

In the subsequent discussions, we discovered two possible spec changes we could make to drive down that 0.000015% even further:
  • A bug fix to allow cancel events to fire for the first-created close watcher, as long as it was created by user activation. Even if the user never interacts with the <dialog>. Spec issue, spec PR, wip CL.
  • A more significant change, to consider firing cancel events even if you cannot call preventDefault(). Spec issue.
Our plan is to implement the first of these bug fixes, which will address the cases reported on the Chromium bug tracker so far, and then re-enable the feature starting in M122.

In parallel we'll explore fast-following with the second change, after a bit more discussion with the community. (The main goal of the second change is to enable a use case; we don't think the compat improvement is urgent. See more discussion on the spec issue.)

Let me know if there are any concerns,
-Domenic

Philip Jägenstedt

unread,
Jan 11, 2024, 3:02:05 AMJan 11
to Domenic Denicola, Yoav Weiss, blink-dev, Chris Harrelson, Alex Russell
Thanks for the update, Domenic, that sounds like a solid plan to me. Keeping existing content working is important, and if one developer filed a bug others have probably been affected too. Happily the spec fix doesn't seem like it complicates things, either.
Reply all
Reply to author
Forward
0 new messages