chrome-devtools-frontend 1.0.1650035 → 1.0.1650232

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 (43) hide show
  1. package/front_end/core/host/UserMetrics.ts +0 -15
  2. package/front_end/core/sdk/CSSMetadata.ts +91 -1
  3. package/front_end/generated/SupportedCSSProperties.js +342 -114
  4. package/front_end/models/ai_assistance/AiConversation.ts +2 -1
  5. package/front_end/models/ai_assistance/agents/AccessibilityAgent.ts +10 -57
  6. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +1 -1
  7. package/front_end/models/ai_assistance/agents/ExecuteJavascript.ts +13 -4
  8. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +1 -7
  9. package/front_end/models/ai_assistance/ai_assistance.ts +2 -0
  10. package/front_end/models/ai_assistance/contexts/AccessibilityContext.snapshot.txt +26 -0
  11. package/front_end/models/ai_assistance/contexts/AccessibilityContext.ts +63 -0
  12. package/front_end/models/bindings/DebuggerWorkspaceBinding.ts +3 -2
  13. package/front_end/models/stack_trace/DetailedErrorStackParser.ts +18 -0
  14. package/front_end/models/stack_trace/stack_trace.ts +0 -4
  15. package/front_end/panels/accessibility/ARIAAttributesView.ts +1 -0
  16. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +5 -5
  17. package/front_end/panels/ai_assistance/components/ChatInput.ts +1 -1
  18. package/front_end/panels/network/RequestConditionsDrawer.ts +236 -198
  19. package/front_end/panels/network/requestConditionsDrawer.css +3 -0
  20. package/front_end/panels/whats_new/ReleaseNoteText.ts +12 -6
  21. package/front_end/panels/whats_new/resources/WNDT.md +8 -7
  22. package/front_end/third_party/third-party-web/lib/nostats-subset.js +21 -15
  23. package/front_end/third_party/third-party-web/package/README.md +619 -582
  24. package/front_end/third_party/third-party-web/package/dist/entities-httparchive-nostats.json +1 -1
  25. package/front_end/third_party/third-party-web/package/dist/entities-httparchive.json +1 -1
  26. package/front_end/third_party/third-party-web/package/dist/entities-nostats.json +1 -1
  27. package/front_end/third_party/third-party-web/package/dist/entities.json +1 -1
  28. package/front_end/third_party/third-party-web/package/lib/create-entity-finder-api.js +27 -15
  29. package/front_end/third_party/third-party-web/package/lib/create-entity-finder-api.test.js +14 -0
  30. package/front_end/third_party/third-party-web/package/lib/entities.test.js +10 -0
  31. package/front_end/third_party/third-party-web/package/lib/index.test.js +6 -6
  32. package/front_end/third_party/third-party-web/package/lib/markdown/template.md +1 -3
  33. package/front_end/third_party/third-party-web/package/package.json +7 -3
  34. package/front_end/third_party/third-party-web/package.json +1 -1
  35. package/front_end/ui/components/buttons/floatingButton.css +3 -3
  36. package/front_end/ui/components/lists/list.css +2 -0
  37. package/front_end/ui/legacy/ListWidget.ts +8 -5
  38. package/front_end/ui/legacy/TextPrompt.ts +5 -2
  39. package/front_end/ui/legacy/components/utils/Linkifier.ts +12 -6
  40. package/front_end/ui/legacy/textPrompt.css +1 -0
  41. package/front_end/ui/visual_logging/KnownContextValues.ts +1 -0
  42. package/package.json +1 -1
  43. package/front_end/models/stack_trace/ErrorStackParser.ts +0 -174
@@ -1,22 +1,35 @@
1
1
  const DOMAIN_IN_URL_REGEX = /:\/\/(\S*?)(:\d+)?(\/|$)/
2
- const DOMAIN_CHARACTERS = /([a-z0-9.-]+\.[a-z0-9]+|localhost)/i
2
+ const DOMAIN_CHARACTERS = /(?:[a-z0-9.-]+\.[a-z0-9]+|localhost)/i
3
3
  const IP_REGEX = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/
