Chrome for Developers - Origin TrialsBuild the next generation of web experiences.2023-11-10T00:00:00Zhttps://developer.chrome.com/tags/origin-trialsChrome for DevelopersService Worker Static Routing API Origin Trial2023-11-10T00:00:00Zhttps://developer.chrome.com/en/blog/service-worker-static-routing-api-origin-trial/Brendan Kennyhttps://developer.chrome.com/authors/brendankenny/<p>Service workers are a powerful tool for allowing websites to work offline and create specialized caching rules for themselves. A service worker <code>fetch</code> handler sees every request from a page it controls, and can decide if it wants to serve a response to it from the service worker cache, or even rewrite the URL to fetch a different response entirely—for instance, based on local user preferences.</p>
<p>However, there can be a performance cost to service workers when a page is loaded for the first time in a while and the controlling service worker isn't currently running. Since all fetches need to happen through the service worker, the browser has to wait for the service worker to start up and run to know what content to load. This startup cost can be small, but significant, for developers using service workers to improve performance through caching strategies.</p>
<p><a href="https://web.dev/blog/navigation-preload">Navigation preload</a> is one approach to solving the problem—allowing navigation requests to be made over the network in parallel to service worker startup—but it's limited to initial navigation requests and still includes the service worker in the critical path. Since navigation preload launched, there have been multiple efforts to develop a more general solution to the problem space, including ways for some requests to not be blocked on service worker startup at all.</p>
<h2 id="the-service-worker-static-routing-api" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/service-worker-static-routing-api-origin-trial/#the-service-worker-static-routing-api" aria-hidden="true">#</a> The Service Worker Static Routing API</h2>
<p>Starting in Chrome 116, the experimental Service Worker Static Routing API is available for testing the first step to such a solution. When a service worker is installed, it can use the Service Worker Static Routing API to declaratively state how certain resource paths should be fetched.</p>
<p>In the initial version of the API, paths can be declared to always be served from the network, not the service worker. When a controlled URL is later loaded, the browser can start fetching resources from those paths before the service worker has finished starting. This removes the service worker from the paths that you know don't need a service worker.</p>
<p>To use the API, the service worker calls <code>event.registerRouter</code> on the <code>install</code> event with a set of rules:</p>
<pre class="language-js"><code class="language-js">self<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'install'</span><span class="token punctuation">,</span> <span class="token parameter">event</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span>event<span class="token punctuation">.</span>registerRouter<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token comment">// Go straight to the network and bypass invoking "fetch" handlers for all</span><br /> <span class="token comment">// same-origin URLs that start with '/form/'.</span><br /> event<span class="token punctuation">.</span><span class="token function">registerRouter</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token punctuation">{</span><br /> <span class="token literal-property property">condition</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">urlPattern</span><span class="token operator">:</span> <span class="token punctuation">{</span><span class="token literal-property property">pathname</span><span class="token operator">:</span> <span class="token string">'/form/*'</span><span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">source</span><span class="token operator">:</span> <span class="token string">'network'</span><span class="token punctuation">,</span><br /> <span class="token punctuation">}</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Each rule generally has two properties:</p>
<ul>
<li>
<p><code>condition</code>: specifies when the rule applies using the <a href="https://developer.mozilla.org/docs/Web/API/URL_Pattern_API">URL Pattern API</a> to match resource paths. The property can take a <code>URLPattern</code> instance, or the equivalent plain object that is compatible with being passed into the <a href="https://developer.mozilla.org/docs/Web/API/URLPattern/URLPattern"><code>URLPattern</code> constructor</a> (for instance, either <code>new URLPattern({pathname: '*.jpg'})</code> or just <code>{pathname: '*.jpg'}</code>).</p>
<p>The flexibility of URL Patterns means that the rule can match something as simple as any resource under a path, to very specific and detailed conditions. The patterns should generally be familiar to users of popular routing libraries.</p>
</li>
<li>
<p><code>source</code>: specifies how the resources matching <code>condition</code> will be loaded. Today, only the <code>'network'</code> value is supported (bypassing the service worker to load the resource over the network directly), but the plan is to <a href="https://github.com/WICG/service-worker-static-routing-api/blob/main/final-form.md#:~:text=enum%20RouterSourceEnum%20%7B%20%22network%22%2C%20%22cache%22%2C%20%22fetch%2Devent%22%2C%20%22race%2Dnetwork%2Dand%2Dfetch%2Dhandler%22%20%7D%3B">expand this to other values</a> in the future.</p>
</li>
</ul>
<h2 id="use-cases" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/service-worker-static-routing-api-origin-trial/#use-cases" aria-hidden="true">#</a> Use cases</h2>
<p>As explained, the initial version of the API is essentially an escape hatch from service worker control for some paths. Where this will make sense to use is going to be dependent on how you use your service worker and how users traverse your site.</p>
<p>One example might be if your site uses a <a href="https://web.dev/articles/offline-cookbook#cache-falling-back-to-network">cache-first strategy</a> (falling back to network), but there's some content that's so rarely visited that there's little to no value in it ever hitting the cache (like archived content or RSS feeds). Restricting these paths to be fetched from the network only <em>can</em> be set up in the service worker, but the service worker still has to start up and run to decide how to handle those requests.</p>
<p>The static routing API, in contrast, bypasses the service worker completely with a few declarative lines:</p>
<pre class="language-js"><code class="language-js">self<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'install'</span><span class="token punctuation">,</span> <span class="token parameter">event</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span>event<span class="token punctuation">.</span>registerRouter<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> event<span class="token punctuation">.</span><span class="token function">registerRouter</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token punctuation">{</span><br /> <span class="token literal-property property">condition</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">urlPattern</span><span class="token operator">:</span> <span class="token punctuation">{</span><span class="token literal-property property">pathname</span><span class="token operator">:</span> <span class="token string">'/feeds/*.xml'</span><span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">source</span><span class="token operator">:</span> <span class="token string">'network'</span><span class="token punctuation">,</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">condition</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">urlPattern</span><span class="token operator">:</span> <span class="token punctuation">{</span><span class="token literal-property property">pathname</span><span class="token operator">:</span> <span class="token string">'/archives/*'</span><span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">source</span><span class="token operator">:</span> <span class="token string">'network'</span><span class="token punctuation">,</span><br /> <span class="token punctuation">}</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>As the Service Worker Static Routing API evolves, the plan is for this configuration to get more flexible and support more scenarios, like declaratively racing a network fetch and service worker startup. See <a href="https://github.com/WICG/service-worker-static-routing-api/blob/main/final-form.md">the spec explainer's exploration of a potential "final form" of the API</a> for more details.</p>
<h2 id="trying-out-the-service-worker-static-routing-api" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/service-worker-static-routing-api-origin-trial/#trying-out-the-service-worker-static-routing-api" aria-hidden="true">#</a> Trying out the Service Worker Static Routing API</h2>
<p>The Service Worker Static Routing API is available in Chrome starting in version 116 behind <a href="https://developer.chrome.com/origintrials/#/view_trial/1458040379361198081">an origin trial</a>, which allows developers to test out the API on their site with real users to measure the effect. See <a href="https://developer.chrome.com/docs/web-platform/origin-trials/">"Get started with origin trials"</a> for background information on origin trials.</p>
<div class="aside aside--important"><div class="aside__label gap-bottom-300"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6A4.997 4.997 0 017 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z"></path></svg><span>Important</span></div>
<p>Unlike other origin trials you may have tried before, this origin trial will only work by putting the origin trial token in an HTTP header (<code>Origin-Trial: TOKEN_GOES_HERE</code>) <em>served on the service worker script</em>. It will not work in a <code><meta></code> element with the <code>http-equiv</code> attribute, or as a header on a page's HTML document.</p>
</div>
<p>For local testing, the Service Worker Static Routing API can be enabled with a flag at <code>chrome://flags/#service-worker-static-router</code>, or by running Chrome from the command like with <code>--enable-features=ServiceWorkerStaticRouter</code>.</p>
<h2 id="feedback-and-future-directions" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/service-worker-static-routing-api-origin-trial/#feedback-and-future-directions" aria-hidden="true">#</a> Feedback and future directions</h2>
<p>The Service Worker Static Routing API is actively being developed and still being shaped. If it seems like it could be useful for you, please try it out via <a href="https://developer.chrome.com/origintrials/#/view_trial/1458040379361198081">the origin trial</a> and <a href="https://github.com/WICG/service-worker-static-routing-api/issues">provide feedback on the API, implementation, and available functionality</a>.</p>
<p><em>Hero image from <a href="https://unsplash.com/">Unsplash</a>, by <a href="https://unsplash.com/@jankolar">Jan Antonin Kolar</a>.</em></p>
Introducing the scheduler.yield origin trial2023-08-29T00:00:00Zhttps://developer.chrome.com/en/blog/introducing-scheduler-yield-origin-trial/Jeremy Wagnerhttps://developer.chrome.com/authors/jlwagner/<p>Building websites that respond quickly to user input has been one of the most challenging aspects of web performance—one that the Chrome Team has been working hard to help web developers meet. Just this year, <a href="https://web.dev/articles/inp-cwv">it was announced</a> that the <a href="https://web.dev/articles/inp">Interaction to Next Paint (INP) metric</a> would graduate from experimental to pending status. It is now poised to replace <a href="https://web.dev/articles/fid">First Input Delay (FID)</a> as a Core Web Vital in March of 2024.</p>
<p>In a continued effort to deliver new APIs that help web developers make their websites as snappy as they can be, the Chrome Team is currently running an <a href="https://developer.chrome.com/origintrials/#/view_trial/836543630784069633">origin trial for <code>scheduler.yield</code></a> starting in version 115 of Chrome. <code>scheduler.yield</code> is a proposed new addition to the scheduler API that allows for both an easier and better way to yield control back to the main thread than <a href="https://web.dev/articles/optimize-long-tasks#manually_defer_code_execution">the methods that have been traditionally relied upon</a>.</p>
<h2 id="on-yielding" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/introducing-scheduler-yield-origin-trial/#on-yielding" aria-hidden="true">#</a> On yielding</h2>
<p>JavaScript uses the run-to-completion model to deal with tasks. This means that, when a task runs on the main thread, that task runs as long as necessary in order to complete. Upon a task's completion, control is <em>yielded</em> back to the main thread, which allows the main thread to process the next task in the queue.</p>
<p>Aside from extreme cases when a task never finishes—such as an infinite loop, for example—yielding is an inevitable aspect of JavaScript's task scheduling logic. It <em>will</em> happen, it's just a matter of <em>when</em>, and sooner is better than later. When tasks take too long to run—greater than 50 milliseconds, to be exact—they are considered to be <a href="https://web.dev/articles/long-tasks-devtools#what_are_long_tasks">long tasks</a>.</p>
<p>Long tasks are a source of poor page responsiveness, because they delay the browser's ability to respond to user input. The more often long tasks occur—and the longer they run—the more likely it is that users may get the impression that the page is sluggish, or even feel that it's altogether broken.</p>
<p>However, just because your code kicks off a task in the browser doesn't mean you have to wait until that task is finished before control is yielded back to the main thread. You can improve responsiveness to user input on a page by yielding explicitly in a task, which breaks the task up to be finished at the next available opportunity. This allows other tasks to get time on the main thread sooner than if they had to wait for long tasks to finish.</p>
<figure>
<img alt="A depiction of how breaking up a task can facilitate better input responsiveness. At the top, a long task blocks an event handler from running until the task is finished. At the bottom, the chunked up task permits the event handler to run sooner than it otherwise would have." decoding="async" height="448" loading="lazy" sizes="(min-width: 800px) 800px, calc(100vw - 48px)" src="https://wd.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0yV0ynwW7FujIwvCbCxQ.png?auto=format" srcset="https://wd.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0yV0ynwW7FujIwvCbCxQ.png?auto=format&w=200 200w, https://wd.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0yV0ynwW7FujIwvCbCxQ.png?auto=format&w=228 228w, https://wd.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0yV0ynwW7FujIwvCbCxQ.png?auto=format&w=260 260w, https://wd.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0yV0ynwW7FujIwvCbCxQ.png?auto=format&w=296 296w, https://wd.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0yV0ynwW7FujIwvCbCxQ.png?auto=format&w=338 338w, https://wd.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0yV0ynwW7FujIwvCbCxQ.png?auto=format&w=385 385w, https://wd.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0yV0ynwW7FujIwvCbCxQ.png?auto=format&w=439 439w, https://wd.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0yV0ynwW7FujIwvCbCxQ.png?auto=format&w=500 500w, https://wd.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0yV0ynwW7FujIwvCbCxQ.png?auto=format&w=571 571w, https://wd.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0yV0ynwW7FujIwvCbCxQ.png?auto=format&w=650 650w, https://wd.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0yV0ynwW7FujIwvCbCxQ.png?auto=format&w=741 741w, https://wd.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0yV0ynwW7FujIwvCbCxQ.png?auto=format&w=845 845w, https://wd.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0yV0ynwW7FujIwvCbCxQ.png?auto=format&w=964 964w, https://wd.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0yV0ynwW7FujIwvCbCxQ.png?auto=format&w=1098 1098w, https://wd.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0yV0ynwW7FujIwvCbCxQ.png?auto=format&w=1252 1252w, https://wd.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0yV0ynwW7FujIwvCbCxQ.png?auto=format&w=1428 1428w, https://wd.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0yV0ynwW7FujIwvCbCxQ.png?auto=format&w=1600 1600w" width="800" />
<figcaption>
A visualization of yielding control back to the main thread. At top, yielding occurs only after a task runs to completion, which means tasks can take longer to complete before returning control back to the main thread. At bottom, yielding is done explicitly, breaking up a long task into several smaller ones. This allows user interactions to run sooner, which improves input responsiveness and INP.
</figcaption>
</figure>
<p>When you explicitly yield, you're telling the browser "hey, I understand that the work I'm about to do could take a while, and I don't want you to have to do <em>all</em> of that work before responding to user input or other tasks that might be important, too". It's a valuable tool in a developer's toolbox that can go a long way towards improving the user experience.</p>
<h2 id="the-problem-with-current-yielding-strategies" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/introducing-scheduler-yield-origin-trial/#the-problem-with-current-yielding-strategies" aria-hidden="true">#</a> The problem with current yielding strategies</h2>
<div class="aside aside--important"><div class="aside__label gap-bottom-300"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6A4.997 4.997 0 017 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z"></path></svg><span>Important</span></div>
<p>If you're already familiar with current yielding methods—such as using <code>setTimeout</code>—you can jump <a href="https://developer.chrome.com/en/blog/introducing-scheduler-yield-origin-trial/#enter-scheduleryield">straight to the section about <code>scheduler.yield</code></a>.</p>
</div>
<p>A common method of yielding <a href="https://web.dev/articles/optimize-long-tasks#use_asyncawait_to_create_yield_points">uses <code>setTimeout</code> with a timeout value of <code>0</code></a>. This works because the callback passed to <code>setTimeout</code> will move the remaining work to a separate task that will be queued for subsequent execution. Rather than waiting for the browser to yield on its own, you're saying "let's break this big chunk of work up into smaller bits".</p>
<p>However, yielding with <code>setTimeout</code> carries a potentially undesirable side effect: the work that comes <em>after</em> the yield point will go to the back of the task queue. Tasks scheduled by user interactions will still go to the front of the queue as they should—but the remaining work you wanted to do after explicitly yielding could end up being further delayed by other tasks from competing sources that were queued ahead of it.</p>
<p>To see this in action, try out <a href="https://task-chunking-demo.glitch.me/interact.html">this Glitch demo</a>—or experiment with it in the embedded version below. The demo consists of a few buttons you can click, and a box beneath them that logs when tasks are run. When you land on the page, perform the following actions:</p>
<ol>
<li>Click the top button labeled <strong>Run tasks periodically</strong>, which will schedule blocking tasks to run every so often. When you click this button, the task log will populate with several messages that read <strong>Ran blocking task with <code>setInterval</code></strong>.</li>
<li>Next, click the button labeled <strong>Run loop, yielding with <code>setTimeout</code> on each iteration</strong>.</li>
</ol>
<div class="glitch-embed-wrap" style="height: 420px; width: 100%;"> <iframe style="height: 100%; width: 100%; border: 0;" title="scheduler-yield-demo on Glitch" src="https://glitch.com/embed/#!/embed/scheduler-yield-demo?attributionHidden=true&sidebarCollapsed=true&previewSize=100" allow="camera; clipboard-read; clipboard-write; encrypted-media; geolocation; microphone; midi" loading="lazy"></iframe>
</div>
<div class="aside aside--note">
<p><a href="https://scheduler-yield-demo.glitch.me/js/scripts.js">The source code for this demo is available</a> if you want to dig further into what's going on.</p>
</div>
<p>You'll notice that the box at the bottom of the demo will read something like this:</p>
<pre class="language-txt"><code class="language-txt">Processing loop item 1<br />Processing loop item 2<br />Ran blocking task via setInterval<br />Processing loop item 3<br />Ran blocking task via setInterval<br />Processing loop item 4<br />Ran blocking task via setInterval<br />Processing loop item 5<br />Ran blocking task via setInterval<br />Ran blocking task via setInterval</code></pre>
<p>This output demonstrates the "end of task queue" behavior that occurs when yielding with <code>setTimeout</code>. The loop that runs processes five items, and yields with <code>setTimeout</code> after each one has been processed.</p>
<p>This illustrates a common problem on the web: it's not unusual for a script—particularly a third-party script—to register a timer function that runs work on some interval. The "end of task queue" behavior that comes with yielding with <code>setTimeout</code> means that work from other task sources may get queued ahead of the remaining work that the loop has to do after yielding.</p>
<p>Depending on your application, this may or may not be a desirable outcome—but in many cases, this behavior is why developers may feel reluctant to give up control of the main thread so readily. Yielding is good because user interactions have the opportunity to run sooner, but it also allows other non-user interaction work to get time on the main thread too. It's a real problem—but <code>scheduler.yield</code> can help solve it!</p>
<h2 id="enter-scheduleryield" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/introducing-scheduler-yield-origin-trial/#enter-scheduleryield" aria-hidden="true">#</a> Enter <code>scheduler.yield</code></h2>
<p><code>scheduler.yield</code> has been available behind a flag as an <a href="https://developer.chrome.com/docs/web-platform/chrome-flags/">experimental web platform feature</a> since version 115 of Chrome. One question you might have is "why do I need a special function to yield when <code>setTimeout</code> already does it?"</p>
<p>It's worth noting that yielding was not a design goal of <code>setTimeout</code>, but rather a nice side effect in scheduling a callback to run at a later point in the future—even with a timeout value of <code>0</code> specified. What's more important to remember, however, is that yielding with <code>setTimeout</code> sends remaining work to the <em>back</em> of the task queue. By default, <code>scheduler.yield</code> sends remaining work to the <em>front</em> of the queue. This means that work you wanted to resume immediately after yielding won't take a back seat to tasks from other sources (with the notable exception of user interactions).</p>
<p><code>scheduler.yield</code> is a function that yields to the main thread and returns a <code>Promise</code> when called. This means you can <code>await</code> it in an <code>async</code> function:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">yieldy</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token comment">// Do some work...</span><br /> <span class="token comment">// ...</span><br /><br /> <span class="token comment">// Yield!</span><br /> <span class="token keyword">await</span> scheduler<span class="token punctuation">.</span><span class="token function">yield</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /> <span class="token comment">// Do some more work...</span><br /> <span class="token comment">// ...</span><br /><span class="token punctuation">}</span></code></pre>
<p>To see <code>scheduler.yield</code> in action, do the following:</p>
<ol>
<li>Navigate to <code>chrome://flags</code>.</li>
<li>Enable the <strong>Experimental Web Platform features</strong> experiment. You may have to restart Chrome after doing this.</li>
<li>Navigate to <a href="https://task-chunking-demo.glitch.me/interact.html">the demo page</a> or use the embedded version of it below this list.</li>
<li>Click the top button labeled <strong>Run tasks periodically</strong>.</li>
<li>Finally, click the button labeled <strong>Run loop, yielding with <code>scheduler.yield</code> on each iteration</strong>.</li>
</ol>
<div class="glitch-embed-wrap" style="height: 420px; width: 100%;"> <iframe style="height: 100%; width: 100%; border: 0;" title="scheduler-yield-demo on Glitch" src="https://glitch.com/embed/#!/embed/scheduler-yield-demo?attributionHidden=true&sidebarCollapsed=true&previewSize=100" allow="camera; clipboard-read; clipboard-write; encrypted-media; geolocation; microphone; midi" loading="lazy"></iframe>
</div>
<p>The output in the box at the bottom of the page will look something like this:</p>
<pre class="language-txt"><code class="language-txt">Processing loop item 1<br />Processing loop item 2<br />Processing loop item 3<br />Processing loop item 4<br />Processing loop item 5<br />Ran blocking task via setInterval<br />Ran blocking task via setInterval<br />Ran blocking task via setInterval<br />Ran blocking task via setInterval<br />Ran blocking task via setInterval</code></pre>
<p>Unlike the demo that yields using <code>setTimeout</code>, you can see that the loop—even though it yields after every iteration—doesn't send the remaining work to the back of the queue, but rather to the front of it. This gives you the best of both worlds: you can yield to improve input responsiveness on your website, but also ensure that the work you wanted to finish <em>after</em> yielding doesn't get delayed.</p>
<div class="aside aside--note">
<p>This is a basic primer on <code>scheduler.yield</code>, and is meant to illustrate what benefits it provides by default. However, there are advanced ways of using it, including integration with <a href="https://github.com/WICG/scheduling-apis/blob/main/explainers/prioritized-post-task.md"><code>scheduler.postTask</code></a>, and the ability to yield with explicit priorities. For more information, read this <a href="https://github.com/WICG/scheduling-apis/blob/main/explainers/yield-and-continuation.md">in-depth explainer</a>.</p>
</div>
<h2 id="give-it-a-try" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/introducing-scheduler-yield-origin-trial/#give-it-a-try" aria-hidden="true">#</a> Give it a try!</h2>
<p>If <code>scheduler.yield</code> looks interesting to you and you want to try it out, you can do so in two ways starting in version 115 of Chrome:</p>
<ol>
<li>If you want to experiment with <code>scheduler.yield</code> locally, type and enter <code>chrome://flags</code> in Chrome's address bar and select <strong>Enable</strong> from the dropdown in the <strong>Experimental Web Platform Features</strong> section. This will make <code>scheduler.yield</code> (and any other experimental features) available in only your instance of Chrome.</li>
<li>If you want to enable <code>scheduler.yield</code> for real Chromium users on a publicly accessible origin, you'll need to sign up for the <a href="https://developer.chrome.com/origintrials/#/view_trial/836543630784069633"><code>scheduler.yield</code> origin trial</a>. This allows you to safely experiment with proposed features for a given period of time, and gives the Chrome Team valuable insights into how those features are used in the field. For more information on how origin trials work, <a href="https://developer.chrome.com/docs/web-platform/origin-trials/">read this guide</a>.</li>
</ol>
<p>How you use <code>scheduler.yield</code>—while still supporting browsers that don't implement it—depends on what your goals are. You can use <a href="https://github.com/GoogleChromeLabs/scheduler-polyfill">the official polyfill</a>. The polyfill is useful if the following applies to your situation:</p>
<ol>
<li>You're already using <code>scheduler.postTask</code> in your application to schedule tasks.</li>
<li>You want to be able to set task and yielding priorities.</li>
<li>You want to be able to cancel or reprioritize tasks via the <code>TaskController</code> class the <code>scheduler.postTask</code> API offers.</li>
</ol>
<p>If this doesn't describe your situation, then the polyfill might not be for you. In that case, you can roll your own fallback in a couple of ways. The first approach uses <code>scheduler.yield</code> if it's available, but falls back to <code>setTimeout</code> if it isn't:</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// A function for shimming scheduler.yield and setTimeout:</span><br /><span class="token keyword">function</span> <span class="token function">yieldToMain</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token comment">// Use scheduler.yield if it exists:</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token string">'scheduler'</span> <span class="token keyword">in</span> window <span class="token operator">&&</span> <span class="token string">'yield'</span> <span class="token keyword">in</span> scheduler<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">return</span> scheduler<span class="token punctuation">.</span><span class="token function">yield</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /><br /> <span class="token comment">// Fall back to setTimeout:</span><br /> <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">Promise</span><span class="token punctuation">(</span><span class="token parameter">resolve</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token function">setTimeout</span><span class="token punctuation">(</span>resolve<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><br /><br /><span class="token comment">// Example usage:</span><br /><span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">doWork</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token comment">// Do some work:</span><br /> <span class="token comment">// ...</span><br /><br /> <span class="token keyword">await</span> <span class="token function">yieldToMain</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /> <span class="token comment">// Do some other work:</span><br /> <span class="token comment">// ...</span><br /><span class="token punctuation">}</span></code></pre>
<p>This can work, but as you might guess, browsers that don't support <code>scheduler.yield</code> will yield without "front of queue" behavior. If that means you'd rather not yield at all, you can try another approach which uses <code>scheduler.yield</code> if it's available, but won't yield at all if it isn't:</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// A function for shimming scheduler.yield with no fallback:</span><br /><span class="token keyword">function</span> <span class="token function">yieldToMain</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token comment">// Use scheduler.yield if it exists:</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token string">'scheduler'</span> <span class="token keyword">in</span> window <span class="token operator">&&</span> <span class="token string">'yield'</span> <span class="token keyword">in</span> scheduler<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">return</span> scheduler<span class="token punctuation">.</span><span class="token function">yield</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /><br /> <span class="token comment">// Fall back to nothing:</span><br /> <span class="token keyword">return</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><br /><br /><span class="token comment">// Example usage:</span><br /><span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">doWork</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token comment">// Do some work:</span><br /> <span class="token comment">// ...</span><br /><br /> <span class="token keyword">await</span> <span class="token function">yieldToMain</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /> <span class="token comment">// Do some other work:</span><br /> <span class="token comment">// ...</span><br /><span class="token punctuation">}</span></code></pre>
<p><code>scheduler.yield</code> is an exciting addition to the scheduler API—one that will hopefully make it easier for developers to improve responsiveness than current yielding strategies. If <code>scheduler.yield</code> seems like a useful API to you, please participate in our research to help improve it, and <a href="https://github.com/wicg/scheduling-apis/issues">provide feedback</a> on how it could be further improved.</p>
<p><em>Hero image from <a href="https://unsplash.com/">Unsplash</a>, by <a href="https://unsplash.com/@jallison154">Jonathan Allison</a>.</em></p>
Long Animation Frames API2023-08-22T00:00:00Zhttps://developer.chrome.com/en/articles/long-animation-frames/Barry Pollardhttps://developer.chrome.com/authors/tunetheweb/Noam Rosenthalhttps://developer.chrome.com/authors/nrosenthal/<p>The <a href="https://github.com/w3c/longtasks/blob/main/loaf-explainer.md">Long Animation Frames API</a> (LoAF - pronounced Lo-Af) is a new proposal from the Chrome team to update the <a href="https://w3c.github.io/longtasks/">Long Tasks API</a> to provide a better understanding of slow user interface (UI) updates. This can be useful to identify slow animation frames which are likely to affect the pending <a href="https://web.dev/articles/inp">Interaction to Next Paint (INP)</a> Core Web Vital metric which measures responsiveness, or to identify other UI jank which affects <a href="https://web.dev/articles/smoothness">smoothness</a>.</p>
<p>The LoAF API is available as <a href="https://developer.chrome.com/origintrials/#/view_trial/3935020174414970881">an origin trial from Chrome 116</a> for developers to experiment with and provide feedback on.</p>
<h2 id="the-long-tasks-api" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/articles/long-animation-frames/#the-long-tasks-api" aria-hidden="true">#</a> The Long Tasks API</h2>
<p>The Long Animation Frames API is an alternative to the Long Tasks API which has been available in Chrome for some time now (since Chrome 58). As its name suggests, the Long Task API allows you to monitor for long tasks, which are tasks that occupy the main thread for 50 milliseconds or longer. Long tasks can be monitored using the <a href="https://developer.mozilla.org/docs/Web/API/PerformanceLongTaskTiming"><code>PerformanceLongTaskTiming</code></a> interface, with a <a href="https://developer.mozilla.org/docs/Web/API/PerformanceObserver"><code>PeformanceObserver</code></a>:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> observer <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">PerformanceObserver</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">list</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>list<span class="token punctuation">.</span><span class="token function">getEntries</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br />observer<span class="token punctuation">.</span><span class="token function">observe</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">type</span><span class="token operator">:</span> <span class="token string">'longtask'</span><span class="token punctuation">,</span> <span class="token literal-property property">buffered</span><span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Long tasks are likely to cause responsiveness issues. If a user tries to interact with a page—for example, click a button, or open a menu—but the main thread is already dealing with a long task, then the <a href="https://web.dev/articles/optimize-input-delay#what_is_input_delay">user's interaction is delayed</a> waiting for that task to be completed.</p>
<p>To improve responsiveness, it is often advised to <a href="https://web.dev/articles/optimize-long-tasks">break up long tasks</a>. If each long task is instead broken up into a series of multiple, smaller tasks, it may allow more important tasks to be executed in between them to avoid significant delays in responding to interactions.</p>
<p>So when trying to improve responsiveness, the first effort is often to run a performance trace and look at long tasks. This could be through a lab-based auditing tool like Lighthouse (which has an <strong>Avoid long main-thread tasks</strong> audit), or by <a href="https://web.dev/articles/long-tasks-devtools">looking at long tasks in Chrome DevTools</a>.</p>
<p>Lab-based testing is <a href="https://web.dev/articles/diagnose-slow-interactions-in-the-lab">often a poor starting place for identifying responsiveness issues</a>, as these tools may not include interactions—when they do, they are a small subset of likely interactions. Ideally, you would measure causes of slow interactions in the field.</p>
<h3 id="shortcomings-of-the-long-tasks-api" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/articles/long-animation-frames/#shortcomings-of-the-long-tasks-api" aria-hidden="true">#</a> Shortcomings of the Long Tasks API</h3>
<p>Measuring long tasks in the field using the Performance Observer above is only somewhat useful. In reality, it doesn't give that much information beyond the fact that a long task happened, and how long it took.</p>
<p>Real User Monitoring (RUM) tools often use this to trend the number or duration of long tasks or identifying which pages they happen on—but without the underlying details of what caused the long task, this is only of limited use. The Long Tasks API only has a <a href="https://developer.mozilla.org/docs/Web/API/TaskAttributionTiming">basic attribution model</a>, which at best only tells you the container the long task happened in (the top-level document or an <code><iframe></code>), but not the script or function which called it, as shown by a typical entry below:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span><br /> <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"unknown"</span><span class="token punctuation">,</span><br /> <span class="token property">"entryType"</span><span class="token operator">:</span> <span class="token string">"longtask"</span><span class="token punctuation">,</span><br /> <span class="token property">"startTime"</span><span class="token operator">:</span> <span class="token number">31.799999997019768</span><span class="token punctuation">,</span><br /> <span class="token property">"duration"</span><span class="token operator">:</span> <span class="token number">136</span><span class="token punctuation">,</span><br /> <span class="token property">"attribution"</span><span class="token operator">:</span> <span class="token punctuation">[</span><br /> <span class="token punctuation">{</span><br /> <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"unknown"</span><span class="token punctuation">,</span><br /> <span class="token property">"entryType"</span><span class="token operator">:</span> <span class="token string">"taskattribution"</span><span class="token punctuation">,</span><br /> <span class="token property">"startTime"</span><span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span><br /> <span class="token property">"duration"</span><span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span><br /> <span class="token property">"containerType"</span><span class="token operator">:</span> <span class="token string">"window"</span><span class="token punctuation">,</span><br /> <span class="token property">"containerSrc"</span><span class="token operator">:</span> <span class="token string">""</span><span class="token punctuation">,</span><br /> <span class="token property">"containerId"</span><span class="token operator">:</span> <span class="token string">""</span><span class="token punctuation">,</span><br /> <span class="token property">"containerName"</span><span class="token operator">:</span> <span class="token string">""</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">]</span><br /><span class="token punctuation">}</span></code></pre>
<p>The Long Tasks API is also an incomplete view, since it may also exclude some important tasks. Some updates—like rendering—happen in separate tasks that ideally should be included together with the preceding execution that caused that update to accurately measure the "total work" for that interaction. For more details of the limitations of relying on tasks, see <a href="https://github.com/w3c/longtasks/blob/main/loaf-explainer.md#where-long-tasks-fall-short">the "Where long tasks fall short" section of the API's explainer</a>.</p>
<p>The final issue is that measuring long tasks only reports on individual tasks that take longer than the 50 millisecond limit. An animation frame could be made up of several tasks below this 50 millisecond limit, yet collectively still block the browser's ability to render.</p>
<h2 id="the-long-animation-frames-api" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/articles/long-animation-frames/#the-long-animation-frames-api" aria-hidden="true">#</a> The Long Animation Frames API</h2>
<p>The <a href="https://github.com/w3c/longtasks/blob/main/loaf-explainer.md">Long Animation Frames API</a> (LoAF) is a new API that seeks to address some of the shortcomings of the Long Tasks API to enable developers to get more actionable insights to help address responsiveness problems and improve INP.</p>
<p>Good responsiveness means that a page responds quickly to interactions made with it. That involves being able to paint any updates needed by the user in a timely manner, and avoiding blocking these updates from happening. For INP, <a href="https://web.dev/articles/inp#what_is_a_good_inp_score">it is recommended to respond in 200 milliseconds or less</a>, but for other updates (for example, animations) even that may be too long.</p>
<p>The Long Animation Frames API is an alternative approach to measuring blocking work. Rather than measuring the individual <em>tasks</em>, the Long Animation Frames API—as its name suggests—measures <em>long animation frames</em>. A long animation frame is when a rendering update is delayed beyond 50 milliseconds (the same as the Long Tasks API's threshold).</p>
<p>Long animation frames can be observed in a similar way as long tasks with a <code>PerformanceObserver</code>, but looking at <code>long-animation-frame</code> type instead:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> observer <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">PerformanceObserver</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">list</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>list<span class="token punctuation">.</span><span class="token function">getEntries</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br />observer<span class="token punctuation">.</span><span class="token function">observe</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">type</span><span class="token operator">:</span> <span class="token string">'long-animation-frame'</span><span class="token punctuation">,</span> <span class="token literal-property property">buffered</span><span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Previous long animation frames can also be queried from the <a href="https://www.w3.org/TR/performance-timeline/#performance-timeline">Performance Timeline</a> like so:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> loafs <span class="token operator">=</span> performance<span class="token punctuation">.</span><span class="token function">getEntriesByType</span><span class="token punctuation">(</span><span class="token string">'long-animation-frame'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>However, there is <a href="https://w3c.github.io/timing-entrytypes-registry/#registry">a <code>maxBufferSize</code> for performance entries</a> after which older entries are dropped. The <code>long-animation-frame</code> buffer size is currently set to 200, the same as for <code>long-tasks</code>.</p>
<h3 id="advantages-of-looking-at-frames-instead-of-tasks" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/articles/long-animation-frames/#advantages-of-looking-at-frames-instead-of-tasks" aria-hidden="true">#</a> Advantages of looking at frames instead of tasks</h3>
<p>The key advantage of looking at this from a frame perspective rather than a tasks perspective, is that a long animation can be made up of any number of tasks that cumulatively resulted in a long animation frame. This addresses the final point above, where the sum of many smaller, render-blocking tasks before an animation frame may not be surfaced by the Long Tasks API.</p>
<p>A further advantage of this alternative view on long tasks, is the ability to provide timing breakdowns of the entire frame. Rather than just including a <code>startTime</code> and a <code>duration</code>, like the Long Tasks API, LoAF includes a much more detailed breakdown of the various parts of the frame duration including:</p>
<ul>
<li><code>startTime</code>: the start time of the long animation frame relative to the navigation start time.</li>
<li><code>duration</code>: the duration of the long animation frame.</li>
<li><code>renderStart</code>: the start time of the rendering cycle, which includes <code>requestAnimationFrame</code> callbacks, style and layout calculation, resize observer and intersection observer callbacks.</li>
<li><code>styleAndLayoutStart</code>: the beginning of the time period spent in style and layout calculations.</li>
<li><code>desiredRenderStart</code>: the time the animation frame was queued.</li>
<li><code>presentationTime</code>: the implementation-specific time when the frame was actually presented. (Note: this is not implemented yet.)</li>
<li><code>firstUIEventTimestamp</code>: the time of the first UI event (mouse/keyboard and so on) to be handled during the course of this frame.</li>
<li><code>blockingDuration</code>: the duration in milliseconds for which the animation frame was being blocked.</li>
</ul>
<p>These timestamps allow the long animation frame to be divided into timings:</p>
<table>
<thead>
<tr>
<th>Timing</th>
<th>Calculation</th>
</tr>
</thead>
<tbody>
<tr>
<td>Start Time</td>
<td><code>startTime</code></td>
</tr>
<tr>
<td>End Time</td>
<td><code>startTime + duration</code></td>
</tr>
<tr>
<td>Delay</td>
<td><code>desiredRenderStart ? Math.max(0,startTime - desiredRenderStart) : 0;</code></td>
</tr>
<tr>
<td>Deferred duration</td>
<td><code>Math.max(0, desiredRenderStart - startTime)</code></td>
</tr>
<tr>
<td>Render duration</td>
<td><code>styleAndLayoutStart - renderStart</code></td>
</tr>
<tr>
<td>Work duration</td>
<td><code>renderStart ? renderStart - startTime : duration;</code></td>
</tr>
<tr>
<td>Style and Layout duration</td>
<td><code>styleAndLayoutStart ? (startTime + duration) - styleAndLayoutStart : 0;</code></td>
</tr>
</tbody>
</table>
<p>For more details on these individual timings, <a href="https://github.com/w3c/longtasks/blob/main/loaf-explainer.md#how-a-loaf-entry-might-look-like">refer to the explainer</a>, which provides fine-grained detail as to which activity is contributing to a long animation frame.</p>
<h3 id="better-attribution" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/articles/long-animation-frames/#better-attribution" aria-hidden="true">#</a> Better attribution</h3>
<p>The <code>long-animation-frame</code> entry type includes better attribution data of each script that contributed to a long animation frame. Similar to the Long Tasks API, this will be provided in an array of attribution entries, each of which details:</p>
<ul>
<li>A meaningful <code>name</code>, indicating how the script was called (for example, <code>'IMG#id.onload'</code>, <code>'Window.requestAnimationFrame'</code>, or <code>'Response.json.then'</code>).</li>
<li>The <code>type</code> for of the script entry point:
<ul>
<li><code>user-callback</code>: A known callback registered from a web platform API (for example, <code>setTimeout</code>, <code>requestAnimationFrame</code>).</li>
<li><code>event-listener</code>: A listener to a platform event (for example, <code>click</code>, <code>load, keyup</code>).</li>
<li><code>resolve-promise</code>: Handler of a platform promise (for example, <code>fetch()</code>. Note that in the case of promises,all the handlers of the same promises are mixed together as one "script")<code>.</code></li>
<li><code>reject-promise</code>: as per above, but for the reject.</li>
<li><code>classic-script</code>: Script evaluation (for example, <code><script> or import()</code>)</li>
<li><code>module-script</code>: same as above, but for module scripts.</li>
</ul>
</li>
<li>Separate timing data for that script:
<ul>
<li><code>startTime</code>: time the entry function was invoked.</li>
<li><code>executionStart</code>: the time after compilation.</li>
<li><code>duration</code>: the duration between <code>startTime</code> and when the subsequent microtask queue has finished processing.</li>
<li><code>forcedStyleAndLayoutDuration</code>: the total time spent processing forced layout/style inside this function (see <a href="https://web.dev/articles/avoid-large-complex-layouts-and-layout-thrashing#avoid_layout_thrashing">thrashing</a>).</li>
<li><code>desiredExecutionStart</code>: the time when the callback was queued.</li>
</ul>
</li>
<li><code>sourceLocation</code>: the script resource name and character position.</li>
<li><code>windowAttribution</code>: the container (the top-level document, or an <code><iframe></code>) the long animation frame occurred in.</li>
<li><code>window</code>: a reference to the same-origin window.</li>
</ul>
<p>This allows developers to know exactly how each script in the long animation frame was called, down to the character position in the calling script giving the exact location in a JavaScript resource that resulted in the long animation frame.</p>
<div class="aside aside--important"><div class="aside__label gap-bottom-300"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6A4.997 4.997 0 017 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z"></path></svg><span>Important</span></div>
<p>Note the script this will be the script <em>entry point</em>, rather than necessarily the precise location in the script that took up the most time. For example, if an event handler calls a library, which calls a function which is slow, the event handler will be reported in the LoAF entry, not the library nor the function.</p>
</div>
<h3 id="example-of-a-long-animation-frame-performance-entry" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/articles/long-animation-frames/#example-of-a-long-animation-frame-performance-entry" aria-hidden="true">#</a> Example of a <code>long-animation-frame</code> performance entry</h3>
<p>A complete <code>long-animation-frame</code> performance entry example, containing a single script, is shown below:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span><br /> <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"long-animation-frame"</span><span class="token punctuation">,</span><br /> <span class="token property">"entryType"</span><span class="token operator">:</span> <span class="token string">"long-animation-frame"</span><span class="token punctuation">,</span><br /> <span class="token property">"startTime"</span><span class="token operator">:</span> <span class="token number">11802.400000000373</span><span class="token punctuation">,</span><br /> <span class="token property">"duration"</span><span class="token operator">:</span> <span class="token number">60</span><span class="token punctuation">,</span><br /> <span class="token property">"navigationId"</span><span class="token operator">:</span> <span class="token string">"b429c2e4-7e65-4f68-8a96-72772911d28a"</span><span class="token punctuation">,</span><br /> <span class="token property">"renderStart"</span><span class="token operator">:</span> <span class="token number">11858.800000000745</span><span class="token punctuation">,</span><br /> <span class="token property">"styleAndLayoutStart"</span><span class="token operator">:</span> <span class="token number">11858.800000000745</span><span class="token punctuation">,</span><br /> <span class="token property">"desiredRenderStart"</span><span class="token operator">:</span> <span class="token number">11846.699999999255</span><span class="token punctuation">,</span><br /> <span class="token property">"firstUIEventTimestamp"</span><span class="token operator">:</span> <span class="token number">11801.099999999627</span><span class="token punctuation">,</span><br /> <span class="token property">"blockingDuration"</span><span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span><br /> <span class="token property">"scripts"</span><span class="token operator">:</span> <span class="token punctuation">[</span><br /> <span class="token punctuation">{</span><br /> <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"DOMWindow.onclick"</span><span class="token punctuation">,</span><br /> <span class="token property">"entryType"</span><span class="token operator">:</span> <span class="token string">"script"</span><span class="token punctuation">,</span><br /> <span class="token property">"startTime"</span><span class="token operator">:</span> <span class="token number">11803.199999999255</span><span class="token punctuation">,</span><br /> <span class="token property">"duration"</span><span class="token operator">:</span> <span class="token number">45</span><span class="token punctuation">,</span><br /> <span class="token property">"navigationId"</span><span class="token operator">:</span> <span class="token string">"b429c2e4-7e65-4f68-8a96-72772911d28a"</span><span class="token punctuation">,</span><br /> <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"event-listener"</span><span class="token punctuation">,</span><br /> <span class="token property">"windowAttribution"</span><span class="token operator">:</span> <span class="token string">"self"</span><span class="token punctuation">,</span><br /> <span class="token property">"executionStart"</span><span class="token operator">:</span> <span class="token number">11803.199999999255</span><span class="token punctuation">,</span><br /> <span class="token property">"desiredExecutionStart"</span><span class="token operator">:</span> <span class="token number">11801.099999999627</span><span class="token punctuation">,</span><br /> <span class="token property">"forcedStyleAndLayoutDuration"</span><span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span><br /> <span class="token property">"pauseDuration"</span><span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span><br /> <span class="token property">"sourceLocation"</span><span class="token operator">:</span> <span class="token string">"https://web.dev/js/index-ffde4443.js:17796"</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">]</span><br /><span class="token punctuation">}</span></code></pre>
<p>As can be seen, this gives an unprecedented amount of data for websites to be able to understand the cause of laggy rendering updates.</p>
<h2 id="enabling-the-long-animation-frames-api" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/articles/long-animation-frames/#enabling-the-long-animation-frames-api" aria-hidden="true">#</a> Enabling the Long Animation Frames API</h2>
<p>The Long Animation Frames API is available in Chrome behind the Experimental Web Platform Features flag and can be enabled at: <code>chrome://flags/#enable-experimental-web-platform-features</code>.</p>
<p>This feature is also <a href="https://developer.chrome.com/origintrials/#/view_trial/3935020174414970881">entering an origin trial from Chrome 116</a>, which allows developers to enable the feature for visitors to their sites to collect data from real users. See <a href="https://developer.chrome.com/en/docs/web-platform/origin-trials/">Get started with origin trials</a> for more information on origin trials.</p>
<h2 id="using-the-long-animation-frames-api-in-the-field" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/articles/long-animation-frames/#using-the-long-animation-frames-api-in-the-field" aria-hidden="true">#</a> Using the Long Animation Frames API in the field</h2>
<p>Tools like Lighthouse—while useful for discovering and reproducing issues—are lab tools that may miss important aspects of the user experience that only field data can provide. The Long Animation Frames API can be used in the field to gather important contextual data for user interactions that the Long Tasks API could not. This can help you to surface and reproduce issues with interactivity that you might not have otherwise discovered.</p>
<p>Some suggested strategies are listed next, but the Chrome team is keen to hear feedback on this API and how developers and RUM providers would see themselves using the information the API provides.</p>
<h3 id="feature-detecting-long-animation-frames-api-support" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/articles/long-animation-frames/#feature-detecting-long-animation-frames-api-support" aria-hidden="true">#</a> Feature detecting Long Animation Frames API support</h3>
<p>You can use the following code to test if the API is supported:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">if</span> <span class="token punctuation">(</span>PerformanceObserver<span class="token punctuation">.</span>supportedEntryTypes<span class="token punctuation">.</span><span class="token function">includes</span><span class="token punctuation">(</span><span class="token string">'long-animation-frame'</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token comment">// Monitor LoAFs</span><br /><span class="token punctuation">}</span></code></pre>
<h3 id="reporting-long-animation-data-back-to-an-analytics-endpoint" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/articles/long-animation-frames/#reporting-long-animation-data-back-to-an-analytics-endpoint" aria-hidden="true">#</a> Reporting long animation data back to an analytics endpoint</h3>
<p>As shown, the LoAF performance entry includes valuable information. One strategy would be to monitor all LoAFs and beacon the ones above a certain threshold back to an analytics endpoint for later analysis:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> <span class="token constant">REPORTING_THRESHOLD_MS</span> <span class="token operator">=</span> <span class="token number">150</span><span class="token punctuation">;</span><br /><br /><span class="token keyword">const</span> observer <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">PerformanceObserver</span><span class="token punctuation">(</span><span class="token parameter">list</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">const</span> entry <span class="token keyword">of</span> list<span class="token punctuation">.</span><span class="token function">getEntries</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span>entry<span class="token punctuation">.</span>duration <span class="token operator">></span> <span class="token constant">REPORTING_THRESHOLD_MS</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token comment">// Example here logs to console, but could also report back to analytics</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>entry<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br />observer<span class="token punctuation">.</span><span class="token function">observe</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">type</span><span class="token operator">:</span> <span class="token string">'long-animation-frame'</span><span class="token punctuation">,</span> <span class="token literal-property property">buffered</span><span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>As the long animation frame entries can be quite large, developers should decide what data from the entry should be sent to analytics. For example, the summary times of the entry and perhaps the script names, or some other minimum set of other contextual data that may be deemed necessary.</p>
<h3 id="observing-the-worst-long-animation-frames" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/articles/long-animation-frames/#observing-the-worst-long-animation-frames" aria-hidden="true">#</a> Observing the worst long animation frames</h3>
<p>Sites may wish to collect data on the longest animation frame (or frames), to reduce the volume of data that needs to be beaconed. So no matter how many long animation frames a page experiences, only data for the worst one, five, or however many long animation frames absolutely necessary is beaconed back.</p>
<pre class="language-js"><code class="language-js"><span class="token constant">MAX_LOAFS_TO_CONSIDER</span> <span class="token operator">=</span> <span class="token number">10</span><span class="token punctuation">;</span><br /><span class="token keyword">let</span> longestBlockingLoAFs <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br /><br /><span class="token keyword">const</span> observer <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">PerformanceObserver</span><span class="token punctuation">(</span><span class="token parameter">list</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> longestBlockingLoAFs <span class="token operator">=</span> longestBlockingLoAFs<span class="token punctuation">.</span><span class="token function">concat</span><span class="token punctuation">(</span>list<span class="token punctuation">.</span><span class="token function">getEntries</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">sort</span><span class="token punctuation">(</span><br /> <span class="token punctuation">(</span><span class="token parameter">a<span class="token punctuation">,</span> b</span><span class="token punctuation">)</span> <span class="token operator">=></span> b<span class="token punctuation">.</span>blockingDuration <span class="token operator">-</span> a<span class="token punctuation">.</span>blockingDuration<br /> <span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">slice</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token constant">MAX_LOAFS_TO_CONSIDER</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br />observer<span class="token punctuation">.</span><span class="token function">observe</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">type</span><span class="token operator">:</span> <span class="token string">'long-animation-frame'</span><span class="token punctuation">,</span> <span class="token literal-property property">buffered</span><span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>At the appropriate time (<a href="https://developer.chrome.com/articles/page-lifecycle-api/#:~:text=it%27s%20always%20better%20to%20rely%20on%20the%20visibilitychange%20event%20to%20determine%20when%20a%20session%20ends">ideally on the <code>visibilitychange</code> event</a>) beacon back to analytics. For local testing you can use <code>console.table</code> periodically:</p>
<pre class="language-js"><code class="language-js">console<span class="token punctuation">.</span><span class="token function">table</span><span class="token punctuation">(</span>longestBlockingLoAFs<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<h3 id="linking-to-the-longest-inp-interaction" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/articles/long-animation-frames/#linking-to-the-longest-inp-interaction" aria-hidden="true">#</a> Linking to the longest INP interaction</h3>
<p>As an extension of the above, the LoAF frame(s) corresponding to the INP entry could be used as attribution data to give further details on how to improve INP.</p>
<p>There is <a href="https://github.com/w3c/longtasks/issues/115">currently no direct API to link an INP entry with its related LoAF entry or entries</a>, though it is possible to do so in code by comparing the start and end times of each (see <a href="https://gist.github.com/noamr/316bd48157ab35e4f632a8c2583281b7">this example script</a>).</p>
<h3 id="reporting-long-animation-frames-with-interactions" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/articles/long-animation-frames/#reporting-long-animation-frames-with-interactions" aria-hidden="true">#</a> Reporting long animation frames with interactions</h3>
<p>An alternative approach that requires less code would be to always send the largest (or top X largest) LoAF entries where an interaction occurred during the frame (which can be detected by the presence of a <code>firstUIEventTimestamp</code> value). In most cases this will include the INP interaction for a given visit, and in rare cases when it doesn't it still surfaces long interactions that are important to fix, as they may be the INP interaction for other users.</p>
<p>The following code logs all LoAF entries greater than 150 milliseconds where an interaction occurred during the frame. The 150 is chosen here because it is slightly less than the 200 millisecond "good" INP threshold. You could choose a higher or lower value depending on your needs.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> <span class="token constant">REPORTING_THRESHOLD_MS</span> <span class="token operator">=</span> <span class="token number">150</span><span class="token punctuation">;</span><br /><br /><span class="token keyword">const</span> observer <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">PerformanceObserver</span><span class="token punctuation">(</span><span class="token parameter">list</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">const</span> entry <span class="token keyword">of</span> list<span class="token punctuation">.</span><span class="token function">getEntries</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span>entry<span class="token punctuation">.</span>duration <span class="token operator">></span> <span class="token constant">REPORTING_THRESHOLD_MS</span> <span class="token operator">&&</span><br /> entry<span class="token punctuation">.</span>firstUIEventTimestamp <span class="token operator">></span> <span class="token number">0</span><br /> <span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token comment">// Example here logs to console, but could also report back to analytics</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>entry<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br />observer<span class="token punctuation">.</span><span class="token function">observe</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">type</span><span class="token operator">:</span> <span class="token string">'long-animation-frame'</span><span class="token punctuation">,</span> <span class="token literal-property property">buffered</span><span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<h3 id="identifying-common-patterns-in-long-animation-frames" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/articles/long-animation-frames/#identifying-common-patterns-in-long-animation-frames" aria-hidden="true">#</a> Identifying common patterns in long animation frames</h3>
<p>An alternative strategy would be to look at common scripts appearing the most in long animation frame entries. Data could be reported back at a script and/or character position level to identify repeat offenders.</p>
<p>This may work particularly well for customizable platforms where themes or plugins causing performance issues could be more easily identified across a number of sites.</p>
<p>The execution time of common scripts—or third-party origins—in long animation frames could be summed up and reported back to identify common contributors to long animation frames across a site or a collection of sites.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> observer <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">PerformanceObserver</span><span class="token punctuation">(</span><span class="token parameter">list</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token keyword">const</span> allScripts <span class="token operator">=</span> list<span class="token punctuation">.</span><span class="token function">getEntries</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">flatMap</span><span class="token punctuation">(</span><span class="token parameter">entry</span> <span class="token operator">=></span> entry<span class="token punctuation">.</span>scripts<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token keyword">const</span> scriptNames <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token operator">...</span><span class="token keyword">new</span> <span class="token class-name">Set</span><span class="token punctuation">(</span>allScripts<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token parameter">script</span> <span class="token operator">=></span> script<span class="token punctuation">.</span>name<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br /> <span class="token keyword">const</span> scriptsByName <span class="token operator">=</span> scriptNames<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token parameter">name</span> <span class="token operator">=></span> <span class="token punctuation">(</span><span class="token punctuation">[</span>name<span class="token punctuation">,</span><br /> allScripts<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token parameter">script</span> <span class="token operator">=></span> script<span class="token punctuation">.</span>name <span class="token operator">===</span> name<span class="token punctuation">)</span><br /> <span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token keyword">const</span> processedScripts <span class="token operator">=</span> scriptsByName<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">[</span>name<span class="token punctuation">,</span> scripts<span class="token punctuation">]</span></span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">(</span><span class="token punctuation">{</span><br /> name<span class="token punctuation">,</span><br /> <span class="token literal-property property">count</span><span class="token operator">:</span> scripts<span class="token punctuation">.</span>length<span class="token punctuation">,</span><br /> <span class="token literal-property property">totalDuration</span><span class="token operator">:</span> scripts<span class="token punctuation">.</span><span class="token function">reduce</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">subtotal<span class="token punctuation">,</span> script</span><span class="token punctuation">)</span> <span class="token operator">=></span> subtotal <span class="token operator">+</span> script<span class="token punctuation">.</span>duration<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> processedScripts<span class="token punctuation">.</span><span class="token function">sort</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">a<span class="token punctuation">,</span> b</span><span class="token punctuation">)</span> <span class="token operator">=></span> b<span class="token punctuation">.</span>totalDuration <span class="token operator">-</span> a<span class="token punctuation">.</span>totalDuration<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token comment">// Example here logs to console, but could also report back to analytics</span><br /> console<span class="token punctuation">.</span><span class="token function">table</span><span class="token punctuation">(</span>processedScripts<span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br />observer<span class="token punctuation">.</span><span class="token function">observe</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token literal-property property">type</span><span class="token operator">:</span> <span class="token string">'long-animation-frame'</span><span class="token punctuation">,</span> <span class="token literal-property property">buffered</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>And example of this output is shown below:</p>
<figure>
<img alt="A screenshot of example DevTools output of scripts order by duration, consisting of a table with columns for the index, the name of the script, the count, and the total duration." decoding="async" height="149" loading="lazy" sizes="(min-width: 800px) 800px, calc(100vw - 48px)" src="https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wBGFND4u3dgkt2HyEZP2.png?auto=format" srcset="https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wBGFND4u3dgkt2HyEZP2.png?auto=format&w=200 200w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wBGFND4u3dgkt2HyEZP2.png?auto=format&w=228 228w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wBGFND4u3dgkt2HyEZP2.png?auto=format&w=260 260w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wBGFND4u3dgkt2HyEZP2.png?auto=format&w=296 296w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wBGFND4u3dgkt2HyEZP2.png?auto=format&w=338 338w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wBGFND4u3dgkt2HyEZP2.png?auto=format&w=385 385w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wBGFND4u3dgkt2HyEZP2.png?auto=format&w=439 439w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wBGFND4u3dgkt2HyEZP2.png?auto=format&w=500 500w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wBGFND4u3dgkt2HyEZP2.png?auto=format&w=571 571w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wBGFND4u3dgkt2HyEZP2.png?auto=format&w=650 650w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wBGFND4u3dgkt2HyEZP2.png?auto=format&w=741 741w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wBGFND4u3dgkt2HyEZP2.png?auto=format&w=845 845w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wBGFND4u3dgkt2HyEZP2.png?auto=format&w=964 964w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wBGFND4u3dgkt2HyEZP2.png?auto=format&w=1098 1098w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wBGFND4u3dgkt2HyEZP2.png?auto=format&w=1252 1252w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wBGFND4u3dgkt2HyEZP2.png?auto=format&w=1428 1428w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wBGFND4u3dgkt2HyEZP2.png?auto=format&w=1600 1600w" width="800" />
</figure>
<h2 id="using-the-long-animation-frames-api-in-tooling" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/articles/long-animation-frames/#using-the-long-animation-frames-api-in-tooling" aria-hidden="true">#</a> Using the Long Animation Frames API in tooling</h2>
<p>The API could also allow additional developer tooling for local debugging. While some tooling like Lighthouse and Chrome DevTools have been able to gather much of this data using lower-level tracing details, having this higher-level API could allow other tools to access this data.</p>
<h3 id="surfacing-long-animation-frames-data-in-devtools" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/articles/long-animation-frames/#surfacing-long-animation-frames-data-in-devtools" aria-hidden="true">#</a> Surfacing long animation frames data in DevTools</h3>
<p>You can surface long animation frames in DevTools using the <a href="https://developer.mozilla.org/docs/Web/API/Performance/measure"><code>performance.measure()</code></a> API, which are then displayed <a href="https://developer.chrome.com/docs/devtools/performance-insights/#timings">in the DevTools user timings track</a> in performance traces to show where to focus your efforts for performance improvements:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> observer <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">PerformanceObserver</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">list</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">const</span> entry <span class="token keyword">of</span> list<span class="token punctuation">.</span><span class="token function">getEntries</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> performance<span class="token punctuation">.</span><span class="token function">measure</span><span class="token punctuation">(</span><span class="token string">'LoAF'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">start</span><span class="token operator">:</span> entry<span class="token punctuation">.</span>startTime<span class="token punctuation">,</span><br /> <span class="token literal-property property">end</span><span class="token operator">:</span> entry<span class="token punctuation">.</span>startTime <span class="token operator">+</span> entry<span class="token punctuation">.</span>duration<span class="token punctuation">,</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br />observer<span class="token punctuation">.</span><span class="token function">observe</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">type</span><span class="token operator">:</span> <span class="token string">'long-animation-frame'</span><span class="token punctuation">,</span> <span class="token literal-property property">buffered</span><span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>If this API proves useful in the long term, it will likely be incorporated into DevTools itself, but the above code snippet allows it to be surfaced there in the meantime.</p>
<h3 id="using-long-animation-frames-data-in-other-developer-tooling" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/articles/long-animation-frames/#using-long-animation-frames-data-in-other-developer-tooling" aria-hidden="true">#</a> Using long animation frames data in other developer tooling</h3>
<p>The <a href="https://web.dev/articles/debug-cwvs-with-web-vitals-extension">Web Vitals extension has shown the value in logging summary debug information</a> to diagnose performance issues. Should this proposal prove useful and the API is launched, tools like that could more easily surface data to help make developers aware of where to concentrate their efforts. Similarly, this could be added to the <a href="https://github.com/GoogleChrome/web-vitals">web vitals JavaScript library</a> in the future.</p>
<h3 id="using-long-animation-frames-data-in-automated-testing-tools" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/articles/long-animation-frames/#using-long-animation-frames-data-in-automated-testing-tools" aria-hidden="true">#</a> Using long animation frames data in automated testing tools</h3>
<p>Similarly automated testing tools, perhaps in CI/CD pipelines, could surface details on potential performance issues by measuring long animation frames while running various test suites.</p>
<h2 id="faq" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/articles/long-animation-frames/#faq" aria-hidden="true">#</a> FAQ</h2>
<p>Below are some of the frequently asked questions on this API:</p>
<h3 id="why-not-just-extend-or-iterate-on-the-long-tasks-api" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/articles/long-animation-frames/#why-not-just-extend-or-iterate-on-the-long-tasks-api" aria-hidden="true">#</a> Why not just extend or iterate on the Long Tasks API?</h3>
<p>This is an alternative look at reporting a similar—but ultimately different—measurement of potential responsiveness issues. It's important to ensure sites relying on the existing Long Tasks API continue to function to avoid disrupting existing use cases.</p>
<p>While the Long Tasks API may benefit from some of the features of LoAF (such as a better attribution model), we believe that focusing on frames rather than tasks offers many benefits that make this a fundamentally different proposal to the existing Long Tasks API.</p>
<h3 id="will-this-replace-the-long-tasks-api" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/articles/long-animation-frames/#will-this-replace-the-long-tasks-api" aria-hidden="true">#</a> Will this replace the Long Tasks API?</h3>
<p>The Long Animation Frames API proposal is at the experimentation stage. We are inviting developers to try the API, and subsequently comment on it to test its usefulness. At this time, there are no plans to deprecate the Long Tasks API.</p>
<h2 id="feedback-wanted" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/articles/long-animation-frames/#feedback-wanted" aria-hidden="true">#</a> Feedback wanted</h2>
<p>The Chrome team is seeking feedback on the API shape to ensure it's easy to use and meets developers needs before we aim to standardize and release it for general use</p>
<p>Feedback can be provided at the <a href="https://github.com/w3c/longtasks/issues">Long Task GitHub Issues list</a>, or bugs in Chrome's implementation of the API can be filed in <a href="https://bugs.chromium.org/p/chromium/issues/entry?template=Defect&components=Blink%3EPerformanceAPIs">Chrome's issue tracker</a>.</p>
<h2 id="conclusion" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/articles/long-animation-frames/#conclusion" aria-hidden="true">#</a> Conclusion</h2>
<p>The Long Animation Frames API is an exciting proposal with many potential advantages over the existing Long Tasks API.</p>
<p>It could prove to be a key tool for addressing responsiveness issues as measured by INP. INP is a challenging metric to optimize and this API is one way the Chrome team is seeking to make identifying and addressing issues easier for developers.</p>
<p>The scope of the Long Animation Frames API extends beyond just INP though, and it can help identify other causes of slow updates which can affect the overall smoothness of a website's user experience.</p>
<h2 id="acknowledgements" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/articles/long-animation-frames/#acknowledgements" aria-hidden="true">#</a> Acknowledgements</h2>
<p><em>Hero image by <a href="https://unsplash.com/@henry_be">Henry Be</a> on <a href="https://unsplash.com/photos/MWP9cxS4uCg">Unsplash</a>.</em></p>
Deprecating the `unload` event2023-08-10T00:00:00Zhttps://developer.chrome.com/en/articles/deprecating-unload/Demián Renzullihttps://developer.chrome.com/authors/demianrenzulli/Barry Pollardhttps://developer.chrome.com/authors/tunetheweb/<p>The <a href="https://developer.mozilla.org/docs/Web/API/Window/unload_event"><code>unload</code> event</a> will be gradually <a href="https://chromestatus.com/feature/5579556305502208">deprecated</a> by gradually changing the default so that <code>unload</code> handlers stop firing on pages unless a page explicitly opts in to re-enable them.</p>
<h2 id="deprecation-timeline" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/articles/deprecating-unload/#deprecation-timeline" aria-hidden="true">#</a> Deprecation timeline</h2>
<div class="aside aside--update"><div class="aside__label gap-bottom-300"><svg width="24" height="24" viewBox="0 0 18 19" xmlns="http://www.w3.org/2000/svg">
<path d="M8 5.5V10.5L12.25 13.02L13.02 11.74L9.5 9.65V5.5H8ZM18 7.5V0.5L15.36 3.14C13.74 1.51 11.49 0.5 9 0.5C4.03 0.5 0 4.53 0 9.5C0 14.47 4.03 18.5 9 18.5C13.97 18.5 18 14.47 18 9.5H16C16 13.36 12.86 16.5 9 16.5C5.14 16.5 2 13.36 2 9.5C2 5.64 5.14 2.5 9 2.5C10.93 2.5 12.68 3.29 13.95 4.55L11 7.5H18Z" fill="currentColor"></path>
</svg>
<span>Update</span></div>
<p>This timeline has been updated as of 13th November 2023.</p>
</div>
<p>We noted that unload behavior would likely be subject to changes as early as January 2019, when we announced our <a href="https://groups.google.com/a/chromium.org/g/blink-dev/c/OVROmzNUng0/m/1gTmi-I3EQAJ">intent to implement a back/forward cache</a>. In parallel to the implementation work, we conducted a large outreach which resulted in a significant drop of <a href="https://chromestatus.com/metrics/feature/timeline/popularity/203">unload usage</a>. To complement this outreach, we also started to offer ways to test the effect of deprecating unload from Chrome 115:</p>
<ul>
<li>In the wild testing via the <a href="https://developer.chrome.com/en/articles/deprecating-unload/#permissions-policy">Permission-Policy API for unload</a> in Chrome 115 (July 2023)</li>
<li>Local testing by enabling a <a href="https://developer.chrome.com/en/articles/deprecating-unload/#chrome-flags-and-command-line-switches">flag</a> in Chrome 117 (September 2023)</li>
</ul>
<p>Following these outreach and trial phases, here is how we expect the <em>soft deprecation</em> to roll out:</p>
<ul>
<li>A scoped phase where unload will gradually cease to function for the top 50 popular sites (<a href="https://en.wikipedia.org/wiki/List_of_most-visited_websites">reference</a> as of the time of writing).
<ul>
<li>Starting with 1% of users from Chrome 120 (end of November 2023).</li>
<li>Ending with 100% of users by the end of Q3 2024</li>
</ul>
</li>
<li>In addition, from Q3 2024, we intend to start a generic phase where unload will gradually cease to function on any sites, starting with 1% of users and ending with 100% of users by the end of Q1 2025.</li>
</ul>
<p>Note that we also offer a <a href="https://developer.chrome.com/en/articles/deprecating-unload/#options-comparison">menu of opt-out options</a> in case this soft deprecation timeline doesn't provide sufficient time to migrate away from unload. Our goal is to use this <em>soft deprecation</em> to inform the timeline for the last phase (<em>hard deprecation of unload</em>) in which these opt-outs will be removed or reduced.</p>
<figure>
<img alt="Timeline of the unload deprecation." decoding="async" height="317" loading="lazy" sizes="(min-width: 800px) 800px, calc(100vw - 48px)" src="https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/RImjbA5lHjnw9HGDbDy7.png?auto=format" srcset="https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/RImjbA5lHjnw9HGDbDy7.png?auto=format&w=200 200w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/RImjbA5lHjnw9HGDbDy7.png?auto=format&w=228 228w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/RImjbA5lHjnw9HGDbDy7.png?auto=format&w=260 260w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/RImjbA5lHjnw9HGDbDy7.png?auto=format&w=296 296w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/RImjbA5lHjnw9HGDbDy7.png?auto=format&w=338 338w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/RImjbA5lHjnw9HGDbDy7.png?auto=format&w=385 385w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/RImjbA5lHjnw9HGDbDy7.png?auto=format&w=439 439w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/RImjbA5lHjnw9HGDbDy7.png?auto=format&w=500 500w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/RImjbA5lHjnw9HGDbDy7.png?auto=format&w=571 571w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/RImjbA5lHjnw9HGDbDy7.png?auto=format&w=650 650w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/RImjbA5lHjnw9HGDbDy7.png?auto=format&w=741 741w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/RImjbA5lHjnw9HGDbDy7.png?auto=format&w=845 845w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/RImjbA5lHjnw9HGDbDy7.png?auto=format&w=964 964w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/RImjbA5lHjnw9HGDbDy7.png?auto=format&w=1098 1098w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/RImjbA5lHjnw9HGDbDy7.png?auto=format&w=1252 1252w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/RImjbA5lHjnw9HGDbDy7.png?auto=format&w=1428 1428w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/RImjbA5lHjnw9HGDbDy7.png?auto=format&w=1600 1600w" width="800" />
</figure>
<h2 id="background" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/articles/deprecating-unload/#background" aria-hidden="true">#</a> Background</h2>
<p><code>unload</code> was designed to fire when the document is being unloaded. In theory, it can be used to run code any time a user is navigating away from a page, or as an end of session callback.</p>
<p>Scenarios where this event was most commonly used include:</p>
<ul>
<li><strong>Saving user data</strong>: Save data before leaving the page.</li>
<li><strong>Performing cleanup tasks</strong>: Closing open resources before abandoning the page.</li>
<li><strong>Sending analytics</strong>: Sending data related to user interactions at the end of the session.</li>
</ul>
<p>However the <code>unload</code> event <a href="https://developer.chrome.com/articles/page-lifecycle-api/#the-unload-event">is extremely unreliable</a>.</p>
<p>On desktop Chrome and Firefox, <code>unload</code> is reasonably reliable but it has a negative impact on a site's performance by preventing the usage of <a href="https://web.dev/articles/bfcache#never_use_the_unload_event">bfcache (back/forward cache)</a>.</p>
<p>On mobile browsers <code>unload</code> often doesn't run as tabs are frequently backgrounded and then killed. For this reason browsers choose to prioritize the bfcache on mobile over <code>unload</code>, making them even more unreliable. Safari also uses this behaviour on desktop.</p>
<p>The Chrome team believe using the mobile model of prioritizing bfcache over <code>unload</code> on desktop <a href="https://github.com/fergald/docs/blob/master/explainers/permissions-policy-deprecate-unload.md#unload-as-specced-is-a-footgun">would be disruptive</a> by making it more unreliable there too, when previously this has been reasonably reliable in Chrome (and Firefox). Instead, Chrome's aim is to remove the <code>unload</code> event completely. Until then it will remain reliable on desktop for those who have explicitly opted-out of the deprecation.</p>
<h2 id="why-deprecate-the-unload-event" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/articles/deprecating-unload/#why-deprecate-the-unload-event" aria-hidden="true">#</a> Why deprecate the <code>unload</code> event?</h2>
<p>Deprecating <code>unload</code> is a key step in a much bigger recognition of the web we live in now. The <code>unload</code> event gives a false sense of control of the app lifecycle that is increasingly untrue of how we browse the web in the modern computing world.</p>
<p>Mobile operating systems frequently freeze or unload web pages to conserve memory and desktop browsers are doing this more and more now too for the same reasons. Even without operating system interventions, users themselves frequently tab switch and kill old tabs without formally "leaving pages".</p>
<p>Removing the <code>unload</code> event as obselete is a recognition that we as web developers need to ensure our paradigm matches that of the real world and not depend on outdated concepts that no longer hold true—if they ever did.</p>
<h2 id="alternatives-to-unload-events" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/articles/deprecating-unload/#alternatives-to-unload-events" aria-hidden="true">#</a> Alternatives to <code>unload</code> events</h2>
<p>Instead of <code>unload</code> it is recommended to use:</p>
<ul>
<li><a href="https://developer.chrome.com/articles/page-lifecycle-api/#event-visibilitychange"><code>visibilitychange</code></a>: To determine when the visibility of a page changes. This event happens when the user switches tabs, minimizes the browser window, or opens a new page. Consider the <a href="https://developer.chrome.com/articles/page-lifecycle-api/#advice-hidden"><code>hidden</code> state</a> the last reliable time to save app and user data.</li>
<li><a href="https://developer.chrome.com/articles/page-lifecycle-api/#event-pagehide"><code>pagehide</code></a>: To determine when the user has navigated away from the page. This event happens when the user navigates away from the page, reloads the page, or closes the browser window. The <code>pagehide</code> event is not fired when the page is simply minimized or switched to another tab. Note that, as <code>pagehide</code> does not make a page ineligible for the back/forward cache, it is possible a page can be restored after this event fires. If you're cleaning up any resources in this event, then they may have to be restored on page restore.</li>
</ul>
<p>The <a href="https://developer.mozilla.org/docs/Web/API/Window/beforeunload_event"><code>beforeunload</code></a> event has a slightly different use case to <code>unload</code> in that it is a cancellable event. It is often used to warn users of unsaved changes when navigating away. This event is also unrealiable as it will not fire if a background tab is killed. It is recommended to limit use of <code>beforeunload</code> and <a href="https://developer.chrome.com/blog/page-lifecycle-api/#the-beforeunload-event">only add it conditionally</a>. Instead, use the above events for most <code>unload</code> replacements.</p>
<p>For more details, see <a href="https://developer.chrome.com/articles/page-lifecycle-api/#the-unload-event">this advice on never using the <code>unload</code> handler</a>.</p>
<h2 id="detect-usage-of-unload" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/articles/deprecating-unload/#detect-usage-of-unload" aria-hidden="true">#</a> Detect usage of <code>unload</code></h2>
<p>There are different tools to help you find appearances of the <code>unload</code> event on pages. This allows sites to discover if they are using this event—either in their own code, or via libraries—and so may be affected by the upcoming deprecation.</p>
<h3 id="lighthouse" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/articles/deprecating-unload/#lighthouse" aria-hidden="true">#</a> Lighthouse</h3>
<p><a href="https://developer.chrome.com/docs/lighthouse/">Lighthouse</a> has a <a href="https://github.com/GoogleChrome/lighthouse/pull/11085"><code>no-unload-listeners</code> audit</a>, which warns developers if any JavaScript on their pages (including that from third-party libraries) adds an <code>unload</code> event listener.</p>
<figure>
<img alt="Lighthouse audit showing unload handlers in use" decoding="async" height="304" loading="lazy" sizes="(min-width: 800px) 800px, calc(100vw - 48px)" src="https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/0IbgnaeYF2zHOfhwdVcc.png?auto=format" srcset="https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/0IbgnaeYF2zHOfhwdVcc.png?auto=format&w=200 200w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/0IbgnaeYF2zHOfhwdVcc.png?auto=format&w=228 228w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/0IbgnaeYF2zHOfhwdVcc.png?auto=format&w=260 260w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/0IbgnaeYF2zHOfhwdVcc.png?auto=format&w=296 296w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/0IbgnaeYF2zHOfhwdVcc.png?auto=format&w=338 338w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/0IbgnaeYF2zHOfhwdVcc.png?auto=format&w=385 385w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/0IbgnaeYF2zHOfhwdVcc.png?auto=format&w=439 439w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/0IbgnaeYF2zHOfhwdVcc.png?auto=format&w=500 500w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/0IbgnaeYF2zHOfhwdVcc.png?auto=format&w=571 571w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/0IbgnaeYF2zHOfhwdVcc.png?auto=format&w=650 650w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/0IbgnaeYF2zHOfhwdVcc.png?auto=format&w=741 741w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/0IbgnaeYF2zHOfhwdVcc.png?auto=format&w=845 845w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/0IbgnaeYF2zHOfhwdVcc.png?auto=format&w=964 964w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/0IbgnaeYF2zHOfhwdVcc.png?auto=format&w=1098 1098w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/0IbgnaeYF2zHOfhwdVcc.png?auto=format&w=1252 1252w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/0IbgnaeYF2zHOfhwdVcc.png?auto=format&w=1428 1428w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/0IbgnaeYF2zHOfhwdVcc.png?auto=format&w=1600 1600w" width="800" />
</figure>
<h3 id="chrome-devtools" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/articles/deprecating-unload/#chrome-devtools" aria-hidden="true">#</a> Chrome DevTools</h3>
<p><a href="https://developer.chrome.com/docs/devtools/">Chrome DevTools</a> includes a <a href="https://developer.chrome.com/docs/devtools/application/back-forward-cache/"><code>back-foward-cache</code> audit</a> to help you identify issues that may prevent your page from being eligible for back/forward cache, including the usage of the <code>unload</code> handler.</p>
<p>To test back/forward cache, follow these steps:</p>
<ol>
<li>
<p>On your page, <a href="https://developer.chrome.com/docs/devtools/open/">open DevTools</a>, then navigate to <strong>Application</strong> > <strong>Background services</strong> > <strong>Back/forward cache</strong>.</p>
</li>
<li>
<p>Click <strong>Test back/forward cache</strong> Chrome automatically takes you to <code>chrome://terms/</code> and back to your page. Alternatively, you can click the browser's back and forward buttons.</p>
</li>
</ol>
<p>If your page isn't eligible for back/forward caching, the <strong>Back/forward cache</strong> tab shows you a list of issues. Under <strong>Actionable</strong>, you can see if you are using <code>unload</code>:</p>
<figure>
<img alt="Chrome DevTools Back/forward cache testing tool showing an unload handler was used" decoding="async" height="422" loading="lazy" sizes="(min-width: 800px) 800px, calc(100vw - 48px)" src="https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/fY1MHKVLYCr5wcRcnyw3.png?auto=format" srcset="https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/fY1MHKVLYCr5wcRcnyw3.png?auto=format&w=200 200w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/fY1MHKVLYCr5wcRcnyw3.png?auto=format&w=228 228w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/fY1MHKVLYCr5wcRcnyw3.png?auto=format&w=260 260w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/fY1MHKVLYCr5wcRcnyw3.png?auto=format&w=296 296w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/fY1MHKVLYCr5wcRcnyw3.png?auto=format&w=338 338w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/fY1MHKVLYCr5wcRcnyw3.png?auto=format&w=385 385w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/fY1MHKVLYCr5wcRcnyw3.png?auto=format&w=439 439w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/fY1MHKVLYCr5wcRcnyw3.png?auto=format&w=500 500w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/fY1MHKVLYCr5wcRcnyw3.png?auto=format&w=571 571w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/fY1MHKVLYCr5wcRcnyw3.png?auto=format&w=650 650w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/fY1MHKVLYCr5wcRcnyw3.png?auto=format&w=741 741w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/fY1MHKVLYCr5wcRcnyw3.png?auto=format&w=845 845w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/fY1MHKVLYCr5wcRcnyw3.png?auto=format&w=964 964w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/fY1MHKVLYCr5wcRcnyw3.png?auto=format&w=1098 1098w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/fY1MHKVLYCr5wcRcnyw3.png?auto=format&w=1252 1252w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/fY1MHKVLYCr5wcRcnyw3.png?auto=format&w=1428 1428w, https://wd.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/fY1MHKVLYCr5wcRcnyw3.png?auto=format&w=1600 1600w" width="800" />
</figure>
<h3 id="reporting-api" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/articles/deprecating-unload/#reporting-api" aria-hidden="true">#</a> Reporting API</h3>
<p>The <a href="https://www.w3.org/TR/reporting-1/">Reporting API</a> can be used to in conjuction with a read-only Permission Policy to detect usage of <code>unload</code> from your website users.</p>
<p>For more details see <a href="https://github.com/fergald/docs/blob/master/explainers/permissions-policy-deprecate-unload.md#using-reportingapi-to-find-unloads">usUsing Reporting API to find unloads</a></p>
<h3 id="bfcache-notrestoredreasons-api" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/articles/deprecating-unload/#bfcache-notrestoredreasons-api" aria-hidden="true">#</a> Bfcache <code>notRestoredReasons</code> API</h3>
<p>The <a href="https://developer.chrome.com/docs/web-platform/bfcache-notrestoredreasons/"><code>notRestoredReasons</code> property</a>—added to the <a href="https://developer.mozilla.org/docs/Web/API/PerformanceNavigationTiming"><code>PerformanceNavigationTiming</code></a> class—reports information on whether documents were blocked from using the <a href="https://web.dev/articles/bfcache">bfcache</a> on navigation, and why. Usage instructions can be found <a href="https://developer.chrome.com/docs/web-platform/bfcache-notrestoredreasons/">here</a>. This is an example of how the response object warning of an existing <code>unload</code> listener looks like:</p>
<pre class="language-js"><code class="language-js"><span class="token punctuation">{</span><br /> <span class="token literal-property property">blocked</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">children</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">id</span><span class="token operator">:</span> <span class="token string">""</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">""</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">reasons</span><span class="token operator">:</span> <span class="token punctuation">[</span> <span class="token string">"Internal Error"</span><span class="token punctuation">,</span> <span class="token string">"Unload handler"</span> <span class="token punctuation">]</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">src</span><span class="token operator">:</span> <span class="token string">""</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">url</span><span class="token operator">:</span> <span class="token string">"a.com"</span><br /><span class="token punctuation">}</span></code></pre>
<h2 id="control-access-to-unload" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/articles/deprecating-unload/#control-access-to-unload" aria-hidden="true">#</a> Control access to <code>unload</code></h2>
<p>Chrome will deprecate the <code>unload</code> event gradually. In the meantime, you can use different tools to control this behavior and prepare for the upcoming deprecation. Keep in mind that you should not rely on these techniques in the long term, and you should plan to migrate to the alternatives instead as soon as possible.</p>
<p>The following options allow you to enable or disable <code>unload</code> handlers to test how your site would work without them so you can prepare for the upcoming deprecation. There are different types of policies:</p>
<ul>
<li><a href="https://github.com/w3c/webappsec-permissions-policy/blob/main/permissions-policy-explainer.md">Permissions Policy</a>: This is a platform API for site owners to control access to features, at a site or an individual page level, via the usage of HTTP headers.</li>
<li><a href="https://chromeenterprise.google/policies/">Enterprise policies</a>: Tools for IT admins to configure Chrome for their organization or business. They can be configured via an admin panel, like the <a href="https://support.google.com/a/answer/182076?hl=en">Google Admin Console</a>.</li>
<li><a href="https://developer.chrome.com/docs/web-platform/chrome-flags/">Chrome flags</a>: This allows an individual developer to change the <code>unload</code> deprecation setting to test impact on various sites.</li>
</ul>
<h3 id="permissions-policy" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/articles/deprecating-unload/#permissions-policy" aria-hidden="true">#</a> Permissions Policy</h3>
<p>A Permissions Policy <a href="https://chromestatus.com/feature/5760325231050752">has been added from Chrome 115</a> to allow sites to opt-out of using <code>unload</code> handlers and immediately benefit from the bfcache to improve site performance. See <a href="https://github.com/fergald/docs/blob/master/explainers/permissions-policy-unload.md#examples">these examples on how to set this for your site</a>. This allows sites to get ahead of the <code>unload</code> deprecation.</p>
<p>This <a href="https://chromestatus.com/feature/5579556305502208">will be extended in Chrome 117</a> to allow sites to do the reverse, and to opt-in to continuing to try to fire <code>unload</code> handlers, as Chrome changes the default for these to not fire in future. See <a href="https://github.com/fergald/docs/blob/master/explainers/permissions-policy-deprecate-unload.md#reenabling-unload-for-a-frame">these examples on how to continue to allow unload handlers to fire for your site</a>. This opt-in will not remain forever and should be used to allow time for sites to migrate away from <code>unload</code> handlers.</p>
<h3 id="enterprise-policy" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/articles/deprecating-unload/#enterprise-policy" aria-hidden="true">#</a> Enterprise policy</h3>
<p>Enterprises that have software that depends on the <code>unload</code> event to function correctly can use the <a href="https://chromium-review.googlesource.com/c/chromium/src/+/4730081"><code>ForcePermissionPolicyUnloadDefaultEnabled</code> policy</a> to prevent the gradual deprecation for devices under their control. By enabling this policy, <code>unload</code> will continue to default to enabled for all origins. A page may still set a stricter policy if it wants. Like the Permissions Policy opt-out, this is a tool to mitigate potential breaking changes, but it should not be used indefinitely.</p>
<h3 id="chrome-flags-and-command-line-switches" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/articles/deprecating-unload/#chrome-flags-and-command-line-switches" aria-hidden="true">#</a> Chrome flags and command line switches</h3>
<p>As well as the enterprise policy, you can disable the deprecation for individual users via the Chrome flags and command lines swtiches:</p>
<p>Setting <code>chrome://flags/#deprecate-unload</code> this to <code>enabled</code> will bring forward the deprecation default and prevent <code>unload</code> handlers from firing. They can still be overridden on a site-by-site basis via Permissions Policy, but will continue to fire by default.</p>
<p>These settings can be also be controlled by <a href="https://github.com/fergald/docs/blob/master/explainers/permissions-policy-deprecate-unload.md#testing-with-chrome">command line switches</a>.</p>
<h3 id="options-comparison" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/articles/deprecating-unload/#options-comparison" aria-hidden="true">#</a> Options comparison</h3>
<p>The following table summarizes the different uses of the options discussed previously:</p>
<table>
<thead>
<tr>
<td style="min-width: 250px;"></td>
<th>Bring deprecation forward</th>
<th>Bring deprecation forward (with exceptions)</th>
<th>Prevent deprecation to secure time for a migration</th>
</tr>
</thead>
<tbody>
<tr>
<td>Permissions Policy<br /><em>(applies to pages/sites)</em></td>
<td style="text-align: center;">Yes</td>
<td style="text-align: center;">Yes</td>
<td style="text-align: center;">Yes</td>
</tr>
<tr>
<td>Enterprise policy<br /><em>(applies to devices)</em></td>
<td style="text-align: center;">No</td>
<td style="text-align: center;">No</td>
<td style="text-align: center;">Yes</td>
</tr>
<tr>
<td>Chrome flags<br /><em>(applies to individual users)</em></td>
<td style="text-align: center;">Yes</td>
<td style="text-align: center;">No</td>
<td style="text-align: center;">No</td>
</tr>
<tr>
<td>Chrome command line switches<br /><em>(applies to individual users)</em></td>
<td style="text-align: center;">Yes</td>
<td style="text-align: center;">No</td>
<td style="text-align: center;">Yes</td>
</tr>
</tbody>
</table>
<h2 id="conclusion" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/articles/deprecating-unload/#conclusion" aria-hidden="true">#</a> Conclusion</h2>
<p><code>unload</code> handlers are being deprecated. They have been unreliable for a long time and are not guaranteed to be fired on all cases where a document gets destroyed. Additionally, <code>unload</code> handlers are incompatible with <a href="https://web.dev/articles/bfcache">bfcache</a>.</p>
<p>Sites that currently make use of <code>unload</code> handlers should prepare for this upcoming deprecation by testing for any existing <code>unload</code> handlers, removing or migrating them or, as a last resort, delaying the deprecation if more time is needed.</p>
<h2 id="acknowledgements" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/articles/deprecating-unload/#acknowledgements" aria-hidden="true">#</a> Acknowledgements</h2>
<p><em>Thanks to Kenji Baheux, Fergal Daly, Adriana Jara, and Jeremy Wagner for help reviewing this article.</em></p>
<p><em>Hero image by <a href="https://unsplash.com/@anja_hb">Anja Bauermann</a> on <a href="https://unsplash.com/photos/D1LnfycCHks">Unsplash</a></em></p>
Secure popup interactions with `restrict-properties`2023-08-09T00:00:00Zhttps://developer.chrome.com/en/blog/coop-restrict-properties/Arthur Hemeryhttps://developer.chrome.com/authors/hemeryar/Maud Nalpashttps://developer.chrome.com/authors/maudn/<p>A new value for
<a href="https://developer.mozilla.org/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy">Cross-Origin Opener Policy (COOP)</a>
is available: <code>restrict-properties</code>. It brings in security benefits and makes
it easier to adopt <a href="https://web.dev/articles/coop-coep">cross-origin isolation</a> while
allowing your site to interact with third-party popups for payments,
authentication, or other use cases.</p>
<p>To start experimenting with <code>restrict-properties</code> participate in the <a href="https://developer.chrome.com/en/blog/coop-restrict-properties/#origin-trial">origin
trial</a> starting in <a href="https://chromiumdash.appspot.com/schedule">Chrome 116</a>.</p>
<h2 id="why-use-restrict-properties" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/coop-restrict-properties/#why-use-restrict-properties" aria-hidden="true">#</a> Why use <code>restrict-properties</code></h2>
<p><code>restrict-properties</code> has two main use cases:</p>
<ul>
<li>Preventing <a href="https://xsleaks.dev/">cross-site leaks</a> without breakage.</li>
<li>Making your site <a href="https://web.dev/articles/why-coop-coep">cross-origin isolated</a>.</li>
</ul>
<h3 id="prevent-cross-site-leaks-without-breakage" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/coop-restrict-properties/#prevent-cross-site-leaks-without-breakage" aria-hidden="true">#</a> Prevent cross-site leaks without breakage</h3>
<p>By default, any website can open your application in a popup and get a
reference to it.</p>
<p>A malicious website can use this to their advantage to perform attacks such as
<a href="https://xsleaks.dev/">cross-site leaks</a>.
To mitigate this risk, you can use the <code>Cross-Origin-Opener-Policy</code> (COOP) header.</p>
<p>Up until now, your options for <code>Cross-Origin-Opener-Policy</code> were limited. You
could either:</p>
<ul>
<li>Set <code>same-origin,</code> which blocks all cross-origin interactions with popups.</li>
<li>Set <code>same-origin-allow-popups</code>, which blocks all cross-origin interactions
that open your site in a popup.</li>
<li>Set <code>unsafe-none</code>, which allows all cross-origin interactions with popups.</li>
</ul>
<p>This made it impossible for websites that need to be opened in a popup and to
interact with their opener to enforce COOP. This left key use cases like single
sign-on and payments unprotected from cross-site leaks.</p>
<p><code>Cross-Origin-Opener-Policy: restrict-properties</code> solves this.</p>
<p>With <code>restrict-properties</code>, properties that can be used for frame counting and
other cross-site leaks attacks are not available—but basic communication between
windows via <code>postMessage</code> and <code>closed</code> is allowed.</p>
<p>This improves a site's security while maintaining key use cases. For example:</p>
<ul>
<li>If you provide a service in a popup, setting <code>Cross-Origin-Opener-Policy: restrict-properties</code> will protect yourself against a range of cross-site leaks attacks.
You can still open all pages that you could previously open.</li>
<li>If you need to access a cross-origin popup, setting
<code>Cross-Origin-Opener-Policy: restrict-properties</code> will similarly protect
your site from iframe counting. You will be able to open the same set of
popups that you can open today.</li>
<li>If both the opener and openee set the header, and the pages are cross-origin, it behaves similarly
to one of them having set the header. If they are same-origin, full access is
granted.</li>
</ul>
<h3 id="make-your-site-cross-origin-isolated" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/coop-restrict-properties/#make-your-site-cross-origin-isolated" aria-hidden="true">#</a> Make your site cross-origin isolated</h3>
<h4 id="why-we-need-cross-origin-isolation" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/coop-restrict-properties/#why-we-need-cross-origin-isolation" aria-hidden="true">#</a> Why we need cross-origin isolation</h4>
<p>Some web APIs increase the risk of side-channel attacks like
<a href="https://en.wikipedia.org/wiki/Spectre_(security_vulnerability)">Spectre</a>. To
mitigate that risk, browsers offer an opt-in-based isolated environment called
<a href="https://web.dev/articles/coop-coep">cross-origin isolation</a>. With a cross-origin
isolated state, the webpage can use privileged features including
<a href="https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer">SharedArrayBuffer</a>,
<a href="https://web.dev/articles/monitor-total-page-memory-usage">performance.measureUserAgentSpecificMemory()</a>
and
<a href="https://developer.chrome.com/blog/cross-origin-isolated-hr-timers/">high-precision timers</a>
with better resolution, while isolating the origin from others unless they are
opted in.</p>
<p>Up until now, to use these APIs, you had to set <code>Cross-Origin-Opener-Policy: same-origin</code>. However, this would break any cross-origin popup flow you might
need, such as single sign-on and Payments.</p>
<p><code>Cross-Origin-Opener-Policy: restrict-properties</code> can now be used instead of
<code>Cross-Origin-Opener-Policy: same-origin</code> to enable cross-origin isolation.
Instead of severing the opener relationship, it merely restricts it to the
minimal communication subset of <code>window.postMessage()</code> and <code>window.closed</code>.</p>
<p>You will be able to enable cross-origin isolation with the following two
headers:</p>
<pre class="language-http"><code class="language-http"><span class="token header"><span class="token header-name keyword">Cross-Origin-Opener-Policy</span><span class="token punctuation">:</span> <span class="token header-value">restrict-properties</span></span><br /><span class="token header"><span class="token header-name keyword">Cross-Origin-Embedder-Policy</span><span class="token punctuation">:</span> <span class="token header-value">require-corp</span></span></code></pre>
<p>or</p>
<pre class="language-http"><code class="language-http"><span class="token header"><span class="token header-name keyword">Cross-Origin-Opener-Policy</span><span class="token punctuation">:</span> <span class="token header-value">restrict-properties</span></span><br /><span class="token header"><span class="token header-name keyword">Cross-Origin-Embedder-Policy</span><span class="token punctuation">:</span> <span class="token header-value">credentialless</span></span></code></pre>
<p>Learn more about <code>credentialless</code> at
<a href="https://developer.chrome.com/blog/coep-credentialless-origin-trial/">Load cross-origin resources without CORP headers using <code>COEP: credentialless</code></a>.</p>
<h2 id="demo" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/coop-restrict-properties/#demo" aria-hidden="true">#</a> Demo</h2>
<p>Try various header options in this
<a href="https://cross-origin-isolation.glitch.me/">cross-origin isolation demo</a>.</p>
<h2 id="origin-trial" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/coop-restrict-properties/#origin-trial" aria-hidden="true">#</a> Experiment with the origin trial</h2>
<p>To experiment with <code>Cross-Origin-Opener-Policy: restrict-properties</code>, opt
into the
<a href="https://developer.chrome.com/origintrials/#/view_trial/1827335548805578753">origin trial</a>.</p>
<div class="aside aside--note">
<p>Review
<a href="https://developer.chrome.com/docs/web-platform/origin-trials/#take-part-in-an-origin-trial">instructions</a>
on how to participate in an origin trial.</p>
</div>
<h2 id="browser-support" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/coop-restrict-properties/#browser-support" aria-hidden="true">#</a> Browser support</h2>
<p><code>Cross-Origin-Opener-Policy: restrict-properties</code> is currently only supported
in Chrome. Other browsers are
<a href="https://github.com/whatwg/html/issues/6364">actively engaged in the discussion for standardization</a>.</p>
<h2 id="faq" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/coop-restrict-properties/#faq" aria-hidden="true">#</a> FAQ</h2>
<h3 id="my-website-needs-to-communicate-with-same-origin-popups-should-i-use-coop-restrict-properties-to-enable-cross-origin-isolation" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/coop-restrict-properties/#my-website-needs-to-communicate-with-same-origin-popups-should-i-use-coop-restrict-properties-to-enable-cross-origin-isolation" aria-hidden="true">#</a> My website needs to communicate with same-origin popups, should I use <code>COOP: restrict-properties</code> to enable cross-origin isolation?</h3>
<p>Setting <code>COOP: restrict-properties</code> on both the popup and your main page will
not cause restrictions. Setting it either only on the popup or only on the main
page will prevent any access to properties other than <code>postMessage</code> and <code>closed</code>
across the opener, even if they are same-origin.</p>
<h3 id="is-the-set-of-allowed-properties-fixed" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/coop-restrict-properties/#is-the-set-of-allowed-properties-fixed" aria-hidden="true">#</a> Is the set of allowed properties fixed?</h3>
<p>Based on the feedback so far, <code>window.postMessage</code> and <code>window.closed</code> are suspected
to be enough for the majority of workflows, but we're still
considering opening it to other properties. If you have a use case that cannot
be solved using only <code>postMessage</code> and <code>closed</code> leave your feedback
<a href="https://groups.google.com/a/chromium.org/g/blink-dev/c/JBTWXSHE8M0/m/fP4eXvFzAAAJ">on the Intent to Experiment thread</a>.</p>
<h2 id="resources" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/coop-restrict-properties/#resources" aria-hidden="true">#</a> Resources</h2>
<ul>
<li><a href="https://web.dev/articles/coop-coep">Making your website "cross-origin isolated" using COOP and COEP</a></li>
<li><a href="https://web.dev/articles/why-coop-coep">Why you need "cross-origin isolated" for powerful features</a></li>
<li><a href="https://web.dev/articles/cross-origin-isolation-guide">A guide to enable cross-origin isolation</a></li>
<li><a href="https://developer.chrome.com/blog/enabling-shared-array-buffer/">SharedArrayBuffer updates in Android Chrome 88 and Desktop Chrome 92</a></li>
<li><a href="https://developer.chrome.com/blog/coep-credentialless-origin-trial/">Load cross-origin resources without CORP headers using <code>COEP: credentialless</code> - Chrome Developers</a></li>
<li><a href="https://developer.chrome.com/blog/anonymous-iframe-origin-trial/">Anonymous iframe origin trial: Easily embed iframes in COEP environments - Chrome Developers</a></li>
</ul>
FedCM updates: IdP Sign-In Status API, Login Hint, and more2023-07-21T00:00:00Zhttps://developer.chrome.com/en/blog/fedcm-chrome-116-updates/Eiji Kitamurahttps://developer.chrome.com/authors/agektmr/<p>In Chrome 116, Chrome is shipping the following three new <a href="https://developer.chrome.com/docs/privacy-sandbox/fedcm/">Federated Credential
Management (FedCM)</a>
features:</p>
<ul>
<li><strong><a href="https://developer.chrome.com/en/blog/fedcm-chrome-116-updates/#login-hint">Login Hint API</a></strong>: Specify a preferred user account to be
signed in.</li>
<li><strong><a href="https://developer.chrome.com/en/blog/fedcm-chrome-116-updates/#user-info">User Info API</a></strong>: Fetch the information of the returning user
so that the identity provider (IdP) can render a personalized sign-in button
within an iframe.</li>
<li><strong><a href="https://developer.chrome.com/en/blog/fedcm-chrome-116-updates/#rp-context">RP Context API</a></strong>: Use a title different from 'Sign in' in
the FedCM dialog.</li>
</ul>
<p>Additionally, Chrome is starting an <a href="https://developer.chrome.com/docs/web-platform/origin-trials/">origin
trial</a> for the <strong><a href="https://developer.chrome.com/en/blog/fedcm-chrome-116-updates/#idp-signin-status">IdP Sign-In Status
API</a></strong>. The IdP Sign-in Status API is a requirement and will be a
breaking change when it's shipped. If you have an existing implementation of
FedCM, make sure to participate in the origin trial.</p>
<h2 id="login-hint" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/fedcm-chrome-116-updates/#login-hint" aria-hidden="true">#</a> Login Hint API</h2>
<p>When FedCM is invoked, the browser shows the signed-in account from the
specified identity provider (IdP). When the IdP supports multiple accounts, it
lists all signed-in accounts.</p>
<figure class="float-right">
<img alt="A FedCM dialog showing multiple user accounts." decoding="async" height="470" loading="lazy" sizes="(min-width: 794px) 794px, calc(100vw - 48px)" src="https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/sNV371fMQRQM0mr8SEl5.png?auto=format" srcset="https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/sNV371fMQRQM0mr8SEl5.png?auto=format&w=200 200w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/sNV371fMQRQM0mr8SEl5.png?auto=format&w=228 228w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/sNV371fMQRQM0mr8SEl5.png?auto=format&w=260 260w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/sNV371fMQRQM0mr8SEl5.png?auto=format&w=296 296w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/sNV371fMQRQM0mr8SEl5.png?auto=format&w=338 338w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/sNV371fMQRQM0mr8SEl5.png?auto=format&w=385 385w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/sNV371fMQRQM0mr8SEl5.png?auto=format&w=439 439w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/sNV371fMQRQM0mr8SEl5.png?auto=format&w=500 500w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/sNV371fMQRQM0mr8SEl5.png?auto=format&w=571 571w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/sNV371fMQRQM0mr8SEl5.png?auto=format&w=650 650w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/sNV371fMQRQM0mr8SEl5.png?auto=format&w=741 741w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/sNV371fMQRQM0mr8SEl5.png?auto=format&w=845 845w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/sNV371fMQRQM0mr8SEl5.png?auto=format&w=964 964w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/sNV371fMQRQM0mr8SEl5.png?auto=format&w=1098 1098w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/sNV371fMQRQM0mr8SEl5.png?auto=format&w=1252 1252w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/sNV371fMQRQM0mr8SEl5.png?auto=format&w=1428 1428w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/sNV371fMQRQM0mr8SEl5.png?auto=format&w=1588 1588w" width="794" />
<figcaption>A FedCM dialog showing multiple user accounts</figcaption>
</figure>
<p>After the user signs in, sometimes the relying party (RP) asks the user to
reauthenticate. But the user may not be sure which account they've been using.
If the RP can specify which account to sign in with, it would be easier for the
user to pick an account. <a href="https://fedidcg.github.io/FedCM/#dom-identityproviderconfig-loginhint">Login
hint</a> is
shipping in Chrome 116 and with it, the RP can narrow the list down to one.</p>
<p>This extension adds an array of <code>login_hints</code> in the <a href="https://developer.chrome.com/docs/privacy-sandbox/fedcm-developer-guide/#accounts-list-endpoint">accounts list
endpoint</a> response from the
IdP, with all possible filter types that the IdP supports. For example, the
accounts response could look like this when an IdP supports filtering by email
and id:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span><br /> <span class="token property">"accounts"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span><br /> <span class="token property">"id"</span><span class="token operator">:</span> <span class="token string">"demo1"</span><span class="token punctuation">,</span><br /> <span class="token property">"email"</span><span class="token operator">:</span> <span class="token string">"demo1@example.com"</span><span class="token punctuation">,</span><br /> <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"John Doe"</span><span class="token punctuation">,</span><br /> <span class="token property">"login_hints"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">"demo1"</span><span class="token punctuation">,</span> <span class="token string">"demo1@example.com"</span><span class="token punctuation">]</span><span class="token punctuation">,</span><br /> ...<br /> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><br /> <span class="token property">"id"</span><span class="token operator">:</span> <span class="token string">"demo2"</span><span class="token punctuation">,</span><br /> <span class="token property">"email"</span><span class="token operator">:</span> <span class="token string">"demo2@example.com"</span><span class="token punctuation">,</span><br /> <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"Jane Doe"</span><span class="token punctuation">,</span><br /> <span class="token property">"login_hints"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">"demo2"</span><span class="token punctuation">,</span> <span class="token string">"demo2@example.com"</span><span class="token punctuation">]</span><span class="token punctuation">,</span><br /> ...<br /> <span class="token punctuation">}</span><span class="token punctuation">,</span> ...<span class="token punctuation">]</span><br /><span class="token punctuation">}</span></code></pre>
<p>By passing <code>login_hints</code> in the accounts list, the RP can invoke
<code>navigator.credentials.get()</code> with the <code>loginHint</code> property as shown in the
following code sample to selectively show the specified account:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">return</span> <span class="token keyword">await</span> navigator<span class="token punctuation">.</span>credentials<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br /> <span class="token literal-property property">identity</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">providers</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span><br /> <span class="token literal-property property">configURL</span><span class="token operator">:</span> <span class="token string">"https://idp.example/manifest.json"</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">clientId</span><span class="token operator">:</span> <span class="token string">"123"</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">nonce</span><span class="token operator">:</span> nonce<span class="token punctuation">,</span><br /> <span class="token literal-property property">loginHint</span> <span class="token operator">:</span> <span class="token string">"demo1@example.com"</span><br /> <span class="token punctuation">}</span><span class="token punctuation">]</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<h2 id="user-info" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/fedcm-chrome-116-updates/#user-info" aria-hidden="true">#</a> User Info API</h2>
<p>Sign-in buttons decorated with the IdP's logo that let users sign in with
identity federation is now common. However, decorating the button using the
user's profile icon and their information is even more intuitive to sign in
with, especially when a user has signed up on this website with the IdP before.</p>
<div class="columns">
<div class="columns__column">
<figure>
<img alt="Sign-in with Google button." decoding="async" height="118" loading="lazy" sizes="(min-width: 414px) 414px, calc(100vw - 48px)" src="https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/LK2JpVpbo7kuh3uq0ZIc.png?auto=format" srcset="https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/LK2JpVpbo7kuh3uq0ZIc.png?auto=format&w=200 200w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/LK2JpVpbo7kuh3uq0ZIc.png?auto=format&w=228 228w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/LK2JpVpbo7kuh3uq0ZIc.png?auto=format&w=260 260w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/LK2JpVpbo7kuh3uq0ZIc.png?auto=format&w=296 296w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/LK2JpVpbo7kuh3uq0ZIc.png?auto=format&w=338 338w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/LK2JpVpbo7kuh3uq0ZIc.png?auto=format&w=385 385w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/LK2JpVpbo7kuh3uq0ZIc.png?auto=format&w=439 439w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/LK2JpVpbo7kuh3uq0ZIc.png?auto=format&w=500 500w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/LK2JpVpbo7kuh3uq0ZIc.png?auto=format&w=571 571w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/LK2JpVpbo7kuh3uq0ZIc.png?auto=format&w=650 650w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/LK2JpVpbo7kuh3uq0ZIc.png?auto=format&w=741 741w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/LK2JpVpbo7kuh3uq0ZIc.png?auto=format&w=828 828w" width="414" />
<figcaption>Sign-in with Google button</figcaption>
</figure>
</div>
<div class="columns__column">
<figure>
<img alt="Personalized Sign-in with Google button." decoding="async" height="121" loading="lazy" sizes="(min-width: 493px) 493px, calc(100vw - 48px)" src="https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/fDpZXGBZneIqSzUUSCU9.png?auto=format" srcset="https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/fDpZXGBZneIqSzUUSCU9.png?auto=format&w=200 200w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/fDpZXGBZneIqSzUUSCU9.png?auto=format&w=228 228w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/fDpZXGBZneIqSzUUSCU9.png?auto=format&w=260 260w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/fDpZXGBZneIqSzUUSCU9.png?auto=format&w=296 296w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/fDpZXGBZneIqSzUUSCU9.png?auto=format&w=338 338w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/fDpZXGBZneIqSzUUSCU9.png?auto=format&w=385 385w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/fDpZXGBZneIqSzUUSCU9.png?auto=format&w=439 439w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/fDpZXGBZneIqSzUUSCU9.png?auto=format&w=500 500w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/fDpZXGBZneIqSzUUSCU9.png?auto=format&w=571 571w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/fDpZXGBZneIqSzUUSCU9.png?auto=format&w=650 650w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/fDpZXGBZneIqSzUUSCU9.png?auto=format&w=741 741w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/fDpZXGBZneIqSzUUSCU9.png?auto=format&w=845 845w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/fDpZXGBZneIqSzUUSCU9.png?auto=format&w=964 964w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/fDpZXGBZneIqSzUUSCU9.png?auto=format&w=986 986w" width="493" />
<figcaption>Personalized Sign-in with Google button</figcaption>
</figure>
</div>
</div>
<p>The challenge is, since the personalized button depends on the third-party
cookies on the IdP domain within an iframe to identify the signed-in user to
render the button, it won't available once <a href="https://developer.chrome.com/docs/privacy-sandbox/third-party-cookie-phase-out/">third-party cookies are
deprecated</a>.</p>
<p>User Info API, shipping in Chrome 116, provides a way for the IdP to
obtain the information of the returning user from the server without depending
on the third-party cookies.</p>
<p>The API is expected to be called by the IdP from within an iframe embedded on
the RP website so that it can retrieve the user information and render a
personalized button as if it's a part of the RP surface. With the API call, the
browser makes a request to the <a href="https://developer.chrome.com/docs/privacy-sandbox/fedcm-developer-guide/#accounts-list-endpoint">accounts list
endpoint</a>,
then returns an array of user information if:</p>
<ul>
<li>The user has signed in to the RP with the IdP via FedCM in the past on the
same browser instance and the data hasn't been cleared.</li>
<li>The user is signed in to the IdP on the same browser instance.</li>
</ul>
<pre class="language-js"><code class="language-js"><span class="token comment">// Iframe displaying a page from the https://idp.example origin</span><br /><span class="token keyword">const</span> user_info <span class="token operator">=</span> <span class="token keyword">await</span> IdentityProvider<span class="token punctuation">.</span><span class="token function">getUserInfo</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br /> <span class="token literal-property property">configUrl</span><span class="token operator">:</span> <span class="token string">"https://idp.example/fedcm.json"</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">clientId</span><span class="token operator">:</span> <span class="token string">"client1234"</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /><span class="token comment">// IdentityProvider.getUserInfo returns an array of user information.</span><br /><span class="token keyword">if</span> <span class="token punctuation">(</span>user_info<span class="token punctuation">.</span>length <span class="token operator">></span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token comment">// Chrome puts returning accounts first, so the first account received is guaranteed to be a returning account.</span><br /> <span class="token keyword">const</span> name <span class="token operator">=</span> user_info<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>name<span class="token punctuation">;</span><br /> <span class="token keyword">const</span> given_name <span class="token operator">=</span> user_info<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>given_name<span class="token punctuation">;</span><br /> <span class="token keyword">const</span> display_name <span class="token operator">=</span> given_name <span class="token operator">?</span> given_name <span class="token operator">:</span> name<span class="token punctuation">;</span><br /> <span class="token keyword">const</span> picture <span class="token operator">=</span> user_info<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>picture<span class="token punctuation">;</span><br /> <span class="token keyword">const</span> email <span class="token operator">=</span> user_info<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>email<span class="token punctuation">;</span><br /> <span class="token comment">// Renders the personalized sign-in button with the information above.</span><br /><span class="token punctuation">}</span></code></pre>
<p>Note that to allow calling <code>IdentityProvider.getUserInfo()</code> from within an
iframe that is the same origin as the IdP, the embedding HTML must explicitly
allow it with the <code>identity-credentials-get</code> <a href="https://developer.chrome.com/docs/privacy-sandbox/permissions-policy/">permissions
policy</a>.</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>iframe</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://fedcm-idp-demo.glitch.me<span class="token punctuation">"</span></span> <span class="token attr-name">allow</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>identity-credentials-get<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>iframe</span><span class="token punctuation">></span></span></code></pre>
<p>You can see it in action at
<a href="https://fedcm-rp-demo.glitch.me/button">https://fedcm-rp-demo.glitch.me/button</a>.</p>
<h2 id="rp-context" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/fedcm-chrome-116-updates/#rp-context" aria-hidden="true">#</a> RP Context API</h2>
<p>RP Context API, shipping in Chrome 116, allows an RP to modify the string
in the FedCM dialog UI so that it can accommodate predefined authentication
contexts. See the following screenshots for different options:</p>
<div class="columns">
<div class="columns__column">
<figure>
<img alt="FedCM dialog rendered with "Sign in to ****"." decoding="async" height="398" loading="lazy" sizes="(min-width: 794px) 794px, calc(100vw - 48px)" src="https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/xBPyPacCJ0zPiRuCMnX3.png?auto=format" srcset="https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/xBPyPacCJ0zPiRuCMnX3.png?auto=format&w=200 200w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/xBPyPacCJ0zPiRuCMnX3.png?auto=format&w=228 228w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/xBPyPacCJ0zPiRuCMnX3.png?auto=format&w=260 260w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/xBPyPacCJ0zPiRuCMnX3.png?auto=format&w=296 296w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/xBPyPacCJ0zPiRuCMnX3.png?auto=format&w=338 338w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/xBPyPacCJ0zPiRuCMnX3.png?auto=format&w=385 385w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/xBPyPacCJ0zPiRuCMnX3.png?auto=format&w=439 439w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/xBPyPacCJ0zPiRuCMnX3.png?auto=format&w=500 500w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/xBPyPacCJ0zPiRuCMnX3.png?auto=format&w=571 571w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/xBPyPacCJ0zPiRuCMnX3.png?auto=format&w=650 650w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/xBPyPacCJ0zPiRuCMnX3.png?auto=format&w=741 741w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/xBPyPacCJ0zPiRuCMnX3.png?auto=format&w=845 845w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/xBPyPacCJ0zPiRuCMnX3.png?auto=format&w=964 964w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/xBPyPacCJ0zPiRuCMnX3.png?auto=format&w=1098 1098w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/xBPyPacCJ0zPiRuCMnX3.png?auto=format&w=1252 1252w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/xBPyPacCJ0zPiRuCMnX3.png?auto=format&w=1428 1428w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/xBPyPacCJ0zPiRuCMnX3.png?auto=format&w=1588 1588w" width="794" />
<figcaption>FedCM dialog rendered with "Sign in to ****". This is the default option if RP Context is not specified.</figcaption>
</figure>
</div>
<div class="columns__column">
<figure>
<img alt="FedCM dialog rendered with "Sign up to ****"." decoding="async" height="400" loading="lazy" sizes="(min-width: 796px) 796px, calc(100vw - 48px)" src="https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/C99AAEKSxzbzCIhoAbUA.png?auto=format" srcset="https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/C99AAEKSxzbzCIhoAbUA.png?auto=format&w=200 200w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/C99AAEKSxzbzCIhoAbUA.png?auto=format&w=228 228w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/C99AAEKSxzbzCIhoAbUA.png?auto=format&w=260 260w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/C99AAEKSxzbzCIhoAbUA.png?auto=format&w=296 296w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/C99AAEKSxzbzCIhoAbUA.png?auto=format&w=338 338w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/C99AAEKSxzbzCIhoAbUA.png?auto=format&w=385 385w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/C99AAEKSxzbzCIhoAbUA.png?auto=format&w=439 439w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/C99AAEKSxzbzCIhoAbUA.png?auto=format&w=500 500w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/C99AAEKSxzbzCIhoAbUA.png?auto=format&w=571 571w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/C99AAEKSxzbzCIhoAbUA.png?auto=format&w=650 650w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/C99AAEKSxzbzCIhoAbUA.png?auto=format&w=741 741w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/C99AAEKSxzbzCIhoAbUA.png?auto=format&w=845 845w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/C99AAEKSxzbzCIhoAbUA.png?auto=format&w=964 964w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/C99AAEKSxzbzCIhoAbUA.png?auto=format&w=1098 1098w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/C99AAEKSxzbzCIhoAbUA.png?auto=format&w=1252 1252w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/C99AAEKSxzbzCIhoAbUA.png?auto=format&w=1428 1428w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/C99AAEKSxzbzCIhoAbUA.png?auto=format&w=1592 1592w" width="796" />
<figcaption>FedCM dialog rendered with "Sign up to ****"</figcaption>
</figure>
</div>
</div>
<div class="columns">
<div class="columns__column">
<figure>
<img alt="FedCM dialog rendered with "Continue to ****"." decoding="async" height="388" loading="lazy" sizes="(min-width: 786px) 786px, calc(100vw - 48px)" src="https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/diZHUS6N0nwLB94ofzgs.png?auto=format" srcset="https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/diZHUS6N0nwLB94ofzgs.png?auto=format&w=200 200w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/diZHUS6N0nwLB94ofzgs.png?auto=format&w=228 228w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/diZHUS6N0nwLB94ofzgs.png?auto=format&w=260 260w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/diZHUS6N0nwLB94ofzgs.png?auto=format&w=296 296w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/diZHUS6N0nwLB94ofzgs.png?auto=format&w=338 338w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/diZHUS6N0nwLB94ofzgs.png?auto=format&w=385 385w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/diZHUS6N0nwLB94ofzgs.png?auto=format&w=439 439w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/diZHUS6N0nwLB94ofzgs.png?auto=format&w=500 500w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/diZHUS6N0nwLB94ofzgs.png?auto=format&w=571 571w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/diZHUS6N0nwLB94ofzgs.png?auto=format&w=650 650w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/diZHUS6N0nwLB94ofzgs.png?auto=format&w=741 741w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/diZHUS6N0nwLB94ofzgs.png?auto=format&w=845 845w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/diZHUS6N0nwLB94ofzgs.png?auto=format&w=964 964w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/diZHUS6N0nwLB94ofzgs.png?auto=format&w=1098 1098w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/diZHUS6N0nwLB94ofzgs.png?auto=format&w=1252 1252w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/diZHUS6N0nwLB94ofzgs.png?auto=format&w=1428 1428w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/diZHUS6N0nwLB94ofzgs.png?auto=format&w=1572 1572w" width="786" />
<figcaption>FedCM dialog rendered with "Continue to ****"</figcaption>
</figure>
</div>
<div class="columns__column">
<figure>
<img alt="FedCM dialog rendered with "Use ****"" decoding="async" height="408" loading="lazy" sizes="(min-width: 800px) 800px, calc(100vw - 48px)" src="https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/gQpsjT9xjD8ywGQgYyqV.png?auto=format" srcset="https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/gQpsjT9xjD8ywGQgYyqV.png?auto=format&w=200 200w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/gQpsjT9xjD8ywGQgYyqV.png?auto=format&w=228 228w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/gQpsjT9xjD8ywGQgYyqV.png?auto=format&w=260 260w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/gQpsjT9xjD8ywGQgYyqV.png?auto=format&w=296 296w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/gQpsjT9xjD8ywGQgYyqV.png?auto=format&w=338 338w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/gQpsjT9xjD8ywGQgYyqV.png?auto=format&w=385 385w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/gQpsjT9xjD8ywGQgYyqV.png?auto=format&w=439 439w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/gQpsjT9xjD8ywGQgYyqV.png?auto=format&w=500 500w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/gQpsjT9xjD8ywGQgYyqV.png?auto=format&w=571 571w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/gQpsjT9xjD8ywGQgYyqV.png?auto=format&w=650 650w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/gQpsjT9xjD8ywGQgYyqV.png?auto=format&w=741 741w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/gQpsjT9xjD8ywGQgYyqV.png?auto=format&w=845 845w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/gQpsjT9xjD8ywGQgYyqV.png?auto=format&w=964 964w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/gQpsjT9xjD8ywGQgYyqV.png?auto=format&w=1098 1098w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/gQpsjT9xjD8ywGQgYyqV.png?auto=format&w=1252 1252w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/gQpsjT9xjD8ywGQgYyqV.png?auto=format&w=1428 1428w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/gQpsjT9xjD8ywGQgYyqV.png?auto=format&w=1600 1600w" width="800" />
<figcaption>FedCM dialog rendered with "Use ****"</figcaption>
</figure>
</div>
</div>
<p>Usage is simple; provide <code>identity.context</code> property one of <code>"signin"</code>
(default), <code>"signup"</code>, <code>"use"</code> or <code>"continue"</code>.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> credential <span class="token operator">=</span> <span class="token keyword">await</span> navigator<span class="token punctuation">.</span>credentials<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br /> <span class="token literal-property property">identity</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token comment">// "signin" is the default, "signup", "use" and "continue" </span><br /> <span class="token comment">// can also be used</span><br /> <span class="token literal-property property">context</span><span class="token operator">:</span> <span class="token string">"signup"</span><span class="token punctuation">,</span> <br /> <span class="token literal-property property">providers</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span><br /> <span class="token literal-property property">configURL</span><span class="token operator">:</span> <span class="token string">"https://idp.example/fedcm.json"</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">clientId</span><span class="token operator">:</span> <span class="token string">"1234"</span><span class="token punctuation">,</span><br /> <span class="token punctuation">}</span><span class="token punctuation">]</span><span class="token punctuation">,</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<h2 id="idp-signin-status" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/fedcm-chrome-116-updates/#idp-signin-status" aria-hidden="true">#</a> IdP Sign-In Status API origin trial</h2>
<p>Chrome starts an <a href="https://developer.chrome.com/origintrials/#/registration/-7757239251913146367">IdP Sign-In Status API origin
trial</a>
on desktop from Chrome 116, and Android Chrome from 117.
<a href="https://developer.chrome.com/docs/web-platform/origin-trials/">Origin trials</a> give you
access to a new or experimental feature to build functionality your users can
try out for a limited time before the feature is made available to everyone.</p>
<div class="aside aside--important"><div class="aside__label gap-bottom-300"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6A4.997 4.997 0 017 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z"></path></svg><span>Important</span></div>
<p>IdP Sign-In Status API is a breaking change and will be a requirement for FedCM
when it's shipped.</p>
</div>
<p><a href="https://github.com/fedidcg/FedCM/blob/main/proposals/idp-sign-in-status-api.md">IdP Sign-In Status
API</a>
is a mechanism where an IdP informs the browser of the user's sign-in status on
the IdP. With this API, the browser can reduce unnecessary requests to the IdP
and mitigate potential timing attacks.</p>
<div class="aside aside--note">
<p>FedCM introduces a credentialed request to the <a href="https://developer.chrome.com/docs/privacy-sandbox/fedcm-developer-guide/#accounts-list-endpoint">accounts list
endpoint</a>.
This request does not send data identifying the requestor and also does not
allow passing data provided by the RP; however, the browser separately
performs an uncredentialed request containing the RP information to the
<code>client_metadata_endpoint</code>, then the IdP server can correlate the uncredentialed
request with the credentialed request (stochastically) using timing, or other
fingerprinting data. Refer to <a href="https://github.com/fedidcg/FedCM/blob/main/meetings/2022/FedCM_%20Options%20for%20the%20Timing%20Attack%20Problem%202022-08-31.pdf">these
slides</a>
for more details.</p>
</div>
<h3 id="inform-the-browser-of-the-users-sign-in-status" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/fedcm-chrome-116-updates/#inform-the-browser-of-the-users-sign-in-status" aria-hidden="true">#</a> Inform the browser of the user's sign-in status</h3>
<p>IdPs can signal the user's sign-in status to the browser by sending an HTTP
header or by calling a JavaScript API, when the user is signed in on the IdP, or
when the user is signed out from all their IdP accounts. The browser records the
status as one of the following: "sign-in", "sign-out", or "unknown" (default).</p>
<p>To signal that the user is signed in, send an <code>IdP-SignIn-Status: action=signin</code>
HTTP header in a top-level navigation or a same-origin subresource request:</p>
<pre class="language-http"><code class="language-http"><span class="token header"><span class="token header-name keyword">IdP-SignIn-Status</span><span class="token punctuation">:</span> <span class="token header-value">action=signin</span></span></code></pre>
<p>Alternatively, call the JavaScript API <code>IdentityProvider.login()</code> from the IdP
origin:</p>
<pre class="language-http"><code class="language-http">IdentityProvider.login()</code></pre>
<p>These will record the user's sign-in status as "sign-in". When the user's
sign-in status is set to "sign-in", the PR calling FedCM makes requests to the
IdP's accounts list endpoint and displays available accounts to the user in the
FedCM dialog.</p>
<p>To signal that the user is signed out from all their accounts, send the
<code>IdP-SignIn-Status: action=signout-all</code> HTTP header in a top-level navigation or
a same-origin subresource request:</p>
<pre class="language-http"><code class="language-http"><span class="token header"><span class="token header-name keyword">IdP-SignIn-Status</span><span class="token punctuation">:</span> <span class="token header-value">action=signout-all</span></span></code></pre>
<p>Alternatively, call the JavaScript API <code>IdentityProvider.logout()</code> from the IdP
origin:</p>
<pre class="language-http"><code class="language-http">IdentityProvider.logout()</code></pre>
<p>These will record the user's sign-in status as "sign-out". When the user's
sign-in status is "sign-out", calling the FedCM silently fails without making a
request to the IdP's accounts list endpoint.</p>
<p>By default, the IdP sign-in status is set to "unknown". This status is used
before the IdP sends a signal using the IdP Sign-In Status API. We introduce
this status for better transition because a user may have already signed in to
the IdP when we ship this API and the IdP may not have a chance to signal this
to the browser by the time FedCM is first invoked. In this case, we make a
request to the IdP's accounts list endpoint and update the status based on the
response from the accounts list endpoint:</p>
<ul>
<li>If the endpoint returns a list of active accounts, update the status to
"sign-in" and open the FedCM dialog to show those accounts.</li>
<li>If the endpoint returns no accounts, update the status to "sign-out" and fail
the FedCM call.</li>
</ul>
<h3 id="what-if-the-user-session-expires-let-the-user-sign-in-through-a-dynamic-sign-in-flow" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/fedcm-chrome-116-updates/#what-if-the-user-session-expires-let-the-user-sign-in-through-a-dynamic-sign-in-flow" aria-hidden="true">#</a> What if the user session expires? Let the user sign in through a dynamic sign-in flow</h3>
<p>Even though the IdP continues to inform the browser of the user's sign-in
status, it could be out of sync, such as when the session expires. The browser
tries to send a credentialed request to the accounts list endpoint when the
sign-in status is "sign-in", but the server rejects it because the session is no
longer available. In such a scenario, the browser can dynamically let the user
sign in to the IdP through a popup window.</p>
<p>The FedCM dialog will display a message, as shown in the following image:</p>
<figure>
<img alt="A FedCM dialog suggesting to sign in to the IdP." decoding="async" height="449" loading="lazy" sizes="(min-width: 800px) 800px, calc(100vw - 48px)" src="https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/hDf8EI5TBAa42umi7kMu.png?auto=format" srcset="https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/hDf8EI5TBAa42umi7kMu.png?auto=format&w=200 200w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/hDf8EI5TBAa42umi7kMu.png?auto=format&w=228 228w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/hDf8EI5TBAa42umi7kMu.png?auto=format&w=260 260w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/hDf8EI5TBAa42umi7kMu.png?auto=format&w=296 296w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/hDf8EI5TBAa42umi7kMu.png?auto=format&w=338 338w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/hDf8EI5TBAa42umi7kMu.png?auto=format&w=385 385w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/hDf8EI5TBAa42umi7kMu.png?auto=format&w=439 439w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/hDf8EI5TBAa42umi7kMu.png?auto=format&w=500 500w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/hDf8EI5TBAa42umi7kMu.png?auto=format&w=571 571w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/hDf8EI5TBAa42umi7kMu.png?auto=format&w=650 650w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/hDf8EI5TBAa42umi7kMu.png?auto=format&w=741 741w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/hDf8EI5TBAa42umi7kMu.png?auto=format&w=845 845w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/hDf8EI5TBAa42umi7kMu.png?auto=format&w=964 964w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/hDf8EI5TBAa42umi7kMu.png?auto=format&w=1098 1098w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/hDf8EI5TBAa42umi7kMu.png?auto=format&w=1252 1252w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/hDf8EI5TBAa42umi7kMu.png?auto=format&w=1428 1428w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/hDf8EI5TBAa42umi7kMu.png?auto=format&w=1600 1600w" width="800" />
<figcaption>A FedCM dialog suggesting to sign in to the IdP.</figcaption>
</figure>
<p>By clicking on the <strong>Continue</strong> button, the browser opens a popup window
sending the user to the IdP's sign-in page.</p>
<figure>
<img alt="A popup window shown after clicking on the sign in to the IdP button." decoding="async" height="516" loading="lazy" sizes="(min-width: 800px) 800px, calc(100vw - 48px)" src="https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/Lujgpk77nxF81sl8Ca5S.png?auto=format" srcset="https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/Lujgpk77nxF81sl8Ca5S.png?auto=format&w=200 200w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/Lujgpk77nxF81sl8Ca5S.png?auto=format&w=228 228w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/Lujgpk77nxF81sl8Ca5S.png?auto=format&w=260 260w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/Lujgpk77nxF81sl8Ca5S.png?auto=format&w=296 296w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/Lujgpk77nxF81sl8Ca5S.png?auto=format&w=338 338w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/Lujgpk77nxF81sl8Ca5S.png?auto=format&w=385 385w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/Lujgpk77nxF81sl8Ca5S.png?auto=format&w=439 439w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/Lujgpk77nxF81sl8Ca5S.png?auto=format&w=500 500w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/Lujgpk77nxF81sl8Ca5S.png?auto=format&w=571 571w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/Lujgpk77nxF81sl8Ca5S.png?auto=format&w=650 650w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/Lujgpk77nxF81sl8Ca5S.png?auto=format&w=741 741w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/Lujgpk77nxF81sl8Ca5S.png?auto=format&w=845 845w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/Lujgpk77nxF81sl8Ca5S.png?auto=format&w=964 964w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/Lujgpk77nxF81sl8Ca5S.png?auto=format&w=1098 1098w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/Lujgpk77nxF81sl8Ca5S.png?auto=format&w=1252 1252w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/Lujgpk77nxF81sl8Ca5S.png?auto=format&w=1428 1428w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/Lujgpk77nxF81sl8Ca5S.png?auto=format&w=1600 1600w" width="800" />
<figcaption>A popup window shown after clicking on the sign in to the IdP button.</figcaption>
</figure>
<div class="aside aside--note">
<p>The website doesn't have control over the size of the popup window before it's
opened. By default, the size is set to 500 px in width and 600 px in height. The
website may change the size after the window has been opened from within the
content area.</p>
</div>
<p>The sign-in page URL (which must be the IdP's origin) can be specified with <code>signin_url</code>
as part of the <a href="https://developer.chrome.com/docs/privacy-sandbox/fedcm-developer-guide/#idp-config-file">IdP config
file</a>.</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span><br /> <span class="token property">"accounts_endpoint"</span><span class="token operator">:</span> <span class="token string">"/auth/accounts"</span><span class="token punctuation">,</span><br /> <span class="token property">"client_metadata_endpoint"</span><span class="token operator">:</span> <span class="token string">"/auth/metadata"</span><span class="token punctuation">,</span><br /> <span class="token property">"id_assertion_endpoint"</span><span class="token operator">:</span> <span class="token string">"/auth/idtokens"</span><span class="token punctuation">,</span><br /> <span class="token property">"signin_url"</span><span class="token operator">:</span> <span class="token string">"/signin"</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span></code></pre>
<p>The popup window is a regular browser window that uses first-party cookies.
Whatever happens within the content window is up to the IdP, but no window
handles are available to make a cross-origin communication request to the RP
page. After the user is signed in, the IdP should:</p>
<ul>
<li>Send the <code>IdP-SignIn-Status: action=signin</code> header or call the
<code>IdentityProvider.login()</code> API to inform the browser that the user has been
signed in.</li>
<li>Call <code>IdentityProvider.close()</code> to close itself (the popup window).</li>
</ul>
<pre class="language-js"><code class="language-js"><span class="token comment">// User is signed in...</span><br /><span class="token comment">// Don't forget feature detection.</span><br /><span class="token keyword">if</span> <span class="token punctuation">(</span>IdentityProvider<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token comment">// Signal to the browser that the user has signed in.</span><br /> IdentityProvider<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span></code></pre>
<figure class="screenshot">
<video autoplay="" height="559" loop="" muted="" width="800" style="--vid-width: 800; --vid-height: 559"> <source src="https://storage.googleapis.com/web-dev-uploads/video/YLflGBAPWecgtKJLqCJHSzHqe2J2/Dyf5PXQDwD3u0mLsKFBS.mp4" type="video/mp4" /> </video>
<figcaption>A user signs into an RP after signing in to the IdP using FedCM</figcaption>
</figure>
<div class="aside aside--note">
<p>This dynamic sign-in experience is intended for when an IdP session has expired
without an explicit sign-out, causing the browser's sign-in state to contradict
the actual state. This will not be triggered for users who haven't signed
in to the IdP yet. This new flow is designed to cover the cases where the user's
IdP sign-in status on the browser is "sign-in" but the IdP's accounts list
endpoint returns no accounts.</p>
<p>Assuming the IdP Sign-In Status API is called promptly, when FedCM is invoked,
any of the following scenarios can occur:</p>
<ul>
<li>If the browser doesn't know the user's sign-in status ("unknown"), the status
is updated (either "sign-in" or "sign-out") depending on the IdP's response
from the accounts list endpoint. A dynamic sign-in flow won't be triggered.</li>
<li>If a user has signed in to the IdP on the browser ("sign-in"), and then
explicitly signed out from the IdP ("sign-out"), the dynamic sign-in flow
won't be triggered.</li>
<li>If a user has signed in to the IdP on the browser ("sign-in"), but the session
expires without an explicit sign-out, the dynamic sign-in flow will be
triggered.</li>
</ul>
<p>The IdP Sign-In Status API does not provide the ability for the RP to display a
"Sign-in to the IdP" dialog to users who are not signed in to the IdP. This is a
separate feature which may be added in the future.</p>
</div>
<p>You can try the IdP Sign-In Status API behavior in <a href="https://fedcm-rp-demo.glitch.me/">our
demo</a>. The session expires in three minutes
after you sign in to <a href="https://fedcm-idp-demo.glitch.me/">the demo IdP</a>. Then you
can observe the sign-in to the IdP through the popup window behavior.</p>
<h2 id="origin-trial" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/fedcm-chrome-116-updates/#origin-trial" aria-hidden="true">#</a> Participate in the origin trial</h2>
<p>You can try IdP Sign-In Status API locally by turning on <a href="https://developer.chrome.com/docs/web-platform/chrome-flags/">a Chrome<br />
flag</a>
<code>chrome://flags#fedcm-idp-signin-status-api</code> on<br />
Chrome 116 or later.</p>
<p>You can also enable the IdP Sign-In Status API by registering an origin trial
twice:</p>
<ul>
<li>Register an <a href="https://developer.chrome.com/docs/web-platform/origin-trials/">origin trial</a> for the IdP.</li>
<li>Register a <a href="https://developer.chrome.com/docs/web-platform/third-party-origin-trials/">third-party origin
trial</a> for the RP.</li>
</ul>
<p>Origin trials allow you to try new features and give feedback on their
usability, practicality, and effectiveness to the web standards community. For
more information, check out the <a href="https://github.com/GoogleChrome/OriginTrials/blob/gh-pages/developer-guide.md">Origin Trials Guide for Web
Developers</a>.</p>
<p>The IdP Sign-In Status API origin trial is available from Chrome 116 through
Chrome 119.</p>
<h3 id="register-an-origin-trial-for-the-idp" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/fedcm-chrome-116-updates/#register-an-origin-trial-for-the-idp" aria-hidden="true">#</a> Register an origin trial for the IdP</h3>
<ol>
<li>Go to the <a href="https://developer.chrome.com/origintrials/#/view_trial/3196429835526209537">origin trial registration page</a>.</li>
<li>Click the <strong>Register</strong> button and fill out the form to request a token.</li>
<li>Enter the IdP's origin as <strong>Web Origin</strong>.</li>
<li>Click <strong>Submit</strong>.</li>
<li>Add an <code>origin-trial</code> <code><meta></code> tag to the head of the pages that use
<code>IdentityProvider.close()</code>. For example, this may look something like: <br />
<code><meta http-equiv="origin-trial" content="TOKEN_GOES_HERE"></code>.</li>
</ol>
<div class="aside aside--warning"><div class="aside__label gap-bottom-300"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M23 21L12 2 1 21h22zm-12-3v-2h2v2h-2zm0-4h2v-4h-2v4z"></path></svg><span>Warning</span></div>
<p>Sending an <code>Origin-Trial</code> HTTP header won't work for this API.</p>
</div>
<h3 id="register-a-third-party-origin-trial-for-the-rp" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/fedcm-chrome-116-updates/#register-a-third-party-origin-trial-for-the-rp" aria-hidden="true">#</a> Register a third-party origin trial for the RP</h3>
<ol>
<li>Go to the <a href="https://developer.chrome.com/origintrials/#/view_trial/3196429835526209537">origin trial registration page</a>.</li>
<li>Click the <strong>Register</strong> button and fill out the form to request a token.</li>
<li>Enter the IdP's origin as <strong>Web Origin</strong>.</li>
<li>Check <strong>Third-party matching</strong> to inject the token with JavaScript on other origins.</li>
<li>Click <strong>Submit</strong>.</li>
<li>Embed the issued token on a third-party website.</li>
</ol>
<p>To embed the token on a third-party website, add the following code to your
JavaScript library or SDK served from the IdP's origin.</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">const</span> tokenElement <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">'meta'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br />tokenElement<span class="token punctuation">.</span>httpEquiv <span class="token operator">=</span> <span class="token string">'origin-trial'</span><span class="token punctuation">;</span><br />tokenElement<span class="token punctuation">.</span>content <span class="token operator">=</span> <span class="token string">'TOKEN_GOES_HERE'</span><span class="token punctuation">;</span><br />document<span class="token punctuation">.</span>head<span class="token punctuation">.</span><span class="token function">appendChild</span><span class="token punctuation">(</span>tokenElement<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Replace <code>TOKEN_GOES_HERE</code> with your own token.</p>
<div class="aside aside--note">
<p>If FedCM is executed directly by an RP without loading an IdP's scripts, the RP<br />
must register their origin themselves for the origin trial.</p>
</div>
<h2 id="feedback" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/fedcm-chrome-116-updates/#feedback" aria-hidden="true">#</a> Engage and share feedback</h2>
<p>If you have feedback or encounter any issues during testing, you can share them
at
<a href="https://bugs.chromium.org/p/chromium/issues/entry?components=Blink%3EIdentity%3EFedCM">crbug.com</a>.</p>
<p>Photo by <a href="https://unsplash.com/@dancristianpaduret?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Dan Cristian Pădureț</a> on <a href="https://unsplash.com/photos/noOXRT9gfQ8?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a></p>
Storage partitioning deprecation trial extended2023-06-09T00:00:00Zhttps://developer.chrome.com/en/blog/storage-partitioning-deprecation-trial-extended/Milica Mihajlijahttps://developer.chrome.com/authors/mihajlija/<p>To prevent certain types of side-channel cross-site tracking,
<a href="https://developer.chrome.com/docs/privacy-sandbox/storage-partitioning/">Chrome has partitioned storage and communications APIs in third-party contexts</a>
in Chrome 115 and above.</p>
<p>Sites that haven't had time to adapt their sites for third-party storage
partitioning can take part in a deprecation trial to temporarily unpartition and
restore prior behavior of storage, service workers, and communication APIs in
content embedded on their site.</p>
<p>The deprecation trial is available starting in Chrome 115. It was initially
scheduled to expire in Chrome version 123, ending on May 2, 2024.</p>
<p>Based on feedback, to give developers more time to adapt to the new
implementation of storage partitioning, the storage partitioning deprecation
trial has been extended and will be available until the release of Chrome 127,
scheduled for September 3, 2024.</p>
<p><a href="https://developer.chrome.com/blog/storage-partitioning-deprecation-trial/">Learn more about how to participate in deprecation trial for unpartitioned third-party storage, Service Workers, and Communication APIs.</a></p>
Announcing the second Compute Pressure origin trial2023-05-30T00:00:00Zhttps://developer.chrome.com/en/blog/compute-pressure-origin-trial-2/Kenneth Christiansenhttps://developer.chrome.com/authors/kenchris/Arnaud (Arno) Mandyhttps://developer.chrome.com/authors/arskama/<p>Over the last year, Intel has been collaborating with Google and other parties
on the Compute Pressure API. In Chrome 115, you can register for an
<a href="https://developer.chrome.com/origintrials/#/view_trial/1196831600973709313">origin trial</a> to help test
this new API, and this post explains the problems the API has been designed to
solve, and shows how to use it.</p>
<div class="aside aside--note">
<p>The Compute Pressure API was in origin trial from Chrome 92 to
Chrome 94. Since this first origin trial, many <a href="https://github.com/w3c/compute-pressure/blob/main/CHANGES.md">changes</a> have been made to
the API. The existing <a href="https://developer.chrome.com/docs/web-platform/compute-pressure/">article</a> on the
Compute Pressure API is updated with more details. </p></div><p></p>
<h2 id="the-problem" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/compute-pressure-origin-trial-2/#the-problem" aria-hidden="true">#</a> The problem</h2>
<p>The web is becoming a key application platform, with new capabilities making
applications such as video conferencing not just possible, but a delightful
experience for users. Web-based experiences load instantly, they are accessible
from anywhere, and need no up-front installation.</p>
<p>Users want fast-loading and responsive applications. They also want to get as
much as possible out of their battery life, and silent devices that are not hot
to touch. These things can sometimes be hard to achieve when also creating
advanced experiences as smooth animations and background video blurring use a
lot of processing power, pushing hardware to its limits and draining batteries.</p>
<p>In addition, there are a huge variety of devices being used to access web
applications. A five-year old laptop will have very different capabilities to a
brand new desktop computer, even when running the same browser version.</p>
<p>Developers often opt to develop for the lowest common denominator, avoiding
using some features that would tax older or less capable devices. However, if it
were possible to optimize the experience for users who have capable equipment
and are in the right environment to benefit from it, why not do it? As an
example, when joining a video call from your phone, just seeing the current
speaker is likely the best experience. On a desktop however, it would be nice to
see everyone on the call, and the hardware is usually up to the task. To achieve
this, you need live hardware telemetry, without sacrificing the users' privacy,
that can be used for scheduling tasks and progressively turning on and off
features to ensure a smooth user experience. This is where the Compute Pressure
API can help.</p>
<h2 id="what-is-the-compute-pressure-api" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/compute-pressure-origin-trial-2/#what-is-the-compute-pressure-api" aria-hidden="true">#</a> What is the Compute Pressure API?</h2>
<p>The Compute Pressure API offers high-level states that represent the pressure on
the system. These high-level states ensure a good balance between privacy (not
sharing too much specific information that could identify a user) and
information that developers can easily reason about. Additionally, it allows the
implementation to use the right underlying hardware metrics to ensure that users
can take advantage of all the processing power available to them as long as the
system is not under unmanageable stress.</p>
<p>Modern CPUs, for instance, are designed to run fine at 100% utilization in most
situations, on a single core or across all cores, so an API that hardcodes 80%
utilization as being critical could result in developers under-utilizing the
hardware's capabilities and offering a suboptimal user experience. On the other
hand, a system might not have proper cooling, or the ambient temperature might
be very high as in the summer, and the system might be throttling even before
reaching high CPU utilization. The current API works on global CPU pressure, but
we plan to experiment with enabling CPU pressure per page across the main thread
and workers.</p>
<p>Compute pressure has the following states:</p>
<ul>
<li><strong>Nominal:</strong> Current workloads are causing minimal pressure, allowing the
system to run at a lower clock frequency to preserve power.</li>
<li><strong>Fair:</strong> The system is doing fine; everything is smooth, and it can take on
additional work without issues.</li>
<li><strong>Serious:</strong> There is some serious pressure on the system, but it is
manageable, and the system is doing well, but could be getting close to its
limits:
<ul>
<li>Clock speed (depending on AC or DC power) is consistently high.</li>
<li>Thermals are high but still manageable and not causing throttling.</li>
</ul>
</li>
</ul>
<p>At this point if you add more work the system may move into a critical state.</p>
<ul>
<li><strong>Critical:</strong> The system is now about to reach its limits, but it hasn't
reached the limit yet. Critical doesn't mean that the system is being actively
throttled, but this state is not sustainable for the long run and might result
in throttling if the workload remains the same. This signal is the last call
for the web application to lighten its workload.</li>
</ul>
<h2 id="enable-the-compute-pressure-api" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/compute-pressure-origin-trial-2/#enable-the-compute-pressure-api" aria-hidden="true">#</a> Enable the Compute Pressure API</h2>
<p>By default, the Compute Pressure API is not enabled in Chrome, but it can be
experimented with in Chrome 115 by explicitly enabling the functionality.
You can activate it locally by enabling the
<a href="https://developer.chrome.com/docs/web-platform/chrome-flags/#chromeflags"><code>enable-experimental-web-platform-features</code> flag</a>.</p>
<p>To enable it for all visitors to your app, an
<a href="https://developer.chrome.com/origintrials/#/view_trial/1196831600973709313">origin trial</a> is currently
underway and set to end in Chrome 118 (July 18, 2023). To
participate in the trial, sign up and include a meta element with the origin
trial token in either the HTML or HTTP header. For more information, refer to
the <a href="https://developer.chrome.com/docs/web-platform/origin-trials/">Get started with origin trials</a> post.</p>
<h2 id="observe-compute-pressure" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/compute-pressure-origin-trial-2/#observe-compute-pressure" aria-hidden="true">#</a> Observe compute pressure</h2>
<p>The following code snippet illustrates how to monitor and act on changes of
compute pressure:</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// The `records` parameter is a sequence of records between two</span><br /><span class="token comment">// consecutive callbacks. Currently it contains ten entries, but</span><br /><span class="token comment">// this is an implementation detail.</span><br /><span class="token keyword">function</span> <span class="token function">callback</span><span class="token punctuation">(</span><span class="token parameter">records</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">const</span> lastRecord <span class="token operator">=</span> records<span class="token punctuation">.</span><span class="token function">pop</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">Current pressure </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>lastRecord<span class="token punctuation">.</span>state<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span>lastRecord<span class="token punctuation">.</span>state <span class="token operator">===</span> <span class="token string">'critical'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token comment">// Reduce workers load by 4.</span><br /> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>lastRecord<span class="token punctuation">.</span>state <span class="token operator">===</span> <span class="token string">'serious'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token comment">// Reduce workers load by 2.</span><br /> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span><br /> <span class="token comment">// Do not reduce.</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><br /><br /><span class="token keyword">const</span> observer <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">PressureObserver</span><span class="token punctuation">(</span>callback<span class="token punctuation">,</span> <span class="token punctuation">{</span><br /> <span class="token comment">// Sample rate in Hertz.</span><br /> <span class="token literal-property property">sampleRate</span><span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">,</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br />observer<span class="token punctuation">.</span><span class="token function">observe</span><span class="token punctuation">(</span><span class="token string">'cpu'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>The following code snippet illustrates how to use the Compute Pressure API from
an iframe:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>iframe</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://mysite.com/<span class="token punctuation">"</span></span> <span class="token attr-name">allow</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>compute-pressure<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br /> <span class="token comment">// Use Compute Pressure API.</span><br /> </span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>iframe</span><span class="token punctuation">></span></span></code></pre>
<h2 id="platform-support" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/compute-pressure-origin-trial-2/#platform-support" aria-hidden="true">#</a> Platform support</h2>
<p>The Compute Pressure API is available in Chrome 115 on Linux, ChromeOS,
macOS, and Windows.</p>
<div class="aside aside--note">
<p>While due to resource constraints Android support is currently not
supported during the origin trial phase, it's planned to support the operating
system in the future. </p></div><p></p>
<h2 id="demo" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/compute-pressure-origin-trial-2/#demo" aria-hidden="true">#</a> Demo</h2>
<p>Try the demo embedded below to see how the compute pressure state changes based
on some artificial pressure.</p>
<iframe style="border:solid 1px; height:1100px; width:100%;" src="https://w3c.github.io/compute-pressure/demo/" allow="compute-pressure"></iframe>
<p>In case your browser doesn't support the API, the video below shows a recording
of the demo.</p>
<div class="youtube"> <lite-youtube videoid="6hsgWSF-kuw"> </lite-youtube></div>
<h2 id="feedback" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/compute-pressure-origin-trial-2/#feedback" aria-hidden="true">#</a> Feedback</h2>
<p>Developer feedback is really important at this stage, so please
<a href="https://github.com/w3c/compute-pressure/issues/">file issues on GitHub</a> with
suggestions and questions.</p>
<h2 id="useful-links" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/compute-pressure-origin-trial-2/#useful-links" aria-hidden="true">#</a> Useful links</h2>
<ul>
<li><a href="https://github.com/w3c/compute-pressure#readme">Public explainer</a></li>
<li><a href="https://w3c.github.io/compute-pressure/">Specifications</a></li>
<li><a href="https://bugs.chromium.org/p/chromium/issues/detail?id=1067627">Chromium tracking bug</a></li>
<li><a href="https://chromestatus.com/feature/5597608644968448">ChromeStatus.com entry</a></li>
<li><a href="https://github.com/w3ctag/design-reviews/issues/795">TAG Review</a></li>
<li><a href="https://groups.google.com/a/chromium.org/g/blink-dev/c/QfJ4pngu3gc">Intent to Experiment</a></li>
<li><a href="https://w3c.github.io/compute-pressure/demo/">Compute Pressure API Demo</a> | <a href="https://github.com/w3c/compute-pressure/tree/main/demo">Compute Pressure API Demo
source</a></li>
</ul>
<h2 id="acknowledgements" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/compute-pressure-origin-trial-2/#acknowledgements" aria-hidden="true">#</a> Acknowledgements</h2>
<p>The hero image was created by Robert Anasch on
<a href="https://unsplash.com/photos/-C7IKRBZHrg">Unsplash</a>.
This article was reviewed by <a href="https://developer.chrome.com/authors/rachelandrew/">Rachel Andrew</a> and <a href="https://developer.chrome.com/authors/thomassteiner">Thomas Steiner</a>.</p>
Preparing to ship the Privacy Sandbox relevance and measurement APIs2023-05-18T00:00:00Zhttps://developer.chrome.com/en/blog/shipping-privacy-sandbox/Rowan Merewoodhttps://developer.chrome.com/authors/rowan_m/Alexandra Whitehttps://developer.chrome.com/authors/alexandrawhite/<p>The Privacy Sandbox project is gearing up to ship the relevance and measurement
APIs to Chrome Stable. On the
<a href="https://privacysandbox.com/open-web/#the-privacy-sandbox-timeline">project timeline for the web</a>,
we show general availability (GA) starts in Q3 2023. Specifically, we intend to
target <a href="https://chromiumdash.appspot.com/schedule">Chrome Stable 115</a>, which
means we'll begin making the APIs generally available from late July, 2023.</p>
<p>In this post, we review multiple components of this launch, including:</p>
<ul>
<li><strong>What's shipping</strong>. The relevance and measurement APIs launching are Topics,
Protected Audience, Attribution Reporting, Private Aggregation, Shared
Storage, and Fenced Frames. We'll make these APIs available gradually to
monitor for potential issues.</li>
<li><strong>The official launch process</strong>. Each API goes through the standard Chrome
launch process, which includes individual "Intent to Ship" messages published
on the blink-dev mailing list for approval.</li>
<li><strong>Updated user controls</strong>. Users will have Ad privacy controls to manage the APIs.</li>
<li><strong>Status of the origin trial</strong>. The origin trial will continue to be
available through to Stable release.</li>
<li><strong>Enrollment</strong>. Enrollment will be available in June and required to access
the relevance and measurement APIs in August.</li>
<li><strong>Chrome-facilitated testing</strong>. We're preparing options for developers to
test the APIs without third-party cookie data.</li>
</ul>
<p>We'll keep you posted as we get closer to GA. For now, the only immediate
action for developers is to become informed. By identifying what changes are
coming, you can ensure your sites are ready.</p>
<p>When we say "GA," we mean the APIs are available by default in Chrome, without
requiring browser flags or participation in an origin trial. However, this does
not mean 100% of Chrome browsers immediately have the APIs enabled—the
APIs will be made available gradually, and users can always control if the APIs
are active. Once we are ramped up, the ecosystem can use the APIs in
production.</p>
<figure class="screenshot">
<a href="https://privacysandbox.com/open-web/#the-privacy-sandbox-timeline">
<img alt="Web timeline for the Privacy Sandbox." decoding="async" height="562" loading="lazy" sizes="(min-width: 800px) 800px, calc(100vw - 48px)" src="https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/ywbyok1JNTBh5B9Xb8xP.png?auto=format" srcset="https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/ywbyok1JNTBh5B9Xb8xP.png?auto=format&w=200 200w, https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/ywbyok1JNTBh5B9Xb8xP.png?auto=format&w=228 228w, https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/ywbyok1JNTBh5B9Xb8xP.png?auto=format&w=260 260w, https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/ywbyok1JNTBh5B9Xb8xP.png?auto=format&w=296 296w, https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/ywbyok1JNTBh5B9Xb8xP.png?auto=format&w=338 338w, https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/ywbyok1JNTBh5B9Xb8xP.png?auto=format&w=385 385w, https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/ywbyok1JNTBh5B9Xb8xP.png?auto=format&w=439 439w, https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/ywbyok1JNTBh5B9Xb8xP.png?auto=format&w=500 500w, https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/ywbyok1JNTBh5B9Xb8xP.png?auto=format&w=571 571w, https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/ywbyok1JNTBh5B9Xb8xP.png?auto=format&w=650 650w, https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/ywbyok1JNTBh5B9Xb8xP.png?auto=format&w=741 741w, https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/ywbyok1JNTBh5B9Xb8xP.png?auto=format&w=845 845w, https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/ywbyok1JNTBh5B9Xb8xP.png?auto=format&w=964 964w, https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/ywbyok1JNTBh5B9Xb8xP.png?auto=format&w=1098 1098w, https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/ywbyok1JNTBh5B9Xb8xP.png?auto=format&w=1252 1252w, https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/ywbyok1JNTBh5B9Xb8xP.png?auto=format&w=1428 1428w, https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/ywbyok1JNTBh5B9Xb8xP.png?auto=format&w=1600 1600w" width="800" />
</a>
</figure>
<p>These are the same set of APIs available for testing in the
<a href="https://developer.chrome.com/docs/privacy-sandbox/unified-origin-trial/">relevance and measurement origin trial</a>.
The feedback we received from the ecosystem during testing has been absolutely
critical in shaping this functionality to meet important use cases. We're
grateful to all of you who have been testing, reporting issues, and sharing
your results with the world—it's a genuinely collaborative effort!</p>
<p id="included-apis"></p>
<h2 id="whats-shipping" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/shipping-privacy-sandbox/#whats-shipping" aria-hidden="true">#</a> What's shipping</h2>
<p>The relevance and measurement APIs include:</p>
<ul id="blink-intents">
<li><a href="https://developer.chrome.com/docs/privacy-sandbox/topics/">Topics</a>: Generate signals for interest-based advertising without third-party cookies or other user identifiers that track individuals across sites.</li>
<li><a href="https://developer.chrome.com/docs/privacy-sandbox/fledge/">Protected Audience</a>: Select ads to serve remarketing and custom audience use cases, designed to mitigate third-party tracking across sites. (This API was previously named FLEDGE. As we head towards launch, we've updated the name to better reflect the functionality.)</li>
<li><a href="https://developer.chrome.com/docs/privacy-sandbox/attribution-reporting/">Attribution Reporting</a>: Correlate ad clicks or ad views with conversions. Ad techs can generate event-level or summary reports.</li>
<li><a href="https://developer.chrome.com/docs/privacy-sandbox/private-aggregation/">Private Aggregation</a>: Generate aggregate data reports using data from Protected Audience and cross-site data from Shared Storage.</li>
<li><a href="https://developer.chrome.com/docs/privacy-sandbox/shared-storage/">Shared Storage</a>: Allow unlimited, cross-site storage write access with privacy-preserving read access.</li>
<li><a href="https://developer.chrome.com/docs/privacy-sandbox/fenced-frame/">Fenced Frames</a>: Securely embed content onto a page without sharing cross-site data.</li>
</ul>
<h3 id="shipping-features-in-chrome" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/shipping-privacy-sandbox/#shipping-features-in-chrome" aria-hidden="true">#</a> Shipping features in Chrome</h3>
<figure class="float-right">
<img alt="A suitcase with a lock and key" decoding="async" height="338" loading="lazy" sizes="(min-width: 444px) 444px, calc(100vw - 48px)" src="https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/wtfeBg9L5DZVLQYoKKvO.png?auto=format" srcset="https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/wtfeBg9L5DZVLQYoKKvO.png?auto=format&w=200 200w, https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/wtfeBg9L5DZVLQYoKKvO.png?auto=format&w=228 228w, https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/wtfeBg9L5DZVLQYoKKvO.png?auto=format&w=260 260w, https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/wtfeBg9L5DZVLQYoKKvO.png?auto=format&w=296 296w, https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/wtfeBg9L5DZVLQYoKKvO.png?auto=format&w=338 338w, https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/wtfeBg9L5DZVLQYoKKvO.png?auto=format&w=385 385w, https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/wtfeBg9L5DZVLQYoKKvO.png?auto=format&w=439 439w, https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/wtfeBg9L5DZVLQYoKKvO.png?auto=format&w=500 500w, https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/wtfeBg9L5DZVLQYoKKvO.png?auto=format&w=571 571w, https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/wtfeBg9L5DZVLQYoKKvO.png?auto=format&w=650 650w, https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/wtfeBg9L5DZVLQYoKKvO.png?auto=format&w=741 741w, https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/wtfeBg9L5DZVLQYoKKvO.png?auto=format&w=845 845w, https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/wtfeBg9L5DZVLQYoKKvO.png?auto=format&w=888 888w" width="444" />
</figure>
<p>All proposals for new web platform features, including those in the Privacy Sandbox, go through our <a href="https://www.chromium.org/blink/launching-features/">standard process to ship new functionality</a> in Chrome. Each milestone in an API's lifecycle is signaled by an <a href="https://www.youtube.com/watch?v=9cvzZ5J_DTg&list=PLNYkxOF6rcIBzsbjZKyOdO-iwQTjidz1P&index=1&t=3s&ab_channel=GoogleChromeDevelopers">Intent</a> message that we share on the public <a href="https://groups.google.com/a/chromium.org/g/blink-dev">blink-dev mailing list</a>. That means for each of the Privacy Sandbox features, we sent an "Intent to Prototype" (I2P) when we shared the initial proposal for discussion and an "Intent to Experiment" (I2E) when we made the features available for testing via origin trial.</p>
<p>Soon, we'll send an "Intent to Ship" (I2S) message to blink-dev for each feature. The I2S messages will include additional detail on exact functionality and the plan to target Chrome version 115. An I2S must receive approvals from three Chromium API owners before it can proceed.</p>
<p>The APIs will not immediately be enabled for all browser instances with the Stable release. As with some previous Privacy Sandbox features, we'll gradually enable the APIs for an increasing percentage of browser instances to ensure that we can monitor and respond to any potential issues. As we progress, we'll share the status across our developer channels: here on developer.chrome.com, the blink-dev I2S threads, and the <a href="https://developer.chrome.com/docs/privacy-sandbox/events/#future-events">developer mailing lists</a>.</p>
<h3 id="shipped" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/shipping-privacy-sandbox/#shipped" aria-hidden="true">#</a> Already shipped</h3>
<p>The relevance and measurement APIs are a critical piece of the Privacy Sandbox project. But, there are also some significant milestones we've already hit and plenty more to come:</p>
<ul>
<li><a href="https://developer.chrome.com/docs/privacy-sandbox/user-agent/">User-Agent reduction</a>: Limit passively shared browser data to reduce the volume of sensitive information which leads to fingerprinting, while providing User-Agent Client Hints to actively request data. We began reduction of these values in May 2022 and completed in May 2023.</li>
<li><a href="https://developer.chrome.com/docs/privacy-sandbox/chips/">CHIPS</a>: Allow developers to opt-in a cookie to partitioned storage, with a separate cookie jar per top-level site. CHIPS became available in Chrome Stable in February 2023.</li>
<li><a href="https://developer.chrome.com/docs/privacy-sandbox/first-party-sets/">First-Party Sets</a>: Declare relationships among sites to allow for limited cross-site cookie access using the Storage Access API. First-Party Sets is slowly rolling out with Chrome Stable 113, this week.</li>
<li><a href="https://developer.chrome.com/docs/privacy-sandbox/fedcm/">Federated Credential Management (FedCM)</a>: Support federated identity without sharing the user's email address or other identifying information with a third-party service or website, unless the user explicitly agrees to do so. FedCM shipped in November 2022.</li>
</ul>
<h2 id="user-controls" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/shipping-privacy-sandbox/#user-controls" aria-hidden="true">#</a> Updated user controls</h2>
<p>Alongside shipping the web platform APIs, we're updating the interface in Chrome to configure the features. We're evolving this interface from the trial participation controls to be more integrated with the overall Chrome settings. Currently, we're testing an updated Ad privacy interface with a small percentage of Chrome Stable users.</p>
<p>Developers can preview these controls by setting the <code>chrome://flags/#privacy-sandbox-settings-4</code> flag. We're continuing to evaluate the updated controls and the current version may differ from what we ship by default. However, these user controls don't change how sites interact with the API surface—the methods for feature detection and calling the APIs remain the same.</p>
<figure class="screenshot">
<img alt="Ad privacy controls preview in Chrome." decoding="async" height="509" loading="lazy" sizes="(min-width: 800px) 800px, calc(100vw - 48px)" src="https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/K7udJ3mRsR3ltLwZVJnL.png?auto=format" srcset="https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/K7udJ3mRsR3ltLwZVJnL.png?auto=format&w=200 200w, https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/K7udJ3mRsR3ltLwZVJnL.png?auto=format&w=228 228w, https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/K7udJ3mRsR3ltLwZVJnL.png?auto=format&w=260 260w, https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/K7udJ3mRsR3ltLwZVJnL.png?auto=format&w=296 296w, https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/K7udJ3mRsR3ltLwZVJnL.png?auto=format&w=338 338w, https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/K7udJ3mRsR3ltLwZVJnL.png?auto=format&w=385 385w, https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/K7udJ3mRsR3ltLwZVJnL.png?auto=format&w=439 439w, https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/K7udJ3mRsR3ltLwZVJnL.png?auto=format&w=500 500w, https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/K7udJ3mRsR3ltLwZVJnL.png?auto=format&w=571 571w, https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/K7udJ3mRsR3ltLwZVJnL.png?auto=format&w=650 650w, https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/K7udJ3mRsR3ltLwZVJnL.png?auto=format&w=741 741w, https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/K7udJ3mRsR3ltLwZVJnL.png?auto=format&w=845 845w, https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/K7udJ3mRsR3ltLwZVJnL.png?auto=format&w=964 964w, https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/K7udJ3mRsR3ltLwZVJnL.png?auto=format&w=1098 1098w, https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/K7udJ3mRsR3ltLwZVJnL.png?auto=format&w=1252 1252w, https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/K7udJ3mRsR3ltLwZVJnL.png?auto=format&w=1428 1428w, https://wd.imgix.net/image/VbsHyyQopiec0718rMq2kTE1hke2/K7udJ3mRsR3ltLwZVJnL.png?auto=format&w=1600 1600w" width="800" />
</figure>
<h2 id="origin-trial" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/shipping-privacy-sandbox/#origin-trial" aria-hidden="true">#</a> Origin trial</h2>
<p>The <a href="https://developer.chrome.com/docs/privacy-sandbox/unified-origin-trial/">Privacy Sandbox Relevance and Measurement origin trial</a>
allows sites to run unified
experiments across Attribution Reporting, Protected Audience, Topics, Fenced
Frames, and Shared Storage. We intend to continue the origin trial through to
Chrome Stable 115. Testers participating in the origin trial may see
some gaps in availability or data from the APIs as Stable rolls
out, and we will provide additional guidance and details to help testers manage
this transition.</p>
<p>We'll update our <a href="https://developer.chrome.com/docs/privacy-sandbox/unified-origin-trial/">documentation</a>
as this progresses.</p>
<h2 id="enrollment" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/shipping-privacy-sandbox/#enrollment" aria-hidden="true">#</a> Enrollment and next steps</h2>
<p>Alongside GA, we want to ensure these APIs are used as intended and with
transparency. We announced a new
<a href="https://developer.chrome.com/blog/announce-enrollment-privacy-sandbox/">developer enrollment process</a> for
Privacy Sandbox relevance and measurement APIs, across Chrome and Android.
We'll share updates and instructions in the <a href="https://developer.chrome.com/docs/privacy-sandbox/enroll/">enrollment documentation</a>.</p>
<h2 id="testing" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/shipping-privacy-sandbox/#testing" aria-hidden="true">#</a> Chrome-facilitated testing modes</h2>
<p>We intend to provide Chrome-facilitated testing that allows sites to
meaningfully preview what it's like to operate in a world without third-party
cookies. This will allow us to perform more effective API testing and grow
confidence within the ecosystem, as to its readiness for third-party cookie
phase out.</p>
<p>We have worked with the CMA to ensure these testing modes align with the
testing framework (and timeline) for third parties laid out in its note on
<em><a href="https://assets.publishing.service.gov.uk/media/6363b00de90e0705a8c3544d/CMA_Experiments_note.pdf">Quantitative testing of Google's Privacy Sandbox technologies</a></em>.
As a result, the CMA anticipates that the results from testing in these modes
can be used in its assessment of the Privacy Sandbox.</p>
<p>We plan to have two modes of Chrome-facilitated testing:</p>
<ul>
<li><strong>Mode A</strong>: Ad techs can receive control and experiment labels on a portion
of traffic and use these to conduct testing and experiments.</li>
<li><strong>Mode B</strong>: Chrome globally disables third-party cookies for some portion of
all Chrome users.</li>
</ul>
<p>These details are not final, and we'll publish further implementation guidance
as we progress in Q3 2023. The current proposals are as follows.</p>
<h3 id="mode-a" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/shipping-privacy-sandbox/#mode-a" aria-hidden="true">#</a> Mode A: Opt-in testing</h3>
<p>Ad techs will be able to receive experiment labels for a portion of Chrome
traffic. An ad tech can choose to coordinate with other ad techs, for example,
to run <a href="https://developer.chrome.com/docs/privacy-sandbox/fledge/">Protected Audience</a> auctions without
third-party cookies for a consistent experiment group. Ad techs can also use
these labels for their own independent experiments and testing.</p>
<p>Chrome will not modify the state of third-party cookies for users in Mode A.
Chrome only provides the labels, as to ensure that ad techs can experiment with
consistent control and experiment groups. This means that a publisher's site
could still receive third-party cookie data for the publisher's own usage, even
if their ad tech partners are participating in the experiment.</p>
<p>We expect this to allow for meaningful experimentation, where all involved
sites and services can coordinate to ensure third-party cookies are not used at
any point within the process. We anticipate providing labels for up to 10% of
Chrome browsers via a new request header and low-entropy client hint. We
encourage anyone interested in testing to provide
<a href="https://github.com/GoogleChromeLabs/privacy-sandbox-dev-support/issues">feedback</a>
from the ecosystem on the method for accessing labels and the granularity of
labels.</p>
<p>We plan to make the opt-in testing mode available starting in Q4 2023, and
we'll continue this mode until third-party cookie deprecation.</p>
<h3 id="mode-b" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/shipping-privacy-sandbox/#mode-b" aria-hidden="true">#</a> Mode B: 1% third-party cookie deprecation</h3>
<p>Chrome will deprecate third-party cookies for up to 1% of browsers. There is no
opt-in for this mode, as it will be applied globally. There is, of
course, the possibility that some site features may be impacted if the site
hasn't yet adopted an alternative solution, such as
<a href="https://developer.chrome.com/docs/privacy-sandbox/chips/">CHIPS</a> or
<a href="https://developer.chrome.com/docs/privacy-sandbox/first-party-sets/">First-Party Sets</a>.</p>
<div class="aside aside--note">
<p>If you rely on third-party cookie data for site functionality, read our
<a href="https://developer.chrome.com/docs/privacy-sandbox/third-party-cookie-phase-out/">guide to prepare for third-party cookie phase-out</a>
to understand if CHIPS or First-Party Sets can address your needs. We've
launched a <a href="https://goo.gle/report-3pc-broken">public issue tracker</a>, where you
can report site issues resulting from third-party cookie deprecation.</p>
</div>
<p>We're working on mitigations to detect, address, and proactively alert site
owners of issues that impact user experience during this phase.</p>
<p>Additionally, we plan to provide a small fraction of traffic within Mode B that
has Privacy Sandbox relevance and measurement APIs disabled. Other APIs, like
First-Party Sets, CHIPS, FedCM, and so on, will not be disabled. We anticipate
that this combination will be helpful to establish a baseline of
performance without third-party cookies, and we're seeking
<a href="https://github.com/GoogleChromeLabs/privacy-sandbox-dev-support/labels/chrome-testing">feedback</a> on
an appropriate fraction of traffic to devote to this subset of testing.</p>
<p>We plan to deprecate 1% of third party cookies in Q1 2024, and we'll work
closely with the CMA before taking further steps to expand deprecation.</p>
<h2 id="feedback" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/shipping-privacy-sandbox/#feedback" aria-hidden="true">#</a> Engage and share feedback</h2>
<p>If you're not already participating in the relevance and measurement origin
trial, you can still <a href="https://developer.chrome.com/docs/privacy-sandbox/unified-origin-trial/">sign up and experiment</a>
with these APIs. By signing up now, you'll have a chance to get more familiar
with how these APIs work in practice and try different techniques before they
are widely available.</p>
<p>Feedback from a diverse set of stakeholders across the web ecosystem is
critical to the Privacy Sandbox initiative. Our dedicated
<a href="https://developer.chrome.com/docs/privacy-sandbox/feedback/">feedback section</a> provides an overview of the
existing public channels, where you can follow or contribute to discussion,
along with a feedback form to ensure you can always reach the Chrome team
directly.</p>
<p>If you're a developer, you can ask questions and join discussions in the
<a href="https://github.com/GoogleChromeLabs/privacy-sandbox-dev-support">Privacy Sandbox Developer Support repository</a>
on GitHub.</p>
FedCM updates: Origin trial for auto-reauthentication2023-03-09T00:00:00Zhttps://developer.chrome.com/en/blog/fedcm-auto-reauthn-origin-trial/Eiji Kitamurahttps://developer.chrome.com/authors/agektmr/<p><a href="https://developer.chrome.com/docs/privacy-sandbox/fedcm/">Federated Credential Management API (FedCM)</a> is a
web API for privacy-preserving identity federation. With identity federation, an
RP (relying party) relies on an IdP (identity provider) to provide the user an
account without requiring a new username and password.</p>
<p>FedCM is a purpose-built API that allows the browser to understand the context
in which the RP and IdP exchange information, inform the user as to the
information and privilege levels being shared and prevent unintended abuse.</p>
<h2 id="updates" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/fedcm-auto-reauthn-origin-trial/#updates" aria-hidden="true">#</a> Updates</h2>
<p>There are a few updates to Chrome's FedCM implementation:</p>
<ul>
<li>For the <a href="https://developer.chrome.com/docs/privacy-sandbox/fedcm/#id-assertion-endpoint">ID assertion
endpoint</a>, IdPs need to
check the <code>Origin</code> header (instead of the <code>Referer</code> header) to see if the
value matches the origin of the client ID.</li>
<li>A new <a href="https://developer.chrome.com/docs/web-platform/chrome-flags/">Chrome flag</a>
<code>chrome://flags/#fedcm-without-third-party-cookies</code> added. With this flag, you can
<a href="https://developer.chrome.com/docs/privacy-sandbox/fedcm/#block-third-party-cookies">test FedCM functionality in Chrome by blocking third-party
cookies</a>.</li>
</ul>
<p>For all the past updates to the API check out <a href="https://developer.chrome.com/docs/privacy-sandbox/fedcm-updates/">Federated Credential Management
API updates</a>.</p>
<p>The latest version of FedCM includes a new auto-reauthentication feature, which enables reauthenticaticating users automatically when they come back after their initial authentication using FedCM. Auto-reauthentication is available as an origin trial starting in Chrome 112.</p>
<h2 id="auto-reauthentication" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/fedcm-auto-reauthn-origin-trial/#auto-reauthentication" aria-hidden="true">#</a> Auto-reauthentication</h2>
<p>Currently, after a user has <a href="https://developer.chrome.com/docs/privacy-sandbox/fedcm/#sign-in">created a federated account on an RP with an IdP
via the FedCM</a>, the next time they visit
the website they need to go through the same steps in the user interface.
That is, they need to explicitly confirm and reauthenticate to
proceed with the sign-in flow. As one of the main
goals of FedCM is to prevent covert tracking, this user experience (UX) makes sense before the user
has created the federated account, but it becomes unnecessary and cumbersome after the user has
gone through it once. After the user grants permission to allow communication
between the RP and the IdP, there's no privacy or security benefit for
enforcing another explicit user confirmation for something that they have
already previously acknowledged. That's why Chrome is introducing a more streamlined
UX that RPs can choose for their returning users.</p>
<p><a href="https://github.com/fedidcg/FedCM/issues/429">FedCM auto-reauthentication</a>
("auto-reauthn" in short) reauthenticates users automatically (when RPs opt-in),
when they come back after their initial authentication using FedCM. "The initial
authentication" here means the user creates an account or signs into the RP's
website by tapping on the <strong>"Continue as..."</strong> button on FedCM's sign-in dialog
for the first time on the same browser instance.</p>
<figure style="width: 300px; margin: auto; margin-top: 2em;">
<img alt="A dialog the user taps on to create an account or to authenticate." class="screenshot" decoding="async" height="566" loading="lazy" sizes="(min-width: 800px) 800px, calc(100vw - 48px)" src="https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/jQvYTB6sHw5jGshyRxn6.png?auto=format" srcset="https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/jQvYTB6sHw5jGshyRxn6.png?auto=format&w=200 200w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/jQvYTB6sHw5jGshyRxn6.png?auto=format&w=228 228w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/jQvYTB6sHw5jGshyRxn6.png?auto=format&w=260 260w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/jQvYTB6sHw5jGshyRxn6.png?auto=format&w=296 296w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/jQvYTB6sHw5jGshyRxn6.png?auto=format&w=338 338w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/jQvYTB6sHw5jGshyRxn6.png?auto=format&w=385 385w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/jQvYTB6sHw5jGshyRxn6.png?auto=format&w=439 439w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/jQvYTB6sHw5jGshyRxn6.png?auto=format&w=500 500w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/jQvYTB6sHw5jGshyRxn6.png?auto=format&w=571 571w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/jQvYTB6sHw5jGshyRxn6.png?auto=format&w=650 650w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/jQvYTB6sHw5jGshyRxn6.png?auto=format&w=741 741w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/jQvYTB6sHw5jGshyRxn6.png?auto=format&w=845 845w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/jQvYTB6sHw5jGshyRxn6.png?auto=format&w=964 964w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/jQvYTB6sHw5jGshyRxn6.png?auto=format&w=1098 1098w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/jQvYTB6sHw5jGshyRxn6.png?auto=format&w=1252 1252w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/jQvYTB6sHw5jGshyRxn6.png?auto=format&w=1428 1428w, https://wd.imgix.net/image/YLflGBAPWecgtKJLqCJHSzHqe2J2/jQvYTB6sHw5jGshyRxn6.png?auto=format&w=1600 1600w" width="800" />
<figcaption>A dialog the user taps on to create an account or to authenticate.</figcaption>
</figure>
<p>The RP can request auto-reauthn by calling <code>navigator.credentials.get()</code> with <code>autoReauthn: true</code>.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> cred <span class="token operator">=</span> <span class="token keyword">await</span> navigator<span class="token punctuation">.</span>credentials<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br /> <span class="token literal-property property">identity</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">providers</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span><br /> <span class="token literal-property property">configURL</span><span class="token operator">:</span> <span class="token string">"https://idp.example/fedcm.json"</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">clientId</span><span class="token operator">:</span> <span class="token string">"1234"</span><span class="token punctuation">,</span><br /> <span class="token punctuation">}</span><span class="token punctuation">]</span><span class="token punctuation">,</span><br /> <span class="token comment">// NOTE: We are exploring different API options to expose this</span><br /> <span class="token comment">// functionality here:</span><br /> <span class="token comment">// https://github.com/fedidcg/FedCM/issues/429#issuecomment-1426358523</span><br /> <span class="token comment">// You should expect that, as a result of the origin trial, we'll</span><br /> <span class="token comment">// learn more from developers and browser vendors what works best here.</span><br /> <span class="token literal-property property">autoReauthn</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token comment">// default to false</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /></code></pre>
<p>With this call, auto-reauthentication happens under the following conditions:</p>
<ul>
<li>FedCM is available to use. For example, the user has not disabled FedCM either
globally or for the RP.</li>
<li>The user used only one FedCM account to sign into the website on this browser.</li>
<li>The user is signed into the IdP with that account.</li>
<li>The auto-reauthn didn't happen within the last 10 minutes.</li>
</ul>
<p>When the above conditions are met, an attempt to automatically reauthenticate the
user starts as soon as the FedCM <code>navigator.credentials.get()</code> is invoked.</p>
<figure style="width: 300px; margin: auto; margin-top: 2em;">
<video autoplay="" class="screenshot" loop="" muted=""> <source src="https://storage.googleapis.com/web-dev-uploads/video/YLflGBAPWecgtKJLqCJHSzHqe2J2/eYnQxoDMNh4rpsyjcsQI.mp4" type="video/mp4" /> </video>
<figcaption>A user is auto-reauthenticating to an RP using FedCM</figcaption>
</figure>
<div class="aside aside--note">
<p>To avoid a frustrating experience of auto-reauthentication immediately after a
user has signed out, it's recommended to design a sign-out flow that would prevent that.</p>
<p>FedCM has a 10 minute quiet period after an auto-reauthentication to
prevent this behavior. We are also exploring other approaches to achieve this
such as using
<a href="https://developer.mozilla.org/docs/Web/API/CredentialsContainer/preventSilentAccess"><code>CredentialsContainer.preventSilentAccess()</code></a>.</p>
</div>
<h2 id="try-it-out" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/fedcm-auto-reauthn-origin-trial/#try-it-out" aria-hidden="true">#</a> Try it out</h2>
<p>You can try FedCM auto-reauthentication locally by turning on <a href="https://developer.chrome.com/docs/web-platform/chrome-flags/">a Chrome
flag</a> <code>chrome://flags#fedcm-auto-re-authn</code> on
Chrome 112 or later.</p>
<p>For testing purposes, you can reset the 10 minute quiet period by removing
browser data.</p>
<ol>
<li>Navigate to <code>chrome://history</code>.</li>
<li>In the search history box, enter the origin of the RP.</li>
<li>Click the three-dot icon ⋮ and select <strong>Remove from history</strong>.</li>
<li>Restart Chrome.</li>
</ol>
<h2 id="participate-in-the-origin-trial" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/fedcm-auto-reauthn-origin-trial/#participate-in-the-origin-trial" aria-hidden="true">#</a> Participate in the origin trial</h2>
<p>You can also enable the feature on your website by joining <a href="https://developer.chrome.com/docs/web-platform/third-party-origin-trials/">the third-party
origin trial</a> available from Chrome 112
through Chrome 114.</p>
<p>Origin trials allow you to try new features and give feedback on their
usability, practicality, and effectiveness to the web standards community. For
more information, see the <a href="https://github.com/GoogleChrome/OriginTrials/blob/gh-pages/developer-guide.md">Origin Trials Guide for Web Developers</a>.
To sign up for this or another origin trial, visit the <a href="https://developers.chrome.com/origintrials/#/trials/active">registration page</a>.</p>
<p>To register for <a href="https://developer.chrome.com/docs/web-platform/third-party-origin-trials/">the third-party origin
trial</a> and activate the feature
on third-parties:</p>
<ol>
<li>Go to the <a href="https://developer.chrome.com/origintrials/#/view_trial/2426314299245854721">origin trial registration page</a>.</li>
<li>Click the <strong>Register</strong> button and fill out the form to request a token</li>
<li>Enter the serving origin as <strong>Web Origin</strong>.</li>
<li>Check <strong>Third-party matching</strong> to inject the token with JavaScript on other origins.</li>
<li>Click <strong>Submit</strong>.</li>
<li>Embed the issued token on a third-party.</li>
</ol>
<p>To embed the token to a third-party, add the following code to your JavaScript
library or SDK served from the registered website's origin.</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">const</span> tokenElement <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">'meta'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br />tokenElement<span class="token punctuation">.</span>httpEquiv <span class="token operator">=</span> <span class="token string">'origin-trial'</span><span class="token punctuation">;</span><br />tokenElement<span class="token punctuation">.</span>content <span class="token operator">=</span> <span class="token string">'TOKEN_GOES_HERE'</span><span class="token punctuation">;</span><br />document<span class="token punctuation">.</span>head<span class="token punctuation">.</span><span class="token function">appendChild</span><span class="token punctuation">(</span>tokenElement<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Replace <code>TOKEN_GOES_HERE</code> with your own token.</p>
<div class="aside aside--note">
<p>If FedCM is executed directly by an RP without loading an IdP's scripts, the RP
must register their origin by themselves for the origin trial.</p>
</div>
<h2 id="engage-and-share-feedback" tabindex="-1"><a class="heading-link" href="https://developer.chrome.com/en/blog/fedcm-auto-reauthn-origin-trial/#engage-and-share-feedback" aria-hidden="true">#</a> Engage and share feedback</h2>
<p>If you have feedback or encounter any issues during testing, you can share them at <a href="http://crbug.com/">crbug.com</a>
under the <strong>Blink>Identity>FedCM</strong> component.</p>
<p>Photo by <a href="https://unsplash.com/es/@adventureregistry?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Alex
Perz</a>
on
<a href="https://unsplash.com/photos/ysgELOy_t0c?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a></p>