chrome-devtools-mcp 0.6.0 → 0.7.0
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 +13 -6
- package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Color.js +13 -9
- package/build/node_modules/chrome-devtools-frontend/front_end/core/common/ColorConverter.js +9 -7
- package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Gzip.js +1 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/core/common/MapWithDefault.js +5 -3
- package/build/node_modules/chrome-devtools-frontend/front_end/core/common/ResourceType.js +0 -11
- package/build/node_modules/chrome-devtools-frontend/front_end/core/common/ReturnToPanel.js +6 -4
- package/build/node_modules/chrome-devtools-frontend/front_end/core/host/AidaClient.js +1 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/core/host/GdpClient.js +116 -59
- package/build/node_modules/chrome-devtools-frontend/front_end/core/host/Platform.js +5 -3
- package/build/node_modules/chrome-devtools-frontend/front_end/core/host/UserMetrics.js +6 -4
- package/build/node_modules/chrome-devtools-frontend/front_end/core/platform/ArrayUtilities.js +1 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/core/platform/StringUtilities.js +33 -31
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSMetadata.js +4 -2
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSPropertyParser.js +11 -9
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSPropertyParserMatchers.js +19 -13
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/ChildTargetManager.js +30 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/DOMModel.js +1 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/EventBreakpointsModel.js +4 -2
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/HttpReasonPhraseStrings.js +4 -2
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/NetworkManager.js +9 -41
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/NetworkRequest.js +0 -14
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/PageResourceLoader.js +1 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/PreloadingModel.js +7 -5
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/RehydratingConnection.js +1 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/RemoteObject.js +1 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/ResourceTreeModel.js +1 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/ScreenCaptureModel.js +20 -18
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/Target.js +7 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/TraceObject.js +2 -2
- package/build/node_modules/chrome-devtools-frontend/front_end/generated/Deprecation.js +4 -4
- package/build/node_modules/chrome-devtools-frontend/front_end/generated/InspectorBackendCommands.js +2 -2
- package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/NetworkRequestFormatter.js +30 -3
- package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/performance/AIContext.js +18 -4
- package/build/node_modules/chrome-devtools-frontend/front_end/models/crux-manager/CrUXManager.js +1 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/models/network_time_calculator/RequestTimeRanges.js +6 -4
- package/build/node_modules/chrome-devtools-frontend/front_end/models/source_map_scopes/NamesResolver.js +7 -5
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/LanternComputationData.js +1 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/extras/TraceTree.js +1 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/FramesHandler.js +7 -5
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/LayoutShiftsHandler.js +8 -4
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/NetworkRequestsHandler.js +17 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/helpers.js +1 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/helpers/Timing.js +4 -2
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/helpers/Trace.js +8 -4
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/DocumentLatency.js +10 -10
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/INPBreakdown.js +12 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/LCPBreakdown.js +11 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/NetworkDependencyTree.js +2 -2
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/types/TraceEvents.js +5 -3
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace_source_maps_resolver/SourceMapsResolver.js +1 -1
- package/build/src/McpContext.js +24 -7
- package/build/src/McpResponse.js +49 -20
- package/build/src/Mutex.js +3 -6
- package/build/src/browser.js +6 -5
- package/build/src/cli.js +10 -2
- package/build/src/formatters/consoleFormatter.js +1 -1
- package/build/src/formatters/networkFormatter.js +44 -0
- package/build/src/tools/emulation.js +13 -2
- package/build/src/tools/performance.js +3 -4
- package/build/src/tools/screenshot.js +2 -3
- package/build/src/trace-processing/parse.js +7 -6
- package/package.json +7 -6
package/README.md
CHANGED
|
@@ -29,7 +29,7 @@ MCP clients.
|
|
|
29
29
|
|
|
30
30
|
## Requirements
|
|
31
31
|
|
|
32
|
-
- [Node.js
|
|
32
|
+
- [Node.js](https://nodejs.org/) v20.19 or a newer [latest maintenance LTS](https://github.com/nodejs/Release#release-schedule) version.
|
|
33
33
|
- [Chrome](https://www.google.com/chrome/) current stable version or newer.
|
|
34
34
|
- [npm](https://www.npmjs.com/).
|
|
35
35
|
|
|
@@ -111,7 +111,7 @@ Start the dialog to add a new MCP server by running:
|
|
|
111
111
|
/mcp add
|
|
112
112
|
```
|
|
113
113
|
|
|
114
|
-
Configure the following fields and press `
|
|
114
|
+
Configure the following fields and press `CTRL+S` to save the configuration:
|
|
115
115
|
|
|
116
116
|
- **Server name:** `chrome-devtools`
|
|
117
117
|
- **Server Type:** `[1] Local`
|
|
@@ -185,6 +185,13 @@ The same way chrome-devtools-mcp can be configured for JetBrains Junie in `Setti
|
|
|
185
185
|
[<img src="https://img.shields.io/badge/Visual_Studio-Install-C16FDE?logo=visualstudio&logoColor=white" alt="Install in Visual Studio">](https://vs-open.link/mcp-install?%7B%22name%22%3A%22chrome-devtools%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22chrome-devtools-mcp%40latest%22%5D%7D)
|
|
186
186
|
</details>
|
|
187
187
|
|
|
188
|
+
<details>
|
|
189
|
+
<summary>Warp</summary>
|
|
190
|
+
|
|
191
|
+
Go to `Settings | AI | Manage MCP Servers` -> `+ Add` to [add an MCP Server](https://docs.warp.dev/knowledge-and-collaboration/mcp#adding-an-mcp-server). Use the config provided above.
|
|
192
|
+
|
|
193
|
+
</details>
|
|
194
|
+
|
|
188
195
|
### Your first prompt
|
|
189
196
|
|
|
190
197
|
Enter the following prompt in your MCP Client to check if everything is working:
|
|
@@ -273,7 +280,7 @@ The Chrome DevTools MCP server supports the following configuration option:
|
|
|
273
280
|
- **Type:** string
|
|
274
281
|
|
|
275
282
|
- **`--viewport`**
|
|
276
|
-
Initial viewport size for the
|
|
283
|
+
Initial viewport size for the Chrome instances started by the server. For example, `1280x720`. In headless mode, max size is 3840x2160px.
|
|
277
284
|
- **Type:** string
|
|
278
285
|
|
|
279
286
|
- **`--proxyServer`**
|
|
@@ -313,8 +320,8 @@ You can also run `npx chrome-devtools-mcp@latest --help` to see all available co
|
|
|
313
320
|
`chrome-devtools-mcp` starts a Chrome's stable channel instance using the following user
|
|
314
321
|
data directory:
|
|
315
322
|
|
|
316
|
-
- Linux /
|
|
317
|
-
-
|
|
323
|
+
- Linux / macOS: `$HOME/.cache/chrome-devtools-mcp/chrome-profile-$CHANNEL`
|
|
324
|
+
- Windows: `%HOMEPATH%/.cache/chrome-devtools-mcp/chrome-profile-$CHANNEL`
|
|
318
325
|
|
|
319
326
|
The user data directory is not cleared between runs and shared across
|
|
320
327
|
all instances of `chrome-devtools-mcp`. Set the `isolated` option to `true`
|
|
@@ -329,5 +336,5 @@ Some MCP clients allow sandboxing the MCP server using macOS Seatbelt or Linux
|
|
|
329
336
|
containers. If sandboxes are enabled, `chrome-devtools-mcp` is not able to start
|
|
330
337
|
Chrome that requires permissions to create its own sandboxes. As a workaround,
|
|
331
338
|
either disable sandboxing for `chrome-devtools-mcp` in your MCP client or use
|
|
332
|
-
`--
|
|
339
|
+
`--browser-url` to connect to a Chrome instance that you start manually outside
|
|
333
340
|
of the MCP client sandbox.
|
|
@@ -32,11 +32,13 @@
|
|
|
32
32
|
import * as Platform from '../platform/platform.js';
|
|
33
33
|
import { ColorConverter } from './ColorConverter.js';
|
|
34
34
|
import { blendColors, contrastRatioAPCA, desiredLuminanceAPCA, luminance, luminanceAPCA, rgbToHsl, rgbToHwb, } from './ColorUtils.js';
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
/**
|
|
36
|
+
* <hue> is defined as a <number> or <angle>
|
|
37
|
+
* and we hold this in degrees. However, after
|
|
38
|
+
* the conversions, these degrees can result in
|
|
39
|
+
* negative values. That's why we normalize the hue to be
|
|
40
|
+
* between [0 - 360].
|
|
41
|
+
**/
|
|
40
42
|
function normalizeHue(hue) {
|
|
41
43
|
// Even though it is highly unlikely, hue can be
|
|
42
44
|
// very negative like -400. The initial modulo
|
|
@@ -44,9 +46,11 @@ function normalizeHue(hue) {
|
|
|
44
46
|
// negative, it is between [-360, 0].
|
|
45
47
|
return ((hue % 360) + 360) % 360;
|
|
46
48
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
49
|
+
/**
|
|
50
|
+
* Parses angle in the form of
|
|
51
|
+
* `<angle>deg`, `<angle>turn`, `<angle>grad and `<angle>rad`
|
|
52
|
+
* and returns the canonicalized `degree`.
|
|
53
|
+
**/
|
|
50
54
|
function parseAngle(angleText) {
|
|
51
55
|
const angle = angleText.replace(/(deg|g?rad|turn)$/, '');
|
|
52
56
|
// @ts-expect-error: isNaN can accept strings
|
|
@@ -69,7 +73,7 @@ function parseAngle(angleText) {
|
|
|
69
73
|
// 1deg === 1deg ^_^
|
|
70
74
|
return number;
|
|
71
75
|
}
|
|
72
|
-
|
|
76
|
+
/** Returns the `Format` equivalent from the format text **/
|
|
73
77
|
export function getFormat(formatText) {
|
|
74
78
|
switch (formatText) {
|
|
75
79
|
case "hex" /* Format.HEX */:
|
|
@@ -37,13 +37,15 @@ class Matrix3x3 {
|
|
|
37
37
|
return dst;
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
40
|
+
/**
|
|
41
|
+
* A transfer function mapping encoded values to linear values,
|
|
42
|
+
* represented by this 7-parameter piecewise function:
|
|
43
|
+
*
|
|
44
|
+
* linear = sign(encoded) * (c*|encoded| + f) , 0 <= |encoded| < d
|
|
45
|
+
* = sign(encoded) * ((a*|encoded| + b)^g + e), d <= |encoded|
|
|
46
|
+
*
|
|
47
|
+
* (A simple gamma transfer function sets g to gamma and a to 1.)
|
|
48
|
+
**/
|
|
47
49
|
class TransferFunction {
|
|
48
50
|
g;
|
|
49
51
|
a;
|
|
@@ -43,7 +43,7 @@ export async function compress(str) {
|
|
|
43
43
|
const buffer = await gzipCodec(encoded, new CompressionStream('gzip'));
|
|
44
44
|
return buffer;
|
|
45
45
|
}
|
|
46
|
-
|
|
46
|
+
/** Private coder/decoder **/
|
|
47
47
|
function gzipCodec(buffer, codecStream) {
|
|
48
48
|
const { readable, writable } = new TransformStream();
|
|
49
49
|
const codecReadable = readable.pipeThrough(codecStream);
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
// Copyright 2024 The Chromium Authors
|
|
2
2
|
// Use of this source code is governed by a BSD-style license that can be
|
|
3
3
|
// found in the LICENSE file.
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Polyfill of https://github.com/tc39/proposal-upsert with a subclass.
|
|
6
|
+
*
|
|
7
|
+
* TODO: Once the proposal is merged, just replace `MapWithDefault` with `Map` and remove it.
|
|
8
|
+
**/
|
|
7
9
|
export class MapWithDefault extends Map {
|
|
8
10
|
getOrInsert(key, defaultValue) {
|
|
9
11
|
if (!this.has(key)) {
|
|
@@ -140,10 +140,6 @@ const UIStrings = {
|
|
|
140
140
|
* @description Name of a network initiator type
|
|
141
141
|
*/
|
|
142
142
|
preflight: 'Preflight',
|
|
143
|
-
/**
|
|
144
|
-
* @description Name of a network initiator type
|
|
145
|
-
*/
|
|
146
|
-
webbundle: 'WebBundle',
|
|
147
143
|
/**
|
|
148
144
|
* @description Name of a network initiator type for FedCM requests
|
|
149
145
|
*/
|
|
@@ -199,9 +195,6 @@ export class ResourceType {
|
|
|
199
195
|
if (mimeType === 'application/wasm') {
|
|
200
196
|
return resourceTypes.Wasm;
|
|
201
197
|
}
|
|
202
|
-
if (mimeType === 'application/webbundle') {
|
|
203
|
-
return resourceTypes.WebBundle;
|
|
204
|
-
}
|
|
205
198
|
return null;
|
|
206
199
|
}
|
|
207
200
|
static fromURL(url) {
|
|
@@ -299,9 +292,6 @@ export class ResourceType {
|
|
|
299
292
|
isFromSourceMap() {
|
|
300
293
|
return this.#name.startsWith('sm-');
|
|
301
294
|
}
|
|
302
|
-
isWebbundle() {
|
|
303
|
-
return this.#name === 'webbundle';
|
|
304
|
-
}
|
|
305
295
|
toString() {
|
|
306
296
|
return this.#name;
|
|
307
297
|
}
|
|
@@ -371,7 +361,6 @@ export const resourceTypes = {
|
|
|
371
361
|
Preflight: new ResourceType('preflight', i18nLazyString(UIStrings.preflight), resourceCategories.Other, true),
|
|
372
362
|
SourceMapScript: new ResourceType('sm-script', i18nLazyString(UIStrings.script), resourceCategories.Script, true),
|
|
373
363
|
SourceMapStyleSheet: new ResourceType('sm-stylesheet', i18nLazyString(UIStrings.stylesheet), resourceCategories.Stylesheet, true),
|
|
374
|
-
WebBundle: new ResourceType('webbundle', i18nLazyString(UIStrings.webbundle), resourceCategories.Other, false),
|
|
375
364
|
FedCM: new ResourceType('fedcm', i18nLazyString(UIStrings.fedcm), resourceCategories.Other, false),
|
|
376
365
|
};
|
|
377
366
|
const mimeTypeByName = new Map([
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
// Copyright 2025 The Chromium Authors
|
|
2
2
|
// Use of this source code is governed by a BSD-style license that can be
|
|
3
3
|
// found in the LICENSE file.
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Set instance of this class as flavor to mark what panel triggered the
|
|
6
|
+
* 'elements.toggle-element-search' action if it was not the elements panel.
|
|
7
|
+
* This will cause specified panel to be made visible instead of the elements
|
|
8
|
+
* panel after the inspection is done.
|
|
9
|
+
**/
|
|
8
10
|
export class ReturnToPanelFlavor {
|
|
9
11
|
viewId;
|
|
10
12
|
constructor(viewId) {
|
|
@@ -24,7 +24,7 @@ export var FunctionalityType;
|
|
|
24
24
|
FunctionalityType[FunctionalityType["EXPLAIN_ERROR"] = 2] = "EXPLAIN_ERROR";
|
|
25
25
|
FunctionalityType[FunctionalityType["AGENTIC_CHAT"] = 5] = "AGENTIC_CHAT";
|
|
26
26
|
})(FunctionalityType || (FunctionalityType = {}));
|
|
27
|
-
|
|
27
|
+
/** See: cs/aida.proto (google3). **/
|
|
28
28
|
export var ClientFeature;
|
|
29
29
|
(function (ClientFeature) {
|
|
30
30
|
// Unspecified client feature.
|
|
@@ -29,27 +29,49 @@ export var EmailPreference;
|
|
|
29
29
|
EmailPreference["ENABLED"] = "ENABLED";
|
|
30
30
|
EmailPreference["DISABLED"] = "DISABLED";
|
|
31
31
|
})(EmailPreference || (EmailPreference = {}));
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
export var GdpErrorType;
|
|
33
|
+
(function (GdpErrorType) {
|
|
34
|
+
GdpErrorType["HTTP_RESPONSE_UNAVAILABLE"] = "HTTP_RESPONSE_UNAVAILABLE";
|
|
35
|
+
GdpErrorType["NOT_FOUND"] = "NOT_FOUND";
|
|
36
|
+
})(GdpErrorType || (GdpErrorType = {}));
|
|
37
|
+
class GdpError extends Error {
|
|
38
|
+
type;
|
|
39
|
+
constructor(type, options) {
|
|
40
|
+
super(undefined, options);
|
|
41
|
+
this.type = type;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The `batchGet` awards endpoint returns badge names with an
|
|
46
|
+
* obfuscated user ID (e.g., `profiles/12345/awards/badge-name`).
|
|
47
|
+
* This function normalizes them to use `me` instead of the ID
|
|
48
|
+
* (e.g., `profiles/me/awards/badge-path`) to match the format
|
|
49
|
+
* used for client-side requests.
|
|
50
|
+
**/
|
|
37
51
|
function normalizeBadgeName(name) {
|
|
38
52
|
return name.replace(/profiles\/[^/]+\/awards\//, 'profiles/me/awards/');
|
|
39
53
|
}
|
|
40
54
|
export const GOOGLE_DEVELOPER_PROGRAM_PROFILE_LINK = 'https://developers.google.com/profile/u/me';
|
|
41
55
|
async function makeHttpRequest(request) {
|
|
42
56
|
if (!isGdpProfilesAvailable()) {
|
|
43
|
-
|
|
57
|
+
throw new GdpError(GdpErrorType.HTTP_RESPONSE_UNAVAILABLE);
|
|
44
58
|
}
|
|
45
59
|
const response = await new Promise(resolve => {
|
|
46
60
|
InspectorFrontendHostInstance.dispatchHttpRequest(request, resolve);
|
|
47
61
|
});
|
|
48
62
|
debugLog({ request, response });
|
|
63
|
+
if (response.statusCode === 404) {
|
|
64
|
+
throw new GdpError(GdpErrorType.NOT_FOUND);
|
|
65
|
+
}
|
|
49
66
|
if ('response' in response && response.statusCode === 200) {
|
|
50
|
-
|
|
67
|
+
try {
|
|
68
|
+
return JSON.parse(response.response);
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
throw new GdpError(GdpErrorType.HTTP_RESPONSE_UNAVAILABLE, { cause: err });
|
|
72
|
+
}
|
|
51
73
|
}
|
|
52
|
-
|
|
74
|
+
throw new GdpError(GdpErrorType.HTTP_RESPONSE_UNAVAILABLE);
|
|
53
75
|
}
|
|
54
76
|
const SERVICE_NAME = 'gdpService';
|
|
55
77
|
let gdpClientInstance = null;
|
|
@@ -64,21 +86,41 @@ export class GdpClient {
|
|
|
64
86
|
}
|
|
65
87
|
return gdpClientInstance;
|
|
66
88
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
89
|
+
/**
|
|
90
|
+
* Fetches the user's GDP profile and eligibility status.
|
|
91
|
+
*
|
|
92
|
+
* It first attempts to fetch the profile. If the profile is not found
|
|
93
|
+
* (a `NOT_FOUND` error), this is handled gracefully by treating the profile
|
|
94
|
+
* as `null` and then proceeding to check for eligibility.
|
|
95
|
+
*
|
|
96
|
+
* @returns A promise that resolves with an object containing the `profile`
|
|
97
|
+
* and `isEligible` status, or `null` if an unexpected error occurs.
|
|
98
|
+
*/
|
|
99
|
+
async getProfile() {
|
|
100
|
+
try {
|
|
101
|
+
const profile = await this.#getProfile();
|
|
70
102
|
return {
|
|
71
|
-
|
|
103
|
+
profile,
|
|
72
104
|
isEligible: true,
|
|
73
105
|
};
|
|
74
106
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
if (err instanceof GdpError && err.type === GdpErrorType.HTTP_RESPONSE_UNAVAILABLE) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
try {
|
|
113
|
+
const checkEligibilityResponse = await this.#checkEligibility();
|
|
114
|
+
return {
|
|
115
|
+
profile: null,
|
|
116
|
+
isEligible: checkEligibilityResponse.createProfile === EligibilityStatus.ELIGIBLE,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
80
122
|
}
|
|
81
|
-
async getProfile() {
|
|
123
|
+
async #getProfile() {
|
|
82
124
|
if (this.#cachedProfilePromise) {
|
|
83
125
|
return await this.#cachedProfilePromise;
|
|
84
126
|
}
|
|
@@ -86,14 +128,13 @@ export class GdpClient {
|
|
|
86
128
|
service: SERVICE_NAME,
|
|
87
129
|
path: '/v1beta1/profile:get',
|
|
88
130
|
method: 'GET',
|
|
89
|
-
})
|
|
90
|
-
const profile = await this.#cachedProfilePromise;
|
|
91
|
-
if (profile) {
|
|
131
|
+
}).then(profile => {
|
|
92
132
|
this.#cachedEligibilityPromise = Promise.resolve({ createProfile: EligibilityStatus.ELIGIBLE });
|
|
93
|
-
|
|
94
|
-
|
|
133
|
+
return profile;
|
|
134
|
+
});
|
|
135
|
+
return await this.#cachedProfilePromise;
|
|
95
136
|
}
|
|
96
|
-
async checkEligibility() {
|
|
137
|
+
async #checkEligibility() {
|
|
97
138
|
if (this.#cachedEligibilityPromise) {
|
|
98
139
|
return await this.#cachedEligibilityPromise;
|
|
99
140
|
}
|
|
@@ -105,52 +146,60 @@ export class GdpClient {
|
|
|
105
146
|
* @returns null if the request fails, the awarded badge names otherwise.
|
|
106
147
|
*/
|
|
107
148
|
async getAwardedBadgeNames({ names }) {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
149
|
+
try {
|
|
150
|
+
const response = await makeHttpRequest({
|
|
151
|
+
service: SERVICE_NAME,
|
|
152
|
+
path: '/v1beta1/profiles/me/awards:batchGet',
|
|
153
|
+
method: 'GET',
|
|
154
|
+
queryParams: {
|
|
155
|
+
allowMissing: 'true',
|
|
156
|
+
names,
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
return new Set(response.awards?.map(award => normalizeBadgeName(award.name)) ?? []);
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
118
162
|
return null;
|
|
119
163
|
}
|
|
120
|
-
return new Set(result.awards?.map(award => normalizeBadgeName(award.name)) ?? []);
|
|
121
|
-
}
|
|
122
|
-
async isEligibleToCreateProfile() {
|
|
123
|
-
return (await this.checkEligibility())?.createProfile === EligibilityStatus.ELIGIBLE;
|
|
124
164
|
}
|
|
125
165
|
async createProfile({ user, emailPreference }) {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
166
|
+
try {
|
|
167
|
+
const response = await makeHttpRequest({
|
|
168
|
+
service: SERVICE_NAME,
|
|
169
|
+
path: '/v1beta1/profiles',
|
|
170
|
+
method: 'POST',
|
|
171
|
+
body: JSON.stringify({
|
|
172
|
+
user,
|
|
173
|
+
newsletter_email: emailPreference,
|
|
174
|
+
}),
|
|
175
|
+
});
|
|
136
176
|
this.#clearCache();
|
|
177
|
+
return response;
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
return null;
|
|
137
181
|
}
|
|
138
|
-
return result;
|
|
139
182
|
}
|
|
140
183
|
#clearCache() {
|
|
141
184
|
this.#cachedProfilePromise = undefined;
|
|
142
185
|
this.#cachedEligibilityPromise = undefined;
|
|
143
186
|
}
|
|
144
|
-
createAward({ name }) {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
187
|
+
async createAward({ name }) {
|
|
188
|
+
try {
|
|
189
|
+
const response = await makeHttpRequest({
|
|
190
|
+
service: SERVICE_NAME,
|
|
191
|
+
path: '/v1beta1/profiles/me/awards',
|
|
192
|
+
method: 'POST',
|
|
193
|
+
body: JSON.stringify({
|
|
194
|
+
awardingUri: 'devtools://devtools',
|
|
195
|
+
name,
|
|
196
|
+
})
|
|
197
|
+
});
|
|
198
|
+
return response;
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
154
203
|
}
|
|
155
204
|
}
|
|
156
205
|
function isDebugMode() {
|
|
@@ -182,5 +231,13 @@ export function getGdpProfilesEnterprisePolicy() {
|
|
|
182
231
|
return (Root.Runtime.hostConfig.devToolsGdpProfilesAvailability?.enterprisePolicyValue ??
|
|
183
232
|
Root.Runtime.GdpProfilesEnterprisePolicyValue.DISABLED);
|
|
184
233
|
}
|
|
234
|
+
export function isBadgesEnabled() {
|
|
235
|
+
const isBadgesEnabledByEnterprisePolicy = getGdpProfilesEnterprisePolicy() === Root.Runtime.GdpProfilesEnterprisePolicyValue.ENABLED;
|
|
236
|
+
const isBadgesEnabledByFeatureFlag = Boolean(Root.Runtime.hostConfig.devToolsGdpProfiles?.badgesEnabled);
|
|
237
|
+
return isBadgesEnabledByEnterprisePolicy && isBadgesEnabledByFeatureFlag;
|
|
238
|
+
}
|
|
239
|
+
export function isStarterBadgeEnabled() {
|
|
240
|
+
return Boolean(Root.Runtime.hostConfig.devToolsGdpProfiles?.starterBadgeEnabled);
|
|
241
|
+
}
|
|
185
242
|
// @ts-expect-error
|
|
186
243
|
globalThis.setDebugGdpIntegrationEnabled = setDebugGdpIntegrationEnabled;
|
|
@@ -24,9 +24,11 @@ export function isWin() {
|
|
|
24
24
|
}
|
|
25
25
|
return _isWin;
|
|
26
26
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
/**
|
|
28
|
+
* In Chrome Layout tests the imported 'Platform' object is not writable/
|
|
29
|
+
* configurable, which prevents us from monkey-patching 'Platform''s methods.
|
|
30
|
+
* We circumvent this by adding 'setPlatformForTests'.
|
|
31
|
+
**/
|
|
30
32
|
export function setPlatformForTests(platform) {
|
|
31
33
|
_platform = platform;
|
|
32
34
|
_isMac = undefined;
|
|
@@ -231,9 +231,11 @@ export class UserMetrics {
|
|
|
231
231
|
* 1. Delete the line with the unneeded value
|
|
232
232
|
* 2. Do not update any 'MAX_VALUE' or any other value.
|
|
233
233
|
*/
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
234
|
+
/**
|
|
235
|
+
* Codes below are used to collect UMA histograms in the Chromium port.
|
|
236
|
+
* Do not change the values below, additional actions are needed on the Chromium side
|
|
237
|
+
* in order to add more codes.
|
|
238
|
+
**/
|
|
237
239
|
export var Action;
|
|
238
240
|
(function (Action) {
|
|
239
241
|
/* eslint-disable @typescript-eslint/naming-convention */
|
|
@@ -706,7 +708,7 @@ export var DevtoolsExperiments;
|
|
|
706
708
|
// Increment this when new experiments are added.
|
|
707
709
|
DevtoolsExperiments[DevtoolsExperiments["MAX_VALUE"] = 110] = "MAX_VALUE";
|
|
708
710
|
})(DevtoolsExperiments || (DevtoolsExperiments = {}));
|
|
709
|
-
|
|
711
|
+
/** Update DevToolsIssuesPanelIssueExpanded from tools/metrics/histograms/enums.xml if new enum is added. **/
|
|
710
712
|
export var IssueExpanded;
|
|
711
713
|
(function (IssueExpanded) {
|
|
712
714
|
/* eslint-disable @typescript-eslint/naming-convention */
|
package/build/node_modules/chrome-devtools-frontend/front_end/core/platform/ArrayUtilities.js
CHANGED
|
@@ -193,7 +193,7 @@ export function nearestIndexFromBeginning(arr, predicate) {
|
|
|
193
193
|
export function nearestIndexFromEnd(arr, predicate) {
|
|
194
194
|
return nearestIndex(arr, predicate, "END" /* NearestSearchStart.END */);
|
|
195
195
|
}
|
|
196
|
-
|
|
196
|
+
/** Type guard for ensuring that `arr` does not contain null or undefined **/
|
|
197
197
|
export function arrayDoesNotContainNullOrUndefined(arr) {
|
|
198
198
|
return !arr.includes(null) && !arr.includes(undefined);
|
|
199
199
|
}
|
package/build/node_modules/chrome-devtools-frontend/front_end/core/platform/StringUtilities.js
CHANGED
|
@@ -233,7 +233,7 @@ export const stripLineBreaks = (inputStr) => {
|
|
|
233
233
|
};
|
|
234
234
|
const EXTENDED_KEBAB_CASE_REGEXP = /^([a-z0-9]+(?:-[a-z0-9]+)*\.)*[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
235
235
|
/**
|
|
236
|
-
* Tests if the `inputStr` is following the extended Kebab Case naming
|
|
236
|
+
* Tests if the `inputStr` is following the extended Kebab Case naming convention,
|
|
237
237
|
* where words are separated with either a dash (`-`) or a dot (`.`), and all
|
|
238
238
|
* characters must be lower-case alphanumeric.
|
|
239
239
|
*
|
|
@@ -351,7 +351,7 @@ export const escapeForRegExp = (str) => {
|
|
|
351
351
|
};
|
|
352
352
|
export const naturalOrderComparator = (a, b) => {
|
|
353
353
|
const chunk = /^\d+|^\D+/;
|
|
354
|
-
let
|
|
354
|
+
let chunkA, chunkB, numA, numB;
|
|
355
355
|
while (true) {
|
|
356
356
|
if (a) {
|
|
357
357
|
if (!b) {
|
|
@@ -364,33 +364,33 @@ export const naturalOrderComparator = (a, b) => {
|
|
|
364
364
|
}
|
|
365
365
|
return 0;
|
|
366
366
|
}
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
if (
|
|
367
|
+
chunkA = a.match(chunk)[0];
|
|
368
|
+
chunkB = b.match(chunk)[0];
|
|
369
|
+
numA = !Number.isNaN(Number(chunkA));
|
|
370
|
+
numB = !Number.isNaN(Number(chunkB));
|
|
371
|
+
if (numA && !numB) {
|
|
372
372
|
return -1;
|
|
373
373
|
}
|
|
374
|
-
if (
|
|
374
|
+
if (numB && !numA) {
|
|
375
375
|
return 1;
|
|
376
376
|
}
|
|
377
|
-
if (
|
|
378
|
-
const diff = Number(
|
|
377
|
+
if (numA && numB) {
|
|
378
|
+
const diff = Number(chunkA) - Number(chunkB);
|
|
379
379
|
if (diff) {
|
|
380
380
|
return diff;
|
|
381
381
|
}
|
|
382
|
-
if (
|
|
383
|
-
if (!Number(
|
|
384
|
-
return
|
|
382
|
+
if (chunkA.length !== chunkB.length) {
|
|
383
|
+
if (!Number(chunkA) && !Number(chunkB)) { // chunks are strings of all 0s (special case)
|
|
384
|
+
return chunkA.length - chunkB.length;
|
|
385
385
|
}
|
|
386
|
-
return
|
|
386
|
+
return chunkB.length - chunkA.length;
|
|
387
387
|
}
|
|
388
388
|
}
|
|
389
|
-
else if (
|
|
390
|
-
return (
|
|
389
|
+
else if (chunkA !== chunkB) {
|
|
390
|
+
return (chunkA < chunkB) ? -1 : 1;
|
|
391
391
|
}
|
|
392
|
-
a = a.substring(
|
|
393
|
-
b = b.substring(
|
|
392
|
+
a = a.substring(chunkA.length);
|
|
393
|
+
b = b.substring(chunkB.length);
|
|
394
394
|
}
|
|
395
395
|
};
|
|
396
396
|
export const base64ToSize = function (content) {
|
|
@@ -458,19 +458,21 @@ export const createPlainTextSearchRegex = function (query, flags) {
|
|
|
458
458
|
export const toLowerCaseString = function (input) {
|
|
459
459
|
return input.toLowerCase();
|
|
460
460
|
};
|
|
461
|
+
/**
|
|
462
|
+
* 1: two or more consecutive uppercase letters. This is useful for identifying acronyms
|
|
463
|
+
* 2: lookahead assertion that matches a word boundary
|
|
464
|
+
* 3: numeronym: single letter followed by number and another letter
|
|
465
|
+
* 4: word starting with an optional uppercase letter
|
|
466
|
+
* 5: single digit followed by word to handle '3D' or '2px' (this might be controverial)
|
|
467
|
+
* 6: single uppercase letter or number
|
|
468
|
+
* 7: a dot character. We extract it into a separate word and remove dashes around it later.
|
|
469
|
+
* This is makes more sense conceptually and allows accounting for all possible word variants.
|
|
470
|
+
* Making dot a part of a word prevent us from handling acronyms or numeronyms after the word
|
|
471
|
+
* correctly without making the RegExp prohibitively complicated.
|
|
472
|
+
* https://regex101.com/r/FhMVKc/1
|
|
473
|
+
* <---1---><------------2-----------> <---------3--------> <-----4----> <------5-----> <-----6----> <7>
|
|
474
|
+
*/
|
|
461
475
|
const WORD = /[A-Z]{2,}(?=[A-Z0-9][a-z0-9]+|\b|_)|[A-Za-z][0-9]+[a-z]?|[A-Z]?[a-z]+|[0-9][A-Za-z]+|[A-Z]|[0-9]+|[.]/g;
|
|
462
|
-
// <---1---><------------2-----------> <---------3--------> <-----4----> <------5-----> <-----6----> <7>
|
|
463
|
-
// 1: two or more consecutive uppercase letters. This is useful for identifying acronyms
|
|
464
|
-
// 2: lookahead assertion that matches a word boundary
|
|
465
|
-
// 3: numeronym: single letter followed by number and another letter
|
|
466
|
-
// 4: word starting with an optional uppercase letter
|
|
467
|
-
// 5: single digit followed by word to handle '3D' or '2px' (this might be controverial)
|
|
468
|
-
// 6: single uppercase letter or number
|
|
469
|
-
// 7: a dot character. We extract it into a separate word and remove dashes around it later.
|
|
470
|
-
// This is makes more sense conceptually and allows accounting for all possible word variants.
|
|
471
|
-
// Making dot a part of a word prevent us from handling acronyms or numeronyms after the word
|
|
472
|
-
// correctly without making the RegExp prohibitively complicated.
|
|
473
|
-
// https://regex101.com/r/FhMVKc/1
|
|
474
476
|
export const toKebabCase = function (input) {
|
|
475
477
|
return (input.match?.(WORD)?.map(w => w.toLowerCase()).join('-').replaceAll('-.-', '.') || input);
|
|
476
478
|
};
|
|
@@ -507,7 +509,7 @@ export function toSnakeCase(text) {
|
|
|
507
509
|
.replace(/^_|_$/g, ''); // 5
|
|
508
510
|
return result;
|
|
509
511
|
}
|
|
510
|
-
|
|
512
|
+
/** Replaces the last occurrence of parameter `search` with parameter `replacement` in `input` **/
|
|
511
513
|
export const replaceLast = function (input, search, replacement) {
|
|
512
514
|
const replacementStartIndex = input.lastIndexOf(search);
|
|
513
515
|
if (replacementStartIndex === -1) {
|
|
@@ -279,8 +279,10 @@ export const CubicBezierKeywordValues = new Map([
|
|
|
279
279
|
['ease-in-out', 'cubic-bezier(0.42, 0, 0.58, 1)'],
|
|
280
280
|
['ease-out', 'cubic-bezier(0, 0, 0.58, 1)'],
|
|
281
281
|
]);
|
|
282
|
-
|
|
283
|
-
|
|
282
|
+
/**
|
|
283
|
+
* Spec: https://drafts.csswg.org/css-cascade/#defaulting-keywords
|
|
284
|
+
* https://drafts.csswg.org/css-cascade-5/#revert-layer
|
|
285
|
+
**/
|
|
284
286
|
export const CSSWideKeywords = [
|
|
285
287
|
"inherit" /* CSSWideKeyword.INHERIT */,
|
|
286
288
|
"initial" /* CSSWideKeyword.INITIAL */,
|