chrome-devtools-frontend 1.0.1643099 → 1.0.1645245
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/eslint.config.mjs +3 -1
- package/extension-api/ExtensionAPI.d.ts +83 -12
- package/front_end/core/host/UserMetrics.ts +3 -3
- package/front_end/core/root/ExperimentNames.ts +0 -1
- package/front_end/core/sdk/CSSPropertyParserMatchers.ts +2 -3
- package/front_end/core/sdk/ConsoleModel.ts +4 -0
- package/front_end/core/sdk/NetworkRequest.ts +12 -1
- package/front_end/core/sdk/SourceMap.ts +15 -18
- package/front_end/entrypoints/greendev_floaty/FloatyEntrypoint.ts +0 -2
- package/front_end/entrypoints/greendev_floaty/greendev_floaty.ts +0 -2
- package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshot.ts +37 -0
- package/front_end/entrypoints/main/MainImpl.ts +0 -6
- package/front_end/generated/SupportedCSSProperties.js +4 -2
- package/front_end/models/ai_assistance/AiAgent2.ts +23 -12
- package/front_end/models/ai_assistance/README.md +5 -4
- package/front_end/models/ai_assistance/agents/AccessibilityAgent.ts +22 -16
- package/front_end/models/ai_assistance/agents/AiAgent.ts +19 -6
- package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +5 -5
- package/front_end/models/ai_assistance/agents/ExecuteJavascript.ts +1 -94
- package/front_end/models/ai_assistance/agents/NetworkAgent.ts +25 -0
- package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +37 -0
- package/front_end/models/ai_assistance/agents/README.md +57 -0
- package/front_end/models/ai_assistance/agents/StorageAgent.ts +54 -1
- package/front_end/models/ai_assistance/agents/StylingAgent.snapshot.txt +1 -2
- package/front_end/models/ai_assistance/agents/StylingAgent.ts +31 -3
- package/front_end/models/ai_assistance/tools/ExecuteJavaScript.ts +12 -21
- package/front_end/models/ai_assistance/tools/GetStyles.ts +19 -12
- package/front_end/models/ai_assistance/tools/README.md +45 -0
- package/front_end/models/ai_assistance/tools/Tool.ts +78 -9
- package/front_end/models/ai_assistance/tools/ToolRegistry.ts +21 -5
- package/front_end/models/bindings/DebuggerWorkspaceBinding.ts +6 -9
- package/front_end/models/bindings/DefaultScriptMapping.ts +2 -1
- package/front_end/models/bindings/SymbolizedError.ts +45 -35
- package/front_end/models/extensions/ExtensionAPI.ts +138 -47
- package/front_end/models/har/Importer.ts +1 -0
- package/front_end/models/heap_snapshot/HeapSnapshotModel.ts +18 -2
- package/front_end/models/heap_snapshot/HeapSnapshotProxy.ts +4 -0
- package/front_end/models/source_map_scopes/FunctionCodeResolver.ts +12 -2
- package/front_end/models/stack_trace/DetailedErrorStackParser.ts +58 -52
- package/front_end/models/stack_trace/StackTrace.ts +7 -0
- package/front_end/models/stack_trace/StackTraceImpl.ts +13 -4
- package/front_end/models/stack_trace/StackTraceModel.ts +43 -9
- package/front_end/models/trace/Styles.ts +29 -7
- package/front_end/models/trace/handlers/PageLoadMetricsHandler.ts +33 -4
- package/front_end/models/trace/helpers/Timing.ts +10 -0
- package/front_end/models/trace/types/TraceEvents.ts +22 -2
- package/front_end/panels/accessibility/AccessibilitySidebarView.ts +2 -1
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +9 -2
- package/front_end/panels/ai_assistance/ai_assistance-meta.ts +16 -0
- package/front_end/panels/application/ApplicationPanelSidebar.ts +83 -0
- package/front_end/panels/application/ApplicationPanelTreeElement.ts +39 -0
- package/front_end/panels/application/CookieItemsView.ts +2 -2
- package/front_end/panels/application/ServiceWorkersView.ts +2 -2
- package/front_end/panels/application/WebMCPView.ts +0 -1
- package/front_end/panels/application/components/BackForwardCacheView.ts +1 -2
- package/front_end/panels/application/resourcesSidebar.css +11 -0
- package/front_end/panels/console/ConsoleView.ts +6 -1
- package/front_end/panels/console/ConsoleViewMessage.ts +46 -213
- package/front_end/panels/console/SymbolizedErrorWidget.ts +14 -8
- package/front_end/panels/elements/AdoptedStyleSheetTreeElement.ts +0 -1
- package/front_end/panels/elements/ElementsTreeElement.ts +0 -2
- package/front_end/panels/elements/PropertyRenderer.ts +0 -1
- package/front_end/panels/elements/StylesSidebarPane.ts +9 -2
- package/front_end/panels/issues/AffectedResourcesView.ts +1 -1
- package/front_end/panels/issues/AffectedSourcesView.ts +1 -1
- package/front_end/panels/lighthouse/LighthouseReportRenderer.ts +0 -1
- package/front_end/panels/mobile_throttling/ThrottlingSettingsTab.ts +10 -0
- package/front_end/panels/network/NetworkDataGridNode.ts +1 -2
- package/front_end/panels/network/NetworkLogView.ts +34 -7
- package/front_end/panels/profiler/HeapProfileView.ts +0 -1
- package/front_end/panels/profiler/HeapSnapshotGridNodes.ts +0 -1
- package/front_end/panels/profiler/HeapSnapshotView.ts +1 -1
- package/front_end/panels/settings/components/SyncSection.ts +1 -1
- package/front_end/panels/settings/emulation/DevicesSettingsTab.ts +1 -0
- package/front_end/panels/sources/SourcesPanel.ts +2 -1
- package/front_end/panels/timeline/TimelineFlameChartView.ts +5 -4
- package/front_end/panels/timeline/TimelinePanel.ts +7 -0
- package/front_end/panels/timeline/TimelineUIUtils.ts +13 -14
- package/front_end/panels/timeline/TimingsTrackAppender.ts +7 -5
- package/front_end/panels/timeline/components/LayoutShiftDetails.ts +0 -1
- package/front_end/panels/timeline/components/NetworkRequestDetails.ts +0 -2
- package/front_end/panels/timeline/components/insights/ForcedReflow.ts +0 -1
- package/front_end/panels/timeline/components/insights/NodeLink.ts +0 -1
- package/front_end/panels/timeline/overlays/OverlaysImpl.ts +2 -0
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/ui/helpers/OpenInNewTab.ts +3 -3
- package/front_end/ui/kit/link/Link.ts +16 -2
- package/front_end/ui/legacy/InspectorDrawerView.ts +14 -5
- package/front_end/ui/legacy/InspectorView.ts +4 -1
- package/front_end/ui/legacy/PlusButton.ts +6 -1
- package/front_end/ui/legacy/StackedPane.ts +229 -0
- package/front_end/ui/legacy/ViewManager.ts +59 -169
- package/front_end/ui/legacy/Widget.ts +19 -1
- package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +95 -31
- package/front_end/ui/legacy/components/utils/JSPresentationUtils.ts +0 -1
- package/front_end/ui/legacy/components/utils/Linkifier.ts +2 -16
- package/front_end/ui/legacy/legacy.ts +3 -1
- package/front_end/ui/visual_logging/KnownContextValues.ts +5 -0
- package/package.json +1 -1
|
@@ -40,6 +40,7 @@ import * as Platform from '../../core/platform/platform.js';
|
|
|
40
40
|
import * as Root from '../../core/root/root.js';
|
|
41
41
|
import * as SDK from '../../core/sdk/sdk.js';
|
|
42
42
|
import * as Protocol from '../../generated/protocol.js';
|
|
43
|
+
import * as AiAssistance from '../../models/ai_assistance/ai_assistance.js';
|
|
43
44
|
import * as LegacyWrapper from '../../ui/components/legacy_wrapper/legacy_wrapper.js';
|
|
44
45
|
import {createIcon} from '../../ui/kit/kit.js';
|
|
45
46
|
import * as SourceFrame from '../../ui/legacy/components/source_frame/source_frame.js';
|
|
@@ -93,6 +94,18 @@ import {TrustTokensTreeElement} from './TrustTokensTreeElement.js';
|
|
|
93
94
|
import {WebMCPTreeElement} from './WebMCPTreeElement.js';
|
|
94
95
|
|
|
95
96
|
const UIStrings = {
|
|
97
|
+
/**
|
|
98
|
+
* @description Text of a context menu item to start a chat with AI
|
|
99
|
+
*/
|
|
100
|
+
startAChat: 'Start a chat',
|
|
101
|
+
/**
|
|
102
|
+
* @description Text of a context menu item to explain contents of a local/session storage bucket with AI
|
|
103
|
+
*/
|
|
104
|
+
explainStorage: 'Explain storage',
|
|
105
|
+
/**
|
|
106
|
+
* @description Text of a context menu item to explain web cookies with AI
|
|
107
|
+
*/
|
|
108
|
+
explainCookies: 'Explain cookies',
|
|
96
109
|
/**
|
|
97
110
|
* @description Text in Application Panel Sidebar of the Application panel
|
|
98
111
|
*/
|
|
@@ -1762,15 +1775,51 @@ export class DOMStorageTreeElement extends ApplicationPanelTreeElement {
|
|
|
1762
1775
|
return false;
|
|
1763
1776
|
}
|
|
1764
1777
|
|
|
1778
|
+
/**
|
|
1779
|
+
* Resolves the DOM storage partition context (`localStorage` or `sessionStorage`)
|
|
1780
|
+
* associated with this tree element for AI assistance.
|
|
1781
|
+
*/
|
|
1782
|
+
#getStorageItem(): AiAssistance.StorageItem.StorageItem|null {
|
|
1783
|
+
const target = SDK.TargetManager.TargetManager.instance().primaryPageTarget();
|
|
1784
|
+
const mainPageOrigin =
|
|
1785
|
+
target?.inspectedURL() ? Common.ParsedURL.ParsedURL.extractOrigin(target.inspectedURL()) : '';
|
|
1786
|
+
if (!mainPageOrigin || !this.domStorage.storageKey) {
|
|
1787
|
+
return null;
|
|
1788
|
+
}
|
|
1789
|
+
const origin = SDK.StorageKeyManager.parseStorageKey(this.domStorage.storageKey).origin;
|
|
1790
|
+
const storageType = this.domStorage.isLocalStorage ? 'localStorage' : 'sessionStorage';
|
|
1791
|
+
return new AiAssistance.StorageItem.DOMStorageItem(mainPageOrigin, origin, this.domStorage.storageKey, storageType);
|
|
1792
|
+
}
|
|
1793
|
+
|
|
1765
1794
|
override onattach(): void {
|
|
1766
1795
|
super.onattach();
|
|
1767
1796
|
this.listItemElement.addEventListener('contextmenu', this.handleContextMenuEvent.bind(this), true);
|
|
1797
|
+
const storageItem = this.#getStorageItem();
|
|
1798
|
+
if (storageItem) {
|
|
1799
|
+
this.createAiButton(storageItem);
|
|
1800
|
+
}
|
|
1768
1801
|
}
|
|
1769
1802
|
|
|
1770
1803
|
private handleContextMenuEvent(event: MouseEvent): void {
|
|
1771
1804
|
const contextMenu = new UI.ContextMenu.ContextMenu(event);
|
|
1772
1805
|
contextMenu.defaultSection().appendItem(
|
|
1773
1806
|
i18nString(UIStrings.clear), () => this.domStorage.clear(), {jslogContext: 'clear'});
|
|
1807
|
+
|
|
1808
|
+
const storageItem = this.#getStorageItem();
|
|
1809
|
+
if (storageItem) {
|
|
1810
|
+
const openAiAssistanceId = 'ai-assistance.application-panel-context';
|
|
1811
|
+
if (UI.ActionRegistry.ActionRegistry.instance().hasAction(openAiAssistanceId)) {
|
|
1812
|
+
UI.Context.Context.instance().setFlavor(AiAssistance.StorageItem.StorageItem, storageItem);
|
|
1813
|
+
const action = UI.ActionRegistry.ActionRegistry.instance().getAction(openAiAssistanceId);
|
|
1814
|
+
const submenu = contextMenu.footerSection().appendSubMenuItem(action.title(), false, openAiAssistanceId);
|
|
1815
|
+
submenu.defaultSection().appendAction(openAiAssistanceId, i18nString(UIStrings.startAChat));
|
|
1816
|
+
submenu.defaultSection().appendItem(
|
|
1817
|
+
i18nString(UIStrings.explainStorage),
|
|
1818
|
+
() => action.execute({prompt: 'What is the purpose of this storage bucket?'}),
|
|
1819
|
+
{disabled: !action.enabled(), jslogContext: openAiAssistanceId + '.storage'});
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1822
|
+
|
|
1774
1823
|
void contextMenu.show();
|
|
1775
1824
|
}
|
|
1776
1825
|
}
|
|
@@ -1851,9 +1900,27 @@ export class CookieTreeElement extends ApplicationPanelTreeElement {
|
|
|
1851
1900
|
return this.#cookieDomain;
|
|
1852
1901
|
}
|
|
1853
1902
|
|
|
1903
|
+
/**
|
|
1904
|
+
* Resolves the cookie domain security context associated with this tree element
|
|
1905
|
+
* for AI assistance.
|
|
1906
|
+
*/
|
|
1907
|
+
#getStorageItem(): AiAssistance.StorageItem.StorageItem|null {
|
|
1908
|
+
const primaryTarget = SDK.TargetManager.TargetManager.instance().primaryPageTarget();
|
|
1909
|
+
const mainPageOrigin =
|
|
1910
|
+
primaryTarget?.inspectedURL() ? Common.ParsedURL.ParsedURL.extractOrigin(primaryTarget.inspectedURL()) : '';
|
|
1911
|
+
if (!mainPageOrigin || !this.#cookieDomain) {
|
|
1912
|
+
return null;
|
|
1913
|
+
}
|
|
1914
|
+
return new AiAssistance.StorageItem.CookieItem(mainPageOrigin, this.#cookieDomain);
|
|
1915
|
+
}
|
|
1916
|
+
|
|
1854
1917
|
override onattach(): void {
|
|
1855
1918
|
super.onattach();
|
|
1856
1919
|
this.listItemElement.addEventListener('contextmenu', this.handleContextMenuEvent.bind(this), true);
|
|
1920
|
+
const storageItem = this.#getStorageItem();
|
|
1921
|
+
if (storageItem) {
|
|
1922
|
+
this.createAiButton(storageItem);
|
|
1923
|
+
}
|
|
1857
1924
|
}
|
|
1858
1925
|
|
|
1859
1926
|
private handleContextMenuEvent(event: Event): void {
|
|
@@ -1861,6 +1928,22 @@ export class CookieTreeElement extends ApplicationPanelTreeElement {
|
|
|
1861
1928
|
contextMenu.defaultSection().appendItem(
|
|
1862
1929
|
i18nString(UIStrings.clear), () => this.resourcesPanel.clearCookies(this.target, this.#cookieDomain),
|
|
1863
1930
|
{jslogContext: 'clear'});
|
|
1931
|
+
|
|
1932
|
+
const storageItem = this.#getStorageItem();
|
|
1933
|
+
if (storageItem) {
|
|
1934
|
+
const openAiAssistanceId = 'ai-assistance.application-panel-context';
|
|
1935
|
+
if (UI.ActionRegistry.ActionRegistry.instance().hasAction(openAiAssistanceId)) {
|
|
1936
|
+
UI.Context.Context.instance().setFlavor(AiAssistance.StorageItem.StorageItem, storageItem);
|
|
1937
|
+
const action = UI.ActionRegistry.ActionRegistry.instance().getAction(openAiAssistanceId);
|
|
1938
|
+
const submenu = contextMenu.footerSection().appendSubMenuItem(action.title(), false, openAiAssistanceId);
|
|
1939
|
+
submenu.defaultSection().appendAction(openAiAssistanceId, i18nString(UIStrings.startAChat));
|
|
1940
|
+
submenu.defaultSection().appendItem(
|
|
1941
|
+
i18nString(UIStrings.explainCookies),
|
|
1942
|
+
() => action.execute({prompt: 'What is the purpose of these cookies?'}),
|
|
1943
|
+
{disabled: !action.enabled(), jslogContext: openAiAssistanceId + '.cookies'});
|
|
1944
|
+
}
|
|
1945
|
+
}
|
|
1946
|
+
|
|
1864
1947
|
void contextMenu.show();
|
|
1865
1948
|
}
|
|
1866
1949
|
|
|
@@ -2,15 +2,24 @@
|
|
|
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
|
+
/* eslint-disable @devtools/no-lit-render-outside-of-view */
|
|
6
|
+
|
|
7
|
+
import '../../ui/components/buttons/buttons.js';
|
|
8
|
+
|
|
5
9
|
import * as Common from '../../core/common/common.js';
|
|
6
10
|
import type * as Platform from '../../core/platform/platform.js';
|
|
11
|
+
import * as AiAssistance from '../../models/ai_assistance/ai_assistance.js';
|
|
7
12
|
import * as UI from '../../ui/legacy/legacy.js';
|
|
13
|
+
import * as Lit from '../../ui/lit/lit.js';
|
|
8
14
|
|
|
9
15
|
import type {ResourcesPanel} from './ResourcesPanel.js';
|
|
10
16
|
|
|
17
|
+
const {html} = Lit;
|
|
18
|
+
|
|
11
19
|
export class ApplicationPanelTreeElement extends UI.TreeOutline.TreeElement {
|
|
12
20
|
protected readonly resourcesPanel: ResourcesPanel;
|
|
13
21
|
private customItemURL?: Platform.DevToolsPath.UrlString;
|
|
22
|
+
protected aiButtonContainer?: HTMLElement;
|
|
14
23
|
|
|
15
24
|
constructor(resourcesPanel: ResourcesPanel, title: string, expandable: boolean, jslogContext: string) {
|
|
16
25
|
super(title, expandable, jslogContext);
|
|
@@ -56,6 +65,36 @@ export class ApplicationPanelTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
56
65
|
showView(view: UI.Widget.AnyWidget|null): void {
|
|
57
66
|
this.resourcesPanel.showView(view);
|
|
58
67
|
}
|
|
68
|
+
|
|
69
|
+
protected createAiButton(storageItem: AiAssistance.StorageItem.StorageItem): void {
|
|
70
|
+
const STORAGE_FLOATING_BUTTON_ACTION_ID = 'ai-assistance.storage-floating-button';
|
|
71
|
+
const actionRegistry = UI.ActionRegistry.ActionRegistry.instance();
|
|
72
|
+
if (!actionRegistry.hasAction(STORAGE_FLOATING_BUTTON_ACTION_ID)) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const action = actionRegistry.getAction(STORAGE_FLOATING_BUTTON_ACTION_ID);
|
|
76
|
+
if (!this.aiButtonContainer) {
|
|
77
|
+
this.aiButtonContainer = this.listItemElement.createChild('span', 'ai-button-container');
|
|
78
|
+
const icon = AiAssistance.AiUtils.getIconName();
|
|
79
|
+
const onClick = (ev: Event): void => {
|
|
80
|
+
ev.stopPropagation();
|
|
81
|
+
UI.Context.Context.instance().setFlavor(AiAssistance.StorageItem.StorageItem, storageItem);
|
|
82
|
+
void action.execute();
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// clang-format off
|
|
86
|
+
Lit.render(html`
|
|
87
|
+
<devtools-floating-button
|
|
88
|
+
icon-name=${icon}
|
|
89
|
+
title=${action.title()}
|
|
90
|
+
jslogcontext="ask-ai"
|
|
91
|
+
@click=${onClick}
|
|
92
|
+
@mousedown=${(ev: Event) => ev.stopPropagation()}>
|
|
93
|
+
</devtools-floating-button>
|
|
94
|
+
`, this.aiButtonContainer);
|
|
95
|
+
// clang-format on
|
|
96
|
+
}
|
|
97
|
+
}
|
|
59
98
|
}
|
|
60
99
|
|
|
61
100
|
export class ExpandableApplicationPanelTreeElement extends ApplicationPanelTreeElement {
|
|
@@ -317,7 +317,7 @@ export class CookieItemsView extends UI.Widget.VBox {
|
|
|
317
317
|
}
|
|
318
318
|
|
|
319
319
|
private updateAiAssistanceContext(cookie: SDK.Cookie.Cookie|null): void {
|
|
320
|
-
if (
|
|
320
|
+
if (cookie && cookie.httpOnly()) {
|
|
321
321
|
UI.Context.Context.instance().setFlavor(AiAssistanceModel.StorageItem.StorageItem, null);
|
|
322
322
|
return;
|
|
323
323
|
}
|
|
@@ -332,7 +332,7 @@ export class CookieItemsView extends UI.Widget.VBox {
|
|
|
332
332
|
return;
|
|
333
333
|
}
|
|
334
334
|
|
|
335
|
-
const storageItem = new AiAssistanceModel.StorageItem.CookieItem(mainPageOrigin, this.cookieDomain, cookie
|
|
335
|
+
const storageItem = new AiAssistanceModel.StorageItem.CookieItem(mainPageOrigin, this.cookieDomain, cookie?.name());
|
|
336
336
|
UI.Context.Context.instance().setFlavor(AiAssistanceModel.StorageItem.StorageItem, storageItem);
|
|
337
337
|
}
|
|
338
338
|
|
|
@@ -228,8 +228,8 @@ export class ServiceWorkersView extends UI.Widget.VBox implements
|
|
|
228
228
|
othersView.show(othersDiv);
|
|
229
229
|
const othersSection = othersView.appendSection(i18nString(UIStrings.serviceWorkersFromOtherOrigins));
|
|
230
230
|
const othersSectionRow = othersSection.appendRow();
|
|
231
|
-
const seeOthers = Link.create(
|
|
232
|
-
|
|
231
|
+
const seeOthers = Link.create('chrome://serviceworker-internals', i18nString(UIStrings.seeAllRegistrations),
|
|
232
|
+
undefined, 'view-all', 0, /* allowPrivileged=*/ true);
|
|
233
233
|
othersSectionRow.appendChild(seeOthers);
|
|
234
234
|
|
|
235
235
|
this.toolbar.appendToolbarItem(
|
|
@@ -341,7 +341,7 @@ function maybeRenderReasonContext(explanation: Protocol.Page.BackForwardCacheNot
|
|
|
341
341
|
const link = 'chrome://extensions/?id=' + explanation.context as Platform.DevToolsPath.UrlString;
|
|
342
342
|
// clang-format off
|
|
343
343
|
return html`${i18nString(UIStrings.blockingExtensionId)}
|
|
344
|
-
<devtools-link .href=${link}>${explanation.context}</devtools-link>`;
|
|
344
|
+
<devtools-link .href=${link} allow-privileged>${explanation.context}</devtools-link>`;
|
|
345
345
|
// clang-format on
|
|
346
346
|
}
|
|
347
347
|
return nothing;
|
|
@@ -393,7 +393,6 @@ function maybeRenderJavaScriptDetails(details: Protocol.Page.BackForwardCacheBlo
|
|
|
393
393
|
options: {
|
|
394
394
|
columnNumber: detail.columnNumber,
|
|
395
395
|
showColumnNumber: true,
|
|
396
|
-
inlineFrameIndex: 0,
|
|
397
396
|
maxLength: maxLengthForDisplayedURLs,
|
|
398
397
|
}
|
|
399
398
|
})}`));
|
|
@@ -64,3 +64,14 @@ devtools-icon.navigator-font-tree-item {
|
|
|
64
64
|
.no-device-bound-session {
|
|
65
65
|
font-style: italic;
|
|
66
66
|
}
|
|
67
|
+
|
|
68
|
+
.ai-button-container {
|
|
69
|
+
display: none;
|
|
70
|
+
position: absolute;
|
|
71
|
+
z-index: 999;
|
|
72
|
+
right: var(--sys-size-3);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.tree-outline li:hover .ai-button-container {
|
|
76
|
+
display: inline-flex;
|
|
77
|
+
}
|
|
@@ -75,6 +75,7 @@ import {
|
|
|
75
75
|
getMessageForElement,
|
|
76
76
|
} from './ConsoleViewMessage.js';
|
|
77
77
|
import {ConsoleViewport, type ConsoleViewportElement, type ConsoleViewportProvider} from './ConsoleViewport.js';
|
|
78
|
+
import symbolizedErrorWidgetStyles from './symbolizedErrorWidget.css.js';
|
|
78
79
|
|
|
79
80
|
const UIStrings = {
|
|
80
81
|
/**
|
|
@@ -356,7 +357,11 @@ export class ConsoleView extends UI.Widget.VBox implements
|
|
|
356
357
|
constructor(viewportThrottlerTimeout: number) {
|
|
357
358
|
super();
|
|
358
359
|
this.setMinimumSize(0, 35);
|
|
359
|
-
|
|
360
|
+
// We register the SymbolizedErrorWidget styles here because many web and e2e
|
|
361
|
+
// tests use `deepTextContent`, which would also include <style> tags if they
|
|
362
|
+
// were injected directly into the widget's shadow DOM.
|
|
363
|
+
this.registerRequiredCSS(consoleViewStyles, symbolizedErrorWidgetStyles, objectValueStyles,
|
|
364
|
+
CodeHighlighter.codeHighlighterStyles);
|
|
360
365
|
|
|
361
366
|
this.#searchableView = new UI.SearchableView.SearchableView(this, null);
|
|
362
367
|
this.#searchableView.element.classList.add('console-searchable-view');
|
|
@@ -34,7 +34,6 @@
|
|
|
34
34
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
35
35
|
*/
|
|
36
36
|
|
|
37
|
-
import type {Chrome} from '../../../extension-api/ExtensionAPI.js';
|
|
38
37
|
import * as Common from '../../core/common/common.js';
|
|
39
38
|
import * as Host from '../../core/host/host.js';
|
|
40
39
|
import * as i18n from '../../core/i18n/i18n.js';
|
|
@@ -45,7 +44,6 @@ import * as AiAssistanceModel from '../../models/ai_assistance/ai_assistance.js'
|
|
|
45
44
|
import * as Bindings from '../../models/bindings/bindings.js';
|
|
46
45
|
import type * as IssuesManager from '../../models/issues_manager/issues_manager.js';
|
|
47
46
|
import * as Logs from '../../models/logs/logs.js';
|
|
48
|
-
import * as StackTrace from '../../models/stack_trace/stack_trace.js';
|
|
49
47
|
import * as TextUtils from '../../models/text_utils/text_utils.js';
|
|
50
48
|
import * as Workspace from '../../models/workspace/workspace.js';
|
|
51
49
|
import * as CodeHighlighter from '../../ui/components/code_highlighter/code_highlighter.js';
|
|
@@ -66,6 +64,7 @@ import {format, updateStyle} from './ConsoleFormat.js';
|
|
|
66
64
|
import {ConsoleInsightTeaser} from './ConsoleInsightTeaser.js';
|
|
67
65
|
import consoleViewStyles from './consoleView.css.js';
|
|
68
66
|
import type {ConsoleViewportElement} from './ConsoleViewport.js';
|
|
67
|
+
import {SymbolizedErrorWidget} from './SymbolizedErrorWidget.js';
|
|
69
68
|
|
|
70
69
|
const UIStrings = {
|
|
71
70
|
/**
|
|
@@ -449,7 +448,14 @@ export class ConsoleViewMessage implements ConsoleViewportElement {
|
|
|
449
448
|
if (this.message.parameters?.length === 1) {
|
|
450
449
|
const parameter = this.message.parameters[0];
|
|
451
450
|
if (typeof parameter !== 'string' && parameter.type === 'string') {
|
|
452
|
-
|
|
451
|
+
const value = parameter.value as string;
|
|
452
|
+
const runtimeModel = this.message.runtimeModel();
|
|
453
|
+
if (runtimeModel && Bindings.SymbolizedError.isErrorLike(value)) {
|
|
454
|
+
const remoteObj = parameter instanceof SDK.RemoteObject.RemoteObject ?
|
|
455
|
+
parameter :
|
|
456
|
+
runtimeModel.createRemoteObject(parameter);
|
|
457
|
+
messageElement = this.renderSymbolizedError(remoteObj);
|
|
458
|
+
}
|
|
453
459
|
}
|
|
454
460
|
}
|
|
455
461
|
const args = this.message.parameters || [messageText];
|
|
@@ -570,23 +576,21 @@ export class ConsoleViewMessage implements ConsoleViewportElement {
|
|
|
570
576
|
const userMetric = this.#getLinkifierMetric();
|
|
571
577
|
if (stackFrameWithBreakpoint) {
|
|
572
578
|
return this.linkifier.maybeLinkifyConsoleCallFrame(runtimeModel.target(), stackFrameWithBreakpoint, {
|
|
573
|
-
inlineFrameIndex: 0,
|
|
574
579
|
revealBreakpoint: true,
|
|
575
580
|
userMetric,
|
|
576
581
|
});
|
|
577
582
|
}
|
|
578
583
|
if (scriptId) {
|
|
579
|
-
return this.linkifier.linkifyScriptLocation(
|
|
580
|
-
|
|
581
|
-
|
|
584
|
+
return this.linkifier.linkifyScriptLocation(runtimeModel.target(), scriptId,
|
|
585
|
+
url || Platform.DevToolsPath.EmptyUrlString, line,
|
|
586
|
+
{columnNumber: column, userMetric});
|
|
582
587
|
}
|
|
583
588
|
if (stackTrace?.callFrames.length) {
|
|
584
589
|
return this.linkifier.linkifyStackTraceTopFrame(runtimeModel.target(), stackTrace);
|
|
585
590
|
}
|
|
586
591
|
if (url && url !== 'undefined') {
|
|
587
|
-
return this.linkifier.linkifyScriptLocation(
|
|
588
|
-
|
|
589
|
-
{columnNumber: column, inlineFrameIndex: 0, userMetric});
|
|
592
|
+
return this.linkifier.linkifyScriptLocation(runtimeModel.target(), /* scriptId */ null, url, line,
|
|
593
|
+
{columnNumber: column, userMetric});
|
|
590
594
|
}
|
|
591
595
|
return null;
|
|
592
596
|
};
|
|
@@ -695,17 +699,19 @@ export class ConsoleViewMessage implements ConsoleViewportElement {
|
|
|
695
699
|
stackTracePreview.options = {widthConstrained: true};
|
|
696
700
|
if (stackTraceTarget && stackTrace) {
|
|
697
701
|
const selectableChildIndex = this.selectableChildren.length;
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
702
|
+
const stackTracePromise = Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance()
|
|
703
|
+
.createStackTraceFromProtocolRuntime(stackTrace, stackTraceTarget)
|
|
704
|
+
.then(stackTrace => {
|
|
705
|
+
stackTracePreview.stackTrace = stackTrace;
|
|
706
|
+
return stackTracePreview.updateComplete;
|
|
707
|
+
})
|
|
708
|
+
.then(() => {
|
|
709
|
+
const selectableLinks = stackTracePreview.linkElements.map(
|
|
710
|
+
(element: HTMLElement) => ({element, forceSelect: () => element.focus()}));
|
|
711
|
+
this.selectableChildren.splice(selectableChildIndex, 0, ...selectableLinks);
|
|
712
|
+
});
|
|
713
|
+
this.#formatErrorStackPromiseForTest =
|
|
714
|
+
Promise.all([this.#formatErrorStackPromiseForTest, stackTracePromise]).then(() => {});
|
|
709
715
|
}
|
|
710
716
|
stackTracePreview.markAsRoot();
|
|
711
717
|
stackTracePreview.show(stackTraceElement);
|
|
@@ -771,7 +777,7 @@ export class ConsoleViewMessage implements ConsoleViewportElement {
|
|
|
771
777
|
let element;
|
|
772
778
|
switch (outputType) {
|
|
773
779
|
case 'error':
|
|
774
|
-
element = this.
|
|
780
|
+
element = this.renderSymbolizedError(output);
|
|
775
781
|
break;
|
|
776
782
|
case 'function':
|
|
777
783
|
element = this.formatParameterAsFunction(output, includePreview);
|
|
@@ -980,42 +986,28 @@ export class ConsoleViewMessage implements ConsoleViewportElement {
|
|
|
980
986
|
return result;
|
|
981
987
|
}
|
|
982
988
|
|
|
983
|
-
private
|
|
984
|
-
const
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
// linking.
|
|
989
|
-
const formatErrorStack =
|
|
990
|
-
async(errorObj: SDK.RemoteObject.RemoteObject, includeCausedByPrefix: boolean): Promise<void> => {
|
|
991
|
-
const error = SDK.RemoteObject.RemoteError.objectAsError(errorObj);
|
|
992
|
-
const [details, cause] = await Promise.all([error.exceptionDetails(), error.cause()]);
|
|
993
|
-
let errorElement = this.tryFormatAsError(error.errorStack, details);
|
|
994
|
-
if (!errorElement) {
|
|
995
|
-
errorElement = document.createElement('span');
|
|
996
|
-
appendOrShow(errorElement, this.linkifyStringAsFragment(error.errorStack));
|
|
997
|
-
}
|
|
998
|
-
|
|
999
|
-
if (includeCausedByPrefix) {
|
|
1000
|
-
const causeElement = document.createElement('div');
|
|
1001
|
-
causeElement.append('Caused by: ', errorElement);
|
|
1002
|
-
result.appendChild(causeElement);
|
|
1003
|
-
} else {
|
|
1004
|
-
result.appendChild(errorElement);
|
|
1005
|
-
}
|
|
989
|
+
private renderSymbolizedError(errorRemoteObject: SDK.RemoteObject.RemoteObject): HTMLElement {
|
|
990
|
+
const container = document.createElement('span');
|
|
991
|
+
const widget = new SymbolizedErrorWidget();
|
|
992
|
+
widget.ignoreListManager = Workspace.IgnoreListManager.IgnoreListManager.instance();
|
|
993
|
+
const selectableChildIndex = this.selectableChildren.length;
|
|
1006
994
|
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
result.append(stringCauseElement);
|
|
995
|
+
const format = async(): Promise<void> => {
|
|
996
|
+
const error = await Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance().createSymbolizedError(
|
|
997
|
+
errorRemoteObject, this.message.exceptionDetails);
|
|
998
|
+
if (error) {
|
|
999
|
+
widget.error = error;
|
|
1013
1000
|
}
|
|
1001
|
+
await widget.updateComplete;
|
|
1002
|
+
const selectableLinks =
|
|
1003
|
+
widget.linkElements.map((element: HTMLElement) => ({element, forceSelect: () => element.focus()}));
|
|
1004
|
+
this.selectableChildren.splice(selectableChildIndex, 0, ...selectableLinks);
|
|
1014
1005
|
};
|
|
1015
1006
|
|
|
1016
|
-
this.#formatErrorStackPromiseForTest =
|
|
1017
|
-
|
|
1018
|
-
|
|
1007
|
+
this.#formatErrorStackPromiseForTest = Promise.all([this.#formatErrorStackPromiseForTest, format()]).then(() => {});
|
|
1008
|
+
widget.markAsRoot();
|
|
1009
|
+
widget.show(container);
|
|
1010
|
+
return container;
|
|
1019
1011
|
}
|
|
1020
1012
|
|
|
1021
1013
|
private formatAsArrayEntry(output: SDK.RemoteObject.RemoteObject): DocumentFragment {
|
|
@@ -1781,165 +1773,6 @@ export class ConsoleViewMessage implements ConsoleViewportElement {
|
|
|
1781
1773
|
return this.searchHighlightNodes[index];
|
|
1782
1774
|
}
|
|
1783
1775
|
|
|
1784
|
-
private async getInlineFrames(
|
|
1785
|
-
debuggerModel: SDK.DebuggerModel.DebuggerModel, url: Platform.DevToolsPath.UrlString,
|
|
1786
|
-
lineNumber: number|undefined, columnNumber: number|undefined): Promise<{frames: Chrome.DevTools.FunctionInfo[]}> {
|
|
1787
|
-
const debuggerWorkspaceBinding = Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance();
|
|
1788
|
-
const projects = Workspace.Workspace.WorkspaceImpl.instance().projects();
|
|
1789
|
-
const uiSourceCodes = projects.map(project => project.uiSourceCodeForURL(url)).flat().filter(f => !!f);
|
|
1790
|
-
const scripts =
|
|
1791
|
-
uiSourceCodes.map(uiSourceCode => debuggerWorkspaceBinding.scriptsForUISourceCode(uiSourceCode)).flat();
|
|
1792
|
-
if (scripts.length) {
|
|
1793
|
-
const location =
|
|
1794
|
-
new SDK.DebuggerModel.Location(debuggerModel, scripts[0].scriptId, lineNumber || 0, columnNumber);
|
|
1795
|
-
const functionInfo = await debuggerWorkspaceBinding.pluginManager.getFunctionInfo(scripts[0], location);
|
|
1796
|
-
return functionInfo && 'frames' in functionInfo ? functionInfo : {frames: []};
|
|
1797
|
-
}
|
|
1798
|
-
|
|
1799
|
-
return {frames: []};
|
|
1800
|
-
}
|
|
1801
|
-
|
|
1802
|
-
// Expand inline stack frames in the formatted error in the stackTrace element, inserting new elements before the
|
|
1803
|
-
// insertBefore anchor.
|
|
1804
|
-
private async expandInlineStackFrames(
|
|
1805
|
-
debuggerModel: SDK.DebuggerModel.DebuggerModel, prefix: string, suffix: string,
|
|
1806
|
-
url: Platform.DevToolsPath.UrlString, lineNumber: number|undefined, columnNumber: number|undefined,
|
|
1807
|
-
stackTrace: HTMLElement, insertBefore: HTMLElement): Promise<boolean> {
|
|
1808
|
-
const {frames} = await this.getInlineFrames(debuggerModel, url, lineNumber, columnNumber);
|
|
1809
|
-
if (!frames.length) {
|
|
1810
|
-
return false;
|
|
1811
|
-
}
|
|
1812
|
-
|
|
1813
|
-
for (let f = 0; f < frames.length; ++f) {
|
|
1814
|
-
const {name} = frames[f];
|
|
1815
|
-
const formattedLine = document.createElement('span');
|
|
1816
|
-
appendOrShow(formattedLine, this.linkifyStringAsFragment(`${prefix} ${name} (`));
|
|
1817
|
-
const scriptLocationLink = this.linkifier.linkifyScriptLocation(
|
|
1818
|
-
debuggerModel.target(), null, url, lineNumber, {columnNumber, inlineFrameIndex: f});
|
|
1819
|
-
scriptLocationLink.tabIndex = -1;
|
|
1820
|
-
this.selectableChildren.push({element: scriptLocationLink, forceSelect: () => scriptLocationLink.focus()});
|
|
1821
|
-
formattedLine.appendChild(scriptLocationLink);
|
|
1822
|
-
appendOrShow(formattedLine, this.linkifyStringAsFragment(suffix));
|
|
1823
|
-
formattedLine.classList.add('formatted-stack-frame');
|
|
1824
|
-
stackTrace.insertBefore(formattedLine, insertBefore);
|
|
1825
|
-
}
|
|
1826
|
-
return true;
|
|
1827
|
-
}
|
|
1828
|
-
|
|
1829
|
-
private createScriptLocationLinkForSyntaxError(
|
|
1830
|
-
debuggerModel: SDK.DebuggerModel.DebuggerModel, exceptionDetails: Protocol.Runtime.ExceptionDetails): HTMLElement
|
|
1831
|
-
|undefined {
|
|
1832
|
-
const {scriptId, lineNumber, columnNumber} = exceptionDetails;
|
|
1833
|
-
if (!scriptId) {
|
|
1834
|
-
return;
|
|
1835
|
-
}
|
|
1836
|
-
|
|
1837
|
-
// SyntaxErrors might not populate the URL field. Try to resolve it via scriptId.
|
|
1838
|
-
const url =
|
|
1839
|
-
exceptionDetails.url as Platform.DevToolsPath.UrlString || debuggerModel.scriptForId(scriptId)?.sourceURL;
|
|
1840
|
-
if (!url) {
|
|
1841
|
-
return;
|
|
1842
|
-
}
|
|
1843
|
-
|
|
1844
|
-
const scriptLocationLink = this.linkifier.linkifyScriptLocation(
|
|
1845
|
-
debuggerModel.target(), exceptionDetails.scriptId || null, url, lineNumber, {
|
|
1846
|
-
columnNumber,
|
|
1847
|
-
inlineFrameIndex: 0,
|
|
1848
|
-
showColumnNumber: true,
|
|
1849
|
-
});
|
|
1850
|
-
scriptLocationLink.tabIndex = -1;
|
|
1851
|
-
return scriptLocationLink;
|
|
1852
|
-
}
|
|
1853
|
-
|
|
1854
|
-
private tryFormatAsError(string: string, exceptionDetails?: Protocol.Runtime.ExceptionDetails): HTMLElement|null {
|
|
1855
|
-
const runtimeModel = this.message.runtimeModel();
|
|
1856
|
-
if (!runtimeModel) {
|
|
1857
|
-
return null;
|
|
1858
|
-
}
|
|
1859
|
-
|
|
1860
|
-
const issueSummary = exceptionDetails?.exceptionMetaData?.issueSummary;
|
|
1861
|
-
if (typeof issueSummary === 'string') {
|
|
1862
|
-
string = StackTrace.ErrorStackParser.concatErrorDescriptionAndIssueSummary(string, issueSummary);
|
|
1863
|
-
}
|
|
1864
|
-
|
|
1865
|
-
const linkInfos = StackTrace.ErrorStackParser.parseSourcePositionsFromErrorStack(runtimeModel, string);
|
|
1866
|
-
if (!linkInfos?.length) {
|
|
1867
|
-
return null;
|
|
1868
|
-
}
|
|
1869
|
-
if (exceptionDetails?.stackTrace) {
|
|
1870
|
-
StackTrace.ErrorStackParser.augmentErrorStackWithScriptIds(linkInfos, exceptionDetails.stackTrace);
|
|
1871
|
-
}
|
|
1872
|
-
|
|
1873
|
-
const debuggerModel = runtimeModel.debuggerModel();
|
|
1874
|
-
const formattedResult = document.createElement('span');
|
|
1875
|
-
|
|
1876
|
-
for (let i = 0; i < linkInfos.length; ++i) {
|
|
1877
|
-
const newline = i < linkInfos.length - 1 ? '\n' : '';
|
|
1878
|
-
const {line, link, isCallFrame} = linkInfos[i];
|
|
1879
|
-
// Syntax errors don't have a stack frame that points to the source position
|
|
1880
|
-
// where the error occurred. We use the source location from the
|
|
1881
|
-
// exceptionDetails and append it to the end of the message instead.
|
|
1882
|
-
if (!link && exceptionDetails && line.startsWith('SyntaxError')) {
|
|
1883
|
-
appendOrShow(formattedResult, this.linkifyStringAsFragment(line));
|
|
1884
|
-
const maybeScriptLocation = this.createScriptLocationLinkForSyntaxError(debuggerModel, exceptionDetails);
|
|
1885
|
-
if (maybeScriptLocation) {
|
|
1886
|
-
formattedResult.append(' (at ');
|
|
1887
|
-
formattedResult.appendChild(maybeScriptLocation);
|
|
1888
|
-
formattedResult.append(')');
|
|
1889
|
-
}
|
|
1890
|
-
formattedResult.append(newline);
|
|
1891
|
-
continue;
|
|
1892
|
-
}
|
|
1893
|
-
if (!isCallFrame) {
|
|
1894
|
-
appendOrShow(formattedResult, this.linkifyStringAsFragment(`${line}${newline}`));
|
|
1895
|
-
continue;
|
|
1896
|
-
}
|
|
1897
|
-
const formattedLine = document.createElement('span');
|
|
1898
|
-
if (!link) {
|
|
1899
|
-
appendOrShow(formattedLine, this.linkifyStringAsFragment(`${line}${newline}`));
|
|
1900
|
-
formattedLine.classList.add('formatted-builtin-stack-frame');
|
|
1901
|
-
formattedResult.appendChild(formattedLine);
|
|
1902
|
-
continue;
|
|
1903
|
-
}
|
|
1904
|
-
const suffix = `${link.suffix}${newline}`;
|
|
1905
|
-
appendOrShow(formattedLine, this.linkifyStringAsFragment(link.prefix));
|
|
1906
|
-
const scriptLocationLink = this.linkifier.linkifyScriptLocation(
|
|
1907
|
-
debuggerModel.target(), link.scriptId || null, link.url, link.lineNumber, {
|
|
1908
|
-
columnNumber: link.columnNumber,
|
|
1909
|
-
inlineFrameIndex: 0,
|
|
1910
|
-
showColumnNumber: true,
|
|
1911
|
-
});
|
|
1912
|
-
scriptLocationLink.tabIndex = -1;
|
|
1913
|
-
this.selectableChildren.push({element: scriptLocationLink, forceSelect: () => scriptLocationLink.focus()});
|
|
1914
|
-
formattedLine.appendChild(scriptLocationLink);
|
|
1915
|
-
appendOrShow(formattedLine, this.linkifyStringAsFragment(suffix));
|
|
1916
|
-
formattedLine.classList.add('formatted-stack-frame');
|
|
1917
|
-
formattedResult.appendChild(formattedLine);
|
|
1918
|
-
|
|
1919
|
-
if (!link.enclosedInBraces) {
|
|
1920
|
-
continue;
|
|
1921
|
-
}
|
|
1922
|
-
|
|
1923
|
-
const prefixWithoutFunction = link.prefix.substring(0, link.prefix.lastIndexOf(' ', link.prefix.length - 3));
|
|
1924
|
-
|
|
1925
|
-
// If we were able to parse the function name from the stack trace line, try to replace it with an expansion of
|
|
1926
|
-
// any inline frames.
|
|
1927
|
-
const selectableChildIndex = this.selectableChildren.length - 1;
|
|
1928
|
-
void this
|
|
1929
|
-
.expandInlineStackFrames(
|
|
1930
|
-
debuggerModel, prefixWithoutFunction, suffix, link.url, link.lineNumber, link.columnNumber,
|
|
1931
|
-
formattedResult, formattedLine)
|
|
1932
|
-
.then(modified => {
|
|
1933
|
-
if (modified) {
|
|
1934
|
-
formattedResult.removeChild(formattedLine);
|
|
1935
|
-
this.selectableChildren.splice(selectableChildIndex, 1);
|
|
1936
|
-
}
|
|
1937
|
-
});
|
|
1938
|
-
}
|
|
1939
|
-
|
|
1940
|
-
return formattedResult;
|
|
1941
|
-
}
|
|
1942
|
-
|
|
1943
1776
|
static linkifyWithCustomLinkifier(
|
|
1944
1777
|
string: string,
|
|
1945
1778
|
linkifier: (arg0: string, arg1: Platform.DevToolsPath.UrlString, arg2?: number, arg3?: number) => Node):
|