diff --git a/src/DevtoolsUtils.ts b/src/DevtoolsUtils.ts index c1f6efcb4..7a4abe9dc 100644 --- a/src/DevtoolsUtils.ts +++ b/src/DevtoolsUtils.ts @@ -15,61 +15,6 @@ import type { Target as PuppeteerTarget, } from './third_party/index.js'; -export function extractUrlLikeFromDevToolsTitle( - title: string, -): string | undefined { - const match = title.match(new RegExp(`DevTools - (.*)`)); - return match?.[1] ?? undefined; -} - -export function urlsEqual(url1: string, url2: string): boolean { - const normalizedUrl1 = normalizeUrl(url1); - const normalizedUrl2 = normalizeUrl(url2); - return normalizedUrl1 === normalizedUrl2; -} - -/** - * For the sake of the MCP server, when we determine if two URLs are equal we - * remove some parts: - * - * 1. We do not care about the protocol. - * 2. We do not care about trailing slashes. - * 3. We do not care about "www". - * 4. We ignore the hash parts. - * - * For example, if the user types "record a trace on foo.com", we would want to - * match a tab in the connected Chrome instance that is showing "www.foo.com/" - */ -function normalizeUrl(url: string): string { - let result = url.trim(); - - // Remove protocols - if (result.startsWith('https://')) { - result = result.slice(8); - } else if (result.startsWith('http://')) { - result = result.slice(7); - } - - // Remove 'www.'. This ensures that we find the right URL regardless of if the user adds `www` or not. - if (result.startsWith('www.')) { - result = result.slice(4); - } - - // We use target URLs to locate DevTools but those often do - // no include hash. - const hashIdx = result.lastIndexOf('#'); - if (hashIdx !== -1) { - result = result.slice(0, hashIdx); - } - - // Remove trailing slash - if (result.endsWith('/')) { - result = result.slice(0, -1); - } - - return result; -} - /** * A mock implementation of an issues manager that only implements the methods * that are actually used by the IssuesAggregator diff --git a/src/McpContext.ts b/src/McpContext.ts index 889103fab..4647c6118 100644 --- a/src/McpContext.ts +++ b/src/McpContext.ts @@ -8,11 +8,7 @@ import fs from 'node:fs/promises'; import path from 'node:path'; import type {TargetUniverse} from './DevtoolsUtils.js'; -import { - extractUrlLikeFromDevToolsTitle, - UniverseManager, - urlsEqual, -} from './DevtoolsUtils.js'; +import {UniverseManager} from './DevtoolsUtils.js'; import {McpPage} from './McpPage.js'; import type {ListenerMap, UncaughtError} from './PageCollector.js'; import {NetworkCollector, ConsoleCollector} from './PageCollector.js'; @@ -653,37 +649,21 @@ export class McpContext implements Context { async detectOpenDevToolsWindows() { this.logger('Detecting open DevTools windows'); const {pages} = await this.#getAllPages(); - // Clear all devToolsPage references before re-detecting. - for (const mcpPage of this.#mcpPages.values()) { - mcpPage.devToolsPage = undefined; - } - for (const devToolsPage of pages) { - if (devToolsPage.url().startsWith('devtools://')) { - try { - this.logger('Calling getTargetInfo for ' + devToolsPage.url()); - const data = await devToolsPage - // @ts-expect-error no types for _client(). - ._client() - .send('Target.getTargetInfo'); - const devtoolsPageTitle = data.targetInfo.title; - const urlLike = extractUrlLikeFromDevToolsTitle(devtoolsPageTitle); - if (!urlLike) { - continue; - } - // TODO: lookup without a loop. - for (const page of this.#pages) { - if (urlsEqual(page.url(), urlLike)) { - const mcpPage = this.#mcpPages.get(page); - if (mcpPage) { - mcpPage.devToolsPage = devToolsPage; - } - } - } - } catch (error) { - this.logger('Issue occurred while trying to find DevTools', error); + + await Promise.all( + pages.map(async page => { + const mcpPage = this.#mcpPages.get(page); + if (!mcpPage) { + return; } - } - } + + if (await page.hasDevTools()) { + mcpPage.devToolsPage = await page.openDevTools(); + } else { + mcpPage.devToolsPage = undefined; + } + }), + ); } getExtensionServiceWorkers(): ExtensionServiceWorker[] { diff --git a/tests/DevtoolsUtils.test.ts b/tests/DevtoolsUtils.test.ts index a5e43009c..8f9aa3794 100644 --- a/tests/DevtoolsUtils.test.ts +++ b/tests/DevtoolsUtils.test.ts @@ -9,11 +9,7 @@ import {afterEach, describe, it} from 'node:test'; import sinon from 'sinon'; -import { - extractUrlLikeFromDevToolsTitle, - urlsEqual, - UniverseManager, -} from '../src/DevtoolsUtils.js'; +import {UniverseManager} from '../src/DevtoolsUtils.js'; import {DevTools} from '../src/third_party/index.js'; import type {Browser, Target} from '../src/third_party/index.js'; @@ -24,76 +20,6 @@ import { withBrowser, } from './utils.js'; -describe('extractUrlFromDevToolsTitle', () => { - it('deals with no trailing /', () => { - assert.strictEqual( - extractUrlLikeFromDevToolsTitle('DevTools - example.com'), - 'example.com', - ); - }); - it('deals with a trailing /', () => { - assert.strictEqual( - extractUrlLikeFromDevToolsTitle('DevTools - example.com/'), - 'example.com/', - ); - }); - it('deals with www', () => { - assert.strictEqual( - extractUrlLikeFromDevToolsTitle('DevTools - www.example.com/'), - 'www.example.com/', - ); - }); - it('deals with complex url', () => { - assert.strictEqual( - extractUrlLikeFromDevToolsTitle( - 'DevTools - www.example.com/path.html?a=b#3', - ), - 'www.example.com/path.html?a=b#3', - ); - }); -}); - -describe('urlsEqual', () => { - it('ignores trailing slashes', () => { - assert.strictEqual( - urlsEqual('https://google.com/', 'https://google.com'), - true, - ); - }); - - it('ignores www', () => { - assert.strictEqual( - urlsEqual('https://google.com/', 'https://www.google.com'), - true, - ); - }); - - it('ignores protocols', () => { - assert.strictEqual( - urlsEqual('https://google.com/', 'http://www.google.com'), - true, - ); - }); - - it('does not ignore other subdomains', () => { - assert.strictEqual( - urlsEqual('https://google.com/', 'https://photos.google.com'), - false, - ); - }); - - it('ignores hash', () => { - assert.strictEqual( - urlsEqual('https://google.com/#', 'http://www.google.com'), - true, - ); - assert.strictEqual( - urlsEqual('https://google.com/#21', 'http://www.google.com#12'), - true, - ); - }); -}); - describe('UniverseManager', () => { afterEach(() => { sinon.restore();