Skip to content

Commit

Permalink
Merge pull request #284 from GoogleChrome/discarded-nav-type
Browse files Browse the repository at this point in the history
Support 'restore' as a navigationType
  • Loading branch information
philipwalton authored Nov 15, 2022
2 parents 861d065 + 70e4d78 commit 89affdf
Show file tree
Hide file tree
Showing 11 changed files with 158 additions and 6 deletions.
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -697,10 +697,13 @@ interface Metric {
* The type of navigation
*
* Navigation Timing API (or `undefined` if the browser doesn't
* support that API). For pages that are restored from the bfcache, this
* value will be 'back-forward-cache'.
* support that API).
* For pages that are restored from the bfcache, this value will
* be 'back-forward-cache'.
* For pages that are restored after being discarded, this value will
* be 'restore'.
*/
navigationType: 'navigate' | 'reload' | 'back-forward' | 'back-forward-cache' | 'prerender';
navigationType: 'navigate' | 'reload' | 'back-forward' | 'back-forward-cache' | 'prerender' | 'restore';
}
```

Expand Down
2 changes: 2 additions & 0 deletions src/lib/initMetric.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ export const initMetric = (name: Metric['name'], value?: number): Metric => {
} else if (navEntry) {
if (document.prerendering || getActivationStart() > 0) {
navigationType = 'prerender';
} else if (document.wasDiscarded) {
navigationType = 'restore';
} else {
navigationType =
navEntry.type.replace(/_/g, '-') as Metric['navigationType'];
Expand Down
6 changes: 4 additions & 2 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,11 @@ interface PerformanceEntryMap {

// Update built-in types to be more accurate.
declare global {
// https://wicg.github.io/nav-speculation/prerendering.html#document-prerendering
interface Document {
prerendering?: boolean
// https://wicg.github.io/nav-speculation/prerendering.html#document-prerendering
prerendering?: boolean;
// https://wicg.github.io/page-lifecycle/#sec-api
wasDiscarded?: boolean;
}

interface Performance {
Expand Down
2 changes: 1 addition & 1 deletion src/types/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export interface Metric {
* support that API). For pages that are restored from the bfcache, this
* value will be 'back-forward-cache'.
*/
navigationType: 'navigate' | 'reload' | 'back-forward' | 'back-forward-cache' | 'prerender';
navigationType: 'navigate' | 'reload' | 'back-forward' | 'back-forward-cache' | 'prerender' | 'restore';
}

/**
Expand Down
21 changes: 21 additions & 0 deletions test/e2e/onCLS-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -632,6 +632,27 @@ describe('onCLS()', async function() {
assert.strictEqual(cls.navigationType, 'back-forward-cache');
});

it('reports restore as nav type for wasDiscarded', async function() {
if (!browserSupportsCLS) this.skip();

await browser.url('/test/cls?wasDiscarded=1');

// Wait until all images are loaded and rendered, then change to hidden.
await imagesPainted();
await stubVisibilityChange('hidden');

await beaconCountIs(1);
const [cls] = await getBeacons();

assert(cls.value >= 0);
assert(cls.id.match(/^v3-\d+-\d+$/));
assert.strictEqual(cls.name, 'CLS');
assert.strictEqual(cls.value, cls.delta);
assert.strictEqual(cls.rating, 'good');
assert.strictEqual(cls.entries.length, 2);
assert.strictEqual(cls.navigationType, 'restore');
});

describe('attribution', function() {
it('includes attribution data on the metric object', async function() {
if (!browserSupportsCLS) this.skip();
Expand Down
17 changes: 17 additions & 0 deletions test/e2e/onFCP-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,23 @@ describe('onFCP()', async function() {
assert.strictEqual(fcp2.navigationType, 'back-forward-cache');
});

it('reports restore as nav type for wasDiscarded', async function() {
if (!browserSupportsFCP) this.skip();

await browser.url('/test/fcp?wasDiscarded=1');

await beaconCountIs(1);

const [fcp] = await getBeacons();
assert(fcp.value >= 0);
assert(fcp.id.match(/^v3-\d+-\d+$/));
assert.strictEqual(fcp.name, 'FCP');
assert.strictEqual(fcp.value, fcp.delta);
assert.strictEqual(fcp.rating, 'good');
assert.strictEqual(fcp.entries.length, 1);
assert.strictEqual(fcp.navigationType, 'restore');
});

describe('attribution', function() {
it('includes attribution data on the metric object', async function() {
if (!browserSupportsFCP) this.skip();
Expand Down
22 changes: 22 additions & 0 deletions test/e2e/onFID-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,28 @@ describe('onFID()', async function() {
assert.match(fid2.entries[0].name, /(mouse|pointer)down/);
});

it('reports restore as nav type for wasDiscarded', async function() {
if (!browserSupportsFID) this.skip();

await browser.url('/test/fid?wasDiscarded=1');

// Click on the <h1>.
const h1 = await $('h1');
await h1.click();

await beaconCountIs(1);

const [fid] = await getBeacons();
assert(fid.value >= 0);
assert(fid.id.match(/^v3-\d+-\d+$/));
assert.strictEqual(fid.name, 'FID');
assert.strictEqual(fid.value, fid.delta);
assert.strictEqual(fid.rating, 'good');
assert.strictEqual(fid.navigationType, 'restore');
assert.match(fid.entries[0].name, /(mouse|pointer)down/);
});


describe('attribution', function() {
it('includes attribution data on the metric object', async function() {
if (!browserSupportsFID) this.skip();
Expand Down
24 changes: 24 additions & 0 deletions test/e2e/onINP-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,30 @@ describe('onINP()', async function() {
assert.strictEqual(beacons.length, 0);
});

it('reports restore as nav type for wasDiscarded', async function() {
if (!browserSupportsINP) this.skip();

await browser.url('/test/inp?click=100&wasDiscarded=1');

const h1 = await $('h1');
await h1.click();

await stubVisibilityChange('hidden');

await beaconCountIs(1);

const [inp] = await getBeacons();
assert(inp.value >= 0);
assert(inp.id.match(/^v3-\d+-\d+$/));
assert.strictEqual(inp.name, 'INP');
assert.strictEqual(inp.value, inp.delta);
assert.strictEqual(inp.rating, 'good');
assert(containsEntry(inp.entries, 'click', 'h1'));
assert(interactionIDsMatch(inp.entries));
assert(inp.entries[0].interactionId > 0);
assert.strictEqual(inp.navigationType, 'restore');
});

describe('attribution', function() {
it('includes attribution data on the metric object', async function() {
if (!browserSupportsINP) this.skip();
Expand Down
24 changes: 24 additions & 0 deletions test/e2e/onLCP-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,30 @@ describe('onLCP()', async function() {
assert.strictEqual(lcp2.navigationType, 'back-forward-cache');
});

it('reports restore as nav type for wasDiscarded', async function() {
if (!browserSupportsLCP) this.skip();

await browser.url('/test/lcp?wasDiscarded=1');

// Wait until all images are loaded and fully rendered.
await imagesPainted();

// Load a new page to trigger the hidden state.
await browser.url('about:blank');

await beaconCountIs(1);

const [lcp] = await getBeacons();

assert(lcp.value > 0); // Greater than the image load delay.
assert(lcp.id.match(/^v3-\d+-\d+$/));
assert.strictEqual(lcp.name, 'LCP');
assert.strictEqual(lcp.value, lcp.delta);
assert.strictEqual(lcp.rating, 'good');
assert.strictEqual(lcp.entries.length, 1);
assert.strictEqual(lcp.navigationType, 'restore');
});

describe('attribution', function() {
it('includes attribution data on the metric object', async function() {
if (!browserSupportsLCP) this.skip();
Expand Down
18 changes: 18 additions & 0 deletions test/e2e/onTTFB-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,24 @@ describe('onTTFB()', async function() {
}
});

it('reports restore as nav type for wasDiscarded', async function() {
await browser.url('/test/ttfb?wasDiscarded=1');

const ttfb = await getTTFBBeacon();

assert(ttfb.value >= 0);
assert(ttfb.value >= ttfb.entries[0].requestStart);
assert(ttfb.value <= ttfb.entries[0].loadEventEnd);
assert(ttfb.id.match(/^v3-\d+-\d+$/));
assert.strictEqual(ttfb.name, 'TTFB');
assert.strictEqual(ttfb.value, ttfb.delta);
assert.strictEqual(ttfb.rating, 'good');
assert.strictEqual(ttfb.navigationType, 'restore');
assert.strictEqual(ttfb.entries.length, 1);

assertValidEntry(ttfb.entries[0]);
});

describe('attribution', function() {
it('includes attribution data on the metric object', async function() {
await browser.url('/test/ttfb?attribution=1');
Expand Down
19 changes: 19 additions & 0 deletions test/views/layout.njk
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,21 @@
});
}
/**
* @return {Promise<void>}
*/
self.__stubWasDiscarded = () => {
return new Promise((resolve) => {
// Only stub if the page isn't actually discarded.
if (!document.wasDiscarded) {
Object.defineProperty(document, 'wasDiscarded', {
value: true,
configurable: true,
});
}
});
}
// Uncomment to stub running in a browser that doesn't support performance APIs
// (e.g. some version of Opera support this).
// delete self.performance;
Expand Down Expand Up @@ -131,6 +146,10 @@
if (params.has('prerender')) {
self.__stubPrerender();
}
if (params.has('wasDiscarded')) {
self.__stubWasDiscarded();
}
}());
</script>
{% if polyfill %}
Expand Down

0 comments on commit 89affdf

Please sign in to comment.