├── README.md └── explainers ├── css-shadow-parts-1.md ├── devtools.png ├── permissions-policy-deprecate-unload.md ├── permissions-policy-unload.md └── queueMicrotask.md /README.md: -------------------------------------------------------------------------------- 1 | # Doc 2 | A place to store things for public consumption 3 | -------------------------------------------------------------------------------- /explainers/css-shadow-parts-1.md: -------------------------------------------------------------------------------- 1 | # Explainer: CSS Shadow ::part and ::theme 2 | 3 | ## About this doc 4 | 5 | This explainer is adapted from [Monica Dinculescu](https://meowni.ca/about/)'s [original explainer](https://meowni.ca/posts/part-theme-explainer/) 6 | by [Fergal Daly](mailto:fergal@chromium.org) 7 | and updated to match the latest spec. 8 | 9 | ## Motivation 10 | 11 | [Shadow DOM](https://www.w3.org/TR/shadow-dom/) is a spec 12 | that gives you DOM and style encapsulation. 13 | This is great for reusable [web components](https://meowni.ca/posts/web-components-with-otters/), 14 | as it reduces the ability of these components’ styles 15 | getting accidentally stomped over 16 | (the old "I have a class called `button` 17 | and you have a class called `button`, 18 | now we both look busted" problem), 19 | but it adds a barrier for styling and theming these components deliberately. 20 | 21 | When talking about styling a component, 22 | there are usually two different problems you might want to solve: 23 | 24 | - *Styling*: I am using a third-party `` element on my site 25 | and I want this one to be blue. 26 | 27 | - *Theming*: I am using many third-party elements on my site, 28 | and some of them have a ``; 29 | I want all the ``s to be blue. 30 | 31 | ### Previous attempts at solving this problem 32 | 33 | There have been several previous attempts at solving this, 34 | some more successful than others. 35 | More detail is available in [this post](https://meowni.ca/posts/styling-the-dome/). 36 | The short version is: 37 | 38 | - First came `:shadow` and `/deep/` 39 | (which have since been deprecated, and removed as of Chrome 60). 40 | These were shadow-piercing selectors 41 | that allowed you to target any node in an element’s Shadow DOM. 42 | Apart from being terrible for performance, 43 | they also required the user of an element 44 | to be intimately familiar with some random element’s implementation, 45 | which was unlikely 46 | and lead to them just breaking the whole element by accident 47 | 48 | - [Custom properties](https://developer.mozilla.org/en-US/docs/Web/CSS/--*) allow you to create custom CSS properties 49 | that can be used throughout an app. 50 | In particular, they pierce the shadow boundary, 51 | which means they can be used for styling elements with a Shadow DOM: 52 | If `` uses a `--fancy-button-background` property to control its background, 53 | then: 54 | ```css 55 | fancy-button#one { --fancy-button-background: blue; } /* solves the styling problem and */ 56 | fancy-button { --fancy-button-background: blue; } /* solves the theming problem */ 57 | ``` 58 | 59 | - The problem with using just custom properties for styling/theming 60 | is that it places the onus on the element author 61 | to basically declare every possible styleable property as a custom property. 62 | As a result, `@apply` was proposed, 63 | which basically allowed a custom property to hold an entire ruleset 64 | (a bag of other properties!). 65 | [Tab Atkins](https://twitter.com/tabatkins) has a [post](https://www.xanthir.com/b4o00) 66 | on why this approach was abandoned, 67 | the tl;dr; is that it interacted pretty poorly with pseudo classes and elements 68 | (like `:focus`, `:hover`, `::placeholder` for input), 69 | which still meant the element author would have to define a looooot of these bags of properties 70 | to be used in the right places. 71 | 72 | ## A different approach 73 | 74 | The current new [proposal](https://drafts.csswg.org/css-shadow-parts-1/) is `::part` and `::theme`, 75 | a set of pseudo-elements that allow you to style inside a shadow tree, 76 | from outside of that shadow tree. 77 | Unlike `:shadow` and `/deep/`, 78 | they don’t allow you to style arbitrary elements inside a shadow tree: 79 | they only allow you to style elements that an author has tagged as being eligible for styling. 80 | 81 | ### How ::part works 82 | 83 | You can specify a "styleable" part on any element in your shadow tree: 84 | 85 | ```html 86 | 87 | #shadow-root 88 |
...
89 | 90 |
...
91 |
92 | ``` 93 | 94 | If you’re in a document that has an `` in it, then you can style those parts with: 95 | 96 | ```css 97 | x-foo::part(some-box) { ... } 98 | ``` 99 | 100 | You *can* use other pseudo elements or selectors 101 | (that were not explicitly exposed as shadow parts), 102 | so both of these work: 103 | 104 | ```css 105 | x-foo::part(some-box):hover { ... } 106 | x-foo::part(some-input)::placeholder { ... } 107 | ``` 108 | 109 | You *cannot* select inside of those parts, 110 | so this *doesn’t* work: 111 | 112 | ```css 113 | x-foo::part(some-box) span { ... } nor 114 | x-foo::part(some-box)::part(some-other-thing) { ... } 115 | ``` 116 | 117 | You *cannot* style this part more than one level up if you don’t forward it. 118 | So without any extra work, 119 | if you have an element that contains an `x-foo` like this: 120 | 121 | ```html 122 | 123 | #shadow-root 124 | 125 | 126 | ``` 127 | 128 | You *cannot* select and style the `x-foo`’s part like this: 129 | 130 | ```css 131 | x-bar::part(some-box) { ... } 132 | ``` 133 | 134 | ### Forwarding parts 135 | 136 | You *can* explicitly forward a child’s part 137 | to be styleable outside of the parent’s shadow tree. 138 | So in the previous example, 139 | to allow the some-box part to be styleable by `x-bar`’s parent, 140 | it would have to be exposed: 141 | 142 | ```html 143 | 144 | #shadow-root 145 | 146 | 147 | ``` 148 | 149 | The `::part` forwarding syntax has options a-plenty. 150 | 151 | - `exportparts="some-box, some-input"`: 152 | explicitly forward `x-foo`’s parts that you know about 153 | (i.e. some-box and some-input) as they are. 154 | These selectors *would* match: 155 | 156 | ```css 157 | x-bar::part(some-box) { ... } 158 | x-bar::part(some-input) { ... } 159 | ``` 160 | 161 | - `exportparts="some-input: foo-input"`: 162 | explicitly forward (some) of `x-foo`’s parts (i.e. some-input) 163 | but rename them. 164 | These selectors *would* match: 165 | 166 | ```css 167 | x-bar::part(foo-input) { ... } 168 | ``` 169 | 170 | These selectors *would not* match: 171 | 172 | ```css 173 | x-bar::part(some-box) { ... } 174 | x-bar::part(some-input) { ... } 175 | x-bar::part(foo-box) { ... } 176 | ``` 177 | 178 | #### Forwarding with -* 179 | *Note: `-*` forwarding is currently not in the spec. 180 | The following is how it could work.* 181 | 182 | - `part="* foo-*"`: implicitly forward all of `x-foo`’s parts as they are, but prefixed. 183 | These selectors *would* match: 184 | 185 | ```css 186 | x-bar::part(foo-some-box) { ... } 187 | x-bar::part(foo-some-input) { ... } 188 | ``` 189 | 190 | These selectors *would not* match: 191 | 192 | ```css 193 | x-bar::part(some-box) { ... } 194 | x-bar::part(some-input) { ... } 195 | ``` 196 | 197 | You _can_ chain these, 198 | as well as add a part to `x-foo` itself 199 | (`some-foo` below). 200 | This means "style this particular `x-foo`, 201 | but not the other one, 202 | if you had more". 203 | All of these are valid: 204 | 205 | ```html 206 | 207 | #shadow-root 208 | 209 | 210 | 211 | 212 | ``` 213 | 214 | - You cannot forward all parts at once, 215 | i.e. `exportparts="*: *"` 216 | since this might break your element in the future 217 | (if the nested shadow element adds new parts). 218 | So this is invalid: 219 | 220 | ```html 221 | 222 | #shadow-root 223 | 224 | #shadow-root 225 | 226 | 227 | 228 | ``` 229 | 230 | - However, as mentioned, 231 | you can forward all the parts if you prefix them, 232 | so this is ok: 233 | ```html 234 | 235 | #shadow-root 236 | 237 | #shadow-root 238 | 239 | 240 | 241 | ``` 242 | 243 | This selector would be valid: 244 | 245 | ```css 246 | x-form::part(bar-foo-some-input) { ... } 247 | ``` 248 | 249 | ## The "all buttons in this app should be blue" theming problem 250 | 251 | Given the above prefixing rules, 252 | to style all inputs in a document at once, 253 | you need to ensure that all elements correctly forward their parts 254 | and select all their parts. 255 | 256 | So given this shadow tree: 257 | 258 | ```html 259 | 260 | #shadow-root 261 | 262 | #shadow-root 263 | 264 | #shadow-root 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | ``` 273 | 274 | You can style all the inputs with: 275 | 276 | ```css 277 | :root::part(some-input) { ... } 278 | ``` 279 | 280 | 👉 This is a lot of effort on the element author, 281 | but easy on the theme user. 282 | 283 | If you hadn’t forwarded them with the same name 284 | and some-input was used at every level of the app 285 | (the non contrived example is just an `` tag that’s used in many shadow roots), 286 | then you’d have to write: 287 | 288 | ```css 289 | :root::part(form-bar-foo-some-input), 290 | :root::part(bar-foo-some-input, 291 | :root::part(foo-some-input), 292 | :root::part(some-input) { ... } 293 | ``` 294 | 295 | 👉 This is a lot of effort on the theme user, 296 | but easy on the element author. 297 | 298 | Both of these examples show that if an element author forgot to forward a part, 299 | then the app can’t be themed correctly. 300 | 301 | ### How ::theme works 302 | 303 | Elements can have a `theme` tag, similar to the `part` tag 304 | but unlike `::part`, `::theme` matches elements parts with that theme name, 305 | anywhere in the document. 306 | This means that even without forwarding parts, i.e.: 307 | 308 | ```html 309 | 310 | #shadow-root 311 | 312 | 313 | 314 | 315 | ``` 316 | 317 | You could style all of the inputs in `x-bar` with: 318 | 319 | ```css 320 | x-bar::theme(some-input) { ... } 321 | ``` 322 | 323 | This can go arbitrarily deep in the shadow tree. 324 | So, no matter how deeply nested they are, 325 | you could style all the inputs with `theme="some-input"` in the app with: 326 | 327 | ```css 328 | :root::theme(some-input) { ... } 329 | ``` 330 | 331 | ## Demo 332 | 333 | Chrome 69 shipped with `::part` working behind a flag. 334 | Starting Chrome with 335 | 336 | ```sh 337 | google-chrome --enable-blink-features=CSSPartPseudoElement 338 | ``` 339 | 340 | will allow you to try this out. 341 | It does not support the `-*` syntax to forward multiple parts. 342 | 343 | ## Other future work 344 | 345 | Feedback from developers has identified several areas for future work. 346 | 347 | ### Access control 348 | 349 | [Issue](https://github.com/w3c/csswg-drafts/issues/3506) 350 | 351 | Component authors may want to restrict what style-properties of a part can be 352 | set by users of the component, e.g. allowing changes to color but not height. 353 | 354 | ### Controlling theme 355 | 356 | [Issue](https://github.com/w3c/csswg-drafts/issues/3507) 357 | 358 | The `::theme` selector, as described above, 359 | allows deep components to expose themselves for theming 360 | with no control allowed by the containing components. 361 | In real usage it's likely that component authors 362 | will want more control than this. 363 | 364 | This could be some kind of white-/black-listing of themes by components or 365 | defining themes by patterns against part names. 366 | -------------------------------------------------------------------------------- /explainers/devtools.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fergald/docs/02672a08e23fd681606e30757445429edae728bb/explainers/devtools.png -------------------------------------------------------------------------------- /explainers/permissions-policy-deprecate-unload.md: -------------------------------------------------------------------------------- 1 | # Disable unload handlers by default and add Permissions-Policy to opt-in to enabling them. 2 | 3 | ## What is this? 4 | 5 | This is a proposal to disable running `unload` handlers by default 6 | and to add a new [Permissions-Policy](https://github.com/w3c/webappsec-permissions-policy/blob/main/permissions-policy-explainer.md) entry 7 | that will allow them to be re-enabled 8 | by sites which cannot easily stop using them. 9 | 10 | ## Background 11 | 12 | ### Unload reliability 13 | 14 | According to [Nic Jansma's survey](https://nicj.net/beaconing-in-practice/#beaconing-reliability-unload), 15 | `unload` handlers run 95%+ of the time on desktop Chrome, Edge and Firefox 16 | but considerably less (57%-68%) on mobile browsers and Safari desktop 17 | which already prioritize BFCaching over running `unload` handlers. 18 | 19 | `unload` handlers are unreliable on mobile 20 | because mobile OSes kill tabs and apps in the background. 21 | For some time, 22 | developers have been [advised](https://developers.google.com/web/updates/2018/07/page-lifecycle-api#the-unload-event) 23 | not to use `unload` handlers. 24 | 25 | ### Unload as specced 26 | 27 | `unload` handlers are [currently specced](https://whatpr.org/html/4288/browsing-the-web.html#unloading-documents) to run 28 | when the document is destroyed 29 | *as long as* "the user agent does not intend to keep document alive in a session history entry". 30 | This means that `unload` handlers only runs if the document will not enter BFCache, 31 | i.e. a main-frame navigation where BFCaching is not allowed or 32 | a subframe navigation. 33 | 34 | This was specced in this [PR](https://github.com/whatwg/html/pull/5889). 35 | 36 | ### Unload as implemented. 37 | 38 | WebKit's implementation matches the spec. 39 | Mozilla's and Chrome's match the spec on mobile 40 | but on desktop they both give priority to running `unload` handlers 41 | and block BFCache if an `unload` handler is present. 42 | In Chrome's case we felt that since it was already unreliable on mobile 43 | that making it moreso was not a problem. 44 | However with 95% reliability on desktop, 45 | reducing that significantly would be a problem. 46 | 47 | ### Previous proposal 48 | 49 | Chrome's [original proposed][previous] to allow sites 50 | to opt-in to disabling `unload` handlers. 51 | This was an attempt to move the ecosystem 52 | away from `unload` handlers, 53 | making it easier to align with the spec eventually. 54 | It found no support with other vendors. 55 | 56 | ### Frames which cannot fully use Permissions-Policy 57 | 58 | There are two classes of frames 59 | where only partial control over their Permissions-Policy 60 | is available. 61 | This tends to cause little or no problems currently 62 | but they need to be accounted for 63 | when introducing a policy default of disabled. 64 | 65 | #### Frames without `allow` 66 | 67 | ``, `` and `` can all contain a HTML document 68 | but none of them have an `allow` attribute 69 | and so cannot end up with a policy for a feature 70 | that is less restrictive than the default forthat feature. 71 | 72 | #### Headerless documents 73 | 74 | Documents with a URL that is 75 | 76 | - empty 77 | - `about:blank` 78 | ' `javascript:` 79 | - `data:` 80 | - a blob URL 81 | 82 | have no headers 83 | and so cannot set `Permissions-Policy` header. 84 | 85 | ## Motivation 86 | 87 | If we were starting from scratch, 88 | there would not be an `unload` handler. 89 | Keeping all other things constant, 90 | the web platform would be better without it 91 | If we're going to make a disruptive change, 92 | let's aim for the best end-point. 93 | 94 | ### Unload as specced is a foot-gun 95 | 96 | #### Unload is biased 97 | 98 | Safari is the only desktop browser to match the spec 99 | and runs unload about 58% of the time. 100 | Presumably others would see similar rates. 101 | 102 | If a site collects data via `unload` handlers, 103 | some fraction of this data will be missing 104 | due to handlers that did not run. 105 | When `unload` handlers running depends on BFCache eligibility, 106 | and that in turns depends the activity of 3rd party subframes, 107 | there could be significant bias 108 | in which data is missing. 109 | It may be that whether the data is collected 110 | depends what actions the user took 111 | or which ad network's ads were shown. 112 | 113 | The rate of running unload 114 | may also vary widely from page to page 115 | within the same site 116 | due to features used by the page. 117 | 118 | This bias means that compensating for this missing data 119 | may be hard or impossible. 120 | 121 | #### Unload handlers are rarely the correct choice 122 | 123 | This [doc](https://developer.chrome.com/blog/page-lifecycle-api/#the-unload-event) on the page lifecycle 124 | covers the problems with unload 125 | and the better alternatives to use. 126 | 127 | #### Whether unload handlers will run is not predictable 128 | 129 | Whether `unload` handlers run is deterministic 130 | but usually, 131 | neither main frames nor subframes 132 | have enough information 133 | to know if they will run. 134 | 135 | As specced the only way to reliably predict 136 | whether an `unload` handler will run 137 | when the main frame navigates 138 | is to carefully force it run or to not run. 139 | I.e. 140 | 141 | - ensure all frames of the page are compatible with BFCache. 142 | This mostly precludes having frames from 3rd party origins 143 | - ensure at least one frame of the page is not compatible with BFCache. 144 | This prevents BFCacheing, hurting performance. 145 | 146 | Without doing one or the other, 147 | whether the `unload` handler runs or not 148 | is dependent on state that is hard or impossible to see. 149 | 150 | ### Reliable unload handlers are incompatible with BFCache 151 | 152 | As they are currently specced, 153 | the only way, 154 | for a complex site, 155 | to make `unload` handlers reliable, predictable and unbiased 156 | is to force them to run by blocking BFCache. 157 | 158 | ### Aligning with the current spec is disruptive 159 | 160 | [As pointed out](https://github.com/whatwg/html/pull/5889#pullrequestreview-505683024) when the spec was being updated, 161 | skipping `unload` handlers 162 | is a breaking change. 163 | On platforms where unload is 95% reliable, 164 | it requires a careful rollout. 165 | Making a breaking change 166 | to get to an end-point where 167 | unload is even more of a foot-gun 168 | is not ideal. 169 | 170 | If Chrome and Mozilla are to undertake a disruptive change, 171 | this is an opportunity to reach a better end-point, 172 | where unload is gone by default 173 | but still available for those who opt-in. 174 | 175 | ### Sites are Trying to Keep Unload Away 176 | 177 | Some sites have removed some or all of their `unload` handlers, 178 | but with large complex sites, 179 | even without 3rd-party scripts and iframes, 180 | it is hard to ensure 181 | that nobody introduces an `unload` handler. 182 | 183 | By disabling `unload` handlers by default, 184 | this will prevent new instances from introduces inadvertently. 185 | 186 | ## Proposed Solution 187 | 188 | * Add `unload` as a [policy-controlled feature](https://www.w3.org/TR/permissions-policy-1/#features). 189 | * Use default allowlist of `none` for the `unload` feature 190 | ([issue](https://github.com/w3c/webappsec-permissions-policy/issues/513), 191 | [draft spec PR](https://github.com/w3c/webappsec-permissions-policy/pull/515)). 192 | * Give special treatment to [certain types of frames](#frames-which-cannot-fully-use-permissions-policy) 193 | which cannot fully use Permissions-Policy. 194 | * Frames without headers that are same-origin with their parent document 195 | will behave as if they had sent the same `Permissions-Policy` header 196 | as their parent for the `unload` feature. 197 | This means that their policy for `unload` 198 | is completely determined by their parent's policy 199 | and their `allow` attribute. 200 | * Frames without an `allow` attribute will behave as if they had one 201 | and it was set `unload=*`. 202 | This means that their policy for `unload` 203 | is completely determined by their parent's policy 204 | and their `Permissions-Policy` header. 205 | 206 | ## Concerns about non-main frame navigations 207 | 208 | As discussed [here](https://github.com/mozilla/standards-positions/issues/691#issuecomment-1484997320), 209 | there may be sites that rely on `unload` handlers 210 | when subframe navigate. 211 | E.g. they may signal to their parent frame that they have navigated. 212 | Sites like this would be broken 213 | by disabling `unload` handlers. 214 | 215 | Chrome's telemetry tells us that on Chrome 114, 216 | among subframe navigations, 217 | the following fractions involve an `unload` event handler: 218 | 219 | ``` 220 | Android: 9% 221 | Lacros: 10% 222 | Linux: 15% 223 | MacOS: 27% 224 | Windows: 24% 225 | ``` 226 | 227 | The denominator is 228 | 229 | - subframe navigations 230 | - excluding navigation away from initial empty document/about:blank 231 | 232 | Numerator is as above AND 233 | 234 | - an unload handler would fire for this navigation (in any frame) 235 | 236 | Restricting to unload handlers 237 | that are same-origin as the navigating frame 238 | reduces the numbers by about 1-2 percentage points. 239 | 240 | Being conservative 241 | and also adding navigations 242 | from initial empty document/about:blank that would fire unload 243 | to the numerator 244 | (leaving the denominator unchanged 245 | since the vast majority of those navigations are not "real" navigations, 246 | they typically happen immediately after the frame is created) 247 | adds about 1 percentage point. 248 | 249 | It is unknown what fraction of these `unload` handlers 250 | are important to the state/logic of the page within the subframe. 251 | The numbers are large enough 252 | that there could be a significant number of them. 253 | 254 | Since `pagehide` serves as a perfectly good substitute 255 | for `unload` in this case 256 | and since the `Permissions-Policy` header 257 | will be available, 258 | we propose to include subframe navigations. 259 | If this turns out to be a significant problem, 260 | we could change to only impacting 261 | `unload` handlers for main-frame navigations. 262 | 263 | ## Logistics of deprecation 264 | 265 | We do no think we can suddenly flip this switch. 266 | We would like to roll it out gradually 267 | so that sites who miss the announcement 268 | still have time to react 269 | before being heavily disrupted. 270 | 271 | A straw-person proposal for the rollout would be 272 | 273 | - Enable permissions-policy for `unload` 274 | with a default allowlist of `*` 275 | - Flip the default allowlist to `()` for N% of navigations 276 | - Increase N to 100 over time 277 | 278 | We assume there will be some user-impacting breakage 279 | on multiple sites 280 | that will be fixed 281 | as sites become aware of it. 282 | So choosing the N% of page-loads needs some care. 283 | 284 | - Choosing at random would lead to problems 285 | that are hard to detect and reproduce. 286 | - Choosing by user 287 | would inflict full breakage on a fraction of users. 288 | - Choosing by site/URL would suddenly inflict full breakage 289 | on a fraction of sites. 290 | - **(Proposed)** Choosing by a combined hash of user 291 | and site/URL is more complex but 292 | it gives consistently reproducible results 293 | for each user 294 | without subjecting any user to all of the breakage. 295 | 296 | ## Considered alternatives 297 | 298 | ### Opt-out via Permissions-Policy 299 | 300 | The [original proposal][previous] had a default allowlist of `*`. 301 | It required sites to opt in to disabling `unload` handlers. 302 | There were concerns that this gave sites a way 303 | to control the execution of code in iframes. 304 | 305 | Disabling by default avoids this problem. 306 | 307 | Several other alternatives are discussed 308 | in the [original proposal][previous-considered-alternatives]. 309 | 310 | ### Reverse Orgin Trial 311 | 312 | A reverse origin trial (ROT) would allow us 313 | to disable `unload` handlers 314 | for the majority of page-loads 315 | while still allowing sites 316 | to re-enable if needed. 317 | 318 | Unload appears in over 30% of all 319 | potentially BFCacheable navigations. 320 | So we can assume that its prevalence 321 | across all pageloads is something close to that. 322 | We have also seen enterprise software 323 | that relies on `unload` handlers to function correctly. 324 | So we have no clear idea 325 | of when we could safely remove `unload` handlers 326 | from the Web Platform. 327 | 328 | Since this is a disruptive intervention, 329 | we do not want to suddenly apply to 100% of page-loads. 330 | This would mean our ROT would have to ramp up over time. 331 | This is an unusual (unique?) way to run an ROT. 332 | ROTs are typically used when usage of the feature 333 | is already very small. 334 | 335 | With no end-date, 336 | an ROT for this deprecation, 337 | even if we were were to ramp up gradually, 338 | is not a good option. 339 | If we manage to massively drive down usage 340 | of `unload` handlers across the web 341 | then we could consider an ROT 342 | as the final step to removal of the feature. 343 | 344 | ## Privacy Considerations 345 | 346 | None. 347 | 348 | ## Security Considerations 349 | 350 | There would be a period where 351 | the default allowlist for the policy would be `*`. 352 | This was discussed in detail in the [previous proposal]. 353 | 354 | The current proposal makes this a temporary state. 355 | 356 | No other concerns are known. 357 | 358 | # Testing with Chrome 359 | 360 | ## Controlling unload with Permissions Policy 361 | 362 | As of [106.0.5229.0](https://chromiumdash.appspot.com/commit/5c436e20b6d923241eadd2afe8b846ed32d46eea), 363 | a flag is available to control whether Permissions-Policy controls unload. 364 | To enable this, 365 | you can start Chrome with 366 | 367 | ``` 368 | --enable-features=PermissionsPolicyUnload 369 | ``` 370 | 371 | ## Disabling unload by default 372 | 373 | As of [117.0.5924.2](https://chromiumdash.appspot.com/commit/2cf526285beacdfebe2251a20ad5550b3487a277), 374 | flags are available to control the deprecation. 375 | To force the deprecation, 376 | you can start Chrome with 377 | 378 | ``` 379 | --enable-features=PermissionsPolicyUnload,DeprecateUnload --disable-features=DeprecateUnloadByUserAndOrigin 380 | ``` 381 | 382 | You should see in devtools, 383 | under Application -> Frames, 384 | that `unload` is disabled in the Permissions-Policy section. 385 | 386 | ![Screenshot of devtools](devtools.png) 387 | 388 | If you are running chrome with `--enable-blink-features=FeaturePolicyReporting`, 389 | you can also try 390 | 391 | ``` 392 | addEventListener("unload", () => {}) 393 | ``` 394 | 395 | You should see 396 | 397 | ``` 398 | [Violation] Permissions policy violation: unload is not allowed in this document. 399 | ``` 400 | 401 | ## Using ReportingAPI to find unloads 402 | 403 | You can use the [ReportingAPI](https://www.w3.org/TR/reporting-1/) 404 | to detect uses of unload in your pages 405 | without preventing the usage. E.g. 406 | 407 | ``` 408 | Permissions-Policy-Report-Only: unload=() 409 | 410 | ``` 411 | 412 | You must also supply the correct ReportingAPI headers e.g. 413 | 414 | ``` 415 | Report-To: {"group":"default","max_age":1800,"endpoints":[{"url":"https://exmaple.com/report"}],"include_subdomains":true} 416 | ``` 417 | 418 | for the reports to be delivered. 419 | 420 | ## Reenabling unload for a frame 421 | 422 | If the frame in question is a top-level frame 423 | then all that's needed is the header. E.g. 424 | 425 | ``` 426 | Permissions-Policy: unload=self 427 | ``` 428 | 429 | If the frame in question is a subframe 430 | then the header must be present on that frame 431 | and all ancestor frames and 432 | all containing `iframe` elements must set `allow="unload"`. 433 | If the frame is a cross-origin subframe, 434 | e.g. if a.com has a b.com subframe 435 | then the header on top-level frame must allow unload for both domains, e.g. 436 | 437 | ``` 438 | Permissions-Policy: unload=(self,"https://b.com") 439 | ``` 440 | 441 | and the header on b.com must allow it for itself. 442 | 443 | [Here](https://dyn.fergaldaly.com/~fergal/html/pp-unload/enabled/) is a same-origin example 444 | 445 | ## Workarounds 446 | 447 | Most uses of `unload` can be replaced with `pagehide` 448 | see [here](https://developer.chrome.com/blog/page-lifecycle-api/#the-unload-event) for advice. 449 | 450 | There are some usages `unload` that really are related to document destruction 451 | and cannot be replace by `pagehide` or other events. 452 | 453 | ### MessageChannel 454 | 455 | The [MessageChannel API](https://developer.mozilla.org/en-US/docs/Web/API/MessageChannel) provides a way 456 | to get a pair of `MessagePorts` which can be used 457 | to communicate directly between cross-origin documents. 458 | Let's call them documentA/portA and documentB/portB. 459 | We know that some users of this rely on the `unload` event 460 | to send a notification from documentB via portB 461 | when documentB is being destroyed. 462 | This allows documentA 463 | to release any resources related to this communication. 464 | This is somewhat unreliable. 465 | If the documentB crashes, 466 | unload will not run 467 | and documentA will never free the resources. 468 | 469 | - If documentB is not a top-level document 470 | (i.e. documentB is a subframe of some other frame) 471 | then the `pagehide` event can be used 472 | as an alternative to `unload`. 473 | They both fire in exactly the same circumstances. 474 | - If documentB is or may be a top-level document 475 | then it may enter BFCache, 476 | in which case it may later be destroyed 477 | without any further events firing. 478 | `pagehide` can be used instead of `unload` 479 | depending on the `pagehide` event's `persisted` property: 480 | - If `persisted` is false, 481 | the document is being destroyed and not entering BFCache. 482 | This is equivalent to an `unload` event. 483 | - If `persisted` is true then the page may enter BFCache 484 | (it is still possible for the document to be destroyed 485 | even though `persisted` is true). 486 | DocumentB may want to inform documentA that it is entering BFCache 487 | (and inform that it was restored on the `pageshow` event). 488 | 489 | Finally to reliably free resources, 490 | documentA can hold portA in a `WeakRef`. 491 | When portB is closed or documentB is destroyed 492 | (e.g. enters BFCache but reaches its time-to-live in the cache), 493 | portA will also close. 494 | At that point, 495 | it becomes eligible for garbage collection. 496 | When garbage collections occurs, 497 | the `WeakRef` will return `null``. 498 | DocumentA can occasionally scan the collection of `WeakRefs` 499 | to find ports which have been closed. 500 | 501 | This could be made considerably simpler 502 | with the addition of an `onclose` 503 | or equivalent signal on `MessagePort`. 504 | This is discussed [is this issue](https://github.com/whatwg/html/issues/1766). 505 | 506 | # [Self-Review Questionnaire: Security and Privacy](https://w3ctag.github.io/security-questionnaire/) 507 | 508 | 01. What information might this feature expose to Web sites or other parties, 509 | and for what purposes is that exposure necessary? 510 | 511 | > None. 512 | 513 | 02. Do features in your specification expose the minimum amount of information 514 | necessary to enable their intended uses? 515 | 516 | > Yes. No information is directly exposed. 517 | > Some information about whether a subframe has an `unload` event handler 518 | > might be deducible. 519 | 520 | 03. How do the features in your specification deal with personal information, 521 | personally-identifiable information (PII), or information derived from 522 | them? 523 | 524 | > N/A 525 | 526 | 04. How do the features in your specification deal with sensitive information? 527 | 528 | > N/A 529 | 530 | 05. Do the features in your specification introduce new state for an origin 531 | that persists across browsing sessions? 532 | 533 | > No. 534 | 535 | 06. Do the features in your specification expose information about the 536 | underlying platform to origins? 537 | 538 | > No. 539 | 540 | 07. Does this specification allow an origin to send data to the underlying 541 | platform? 542 | 543 | > No. 544 | 545 | 08. Do features in this specification enable access to device sensors? 546 | 547 | > No. 548 | 549 | 09. Do features in this specification enable new script execution/loading 550 | mechanisms? 551 | 552 | > No. 553 | 554 | 10. Do features in this specification allow an origin to access other devices? 555 | 556 | > No. 557 | 558 | 11. Do features in this specification allow an origin some measure of control over 559 | a user agent's native UI? 560 | 561 | > No. 562 | 563 | 12. What temporary identifiers do the features in this specification create or 564 | expose to the web? 565 | 566 | > No. 567 | 568 | 13. How does this specification distinguish between behavior in first-party and 569 | third-party contexts? 570 | 571 | > This disables a feature. 572 | > 1st party can re-enable for all 3rd parties. 573 | > 3rd parties can disable the feature for themselves and their subframes. 574 | 575 | 14. How do the features in this specification work in the context of a browser’s 576 | Private Browsing or Incognito mode? 577 | 578 | > No difference. 579 | 580 | 15. Does this specification have both "Security Considerations" and "Privacy 581 | Considerations" sections? 582 | 583 | > Yes. 584 | 585 | 16. Do features in your specification enable origins to downgrade default 586 | security protections? 587 | 588 | > No. 589 | 590 | 17. How does your feature handle non-"fully active" documents? 591 | 592 | > N/A 593 | 594 | 18. What should this questionnaire have asked? 595 | 596 | > ? 597 | 598 | 599 | [previous]: permissions-policy-unload.md 600 | [previous-considered-alternatives]: permissions-policy-unload.md#considered-alternatives 601 | [previous-security]: permissions-policy-unload.md#security-considerations 602 | -------------------------------------------------------------------------------- /explainers/permissions-policy-unload.md: -------------------------------------------------------------------------------- 1 | # Permissions-Policy to disable unload handler 2 | 3 | ## Status 4 | 5 | This has been superceeded by a [proposal to deprecate unload](permissions-policy-deprecate-unload.md). 6 | However it is step 1 of that proposal 7 | and it's worth reading that 8 | to understand the whole picture. 9 | 10 | ## What is this? 11 | 12 | This is a proposal for a new [Permissions-Policy](https://github.com/w3c/webappsec-permissions-policy/blob/main/permissions-policy-explainer.md) entry 13 | that will disable unload handlers. 14 | Permissions-Policy is a mechanism for controlling individual page's 15 | access to features. 16 | This proposal would add a way for a page to opt out of firing unload handlers 17 | while allowing exceptions. 18 | 19 | ## Motivation 20 | 21 | ### Unload handlers are incompatible with BFCache 22 | 23 | Unload handlers have been [unreliable](https://developers.google.com/web/updates/2018/07/page-lifecycle-api#the-unload-event) for a long time. 24 | Unload handlers are not guaranteed to be fired 25 | on all cases where a document gets destroyed, 26 | especially on mobile platforms 27 | where tabs are more likely to get killed in the background 28 | or with the app being swiped away. 29 | 30 | Additionally, unload handlers are incompatible with [BFCache](https://web.dev/bfcache/). 31 | There is no correct time to fire the unload handler 32 | for a page that enters BFCache. 33 | When entering BFCache a page receives a pagehide event. 34 | We cannot fire the unload event at this point 35 | because the page may be restored later 36 | (and unload handlers are written with the assumption 37 | that the page is gone forever). 38 | If the user never returns to the page, 39 | the page will be destroyed. 40 | We cannot run the unload handler 41 | at the time of destruction 42 | because in the user's mind, 43 | the page is long gone 44 | and having it suddenly start to run JS 45 | and maybe make network connections 46 | while remaining copletely invisible 47 | would be quite unexpected. 48 | 49 | So, for a page with unload event handlers, 50 | we must choose whether to allow BFCacheing 51 | or whether to make the unload event reliable. 52 | 53 | Some browsers, like Desktop Chrome and Desktop Firefox, 54 | have chosen to keep the unload event reliable, 55 | so the existence of unload handlers makes a page ineligible for BFCache, 56 | hurting performance. 57 | In others like Android Chrome & Android [Firefox](https://groups.google.com/a/mozilla.org/g/dev-platform/c/3pQRLPZnQgQ/m/dsA1w4iiAwAJ), 58 | and WebKit-based browsers (Desktop Safari & all iOS browsers), 59 | if the page is eligible for BFCache, 60 | the unload handlers will not run, 61 | further increasing the unreliability of unload handlers. 62 | 63 | For best performance, 64 | sites should avoid using unload handlers. 65 | Alternatives exist for most uses, 66 | see https://web.dev/bfcache/#never-use-the-unload-event for recommendations. 67 | We are, in parallel, proposing the [unload beacon API](https://github.com/darrenw/docs/blob/main/explainers/beacon_api.md) 68 | which makes migrating away from unload easier for some common use cases. 69 | If there are use cases that still require unload handlers 70 | please let us know. 71 | 72 | ### Unload Handlers are Preventing BFCache Use. 73 | 74 | From Chromium telemetry, 75 | for 16% of history navigations, 76 | the only reason blocking from entering BFCache 77 | is that they have an unload handler in some frame. 78 | There are lots more pages blocked by unload+some other reasons, 79 | so other browsers (that do not block on those reasons) 80 | could be seeing far higher than 16% that are only blocked by unload. 81 | 82 | ### Sites are Trying to Keep Unload Away 83 | 84 | For sites that have removed some or all of their unload handlers, 85 | we would like them to have way to ensure 86 | that no new handlers are introduced in the future. 87 | For large, multi-team sites, 88 | it is impossible to prevent unload handlers 89 | from being added by teams 90 | who are unaware of the performance problems it cases. 91 | We have seen this several times recently 92 | on Google properties. 93 | In addition to that, 94 | sometimes getting rid of unload handlers from the page is not easy 95 | e.g. in the case when a third party code library 96 | uses unload handlers silently. 97 | 98 | ### Expected impact 99 | 100 | This proposal gives the site 101 | a way stop firing unload handlers 102 | even if they are added by code outside of their control. 103 | 104 | If a page uses this new Permissions-Policy, 105 | the page will be: 106 | 107 | * More likely to be eligible with BFCache 108 | * Protected from accidentally introducing new unload handlers 109 | * More predictable as the unload handler will never run, 110 | instead of maybe running on Desktop but not on Chrome Android/Safari/etc. 111 | 112 | ## Goals 113 | 114 | * Provide a way to ensure that a page is free from unload handlers. 115 | * Provide a way to lock in progress on gradually removing all unload handlers from a site. 116 | * Provide a way to force-disable embedded iframe’s unload handlers from the iframe embedder side. 117 | 118 | ## Non-goals 119 | 120 | * Providing a way to make a page BFCache-able unconditionally. 121 | 122 | ## Proposed Solution 123 | 124 | * Add `unload` as a [policy-controlled feature](https://www.w3.org/TR/permissions-policy-1/#features). 125 | * Use [default allowlist](https://www.w3.org/TR/permissions-policy-1/#default-allowlists) `*` for the unload feature 126 | so that the existing sites behave the same as before. 127 | Use of unload events is allowed in documents in top-level browsing contexts by default, 128 | and when allowed, 129 | use of unload events is allowed by default to documents in child browsing contexts. 130 | 131 | ## Examples 132 | 133 | ### Example 1 - Disable unload events entirely 134 | 135 | When site authors want to disable unload events for the current frame 136 | and all of its child iframes recursively, 137 | site authors can use the following HTTP response header 138 | when serving the document. 139 | 140 | ``` 141 | Permissions-Policy: unload=() 142 | ``` 143 | 144 | ### Example 2 - Disable unload events with an exception for one origin 145 | 146 | When site authors have cleared unload events from most of a page 147 | and want to ensure that none creep back in 148 | but still have a subframe that depends on unload, 149 | site authors can use the following HTTP response header 150 | when serving the document. 151 | 152 | ``` 153 | Permissions-Policy: unload=(https://still-uses-unload.com) 154 | ``` 155 | 156 | ### Example 3 - Report without disabling 157 | 158 | When site authors want to know if unload handlers are present in their main frame, 159 | the following header will cause that to be reported. 160 | See [this doc](https://github.com/w3c/webappsec-permissions-policy/blob/main/reporting.md#can-i-just-trigger-reports-without-actually-enforcing-the-policy) for more details on reporting. 161 | Unfortunately, reporting for sub-frames has privacy implications, 162 | so it won't be permitted. 163 | 164 | ``` 165 | Permissions-Policy-Report-Only: unload=() 166 | ``` 167 | 168 | ## Considered alternatives 169 | 170 | ### Document-Policy 171 | 172 | [Document-Policy](https://github.com/WICG/document-policy/blob/main/document-policy-explainer.md) is another possible way to disable unload handlers. 173 | For example, site authors could use the following HTTP response header. 174 | 175 | ``` 176 | Document-Policy: unload=?0 177 | ``` 178 | 179 | Different from Permissions-Policy, 180 | Document-Policy cannot be used to force disabling its child frames' unload handlers recursively. 181 | The spec includes the `Sec-Required-Document-Policy` [header](https://wicg.github.io/document-policy/#sec-required-document-policy-http-header), 182 | however this is not full specified ([serialization](https://wicg.github.io/document-policy/#serialization)) 183 | and remains unimplemented in browsers. 184 | If implemented, it would force sites to choose between broken iframes and BFCache. 185 | This would be useful on dev and staging deployments 186 | to detect subframes that do not comply with the policy 187 | but deploying it in production would be risky. 188 | 189 | ### Disabling from JS 190 | 191 | It's possible to disable the current document's unload handlers via JavaScript 192 | (e.g. with the following script), 193 | but it isn't perfect. 194 | The following script will only work 195 | if it runs before any other script that adds unload handlers 196 | (which is hard to guarantee), 197 | it also can't disable unload handlers on child frames, etc. 198 | 199 | https://chikamune-cr.github.io/how_to_disable_unload_handlers_by_monkeypatch/ 200 | 201 | ```js 202 | // Ignore the window.onunload attribute. 203 | Object.defineProperty(window, 'onunload', {set: function(handler) {}}); 204 | if (!window.original_addEventListener) { 205 | window.original_addEventListener = window.addEventListener; 206 | function addEventListener_monkeypatch(event_type, handler, opt) { 207 | // Ignore unload handler. 208 | if (event_type !== 'unload') { 209 | this.original_addEventListener(event_type, handler, opt); 210 | } else { 211 | // We can also detect the usage of unload handlers. 212 | console.trace('unload handlers were used'); 213 | } 214 | } 215 | window.addEventListener = addEventListener_monkeypatch; 216 | } 217 | ``` 218 | 219 | ### Reloading iframes 220 | 221 | We’ve considered unloading iframes on navigating away 222 | if the iframes are cross-origin 223 | and make the page ineligible for BFCache, 224 | then reloading the unloaded iframes 225 | when the parent frame is salvaged from BFCache. 226 | This was rejected as it might cause unexpected behavior 227 | when frames communicate with each other. 228 | (We're open to suggestions) 229 | 230 | ## Privacy Considerations 231 | 232 | None. 233 | 234 | ## Security Considerations 235 | 236 | ### Concerns about giving embedders control over the (non)execution of iframe code 237 | 238 | If the embedder of an iframe can control whether the iframe's unload handler runs or not 239 | then that seems to provide a cross-origin control point 240 | that could potentially be used to exploit the iframe. 241 | 242 | While cross-origin control of scripting is, in general, a bad thing, 243 | unload handlers are very different to other APIs. 244 | 245 | #### Unload handlers already often don't run 246 | 247 | Right now unload handlers for an iframe will not be run in the following cases 248 | 249 | - mobile browser backgrounded tab killed by OS 250 | - most mobile browsers and some desktop browsers if they believe the page could be BFCached 251 | - any multi-process browser where a crash occurs in the process responsible for the iframe 252 | - implementations typically allot a time budget for running unload handlers 253 | which may cause some not to complete or not to run at all 254 | 255 | This means that unload handlers are already not a guaranteed capability 256 | that subframes can depend on, 257 | and placing some control over this nondeterminism with the parent frame 258 | does not create a new problem. 259 | 260 | #### Unload handlers are invisible to users 261 | 262 | Unload handlers run *after* navigation has occurred. 263 | The exact timing depends on many factors. 264 | In some cases unload handlers will block the navigation until they complete, 265 | in other cases they run in the background 266 | in parallel with the new page loading. 267 | In either case, 268 | whether the unload handler runs or not 269 | has no bearing on the immediate user experience 270 | apart from possibly slowing down the appearance of the next page. 271 | 272 | It is possible that skipping the unload handler will change the user experience later on, 273 | e.g. if unload is responsible for persisting some data 274 | in storage or over the network. 275 | However using unload for this is already a bad idea 276 | due to its unreliability. 277 | 278 | Since this policy requires opt-in by the embedder, 279 | problematic cases can be excepted until they are fixed. 280 | What remains are the cases where the embedder 281 | accidentally breaks something an iframe 282 | or (hypothetically) intentionally exploits the iframe. 283 | 284 | #### In line with sync-xhr and document-domain 285 | 286 | Both `sync-xhr` and `document-domain` permissions 287 | cause a long-standing feature to throw an exception. 288 | Existing code written to use these features 289 | was unlikely to recover well from this exception 290 | so it was effectively a cross-origin ability to control execution 291 | of certain pieces of script. 292 | This was not a problem in practice. 293 | 294 | 295 | #### Conclusion 296 | 297 | We believe that the danger posed by embedders maliciously disabling unload handlers in iframes is minimal 298 | and not in the same class as an ability to disable other APIs. 299 | Meanwhile conscientious sites are unable to safeguard their work 300 | to improve performance for users with BFCache. 301 | 302 | ## Frequently Asked Questions 303 | 304 | ### Will this Permissions-policy cause problems for 3rd-party iframes that use unload handlers? 305 | Possibly. 306 | Authors should ensure that 307 | all iframes on a page have correctly without unload handlers 308 | before using this header. 309 | Disabling unload handlers may impact e.g. reporting of metrics. 310 | Such issues will tend not be visible in the page itself, 311 | since unload handlers run after document discard. 312 | We’ve started reaching out to common 3rd party iframe providers, 313 | asking them to remove unload handlers. 314 | We will also encourage them to include this header on their iframes 315 | to ensure that they stay unload-free, 316 | once it is available. 317 | 318 | ### What should sites do if they have 3rd-party iframes that rely on unload handlers? 319 | Sites can provide feedback to 3rd-party iframe providers, 320 | asking them to remove unload handlers if possible. 321 | In parallel, sites can use this header, 322 | yet allowing origins with remaining unload handlers. 323 | Doing this will not make the page BFCacheable 324 | but it will help ensure that new unload handlers do not creep in elsewhere. 325 | 326 | ### Will this Permissions-policy also affect the unload handlers added by extensions? 327 | In Chrome's implementation, yes. 328 | We believe that there are alternatives to unload 329 | that can be used by extensions 330 | and so there is no need to make an exception. 331 | We will make an effort to make extension authors aware of this new feature 332 | so that they can proactively migrate away from unload 333 | before sites start using this. 334 | 335 | ### Why unload but not beforeunload? 336 | 337 | While `beforeunload` event handlers are abused, 338 | they also have legitimate uses, 339 | e.g. save-before-exit prompts. 340 | Also, any subframe abusing them 341 | has a very visible and direct impact on the UX of the site. 342 | This makes the abuse self-limiting. 343 | Sites will not embed third party frames 344 | that openly hurt their user experience. 345 | We know of no examples 346 | where an otherwise useful third party subframe 347 | adds an abusive `beforeunload` handler. 348 | This means that there is no motivation 349 | to give the ability to prevent the use of `beforenuload` handlers. 350 | 351 | The negative impact of `unload` handlers, 352 | blocking BFCache and hurting pageload performance, 353 | is much less visible. 354 | This means there is little or no pressure on third parties 355 | to remove `unload` handlers from their code. 356 | 357 | `beforeunload` blocks BFCache on Mozilla 358 | but we believe is an implementation detail. 359 | There is no fundamental conflict between the firing the `beforeunload` event 360 | and allowing the page into BFCache. 361 | On the other hand, 362 | unload handlers block BFCache on Mozilla and Chrome desktop 363 | because they have a fundamental incompatibilty, 364 | as described [above](#). 365 | 366 | # [Self-Review Questionnaire: Security and Privacy](https://w3ctag.github.io/security-questionnaire/) 367 | 368 | 01. What information might this feature expose to Web sites or other parties, 369 | and for what purposes is that exposure necessary? 370 | 371 | > None. 372 | 373 | 02. Do features in your specification expose the minimum amount of information 374 | necessary to enable their intended uses? 375 | 376 | > Yes. No information is directly exposed. 377 | > Some information about whether a subframe has an unload event handler 378 | > might be deducible. 379 | 380 | 03. How do the features in your specification deal with personal information, 381 | personally-identifiable information (PII), or information derived from 382 | them? 383 | 384 | > N/A 385 | 386 | 04. How do the features in your specification deal with sensitive information? 387 | 388 | > N/A 389 | 390 | 05. Do the features in your specification introduce new state for an origin 391 | that persists across browsing sessions? 392 | 393 | > No. 394 | 395 | 06. Do the features in your specification expose information about the 396 | underlying platform to origins? 397 | 398 | > No. 399 | 400 | 07. Does this specification allow an origin to send data to the underlying 401 | platform? 402 | 403 | > No. 404 | 405 | 08. Do features in this specification enable access to device sensors? 406 | 407 | > No. 408 | 409 | 09. Do features in this specification enable new script execution/loading 410 | mechanisms? 411 | 412 | > No. 413 | 414 | 10. Do features in this specification allow an origin to access other devices? 415 | 416 | > No. 417 | 418 | 11. Do features in this specification allow an origin some measure of control over 419 | a user agent's native UI? 420 | 421 | > No. 422 | 423 | 12. What temporary identifiers do the features in this specification create or 424 | expose to the web? 425 | 426 | > No. 427 | 428 | 13. How does this specification distinguish between behavior in first-party and 429 | third-party contexts? 430 | 431 | > This disables a feature. 432 | > 1st party can disable for all 3rd parties. 433 | > 3rd parties can disable the feature for themselves and their subframes. 434 | 435 | 14. How do the features in this specification work in the context of a browser’s 436 | Private Browsing or Incognito mode? 437 | 438 | > No difference. 439 | 440 | 15. Does this specification have both "Security Considerations" and "Privacy 441 | Considerations" sections? 442 | 443 | > It does now! 444 | 445 | 16. Do features in your specification enable origins to downgrade default 446 | security protections? 447 | 448 | > No. 449 | 450 | 17. How does your feature handle non-"fully active" documents? 451 | 452 | > N/A 453 | 454 | 18. What should this questionnaire have asked? 455 | 456 | > Does this feature allow new cross-origin control points. 457 | -------------------------------------------------------------------------------- /explainers/queueMicrotask.md: -------------------------------------------------------------------------------- 1 | # Explainer: queueMicrotask 2 | 3 | 4 | ## Motivation 5 | 6 | There are several existing ways 7 | to queue a microtask 8 | ([introduction to microtasks](https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/)): 9 | 10 | 11 | 12 | * `Promise.resolve().then(callback)` 13 | * Register a mutation observer and trigger it with a mutation 14 | 15 | The library [asap](https://github.com/kriskowal/asap) uses these tricks 16 | to provide a cross-browser way of queueing a microtask. 17 | 18 | Developers and frameworks are already using these methods to queue microtasks. 19 | The platform should support queueing microtasks directly as a primitive operation. 20 | 21 | Reasons to expose this API directly 22 | 23 | * Roughly the same semantics are available via Promise etc 24 | but we should make this lower level primitive directly available 25 | rather than making people emulate it with via higher level APIs 26 | * Emulating this via Promise causes exceptions thrown in the callback 27 | to be converted into rejected promises. 28 | * Emulating via Promise causes unnecessary objects to be created and detroyed. 29 | Other methods of emulating this also cause wasted effort. 30 | * The other semantically distinct async callback queue APIs are available directly, 31 | microtask not being available directly is an anomaly 32 | * Providing a direct API could lead to more widespread understanding of the distinction between 33 | * setTimeout(callback,0) - task 34 | * requestAnimationFrame(callback) - RAF 35 | * queueMicrotask(callback) - microtask 36 | 37 | 38 | ## Proposal 39 | 40 | This [proposal](https://github.com/whatwg/html/issues/512) would add an API 41 | to directly queue a microtask 42 | without the need for any tricks. E.g. 43 | 44 | 45 | ``` 46 | queueMicrotask(callback); 47 | ``` 48 | 49 | 50 | This API would be available to JS in web pages via `Window` 51 | and to JS in workers via `WorkerGlobalScope`. 52 | 53 | 54 | ## Usage 55 | 56 | Microtasks are useful when you need async ordering behaviour, 57 | but want the work done as soon as possible. 58 | Beware that doing large amounts of work in microtasks 59 | is just as bad for jank and interactivity 60 | as doing that work in-place in your JS. 61 | Depending on the desired effect, 62 | `setTimeout(someFunction, 0)` or `requestAnimationFrame(someFunction)` 63 | may be the correct choice. 64 | 65 | Example 66 | 67 | ``` 68 | function microtask() { 69 | console.log("microtask"); 70 | } 71 | 72 | function task() { 73 | console.log("task"); 74 | } 75 | 76 | setTimeout(task, 0); 77 | queueMicrotask(microtask); 78 | ``` 79 | 80 | Will result in the following order of operations 81 | 82 | 1. Log "microtask" 83 | 1. Return to the event loop and allow rendering/input callbacks to occur if appropriate 84 | 1. Log "task" 85 | 86 | ## Risks 87 | 88 | ### Infinite loops of microtasks 89 | 90 | A microtask posted with `queueMicrotask` 91 | may itself post another microtask 92 | so of course buggy websites can create infinite loops 93 | using this API. 94 | Since these are microtasks, 95 | the current task will never complete, 96 | the page will be unresponsive 97 | and the slow script dialog will be triggered. 98 | This is different to infinite chains of `setTimout` calls 99 | which *silently* consume 100% CPU 100 | but are easier to let slip into production 101 | since the tasks complete 102 | and event handling continues as normal. 103 | 104 | No new mitigations are proposed to handle this risk, 105 | the impact of the bug is immediately visible 106 | and the slow script dialog seems adequate. 107 | --------------------------------------------------------------------------------