4
4
  const ROOT_DOMAIN_REGEX = /[^.]+\.([^.]+|(gov|com|co|ne)\.\w{2})$/i
5
5
 
6
- function getDomainFromOriginOrURL(originOrURL) {
7
- if (typeof originOrURL !== 'string') return null
8
- if (originOrURL.length > 10000 || originOrURL.startsWith('data:')) return null
9
- if (DOMAIN_IN_URL_REGEX.test(originOrURL)) return originOrURL.match(DOMAIN_IN_URL_REGEX)[1]
10
- if (DOMAIN_CHARACTERS.test(originOrURL)) return originOrURL.match(DOMAIN_CHARACTERS)[0]
11
- return null
6
+ /**
7
+ * @param {string} originOrURL
8
+ * @return {[string|null, string|null]} - The first item is the root domain, the second item is the domain.
9
+ */
10
+ function parseDomains(originOrURL) {
11
+ if (typeof originOrURL !== 'string') return [null, null]
12
+ if (originOrURL.length > 10000 || originOrURL.startsWith('data:')) return [null, null]
13
+ let m = originOrURL.match(DOMAIN_IN_URL_REGEX)
14
+ let domain;
15
+ if (m) {
16
+ domain = m[1]
17
+ }
18
+ m = originOrURL.match(DOMAIN_CHARACTERS)
19
+ if (m) {
20
+ domain = m[0]
21
+ }
22
+
23
+ if (!domain) return [null, null]
24
+ if (IP_REGEX.test(domain)) return [domain, domain]
25
+ m = domain.match(ROOT_DOMAIN_REGEX)
26
+ const rootDomain = m && m[0] || domain;
27
+
28
+ return [rootDomain, domain]
12
29
  }
13
30
 
14
- function getRootDomain(originOrURL) {
15
- const domain = getDomainFromOriginOrURL(originOrURL)
16
- if (!domain) return null
17
- if (IP_REGEX.test(domain)) return domain
18
- const match = domain.match(ROOT_DOMAIN_REGEX)
19
- return (match && match[0]) || domain
31
+ function getRootDomain(originOrURL,) {
32
+ return parseDomains(originOrURL)[0];
20
33
  }
21
34
 
22
35
  function sliceSubdomainFromDomain(domain, rootDomain) {
@@ -28,8 +41,7 @@ function sliceSubdomainFromDomain(domain, rootDomain) {
28
41
  }
29
42
 
30
43
  function getEntityInDataset(entityByDomain, entityBySubDomain, entityByRootDomain, originOrURL) {
31
- const domain = getDomainFromOriginOrURL(originOrURL)
32
- const rootDomain = getRootDomain(domain)
44
+ const [rootDomain, domain] = parseDomains(originOrURL);
33
45
  if (!domain || !rootDomain) return undefined
34
46
  if (entityByDomain.has(domain)) return entityByDomain.get(domain)
35
47
 
@@ -1,3 +1,5 @@
1
+ const fs = require('fs')
2
+ const path = require('path')
1
3
  const {createAPIFromDataset} = require('./create-entity-finder-api.js')
2
4
 
3
5
  describe('getEntity', () => {
@@ -41,4 +43,16 @@ describe('getEntity', () => {
41
43
  expect(api.getEntity('https://bar.example.co.uk/path').name).toEqual('Domain')
42
44
  expect(api.getEntity('https://baz.bar.example.co.uk/path').name).toEqual('Domain')
43
45
  })
46
+
47
+ it.skip('stress test', () => {
48
+ const urls = fs
49
+ .readFileSync(path.join(__dirname, '../data/random-urls.txt'), 'utf8')
50
+ .split('\n')
51
+ .filter(Boolean)
52
+ console.time('getEntity')
53
+ for (let i = 0; i < 1_000_000; i++) {
54
+ api.getEntity(urls[i % urls.length])
55
+ }
56
+ console.timeEnd('getEntity')
57
+ })
44
58
  })
@@ -24,4 +24,14 @@ describe('Entities', () => {
24
24
  }
25
25
  }
26
26
  })
27
+
28
+ it('should not have commas within a domain', () => {
29
+ for (const entity of entities) {
30
+ for (const domain of entity.domains) {
31
+ // A domain can be `*.maxymiser.net` or `maxymiser.hs.llnwd.net`
32
+ // A domain can't be `*.maxymiser.net, maxymiser.hs.llnwd.net`
33
+ expect(domain).toEqual(expect.not.stringContaining(','))
34
+ }
35
+ }
36
+ })
27
37
  })
