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.
Files changed (99) hide show
  1. package/eslint.config.mjs +3 -1
  2. package/extension-api/ExtensionAPI.d.ts +83 -12
  3. package/front_end/core/host/UserMetrics.ts +3 -3
  4. package/front_end/core/root/ExperimentNames.ts +0 -1
  5. package/front_end/core/sdk/CSSPropertyParserMatchers.ts +2 -3
  6. package/front_end/core/sdk/ConsoleModel.ts +4 -0
  7. package/front_end/core/sdk/NetworkRequest.ts +12 -1
  8. package/front_end/core/sdk/SourceMap.ts +15 -18
  9. package/front_end/entrypoints/greendev_floaty/FloatyEntrypoint.ts +0 -2
  10. package/front_end/entrypoints/greendev_floaty/greendev_floaty.ts +0 -2
  11. package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshot.ts +37 -0
  12. package/front_end/entrypoints/main/MainImpl.ts +0 -6
  13. package/front_end/generated/SupportedCSSProperties.js +4 -2
  14. package/front_end/models/ai_assistance/AiAgent2.ts +23 -12
  15. package/front_end/models/ai_assistance/README.md +5 -4
  16. package/front_end/models/ai_assistance/agents/AccessibilityAgent.ts +22 -16
  17. package/front_end/models/ai_assistance/agents/AiAgent.ts +19 -6
  18. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +5 -5
  19. package/front_end/models/ai_assistance/agents/ExecuteJavascript.ts +1 -94
  20. package/front_end/models/ai_assistance/agents/NetworkAgent.ts +25 -0
  21. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +37 -0
  22. package/front_end/models/ai_assistance/agents/README.md +57 -0
  23. package/front_end/models/ai_assistance/agents/StorageAgent.ts +54 -1
  24. package/front_end/models/ai_assistance/agents/StylingAgent.snapshot.txt +1 -2
  25. package/front_end/models/ai_assistance/agents/StylingAgent.ts +31 -3
  26. package/front_end/models/ai_assistance/tools/ExecuteJavaScript.ts +12 -21
  27. package/front_end/models/ai_assistance/tools/GetStyles.ts +19 -12
  28. package/front_end/models/ai_assistance/tools/README.md +45 -0
  29. package/front_end/models/ai_assistance/tools/Tool.ts +78 -9
  30. package/front_end/models/ai_assistance/tools/ToolRegistry.ts +21 -5
  31. package/front_end/models/bindings/DebuggerWorkspaceBinding.ts +6 -9
  32. package/front_end/models/bindings/DefaultScriptMapping.ts +2 -1
  33. package/front_end/models/bindings/SymbolizedError.ts +45 -35
  34. package/front_end/models/extensions/ExtensionAPI.ts +138 -47
  35. package/front_end/models/har/Importer.ts +1 -0
  36. package/front_end/models/heap_snapshot/HeapSnapshotModel.ts +18 -2
  37. package/front_end/models/heap_snapshot/HeapSnapshotProxy.ts +4 -0
  38. package/front_end/models/source_map_scopes/FunctionCodeResolver.ts +12 -2
  39. package/front_end/models/stack_trace/DetailedErrorStackParser.ts +58 -52
  40. package/front_end/models/stack_trace/StackTrace.ts +7 -0
  41. package/front_end/models/stack_trace/StackTraceImpl.ts +13 -4
  42. package/front_end/models/stack_trace/StackTraceModel.ts +43 -9
  43. package/front_end/models/trace/Styles.ts +29 -7
  44. package/front_end/models/trace/handlers/PageLoadMetricsHandler.ts +33 -4
  45. package/front_end/models/trace/helpers/Timing.ts +10 -0
  46. package/front_end/models/trace/types/TraceEvents.ts +22 -2
  47. package/front_end/panels/accessibility/AccessibilitySidebarView.ts +2 -1
  48. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +9 -2
  49. package/front_end/panels/ai_assistance/ai_assistance-meta.ts +16 -0
  50. package/front_end/panels/application/ApplicationPanelSidebar.ts +83 -0
  51. package/front_end/panels/application/ApplicationPanelTreeElement.ts +39 -0
  52. package/front_end/panels/application/CookieItemsView.ts +2 -2
  53. package/front_end/panels/application/ServiceWorkersView.ts +2 -2
  54. package/front_end/panels/application/WebMCPView.ts +0 -1
  55. package/front_end/panels/application/components/BackForwardCacheView.ts +1 -2
  56. package/front_end/panels/application/resourcesSidebar.css +11 -0
  57. package/front_end/panels/console/ConsoleView.ts +6 -1
  58. package/front_end/panels/console/ConsoleViewMessage.ts +46 -213
  59. package/front_end/panels/console/SymbolizedErrorWidget.ts +14 -8
  60. package/front_end/panels/elements/AdoptedStyleSheetTreeElement.ts +0 -1
  61. package/front_end/panels/elements/ElementsTreeElement.ts +0 -2
  62. package/front_end/panels/elements/PropertyRenderer.ts +0 -1
  63. package/front_end/panels/elements/StylesSidebarPane.ts +9 -2
  64. package/front_end/panels/issues/AffectedResourcesView.ts +1 -1
  65. package/front_end/panels/issues/AffectedSourcesView.ts +1 -1
  66. package/front_end/panels/lighthouse/LighthouseReportRenderer.ts +0 -1
  67. package/front_end/panels/mobile_throttling/ThrottlingSettingsTab.ts +10 -0
  68. package/front_end/panels/network/NetworkDataGridNode.ts +1 -2
  69. package/front_end/panels/network/NetworkLogView.ts +34 -7
  70. package/front_end/panels/profiler/HeapProfileView.ts +0 -1
  71. package/front_end/panels/profiler/HeapSnapshotGridNodes.ts +0 -1
  72. package/front_end/panels/profiler/HeapSnapshotView.ts +1 -1
  73. package/front_end/panels/settings/components/SyncSection.ts +1 -1
  74. package/front_end/panels/settings/emulation/DevicesSettingsTab.ts +1 -0
  75. package/front_end/panels/sources/SourcesPanel.ts +2 -1
  76. package/front_end/panels/timeline/TimelineFlameChartView.ts +5 -4
  77. package/front_end/panels/timeline/TimelinePanel.ts +7 -0
  78. package/front_end/panels/timeline/TimelineUIUtils.ts +13 -14
  79. package/front_end/panels/timeline/TimingsTrackAppender.ts +7 -5
  80. package/front_end/panels/timeline/components/LayoutShiftDetails.ts +0 -1
  81. package/front_end/panels/timeline/components/NetworkRequestDetails.ts +0 -2
  82. package/front_end/panels/timeline/components/insights/ForcedReflow.ts +0 -1
  83. package/front_end/panels/timeline/components/insights/NodeLink.ts +0 -1
  84. package/front_end/panels/timeline/overlays/OverlaysImpl.ts +2 -0
  85. package/front_end/third_party/chromium/README.chromium +1 -1
  86. package/front_end/ui/helpers/OpenInNewTab.ts +3 -3
  87. package/front_end/ui/kit/link/Link.ts +16 -2
  88. package/front_end/ui/legacy/InspectorDrawerView.ts +14 -5
  89. package/front_end/ui/legacy/InspectorView.ts +4 -1
  90. package/front_end/ui/legacy/PlusButton.ts +6 -1
  91. package/front_end/ui/legacy/StackedPane.ts +229 -0
  92. package/front_end/ui/legacy/ViewManager.ts +59 -169
  93. package/front_end/ui/legacy/Widget.ts +19 -1
  94. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +95 -31
  95. package/front_end/ui/legacy/components/utils/JSPresentationUtils.ts +0 -1
  96. package/front_end/ui/legacy/components/utils/Linkifier.ts +2 -16
  97. package/front_end/ui/legacy/legacy.ts +3 -1
  98. package/front_end/ui/visual_logging/KnownContextValues.ts +5 -0
  99. 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 (!cookie || cookie.httpOnly()) {
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.name());
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
- 'chrome://serviceworker-internals', i18nString(UIStrings.seeAllRegistrations), undefined, 'view-all');
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(
@@ -1014,7 +1014,6 @@ export const PAYLOAD_DEFAULT_VIEW = (input: PayloadViewInput, output: object, ta
1014
1014
  link.lineNumber,
1015
1015
  {
1016
1016
  columnNumber: link.columnNumber,
1017
- inlineFrameIndex: 0,
1018
1017
  showColumnNumber: true,
1019
1018
  },
1020
1019
  );
@@ -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
- this.registerRequiredCSS(consoleViewStyles, objectValueStyles, CodeHighlighter.codeHighlighterStyles);
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
- messageElement = this.tryFormatAsError((parameter.value as string));
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
- runtimeModel.target(), scriptId, url || Platform.DevToolsPath.EmptyUrlString, line,
581
- {columnNumber: column, inlineFrameIndex: 0, userMetric});
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
- runtimeModel.target(), /* scriptId */ null, url, line,
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
- void Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance()
699
- .createStackTraceFromProtocolRuntime(stackTrace, stackTraceTarget)
700
- .then(stackTrace => {
701
- stackTracePreview.stackTrace = stackTrace;
702
- return stackTracePreview.updateComplete;
703
- })
704
- .then(() => {
705
- const selectableLinks =
706
- stackTracePreview.linkElements.map(element => ({element, forceSelect: () => element.focus()}));
707
- this.selectableChildren.splice(selectableChildIndex, 0, ...selectableLinks);
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.formatParameterAsError(output);
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 formatParameterAsError(output: SDK.RemoteObject.RemoteObject): HTMLElement {
984
- const result = document.createElement('span');
985
-
986
- // Combine the ExceptionDetails for this error object with the parsed Error#stack.
987
- // The Exceptiondetails include script IDs for stack frames, which allows more accurate
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
- if (cause?.subtype === 'error') {
1008
- await formatErrorStack(cause, /* includeCausedByPrefix */ true);
1009
- } else if (cause?.type === 'string') {
1010
- const stringCauseElement = document.createElement('div');
1011
- stringCauseElement.append(`Caused by: ${cause.value}`);
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 = formatErrorStack(output, /* includeCausedByPrefix */ false);
1017
-
1018
- return result;
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):