mirror of
https://github.com/ChromeDevTools/chrome-devtools-mcp.git
synced 2026-09-14 19:45:30 +08:00
fix: do not enable the DevTools frontend Audits subscription (#2625)
## Summary Puppeteer already subscribes to `Audits.issueAdded`. `overrideDevToolsGlobals` did not stop the DevTools frontend from enabling its own `Audits` domain, so a second consumer of the same stream was created on session setup. Against a target holding a large retained issue backlog, that second subscription replays the entire backlog through `IssuesManager`, logging `No handler registered for issue code PerformanceIssue` per unsupported entry and delaying unrelated page work. This stubs `Audits.invoke_enable` on the agent prototype exactly the way the Network emulation commands are already stubbed, so the redundant subscription is never enabled. The four identical no-op command bodies are collapsed into one `noopAgentCommand` helper while adding the fourth. ## Why this matters Closes #2556 The guard added in #2535 covers `PageEventSubscriber.#onIssueAdded`, the collector's own subscription. It does not cover the frontend's `IssuesManager` path, which is the other consumer of the same CDP event, so the warning and the backlog replay both survive it. Fixing it at the `invoke_enable` boundary means the frontend never receives the replay at all, rather than filtering each issue after the fact. ## Testing `node scripts/test.js tests/devtools/DevtoolsUtils.test.ts` -> 20 passing. `npm run build` and `npm run typecheck` both clean. The added case builds a real 500-issue `PerformanceIssue` backlog on a page, detaches, then constructs the target universe and asserts the warning is never emitted while `Runtime.evaluate` still works. It pins the defect rather than just covering the path. Reverting only `src/devtools/DevtoolsUtils.ts`, rebuilding, and re-running: ``` warn('No handler registered for issue code PerformanceIssue') at createIssuesFromProtocolIssue (build/third_party/devtools-frontend/front_end/models/issues_manager/IssuesManager.js:164:13) ... (repeated) at assert2.<computed> [as neverCalledWithMatch] ✖ createTargetUniverse (1533.363458ms) '1 subtest failed' ``` Not tested: no measurement of the tool-call timeout the report describes on a long-lived session. The test asserts the backlog is no longer replayed, which is the mechanism, not the end-to-end latency. AI was used for assistance.
This commit is contained in:
@@ -36,6 +36,12 @@ export function overrideDevToolsGlobals({
|
||||
// DevTools CDP errors can get noisy.
|
||||
DevTools.ProtocolClient.InspectorBackend.test.suppressRequestErrors = true;
|
||||
|
||||
const noopAgentCommand = () => {
|
||||
return Promise.resolve({
|
||||
getError: () => undefined,
|
||||
});
|
||||
};
|
||||
|
||||
// Stub out Network emulation commands on the DevTools Agent prototype globally.
|
||||
// This prevents the DevTools Frontend from ever resetting/clearing Puppeteer's
|
||||
// active network blocking/throttling rules during target setup or session lifetime.
|
||||
@@ -63,42 +69,42 @@ export function overrideDevToolsGlobals({
|
||||
networkAgentPrototype,
|
||||
'invoke_overrideNetworkState',
|
||||
{
|
||||
value: () => {
|
||||
return Promise.resolve({
|
||||
getError: () => undefined,
|
||||
});
|
||||
},
|
||||
value: noopAgentCommand,
|
||||
writable: true,
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
},
|
||||
);
|
||||
Object.defineProperty(networkAgentPrototype, 'invoke_enable', {
|
||||
value: () => {
|
||||
return Promise.resolve({
|
||||
getError: () => undefined,
|
||||
});
|
||||
},
|
||||
value: noopAgentCommand,
|
||||
writable: true,
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
});
|
||||
Object.defineProperty(networkAgentPrototype, 'invoke_disable', {
|
||||
value: () => {
|
||||
return Promise.resolve({
|
||||
getError: () => undefined,
|
||||
});
|
||||
},
|
||||
value: noopAgentCommand,
|
||||
writable: true,
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
});
|
||||
Object.defineProperty(networkAgentPrototype, 'invoke_setBlockedURLs', {
|
||||
value: () => {
|
||||
return Promise.resolve({
|
||||
getError: () => undefined,
|
||||
});
|
||||
},
|
||||
value: noopAgentCommand,
|
||||
writable: true,
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
});
|
||||
}
|
||||
|
||||
// Puppeteer already collects issues from its own Audits subscription. Avoid
|
||||
// enabling the DevTools Frontend's redundant subscription, which can replay
|
||||
// a large retained issue backlog and delay unrelated page work.
|
||||
const auditsAgentPrototype =
|
||||
DevTools.ProtocolClient.InspectorBackend.inspectorBackend.agentPrototypes.get(
|
||||
'Audits',
|
||||
);
|
||||
if (auditsAgentPrototype) {
|
||||
Object.defineProperty(auditsAgentPrototype, 'invoke_enable', {
|
||||
value: noopAgentCommand,
|
||||
writable: true,
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
|
||||
@@ -100,6 +100,58 @@ describe('createTargetUniverse', () => {
|
||||
sinon.assert.notCalled(requestStartedSpy);
|
||||
});
|
||||
});
|
||||
|
||||
it('does not replay retained Audits issues', async () => {
|
||||
server.addHtmlRoute('/audits', html`<div>Audits</div>`);
|
||||
|
||||
await withBrowser(async (browser, page) => {
|
||||
await page.goto(server.getRoute('/audits'));
|
||||
|
||||
const auditsSession = await page.createCDPSession();
|
||||
await auditsSession.send('Audits.enable');
|
||||
|
||||
const backlogCreated = Promise.withResolvers<void>();
|
||||
let performanceIssueCount = 0;
|
||||
auditsSession.on('Audits.issueAdded', event => {
|
||||
if (
|
||||
event.issue.code === 'PerformanceIssue' &&
|
||||
event.issue.details.performanceIssueDetails?.performanceIssueType ===
|
||||
'DocumentCookie'
|
||||
) {
|
||||
performanceIssueCount++;
|
||||
if (performanceIssueCount === 500) {
|
||||
backlogCreated.resolve();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await page.evaluate(() => {
|
||||
document.cookie = 'test=value';
|
||||
for (let i = 0; i < 500; i++) {
|
||||
void document.cookie;
|
||||
}
|
||||
});
|
||||
|
||||
await backlogCreated.promise;
|
||||
assert.strictEqual(performanceIssueCount, 500);
|
||||
await auditsSession.detach();
|
||||
|
||||
const warnStub = sinon.stub(console, 'warn');
|
||||
const targetUniverse = await createTargetUniverse(
|
||||
await page.createCDPSession(),
|
||||
);
|
||||
|
||||
assert.ok(targetUniverse.target.model(DevTools.DebuggerModel));
|
||||
assert.strictEqual(await page.evaluate('1 + 1'), 2);
|
||||
await targetUniverse.session.send('Runtime.evaluate', {
|
||||
expression: '1 + 1',
|
||||
});
|
||||
sinon.assert.neverCalledWithMatch(
|
||||
warnStub,
|
||||
'No handler registered for issue code PerformanceIssue',
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('SymbolizedError', () => {
|
||||
|
||||
Reference in New Issue
Block a user