Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

core(driver): attach to worker targets #14212

Merged
merged 15 commits into from
Sep 11, 2023
9 changes: 7 additions & 2 deletions cli/test/smokehouse/test-definitions/oopif-scripts.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,13 @@ const expectations = {
{url: 'http://localhost:10503/simple-script.js', resourceType: 'Fetch'},
{url: 'http://localhost:10200/simple-worker.js'},
{url: 'http://localhost:10503/simple-worker.js'},
// Several requests are emitted in workers but Lighthouse doesn't capture them.
// https://github.com/GoogleChrome/lighthouse/issues/14211
// Requests from worker targets
{url: 'http://localhost:10200/simple-worker.mjs'},
{url: 'http://localhost:10503/simple-worker.mjs'},
{url: 'http://localhost:10200/simple-script.js?esm', resourceType: 'Script'},
{url: 'http://localhost:10503/simple-script.js?esm', resourceType: 'Script'},
{url: 'http://localhost:10200/simple-script.js?importScripts', resourceType: 'Other'},
{url: 'http://localhost:10503/simple-script.js?importScripts', resourceType: 'Other'},
],
// Ensure the above is exhaustive (except for favicon, which won't be fetched in devtools/LR).
_excludes: [
Expand Down
1 change: 1 addition & 0 deletions core/computed/page-dependency-graph.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ class PageDependencyGraph {

networkRecords.forEach(record => {
if (IGNORED_MIME_TYPES_REGEX.test(record.mimeType)) return;
if (record.sessionTargetType === 'worker') return;

// Network record requestIds can be duplicated for an unknown reason
// Suffix all subsequent records with `:duplicate` until it's unique
Expand Down
26 changes: 18 additions & 8 deletions core/gather/driver/target-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,16 @@ class TargetManager extends ProtocolEventEmitter {
throw new Error(`session ${sessionId} not found`);
}

/**
* @param {string} targetType
* @return {targetType is LH.Protocol.TargetType}
*/
_isAcceptedTargetType(targetType) {
return targetType === 'page' ||
targetType === 'iframe' ||
targetType === 'worker';
}

/**
* Returns the root session.
* @return {LH.Gatherer.ProtocolSession}
Expand All @@ -116,19 +126,19 @@ class TargetManager extends ProtocolEventEmitter {
const newSession = new ProtocolSession(cdpSession);

try {
const target = await newSession.sendCommand('Target.getTargetInfo').catch(() => null);
const targetType = target?.targetInfo?.type;
const hasValidTargetType = targetType === 'page' || targetType === 'iframe';
const {targetInfo} = await newSession.sendCommand('Target.getTargetInfo');
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before M110 this command would throw an error on worker targets. With this patch we would require a Chrome version >=M110 which is the current stable.

const targetType = targetInfo.type;

// TODO: should detach from target in this case?
// See pptr: https://github.com/puppeteer/puppeteer/blob/733cbecf487c71483bee8350e37030edb24bc021/src/common/Page.ts#L495-L526
if (!target || !hasValidTargetType) return;
if (!this._isAcceptedTargetType(targetType)) return;

// No need to continue if target has already been seen.
const targetId = target.targetInfo.targetId;
const targetId = targetInfo.targetId;
if (this._targetIdToTargets.has(targetId)) return;

newSession.setTargetInfo(target.targetInfo);
const targetName = target.targetInfo.url || target.targetInfo.targetId;
newSession.setTargetInfo(targetInfo);
const targetName = targetInfo.url || targetInfo.targetId;
log.verbose('target-manager', `target ${targetName} attached`);

const trueProtocolListener = this._getProtocolEventListener(targetType, newSession.id());
Expand All @@ -139,7 +149,7 @@ class TargetManager extends ProtocolEventEmitter {
cdpSession.on('sessionattached', this._onSessionAttached);

const targetWithSession = {
target: target.targetInfo,
target: targetInfo,
cdpSession,
session: newSession,
protocolListener,
Expand Down
4 changes: 2 additions & 2 deletions core/gather/gatherers/dobetterweb/optimized-images.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ class OptimizedImages extends BaseGatherer {
/** @type {Set<string>} */
const seenUrls = new Set();
return networkRecords.reduce((prev, record) => {
// Skip records that we've seen before, never finished, or came from OOPIFs.
if (seenUrls.has(record.url) || !record.finished || record.isOutOfProcessIframe) {
// Skip records that we've seen before, never finished, or came from OOPIFs/web workers.
if (seenUrls.has(record.url) || !record.finished || record.sessionTargetType !== 'page') {
return prev;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class ResponseCompression extends BaseGatherer {
const unoptimizedResponses = [];

networkRecords.forEach(record => {
if (record.isOutOfProcessIframe) return;
if (record.sessionTargetType !== 'page') return;

const mimeType = record.mimeType;
const resourceType = record.resourceType || NetworkRequest.TYPES.Other;
Expand Down
2 changes: 1 addition & 1 deletion core/gather/gatherers/script-elements.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@

const scriptRecords = networkRecords
.filter(record => record.resourceType === NetworkRequest.TYPES.Script)
.filter(record => !record.isOutOfProcessIframe);
.filter(record => record.sessionTargetType === 'page');

Check warning on line 68 in core/gather/gatherers/script-elements.js

View check run for this annotation

Codecov / codecov/patch

core/gather/gatherers/script-elements.js#L68

Added line #L68 was not covered by tests
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are several places isOutOfProcessIframe is used where it could make more sense to use a sessionTargetType === 'page' now that we have worker requests as well. This is just the only place that caused a test failure.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update: I changed the other usages of isOutOfProcessIframe too. We don't use isOutOfProcessIframe anywhere now.


for (let i = 0; i < scriptRecords.length; i++) {
const record = scriptRecords[i];
Expand Down
26 changes: 24 additions & 2 deletions core/test/computed/page-dependency-graph-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,19 @@ function createRequest(
url,
networkRequestTime = 0,
initiator = null,
resourceType = NetworkRequest.TYPES.Document
resourceType = NetworkRequest.TYPES.Document,
sessionTargetType = 'page'
) {
const networkEndTime = networkRequestTime + 50;
return {requestId, url, networkRequestTime, networkEndTime, initiator, resourceType};
return {
requestId,
url,
networkRequestTime,
networkEndTime,
initiator,
resourceType,
sessionTargetType,
};
}

const TOPLEVEL_TASK_NAME = 'TaskQueueManager::ProcessTaskFromWorkQueue';
Expand Down Expand Up @@ -105,6 +114,19 @@ describe('PageDependencyGraph computed artifact:', () => {
}
});

it('should ignore worker requests', () => {
const workerRequest = createRequest(4, 'https://example.com/worker.js', 0, null, 'Script', 'worker');
const recordsWithWorker = [
...networkRecords,
workerRequest,
];

const networkNodeOutput = PageDependencyGraph.getNetworkNodeOutput(recordsWithWorker);

expect(networkNodeOutput.nodes).toHaveLength(3);
expect(networkNodeOutput.nodes.map(node => node.record)).not.toContain(workerRequest);
});

it('should index nodes by ID', () => {
const networkNodeOutput = PageDependencyGraph.getNetworkNodeOutput(networkRecords);
const indexedById = networkNodeOutput.idToNodeMap;
Expand Down
7 changes: 4 additions & 3 deletions core/test/gather/driver/target-manager-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,8 @@ describe('TargetManager', () => {
expect(sendMock.findAllInvocations('Runtime.runIfWaitingForDebugger')).toHaveLength(4);
});

it('should ignore non-frame targets', async () => {
targetInfo.type = 'worker';
it('should ignore targets that are not frames or web workers', async () => {
targetInfo.type = 'service_worker';
sendMock
.mockResponse('Target.getTargetInfo', {targetInfo})
.mockResponse('Target.setAutoAttach');
Expand Down Expand Up @@ -179,7 +179,8 @@ describe('TargetManager', () => {
});

it('should resume the target when finished', async () => {
sendMock.mockResponse('Target.getTargetInfo', {});
targetInfo.type = 'service_worker';
sendMock.mockResponse('Target.getTargetInfo', {targetInfo});
await targetManager.enable();

const invocations = sendMock.findAllInvocations('Runtime.runIfWaitingForDebugger');
Expand Down
10 changes: 10 additions & 0 deletions core/test/gather/gatherers/dobetterweb/optimized-images-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,16 @@ describe('Optimized images', () => {
transferSize: 20000,
finished: true,
},
{
requestId: '11',
url: 'http://gmail.com/image-worker.jpg',
mimeType: 'image/jpeg',
resourceSize: 15000,
transferSize: 20000,
resourceType: 'Image',
finished: true,
sessionTargetType: 'worker', // ignore for being a worker
},
]);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const networkRecords = [
}],
content: 'aaabbbccc',
finished: true,
sessionTargetType: 'page',
},
{
url: 'http://google.com/index.css',
Expand All @@ -42,6 +43,7 @@ const networkRecords = [
responseHeaders: [],
content: 'abcabc',
finished: true,
sessionTargetType: 'page',
},
{
url: 'http://google.com/index.json',
Expand All @@ -54,6 +56,7 @@ const networkRecords = [
responseHeaders: [],
content: '1234567',
finished: true,
sessionTargetType: 'page',
},
{
url: 'http://google.com/index-oopif.json',
Expand All @@ -79,6 +82,7 @@ const networkRecords = [
responseHeaders: [],
content: '1234567',
finished: true,
sessionTargetType: 'page',
},
{
url: 'http://google.com/other.json',
Expand All @@ -91,6 +95,7 @@ const networkRecords = [
responseHeaders: [],
content: '1234567',
finished: false, // ignore for not finishing
sessionTargetType: 'page',
},
{
url: 'http://google.com/index.jpg',
Expand All @@ -103,6 +108,7 @@ const networkRecords = [
responseHeaders: [],
content: 'aaaaaaaaaa',
finished: true,
sessionTargetType: 'page',
},
{
url: 'http://google.com/helloworld.mp4',
Expand All @@ -115,6 +121,20 @@ const networkRecords = [
responseHeaders: [],
content: 'bbbbbbbb',
finished: true,
sessionTargetType: 'page',
},
{
url: 'http://google.com/index-worker.json',
statusCode: 200,
mimeType: 'application/json',
requestId: 28,
resourceSize: 7,
transferSize: 8,
resourceType: 'XHR',
responseHeaders: [],
content: '1234567',
finished: true,
sessionTargetType: 'worker', // ignore for being from a worker
},
].map((record) => Object.assign(new NetworkRequest(), record));

Expand Down Expand Up @@ -172,6 +192,7 @@ describe('Optimized responses', () => {
responseHeaders: [],
content: 'aaaaaaaaaa',
finished: true,
sessionTargetType: 'page',
},
{
url: 'http://google.com/chrome-extension.css',
Expand All @@ -183,6 +204,7 @@ describe('Optimized responses', () => {
responseHeaders: [],
content: 'aaaaaaaaaa',
finished: true,
sessionTargetType: 'page',
},
];

Expand Down
4 changes: 3 additions & 1 deletion core/test/gather/gatherers/script-elements-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const {NetworkRequest} = await import('../../../lib/network-request.js');
function mockRecord(partial) {
const request = new NetworkRequest();
request.resourceType = NetworkRequest.TYPES.Script;
request.sessionTargetType = 'page';
return Object.assign(request, partial);
}

Expand Down Expand Up @@ -80,11 +81,12 @@ describe('_getArtifact', () => {
]);
});

it('ignore OOPIF records', async () => {
it('ignore OOPIF and worker records', async () => {
networkRecords = [
mainDocument,
mockRecord({url: 'https://example.com/script.js', requestId: '1'}),
mockRecord({url: 'https://oopif.com/script.js', requestId: '2', sessionTargetType: 'iframe'}),
mockRecord({url: 'https://oopif.com/worker.js', requestId: '2', sessionTargetType: 'worker'}),
];
// OOPIF would not produce script element
scriptElements = [
Expand Down
27 changes: 26 additions & 1 deletion core/test/scenarios/api-test-pptr.js
Original file line number Diff line number Diff line change
Expand Up @@ -180,27 +180,52 @@ describe('Individual modes API', function() {
.map((r) => ({url: r.url, sessionTargetType: r.sessionTargetType}))
// @ts-expect-error
.sort((a, b) => a.url.localeCompare(b.url));
expect(networkRequests).toHaveLength(4);
expect(networkRequests).toHaveLength(10);
expect(networkRequests.filter(r => r.sessionTargetType === 'page')).toHaveLength(2);
expect(networkRequests.filter(r => r.sessionTargetType === 'iframe')).toHaveLength(2);
expect(networkRequests.filter(r => r.sessionTargetType === 'worker')).toHaveLength(6);
expect(networkRequests).toMatchInlineSnapshot(`
Array [
Object {
"sessionTargetType": "page",
"url": "http://localhost:10200/simple-script.js",
},
Object {
"sessionTargetType": "worker",
"url": "http://localhost:10200/simple-script.js?esm",
},
Object {
"sessionTargetType": "worker",
"url": "http://localhost:10200/simple-script.js?importScripts",
},
Object {
"sessionTargetType": "page",
"url": "http://localhost:10200/simple-worker.js",
},
Object {
"sessionTargetType": "worker",
"url": "http://localhost:10200/simple-worker.mjs",
},
Object {
"sessionTargetType": "iframe",
"url": "http://localhost:10503/simple-script.js",
},
Object {
"sessionTargetType": "worker",
"url": "http://localhost:10503/simple-script.js?esm",
},
Object {
"sessionTargetType": "worker",
"url": "http://localhost:10503/simple-script.js?importScripts",
},
Object {
"sessionTargetType": "iframe",
"url": "http://localhost:10503/simple-worker.js",
},
Object {
"sessionTargetType": "worker",
"url": "http://localhost:10503/simple-worker.mjs",
},
]
`);

Expand Down
Loading