Skip to content

Commit e9b8c47

Browse files
authored
core(fps): make lhId less dependent on chrome internals (#14272)
1 parent 371c671 commit e9b8c47

File tree

4 files changed

+37
-7
lines changed

4 files changed

+37
-7
lines changed

core/gather/driver/execution-context.js

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@ class ExecutionContext {
1616

1717
/** @type {number|undefined} */
1818
this._executionContextId = undefined;
19+
/**
20+
* Marks how many execution context ids have been created, for purposes of having a unique
21+
* value (that doesn't expose the actual execution context id) to
22+
* use for __lighthouseExecutionContextUniqueIdentifier.
23+
* @type {number}
24+
*/
25+
this._executionContextIdentifiersCreated = 0;
1926

2027
// We use isolated execution contexts for `evaluateAsync` that can be destroyed through navigation
2128
// and other page actions. Cleanup our relevant bookkeeping as we see those events.
@@ -64,10 +71,10 @@ class ExecutionContext {
6471
});
6572

6673
this._executionContextId = isolatedWorldResponse.executionContextId;
74+
this._executionContextIdentifiersCreated++;
6775
return isolatedWorldResponse.executionContextId;
6876
}
6977

70-
7178
/**
7279
* Evaluate an expression in the given execution context; an undefined contextId implies the main
7380
* page without isolation.
@@ -82,15 +89,25 @@ class ExecutionContext {
8289
this._session.getNextProtocolTimeout() :
8390
60000;
8491

92+
// `__lighthouseExecutionContextUniqueIdentifier` is only used by the FullPageScreenshot gatherer.
93+
// See `getNodeDetails` in page-functions.
94+
const uniqueExecutionContextIdentifier = contextId === undefined ?
95+
undefined :
96+
this._executionContextIdentifiersCreated;
97+
8598
const evaluationParams = {
8699
// We need to explicitly wrap the raw expression for several purposes:
87100
// 1. Ensure that the expression will be a native Promise and not a polyfill/non-Promise.
88101
// 2. Ensure that errors in the expression are captured by the Promise.
89102
// 3. Ensure that errors captured in the Promise are converted into plain-old JS Objects
90103
// so that they can be serialized properly b/c JSON.stringify(new Error('foo')) === '{}'
104+
//
105+
// `__lighthouseExecutionContextUniqueIdentifier` is only used by the FullPageScreenshot gatherer.
106+
// See `getNodeDetails` in page-functions.
91107
expression: `(function wrapInNativePromise() {
92108
${ExecutionContext._cachedNativesPreamble};
93-
globalThis.__lighthouseExecutionContextId = ${contextId};
109+
globalThis.__lighthouseExecutionContextUniqueIdentifier =
110+
${uniqueExecutionContextIdentifier};
94111
return new Promise(function (resolve) {
95112
return Promise.resolve()
96113
.then(_ => ${expression})

core/lib/page-functions.js

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,18 @@ function getNodeDetails(element) {
498498
const selector = getNodeSelector(element);
499499

500500
// Create an id that will be unique across all execution contexts.
501+
//
502+
// Made up of 3 components:
503+
// - prefix unique to specific execution context
504+
// - nth unique node seen by this function for this execution context
505+
// - node tagName
506+
//
507+
// Every page load only has up to two associated contexts - the page context
508+
// (denoted as `__lighthouseExecutionContextUniqueIdentifier` being undefined)
509+
// and the isolated context. The id must be unique to distinguish gatherers running
510+
// on different page loads that identify the same logical element, for purposes
511+
// of the full page screenshot node lookup; hence the prefix.
512+
//
501513
// The id could be any arbitrary string, the exact value is not important.
502514
// For example, tagName is added only because it might be useful for debugging.
503515
// But execution id and map size are added to ensure uniqueness.
@@ -507,9 +519,9 @@ function getNodeDetails(element) {
507519
let lhId = window.__lighthouseNodesDontTouchOrAllVarianceGoesAway.get(element);
508520
if (!lhId) {
509521
lhId = [
510-
window.__lighthouseExecutionContextId !== undefined ?
511-
window.__lighthouseExecutionContextId :
512-
'page',
522+
window.__lighthouseExecutionContextUniqueIdentifier === undefined ?
523+
'page' :
524+
window.__lighthouseExecutionContextUniqueIdentifier,
513525
window.__lighthouseNodesDontTouchOrAllVarianceGoesAway.size,
514526
element.tagName,
515527
].join('-');

core/test/gather/driver/execution-context-test.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,8 @@ describe('.evaluate', () => {
218218
const URL = globalThis.__nativeURL || globalThis.URL;
219219
const performance = globalThis.__nativePerformance || globalThis.performance;
220220
const fetch = globalThis.__nativeFetch || globalThis.fetch;
221-
globalThis.__lighthouseExecutionContextId = undefined;
221+
globalThis.__lighthouseExecutionContextUniqueIdentifier =
222+
undefined;
222223
return new Promise(function (resolve) {
223224
return Promise.resolve()
224225
.then(_ => (() => {

types/externs.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ declare global {
3030

3131
/** Used by FullPageScreenshot gatherer. */
3232
__lighthouseNodesDontTouchOrAllVarianceGoesAway: Map<Element, string>;
33-
__lighthouseExecutionContextId?: number;
33+
__lighthouseExecutionContextUniqueIdentifier?: number;
3434

3535
/** Injected into the page when the `--debug` flag is used. */
3636
continueLighthouseRun(): void;

0 commit comments

Comments
 (0)