chrome-devtools-frontend 1.0.1697998 → 1.0.1698733
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/.agents/skills/version-control/SKILL.md +21 -9
- package/front_end/core/platform/HostRuntime.ts +12 -0
- package/front_end/core/platform/api/HostRuntime.ts +13 -0
- package/front_end/core/platform/browser/HostRuntime.ts +34 -0
- package/front_end/core/platform/node/HostRuntime.ts +40 -0
- package/front_end/core/sdk/AnimationModel.ts +12 -98
- package/front_end/core/sdk/CSSModel.ts +4 -2
- package/front_end/core/sdk/CSSPropertyParser.ts +4 -4
- package/front_end/core/sdk/CSSPropertyParserMatchers.ts +4 -44
- package/front_end/core/sdk/Connections.ts +6 -1
- package/front_end/core/sdk/ConsoleModel.ts +1 -12
- package/front_end/core/sdk/DOMModel.ts +3 -51
- package/front_end/core/sdk/DebuggerModel.ts +9 -8
- package/front_end/core/sdk/NetworkRequest.ts +1 -1
- package/front_end/core/sdk/PageFunctions.ts +187 -0
- package/front_end/core/sdk/Resource.ts +1 -1
- package/front_end/core/sdk/RuntimeModel.ts +1 -20
- package/front_end/core/sdk/Script.ts +21 -0
- package/front_end/core/sdk/SourceMap.ts +15 -2
- package/front_end/core/sdk/SourceMapCache.ts +13 -10
- package/front_end/core/sdk/SourceMapManager.ts +11 -9
- package/front_end/core/sdk/SourceMapScopeChainEntry.ts +59 -17
- package/front_end/core/sdk/SourceMapScopesInfo.ts +8 -2
- package/front_end/entrypoints/heap_snapshot_worker/ContextAnalyzer.ts +635 -0
- package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshot.ts +26 -12
- package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshotLoader.ts +16 -2
- package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshotWorkerDispatcher.ts +4 -4
- package/front_end/entrypoints/heap_snapshot_worker/heap_snapshot_worker.ts +2 -0
- package/front_end/entrypoints/shell/shell.ts +1 -1
- package/front_end/models/ai_assistance/DOMHelpers.ts +95 -0
- package/front_end/models/ai_assistance/EvaluateAction.ts +5 -42
- package/front_end/models/ai_assistance/ExtensionScope.ts +2 -31
- package/front_end/models/ai_assistance/agents/AiAgent.ts +2 -1
- package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +3 -4
- package/front_end/models/ai_assistance/ai_assistance.ts +2 -0
- package/front_end/models/ai_assistance/contexts/DOMNodeContext.ts +10 -7
- package/front_end/models/ai_assistance/contexts/FileContext.ts +2 -10
- package/front_end/models/ai_assistance/tools/GetSourceContent.ts +3 -2
- package/front_end/models/ai_assistance/tools/ListSources.ts +21 -14
- package/front_end/models/bindings/CompilerScriptMapping.ts +3 -10
- package/front_end/models/bindings/DebuggerLanguagePlugins.ts +48 -17
- package/front_end/models/bindings/ResourceScriptMapping.ts +2 -2
- package/front_end/models/bindings/StylesSourceMapping.ts +1 -1
- package/front_end/models/comment_manager/CD4ABridge.ts +55 -12
- package/front_end/models/comment_manager/CommentManager.ts +46 -91
- package/front_end/models/comment_manager/CommentThread.ts +145 -0
- package/front_end/models/comment_manager/comment_manager.ts +2 -0
- package/front_end/models/emulation/DeviceModeModel.ts +53 -0
- package/front_end/models/extensions/ExtensionAPI.ts +8 -6
- package/front_end/models/extensions/ExtensionEndpoint.ts +11 -8
- package/front_end/models/extensions/HostUrlPattern.ts +4 -3
- package/front_end/models/extensions/LanguageExtensionEndpoint.ts +3 -2
- package/front_end/models/extensions/RecorderExtensionEndpoint.ts +2 -1
- package/front_end/models/formatter/FormatterWorkerPool.ts +4 -6
- package/front_end/models/heap_snapshot/HeapSnapshotModel.ts +55 -0
- package/front_end/models/heap_snapshot/HeapSnapshotProxy.ts +18 -14
- package/front_end/models/issues_manager/IssuesManager.ts +5 -0
- package/front_end/models/issues_manager/WebInstallIssue.ts +63 -0
- package/front_end/models/issues_manager/descriptions/webInstallManifestMissingId.md +3 -0
- package/front_end/models/issues_manager/descriptions/webInstallManifestMissingNameOrShortName.md +3 -0
- package/front_end/models/issues_manager/descriptions/webInstallManifestParsingOrNetworkError.md +3 -0
- package/front_end/models/issues_manager/descriptions/webInstallNoManifest.md +3 -0
- package/front_end/models/issues_manager/descriptions/webInstallStartUrlInvalid.md +3 -0
- package/front_end/models/issues_manager/issues_manager.ts +2 -0
- package/front_end/models/workspace/UISourceCode.ts +20 -0
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +4 -6
- package/front_end/panels/ai_assistance/components/AIv2MarkdownRenderer.ts +4 -10
- package/front_end/{ui/comments → panels/common}/CommentThreadWidget.ts +4 -4
- package/front_end/{ui/comments → panels/common}/CommentsOverlayWidget.ts +39 -62
- package/front_end/{ui/comments → panels/common}/CommentsStatusBarPill.ts +5 -5
- package/front_end/panels/common/ExtensionServer.ts +2 -1
- package/front_end/{ui/comments → panels/common}/commentsOverlay.css +2 -1
- package/front_end/{ui/comments/comments-meta.ts → panels/common/common-meta.ts} +11 -11
- package/front_end/panels/common/common.ts +3 -0
- package/front_end/panels/elements/AdoptedStyleSheetTreeElement.ts +3 -3
- package/front_end/panels/elements/ElementsPanel.ts +5 -1
- package/front_end/panels/elements/ElementsTreeElement.ts +4 -1
- package/front_end/panels/elements/ElementsTreeOutline.ts +58 -51
- package/front_end/panels/elements/PropertiesWidget.ts +11 -10
- package/front_end/panels/elements/PropertyRenderer.ts +29 -25
- package/front_end/panels/elements/StylePropertyTreeElement.ts +6 -6
- package/front_end/panels/elements/elementsTreeOutline.css +7 -1
- package/front_end/panels/emulation/DeviceModeToolbar.ts +2 -3
- package/front_end/panels/event_listeners/EventListenersView.ts +33 -44
- package/front_end/panels/linear_memory_inspector/LinearMemoryInspectorController.ts +6 -7
- package/front_end/panels/linear_memory_inspector/components/linearMemoryInspector.css +5 -1
- package/front_end/panels/linear_memory_inspector/linear_memory_inspector-meta.ts +1 -1
- package/front_end/panels/recorder/stepView.css +1 -0
- package/front_end/panels/security/SecurityPanel.ts +5 -5
- package/front_end/panels/security/SecurityPanelSidebar.ts +2 -11
- package/front_end/panels/sources/DebuggerPlugin.ts +1 -1
- package/front_end/panels/sources/WatchExpressionsSidebarPane.ts +11 -16
- package/front_end/panels/sources/sources-meta.ts +1 -1
- package/front_end/panels/timeline/overlays/OverlaysImpl.ts +0 -3
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/ui/comments/CommentOverlayManager.ts +28 -110
- package/front_end/ui/comments/comments.ts +0 -6
- package/front_end/ui/legacy/Treeoutline.ts +4 -0
- package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +14 -7
- package/front_end/ui/visual_logging/KnownContextValues.ts +1 -0
- package/package.json +1 -1
- /package/front_end/{ui/comments → panels/common}/commentThreadWidget.css +0 -0
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
/* eslint-disable @devtools/prefer-private-class-members */
|
|
6
6
|
|
|
7
7
|
import * as i18n from '../../core/i18n/i18n.js';
|
|
8
|
-
import type * as PlatformApi from '../../core/platform/api/api.js';
|
|
9
8
|
import * as Platform from '../../core/platform/platform.js';
|
|
10
9
|
import * as HeapSnapshotModel from '../../models/heap_snapshot/heap_snapshot.js';
|
|
11
10
|
|
|
12
11
|
import {AllocationProfile} from './AllocationProfile.js';
|
|
12
|
+
import * as ContextAnalyzer from './ContextAnalyzer.js';
|
|
13
13
|
import type {HeapSnapshotWorkerDispatcher} from './HeapSnapshotWorkerDispatcher.js';
|
|
14
14
|
|
|
15
15
|
export interface HeapSnapshotItem {
|
|
@@ -724,8 +724,8 @@ function formatProblemReport(snapshot: HeapSnapshot, report: HeapSnapshotProblem
|
|
|
724
724
|
})
|
|
725
725
|
.join('\n ');
|
|
726
726
|
}
|
|
727
|
-
function reportProblemToPrimaryWorker(
|
|
728
|
-
|
|
727
|
+
function reportProblemToPrimaryWorker(problemReport: HeapSnapshotProblemReport,
|
|
728
|
+
port: Platform.HostRuntime.WorkerMessagePort): void {
|
|
729
729
|
port.postMessage({problemReport});
|
|
730
730
|
}
|
|
731
731
|
|
|
@@ -738,6 +738,9 @@ export interface Profile {
|
|
|
738
738
|
samples: number[];
|
|
739
739
|
strings: string[];
|
|
740
740
|
locations: number[];
|
|
741
|
+
scopes?: number[];
|
|
742
|
+
scope_context_vars?: number[];
|
|
743
|
+
scope_uses?: number[];
|
|
741
744
|
trace_function_infos: Uint32Array;
|
|
742
745
|
trace_tree: Object;
|
|
743
746
|
/* eslint-enable @typescript-eslint/naming-convention */
|
|
@@ -790,7 +793,7 @@ interface ArgumentsToComputeDominatorsAndRetainedSizes extends SecondaryInitArgu
|
|
|
790
793
|
// should be used when computing dominators.
|
|
791
794
|
essentialEdges: Platform.TypedArrayUtilities.BitVector;
|
|
792
795
|
// A message port for reporting problems to the primary worker.
|
|
793
|
-
port:
|
|
796
|
+
port: Platform.HostRuntime.WorkerMessagePort;
|
|
794
797
|
// For each node ordinal, this array will contain the node's shallow size.
|
|
795
798
|
nodeSelfSizesPromise: Promise<Uint32Array>;
|
|
796
799
|
}
|
|
@@ -825,7 +828,7 @@ export class SecondaryInitManager {
|
|
|
825
828
|
argsStep1: Promise<SecondaryInitArgumentsStep1>;
|
|
826
829
|
argsStep2: Promise<SecondaryInitArgumentsStep2>;
|
|
827
830
|
argsStep3: Promise<SecondaryInitArgumentsStep3>;
|
|
828
|
-
constructor(port:
|
|
831
|
+
constructor(port: Platform.HostRuntime.WorkerMessagePort) {
|
|
829
832
|
const {promise: argsStep1, resolve: resolveArgsStep1} = Promise.withResolvers<SecondaryInitArgumentsStep1>();
|
|
830
833
|
this.argsStep1 = argsStep1;
|
|
831
834
|
const {promise: argsStep2, resolve: resolveArgsStep2} = Promise.withResolvers<SecondaryInitArgumentsStep2>();
|
|
@@ -833,7 +836,7 @@ export class SecondaryInitManager {
|
|
|
833
836
|
const {promise: argsStep3, resolve: resolveArgsStep3} = Promise.withResolvers<SecondaryInitArgumentsStep3>();
|
|
834
837
|
this.argsStep3 = argsStep3;
|
|
835
838
|
const listener = (e: unknown): void => {
|
|
836
|
-
const data = (e as
|
|
839
|
+
const data = (e as Platform.HostRuntime.WorkerMessageEvent).data;
|
|
837
840
|
switch (data.step) {
|
|
838
841
|
case 1:
|
|
839
842
|
resolveArgsStep1(data.args);
|
|
@@ -856,7 +859,7 @@ export class SecondaryInitManager {
|
|
|
856
859
|
return (await this.argsStep3).nodeSelfSizes;
|
|
857
860
|
}
|
|
858
861
|
|
|
859
|
-
private async initialize(port:
|
|
862
|
+
private async initialize(port: Platform.HostRuntime.WorkerMessagePort): Promise<void> {
|
|
860
863
|
try {
|
|
861
864
|
const argsStep1 = await this.argsStep1;
|
|
862
865
|
const retainers = HeapSnapshot.buildRetainers(argsStep1);
|
|
@@ -1024,7 +1027,7 @@ export abstract class HeapSnapshot {
|
|
|
1024
1027
|
this.#edgeNamesThatAreNotWeakMaps = Platform.TypedArrayUtilities.createBitVector(this.strings.length);
|
|
1025
1028
|
}
|
|
1026
1029
|
|
|
1027
|
-
async initialize(secondWorker:
|
|
1030
|
+
async initialize(secondWorker: Platform.HostRuntime.WorkerMessagePort): Promise<void> {
|
|
1028
1031
|
const meta = this.#metaNode;
|
|
1029
1032
|
|
|
1030
1033
|
this.nodeTypeOffset = meta.node_fields.indexOf('type');
|
|
@@ -1170,11 +1173,19 @@ export abstract class HeapSnapshot {
|
|
|
1170
1173
|
};
|
|
1171
1174
|
}
|
|
1172
1175
|
|
|
1173
|
-
|
|
1176
|
+
/**
|
|
1177
|
+
* Computes for every context field whether it is still live, i.e. whether it
|
|
1178
|
+
* can be reached by some closure, and reports the dead fields.
|
|
1179
|
+
*/
|
|
1180
|
+
analyzeContexts(): HeapSnapshotModel.HeapSnapshotModel.ContextAnalysisResult {
|
|
1181
|
+
return ContextAnalyzer.analyzeContexts(this);
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
private startInitStep1InSecondThread(secondWorker: Platform.HostRuntime.WorkerMessagePort):
|
|
1174
1185
|
Promise<ResultsFromSecondWorker> {
|
|
1175
1186
|
const resultsFromSecondWorker = new Promise<ResultsFromSecondWorker>((resolve, reject) => {
|
|
1176
1187
|
const listener = (e: unknown): void => {
|
|
1177
|
-
const data = (e as
|
|
1188
|
+
const data = (e as Platform.HostRuntime.WorkerMessageEvent).data;
|
|
1178
1189
|
if (data?.problemReport) {
|
|
1179
1190
|
const problemReport: HeapSnapshotProblemReport = data.problemReport;
|
|
1180
1191
|
console.warn(formatProblemReport(this, problemReport));
|
|
@@ -1211,14 +1222,14 @@ export abstract class HeapSnapshot {
|
|
|
1211
1222
|
return resultsFromSecondWorker;
|
|
1212
1223
|
}
|
|
1213
1224
|
|
|
1214
|
-
private startInitStep2InSecondThread(secondWorker:
|
|
1225
|
+
private startInitStep2InSecondThread(secondWorker: Platform.HostRuntime.WorkerMessagePort): void {
|
|
1215
1226
|
const rootNodeOrdinal = this.rootNodeIndexInternal / this.nodeFieldCount;
|
|
1216
1227
|
const essentialEdges = this.initEssentialEdges();
|
|
1217
1228
|
const args: SecondaryInitArgumentsStep2 = {rootNodeOrdinal, essentialEdgesBuffer: essentialEdges.buffer};
|
|
1218
1229
|
secondWorker.postMessage({step: 2, args}, [essentialEdges.buffer]);
|
|
1219
1230
|
}
|
|
1220
1231
|
|
|
1221
|
-
private startInitStep3InSecondThread(secondWorker:
|
|
1232
|
+
private startInitStep3InSecondThread(secondWorker: Platform.HostRuntime.WorkerMessagePort): void {
|
|
1222
1233
|
const {nodes, nodeFieldCount, nodeSelfSizeOffset, nodeCount} = this;
|
|
1223
1234
|
const nodeSelfSizes = new Uint32Array(nodeCount);
|
|
1224
1235
|
for (let nodeOrdinal = 0; nodeOrdinal < nodeCount; ++nodeOrdinal) {
|
|
@@ -3837,6 +3848,9 @@ interface HeapSnapshotMetaInfo {
|
|
|
3837
3848
|
trace_node_fields: string[];
|
|
3838
3849
|
sample_fields: string[];
|
|
3839
3850
|
type_strings: Record<string, string>;
|
|
3851
|
+
scope_fields?: string[];
|
|
3852
|
+
scope_context_var_fields?: string[];
|
|
3853
|
+
scope_use_fields?: string[];
|
|
3840
3854
|
/* eslint-enable @typescript-eslint/naming-convention */
|
|
3841
3855
|
}
|
|
3842
3856
|
|
|
@@ -2,7 +2,6 @@
|
|
|
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
4
|
|
|
5
|
-
import type * as PlatformApi from '../../core/platform/api/api.js';
|
|
6
5
|
import * as Platform from '../../core/platform/platform.js';
|
|
7
6
|
import * as TextUtils from '../../core/text_utils/text_utils.js';
|
|
8
7
|
|
|
@@ -49,7 +48,7 @@ export class HeapSnapshotLoader {
|
|
|
49
48
|
}
|
|
50
49
|
}
|
|
51
50
|
|
|
52
|
-
async buildSnapshot(secondWorker:
|
|
51
|
+
async buildSnapshot(secondWorker: Platform.HostRuntime.WorkerMessagePort): Promise<JSHeapSnapshot> {
|
|
53
52
|
await this.parsingComplete;
|
|
54
53
|
|
|
55
54
|
this.#snapshot = this.#snapshot || {};
|
|
@@ -252,6 +251,21 @@ export class HeapSnapshotLoader {
|
|
|
252
251
|
this.#snapshot.locations = [];
|
|
253
252
|
}
|
|
254
253
|
|
|
254
|
+
if (this.#snapshot.snapshot.meta.scope_fields) {
|
|
255
|
+
const scopes = await this.#parseArray('"scopes"', 'Loading scopes…');
|
|
256
|
+
this.#snapshot.scopes = scopes.asArrayOrFail();
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (this.#snapshot.snapshot.meta.scope_context_var_fields) {
|
|
260
|
+
const scopeContextVars = await this.#parseArray('"scope_context_vars"', 'Loading scope context vars…');
|
|
261
|
+
this.#snapshot.scope_context_vars = scopeContextVars.asArrayOrFail();
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (this.#snapshot.snapshot.meta.scope_use_fields) {
|
|
265
|
+
const scopeUses = await this.#parseArray('"scope_uses"', 'Loading scope uses…');
|
|
266
|
+
this.#snapshot.scope_uses = scopeUses.asArrayOrFail();
|
|
267
|
+
}
|
|
268
|
+
|
|
255
269
|
this.#progress.updateStatus('Loading strings…');
|
|
256
270
|
const stringsTokenIndex = await this.#findToken('"strings"');
|
|
257
271
|
const bracketIndex = await this.#findToken('[', stringsTokenIndex);
|
|
@@ -2,7 +2,7 @@
|
|
|
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
4
|
|
|
5
|
-
import type * as
|
|
5
|
+
import type * as Platform from '../../core/platform/platform.js';
|
|
6
6
|
import * as HeapSnapshotModel from '../../models/heap_snapshot/heap_snapshot.js';
|
|
7
7
|
|
|
8
8
|
// We mirror what heap_snapshot_worker.ts does, but we can't use it here as we'd have a
|
|
@@ -24,8 +24,8 @@ export class HeapSnapshotWorkerDispatcher {
|
|
|
24
24
|
// TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration)
|
|
25
25
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
26
26
|
#objects: any[] = [];
|
|
27
|
-
readonly #postMessage:
|
|
28
|
-
constructor(postMessage:
|
|
27
|
+
readonly #postMessage: Platform.HostRuntime.Worker['postMessage'];
|
|
28
|
+
constructor(postMessage: Platform.HostRuntime.Worker['postMessage']) {
|
|
29
29
|
this.#postMessage = postMessage;
|
|
30
30
|
}
|
|
31
31
|
|
|
@@ -36,7 +36,7 @@ export class HeapSnapshotWorkerDispatcher {
|
|
|
36
36
|
async dispatchMessage({
|
|
37
37
|
data,
|
|
38
38
|
ports,
|
|
39
|
-
}:
|
|
39
|
+
}: Platform.HostRuntime.WorkerMessageEvent<HeapSnapshotModel.HeapSnapshotModel.WorkerCommand>): Promise<void> {
|
|
40
40
|
const response: DispatcherResponse = {
|
|
41
41
|
callId: data.callId,
|
|
42
42
|
result: null,
|
|
@@ -3,12 +3,14 @@
|
|
|
3
3
|
// found in the LICENSE file.
|
|
4
4
|
|
|
5
5
|
import * as AllocationProfile from './AllocationProfile.js';
|
|
6
|
+
import * as ContextAnalyzer from './ContextAnalyzer.js';
|
|
6
7
|
import * as HeapSnapshot from './HeapSnapshot.js';
|
|
7
8
|
import * as HeapSnapshotLoader from './HeapSnapshotLoader.js';
|
|
8
9
|
import * as HeapSnapshotWorkerDispatcher from './HeapSnapshotWorkerDispatcher.js';
|
|
9
10
|
|
|
10
11
|
export {
|
|
11
12
|
AllocationProfile,
|
|
13
|
+
ContextAnalyzer,
|
|
12
14
|
HeapSnapshot,
|
|
13
15
|
HeapSnapshotLoader,
|
|
14
16
|
HeapSnapshotWorkerDispatcher,
|
|
@@ -23,7 +23,7 @@ import '../../ui/legacy/components/source_frame/source_frame-meta.js';
|
|
|
23
23
|
import '../../panels/console_counters/console_counters-meta.js';
|
|
24
24
|
import '../../panels/explain/explain-meta.js';
|
|
25
25
|
import '../../panels/ai_assistance/ai_assistance-meta.js';
|
|
26
|
-
import '../../
|
|
26
|
+
import '../../panels/common/common-meta.js';
|
|
27
27
|
import '../main/main.js';
|
|
28
28
|
|
|
29
29
|
// We generate the descriptors in this file, which depend on the runtime.
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// Copyright 2026 The Chromium Authors
|
|
2
|
+
// Use of this source code is governed by a BSD-style license that can be
|
|
3
|
+
// found in the LICENSE file.
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @file This file includes DOM helpers and page-evaluated serialization helpers
|
|
7
|
+
* used by AI assistance that require DOM types. They are isolated in a separate
|
|
8
|
+
* target so DOM types do not leak into the rest of models/ai_assistance.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import * as Platform from '../../core/platform/platform.js';
|
|
12
|
+
|
|
13
|
+
export interface GetErrorStackOutput {
|
|
14
|
+
message: string;
|
|
15
|
+
stack?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/* istanbul ignore next */
|
|
19
|
+
export function getErrorStackOnThePage(this: Error): GetErrorStackOutput {
|
|
20
|
+
// Using this.stack causes side effect checks to throw.
|
|
21
|
+
return {stack: '', message: this.message};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/* istanbul ignore next */
|
|
25
|
+
export function stringifyObjectOnThePage(this: unknown): string {
|
|
26
|
+
const seenBefore = new Map();
|
|
27
|
+
return JSON.stringify(this, function replacer(this: unknown, key: string, value: unknown) {
|
|
28
|
+
if (typeof value === 'object' && value !== null) {
|
|
29
|
+
if (seenBefore.has(value)) {
|
|
30
|
+
return '(cycle)';
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
seenBefore.set(value, true);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (value instanceof HTMLElement) {
|
|
37
|
+
const idAttribute = value.id ? ` id="${value.id}"` : '';
|
|
38
|
+
const classAttribute = value.classList.value ? ` class="${value.classList.value}"` : '';
|
|
39
|
+
|
|
40
|
+
return `<${value.nodeName.toLowerCase()}${idAttribute}${classAttribute}>${value.hasChildNodes() ? '...' : ''}</${
|
|
41
|
+
value.nodeName.toLowerCase()}>`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (this instanceof CSSStyleDeclaration) {
|
|
45
|
+
// Do not add number keys to the output.
|
|
46
|
+
if (!isNaN(Number(key))) {
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return value;
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export async function sanitizeStyleChanges(selector: string,
|
|
56
|
+
styles: Record<string, string>): Promise<Record<string, string>> {
|
|
57
|
+
const cssStyleValue: string[] = [];
|
|
58
|
+
const changedStyles: string[] = [];
|
|
59
|
+
const kebabStyles = Platform.StringUtilities.toKebabCaseKeys(styles);
|
|
60
|
+
for (const [style, value] of Object.entries(kebabStyles)) {
|
|
61
|
+
// Build up the CSS style
|
|
62
|
+
cssStyleValue.push(`${style}: ${value};`);
|
|
63
|
+
// Keep track of what style changed to query later.
|
|
64
|
+
changedStyles.push(style);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (typeof CSSStyleSheet === 'undefined' || typeof CSSStyleRule === 'undefined') {
|
|
68
|
+
return kebabStyles;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const styleSheet = new CSSStyleSheet({disabled: true});
|
|
72
|
+
// Build up the CSS stylesheet value.
|
|
73
|
+
await styleSheet.replace(`${selector} { ${cssStyleValue.join(' ')} }`);
|
|
74
|
+
|
|
75
|
+
const sanitizedStyles: Record<string, string> = {};
|
|
76
|
+
for (const cssRule of styleSheet.cssRules) {
|
|
77
|
+
if (!(cssRule instanceof CSSStyleRule)) {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
for (const style of changedStyles) {
|
|
81
|
+
const value = cssRule.style.getPropertyValue(style);
|
|
82
|
+
if (value) {
|
|
83
|
+
sanitizedStyles[style] = value;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return sanitizedStyles;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function dispatchAiAssistanceDoneEvent(): void {
|
|
92
|
+
if (typeof window !== 'undefined') {
|
|
93
|
+
window.dispatchEvent(new CustomEvent('aiassistancedone'));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -5,6 +5,11 @@
|
|
|
5
5
|
import * as SDK from '../../core/sdk/sdk.js';
|
|
6
6
|
import * as Protocol from '../../generated/protocol.js';
|
|
7
7
|
|
|
8
|
+
import {
|
|
9
|
+
getErrorStackOnThePage,
|
|
10
|
+
type GetErrorStackOutput,
|
|
11
|
+
stringifyObjectOnThePage,
|
|
12
|
+
} from './DOMHelpers.js';
|
|
8
13
|
import {PAGE_EXPOSED_FUNCTIONS} from './injected.js';
|
|
9
14
|
|
|
10
15
|
export function formatError(message: string): string {
|
|
@@ -12,48 +17,6 @@ export function formatError(message: string): string {
|
|
|
12
17
|
}
|
|
13
18
|
export class SideEffectError extends Error {}
|
|
14
19
|
|
|
15
|
-
export interface GetErrorStackOutput {
|
|
16
|
-
message: string;
|
|
17
|
-
stack?: string;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/* istanbul ignore next */
|
|
21
|
-
export function getErrorStackOnThePage(this: Error): GetErrorStackOutput {
|
|
22
|
-
// Using this.stack causes side effect checks to throw.
|
|
23
|
-
return {stack: '', message: this.message};
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/* istanbul ignore next */
|
|
27
|
-
export function stringifyObjectOnThePage(this: unknown): string {
|
|
28
|
-
const seenBefore = new Map();
|
|
29
|
-
return JSON.stringify(this, function replacer(this: unknown, key: string, value: unknown) {
|
|
30
|
-
if (typeof value === 'object' && value !== null) {
|
|
31
|
-
if (seenBefore.has(value)) {
|
|
32
|
-
return '(cycle)';
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
seenBefore.set(value, true);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (value instanceof HTMLElement) {
|
|
39
|
-
const idAttribute = value.id ? ` id="${value.id}"` : '';
|
|
40
|
-
const classAttribute = value.classList.value ? ` class="${value.classList.value}"` : '';
|
|
41
|
-
|
|
42
|
-
return `<${value.nodeName.toLowerCase()}${idAttribute}${classAttribute}>${value.hasChildNodes() ? '...' : ''}</${
|
|
43
|
-
value.nodeName.toLowerCase()}>`;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if (this instanceof CSSStyleDeclaration) {
|
|
47
|
-
// Do not add number keys to the output.
|
|
48
|
-
if (!isNaN(Number(key))) {
|
|
49
|
-
return undefined;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return value;
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
|
|
57
20
|
export async function stringifyRemoteObject(
|
|
58
21
|
object: SDK.RemoteObject.RemoteObject, functionDeclaration: string): Promise<string> {
|
|
59
22
|
switch (object.type) {
|
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
// found in the LICENSE file.
|
|
4
4
|
|
|
5
5
|
import * as Common from '../../core/common/common.js';
|
|
6
|
-
import * as Platform from '../../core/platform/platform.js';
|
|
7
6
|
import * as SDK from '../../core/sdk/sdk.js';
|
|
8
7
|
import * as Protocol from '../../generated/protocol.js';
|
|
9
8
|
|
|
10
9
|
import {getOrCreateIsolatedWorld} from './agents/ExecuteJavascript.js';
|
|
11
10
|
import type {ChangeManager} from './ChangeManager.js';
|
|
11
|
+
import {sanitizeStyleChanges} from './DOMHelpers.js';
|
|
12
12
|
import {
|
|
13
13
|
AI_ASSISTANCE_CSS_CLASS_NAME,
|
|
14
14
|
type FreestyleCallbackArgs,
|
|
@@ -330,36 +330,7 @@ export class ExtensionScope {
|
|
|
330
330
|
}
|
|
331
331
|
|
|
332
332
|
async sanitizedStyleChanges(selector: string, styles: Record<string, string>): Promise<Record<string, string>> {
|
|
333
|
-
const
|
|
334
|
-
const changedStyles: string[] = [];
|
|
335
|
-
const styleSheet = new CSSStyleSheet({disabled: true});
|
|
336
|
-
const kebabStyles = Platform.StringUtilities.toKebabCaseKeys(styles);
|
|
337
|
-
for (const [style, value] of Object.entries(kebabStyles)) {
|
|
338
|
-
// Build up the CSS style
|
|
339
|
-
cssStyleValue.push(`${style}: ${value};`);
|
|
340
|
-
// Keep track of what style changed to query later.
|
|
341
|
-
changedStyles.push(style);
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
// Build up the CSS stylesheet value.
|
|
345
|
-
await styleSheet.replace(`${selector} { ${cssStyleValue.join(' ')} }`);
|
|
346
|
-
|
|
347
|
-
const sanitizedStyles: Record<string, string> = {};
|
|
348
|
-
for (const cssRule of styleSheet.cssRules) {
|
|
349
|
-
if (!(cssRule instanceof CSSStyleRule)) {
|
|
350
|
-
continue;
|
|
351
|
-
}
|
|
352
|
-
for (const style of changedStyles) {
|
|
353
|
-
// We need to use the style rather then the stylesMap
|
|
354
|
-
// as the latter expands the styles to each separate part
|
|
355
|
-
// Example:
|
|
356
|
-
// padding: 10px 20px -> padding-top: 10px, padding-bottom: 10px, etc.
|
|
357
|
-
const value = cssRule.style.getPropertyValue(style);
|
|
358
|
-
if (value) {
|
|
359
|
-
sanitizedStyles[style] = value;
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
}
|
|
333
|
+
const sanitizedStyles = await sanitizeStyleChanges(selector, styles);
|
|
363
334
|
|
|
364
335
|
if (Object.keys(sanitizedStyles).length === 0) {
|
|
365
336
|
throw new Error(
|
|
@@ -12,6 +12,7 @@ import type * as LHModel from '../../lighthouse/lighthouse.js';
|
|
|
12
12
|
import type * as Trace from '../../trace/trace.js';
|
|
13
13
|
import type * as Workspace from '../../workspace/workspace.js';
|
|
14
14
|
import {debugLog, isStructuredLogEnabled} from '../debug.js';
|
|
15
|
+
import {dispatchAiAssistanceDoneEvent} from '../DOMHelpers.js';
|
|
15
16
|
import type {ContextHandlerResult, DataHandlerResult} from '../tools/Tool.js';
|
|
16
17
|
|
|
17
18
|
type UrlString = Platform.DevToolsPath.UrlString;
|
|
@@ -956,7 +957,7 @@ export abstract class AiAgent<T> {
|
|
|
956
957
|
}
|
|
957
958
|
|
|
958
959
|
if (isStructuredLogEnabled()) {
|
|
959
|
-
|
|
960
|
+
dispatchAiAssistanceDoneEvent();
|
|
960
961
|
}
|
|
961
962
|
return;
|
|
962
963
|
}
|
|
@@ -292,7 +292,7 @@ export class ContextSelectionAgent extends AiAgent<never> {
|
|
|
292
292
|
const files: Array<{file: string, id: number | undefined}> = [];
|
|
293
293
|
const uiSourceCodes: Workspace.UISourceCode.UISourceCode[] = [];
|
|
294
294
|
for (const file of ContextSelectionAgent.getUISourceCodes(this.#workspace)) {
|
|
295
|
-
const fileSecurityOrigin =
|
|
295
|
+
const fileSecurityOrigin = file.securityOrigin();
|
|
296
296
|
|
|
297
297
|
if (origin && !fileSecurityOrigin.isSameOriginWith(origin)) {
|
|
298
298
|
continue;
|
|
@@ -352,7 +352,7 @@ export class ContextSelectionAgent extends AiAgent<never> {
|
|
|
352
352
|
if (ContextSelectionAgent.uiSourceCodeId.get(file) !== params.id) {
|
|
353
353
|
return false;
|
|
354
354
|
}
|
|
355
|
-
const fileSecurityOrigin =
|
|
355
|
+
const fileSecurityOrigin = file.securityOrigin();
|
|
356
356
|
return !origin || fileSecurityOrigin.isSameOriginWith(origin);
|
|
357
357
|
});
|
|
358
358
|
|
|
@@ -609,8 +609,7 @@ export class ContextSelectionAgent extends AiAgent<never> {
|
|
|
609
609
|
if (ContextSelectionAgent.uiSourceCodeId.get(file) !== id) {
|
|
610
610
|
return false;
|
|
611
611
|
}
|
|
612
|
-
return isOriginAllowedByLock({status: 'ESTABLISHED_ORIGIN', origin: establishedOrigin},
|
|
613
|
-
FileContext.originForUISourceCode(file));
|
|
612
|
+
return isOriginAllowedByLock({status: 'ESTABLISHED_ORIGIN', origin: establishedOrigin}, file.securityOrigin());
|
|
614
613
|
});
|
|
615
614
|
}
|
|
616
615
|
}
|
|
@@ -30,6 +30,7 @@ import * as PerformanceInsightFormatter from './data_formatters/PerformanceInsig
|
|
|
30
30
|
import * as PerformanceTraceFormatter from './data_formatters/PerformanceTraceFormatter.js';
|
|
31
31
|
import * as UnitFormatters from './data_formatters/UnitFormatters.js';
|
|
32
32
|
import * as Debug from './debug.js';
|
|
33
|
+
import * as DOMHelpers from './DOMHelpers.js';
|
|
33
34
|
import * as EvaluateAction from './EvaluateAction.js';
|
|
34
35
|
import * as ExtensionScope from './ExtensionScope.js';
|
|
35
36
|
import * as Injected from './injected.js';
|
|
@@ -88,6 +89,7 @@ export {
|
|
|
88
89
|
ConversationSummary,
|
|
89
90
|
CookieUtils,
|
|
90
91
|
Debug,
|
|
92
|
+
DOMHelpers,
|
|
91
93
|
DOMNodeContext,
|
|
92
94
|
DOMStorageUtils,
|
|
93
95
|
EvaluateAction,
|
|
@@ -124,8 +124,9 @@ ${await this.describe()}`;
|
|
|
124
124
|
* Its selector is \`${element.simpleSelector()}\``;
|
|
125
125
|
const childNodes = await element.getChildNodesPromise();
|
|
126
126
|
if (childNodes) {
|
|
127
|
-
const textChildNodes = childNodes.filter(childNode => childNode.nodeType() ===
|
|
128
|
-
const elementChildNodes =
|
|
127
|
+
const textChildNodes = childNodes.filter(childNode => childNode.nodeType() === SDK.DOMModel.NodeType.TEXT_NODE);
|
|
128
|
+
const elementChildNodes =
|
|
129
|
+
childNodes.filter(childNode => childNode.nodeType() === SDK.DOMModel.NodeType.ELEMENT_NODE);
|
|
129
130
|
switch (elementChildNodes.length) {
|
|
130
131
|
case 0:
|
|
131
132
|
output += '\n* It doesn\'t have any child element nodes';
|
|
@@ -151,14 +152,14 @@ ${await this.describe()}`;
|
|
|
151
152
|
}
|
|
152
153
|
|
|
153
154
|
if (element.nextSibling) {
|
|
154
|
-
const elementOrNodeElementNodeText = element.nextSibling.nodeType() ===
|
|
155
|
+
const elementOrNodeElementNodeText = element.nextSibling.nodeType() === SDK.DOMModel.NodeType.ELEMENT_NODE ?
|
|
155
156
|
`an element (uid=${element.nextSibling.backendNodeId()})` :
|
|
156
157
|
'a non element';
|
|
157
158
|
output += `\n* It has a next sibling and it is ${elementOrNodeElementNodeText} node`;
|
|
158
159
|
}
|
|
159
160
|
|
|
160
161
|
if (element.previousSibling) {
|
|
161
|
-
const elementOrNodeElementNodeText = element.previousSibling.nodeType() ===
|
|
162
|
+
const elementOrNodeElementNodeText = element.previousSibling.nodeType() === SDK.DOMModel.NodeType.ELEMENT_NODE ?
|
|
162
163
|
`an element (uid=${element.previousSibling.backendNodeId()})` :
|
|
163
164
|
'a non element';
|
|
164
165
|
output += `\n* It has a previous sibling and it is ${elementOrNodeElementNodeText} node`;
|
|
@@ -172,14 +173,15 @@ ${await this.describe()}`;
|
|
|
172
173
|
if (parentNode) {
|
|
173
174
|
const parentChildrenNodes = await parentNode.getChildNodesPromise();
|
|
174
175
|
output += `\n* Its parent's selector is \`${parentNode.simpleSelector()}\` (uid=${parentNode.backendNodeId()})`;
|
|
175
|
-
const elementOrNodeElementNodeText =
|
|
176
|
+
const elementOrNodeElementNodeText =
|
|
177
|
+
parentNode.nodeType() === SDK.DOMModel.NodeType.ELEMENT_NODE ? 'an element' : 'a non element';
|
|
176
178
|
output += `\n* Its parent is ${elementOrNodeElementNodeText} node`;
|
|
177
179
|
if (parentNode.isShadowRoot()) {
|
|
178
180
|
output += '\n* Its parent is a shadow root.';
|
|
179
181
|
}
|
|
180
182
|
if (parentChildrenNodes) {
|
|
181
183
|
const childElementNodes =
|
|
182
|
-
parentChildrenNodes.filter(siblingNode => siblingNode.nodeType() ===
|
|
184
|
+
parentChildrenNodes.filter(siblingNode => siblingNode.nodeType() === SDK.DOMModel.NodeType.ELEMENT_NODE);
|
|
183
185
|
switch (childElementNodes.length) {
|
|
184
186
|
case 0:
|
|
185
187
|
break;
|
|
@@ -193,7 +195,8 @@ ${await this.describe()}`;
|
|
|
193
195
|
break;
|
|
194
196
|
}
|
|
195
197
|
|
|
196
|
-
const siblingTextNodes =
|
|
198
|
+
const siblingTextNodes =
|
|
199
|
+
parentChildrenNodes.filter(siblingNode => siblingNode.nodeType() === SDK.DOMModel.NodeType.TEXT_NODE);
|
|
197
200
|
switch (siblingTextNodes.length) {
|
|
198
201
|
case 0:
|
|
199
202
|
break;
|
|
@@ -2,7 +2,7 @@
|
|
|
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
4
|
|
|
5
|
-
import * as SDK from '../../../core/sdk/sdk.js';
|
|
5
|
+
import type * as SDK from '../../../core/sdk/sdk.js';
|
|
6
6
|
import type * as Bindings from '../../bindings/bindings.js';
|
|
7
7
|
import type * as Workspace from '../../workspace/workspace.js';
|
|
8
8
|
import {type ContextDetail, ConversationContext} from '../agents/AiAgent.js';
|
|
@@ -20,20 +20,12 @@ export class FileContext extends ConversationContext<Workspace.UISourceCode.UISo
|
|
|
20
20
|
this.#debuggerWorkspaceBinding = debuggerWorkspaceBinding;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
/**
|
|
24
|
-
* Resolves the security origin of a given UISourceCode.
|
|
25
|
-
* Prefers the project security origin, falling back to the origin of the file URL.
|
|
26
|
-
*/
|
|
27
|
-
static originForUISourceCode(file: Workspace.UISourceCode.UISourceCode): SDK.SecurityOrigin.SecurityOrigin {
|
|
28
|
-
return file.project()?.securityOrigin?.() ?? SDK.SecurityOrigin.SecurityOrigin.create(file.url());
|
|
29
|
-
}
|
|
30
|
-
|
|
31
23
|
/**
|
|
32
24
|
* Returns the security origin of the project containing the file, falling
|
|
33
25
|
* back to the origin derived from the file URL.
|
|
34
26
|
*/
|
|
35
27
|
override getOrigin(): SDK.SecurityOrigin.SecurityOrigin {
|
|
36
|
-
return
|
|
28
|
+
return this.#file.securityOrigin();
|
|
37
29
|
}
|
|
38
30
|
|
|
39
31
|
override getItem(): Workspace.UISourceCode.UISourceCode {
|
|
@@ -65,12 +65,13 @@ export class GetSourceContentTool implements
|
|
|
65
65
|
args: GetSourceContentArgs,
|
|
66
66
|
context: BaseToolCapability&OriginLockCapability,
|
|
67
67
|
): Promise<DataHandlerResult<{content: string}>> {
|
|
68
|
-
const
|
|
68
|
+
const originLock = context.getOriginLock();
|
|
69
|
+
const originResult = resolveOriginFromLock(originLock);
|
|
69
70
|
if ('error' in originResult) {
|
|
70
71
|
return originResult;
|
|
71
72
|
}
|
|
72
73
|
|
|
73
|
-
const file = ListSourcesTool.getSourceById(args.id,
|
|
74
|
+
const file = ListSourcesTool.getSourceById(args.id, originLock);
|
|
74
75
|
|
|
75
76
|
if (!file) {
|
|
76
77
|
return {
|