@mcp-b/chrome-devtools-mcp 2.0.2 → 2.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +120 -10
- package/build/src/McpContext.js +69 -5
- package/build/src/browser.js +157 -51
- package/build/src/cli.js +11 -5
- package/build/src/formatters/IssueFormatter.js +190 -0
- package/build/src/main.js +83 -2
- package/build/src/telemetry/clearcut-logger.js +102 -0
- package/build/src/telemetry/flag-utils.js +45 -0
- package/build/src/telemetry/metric-utils.js +14 -0
- package/build/src/telemetry/persistence.js +53 -0
- package/build/src/telemetry/types.js +33 -0
- package/build/src/telemetry/watchdog/clearcut-sender.js +201 -0
- package/build/src/telemetry/watchdog/main.js +127 -0
- package/build/src/telemetry/watchdog-client.js +60 -0
- package/build/src/third_party/devtools-formatter-worker.js +7 -0
- package/build/src/third_party/index.js +1 -1
- package/build/src/tools/browser.js +92 -0
- package/build/src/tools/extension.js +31 -0
- package/build/src/tools/extensions.js +79 -0
- package/build/src/tools/input.js +6 -1
- package/build/src/tools/pages.js +7 -1
- package/build/src/tools/script.js +31 -4
- package/build/src/tools/tools.js +4 -0
- package/build/src/transports/CDPClientTransport.js +184 -0
- package/build/src/transports/WebMCPBridgeScript.js +11 -2
- package/build/src/utils/ExtensionRegistry.js +35 -0
- package/build/src/utils/string.js +36 -0
- package/build/vendor/chrome-devtools-frontend/front_end/core/common/Base64.js +20 -2
- package/build/vendor/chrome-devtools-frontend/front_end/core/common/Debouncer.js +8 -1
- package/build/vendor/chrome-devtools-frontend/front_end/core/common/Gzip.js +11 -0
- package/build/vendor/chrome-devtools-frontend/front_end/core/common/Object.js +6 -1
- package/build/vendor/chrome-devtools-frontend/front_end/core/common/ParsedURL.js +3 -0
- package/build/vendor/chrome-devtools-frontend/front_end/core/common/ResourceType.js +6 -0
- package/build/vendor/chrome-devtools-frontend/front_end/core/common/Revealer.js +0 -5
- package/build/vendor/chrome-devtools-frontend/front_end/core/common/Settings.js +18 -8
- package/build/vendor/chrome-devtools-frontend/front_end/core/host/AidaClient.js +24 -0
- package/build/vendor/chrome-devtools-frontend/front_end/core/host/InspectorFrontendHostStub.js +11 -3
- package/build/vendor/chrome-devtools-frontend/front_end/core/host/ResourceLoader.js +1 -1
- package/build/vendor/chrome-devtools-frontend/front_end/core/host/UserMetrics.js +27 -20
- package/build/vendor/chrome-devtools-frontend/front_end/core/i18n/collect-ui-strings.js +7 -8
- package/build/vendor/chrome-devtools-frontend/front_end/core/i18n/generate-locales-js.js +4 -5
- package/build/vendor/chrome-devtools-frontend/front_end/core/platform/ArrayUtilities.js +10 -0
- package/build/vendor/chrome-devtools-frontend/front_end/core/platform/StringUtilities.js +63 -12
- package/build/vendor/chrome-devtools-frontend/front_end/core/protocol_client/CDPConnection.js +1 -0
- package/build/vendor/chrome-devtools-frontend/front_end/core/protocol_client/InspectorBackend.js +4 -1
- package/build/vendor/chrome-devtools-frontend/front_end/core/root/ExperimentNames.js +30 -0
- package/build/vendor/chrome-devtools-frontend/front_end/core/root/root.js +2 -1
- package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/AnimationModel.js +0 -4
- package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/CSSMatchedStyles.js +69 -9
- package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/CSSMetadata.js +6 -6
- package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/CSSModel.js +28 -13
- package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/CSSProperty.js +1 -1
- package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/CSSPropertyParserMatchers.js +6 -0
- package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/ConsoleModel.js +0 -2
- package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/CookieModel.js +1 -1
- package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/DOMModel.js +170 -13
- package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/DebuggerModel.js +5 -39
- package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/HeapProfilerModel.js +8 -1
- package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/NetworkManager.js +20 -5
- package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/NetworkRequest.js +12 -21
- package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/OverlayModel.js +19 -6
- package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/RehydratingConnection.js +5 -1
- package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/ResourceTreeModel.js +8 -5
- package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/SourceMap.js +15 -10
- package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/SourceMapManager.js +1 -1
- package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/SourceMapScopesInfo.js +13 -27
- package/build/vendor/chrome-devtools-frontend/front_end/core/sdk/Target.js +3 -1
- package/build/vendor/chrome-devtools-frontend/front_end/generated/ARIAProperties.js +1 -7
- package/build/vendor/chrome-devtools-frontend/front_end/generated/Deprecation.js +1 -16
- package/build/vendor/chrome-devtools-frontend/front_end/generated/InspectorBackendCommands.js +82 -22
- package/build/vendor/chrome-devtools-frontend/front_end/generated/SupportedCSSProperties.js +265 -123
- package/build/vendor/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/NetworkRequestFormatter.js +2 -1
- package/build/vendor/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.js +10 -16
- package/build/vendor/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.js +97 -26
- package/build/vendor/chrome-devtools-frontend/front_end/models/ai_assistance/performance/AICallTree.js +35 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/annotations/AnnotationRepository.js +163 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/annotations/AnnotationType.js +10 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/annotations/annotations.js +5 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/bindings/CompilerScriptMapping.js +5 -3
- package/build/vendor/chrome-devtools-frontend/front_end/models/bindings/DebuggerLanguagePlugins.js +29 -58
- package/build/vendor/chrome-devtools-frontend/front_end/models/bindings/DebuggerWorkspaceBinding.js +7 -45
- package/build/vendor/chrome-devtools-frontend/front_end/models/emulation/DeviceModeModel.js +1 -1
- package/build/vendor/chrome-devtools-frontend/front_end/models/emulation/EmulatedDevices.js +14 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/formatter/FormatterWorkerPool.js +8 -5
- package/build/vendor/chrome-devtools-frontend/front_end/models/greendev/Prototypes.js +33 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/greendev/greendev.js +4 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/ContrastCheckTrigger.js +2 -2
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/CookieIssue.js +0 -21
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/CorsIssue.js +1 -38
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/IssueAggregator.js +8 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/IssuesManager.js +6 -12
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/PermissionElementIssue.js +243 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementActivationDisabled.md +7 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementActivationDisabledWithOccluder.md +9 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementActivationDisabledWithOccluderParent.md +9 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementCspFrameAncestorsMissing.md +5 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementFencedFrameDisallowed.md +5 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementFontSizeTooLarge.md +5 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementFontSizeTooSmall.md +5 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementGeolocationDeprecated.md +5 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementInsetBoxShadowUnsupported.md +5 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementInvalidDisplayStyle.md +5 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementInvalidSizeValue.md +5 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementInvalidType.md +5 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementInvalidTypeActivation.md +5 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementLowContrast.md +5 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementNonOpaqueColor.md +5 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementPaddingBottomUnsupported.md +6 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementPaddingRightUnsupported.md +6 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementPermissionsPolicyBlocked.md +5 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementRegistrationFailed.md +5 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementRequestInProgress.md +5 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementSecurityChecksFailed.md +5 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementTypeNotSupported.md +5 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/permissionElementUntrustedEvent.md +7 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/issues_manager.js +2 -1
- package/build/vendor/chrome-devtools-frontend/front_end/models/logs/NetworkLog.js +0 -8
- package/build/vendor/chrome-devtools-frontend/front_end/models/source_map_scopes/NamesResolver.js +4 -8
- package/build/vendor/chrome-devtools-frontend/front_end/models/stack_trace/StackTrace.js +30 -1
- package/build/vendor/chrome-devtools-frontend/front_end/models/stack_trace/StackTraceImpl.js +70 -1
- package/build/vendor/chrome-devtools-frontend/front_end/models/stack_trace/StackTraceModel.js +82 -30
- package/build/vendor/chrome-devtools-frontend/front_end/models/trace/EventsSerializer.js +10 -2
- package/build/vendor/chrome-devtools-frontend/front_end/models/trace/LanternComputationData.js +2 -2
- package/build/vendor/chrome-devtools-frontend/front_end/models/trace/ModelImpl.js +0 -3
- package/build/vendor/chrome-devtools-frontend/front_end/models/trace/Processor.js +18 -19
- package/build/vendor/chrome-devtools-frontend/front_end/models/trace/Styles.js +12 -4
- package/build/vendor/chrome-devtools-frontend/front_end/models/trace/extras/Initiators.js +46 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/trace/extras/TraceTree.js +4 -3
- package/build/vendor/chrome-devtools-frontend/front_end/models/trace/extras/extras.js +1 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/trace/handlers/LargestImagePaintHandler.js +2 -2
- package/build/vendor/chrome-devtools-frontend/front_end/models/trace/handlers/LayoutShiftsHandler.js +1 -1
- package/build/vendor/chrome-devtools-frontend/front_end/models/trace/handlers/MetaHandler.js +6 -0
- package/build/vendor/chrome-devtools-frontend/front_end/models/trace/handlers/NetworkRequestsHandler.js +10 -1
- package/build/vendor/chrome-devtools-frontend/front_end/models/trace/handlers/PageLoadMetricsHandler.js +44 -27
- package/build/vendor/chrome-devtools-frontend/front_end/models/trace/helpers/Timing.js +9 -2
- package/build/vendor/chrome-devtools-frontend/front_end/models/trace/insights/Common.js +1 -6
- package/build/vendor/chrome-devtools-frontend/front_end/models/trace/insights/LCPBreakdown.js +2 -2
- package/build/vendor/chrome-devtools-frontend/front_end/models/trace/insights/LCPDiscovery.js +2 -4
- package/build/vendor/chrome-devtools-frontend/front_end/models/trace/insights/NetworkDependencyTree.js +3 -2
- package/build/vendor/chrome-devtools-frontend/front_end/models/trace/insights/RenderBlocking.js +1 -1
- package/build/vendor/chrome-devtools-frontend/front_end/models/trace/types/TraceEvents.js +33 -11
- package/build/vendor/chrome-devtools-frontend/front_end/third_party/source-map-scopes-codec/package/src/decode/decode.js +51 -18
- package/build/vendor/chrome-devtools-frontend/front_end/third_party/source-map-scopes-codec/package/src/encode/encoder.js +1 -1
- package/build/vendor/chrome-devtools-frontend/front_end/third_party/source-map-scopes-codec/package/src/scopes.js +4 -0
- package/build/vendor/chrome-devtools-frontend/mcp/HostBindings.js +4 -0
- package/build/vendor/chrome-devtools-frontend/mcp/mcp.js +4 -0
- package/package.json +28 -21
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/SameSiteInvalidSameParty.md +0 -8
- package/build/vendor/chrome-devtools-frontend/front_end/models/issues_manager/descriptions/SameSiteSamePartyCrossPartyContextSet.md +0 -10
|
@@ -3,19 +3,26 @@
|
|
|
3
3
|
* Copyright 2025 Google LLC
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
|
+
import { readFile } from 'node:fs/promises';
|
|
6
7
|
import { zod } from '../third_party/index.js';
|
|
7
8
|
import { ToolCategory } from './categories.js';
|
|
8
9
|
import { defineTool } from './ToolDefinition.js';
|
|
9
10
|
export const evaluateScript = defineTool({
|
|
10
11
|
name: 'evaluate_script',
|
|
11
|
-
description: `Evaluate a JavaScript function
|
|
12
|
-
|
|
12
|
+
description: `Evaluate a JavaScript function or inject a script file into the currently selected page.
|
|
13
|
+
|
|
14
|
+
When \`function\` is provided, evaluates it and returns the result as JSON (values must be JSON-serializable).
|
|
15
|
+
When \`filePath\` is provided, reads the file from disk and injects it as a <script> tag (useful for large scripts like polyfills that are too big to pass inline).
|
|
16
|
+
Exactly one of \`function\` or \`filePath\` must be provided.`,
|
|
13
17
|
annotations: {
|
|
14
18
|
category: ToolCategory.DEBUGGING,
|
|
15
19
|
readOnlyHint: false,
|
|
16
20
|
},
|
|
17
21
|
schema: {
|
|
18
|
-
function: zod
|
|
22
|
+
function: zod
|
|
23
|
+
.string()
|
|
24
|
+
.optional()
|
|
25
|
+
.describe(`A JavaScript function declaration to be executed by the tool in the currently selected page.
|
|
19
26
|
Example without arguments: \`() => {
|
|
20
27
|
return document.title
|
|
21
28
|
}\` or \`async () => {
|
|
@@ -25,6 +32,10 @@ Example with arguments: \`(el) => {
|
|
|
25
32
|
return el.innerText;
|
|
26
33
|
}\`
|
|
27
34
|
`),
|
|
35
|
+
filePath: zod
|
|
36
|
+
.string()
|
|
37
|
+
.optional()
|
|
38
|
+
.describe('Absolute path to a local JavaScript file to inject into the page via <script> tag. The file is read server-side so there are no size limits or CSP/mixed-content restrictions. Use this for large scripts (polyfills, bundled tools, etc).'),
|
|
28
39
|
args: zod
|
|
29
40
|
.array(zod.object({
|
|
30
41
|
uid: zod
|
|
@@ -32,9 +43,25 @@ Example with arguments: \`(el) => {
|
|
|
32
43
|
.describe('The uid of an element on the page from the page content snapshot'),
|
|
33
44
|
}))
|
|
34
45
|
.optional()
|
|
35
|
-
.describe(`An optional list of arguments to pass to the function
|
|
46
|
+
.describe(`An optional list of arguments to pass to the function. Only used with \`function\`, not \`filePath\`.`),
|
|
36
47
|
},
|
|
37
48
|
handler: async (request, response, context) => {
|
|
49
|
+
const { filePath } = request.params;
|
|
50
|
+
// File injection mode
|
|
51
|
+
if (filePath) {
|
|
52
|
+
if (request.params.function) {
|
|
53
|
+
throw new Error('Provide either `function` or `filePath`, not both.');
|
|
54
|
+
}
|
|
55
|
+
const content = await readFile(filePath, 'utf-8');
|
|
56
|
+
const page = context.getSelectedPage();
|
|
57
|
+
await page.addScriptTag({ content });
|
|
58
|
+
response.appendResponseLine(`Injected script from \`${filePath}\` (${content.length} bytes) into page.`);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
// Function evaluation mode (original behavior)
|
|
62
|
+
if (!request.params.function) {
|
|
63
|
+
throw new Error('Either `function` or `filePath` must be provided.');
|
|
64
|
+
}
|
|
38
65
|
const args = [];
|
|
39
66
|
try {
|
|
40
67
|
const frames = new Set();
|
package/build/src/tools/tools.js
CHANGED
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
* Copyright 2025 Google LLC
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
|
+
import * as browserTools from './browser.js';
|
|
6
7
|
import * as consoleTools from './console.js';
|
|
7
8
|
import * as emulationTools from './emulation.js';
|
|
9
|
+
import * as extensionTools from './extension.js';
|
|
8
10
|
import * as inputTools from './input.js';
|
|
9
11
|
import * as networkTools from './network.js';
|
|
10
12
|
import * as pagesTools from './pages.js';
|
|
@@ -14,8 +16,10 @@ import * as scriptTools from './script.js';
|
|
|
14
16
|
import * as snapshotTools from './snapshot.js';
|
|
15
17
|
import * as webmcpTools from './webmcp.js';
|
|
16
18
|
const tools = [
|
|
19
|
+
...Object.values(browserTools),
|
|
17
20
|
...Object.values(consoleTools),
|
|
18
21
|
...Object.values(emulationTools),
|
|
22
|
+
...Object.values(extensionTools),
|
|
19
23
|
...Object.values(inputTools),
|
|
20
24
|
...Object.values(networkTools),
|
|
21
25
|
...Object.values(pagesTools),
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* MCP Client Transport that connects to an extension's MCP server via CDP.
|
|
8
|
+
*
|
|
9
|
+
* Discovers the extension's service worker, attaches to it, and establishes
|
|
10
|
+
* a bidirectional message channel using Runtime.evaluate (client → server)
|
|
11
|
+
* and Runtime.addBinding (server → client).
|
|
12
|
+
*
|
|
13
|
+
* CDP attaches ONLY to the service worker. Pages remain clean with no
|
|
14
|
+
* navigator.webdriver flag, bypassing bot detection entirely.
|
|
15
|
+
*/
|
|
16
|
+
export class CDPClientTransport {
|
|
17
|
+
#browser;
|
|
18
|
+
#extensionId;
|
|
19
|
+
#connectTimeout;
|
|
20
|
+
#session = null;
|
|
21
|
+
#started = false;
|
|
22
|
+
#closed = false;
|
|
23
|
+
#bindingHandler = null;
|
|
24
|
+
#disconnectHandler = null;
|
|
25
|
+
onclose;
|
|
26
|
+
onerror;
|
|
27
|
+
onmessage;
|
|
28
|
+
constructor(options) {
|
|
29
|
+
this.#browser = options.browser;
|
|
30
|
+
this.#extensionId = options.extensionId;
|
|
31
|
+
this.#connectTimeout = options.connectTimeout ?? 10_000;
|
|
32
|
+
}
|
|
33
|
+
async start() {
|
|
34
|
+
if (this.#started) {
|
|
35
|
+
throw new Error('CDPClientTransport already started. If using Client class, note that connect() calls start() automatically.');
|
|
36
|
+
}
|
|
37
|
+
if (this.#closed) {
|
|
38
|
+
throw new Error('CDPClientTransport has been closed');
|
|
39
|
+
}
|
|
40
|
+
this.#started = true;
|
|
41
|
+
try {
|
|
42
|
+
// Handle browser disconnect
|
|
43
|
+
this.#disconnectHandler = () => {
|
|
44
|
+
if (this.#closed)
|
|
45
|
+
return;
|
|
46
|
+
this.#browser = null;
|
|
47
|
+
this.onclose?.();
|
|
48
|
+
};
|
|
49
|
+
this.#browser.on('disconnected', this.#disconnectHandler);
|
|
50
|
+
// Find extension service worker target with retry
|
|
51
|
+
const swTarget = await this.#findExtensionServiceWorker();
|
|
52
|
+
if (!swTarget) {
|
|
53
|
+
throw new Error(this.#extensionId
|
|
54
|
+
? `Extension ${this.#extensionId} service worker not found`
|
|
55
|
+
: 'No extension service worker found. Is an MCP-enabled extension installed?');
|
|
56
|
+
}
|
|
57
|
+
// Create a CDP session directly on the service worker target
|
|
58
|
+
this.#session = await swTarget.createCDPSession();
|
|
59
|
+
// Enable Runtime domain
|
|
60
|
+
await this.#session.send('Runtime.enable');
|
|
61
|
+
// Add binding for receiving messages from the extension
|
|
62
|
+
await this.#session.send('Runtime.addBinding', {
|
|
63
|
+
name: '__mcpCDPToClient',
|
|
64
|
+
});
|
|
65
|
+
// Set up handler for binding calls
|
|
66
|
+
this.#bindingHandler = (event) => {
|
|
67
|
+
if (event.name !== '__mcpCDPToClient')
|
|
68
|
+
return;
|
|
69
|
+
if (this.#closed)
|
|
70
|
+
return;
|
|
71
|
+
try {
|
|
72
|
+
const message = JSON.parse(event.payload);
|
|
73
|
+
this.onmessage?.(message);
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
this.onerror?.(new Error(`Failed to parse message from extension: ${err}`));
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
this.#session.on('Runtime.bindingCalled', this.#bindingHandler);
|
|
80
|
+
// Verify the extension has the CDP transport ready
|
|
81
|
+
const { result } = await this.#session.send('Runtime.evaluate', {
|
|
82
|
+
expression: 'globalThis.__mcpCDPTransport?.isReady === true',
|
|
83
|
+
returnByValue: true,
|
|
84
|
+
});
|
|
85
|
+
if (!result.value) {
|
|
86
|
+
throw new Error('Extension CDP transport not ready. Ensure the extension has CDP bridge support enabled.');
|
|
87
|
+
}
|
|
88
|
+
// Connect our binding to the extension's transport
|
|
89
|
+
await this.#session.send('Runtime.evaluate', {
|
|
90
|
+
expression: `
|
|
91
|
+
globalThis.__mcpCDPTransport._sendBinding = (jsonStr) => {
|
|
92
|
+
__mcpCDPToClient(jsonStr);
|
|
93
|
+
};
|
|
94
|
+
`,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
this.#started = false;
|
|
99
|
+
await this.#cleanup();
|
|
100
|
+
throw err;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
async send(message) {
|
|
104
|
+
if (!this.#started) {
|
|
105
|
+
throw new Error('CDPClientTransport not started');
|
|
106
|
+
}
|
|
107
|
+
if (this.#closed) {
|
|
108
|
+
throw new Error('CDPClientTransport has been closed');
|
|
109
|
+
}
|
|
110
|
+
if (!this.#session) {
|
|
111
|
+
throw new Error('CDP session not available');
|
|
112
|
+
}
|
|
113
|
+
const jsonStr = JSON.stringify(message);
|
|
114
|
+
try {
|
|
115
|
+
await this.#session.send('Runtime.evaluate', {
|
|
116
|
+
expression: `globalThis.__mcpCDPTransport.receiveMessage(${JSON.stringify(jsonStr)})`,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
const error = new Error(`Failed to send message to extension: ${err}`);
|
|
121
|
+
this.onerror?.(error);
|
|
122
|
+
throw error;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
async close() {
|
|
126
|
+
if (this.#closed)
|
|
127
|
+
return;
|
|
128
|
+
this.#closed = true;
|
|
129
|
+
this.#started = false;
|
|
130
|
+
await this.#cleanup();
|
|
131
|
+
if (this.#browser && this.#disconnectHandler) {
|
|
132
|
+
this.#browser.off('disconnected', this.#disconnectHandler);
|
|
133
|
+
this.#disconnectHandler = null;
|
|
134
|
+
}
|
|
135
|
+
this.onclose?.();
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Find the extension's service worker target with retry.
|
|
139
|
+
*/
|
|
140
|
+
async #findExtensionServiceWorker() {
|
|
141
|
+
const deadline = Date.now() + this.#connectTimeout;
|
|
142
|
+
const retryInterval = 1_000;
|
|
143
|
+
while (Date.now() < deadline) {
|
|
144
|
+
const targets = this.#browser.targets();
|
|
145
|
+
const sw = targets.find(t => {
|
|
146
|
+
if (t.type() !== 'service_worker')
|
|
147
|
+
return false;
|
|
148
|
+
if (!t.url().startsWith('chrome-extension://'))
|
|
149
|
+
return false;
|
|
150
|
+
if (this.#extensionId && !t.url().includes(this.#extensionId))
|
|
151
|
+
return false;
|
|
152
|
+
return true;
|
|
153
|
+
});
|
|
154
|
+
if (sw)
|
|
155
|
+
return sw;
|
|
156
|
+
const remaining = deadline - Date.now();
|
|
157
|
+
if (remaining > retryInterval) {
|
|
158
|
+
await new Promise(resolve => setTimeout(resolve, retryInterval));
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Clean up CDP resources.
|
|
168
|
+
*/
|
|
169
|
+
async #cleanup() {
|
|
170
|
+
if (this.#session && this.#bindingHandler) {
|
|
171
|
+
this.#session.off('Runtime.bindingCalled', this.#bindingHandler);
|
|
172
|
+
this.#bindingHandler = null;
|
|
173
|
+
}
|
|
174
|
+
if (this.#session) {
|
|
175
|
+
try {
|
|
176
|
+
await this.#session.detach();
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
// Ignore detach errors
|
|
180
|
+
}
|
|
181
|
+
this.#session = null;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
@@ -100,6 +100,15 @@ export const WEB_MCP_BRIDGE_SCRIPT = `
|
|
|
100
100
|
return false;
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
+
function getTargetOrigin() {
|
|
104
|
+
var origin = window.location && typeof window.location.origin === 'string'
|
|
105
|
+
? window.location.origin
|
|
106
|
+
: '';
|
|
107
|
+
// about:blank, file://, and sandboxed contexts expose "null" origin.
|
|
108
|
+
// Use "*" so postMessage remains functional in those contexts.
|
|
109
|
+
return origin && origin !== 'null' ? origin : '*';
|
|
110
|
+
}
|
|
111
|
+
|
|
103
112
|
// Initial check
|
|
104
113
|
checkWebMCPAvailable();
|
|
105
114
|
|
|
@@ -125,7 +134,7 @@ export const WEB_MCP_BRIDGE_SCRIPT = `
|
|
|
125
134
|
type: 'mcp',
|
|
126
135
|
direction: 'client-to-server',
|
|
127
136
|
payload: payload
|
|
128
|
-
},
|
|
137
|
+
}, getTargetOrigin());
|
|
129
138
|
|
|
130
139
|
return true;
|
|
131
140
|
} catch (err) {
|
|
@@ -169,7 +178,7 @@ export const WEB_MCP_BRIDGE_SCRIPT = `
|
|
|
169
178
|
type: 'mcp',
|
|
170
179
|
direction: 'client-to-server',
|
|
171
180
|
payload: 'mcp-check-ready'
|
|
172
|
-
},
|
|
181
|
+
}, getTargetOrigin());
|
|
173
182
|
},
|
|
174
183
|
|
|
175
184
|
/**
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import fs from 'node:fs/promises';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
export class ExtensionRegistry {
|
|
9
|
+
#extensions = new Map();
|
|
10
|
+
async registerExtension(id, extensionPath) {
|
|
11
|
+
const manifestPath = path.join(extensionPath, 'manifest.json');
|
|
12
|
+
const manifestContent = await fs.readFile(manifestPath, 'utf-8');
|
|
13
|
+
const manifest = JSON.parse(manifestContent);
|
|
14
|
+
const name = manifest.name ?? 'Unknown';
|
|
15
|
+
const version = manifest.version ?? 'Unknown';
|
|
16
|
+
const extension = {
|
|
17
|
+
id,
|
|
18
|
+
name,
|
|
19
|
+
version,
|
|
20
|
+
isEnabled: true,
|
|
21
|
+
path: extensionPath,
|
|
22
|
+
};
|
|
23
|
+
this.#extensions.set(extension.id, extension);
|
|
24
|
+
return extension;
|
|
25
|
+
}
|
|
26
|
+
remove(id) {
|
|
27
|
+
this.#extensions.delete(id);
|
|
28
|
+
}
|
|
29
|
+
list() {
|
|
30
|
+
return Array.from(this.#extensions.values());
|
|
31
|
+
}
|
|
32
|
+
getById(id) {
|
|
33
|
+
return this.#extensions.get(id);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Converts a given string to snake_case.
|
|
8
|
+
* This function handles camelCase, PascalCase, and acronyms, including transitions between letters and numbers.
|
|
9
|
+
* It uses Unicode-aware regular expressions (`\p{L}`, `\p{N}`, `\p{Lu}`, `\p{Ll}` with the `u` flag)
|
|
10
|
+
* to correctly process letters and numbers from various languages.
|
|
11
|
+
*
|
|
12
|
+
* @param text The input string to convert to snake_case.
|
|
13
|
+
* @returns The snake_case version of the input string.
|
|
14
|
+
*/
|
|
15
|
+
export function toSnakeCase(text) {
|
|
16
|
+
if (!text) {
|
|
17
|
+
return '';
|
|
18
|
+
}
|
|
19
|
+
// First, handle case-based transformations to insert underscores correctly.
|
|
20
|
+
// 1. Add underscore between a letter and a number.
|
|
21
|
+
// e.g., "version2" -> "version_2"
|
|
22
|
+
// 2. Add underscore between an uppercase letter sequence and a following uppercase+lowercase sequence.
|
|
23
|
+
// e.g., "APIFlags" -> "API_Flags"
|
|
24
|
+
// 3. Add underscore between a lowercase/number and an uppercase letter.
|
|
25
|
+
// e.g., "lastName" -> "last_Name", "version_2Update" -> "version_2_Update"
|
|
26
|
+
// 4. Replace sequences of non-alphanumeric with a single underscore
|
|
27
|
+
// 5. Remove any leading or trailing underscores.
|
|
28
|
+
const result = text
|
|
29
|
+
.replace(/(\p{L})(\p{N})/gu, '$1_$2') // 1
|
|
30
|
+
.replace(/(\p{Lu}+)(\p{Lu}\p{Ll})/gu, '$1_$2') // 2
|
|
31
|
+
.replace(/(\p{Ll}|\p{N})(\p{Lu})/gu, '$1_$2') // 3
|
|
32
|
+
.toLowerCase()
|
|
33
|
+
.replace(/[^\p{L}\p{N}]+/gu, '_') // 4
|
|
34
|
+
.replace(/^_|_$/g, ''); // 5
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
@@ -29,13 +29,31 @@ export function decode(input) {
|
|
|
29
29
|
}
|
|
30
30
|
return bytes;
|
|
31
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Note: if input can be very large (larger than the max string size), callers should
|
|
34
|
+
* expect this to throw an error.
|
|
35
|
+
*/
|
|
32
36
|
export function encode(input) {
|
|
33
37
|
return new Promise((resolve, reject) => {
|
|
34
38
|
const reader = new FileReader();
|
|
35
|
-
reader.onerror = () => reject(new Error('failed to convert to base64'));
|
|
39
|
+
reader.onerror = () => reject(new Error('failed to convert to base64: internal error'));
|
|
36
40
|
reader.onload = () => {
|
|
41
|
+
// The input was too large to encode as a string. The caller should anticipate
|
|
42
|
+
// this and use a workaround. See TimelinePanel.ts innerSaveToFile for an example.
|
|
43
|
+
// For more information, see crbug.com/436482118.
|
|
44
|
+
if (reader.result === '') {
|
|
45
|
+
reject(new Error('failed to convert to base64: input too large to encode as base64 string'));
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
// This string can be very large, so take care to not double memory. `split`
|
|
49
|
+
// was used here before, which always results in new strings in V8. By using
|
|
50
|
+
// slice instead, we leverage the sliced string optimization in V8 and avoid
|
|
51
|
+
// doubling the memory requirement (even if temporarily: that is a potential
|
|
52
|
+
// source of OOM crashes given large enough input, such as is common with
|
|
53
|
+
// Performance traces).
|
|
37
54
|
const blobAsUrl = reader.result;
|
|
38
|
-
const
|
|
55
|
+
const index = blobAsUrl.indexOf(',');
|
|
56
|
+
const base64 = blobAsUrl.slice(index + 1);
|
|
39
57
|
resolve(base64);
|
|
40
58
|
};
|
|
41
59
|
reader.readAsDataURL(new Blob([input]));
|
|
@@ -8,7 +8,14 @@ export const debounce = function (func, delay) {
|
|
|
8
8
|
let timer;
|
|
9
9
|
const debounced = (...args) => {
|
|
10
10
|
clearTimeout(timer);
|
|
11
|
-
timer = setTimeout(() => func(...args), delay);
|
|
11
|
+
timer = setTimeout(() => func(...args), testDebounceOverride ? 0 : delay);
|
|
12
12
|
};
|
|
13
13
|
return debounced;
|
|
14
14
|
};
|
|
15
|
+
let testDebounceOverride = false;
|
|
16
|
+
export function enableTestOverride() {
|
|
17
|
+
testDebounceOverride = true;
|
|
18
|
+
}
|
|
19
|
+
export function disableTestOverride() {
|
|
20
|
+
testDebounceOverride = false;
|
|
21
|
+
}
|
|
@@ -64,3 +64,14 @@ export function compressStream(stream) {
|
|
|
64
64
|
const cs = new CompressionStream('gzip');
|
|
65
65
|
return stream.pipeThrough(cs);
|
|
66
66
|
}
|
|
67
|
+
export function createMonitoredStream(stream, onProgress) {
|
|
68
|
+
let bytesRead = 0;
|
|
69
|
+
const progressTransformer = new TransformStream({
|
|
70
|
+
transform(chunk, controller) {
|
|
71
|
+
bytesRead += chunk.byteLength;
|
|
72
|
+
onProgress(bytesRead);
|
|
73
|
+
controller.enqueue(chunk);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
return stream.pipeThrough(progressTransformer);
|
|
77
|
+
}
|
|
@@ -57,7 +57,12 @@ export class ObjectWrapper {
|
|
|
57
57
|
// new listeners.
|
|
58
58
|
for (const listener of [...listeners]) {
|
|
59
59
|
if (!listener.disposed) {
|
|
60
|
-
|
|
60
|
+
try {
|
|
61
|
+
listener.listener.call(listener.thisObject, event);
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
console.error(`Event listener for ${String(eventType)} throw an error:`, err);
|
|
65
|
+
}
|
|
61
66
|
}
|
|
62
67
|
}
|
|
63
68
|
}
|
|
@@ -263,6 +263,9 @@ export class ParsedURL {
|
|
|
263
263
|
return '';
|
|
264
264
|
}
|
|
265
265
|
static extractName(url) {
|
|
266
|
+
if (url.endsWith('/')) {
|
|
267
|
+
url = url.slice(0, -1);
|
|
268
|
+
}
|
|
266
269
|
let index = url.lastIndexOf('/');
|
|
267
270
|
const pathAndQuery = index !== -1 ? url.substr(index + 1) : url;
|
|
268
271
|
index = pathAndQuery.indexOf('?');
|
|
@@ -229,6 +229,12 @@ export class ResourceType {
|
|
|
229
229
|
const regex = new RegExp('^application(.*json$|\/json\+.*)');
|
|
230
230
|
return regex.test(contentType) ? 'application/json' : contentType;
|
|
231
231
|
}
|
|
232
|
+
/**
|
|
233
|
+
* Checks whether the given MIME type represents JavaScript content.
|
|
234
|
+
*/
|
|
235
|
+
static isJavaScriptMimeType(mimeType) {
|
|
236
|
+
return mimeType === 'application/javascript' || mimeType === 'text/javascript';
|
|
237
|
+
}
|
|
232
238
|
/**
|
|
233
239
|
* Adds suffixes iff the mimeType is 'text/javascript' to denote whether the JS is minified or from
|
|
234
240
|
* a source map.
|
|
@@ -31,10 +31,6 @@ const UIStrings = {
|
|
|
31
31
|
* @description The UI destination when right clicking an item that can be revealed
|
|
32
32
|
*/
|
|
33
33
|
applicationPanel: 'Application panel',
|
|
34
|
-
/**
|
|
35
|
-
* @description The UI destination when right clicking an item that can be revealed
|
|
36
|
-
*/
|
|
37
|
-
securityPanel: 'Security panel',
|
|
38
34
|
/**
|
|
39
35
|
* @description The UI destination when right clicking an item that can be revealed
|
|
40
36
|
*/
|
|
@@ -158,7 +154,6 @@ export const RevealerDestination = {
|
|
|
158
154
|
TIMELINE_PANEL: i18nLazyString(UIStrings.timelinePanel),
|
|
159
155
|
APPLICATION_PANEL: i18nLazyString(UIStrings.applicationPanel),
|
|
160
156
|
SOURCES_PANEL: i18nLazyString(UIStrings.sourcesPanel),
|
|
161
|
-
SECURITY_PANEL: i18nLazyString(UIStrings.securityPanel),
|
|
162
157
|
MEMORY_INSPECTOR_PANEL: i18nLazyString(UIStrings.memoryInspectorPanel),
|
|
163
158
|
ANIMATIONS_PANEL: i18nLazyString(UIStrings.animationsPanel),
|
|
164
159
|
};
|
|
@@ -162,18 +162,28 @@ export class Settings {
|
|
|
162
162
|
return this.#registry;
|
|
163
163
|
}
|
|
164
164
|
}
|
|
165
|
-
export
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
}
|
|
165
|
+
export class InMemoryStorage {
|
|
166
|
+
#store = new Map();
|
|
167
|
+
register(_setting) {
|
|
168
|
+
}
|
|
169
|
+
set(key, value) {
|
|
170
|
+
this.#store.set(key, value);
|
|
171
|
+
}
|
|
172
|
+
get(key) {
|
|
173
|
+
return this.#store.get(key);
|
|
174
|
+
}
|
|
175
|
+
remove(key) {
|
|
176
|
+
this.#store.delete(key);
|
|
177
|
+
}
|
|
178
|
+
clear() {
|
|
179
|
+
this.#store.clear();
|
|
180
|
+
}
|
|
181
|
+
}
|
|
172
182
|
export class SettingsStorage {
|
|
173
183
|
object;
|
|
174
184
|
backingStore;
|
|
175
185
|
storagePrefix;
|
|
176
|
-
constructor(object, backingStore =
|
|
186
|
+
constructor(object, backingStore = new InMemoryStorage(), storagePrefix = '') {
|
|
177
187
|
this.object = object;
|
|
178
188
|
this.backingStore = backingStore;
|
|
179
189
|
this.storagePrefix = storagePrefix;
|
|
@@ -194,6 +194,12 @@ export class AidaClient {
|
|
|
194
194
|
if (!InspectorFrontendHostInstance.doAidaConversation) {
|
|
195
195
|
throw new Error('doAidaConversation is not available');
|
|
196
196
|
}
|
|
197
|
+
// Disable logging for now.
|
|
198
|
+
// For context, see b/454563259#comment35.
|
|
199
|
+
// We should be able to remove this ~end of April.
|
|
200
|
+
if (Root.Runtime.hostConfig.devToolsGeminiRebranding?.enabled) {
|
|
201
|
+
request.metadata.disable_user_content_logging = true;
|
|
202
|
+
}
|
|
197
203
|
const stream = (() => {
|
|
198
204
|
let { promise, resolve, reject } = Promise.withResolvers();
|
|
199
205
|
options?.signal?.addEventListener('abort', () => {
|
|
@@ -316,6 +322,12 @@ export class AidaClient {
|
|
|
316
322
|
};
|
|
317
323
|
}
|
|
318
324
|
registerClientEvent(clientEvent) {
|
|
325
|
+
// Disable logging for now.
|
|
326
|
+
// For context, see b/454563259#comment35.
|
|
327
|
+
// We should be able to remove this ~end of April.
|
|
328
|
+
if (Root.Runtime.hostConfig.devToolsGeminiRebranding?.enabled) {
|
|
329
|
+
clientEvent.disable_user_content_logging = true;
|
|
330
|
+
}
|
|
319
331
|
const { promise, resolve } = Promise.withResolvers();
|
|
320
332
|
InspectorFrontendHostInstance.registerAidaClientEvent(JSON.stringify({
|
|
321
333
|
client: CLIENT_NAME,
|
|
@@ -328,6 +340,12 @@ export class AidaClient {
|
|
|
328
340
|
if (!InspectorFrontendHostInstance.aidaCodeComplete) {
|
|
329
341
|
throw new Error('aidaCodeComplete is not available');
|
|
330
342
|
}
|
|
343
|
+
// Disable logging for now.
|
|
344
|
+
// For context, see b/454563259#comment35.
|
|
345
|
+
// We should be able to remove this ~end of April.
|
|
346
|
+
if (Root.Runtime.hostConfig.devToolsGeminiRebranding?.enabled) {
|
|
347
|
+
request.metadata.disable_user_content_logging = true;
|
|
348
|
+
}
|
|
331
349
|
const { promise, resolve } = Promise.withResolvers();
|
|
332
350
|
InspectorFrontendHostInstance.aidaCodeComplete(JSON.stringify(request), resolve);
|
|
333
351
|
const completeCodeResult = await promise;
|
|
@@ -369,6 +387,12 @@ export class AidaClient {
|
|
|
369
387
|
return { generatedSamples, metadata };
|
|
370
388
|
}
|
|
371
389
|
async generateCode(request, options) {
|
|
390
|
+
// Disable logging for now.
|
|
391
|
+
// For context, see b/454563259#comment35.
|
|
392
|
+
// We should be able to remove this ~end of April.
|
|
393
|
+
if (Root.Runtime.hostConfig.devToolsGeminiRebranding?.enabled) {
|
|
394
|
+
request.metadata.disable_user_content_logging = true;
|
|
395
|
+
}
|
|
372
396
|
const response = await DispatchHttpRequestClient.makeHttpRequest({
|
|
373
397
|
service: SERVICE_NAME,
|
|
374
398
|
path: '/v1/aida:generateCode',
|
package/build/vendor/chrome-devtools-frontend/front_end/core/host/InspectorFrontendHostStub.js
CHANGED
|
@@ -87,6 +87,9 @@ export class InspectorFrontendHostStub {
|
|
|
87
87
|
setInjectedScriptForOrigin(_origin, _script) {
|
|
88
88
|
}
|
|
89
89
|
inspectedURLChanged(url) {
|
|
90
|
+
if (!('document' in globalThis)) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
90
93
|
document.title = i18nString(UIStrings.devtoolsS, { PH1: url.replace(/^https?:\/\//, '') });
|
|
91
94
|
}
|
|
92
95
|
copyText(text) {
|
|
@@ -175,6 +178,12 @@ export class InspectorFrontendHostStub {
|
|
|
175
178
|
}
|
|
176
179
|
this.recordedPerformanceHistograms.push({ histogramName, duration });
|
|
177
180
|
}
|
|
181
|
+
recordPerformanceHistogramMedium(histogramName, duration) {
|
|
182
|
+
if (this.recordedPerformanceHistograms.length >= MAX_RECORDED_HISTOGRAMS_SIZE) {
|
|
183
|
+
this.recordedPerformanceHistograms.shift();
|
|
184
|
+
}
|
|
185
|
+
this.recordedPerformanceHistograms.push({ histogramName, duration });
|
|
186
|
+
}
|
|
178
187
|
recordUserMetricsAction(_umaName) {
|
|
179
188
|
}
|
|
180
189
|
recordNewBadgeUsage(_featureName) {
|
|
@@ -297,9 +306,6 @@ export class InspectorFrontendHostStub {
|
|
|
297
306
|
devToolsFlexibleLayout: {
|
|
298
307
|
verticalDrawerEnabled: true,
|
|
299
308
|
},
|
|
300
|
-
devToolsStartingStyleDebugging: {
|
|
301
|
-
enabled: false,
|
|
302
|
-
},
|
|
303
309
|
};
|
|
304
310
|
if ('hostConfigForTesting' in globalThis) {
|
|
305
311
|
const { hostConfigForTesting } = globalThis;
|
|
@@ -427,4 +433,6 @@ export class InspectorFrontendHostStub {
|
|
|
427
433
|
}
|
|
428
434
|
recordFunctionCall(_event) {
|
|
429
435
|
}
|
|
436
|
+
setChromeFlag(_flagName, _value) {
|
|
437
|
+
}
|
|
430
438
|
}
|
|
@@ -169,7 +169,7 @@ async function fetchToString(url) {
|
|
|
169
169
|
}
|
|
170
170
|
function canBeRemoteFilePath(url) {
|
|
171
171
|
try {
|
|
172
|
-
const urlObject = new URL(url);
|
|
172
|
+
const urlObject = new URL(new URL(url).toString()); // Normalize first.
|
|
173
173
|
return urlObject.protocol === 'file:' && urlObject.host !== '';
|
|
174
174
|
}
|
|
175
175
|
catch {
|