chrome-devtools-frontend 1.0.1642246 → 1.0.1642899
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/SECURITY.md +1 -0
- package/front_end/core/host/UserMetrics.ts +2 -1
- package/front_end/core/sdk/CSSMatchedStyles.ts +55 -26
- package/front_end/core/sdk/CSSRule.ts +1 -0
- package/front_end/core/sdk/DebuggerModel.ts +5 -0
- package/front_end/entrypoints/greendev_floaty/FloatyEntrypoint.ts +4 -3
- package/front_end/entrypoints/greendev_floaty/greendev_floaty.ts +4 -3
- package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshot.ts +11 -0
- package/front_end/models/ai_assistance/AiAgent2.ts +80 -16
- package/front_end/models/ai_assistance/AiConversation.ts +3 -2
- package/front_end/models/ai_assistance/README.md +8 -0
- package/front_end/models/ai_assistance/agents/AccessibilityAgent.ts +50 -39
- package/front_end/models/ai_assistance/agents/AiAgent.ts +17 -6
- package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +2 -2
- package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +195 -147
- package/front_end/models/ai_assistance/agents/StylingAgent.snapshot.txt +1 -26
- package/front_end/models/ai_assistance/agents/StylingAgent.ts +24 -309
- package/front_end/models/ai_assistance/ai_assistance.ts +8 -0
- package/front_end/models/ai_assistance/contexts/DOMNodeContext.snapshot.txt +51 -0
- package/front_end/models/ai_assistance/contexts/DOMNodeContext.ts +200 -0
- package/front_end/models/ai_assistance/data_formatters/NetworkRequestFormatter.ts +13 -12
- package/front_end/models/ai_assistance/skills/styling.md +36 -2
- package/front_end/models/ai_assistance/tools/GetStyles.ts +137 -0
- package/front_end/models/ai_assistance/tools/Tool.ts +55 -0
- package/front_end/models/ai_assistance/tools/ToolRegistry.ts +34 -0
- package/front_end/models/heap_snapshot/HeapSnapshotProxy.ts +4 -0
- package/front_end/models/javascript_metadata/NativeFunctions.js +16 -0
- package/front_end/models/lighthouse/LighthouseReporterTypes.ts +5 -0
- package/front_end/models/live-metrics/LiveMetrics.ts +24 -13
- package/front_end/models/stack_trace/DetailedErrorStackParser.ts +2 -2
- package/front_end/models/stack_trace/StackTrace.ts +4 -1
- package/front_end/models/stack_trace/StackTraceImpl.ts +9 -2
- package/front_end/models/stack_trace/StackTraceModel.ts +17 -4
- package/front_end/models/stack_trace/Trie.ts +1 -1
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +19 -15
- package/front_end/panels/ai_assistance/ai_assistance-meta.ts +16 -0
- package/front_end/panels/ai_assistance/components/ChatInput.ts +2 -2
- package/front_end/panels/application/DOMStorageItemsView.ts +4 -0
- package/front_end/panels/application/KeyValueStorageItemsView.ts +39 -7
- package/front_end/panels/common/ExtensionServer.ts +26 -15
- package/front_end/panels/console/SymbolizedErrorWidget.ts +96 -2
- package/front_end/panels/console/symbolizedErrorWidget.css +48 -0
- package/front_end/panels/elements/StandaloneStylesContainer.ts +1 -1
- package/front_end/panels/elements/StylePropertiesSection.ts +8 -0
- package/front_end/panels/elements/StylePropertyHighlighter.ts +4 -2
- package/front_end/panels/elements/StylePropertyTreeElement.ts +6 -5
- package/front_end/panels/elements/StylesContainer.ts +1 -1
- package/front_end/panels/elements/StylesSidebarPane.ts +4 -4
- package/front_end/panels/layer_viewer/PaintProfilerView.ts +106 -132
- package/front_end/panels/lighthouse/LighthousePanel.ts +4 -3
- package/front_end/panels/network/NetworkLogView.ts +3 -0
- package/front_end/panels/network/networkLogView.css +0 -15
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/ui/legacy/components/cookie_table/CookiesTable.ts +36 -3
- package/front_end/ui/legacy/components/data_grid/dataGridAiButton.css +20 -0
- package/front_end/ui/legacy/components/utils/Linkifier.ts +19 -4
- package/front_end/ui/visual_logging/KnownContextValues.ts +1 -0
- package/front_end/ui/visual_logging/LoggingDriver.ts +13 -0
- package/package.json +1 -1
|
@@ -362,23 +362,22 @@ function getMarkdownRenderer(conversation?: AiAssistanceModel.AiConversation.AiC
|
|
|
362
362
|
const context = conversation?.selectedContext;
|
|
363
363
|
|
|
364
364
|
if (context instanceof AiAssistanceModel.PerformanceAgent.PerformanceTraceContext) {
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
}
|
|
370
|
-
} else if (conversation?.type === AiAssistanceModel.AiHistoryStorage.ConversationType.PERFORMANCE) {
|
|
365
|
+
const focus = context.getItem();
|
|
366
|
+
return new PerformanceAgentMarkdownRenderer(focus.parsedTrace.data.Meta.mainFrameId, focus.lookupEvent.bind(focus));
|
|
367
|
+
}
|
|
368
|
+
if (conversation?.type === AiAssistanceModel.AiHistoryStorage.ConversationType.PERFORMANCE) {
|
|
371
369
|
// Handle historical conversations (can't linkify anything).
|
|
372
370
|
return new PerformanceAgentMarkdownRenderer();
|
|
373
|
-
}
|
|
374
|
-
|
|
371
|
+
}
|
|
372
|
+
if (Greendev.Prototypes.instance().isEnabled('emulationCapabilities') &&
|
|
375
373
|
conversation?.type === AiAssistanceModel.AiHistoryStorage.ConversationType.STYLING &&
|
|
376
374
|
SDK.TargetManager.TargetManager.instance().primaryPageTarget()?.model(SDK.DOMModel.DOMModel)) {
|
|
377
375
|
const domModel = SDK.TargetManager.TargetManager.instance().primaryPageTarget()?.model(SDK.DOMModel.DOMModel);
|
|
378
376
|
const resourceTreeModel = domModel?.target().model(SDK.ResourceTreeModel.ResourceTreeModel);
|
|
379
377
|
const mainFrameId = resourceTreeModel?.mainFrame?.id;
|
|
380
378
|
return new StylingAgentMarkdownRenderer(mainFrameId);
|
|
381
|
-
}
|
|
379
|
+
}
|
|
380
|
+
if (conversation?.type === AiAssistanceModel.AiHistoryStorage.ConversationType.ACCESSIBILITY) {
|
|
382
381
|
const domModel = SDK.TargetManager.TargetManager.instance().primaryPageTarget()?.model(SDK.DOMModel.DOMModel);
|
|
383
382
|
const mainDocumentURL = domModel?.existingDocument()?.documentURL;
|
|
384
383
|
return new AccessibilityAgentMarkdownRenderer(mainDocumentURL);
|
|
@@ -570,11 +569,11 @@ function defaultView(input: ViewInput, output: PanelViewOutput, target: HTMLElem
|
|
|
570
569
|
// clang-format on
|
|
571
570
|
}
|
|
572
571
|
|
|
573
|
-
function
|
|
572
|
+
function createDOMNodeContext(node: SDK.DOMModel.DOMNode|null): AiAssistanceModel.DOMNodeContext.DOMNodeContext|null {
|
|
574
573
|
if (!node) {
|
|
575
574
|
return null;
|
|
576
575
|
}
|
|
577
|
-
return new AiAssistanceModel.
|
|
576
|
+
return new AiAssistanceModel.DOMNodeContext.DOMNodeContext(node);
|
|
578
577
|
}
|
|
579
578
|
|
|
580
579
|
function createFileContext(file: Workspace.UISourceCode.UISourceCode|null): AiAssistanceModel.FileAgent.FileContext|
|
|
@@ -668,7 +667,7 @@ export class AiAssistancePanel extends UI.Panel.Panel {
|
|
|
668
667
|
#conversation?: AiAssistanceModel.AiConversation.AiConversation;
|
|
669
668
|
|
|
670
669
|
#selectedFile: AiAssistanceModel.FileAgent.FileContext|null = null;
|
|
671
|
-
#selectedElement: AiAssistanceModel.
|
|
670
|
+
#selectedElement: AiAssistanceModel.DOMNodeContext.DOMNodeContext|null = null;
|
|
672
671
|
#selectedPerformanceTrace: AiAssistanceModel.PerformanceAgent.PerformanceTraceContext|null = null;
|
|
673
672
|
#selectedRequest: AiAssistanceModel.NetworkAgent.RequestContext|null = null;
|
|
674
673
|
|
|
@@ -1117,7 +1116,7 @@ export class AiAssistancePanel extends UI.Panel.Panel {
|
|
|
1117
1116
|
this.#viewOutput.chatView?.focusTextInput();
|
|
1118
1117
|
void this.#handleAidaAvailabilityChange();
|
|
1119
1118
|
this.#selectedElement =
|
|
1120
|
-
|
|
1119
|
+
createDOMNodeContext(selectedElementFilter(UI.Context.Context.instance().flavor(SDK.DOMModel.DOMNode)));
|
|
1121
1120
|
this.#selectedRequest =
|
|
1122
1121
|
createRequestContext(UI.Context.Context.instance().flavor(SDK.NetworkRequest.NetworkRequest));
|
|
1123
1122
|
this.#selectedPerformanceTrace =
|
|
@@ -1225,7 +1224,7 @@ export class AiAssistancePanel extends UI.Panel.Panel {
|
|
|
1225
1224
|
return;
|
|
1226
1225
|
}
|
|
1227
1226
|
|
|
1228
|
-
this.#selectedElement =
|
|
1227
|
+
this.#selectedElement = createDOMNodeContext(selectedElementFilter(ev.data));
|
|
1229
1228
|
this.#updateConversationState(this.#conversation);
|
|
1230
1229
|
};
|
|
1231
1230
|
|
|
@@ -1576,6 +1575,11 @@ export class AiAssistancePanel extends UI.Panel.Panel {
|
|
|
1576
1575
|
targetConversationType = AiAssistanceModel.AiHistoryStorage.ConversationType.FILE;
|
|
1577
1576
|
break;
|
|
1578
1577
|
}
|
|
1578
|
+
case 'ai-assistance.storage-floating-button': {
|
|
1579
|
+
Host.userMetrics.actionTaken(Host.UserMetrics.Action.AiAssistanceOpenedFromStoragePanelFloatingButton);
|
|
1580
|
+
targetConversationType = AiAssistanceModel.AiHistoryStorage.ConversationType.STORAGE;
|
|
1581
|
+
break;
|
|
1582
|
+
}
|
|
1579
1583
|
}
|
|
1580
1584
|
|
|
1581
1585
|
if (!targetConversationType) {
|
|
@@ -1727,7 +1731,7 @@ export class AiAssistancePanel extends UI.Panel.Panel {
|
|
|
1727
1731
|
#handleConversationContextChange = (data: unknown): void => {
|
|
1728
1732
|
if (data instanceof AiAssistanceModel.FileAgent.FileContext) {
|
|
1729
1733
|
this.#selectedFile = data;
|
|
1730
|
-
} else if (data instanceof AiAssistanceModel.
|
|
1734
|
+
} else if (data instanceof AiAssistanceModel.DOMNodeContext.DOMNodeContext) {
|
|
1731
1735
|
this.#selectedElement = data;
|
|
1732
1736
|
} else if (data instanceof AiAssistanceModel.NetworkAgent.RequestContext) {
|
|
1733
1737
|
this.#selectedRequest = data;
|
|
@@ -297,3 +297,19 @@ UI.ActionRegistration.registerActionExtension({
|
|
|
297
297
|
},
|
|
298
298
|
condition: config => isFileAgentFeatureAvailable(config) && !isPolicyRestricted(config) && !isGeoRestricted(config),
|
|
299
299
|
});
|
|
300
|
+
|
|
301
|
+
UI.ActionRegistration.registerActionExtension({
|
|
302
|
+
actionId: 'ai-assistance.storage-floating-button',
|
|
303
|
+
contextTypes(): [] {
|
|
304
|
+
return [];
|
|
305
|
+
},
|
|
306
|
+
category: UI.ActionRegistration.ActionCategory.GLOBAL,
|
|
307
|
+
title: i18nAiBrandedString(UIStrings.debugWithGemini, UIStrings.debugWithAi),
|
|
308
|
+
configurableBindings: false,
|
|
309
|
+
async loadActionDelegate() {
|
|
310
|
+
const AiAssistance = await loadAiAssistanceModule();
|
|
311
|
+
return new AiAssistance.ActionDelegate();
|
|
312
|
+
},
|
|
313
|
+
condition: config =>
|
|
314
|
+
isStorageAgentFeatureAvailable(config) && !isPolicyRestricted(config) && !isGeoRestricted(config),
|
|
315
|
+
});
|
|
@@ -174,7 +174,7 @@ function getContextRemoveLabel(context: AiAssistanceModel.AiAgent.ConversationCo
|
|
|
174
174
|
if (context instanceof AiAssistanceModel.FileAgent.FileContext) {
|
|
175
175
|
return lockedString(UIStringsNotTranslate.removeContextFile);
|
|
176
176
|
}
|
|
177
|
-
if (context instanceof AiAssistanceModel.
|
|
177
|
+
if (context instanceof AiAssistanceModel.DOMNodeContext.DOMNodeContext) {
|
|
178
178
|
return lockedString(UIStringsNotTranslate.removeContextElement);
|
|
179
179
|
}
|
|
180
180
|
if (context instanceof AiAssistanceModel.NetworkAgent.RequestContext) {
|
|
@@ -336,7 +336,7 @@ export const DEFAULT_VIEW = (input: ViewInput, _output: ViewOutput, target: HTML
|
|
|
336
336
|
})}
|
|
337
337
|
>
|
|
338
338
|
${
|
|
339
|
-
input.context instanceof AiAssistanceModel.
|
|
339
|
+
input.context instanceof AiAssistanceModel.DOMNodeContext.DOMNodeContext ?
|
|
340
340
|
html`
|
|
341
341
|
<devtools-widget
|
|
342
342
|
class="title"
|
|
@@ -205,6 +205,10 @@ export class DOMStorageItemsView extends KeyValueStorageItemsView {
|
|
|
205
205
|
UI.Context.Context.instance().setFlavor(AiAssistanceModel.StorageItem.StorageItem, storageItem);
|
|
206
206
|
}
|
|
207
207
|
|
|
208
|
+
protected override isAiButtonEnabled(): boolean {
|
|
209
|
+
return UI.ActionRegistry.ActionRegistry.instance().hasAction('ai-assistance.storage-floating-button');
|
|
210
|
+
}
|
|
211
|
+
|
|
208
212
|
protected removeItem(key: string): void {
|
|
209
213
|
this.domStorage?.removeItem(key);
|
|
210
214
|
}
|
|
@@ -28,8 +28,13 @@
|
|
|
28
28
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
29
29
|
*/
|
|
30
30
|
/* eslint no-return-assign: "off" */
|
|
31
|
+
import '../../ui/components/buttons/buttons.js';
|
|
32
|
+
|
|
31
33
|
import * as i18n from '../../core/i18n/i18n.js';
|
|
34
|
+
import * as AIAssistance from '../../models/ai_assistance/ai_assistance.js';
|
|
32
35
|
import * as Geometry from '../../models/geometry/geometry.js';
|
|
36
|
+
// eslint-disable-next-line @devtools/es-modules-import
|
|
37
|
+
import dataGridAiButtonStyles from '../../ui/legacy/components/data_grid/dataGridAiButton.css.js';
|
|
33
38
|
import * as UI from '../../ui/legacy/legacy.js';
|
|
34
39
|
import {Directives as LitDirectives, html, nothing, render} from '../../ui/lit/lit.js';
|
|
35
40
|
import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
|
|
@@ -37,11 +42,13 @@ import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
|
|
|
37
42
|
import * as ApplicationComponents from './components/components.js';
|
|
38
43
|
import {StorageItemsToolbar} from './StorageItemsToolbar.js';
|
|
39
44
|
|
|
45
|
+
const STORAGE_FLOATING_BUTTON_ACTION_ID = 'ai-assistance.storage-floating-button';
|
|
46
|
+
|
|
40
47
|
const {ARIAUtils} = UI;
|
|
41
48
|
const {EmptyWidget} = UI.EmptyWidget;
|
|
42
49
|
const {VBox, widget} = UI.Widget;
|
|
43
50
|
const {Size} = Geometry;
|
|
44
|
-
const {repeat} = LitDirectives;
|
|
51
|
+
const {repeat, ifDefined} = LitDirectives;
|
|
45
52
|
|
|
46
53
|
type Widget = UI.Widget.Widget;
|
|
47
54
|
type VBox = UI.Widget.VBox;
|
|
@@ -87,6 +94,9 @@ export interface ViewInput {
|
|
|
87
94
|
onDeleteAll: () => void;
|
|
88
95
|
jslog?: string;
|
|
89
96
|
classes?: string[];
|
|
97
|
+
aiButtonTitle?: string;
|
|
98
|
+
showAiButton?: boolean;
|
|
99
|
+
onAiButtonClick?: (item: {key: string, value: string}, event: Event) => void;
|
|
90
100
|
}
|
|
91
101
|
|
|
92
102
|
interface ViewOutput {
|
|
@@ -150,6 +160,7 @@ export abstract class KeyValueStorageItemsView extends UI.Widget.VBox {
|
|
|
150
160
|
@deselect=${() => input.onSelect(null)}
|
|
151
161
|
>
|
|
152
162
|
<table>
|
|
163
|
+
${input.showAiButton ? html`<style>${dataGridAiButtonStyles}</style>`: nothing}
|
|
153
164
|
<tr>
|
|
154
165
|
<th id="key" sortable ?editable=${input.editable}>
|
|
155
166
|
${i18nString(UIStrings.key)}
|
|
@@ -165,7 +176,15 @@ export abstract class KeyValueStorageItemsView extends UI.Widget.VBox {
|
|
|
165
176
|
input.onEdit(item.key, item.value, e.detail.columnId, e.detail.valueBeforeEditing, e.detail.newText)}
|
|
166
177
|
@delete=${() => input.onDelete(item.key)}
|
|
167
178
|
selected=${(input.selectedKey === item.key) || nothing}>
|
|
168
|
-
<td>${
|
|
179
|
+
<td>${input.showAiButton ? html`
|
|
180
|
+
<span class="ai-button-container">
|
|
181
|
+
<devtools-floating-button
|
|
182
|
+
icon-name=${AIAssistance.AiUtils.getIconName()}
|
|
183
|
+
title=${ifDefined(input.aiButtonTitle)}
|
|
184
|
+
@click=${(e: Event) => input.onAiButtonClick?.(item, e)}
|
|
185
|
+
></devtools-floating-button>
|
|
186
|
+
</span>
|
|
187
|
+
` : nothing}${item.key}</td>
|
|
169
188
|
<td>${item.value.substr(0, MAX_VALUE_LENGTH)}</td>
|
|
170
189
|
</tr>`)}
|
|
171
190
|
<tr placeholder></tr>
|
|
@@ -217,15 +236,24 @@ export abstract class KeyValueStorageItemsView extends UI.Widget.VBox {
|
|
|
217
236
|
preview: this.#preview,
|
|
218
237
|
jslog: this.#jslog,
|
|
219
238
|
classes: this.#classes,
|
|
239
|
+
showAiButton: this.isAiButtonEnabled(),
|
|
240
|
+
aiButtonTitle: this.isAiButtonEnabled() &&
|
|
241
|
+
UI.ActionRegistry.ActionRegistry.instance().hasAction(STORAGE_FLOATING_BUTTON_ACTION_ID) ?
|
|
242
|
+
UI.ActionRegistry.ActionRegistry.instance().getAction(STORAGE_FLOATING_BUTTON_ACTION_ID).title() :
|
|
243
|
+
undefined,
|
|
220
244
|
onSelect: (item: {key: string, value: string}|null) => {
|
|
221
245
|
this.#toolbar?.setCanDeleteSelected(Boolean(item));
|
|
222
|
-
|
|
223
|
-
void this.#previewEntry(null);
|
|
224
|
-
} else {
|
|
225
|
-
void this.#previewEntry(item);
|
|
226
|
-
}
|
|
246
|
+
void this.#previewEntry(item);
|
|
227
247
|
this.selectedItemChanged(item);
|
|
228
248
|
},
|
|
249
|
+
onAiButtonClick: (item: {key: string, value: string}, event: Event) => {
|
|
250
|
+
event.stopPropagation();
|
|
251
|
+
viewInput.onSelect(item);
|
|
252
|
+
const actionRegistry = UI.ActionRegistry.ActionRegistry.instance();
|
|
253
|
+
if (actionRegistry.hasAction(STORAGE_FLOATING_BUTTON_ACTION_ID)) {
|
|
254
|
+
void actionRegistry.getAction(STORAGE_FLOATING_BUTTON_ACTION_ID).execute();
|
|
255
|
+
}
|
|
256
|
+
},
|
|
229
257
|
|
|
230
258
|
onSort: (ascending: boolean) => {
|
|
231
259
|
this.#isSortOrderAscending = ascending;
|
|
@@ -252,6 +280,10 @@ export abstract class KeyValueStorageItemsView extends UI.Widget.VBox {
|
|
|
252
280
|
this.#view(viewInput, viewOutput, this.contentElement);
|
|
253
281
|
}
|
|
254
282
|
|
|
283
|
+
protected isAiButtonEnabled(): boolean {
|
|
284
|
+
return false;
|
|
285
|
+
}
|
|
286
|
+
|
|
255
287
|
protected get toolbar(): StorageItemsToolbar|undefined {
|
|
256
288
|
return this.#toolbar;
|
|
257
289
|
}
|
|
@@ -27,7 +27,13 @@ import * as ThemeSupport from '../../ui/legacy/theme_support/theme_support.js';
|
|
|
27
27
|
import {ExtensionButton, ExtensionPanel, ExtensionSidebarPane} from './ExtensionPanel.js';
|
|
28
28
|
|
|
29
29
|
const extensionOrigins = new WeakMap<MessagePort, Platform.DevToolsPath.UrlString>();
|
|
30
|
-
const
|
|
30
|
+
const kForbiddenSchemes = [
|
|
31
|
+
'chrome:',
|
|
32
|
+
'chrome-untrusted:',
|
|
33
|
+
'chrome-error:',
|
|
34
|
+
'chrome-search:',
|
|
35
|
+
'devtools:',
|
|
36
|
+
];
|
|
31
37
|
|
|
32
38
|
declare global {
|
|
33
39
|
interface Window {
|
|
@@ -78,7 +84,6 @@ export class HostsPolicy {
|
|
|
78
84
|
}
|
|
79
85
|
|
|
80
86
|
class RegisteredExtension {
|
|
81
|
-
openResourceScheme: null|string = null;
|
|
82
87
|
constructor(readonly name: string, readonly hostsPolicy: HostsPolicy, readonly allowFileAccess: boolean) {
|
|
83
88
|
}
|
|
84
89
|
|
|
@@ -91,8 +96,11 @@ class RegisteredExtension {
|
|
|
91
96
|
return false;
|
|
92
97
|
}
|
|
93
98
|
|
|
94
|
-
|
|
95
|
-
|
|
99
|
+
let parsedURL;
|
|
100
|
+
try {
|
|
101
|
+
parsedURL = new URL(inspectedURL);
|
|
102
|
+
} catch {
|
|
103
|
+
return false;
|
|
96
104
|
}
|
|
97
105
|
|
|
98
106
|
if (!ExtensionServer.canInspectURL(inspectedURL)) {
|
|
@@ -104,12 +112,6 @@ class RegisteredExtension {
|
|
|
104
112
|
}
|
|
105
113
|
|
|
106
114
|
if (!this.allowFileAccess) {
|
|
107
|
-
let parsedURL;
|
|
108
|
-
try {
|
|
109
|
-
parsedURL = new URL(inspectedURL);
|
|
110
|
-
} catch {
|
|
111
|
-
return false;
|
|
112
|
-
}
|
|
113
115
|
return parsedURL.protocol !== 'file:';
|
|
114
116
|
}
|
|
115
117
|
|
|
@@ -863,18 +865,27 @@ export class ExtensionServer extends Common.ObjectWrapper.ObjectWrapper<EventTyp
|
|
|
863
865
|
if (!extension) {
|
|
864
866
|
throw new Error('Received a message from an unregistered extension');
|
|
865
867
|
}
|
|
866
|
-
|
|
867
|
-
|
|
868
|
+
let validScheme = message.urlScheme;
|
|
869
|
+
if (validScheme) {
|
|
870
|
+
try {
|
|
871
|
+
const urlToParse = validScheme.replace(/:?(\/\/)?$/, '') + '://test';
|
|
872
|
+
validScheme = new URL(urlToParse).protocol;
|
|
873
|
+
} catch {
|
|
874
|
+
return this.status.E_BADARG('urlScheme', 'Invalid scheme');
|
|
875
|
+
}
|
|
876
|
+
if (kForbiddenSchemes.includes(validScheme) || validScheme === 'file:') {
|
|
877
|
+
return this.status.E_BADARG('urlScheme', 'Scheme is forbidden');
|
|
878
|
+
}
|
|
868
879
|
}
|
|
869
880
|
const extensionOrigin = this.getExtensionOrigin(port);
|
|
870
881
|
const {name} = extension;
|
|
871
882
|
const registration = {
|
|
872
883
|
title: name,
|
|
873
884
|
origin: extensionOrigin,
|
|
874
|
-
scheme:
|
|
885
|
+
scheme: validScheme,
|
|
875
886
|
handler: this.handleOpenURL.bind(this, port),
|
|
876
887
|
shouldHandleOpenResource: (url: Platform.DevToolsPath.UrlString, schemes: Set<string>) =>
|
|
877
|
-
Components.Linkifier.Linkifier.shouldHandleOpenResource(
|
|
888
|
+
Components.Linkifier.Linkifier.shouldHandleOpenResource(validScheme || null, url, schemes),
|
|
878
889
|
};
|
|
879
890
|
if (message.handlerPresent) {
|
|
880
891
|
Components.Linkifier.Linkifier.registerLinkHandler(registration);
|
|
@@ -1645,7 +1656,7 @@ export class ExtensionServer extends Common.ObjectWrapper.ObjectWrapper<EventTyp
|
|
|
1645
1656
|
return false;
|
|
1646
1657
|
}
|
|
1647
1658
|
|
|
1648
|
-
if (
|
|
1659
|
+
if (kForbiddenSchemes.includes(parsedURL.protocol)) {
|
|
1649
1660
|
return false;
|
|
1650
1661
|
}
|
|
1651
1662
|
|
|
@@ -3,15 +3,108 @@
|
|
|
3
3
|
// found in the LICENSE file.
|
|
4
4
|
|
|
5
5
|
import * as Bindings from '../../models/bindings/bindings.js';
|
|
6
|
+
import type * as StackTrace from '../../models/stack_trace/stack_trace.js';
|
|
6
7
|
import type * as Workspace from '../../models/workspace/workspace.js';
|
|
8
|
+
import * as Components from '../../ui/legacy/components/utils/utils.js';
|
|
7
9
|
import * as UI from '../../ui/legacy/legacy.js';
|
|
10
|
+
import * as Lit from '../../ui/lit/lit.js';
|
|
11
|
+
|
|
12
|
+
const {html, render} = Lit;
|
|
8
13
|
|
|
9
14
|
export interface ViewInput {
|
|
10
15
|
error: Bindings.SymbolizedError.SymbolizedError;
|
|
11
16
|
ignoreListManager?: Workspace.IgnoreListManager.IgnoreListManager;
|
|
12
17
|
}
|
|
13
18
|
|
|
14
|
-
|
|
19
|
+
function renderHeader(content: Lit.LitTemplate|Node|UI.Widget.Widget, isCause: boolean): Lit.LitTemplate {
|
|
20
|
+
if (isCause) {
|
|
21
|
+
return html`<div class="symbolized-error-header"><span>Caused by: </span><span class="error-message-text">${
|
|
22
|
+
content}</span></div>`;
|
|
23
|
+
}
|
|
24
|
+
return html`<span class="error-message-text">${content}</span>`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function renderFramePrefix(
|
|
28
|
+
frame: StackTrace.StackTrace.ParsedErrorStackFrame,
|
|
29
|
+
_options: Components.Linkifier.LinkifyOptions): Lit.LitTemplate {
|
|
30
|
+
const asyncPrefix = frame.isAsync ? 'async ' : '';
|
|
31
|
+
if (frame.promiseIndex !== undefined) {
|
|
32
|
+
const name = frame.name || 'Promise.all';
|
|
33
|
+
return html`${asyncPrefix}${name} (index ${frame.promiseIndex})`;
|
|
34
|
+
}
|
|
35
|
+
const constructorPrefix = frame.isConstructor ? 'new ' : '';
|
|
36
|
+
let name = frame.name || '';
|
|
37
|
+
const isInline = Boolean(frame.rawName) && frame.name !== frame.rawName;
|
|
38
|
+
if (!isInline && frame.methodName && name && name !== frame.methodName && !name.endsWith('.' + frame.methodName) &&
|
|
39
|
+
!name.endsWith(' ' + frame.methodName)) {
|
|
40
|
+
name += ` [as ${frame.methodName}]`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (name) {
|
|
44
|
+
return html`${asyncPrefix}${constructorPrefix}${name} (`;
|
|
45
|
+
}
|
|
46
|
+
return html`${asyncPrefix}${constructorPrefix}`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function renderFrameSuffix(frame: StackTrace.StackTrace.ParsedErrorStackFrame): Lit.LitTemplate {
|
|
50
|
+
if (frame.promiseIndex !== undefined) {
|
|
51
|
+
return Lit.nothing;
|
|
52
|
+
}
|
|
53
|
+
if (frame.name) {
|
|
54
|
+
return html`)`;
|
|
55
|
+
}
|
|
56
|
+
return Lit.nothing;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const DEFAULT_VIEW = (input: ViewInput, _output: object, target: HTMLElement): void => {
|
|
60
|
+
const renderError = (error: Bindings.SymbolizedError.SymbolizedError, isCause: boolean): Lit.LitTemplate => {
|
|
61
|
+
if (!(error instanceof Bindings.SymbolizedError.SymbolizedErrorObject)) {
|
|
62
|
+
console.error('SymbolizedErrorWidget received an unsupported error type:', error);
|
|
63
|
+
return Lit.nothing;
|
|
64
|
+
}
|
|
65
|
+
const linkOptions: Components.Linkifier.LinkifyOptions = {
|
|
66
|
+
showColumnNumber: true,
|
|
67
|
+
inlineFrameIndex: 0,
|
|
68
|
+
maxLength: UI.UIUtils.MaxLengthForDisplayedURLsInConsole,
|
|
69
|
+
ignoreListManager: input.ignoreListManager,
|
|
70
|
+
};
|
|
71
|
+
const headerContent = html`${error.message}`;
|
|
72
|
+
const header = renderHeader(headerContent, isCause);
|
|
73
|
+
const syncFrames = error.stackTrace.syncFragment.frames;
|
|
74
|
+
// clang-format off
|
|
75
|
+
return html`
|
|
76
|
+
<span class=${isCause ? 'console-message-stack-trace-wrapper' : ''}
|
|
77
|
+
>${header}${syncFrames.length > 0 ? '\n' : ''}${syncFrames.map((frame: StackTrace.StackTrace.ParsedErrorStackFrame, i: number) => {
|
|
78
|
+
let linkElement: HTMLElement|Lit.LitTemplate = Lit.nothing;
|
|
79
|
+
let isBuiltin = false;
|
|
80
|
+
if (frame.promiseIndex !== undefined) {
|
|
81
|
+
// Promise.all doesn't have a linkable location.
|
|
82
|
+
isBuiltin = true;
|
|
83
|
+
} else if (frame.url || frame.uiSourceCode) {
|
|
84
|
+
const link = Components.Linkifier.Linkifier.linkifyStackTraceFrame(frame, linkOptions);
|
|
85
|
+
link.tabIndex = -1;
|
|
86
|
+
linkElement = link;
|
|
87
|
+
} else {
|
|
88
|
+
linkElement = html`<span><anonymous></span>`;
|
|
89
|
+
isBuiltin = true;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const newline = i < error.stackTrace.syncFragment.frames.length - 1 ? '\n' : '';
|
|
93
|
+
const frameClass = isBuiltin ? 'formatted-builtin-stack-frame' : 'formatted-stack-frame';
|
|
94
|
+
return html`
|
|
95
|
+
<span class=${frameClass}>${' at '}${renderFramePrefix(frame, linkOptions)}${linkElement}${renderFrameSuffix(frame)}${newline}</span>
|
|
96
|
+
`;
|
|
97
|
+
})}
|
|
98
|
+
</span>
|
|
99
|
+
${error.cause ? renderError(error.cause, true) : Lit.nothing}
|
|
100
|
+
`;
|
|
101
|
+
// clang-format on
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
// clang-format off
|
|
105
|
+
render(html`<span class="symbolized-error-widget">${renderError(input.error, false)}</span>`, target);
|
|
106
|
+
// clang-format on
|
|
107
|
+
};
|
|
15
108
|
|
|
16
109
|
export class SymbolizedErrorWidget extends UI.Widget.Widget {
|
|
17
110
|
#error?: Bindings.SymbolizedError.SymbolizedError;
|
|
@@ -19,7 +112,8 @@ export class SymbolizedErrorWidget extends UI.Widget.Widget {
|
|
|
19
112
|
#ignoreListManager?: Workspace.IgnoreListManager.IgnoreListManager;
|
|
20
113
|
|
|
21
114
|
constructor(element?: HTMLElement, view: typeof DEFAULT_VIEW = DEFAULT_VIEW) {
|
|
22
|
-
|
|
115
|
+
const host = element || document.createElement('span');
|
|
116
|
+
super(host, {classes: ['symbolized-error-widget-host']});
|
|
23
117
|
this.#view = view;
|
|
24
118
|
}
|
|
25
119
|
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026 The Chromium Authors
|
|
3
|
+
* Use of this source code is governed by a BSD-style license that can be
|
|
4
|
+
* found in the LICENSE file.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
.symbolized-error-widget {
|
|
8
|
+
white-space: pre-wrap;
|
|
9
|
+
word-break: break-all;
|
|
10
|
+
|
|
11
|
+
--display-formatted-stack-frame-default: block;
|
|
12
|
+
--display-ignored-formatted-stack-frame-local: var(--display-ignored-formatted-stack-frame, none);
|
|
13
|
+
|
|
14
|
+
&.show-hidden-rows {
|
|
15
|
+
--display-ignored-formatted-stack-frame-local: var(--display-formatted-stack-frame-default);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.symbolized-error-widget .formatted-stack-frame {
|
|
20
|
+
display: var(--display-formatted-stack-frame-default);
|
|
21
|
+
|
|
22
|
+
&:has(.ignore-list-link) {
|
|
23
|
+
display: var(--display-ignored-formatted-stack-frame-local);
|
|
24
|
+
opacity: 60%;
|
|
25
|
+
|
|
26
|
+
/* Subsequent builtin stack frames are also treated as ignored */
|
|
27
|
+
& + .formatted-builtin-stack-frame {
|
|
28
|
+
display: var(--display-ignored-formatted-stack-frame-local);
|
|
29
|
+
opacity: 60%;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.symbolized-error-widget .formatted-builtin-stack-frame {
|
|
35
|
+
display: var(--display-formatted-stack-frame-default);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.symbolized-error-widget-host {
|
|
39
|
+
display: inline;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.symbolized-error-header {
|
|
43
|
+
display: block;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.error-message-text {
|
|
47
|
+
display: inline;
|
|
48
|
+
}
|
|
@@ -276,7 +276,7 @@ export class StandaloneStylesContainer extends Common.ObjectWrapper.eventMixin<E
|
|
|
276
276
|
return null;
|
|
277
277
|
}
|
|
278
278
|
|
|
279
|
-
jumpToFunctionDefinition(_functionName: string): void {
|
|
279
|
+
jumpToFunctionDefinition(_functionName: string, _treeScopeDistance: number): void {
|
|
280
280
|
}
|
|
281
281
|
|
|
282
282
|
continueEditingElement(_sectionIndex: number, _propertyIndex: number): void {
|
|
@@ -379,6 +379,14 @@ export class StylePropertiesSection {
|
|
|
379
379
|
return this.sectionIdx;
|
|
380
380
|
}
|
|
381
381
|
|
|
382
|
+
treeScopeDistance(): number {
|
|
383
|
+
const treeScope = this.styleInternal.parentRule?.treeScope;
|
|
384
|
+
if (!treeScope) {
|
|
385
|
+
return -1;
|
|
386
|
+
}
|
|
387
|
+
return SDK.CSSMatchedStyles.distanceToTreeScope(this.matchedStyles.node(), treeScope);
|
|
388
|
+
}
|
|
389
|
+
|
|
382
390
|
static createRuleOriginNode(
|
|
383
391
|
matchedStyles: SDK.CSSMatchedStyles.CSSMatchedStyles, linkifier: Components.Linkifier.Linkifier,
|
|
384
392
|
rule: SDK.CSSRule.CSSRule|null): LitTemplate {
|
|
@@ -51,9 +51,11 @@ export class StylePropertyHighlighter {
|
|
|
51
51
|
PanelUtils.highlightElement(block.titleElement() as HTMLElement);
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
findAndHighlightSection(sectionName: string, blockName: string): void {
|
|
54
|
+
findAndHighlightSection(sectionName: string, blockName: string, treeScopeDistance = -1): void {
|
|
55
55
|
const block = this.styleSidebarPane.getSectionBlockByName(blockName);
|
|
56
|
-
const section = block?.sections.find(
|
|
56
|
+
const section = block?.sections.find(
|
|
57
|
+
section => section.headerText() === sectionName &&
|
|
58
|
+
(treeScopeDistance === -1 || section.treeScopeDistance() === treeScopeDistance));
|
|
57
59
|
if (!section || !block) {
|
|
58
60
|
return;
|
|
59
61
|
}
|
|
@@ -2661,10 +2661,11 @@ export class StylePropertyTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
2661
2661
|
return container;
|
|
2662
2662
|
}
|
|
2663
2663
|
|
|
2664
|
-
#getLinkableFunction(
|
|
2665
|
-
|
|
2664
|
+
#getLinkableFunction(
|
|
2665
|
+
functionName: string, matchedStyles: SDK.CSSMatchedStyles.CSSMatchedStyles,
|
|
2666
|
+
property: SDK.CSSProperty.CSSProperty): InlineEditor.LinkSwatch.LinkSwatch {
|
|
2666
2667
|
const swatch = new InlineEditor.LinkSwatch.LinkSwatch();
|
|
2667
|
-
const registeredFunction = matchedStyles.getRegisteredFunction(functionName);
|
|
2668
|
+
const {registeredFunction, treeScopeDistance} = matchedStyles.getRegisteredFunction(functionName, property);
|
|
2668
2669
|
const isDefined = Boolean(registeredFunction);
|
|
2669
2670
|
swatch.data = {
|
|
2670
2671
|
jslogContext: 'css-function',
|
|
@@ -2675,7 +2676,7 @@ export class StylePropertyTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
2675
2676
|
if (!registeredFunction) {
|
|
2676
2677
|
return;
|
|
2677
2678
|
}
|
|
2678
|
-
this.#stylesContainer.jumpToFunctionDefinition(registeredFunction);
|
|
2679
|
+
this.#stylesContainer.jumpToFunctionDefinition(registeredFunction, treeScopeDistance);
|
|
2679
2680
|
},
|
|
2680
2681
|
};
|
|
2681
2682
|
return swatch;
|
|
@@ -2696,7 +2697,7 @@ export class StylePropertyTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
2696
2697
|
const tooltipId = this.getTooltipId(`${functionName}-trace`);
|
|
2697
2698
|
// clang-format off
|
|
2698
2699
|
return html`
|
|
2699
|
-
<span tabIndex=-1 class=tracing-anchor aria-details=${tooltipId}>${functionName.startsWith('--') ? this.#getLinkableFunction(functionName, matchedStyles) : functionName}</span>
|
|
2700
|
+
<span tabIndex=-1 class=tracing-anchor aria-details=${tooltipId}>${functionName.startsWith('--') ? this.#getLinkableFunction(functionName, matchedStyles, context.property) : functionName}</span>
|
|
2700
2701
|
<devtools-tooltip
|
|
2701
2702
|
id=${tooltipId}
|
|
2702
2703
|
use-hotkey
|
|
@@ -37,7 +37,7 @@ export interface StylesContainer {
|
|
|
37
37
|
computedValue: string|null): ElementsComponents.CSSVariableValueView.CSSVariableValueView;
|
|
38
38
|
getVariableParserError(matchedStyles: SDK.CSSMatchedStyles.CSSMatchedStyles, variableName: string):
|
|
39
39
|
ElementsComponents.CSSVariableValueView.CSSVariableParserError|null;
|
|
40
|
-
jumpToFunctionDefinition(functionName: string): void;
|
|
40
|
+
jumpToFunctionDefinition(functionName: string, treeScopeDistance: number): void;
|
|
41
41
|
continueEditingElement(sectionIndex: number, propertyIndex: number): void;
|
|
42
42
|
revealProperty(cssProperty: SDK.CSSProperty.CSSProperty): void;
|
|
43
43
|
resetFocus(): void;
|
|
@@ -344,16 +344,16 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
|
|
|
344
344
|
}
|
|
345
345
|
}
|
|
346
346
|
|
|
347
|
-
jumpToSection(sectionName: string, blockName: string): void {
|
|
348
|
-
this.decorator.findAndHighlightSection(sectionName, blockName);
|
|
347
|
+
jumpToSection(sectionName: string, blockName: string, treeScopeDistance?: number): void {
|
|
348
|
+
this.decorator.findAndHighlightSection(sectionName, blockName, treeScopeDistance);
|
|
349
349
|
}
|
|
350
350
|
|
|
351
351
|
jumpToSectionBlock(section: string): void {
|
|
352
352
|
this.decorator.findAndHighlightSectionBlock(section);
|
|
353
353
|
}
|
|
354
354
|
|
|
355
|
-
jumpToFunctionDefinition(functionName: string): void {
|
|
356
|
-
this.jumpToSection(functionName, FUNCTION_SECTION_NAME);
|
|
355
|
+
jumpToFunctionDefinition(functionName: string, treeScopeDistance: number): void {
|
|
356
|
+
this.jumpToSection(functionName, FUNCTION_SECTION_NAME, treeScopeDistance);
|
|
357
357
|
}
|
|
358
358
|
|
|
359
359
|
jumpToFontPaletteDefinition(paletteName: string): void {
|