豆豆友情提示:这是一个非官方 GitHub 代理镜像,主要用于网络测试或访问加速。请勿在此进行登录、注册或处理任何敏感信息。进行这些操作请务必访问官方网站 github.com。 Raw 内容也通过此代理提供。
Skip to content

Commit 9628dab

Browse files
authored
refactor: clean up more of the context getters (#1062)
- moves more code to the McpPage - deleted Context specific tests
1 parent 4cb5a17 commit 9628dab

File tree

10 files changed

+118
-419
lines changed

10 files changed

+118
-419
lines changed

src/McpContext.ts

Lines changed: 25 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import type {
2323
BrowserContext,
2424
ConsoleMessage,
2525
Debugger,
26-
ElementHandle,
2726
HTTPRequest,
2827
Page,
2928
ScreenRecorder,
@@ -34,7 +33,6 @@ import type {
3433
import {Locator} from './third_party/index.js';
3534
import {PredefinedNetworkConditions} from './third_party/index.js';
3635
import {listPages} from './tools/pages.js';
37-
import {takeSnapshot} from './tools/snapshot.js';
3836
import {CLOSE_PAGE_ERROR} from './tools/ToolDefinition.js';
3937
import type {
4038
Context,
@@ -109,7 +107,7 @@ export class McpContext implements Context {
109107
#extensionServiceWorkers: ExtensionServiceWorker[] = [];
110108

111109
#mcpPages = new Map<Page, McpPage>();
112-
#selectedPage?: Page;
110+
#selectedPage?: McpPage;
113111
#networkCollector: NetworkCollector;
114112
#consoleCollector: ConsoleCollector;
115113
#devtoolsUniverseManager: UniverseManager;
@@ -290,7 +288,7 @@ export class McpContext implements Context {
290288
page = await this.browser.newPage({background});
291289
}
292290
await this.createPagesSnapshot();
293-
this.selectPage(page);
291+
this.selectPage(this.#getMcpPage(page));
294292
this.#networkCollector.addPage(page);
295293
this.#consoleCollector.addPage(page);
296294
return this.#getMcpPage(page);
@@ -300,16 +298,15 @@ export class McpContext implements Context {
300298
throw new Error(CLOSE_PAGE_ERROR);
301299
}
302300
const page = this.getPageById(pageId);
303-
const mcpPage = this.#mcpPages.get(page);
304-
if (mcpPage) {
305-
mcpPage.dispose();
306-
this.#mcpPages.delete(page);
301+
if (page) {
302+
page.dispose();
303+
this.#mcpPages.delete(page.pptrPage);
307304
}
308-
const ctx = page.browserContext();
309-
if (this.#focusedPagePerContext.get(ctx) === page) {
305+
const ctx = page.pptrPage.browserContext();
306+
if (this.#focusedPagePerContext.get(ctx) === page.pptrPage) {
310307
this.#focusedPagePerContext.delete(ctx);
311308
}
312-
await page.close({runBeforeUnload: false});
309+
await page.pptrPage.close({runBeforeUnload: false});
313310
}
314311

315312
getNetworkRequestById(page: McpPage, reqid: number): HTTPRequest {
@@ -461,29 +458,21 @@ export class McpContext implements Context {
461458
if (!page) {
462459
throw new Error('No page selected');
463460
}
464-
if (page.isClosed()) {
461+
if (page.pptrPage.isClosed()) {
465462
throw new Error(
466463
`The selected page has been closed. Call ${listPages().name} to see open pages.`,
467464
);
468465
}
469-
return page;
466+
return page.pptrPage;
470467
}
471468

472469
getSelectedMcpPage(): McpPage {
473470
const page = this.getSelectedPptrPage();
474471
return this.#getMcpPage(page);
475472
}
476473

477-
resolvePageById(pageId?: number): McpPage {
478-
if (pageId === undefined) {
479-
return this.getSelectedMcpPage();
480-
}
481-
const page = this.getPageById(pageId);
482-
return this.#getMcpPage(page);
483-
}
484-
485-
getPageById(pageId: number): Page {
486-
const page = this.#pages.find(p => this.#mcpPages.get(p)?.id === pageId);
474+
getPageById(pageId: number): McpPage {
475+
const page = this.#mcpPages.values().find(mcpPage => mcpPage.id === pageId);
487476
if (!page) {
488477
throw new Error('No page found');
489478
}
@@ -507,7 +496,7 @@ export class McpContext implements Context {
507496
}
508497

509498
isPageSelected(page: Page): boolean {
510-
return this.#selectedPage === page;
499+
return this.#selectedPage?.pptrPage === page;
511500
}
512501

513502
assertPageIsFocused(pageToCheck: Page | ContextPage): void {
@@ -524,20 +513,22 @@ export class McpContext implements Context {
524513
}
525514
}
526515

527-
selectPage(pageToSelect: Page | ContextPage): void {
528-
const newPage =
529-
'pptrPage' in pageToSelect ? pageToSelect.pptrPage : pageToSelect;
530-
const ctx = newPage.browserContext();
516+
selectPage(newPage: McpPage): void {
517+
const ctx = newPage.pptrPage.browserContext();
531518
const oldFocused = this.#focusedPagePerContext.get(ctx);
532-
if (oldFocused && oldFocused !== newPage && !oldFocused.isClosed()) {
519+
if (
520+
oldFocused &&
521+
oldFocused !== newPage.pptrPage &&
522+
!oldFocused.isClosed()
523+
) {
533524
void oldFocused.emulateFocusedPage(false).catch(error => {
534525
this.logger('Error turning off focused page emulation', error);
535526
});
536527
}
537-
this.#focusedPagePerContext.set(ctx, newPage);
528+
this.#focusedPagePerContext.set(ctx, newPage.pptrPage);
538529
this.#selectedPage = newPage;
539530
this.#updateSelectedPageTimeouts();
540-
void newPage.emulateFocusedPage(true).catch(error => {
531+
void newPage.pptrPage.emulateFocusedPage(true).catch(error => {
541532
this.logger('Error turning on focused page emulation', error);
542533
});
543534
}
@@ -572,63 +563,6 @@ export class McpContext implements Context {
572563
return undefined;
573564
}
574565

575-
async getElementByUid(
576-
uid: string,
577-
page?: Page,
578-
): Promise<ElementHandle<Element>> {
579-
if (page) {
580-
// Scoped search: only look in the target page's snapshot.
581-
const mcpPage = this.#mcpPages.get(page);
582-
if (!mcpPage?.textSnapshot) {
583-
throw new Error(
584-
`No snapshot found for page ${mcpPage?.id ?? '?'}. Use ${takeSnapshot.name} to capture one.`,
585-
);
586-
}
587-
const node = mcpPage.textSnapshot.idToNode.get(uid);
588-
if (!node) {
589-
throw new Error(
590-
`Element uid "${uid}" not found on page ${mcpPage.id}.`,
591-
);
592-
}
593-
return this.#resolveElementHandle(node, uid);
594-
}
595-
596-
// Cross-page search with context-focus validation.
597-
let anySnapshot = false;
598-
for (const [searchPage, mcpPage] of this.#mcpPages.entries()) {
599-
if (!mcpPage.textSnapshot) {
600-
continue;
601-
}
602-
anySnapshot = true;
603-
const node = mcpPage.textSnapshot.idToNode.get(uid);
604-
if (node) {
605-
const ctx = searchPage.browserContext();
606-
const contextSelectedPage = this.#focusedPagePerContext.get(ctx);
607-
if (contextSelectedPage !== searchPage) {
608-
const targetId = mcpPage.id;
609-
const selectedId = contextSelectedPage
610-
? this.#mcpPages.get(contextSelectedPage)?.id
611-
: this.#getSelectedMcpPage().id;
612-
throw new Error(
613-
`Element uid "${uid}" belongs to page ${targetId}, but page ${selectedId} is currently selected. ` +
614-
`Call select_page with pageId ${targetId} first.`,
615-
);
616-
}
617-
// Align global #selectedPage for waitForEventsAfterAction etc.
618-
if (this.#selectedPage !== searchPage) {
619-
this.#selectedPage = searchPage;
620-
}
621-
return this.#resolveElementHandle(node, uid);
622-
}
623-
}
624-
if (!anySnapshot) {
625-
throw new Error(
626-
`No snapshot found. Use ${takeSnapshot.name} to capture one.`,
627-
);
628-
}
629-
throw new Error('No such element found in any snapshot.');
630-
}
631-
632566
/**
633567
* Creates a snapshot of the extension service workers.
634568
*/
@@ -664,24 +598,6 @@ export class McpContext implements Context {
664598
return this.#extensionServiceWorkers;
665599
}
666600

667-
async #resolveElementHandle(
668-
node: TextSnapshotNode,
669-
uid: string,
670-
): Promise<ElementHandle<Element>> {
671-
const message = `Element with uid ${uid} no longer exists on the page.`;
672-
try {
673-
const handle = await node.elementHandle();
674-
if (!handle) {
675-
throw new Error(message);
676-
}
677-
return handle;
678-
} catch (error) {
679-
throw new Error(message, {
680-
cause: error,
681-
});
682-
}
683-
}
684-
685601
async createPagesSnapshot(): Promise<Page[]> {
686602
const {pages: allPages, isolatedContextNames} = await this.#getAllPages();
687603

@@ -717,10 +633,11 @@ export class McpContext implements Context {
717633
});
718634

719635
if (
720-
(!this.#selectedPage || this.#pages.indexOf(this.#selectedPage) === -1) &&
636+
(!this.#selectedPage ||
637+
this.#pages.indexOf(this.#selectedPage.pptrPage) === -1) &&
721638
this.#pages[0]
722639
) {
723-
this.selectPage(this.#pages[0]);
640+
this.selectPage(this.#getMcpPage(this.#pages[0]));
724641
}
725642

726643
await this.detectOpenDevToolsWindows();
@@ -943,14 +860,6 @@ export class McpContext implements Context {
943860
}
944861
}
945862

946-
getTextSnapshot(targetPage?: Page): TextSnapshot | null {
947-
const page = targetPage ?? this.#selectedPage;
948-
if (!page) {
949-
return null;
950-
}
951-
return this.#mcpPages.get(page)?.textSnapshot ?? null;
952-
}
953-
954863
async saveTemporaryFile(
955864
data: Uint8Array<ArrayBufferLike>,
956865
filename: string,

src/McpResponse.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -274,9 +274,7 @@ export class McpResponse implements Response {
274274
this.#snapshotParams.verbose,
275275
this.#devToolsData,
276276
);
277-
const textSnapshot = context.getTextSnapshot(
278-
this.#snapshotParams.page?.pptrPage,
279-
);
277+
const textSnapshot = this.#page.textSnapshot;
280278
if (textSnapshot) {
281279
const formatter = new SnapshotFormatter(textSnapshot);
282280
if (this.#snapshotParams.filePath) {

src/server.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ export async function createMcpServer(
182182
serverArgs.experimentalPageIdRouting &&
183183
params.pageId &&
184184
!serverArgs.slim
185-
? context.resolvePageById(params.pageId)
185+
? context.getPageById(params.pageId)
186186
: context.getSelectedMcpPage();
187187
response.setPage(page);
188188
await tool.handler(

src/tools/ToolDefinition.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ export interface ImageContentData {
6060
export interface SnapshotParams {
6161
verbose?: boolean;
6262
filePath?: string;
63-
page?: ContextPage;
6463
}
6564

6665
export interface LighthouseData {
@@ -136,16 +135,14 @@ export type Context = Readonly<{
136135
isCruxEnabled(): boolean;
137136
recordedTraces(): TraceResult[];
138137
storeTraceRecording(result: TraceResult): void;
139-
getPageById(pageId: number): Page;
138+
getPageById(pageId: number): ContextPage;
140139
newPage(
141140
background?: boolean,
142141
isolatedContextName?: string,
143142
): Promise<ContextPage>;
144143
closePage(pageId: number): Promise<void>;
145-
selectPage(page: Page): void;
144+
selectPage(page: ContextPage): void;
146145
assertPageIsFocused(page: Page): void;
147-
getElementByUid(uid: string, page?: Page): Promise<ElementHandle<Element>>;
148-
getAXNodeByUid(uid: string): TextSnapshotNode | undefined;
149146
restoreEmulation(page: ContextPage): Promise<void>;
150147
emulate(
151148
options: {

0 commit comments

Comments
 (0)