@@ -59,7 +59,7 @@ describe('getEntity', () => {
59
59
  it('works for direct domain usage', () => {
60
60
  expect(getEntity('https://js.connect.facebook.net/lib.js')).toMatchInlineSnapshot(`
61
61
  Object {
62
- "averageExecutionTime": 347.4278160199557,
62
+ "averageExecutionTime": 513.7506757503379,
63
63
  "categories": Array [
64
64
  "social",
65
65
  ],
@@ -103,8 +103,8 @@ describe('getEntity', () => {
103
103
  ],
104
104
  },
105
105
  ],
106
- "totalExecutionTime": 1097107210,
107
- "totalOccurrences": 3157799,
106
+ "totalExecutionTime": 2901178836,
107
+ "totalOccurrences": 5647056,
108
108
  }
109
109
  `)
110
110
  })
@@ -112,7 +112,7 @@ describe('getEntity', () => {
112
112
  it('works for inferred domain usage', () => {
113
113
  expect(getEntity('https://unknown.typekit.net/fonts.css')).toMatchInlineSnapshot(`
114
114
  Object {
115
- "averageExecutionTime": 660.2645605704683,
115
+ "averageExecutionTime": 691.2697157512289,
116
116
  "categories": Array [
117
117
  "cdn",
118
118
  ],
@@ -129,8 +129,8 @@ describe('getEntity', () => {
129
129
  "homepage": "https://fonts.adobe.com/",
130
130
  "name": "Adobe TypeKit",
131
131
  "products": Array [],
132
- "totalExecutionTime": 78981507,
133
- "totalOccurrences": 119621,
132
+ "totalExecutionTime": 142315844,
133
+ "totalOccurrences": 205876,
134
134
  }
135
135
  `)
136
136
  })
@@ -1,6 +1,4 @@
1
- # [Third Party Web](https://www.thirdpartyweb.today/)
2
-
3
- ## Check out the shiny new web UI https://www.thirdpartyweb.today/
1
+ # Third Party Web
4
2
 
5
3
  Data on third party entities and their impact on the web.
6
4
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "third-party-web",
3
- "version": "0.26.2",
3
+ "version": "0.29.2",
4
4
  "description": "Categorized data on third party entities on the web.",
5
5
  "main": "./lib/index.js",
6
6
  "scripts": {
@@ -23,6 +23,10 @@
23
23
  "url": "https://github.com/patrickhulce/third-party-web.git"
24
24
  },
25
25
  "license": "MIT",
26
+ "publishConfig": {
27
+ "registry": "https://registry.npmjs.org/",
28
+ "provenance": true
29
+ },
26
30
  "devDependencies": {
27
31
  "@google-cloud/bigquery": "^7.9.0",
28
32
  "chart.js": "^2.9.4",
@@ -33,7 +37,7 @@
33
37
  "lodash": "^4.17.15",
34
38
  "prettier": "^1.18.2",
35
39
  "prompts": "^2.4.2",
36
- "semantic-release": "^24.2.0"
40
+ "semantic-release": "^25.0.3"
37
41
  },
38
42
  "config": {
39
43
  "exportAliases": {
@@ -42,5 +46,5 @@
42
46
  "httparchive-subset": "./lib/subsets/httparchive.js"
43
47
  }
44
48
  },
45
- "packageManager": "yarn@4.1.1"
49
+ "packageManager": "yarn@4.10.3"
46
50
  }
@@ -3,6 +3,6 @@
3
3
  "description": "Package list used when building/upgrading third-party-web",
4
4
  "private": true,
5
5
  "dependencies": {
6
- "third-party-web": "0.26.2"
6
+ "third-party-web": "0.29.2"
7
7
  }
8
8
  }
@@ -27,7 +27,7 @@ button {
27
27
  background-color: var(--sys-color-tonal-container);
28
28
  }
29
29
 
30
- :host-context(.theme-with-dark-background) & {
30
+ :host-context(.theme-with-dark-background) &:not(:disabled) {
31
31
  background-color: var(--sys-color-primary);
32
32
  }
33
33
 
@@ -36,11 +36,11 @@ button {
36
36
  height: var(--sys-size-7);
37
37
  }
38
38
 
39
- &:not(:disabled):host-context(:not(.theme-with-dark-background)) & > devtools-icon {
39
+ :host-context(:not(.theme-with-dark-background)) &:not(:disabled) > devtools-icon {
40
40
  color: var(--sys-color-on-tonal-container);
41
41
  }
42
42
 
43
- &:not(:disabled):host-context(.theme-with-dark-background) & > devtools-icon {
43
+ :host-context(.theme-with-dark-background) &:not(:disabled) > devtools-icon {
44
44
  color: var(--sys-color-on-primary);
45
45
  }
46
46
 
@@ -76,6 +76,8 @@ li {
76
76
  pointer-events: none;
77
77
  position: absolute;
78
78
  right: 0;
79
+ top: 0;
80
+ bottom: 0;
79
81
  }
80
82
 
81
83
  .controls-gradient {
@@ -9,7 +9,7 @@ import './Toolbar.js';
9
9
  import * as i18n from '../../core/i18n/i18n.js';
10
10
  import * as Platform from '../../core/platform/platform.js';
11
11
  import * as Buttons from '../../ui/components/buttons/buttons.js';
12
- import {html, render} from '../lit/lit.js';
12
+ import {html, nothing, render} from '../lit/lit.js';
13
13
  import * as VisualLogging from '../visual_logging/visual_logging.js';
14
14
 
15
15
  import * as ARIAUtils from './ARIAUtils.js';
@@ -107,6 +107,7 @@ export class ListWidget<T> extends VBox {
107
107
  updateItem(index: number, newItem: T, editable: boolean, focusable = true, controlLabels: {
108
108
  edit?: string,
109
109
  delete?: string,
110
+ hideEdit?: boolean,
110
111
  } = {}): void {
111
112
  if (index < 0 || index >= this.#items.length) {
112
113
  this.appendItem(newItem, editable, focusable, controlLabels);
@@ -130,7 +131,8 @@ export class ListWidget<T> extends VBox {
130
131
  }
131
132
  }
132
133
 
133
- appendItem(item: T, editable: boolean, focusable = true, controlLabels: {edit?: string, delete?: string} = {}): void {
134
+ appendItem(item: T, editable: boolean, focusable = true,
135
+ controlLabels: {edit?: string, delete?: string, hideEdit?: boolean} = {}): void {
134
136
  if (this.lastSeparator && this.#items.length) {
135
137
  const element = document.createElement('div');
136
138
  element.classList.add('list-separator');
@@ -204,7 +206,8 @@ export class ListWidget<T> extends VBox {
204
206
  this.updatePlaceholder();
205
207
  }
206
208
 
207
- private createControls(item: T, element: HTMLElement, controlLabels: {edit?: string, delete?: string}): Element {
209
+ private createControls(item: T, element: HTMLElement,
210
+ controlLabels: {edit?: string, delete?: string, hideEdit?: boolean}): Element {
208
211
  const controls = document.createElement('div');
209
212
  controls.classList.add('controls-container');
210
213
  controls.classList.add('fill');
@@ -214,12 +217,12 @@ export class ListWidget<T> extends VBox {
214
217
  <div class="controls-gradient"></div>
215
218
  <div class="controls-buttons">
216
219
  <devtools-toolbar>
217
- <devtools-button class=toolbar-button
220
+ ${controlLabels?.hideEdit ? nothing : html`<devtools-button class=toolbar-button
218
221
  .iconName=${'edit'}
219
222
  .jslogContext=${'edit-item'}
220
223
  .title=${controlLabels?.edit ?? i18nString(UIStrings.editString)}
221
224
  .variant=${Buttons.Button.Variant.ICON}
222
- @click=${onEditClicked}></devtools-button>
225
+ @click=${onEditClicked}></devtools-button>`}
223
226
  <devtools-button class=toolbar-button
224
227
  .iconName=${'bin'}
225
228
  .jslogContext=${'remove-item'}
@@ -226,11 +226,12 @@ export class TextPromptElement extends HTMLElement {
226
226
  #done(e: Event, commit: boolean): void {
227
227
  const target = e.target as HTMLElement;
228
228
  const text = target.textContent || '';
229
- this.#internals.setValidity({});
230
229
  if (commit) {
231
230
  const validationMessage = this.#validator?.(text) ?? '';
232
231
  if (validationMessage) {
233
232
  this.#internals.setValidity({customError: true}, validationMessage, this.#entrypoint);
233
+ } else {
234
+ this.#internals.setValidity({});
234
235
  }
235
236
 
236
237
  if (!this.#internals.reportValidity()) {
@@ -239,6 +240,7 @@ export class TextPromptElement extends HTMLElement {
239
240
 
240
241
  this.dispatchEvent(new TextPromptElement.CommitEvent(text));
241
242
  } else {
243
+ this.#internals.setValidity({});
242
244
  this.dispatchEvent(new TextPromptElement.CancelEvent());
243
245
  }
244
246
  e.consume();
@@ -248,12 +250,13 @@ export class TextPromptElement extends HTMLElement {
248
250
  if (event.handled || !(event instanceof KeyboardEvent)) {
249
251
  return;
250
252
  }
251
- this.#internals.setValidity({});
252
253
 
253
254
  if (event.key === 'Enter') {
254
255
  this.#done(event, /* commit=*/ true);
255
256
  } else if (Platform.KeyboardUtilities.isEscKey(event)) {
256
257
  this.#done(event, /* commit=*/ false);
258
+ } else {
259
+ this.#internals.setValidity({});
257
260
  }
258
261
  }
259
262
 
@@ -1060,15 +1060,21 @@ export class LinkHandlerSettingUI {
1060
1060
 
1061
1061
  update(): void {
1062
1062
  this.element.removeChildren();
1063
- const names = [...linkHandlers.keys()];
1064
- names.unshift(i18nString(UIStrings.auto));
1065
- for (const name of names) {
1063
+ // Populate the dropdown with extension origins. The first option is the
1064
+ // special "Auto" value which is not a real origin.
1065
+ const origins = [...linkHandlers.keys()];
1066
+ origins.unshift(i18nString(UIStrings.auto));
1067
+ for (const origin of origins) {
1066
1068
  const option = document.createElement('option');
1067
- option.textContent = name;
1068
- option.selected = name === Linkifier.linkHandlerSetting().get();
1069
+ const registration = linkHandlers.get(origin);
1070
+ // If the origin has a registered handler, display its user-friendly title.
1071
+ // Otherwise, fallback to the origin string itself (e.g. for the "Auto" option).
1072
+ option.textContent = registration === undefined ? origin : registration.title;
1073
+ option.value = origin;
1074
+ option.selected = origin === Linkifier.linkHandlerSetting().get();
1069
1075
  this.element.appendChild(option);
1070
1076
  }
1071
- this.element.disabled = names.length <= 1;
1077
+ this.element.disabled = origins.length <= 1;
1072
1078
  }
1073
1079
 
1074
1080
  private onChange(event: Event): void {
@@ -21,6 +21,7 @@
21
21
  .text-prompt {
22
22
  cursor: text;
23
23
  overflow-x: visible;
24
+ min-width: var(--devtools-text-prompt-min-width, auto);
24
25
  }
25
26
 
26
27
  .text-prompt::-webkit-scrollbar {
@@ -4368,6 +4368,7 @@ export const knownContextValues = new Set([
4368
4368
  'ur',
4369
4369
  'url',
4370
4370
  'url-pattern',
4371
+ 'url-pattern-warning',
4371
4372
  'usage',
4372
4373
  'usb',
4373
4374
  'use-code-with-caution',
package/package.json CHANGED
@@ -92,5 +92,5 @@
92
92
  "webidl2": "24.5.0",
93
93
  "yargs": "17.7.2"
94
94
  },
95
- "version": "1.0.1650035"
95
+ "version": "1.0.1650232"
96
96
  }
@@ -1,174 +0,0 @@
1
- // Copyright 2022 The Chromium Authors
2
- // Use of this source code is governed by a BSD-style license that can be
3
- // found in the LICENSE file.
4
-
5
- import * as Common from '../../core/common/common.js';
6
- import type * as Platform from '../../core/platform/platform.js';
7
- import type * as SDK from '../../core/sdk/sdk.js';
8
- import type * as Protocol from '../../generated/protocol.js';
9
-
10
- export interface ParsedErrorFrame {
11
- line: string;
12
- isCallFrame?: boolean;
13
- link?: {
14
- url: Platform.DevToolsPath.UrlString,
15
- prefix: string,
16
- suffix: string,
17
- enclosedInBraces: boolean,
18
- lineNumber?: number,
19
- columnNumber?: number,
20
- scriptId?: Protocol.Runtime.ScriptId,
21
- };
22
- }
23
-
24
- /**
25
- * Combines the error description (essentially the `Error#stack` property value)
26
- * with the `issueSummary`.
27
- *
28
- * @param description the `description` property of the `Error` remote object.
29
- * @param issueSummary the optional `issueSummary` of the `exceptionMetaData`.
30
- * @returns the enriched description.
31
- * @see https://goo.gle/devtools-reduce-network-noise-design
32
- */
33
- export function concatErrorDescriptionAndIssueSummary(description: string, issueSummary: string): string {
34
- // Insert the issue summary right after the error message.
35
- const pos = description.indexOf('\n');
36
- const prefix = pos === -1 ? description : description.substring(0, pos);
37
- const suffix = pos === -1 ? '' : description.substring(pos);
38
- description = `${prefix}. ${issueSummary}${suffix}`;
39
- return description;
40
- }
41
-
42
- /**
43
- * Takes a V8 Error#stack string and extracts source position information.
44
- *
45
- * The result includes the url, line and column number, as well as where
46
- * the url is found in the raw line.
47
- *
48
- * @returns Null if the provided string has an unexpected format. A
49
- * populated `ParsedErrorFrame[]` otherwise.
50
- */
51
- export function parseSourcePositionsFromErrorStack(
52
- runtimeModel: SDK.RuntimeModel.RuntimeModel, stack: string): ParsedErrorFrame[]|null {
53
- if (!(/\n\s*at\s/.test(stack) || stack.startsWith('SyntaxError:'))) {
54
- return null;
55
- }
56
- const debuggerModel = runtimeModel.debuggerModel();
57
- const baseURL = runtimeModel.target().inspectedURL();
58
-
59
- const lines = stack.split('\n');
60
- const linkInfos = [];
61
- for (const line of lines) {
62
- const match = /^\s*at\s(async\s)?/.exec(line);
63
- if (!match) {
64
- if (linkInfos.length && linkInfos[linkInfos.length - 1].isCallFrame) {
65
- return null;
66
- }
67
- linkInfos.push({line});
68
- continue;
69
- }
70
-
71
- const isCallFrame = true;
72
- let left = match[0].length;
73
- let right = line.length;
74
- let enclosedInBraces = false;
75
- if (line[right - 1] === ')') {
76
- right--;
77
- enclosedInBraces = true;
78
- left = line.lastIndexOf(' (', right);
79
- if (left < 0) {
80
- return null;
81
- }
82
- left += 2;
83
- // Relevant in the `eval at ...` case.
84
- const newRight = line.indexOf('), ', left);
85
- if (newRight > left) {
86
- right = newRight;
87
- }
88
- }
89
-
90
- const linkCandidate = line.substring(left, right);
91
- const splitResult = Common.ParsedURL.ParsedURL.splitLineAndColumn(linkCandidate);
92
- if (splitResult.url === '<anonymous>') {
93
- if (linkInfos.length && linkInfos[linkInfos.length - 1].isCallFrame && !linkInfos[linkInfos.length - 1].link) {
94
- // Combine builtin frames.
95
- linkInfos[linkInfos.length - 1].line += `\n${line}`;
96
- } else {
97
- linkInfos.push({line, isCallFrame});
98
- }
99
- continue;
100
- }
101
- let url = parseOrScriptMatch(debuggerModel, splitResult.url);
102
- if (!url && Common.ParsedURL.ParsedURL.isRelativeURL(splitResult.url)) {
103
- url = parseOrScriptMatch(debuggerModel, Common.ParsedURL.ParsedURL.completeURL(baseURL, splitResult.url));
104
- }
105
- if (!url) {
106
- return null;
107
- }
108
-
109
- linkInfos.push({
110
- line,
111
- isCallFrame,
112
- link: {
113
- url,
114
- prefix: line.substring(0, left),
115
- suffix: line.substring(right),
116
- enclosedInBraces,
117
- lineNumber: splitResult.lineNumber,
118
- columnNumber: splitResult.columnNumber,
119
- },
120
- });
121
- }
122
- return linkInfos;
123
- }
124
-
125
- function parseOrScriptMatch(debuggerModel: SDK.DebuggerModel.DebuggerModel, url: Platform.DevToolsPath.UrlString|null):
126
- Platform.DevToolsPath.UrlString|null {
127
- if (!url) {
128
- return null;
129
- }
130
- if (Common.ParsedURL.ParsedURL.isValidUrlString(url)) {
131
- return url;
132
- }
133
- if (debuggerModel.scriptsForSourceURL(url).length) {
134
- return url;
135
- }
136
- // nodejs stack traces contain (absolute) file paths, but v8 reports them as file: urls.
137
- const fileUrl = new URL(url, 'file://');
138
- if (debuggerModel.scriptsForSourceURL(fileUrl.href).length) {
139
- return fileUrl.href as Platform.DevToolsPath.UrlString;
140
- }
141
- return null;
142
- }
143
-
144
- /**
145
- * Error#stack output only contains script URLs. In some cases we are able to
146
- * retrieve additional exception details from V8 that we can use to augment
147
- * the parsed Error#stack with script IDs.
148
- * This function sets the `scriptId` field in `ParsedErrorFrame` when it finds
149
- * the corresponding info in `Protocol.Runtime.StackTrace`.
150
- */
151
- export function augmentErrorStackWithScriptIds(
152
- parsedFrames: ParsedErrorFrame[], protocolStackTrace: Protocol.Runtime.StackTrace): void {
153
- // Note that the number of frames between the two stack traces can differ. The
154
- // parsed Error#stack can contain Builtin frames which are not present in the protocol
155
- // stack. This means its easier to always search the whole protocol stack for a matching
156
- // frame rather then trying to detect the Builtin frames and skipping them.
157
- for (const parsedFrame of parsedFrames) {
158
- const protocolFrame = protocolStackTrace.callFrames.find(frame => framesMatch(parsedFrame, frame));
159
- if (protocolFrame && parsedFrame.link) {
160
- parsedFrame.link.scriptId = protocolFrame.scriptId;
161
- }
162
- }
163
- }
164
-
165
- /** Returns true iff both stack frames have the same url and line/column numbers. The function name is ignored */
166
- function framesMatch(parsedFrame: ParsedErrorFrame, protocolFrame: Protocol.Runtime.CallFrame): boolean {
167
- if (!parsedFrame.link) {
168
- return false;
169
- }
170
-
171
- const {url, lineNumber, columnNumber} = parsedFrame.link;
172
- return url === protocolFrame.url && lineNumber === protocolFrame.lineNumber &&
173
- columnNumber === protocolFrame.columnNumber;
174
- }