chrome-devtools-frontend 1.0.1010977 → 1.0.1012379

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.
@@ -0,0 +1,4 @@
1
+ # This file is used by git-cl to get repository specific information.
2
+ GERRIT_HOST: True
3
+ CODE_REVIEW_SERVER: codereview.chromium.org
4
+ PROJECT: devtools-frontend
@@ -544,6 +544,7 @@ grd_files_debug_sources = [
544
544
  "front_end/core/common/JavaScriptMetaData.js",
545
545
  "front_end/core/common/Lazy.js",
546
546
  "front_end/core/common/Linkifier.js",
547
+ "front_end/core/common/Mutex.js",
547
548
  "front_end/core/common/Object.js",
548
549
  "front_end/core/common/ParsedURL.js",
549
550
  "front_end/core/common/Progress.js",
package/docs/workflows.md CHANGED
@@ -59,7 +59,7 @@ This works with Chromium 79 or later.
59
59
  **(Requires `brew install coreutils` on Mac.)**
60
60
 
61
61
  ```bash
62
- <path-to-chrome>/chrome --custom-devtools-frontend=file://$(realpath out/Default/gen/front_end)
62
+ <path-to-devtools-frontend>/third_party/chrome/chrome-<platform>/chrome --custom-devtools-frontend=file://$(realpath out/Default/gen/front_end)
63
63
  ```
64
64
 
65
65
  Note that `$(realpath out/Default/gen/front_end)` expands to the absolute path to build artifacts for DevTools frontend.
@@ -79,7 +79,7 @@ Serve the content of `out/Default/gen/front_end` on a web server, e.g. via `pyth
79
79
  Then point to that web server when starting Chromium, for example:
80
80
 
81
81
  ```bash
82
- <path-to-chrome>/chrome --custom-devtools-frontend=http://localhost:8000/
82
+ <path-to-devtools-frontend>/third_party/chrome/chrome-<platform>/chrome --custom-devtools-frontend=http://localhost:8000/
83
83
  ```
84
84
 
85
85
  Open DevTools via F12 on Windows/Linux or Cmd+Option+I on Mac.
@@ -91,7 +91,7 @@ Serve the content of `out/Default/gen/front_end` on a web server, e.g. via `pyth
91
91
  Then point to that web server when starting Chromium, for example:
92
92
 
93
93
  ```bash
94
- <path-to-chrome>/chrome --custom-devtools-frontend=http://localhost:8000/ --remote-debugging-port=9222
94
+ <path-to-devtools-frontend>/third_party/chrome/chrome-<platform>/chrome --custom-devtools-frontend=http://localhost:8000/ --remote-debugging-port=9222
95
95
  ```
96
96
 
97
97
  In a regular Chrome tab, go to the URL `http://localhost:9222#custom=true`. It lists URLs that can be copied to new Chrome tabs to inspect individual debug targets.
@@ -0,0 +1,42 @@
1
+ // Copyright 2022 The Chromium Authors. All rights reserved.
2
+ // Use of this source code is governed by a BSD-style license that can be
3
+ // found in the LICENSE file.
4
+
5
+ type ReleaseFn = () => void;
6
+
7
+ /**
8
+ * Use Mutex class to coordinate local concurrent operations.
9
+ * Once `acquire` promise resolves, you hold the lock and must
10
+ * call `release` function returned by `acquire` to release the
11
+ * lock. Failing to `release` the lock may lead to deadlocks.
12
+ */
13
+ export class Mutex {
14
+ #locked = false;
15
+ #acquiringQueue: Array<(release: ReleaseFn) => void> = [];
16
+
17
+ acquire(): Promise<ReleaseFn> {
18
+ let resolver = (_release: ReleaseFn): void => {};
19
+ const promise = new Promise<ReleaseFn>((resolve): void => {
20
+ resolver = resolve;
21
+ });
22
+ this.#acquiringQueue.push(resolver);
23
+ this.#processAcquiringQueue();
24
+ return promise;
25
+ }
26
+
27
+ #processAcquiringQueue(): void {
28
+ if (this.#locked) {
29
+ return;
30
+ }
31
+ const nextAquirePromise = this.#acquiringQueue.shift();
32
+ if (nextAquirePromise) {
33
+ this.#locked = true;
34
+ nextAquirePromise(this.#release.bind(this));
35
+ }
36
+ }
37
+
38
+ #release(): void {
39
+ this.#locked = false;
40
+ this.#processAcquiringQueue();
41
+ }
42
+ }
@@ -14,6 +14,7 @@ import * as EventTarget from './EventTarget.js';
14
14
  import * as JavaScriptMetaData from './JavaScriptMetaData.js';
15
15
  import * as Lazy from './Lazy.js';
16
16
  import * as Linkifier from './Linkifier.js';
17
+ import * as Mutex from './Mutex.js';
17
18
  import * as ObjectWrapper from './Object.js';
18
19
  import * as ParsedURL from './ParsedURL.js';
19
20
  import * as Progress from './Progress.js';
@@ -58,6 +59,7 @@ export {
58
59
  JavaScriptMetaData,
59
60
  Lazy,
60
61
  Linkifier,
62
+ Mutex,
61
63
  ObjectWrapper,
62
64
  ParsedURL,
63
65
  Progress,
@@ -626,8 +626,9 @@ export enum DevtoolsExperiments {
626
626
  'cssLayers' = 59,
627
627
  'eyedropperColorPicker' = 60,
628
628
  'instrumentationBreakpoints' = 61,
629
+ 'cssAuthoringHints' = 62,
629
630
  // Increment this when new experiments are added.
630
- 'MaxValue' = 62,
631
+ 'MaxValue' = 63,
631
632
  }
632
633
  /* eslint-enable @typescript-eslint/naming-convention */
633
634
 
@@ -291,6 +291,7 @@ export enum ExperimentName {
291
291
  CSS_LAYERS = 'cssLayers',
292
292
  EYEDROPPER_COLOR_PICKER = 'eyedropperColorPicker',
293
293
  INSTRUMENTATION_BREAKPOINTS = 'instrumentationBreakpoints',
294
+ CSS_AUTHORING_HINTS = 'cssAuthoringHints',
294
295
  }
295
296
 
296
297
  // TODO(crbug.com/1167717): Make this a const enum again
@@ -315,7 +315,7 @@ export class CSSMatchedStyles {
315
315
  for (let i = 0; i < pseudoPayload.length; ++i) {
316
316
  const entryPayload = pseudoPayload[i];
317
317
  // PseudoElement nodes are not created unless "content" css property is set.
318
- const pseudoElement = this.#nodeInternal.pseudoElements().get(entryPayload.pseudoType) || null;
318
+ const pseudoElement = this.#nodeInternal.pseudoElements().get(entryPayload.pseudoType)?.at(-1) || null;
319
319
  const pseudoStyles = [];
320
320
  const rules = entryPayload.matches || [];
321
321
 
@@ -517,6 +517,10 @@ export class CSSModel extends SDKModel<EventTypes> {
517
517
  return [...this.#fontFaces.values()];
518
518
  }
519
519
 
520
+ fontFaceForSource(src: string): CSSFontFace|undefined {
521
+ return this.#fontFaces.get(src);
522
+ }
523
+
520
524
  styleSheetHeaderForId(id: Protocol.CSS.StyleSheetId): CSSStyleSheetHeader|null {
521
525
  return this.#styleSheetIdToHeader.get(id) || null;
522
526
  }
@@ -69,7 +69,7 @@ export class DOMNode {
69
69
  #xmlVersion!: string|undefined;
70
70
  #isSVGNodeInternal!: boolean;
71
71
  #creationStackTraceInternal: Promise<Protocol.Runtime.StackTrace|null>|null;
72
- pseudoElementsInternal: Map<string, DOMNode>;
72
+ #pseudoElements: Map<string, DOMNode[]>;
73
73
  #distributedNodesInternal: DOMNodeShortcut[];
74
74
  assignedSlot: DOMNodeShortcut|null;
75
75
  readonly shadowRootsInternal: DOMNode[];
@@ -100,7 +100,7 @@ export class DOMNode {
100
100
  this.#agent = this.#domModelInternal.getAgent();
101
101
  this.index = undefined;
102
102
  this.#creationStackTraceInternal = null;
103
- this.pseudoElementsInternal = new Map();
103
+ this.#pseudoElements = new Map();
104
104
  this.#distributedNodesInternal = [];
105
105
  this.assignedSlot = null;
106
106
  this.shadowRootsInternal = [];
@@ -319,32 +319,33 @@ export class DOMNode {
319
319
  }
320
320
 
321
321
  hasPseudoElements(): boolean {
322
- return this.pseudoElementsInternal.size > 0;
322
+ return this.#pseudoElements.size > 0;
323
323
  }
324
324
 
325
- pseudoElements(): Map<string, DOMNode> {
326
- return this.pseudoElementsInternal;
325
+ pseudoElements(): Map<string, DOMNode[]> {
326
+ return this.#pseudoElements;
327
327
  }
328
328
 
329
- beforePseudoElement(): DOMNode|null {
330
- if (!this.pseudoElementsInternal) {
331
- return null;
332
- }
333
- return this.pseudoElementsInternal.get(DOMNode.PseudoElementNames.Before) || null;
329
+ beforePseudoElement(): DOMNode|undefined {
330
+ return this.#pseudoElements.get(DOMNode.PseudoElementNames.Before)?.at(-1);
334
331
  }
335
332
 
336
- afterPseudoElement(): DOMNode|null {
337
- if (!this.pseudoElementsInternal) {
338
- return null;
339
- }
340
- return this.pseudoElementsInternal.get(DOMNode.PseudoElementNames.After) || null;
333
+ afterPseudoElement(): DOMNode|undefined {
334
+ return this.#pseudoElements.get(DOMNode.PseudoElementNames.After)?.at(-1);
341
335
  }
342
336
 
343
- markerPseudoElement(): DOMNode|null {
344
- if (!this.pseudoElementsInternal) {
345
- return null;
346
- }
347
- return this.pseudoElementsInternal.get(DOMNode.PseudoElementNames.Marker) || null;
337
+ markerPseudoElement(): DOMNode|undefined {
338
+ return this.#pseudoElements.get(DOMNode.PseudoElementNames.Marker)?.at(-1);
339
+ }
340
+
341
+ pageTransitionPseudoElements(): DOMNode[] {
342
+ return [
343
+ ...this.#pseudoElements.get(DOMNode.PseudoElementNames.PageTransition) || [],
344
+ ...this.#pseudoElements.get(DOMNode.PseudoElementNames.PageTransitionContainer) || [],
345
+ ...this.#pseudoElements.get(DOMNode.PseudoElementNames.PageTransitionImageWrapper) || [],
346
+ ...this.#pseudoElements.get(DOMNode.PseudoElementNames.PageTransitionOutgoingImage) || [],
347
+ ...this.#pseudoElements.get(DOMNode.PseudoElementNames.PageTransitionIncomingImage) || [],
348
+ ];
348
349
  }
349
350
 
350
351
  hasAssignedSlot(): boolean {
@@ -632,7 +633,12 @@ export class DOMNode {
632
633
  removeChild(node: DOMNode): void {
633
634
  const pseudoType = node.pseudoType();
634
635
  if (pseudoType) {
635
- this.pseudoElementsInternal.delete(pseudoType);
636
+ const updatedPseudoElements = this.#pseudoElements.get(pseudoType)?.filter(element => element !== node);
637
+ if (updatedPseudoElements && updatedPseudoElements.length > 0) {
638
+ this.#pseudoElements.set(pseudoType, updatedPseudoElements);
639
+ } else {
640
+ this.#pseudoElements.delete(pseudoType);
641
+ }
636
642
  } else {
637
643
  const shadowRootIndex = this.shadowRootsInternal.indexOf(node);
638
644
  if (shadowRootIndex !== -1) {
@@ -678,7 +684,12 @@ export class DOMNode {
678
684
  if (!pseudoType) {
679
685
  throw new Error('DOMNode.pseudoType() is expected to be defined.');
680
686
  }
681
- this.pseudoElementsInternal.set(pseudoType, node);
687
+ const currentPseudoElements = this.#pseudoElements.get(pseudoType);
688
+ if (currentPseudoElements) {
689
+ currentPseudoElements.push(node);
690
+ } else {
691
+ this.#pseudoElements.set(pseudoType, [node]);
692
+ }
682
693
  }
683
694
  }
684
695
 
@@ -960,6 +971,11 @@ export namespace DOMNode {
960
971
  Before = 'before',
961
972
  After = 'after',
962
973
  Marker = 'marker',
974
+ PageTransition = 'page-transition',
975
+ PageTransitionContainer = 'page-transition-container',
976
+ PageTransitionImageWrapper = 'page-transition-image-wrapper',
977
+ PageTransitionOutgoingImage = 'page-transition-outgoing-image',
978
+ PageTransitionIncomingImage = 'page-transition-incoming-image',
963
979
  }
964
980
 
965
981
  // TODO(crbug.com/1167717): Make this a const enum again
@@ -1375,11 +1391,15 @@ export class DOMModel extends SDKModel<EventTypes> {
1375
1391
  if (!pseudoType) {
1376
1392
  throw new Error('DOMModel._pseudoElementAdded expects pseudoType to be defined.');
1377
1393
  }
1378
- const previousPseudoType = parent.pseudoElements().get(pseudoType);
1379
- if (previousPseudoType) {
1380
- throw new Error('DOMModel._pseudoElementAdded expects parent to not already have this pseudo type added.');
1394
+ const currentPseudoElements = parent.pseudoElements().get(pseudoType);
1395
+ if (currentPseudoElements) {
1396
+ Platform.DCHECK(
1397
+ () => pseudoType.startsWith('page-transition'),
1398
+ 'DOMModel.pseudoElementAdded expects parent to not already have this pseudo type added; only page-transition* pseudo elements can coexist under the same parent.');
1399
+ currentPseudoElements.push(node);
1400
+ } else {
1401
+ parent.pseudoElements().set(pseudoType, [node]);
1381
1402
  }
1382
- parent.pseudoElements().set(pseudoType, node);
1383
1403
  this.dispatchEventToListeners(Events.NodeInserted, node);
1384
1404
  this.scheduleMutationEvent(node);
1385
1405
  }
@@ -1420,7 +1440,9 @@ export class DOMModel extends SDKModel<EventTypes> {
1420
1440
  }
1421
1441
  const pseudoElements = node.pseudoElements();
1422
1442
  for (const value of pseudoElements.values()) {
1423
- this.unbind(value);
1443
+ for (const pseudoElement of value) {
1444
+ this.unbind(pseudoElement);
1445
+ }
1424
1446
  }
1425
1447
  const templateContent = node.templateContent();
1426
1448
  if (templateContent) {
@@ -386,6 +386,11 @@ export class MainImpl {
386
386
  Root.Runtime.experiments.register(
387
387
  Root.Runtime.ExperimentName.HEADER_OVERRIDES, 'Local overrides for response headers');
388
388
 
389
+ // Enable CSS Authoring hints for inactive rules, deprecated properties, etc.
390
+ Root.Runtime.experiments.register(
391
+ Root.Runtime.ExperimentName.CSS_AUTHORING_HINTS,
392
+ 'Enable CSS Authoring hints for inactive rules, deprecated properties, etc.');
393
+
389
394
  // New Lighthouse panel with timespan and snapshot mode
390
395
  Root.Runtime.experiments.register('lighthousePanelFR', 'Use Lighthouse panel with timespan and snapshot modes');
391
396
 
@@ -55,7 +55,6 @@ export function dissambleWASM(
55
55
  const lines = [];
56
56
  const offsets = [];
57
57
  const functionBodyOffsets = [];
58
- const MAX_LINES = 1000 * 1000;
59
58
  let chunkSize: number = 128 * 1024;
60
59
  let buffer: Uint8Array = new Uint8Array(chunkSize);
61
60
  let pendingSize = 0;
@@ -86,6 +85,7 @@ export function dissambleWASM(
86
85
  end: number,
87
86
  }>,
88
87
  });
88
+
89
89
  for (const line of result.lines) {
90
90
  lines.push(line);
91
91
  }
@@ -96,14 +96,6 @@ export function dissambleWASM(
96
96
  functionBodyOffsets.push(functionBodyOffset);
97
97
  }
98
98
 
99
- if (lines.length > MAX_LINES) {
100
- lines[MAX_LINES] = ';; .... text is truncated due to size';
101
- lines.splice(MAX_LINES + 1);
102
- if (offsets) {
103
- offsets.splice(MAX_LINES + 1);
104
- }
105
- break;
106
- }
107
99
  if (finished) {
108
100
  break;
109
101
  }
@@ -124,13 +116,9 @@ export function dissambleWASM(
124
116
  postMessage({event: 'progress', params: {percentage}});
125
117
  }
126
118
 
127
- postMessage({event: 'progress', params: {percentage: 99}});
128
-
129
- const source = lines.join('\n');
130
-
131
119
  postMessage({event: 'progress', params: {percentage: 100}});
132
120
 
133
- postMessage({method: 'disassemble', result: {source, offsets, functionBodyOffsets}});
121
+ postMessage({method: 'disassemble', result: {lines, offsets, functionBodyOffsets}});
134
122
  } catch (error) {
135
123
  postMessage({method: 'disassemble', error});
136
124
  }
@@ -2351,6 +2351,10 @@ export namespace CSS {
2351
2351
  * The font-stretch.
2352
2352
  */
2353
2353
  fontStretch: string;
2354
+ /**
2355
+ * The font-display.
2356
+ */
2357
+ fontDisplay: string;
2354
2358
  /**
2355
2359
  * The unicode-range.
2356
2360
  */
@@ -64,7 +64,7 @@ ElementsTestRunner.findNode = async function(matchFunction, callback) {
64
64
  }
65
65
 
66
66
  const pseudoElementsMap = node.pseudoElements();
67
- const pseudoElements = pseudoElementsMap ? [...pseudoElementsMap.values()] : [];
67
+ const pseudoElements = pseudoElementsMap ? [...pseudoElementsMap.values()].flat() : [];
68
68
  const children = (node.children() || []).concat(node.shadowRoots()).concat(pseudoElements);
69
69
  if (node.templateContent()) {
70
70
  children.push(node.templateContent());
@@ -133,7 +133,7 @@ export class BreakpointManager extends Common.ObjectWrapper.ObjectWrapper<EventT
133
133
  }
134
134
 
135
135
  const debuggerModel = script.debuggerModel;
136
- const uiSourceCode = await this.getUpdatedUISourceCode(script);
136
+ const uiSourceCode = await this.getUISourceCodeWithUpdatedBreakpointInfo(script);
137
137
  if (this.#hasBreakpointsForUrl(script.sourceURL)) {
138
138
  await this.#restoreBreakpointsForUrl(uiSourceCode);
139
139
  }
@@ -165,7 +165,8 @@ export class BreakpointManager extends Common.ObjectWrapper.ObjectWrapper<EventT
165
165
  }
166
166
  }
167
167
 
168
- async getUpdatedUISourceCode(script: SDK.Script.Script): Promise<Workspace.UISourceCode.UISourceCode> {
168
+ async getUISourceCodeWithUpdatedBreakpointInfo(script: SDK.Script.Script):
169
+ Promise<Workspace.UISourceCode.UISourceCode> {
169
170
  const isSnippet = script.sourceURL.startsWith('snippet://');
170
171
  const projectType = isSnippet ? Workspace.Workspace.projectTypes.Network : undefined;
171
172
 
@@ -725,6 +725,8 @@ const emulatedDevices = [
725
725
  'capabilities': ['touch', 'mobile'],
726
726
  'user-agent':
727
727
  'Mozilla/5.0 (Linux; Android 11; Pixel 3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.181 Mobile Safari/537.36',
728
+ 'user-agent-metadata':
729
+ {'platform': 'Android', 'platformVersion': '11', 'architecture': '', 'model': 'Pixel 3', 'mobile': true},
728
730
  'type': 'phone',
729
731
  },
730
732
  {
@@ -745,6 +747,8 @@ const emulatedDevices = [
745
747
  'capabilities': ['touch', 'mobile'],
746
748
  'user-agent':
747
749
  'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36',
750
+ 'user-agent-metadata':
751
+ {'platform': 'Android', 'platformVersion': '11', 'architecture': '', 'model': 'Pixel 5', 'mobile': true},
748
752
  'type': 'phone',
749
753
  },
750
754
  {
@@ -765,6 +769,8 @@ const emulatedDevices = [
765
769
  'capabilities': ['touch', 'mobile'],
766
770
  'user-agent':
767
771
  'Mozilla/5.0 (Linux; Android 8.0.0; SM-G955U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Mobile Safari/537.36',
772
+ 'user-agent-metadata':
773
+ {'platform': 'Android', 'platformVersion': '8.0.0', 'architecture': '', 'model': 'SM-G955U', 'mobile': true},
768
774
  'type': 'phone',
769
775
  },
770
776
  {
@@ -785,6 +791,8 @@ const emulatedDevices = [
785
791
  'capabilities': ['touch', 'mobile'],
786
792
  'user-agent':
787
793
  'Mozilla/5.0 (Linux; Android 10; SM-G981B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.162 Mobile Safari/537.36',
794
+ 'user-agent-metadata':
795
+ {'platform': 'Android', 'platformVersion': '10', 'architecture': '', 'model': 'SM-G981B', 'mobile': true},
788
796
  'type': 'phone',
789
797
  },
790
798
  {
@@ -869,7 +877,9 @@ const emulatedDevices = [
869
877
  },
870
878
  'capabilities': ['touch', 'mobile'],
871
879
  'user-agent':
872
- 'Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Mobile Safari/537.36',
880
+ 'Mozilla/5.0 (Linux; Android 11.0; Surface Duo) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Mobile Safari/537.36',
881
+ 'user-agent-metadata':
882
+ {'platform': 'Android', 'platformVersion': '11.0', 'architecture': '', 'model': 'Surface Duo', 'mobile': true},
873
883
  'type': 'phone',
874
884
  'modes': [
875
885
  {'title': 'default', 'orientation': 'vertical', 'insets': {'left': 0, 'top': 0, 'right': 0, 'bottom': 0}},
@@ -896,7 +906,9 @@ const emulatedDevices = [
896
906
  },
897
907
  'capabilities': ['touch', 'mobile'],
898
908
  'user-agent':
899
- 'Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Mobile Safari/537.36',
909
+ 'Mozilla/5.0 (Linux; Android 9.0; SAMSUNG SM-F900U Build/PPR1.180610.011) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Mobile Safari/537.36',
910
+ 'user-agent-metadata':
911
+ {'platform': 'Android', 'platformVersion': '9.0', 'architecture': '', 'model': 'SM-F900U', 'mobile': true},
900
912
  'type': 'phone',
901
913
  'modes': [
902
914
  {'title': 'default', 'orientation': 'vertical', 'insets': {'left': 0, 'top': 0, 'right': 0, 'bottom': 0}},
@@ -927,6 +939,8 @@ const emulatedDevices = [
927
939
  'capabilities': ['touch', 'mobile'],
928
940
  'user-agent':
929
941
  'Mozilla/5.0 (Linux; Android 8.0.0; SM-G955U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Mobile Safari/537.36',
942
+ 'user-agent-metadata':
943
+ {'platform': 'Android', 'platformVersion': '8.0.0', 'architecture': '', 'model': 'SM-G955U', 'mobile': true},
930
944
  'type': 'phone',
931
945
  },
932
946
  {
@@ -976,6 +990,8 @@ const emulatedDevices = [
976
990
  'capabilities': ['touch', 'mobile'],
977
991
  'user-agent':
978
992
  'Mozilla/5.0 (Linux; Android) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.109 Safari/537.36 CrKey/1.54.248666',
993
+ 'user-agent-metadata':
994
+ {'platform': 'Android', 'platformVersion': '', 'architecture': '', 'model': '', 'mobile': true},
979
995
  'type': 'tablet',
980
996
  'modes': [{'title': 'default', 'orientation': 'horizontal'}],
981
997
  },
@@ -1409,6 +1425,8 @@ const emulatedDevices = [
1409
1425
  'capabilities': ['touch', 'mobile'],
1410
1426
  'user-agent':
1411
1427
  'Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 550) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Mobile Safari/537.36 Edge/14.14263',
1428
+ 'user-agent-metadata':
1429
+ {'platform': 'Android', 'platformVersion': '4.2.1', 'architecture': '', 'model': 'Lumia 550', 'mobile': true},
1412
1430
  'type': 'phone',
1413
1431
  },
1414
1432
  {
@@ -1422,6 +1440,8 @@ const emulatedDevices = [
1422
1440
  'capabilities': ['touch', 'mobile'],
1423
1441
  'user-agent':
1424
1442
  'Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Mobile Safari/537.36 Edge/14.14263',
1443
+ 'user-agent-metadata':
1444
+ {'platform': 'Android', 'platformVersion': '4.2.1', 'architecture': '', 'model': 'Lumia 950', 'mobile': true},
1425
1445
  'type': 'phone',
1426
1446
  },
1427
1447
  {
@@ -1512,6 +1532,13 @@ const emulatedDevices = [
1512
1532
  'capabilities': ['touch', 'mobile'],
1513
1533
  'user-agent':
1514
1534
  'Mozilla/5.0 (Mobile; LYF/F300B/LYF-F300B-001-01-15-130718-i;Android; rv:48.0) Gecko/48.0 Firefox/48.0 KAIOS/2.5',
1535
+ 'user-agent-metadata': {
1536
+ 'platform': 'Android',
1537
+ 'platformVersion': '',
1538
+ 'architecture': '',
1539
+ 'model': 'LYF/F300B/LYF-F300B-001-01-15-130718-i',
1540
+ 'mobile': true,
1541
+ },
1515
1542
  'type': 'phone',
1516
1543
  },
1517
1544
  {
@@ -1247,7 +1247,7 @@ export const NativeFunctions = [
1247
1247
  },
1248
1248
  {
1249
1249
  name: 'add',
1250
- signatures: [['install_url']],
1250
+ signatures: [['sub_apps']],
1251
1251
  receivers: ['SubApps']
1252
1252
  },
1253
1253
  {
@@ -1745,11 +1745,6 @@ export const NativeFunctions = [
1745
1745
  name: 'replaceWith',
1746
1746
  signatures: [['...nodes']]
1747
1747
  },
1748
- {
1749
- name: 'read',
1750
- signatures: [['?options']],
1751
- receivers: ['Clipboard']
1752
- },
1753
1748
  {
1754
1749
  name: 'read',
1755
1750
  signatures: [['view']],
@@ -2572,7 +2567,7 @@ export const NativeFunctions = [
2572
2567
  {
2573
2568
  name: 'focus',
2574
2569
  signatures: [['focus_behavior']],
2575
- receivers: ['FocusableMediaStreamTrack']
2570
+ receivers: ['BrowserCaptureMediaStreamTrack','FocusableMediaStreamTrack']
2576
2571
  },
2577
2572
  {
2578
2573
  name: 'assign',
@@ -5898,10 +5893,6 @@ export const NativeFunctions = [
5898
5893
  name: 'registerProperty',
5899
5894
  signatures: [['definition']]
5900
5895
  },
5901
- {
5902
- name: 'setElement',
5903
- signatures: [['element','tag','?options']]
5904
- },
5905
5896
  {
5906
5897
  name: 'timeout',
5907
5898
  signatures: [['milliseconds']]
@@ -6767,7 +6758,7 @@ export const NativeFunctions = [
6767
6758
  },
6768
6759
  {
6769
6760
  name: 'ClipboardItem',
6770
- signatures: [['items','?options']]
6761
+ signatures: [['items']]
6771
6762
  },
6772
6763
  {
6773
6764
  name: 'CompressionStream',
@@ -7113,6 +7104,10 @@ export const NativeFunctions = [
7113
7104
  name: 'cropTo',
7114
7105
  signatures: [['crop_id']]
7115
7106
  },
7107
+ {
7108
+ name: 'fromElement',
7109
+ signatures: [['element']]
7110
+ },
7116
7111
  {
7117
7112
  name: 'getDisplayMediaSet',
7118
7113
  signatures: [['?constraints']]
@@ -7121,10 +7116,6 @@ export const NativeFunctions = [
7121
7116
  name: 'setCaptureHandleConfig',
7122
7117
  signatures: [['?config']]
7123
7118
  },
7124
- {
7125
- name: 'produceCropId',
7126
- signatures: [['target']]
7127
- },
7128
7119
  {
7129
7120
  name: 'MediaStreamEvent',
7130
7121
  signatures: [['type','?eventInitDict']]
@@ -7399,7 +7390,7 @@ export const NativeFunctions = [
7399
7390
  },
7400
7391
  {
7401
7392
  name: 'requestPictureInPictureWindow',
7402
- signatures: [['options']]
7393
+ signatures: [['?options']]
7403
7394
  },
7404
7395
  {
7405
7396
  name: 'PresentationConnectionAvailableEvent',
@@ -8,6 +8,7 @@ import * as Platform from '../../core/platform/platform.js';
8
8
  import * as Root from '../../core/root/root.js';
9
9
  import * as SDK from '../../core/sdk/sdk.js';
10
10
  import * as Protocol from '../../generated/protocol.js';
11
+ import * as Bindings from '../bindings/bindings.js';
11
12
  import * as Workspace from '../workspace/workspace.js';
12
13
 
13
14
  import type {FileSystem} from './FileSystemWorkspaceBinding.js';
@@ -35,6 +36,7 @@ export class NetworkPersistenceManager extends Common.ObjectWrapper.ObjectWrappe
35
36
  private enabled: boolean;
36
37
  private eventDescriptors: Common.EventTarget.EventDescriptor[];
37
38
  #headerOverridesMap: Map<Platform.DevToolsPath.EncodedPathString, HeaderOverrideWithRegex[]> = new Map();
39
+ readonly #sourceCodeToBindProcessMutex = new WeakMap<Workspace.UISourceCode.UISourceCode, Common.Mutex.Mutex>();
38
40
 
39
41
  private constructor(workspace: Workspace.Workspace.WorkspaceImpl) {
40
42
  super();
@@ -66,6 +68,8 @@ export class NetworkPersistenceManager extends Common.ObjectWrapper.ObjectWrappe
66
68
  });
67
69
 
68
70
  PersistenceImpl.instance().addNetworkInterceptor(this.canHandleNetworkUISourceCode.bind(this));
71
+ Bindings.BreakpointManager.BreakpointManager.instance().addUpdateBindingsCallback(
72
+ this.networkUISourceCodeAdded.bind(this));
69
73
 
70
74
  this.eventDescriptors = [];
71
75
  void this.enabledChanged();
@@ -301,24 +305,66 @@ export class NetworkPersistenceManager extends Common.ObjectWrapper.ObjectWrappe
301
305
  return path;
302
306
  }
303
307
 
304
- private async unbind(uiSourceCode: Workspace.UISourceCode.UISourceCode): Promise<void> {
308
+ async #unbind(uiSourceCode: Workspace.UISourceCode.UISourceCode): Promise<void> {
305
309
  const binding = this.bindings.get(uiSourceCode);
306
310
  if (binding) {
307
- this.bindings.delete(binding.network);
308
- this.bindings.delete(binding.fileSystem);
309
- await PersistenceImpl.instance().removeBinding(binding);
311
+ const mutex = this.#getOrCreateMutex(binding.network);
312
+ const release = await mutex.acquire();
313
+ try {
314
+ await this.#innerUnbind(binding);
315
+ } finally {
316
+ release();
317
+ }
310
318
  }
311
319
  }
312
320
 
313
- private async bind(
321
+ async #unbindUnguarded(uiSourceCode: Workspace.UISourceCode.UISourceCode): Promise<void> {
322
+ const binding = this.bindings.get(uiSourceCode);
323
+ if (binding) {
324
+ await this.#innerUnbind(binding);
325
+ }
326
+ }
327
+
328
+ #innerUnbind(binding: PersistenceBinding): Promise<void> {
329
+ this.bindings.delete(binding.network);
330
+ this.bindings.delete(binding.fileSystem);
331
+ return PersistenceImpl.instance().removeBinding(binding);
332
+ }
333
+
334
+ async #bind(
314
335
  networkUISourceCode: Workspace.UISourceCode.UISourceCode,
315
336
  fileSystemUISourceCode: Workspace.UISourceCode.UISourceCode): Promise<void> {
316
- if (this.bindings.has(networkUISourceCode)) {
317
- await this.unbind(networkUISourceCode);
337
+ const mutex = this.#getOrCreateMutex(networkUISourceCode);
338
+ const release = await mutex.acquire();
339
+ try {
340
+ const existingBinding = this.bindings.get(networkUISourceCode);
341
+ if (existingBinding) {
342
+ const {network, fileSystem} = existingBinding;
343
+ if (networkUISourceCode === network && fileSystemUISourceCode === fileSystem) {
344
+ return;
345
+ }
346
+ await this.#unbindUnguarded(networkUISourceCode);
347
+ await this.#unbindUnguarded(fileSystemUISourceCode);
348
+ }
349
+
350
+ await this.#innerAddBinding(networkUISourceCode, fileSystemUISourceCode);
351
+ } finally {
352
+ release();
318
353
  }
319
- if (this.bindings.has(fileSystemUISourceCode)) {
320
- await this.unbind(fileSystemUISourceCode);
354
+ }
355
+
356
+ #getOrCreateMutex(networkUISourceCode: Workspace.UISourceCode.UISourceCode): Common.Mutex.Mutex {
357
+ let mutex = this.#sourceCodeToBindProcessMutex.get(networkUISourceCode);
358
+ if (!mutex) {
359
+ mutex = new Common.Mutex.Mutex();
360
+ this.#sourceCodeToBindProcessMutex.set(networkUISourceCode, mutex);
321
361
  }
362
+ return mutex;
363
+ }
364
+
365
+ async #innerAddBinding(
366
+ networkUISourceCode: Workspace.UISourceCode.UISourceCode,
367
+ fileSystemUISourceCode: Workspace.UISourceCode.UISourceCode): Promise<void> {
322
368
  const binding = new PersistenceBinding(networkUISourceCode, fileSystemUISourceCode);
323
369
  this.bindings.set(networkUISourceCode, binding);
324
370
  this.bindings.set(fileSystemUISourceCode, binding);
@@ -394,7 +440,7 @@ export class NetworkPersistenceManager extends Common.ObjectWrapper.ObjectWrappe
394
440
  const project = this.projectInternal as FileSystem;
395
441
  const fileSystemUISourceCode = project.uiSourceCodeForURL(this.fileUrlFromNetworkUrl(url));
396
442
  if (fileSystemUISourceCode) {
397
- await this.bind(uiSourceCode, fileSystemUISourceCode);
443
+ await this.#bind(uiSourceCode, fileSystemUISourceCode);
398
444
  }
399
445
  }
400
446
 
@@ -408,7 +454,7 @@ export class NetworkPersistenceManager extends Common.ObjectWrapper.ObjectWrappe
408
454
  const networkUISourceCode =
409
455
  this.networkUISourceCodeForEncodedPath.get(Common.ParsedURL.ParsedURL.join(relativePath, '/'));
410
456
  if (networkUISourceCode) {
411
- await this.bind(networkUISourceCode, uiSourceCode);
457
+ await this.#bind(networkUISourceCode, uiSourceCode);
412
458
  }
413
459
  }
414
460
 
@@ -521,7 +567,8 @@ export class NetworkPersistenceManager extends Common.ObjectWrapper.ObjectWrappe
521
567
 
522
568
  private async networkUISourceCodeRemoved(uiSourceCode: Workspace.UISourceCode.UISourceCode): Promise<void> {
523
569
  if (uiSourceCode.project().type() === Workspace.Workspace.projectTypes.Network) {
524
- await this.unbind(uiSourceCode);
570
+ await this.#unbind(uiSourceCode);
571
+ this.#sourceCodeToBindProcessMutex.delete(uiSourceCode);
525
572
  this.networkUISourceCodeForEncodedPath.delete(this.encodedPathFromUrl(uiSourceCode.url()));
526
573
  }
527
574
  }
@@ -532,7 +579,7 @@ export class NetworkPersistenceManager extends Common.ObjectWrapper.ObjectWrappe
532
579
  }
533
580
  this.updateInterceptionPatterns();
534
581
  this.originalResponseContentPromises.delete(uiSourceCode);
535
- await this.unbind(uiSourceCode);
582
+ await this.#unbind(uiSourceCode);
536
583
  }
537
584
 
538
585
  async setProject(project: Workspace.Workspace.Project|null): Promise<void> {
@@ -1218,6 +1218,8 @@ export class ElementsTreeOutline extends
1218
1218
  visibleChildren.push(templateContent);
1219
1219
  }
1220
1220
 
1221
+ visibleChildren.push(...node.pageTransitionPseudoElements());
1222
+
1221
1223
  const markerPseudoElement = node.markerPseudoElement();
1222
1224
  if (markerPseudoElement) {
1223
1225
  visibleChildren.push(markerPseudoElement);
@@ -110,8 +110,14 @@ export class FilteredUISourceCodeListProvider extends QuickOpen.FilteredListWidg
110
110
  multiplier = 5;
111
111
  }
112
112
 
113
+ let contentTypeBonus = 0;
114
+ if (uiSourceCode.contentType().isFromSourceMap()) {
115
+ contentTypeBonus = 100;
116
+ // Maybe also have a bonus for being a script?
117
+ }
118
+
113
119
  const fullDisplayName = uiSourceCode.fullDisplayName();
114
- return score + multiplier * this.scorer.calculateScore(fullDisplayName, null);
120
+ return score + multiplier * (contentTypeBonus + this.scorer.calculateScore(fullDisplayName, null));
115
121
  }
116
122
 
117
123
  renderItem(itemIndex: number, query: string, titleElement: Element, subtitleElement: Element): void {
@@ -454,6 +454,9 @@ export class WebauthnPaneImpl extends UI.Widget.VBox implements
454
454
 
455
455
  #removeAuthenticatorSections(): void {
456
456
  this.#authenticatorsView.innerHTML = '';
457
+ for (const dataGrid of this.#dataGrids.values()) {
458
+ dataGrid.asWidget().detach();
459
+ }
457
460
  this.#dataGrids.clear();
458
461
  }
459
462
 
@@ -788,7 +791,11 @@ export class WebauthnPaneImpl extends UI.Widget.VBox implements
788
791
  child.remove();
789
792
  }
790
793
  }
791
- this.#dataGrids.delete(authenticatorId);
794
+ const dataGrid = this.#dataGrids.get(authenticatorId);
795
+ if (dataGrid) {
796
+ dataGrid.asWidget().detach();
797
+ this.#dataGrids.delete(authenticatorId);
798
+ }
792
799
 
793
800
  if (this.#model) {
794
801
  void this.#model.removeAuthenticator(authenticatorId);
@@ -60,7 +60,7 @@ export class PreviewToggle extends HTMLElement {
60
60
  // clang-format off
61
61
  render(
62
62
  html`
63
- <div class="experiment-preview">
63
+ <label class="experiment-preview">
64
64
  <input type="checkbox" ?checked=${checked} @change=${this.#checkboxChanged} aria-label=${this.#name}/>
65
65
  <${IconButton.Icon.Icon.litTagName} .data=${{
66
66
  iconName: 'ic_preview_feature',
@@ -74,7 +74,7 @@ export class PreviewToggle extends HTMLElement {
74
74
  ${this.#helperText && this.#feedbackURL
75
75
  ? html`<p>${this.#helperText} <x-link href=${this.#feedbackURL}>${i18nString(UIStrings.previewTextFeedbackLink)}</x-link></p>`
76
76
  : nothing}
77
- </div>`,
77
+ </label>`,
78
78
  this.#shadow,
79
79
  {
80
80
  host: this,
@@ -236,7 +236,7 @@ function getTooltipSpace(): DOMRect {
236
236
  return sideBarElement.getBoundingClientRect();
237
237
  }
238
238
 
239
- export function baseConfiguration(text: string): CM.Extension {
239
+ export function baseConfiguration(text: string|CM.Text): CM.Extension {
240
240
  return [
241
241
  theme(),
242
242
  CM.highlightSpecialChars(),
@@ -252,7 +252,7 @@ export function baseConfiguration(text: string): CM.Extension {
252
252
  bracketMatching.instance(),
253
253
  indentUnit.instance(),
254
254
  CM.Prec.lowest(CM.EditorView.contentAttributes.of({'aria-label': i18nString(UIStrings.codeEditor)})),
255
- detectLineSeparator(text),
255
+ text instanceof CM.Text ? [] : detectLineSeparator(text),
256
256
  CM.tooltips({
257
257
  tooltipSpace: getTooltipSpace,
258
258
  }),
@@ -25,7 +25,7 @@
25
25
  bottom: 0;
26
26
  left: 0;
27
27
  right: 0;
28
- overflow: overlay;
28
+ overflow: scroll;
29
29
  transform: translateZ(0);
30
30
  background-color: var(--color-background);
31
31
  }
@@ -105,7 +105,7 @@ export class SourceFrameImpl extends Common.ObjectWrapper.eventMixin<EventTypes,
105
105
  UI.View.SimpleView) implements UI.SearchableView.Searchable, UI.SearchableView.Replaceable, Transformer {
106
106
  private readonly lazyContent: () => Promise<TextUtils.ContentProvider.DeferredContent>;
107
107
  private prettyInternal: boolean;
108
- private rawContent: string|null;
108
+ private rawContent: string|CodeMirror.Text|null;
109
109
  private formattedContentPromise: Promise<Formatter.ScriptFormatter.FormattedContent>|null;
110
110
  private formattedMap: Formatter.ScriptFormatter.FormatterSourceMapping|null;
111
111
  private readonly prettyToggle: UI.Toolbar.ToolbarToggle;
@@ -203,7 +203,7 @@ export class SourceFrameImpl extends Common.ObjectWrapper.eventMixin<EventTypes,
203
203
  });
204
204
  }
205
205
 
206
- protected editorConfiguration(doc: string): CodeMirror.Extension {
206
+ protected editorConfiguration(doc: string|CodeMirror.Text): CodeMirror.Extension {
207
207
  return [
208
208
  CodeMirror.EditorView.updateListener.of(update => this.dispatchEventToListeners(Events.EditorUpdate, update)),
209
209
  TextEditor.Config.baseConfiguration(doc),
@@ -459,7 +459,7 @@ export class SourceFrameImpl extends Common.ObjectWrapper.eventMixin<EventTypes,
459
459
  const worker = Common.Worker.WorkerWrapper.fromURL(
460
460
  new URL('../../../../entrypoints/wasmparser_worker/wasmparser_worker-entrypoint.js', import.meta.url));
461
461
  const promise = new Promise<{
462
- source: string,
462
+ lines: string[],
463
463
  offsets: number[],
464
464
  functionBodyOffsets: {
465
465
  start: number,
@@ -492,8 +492,8 @@ export class SourceFrameImpl extends Common.ObjectWrapper.eventMixin<EventTypes,
492
492
  });
493
493
  worker.postMessage({method: 'disassemble', params: {content}});
494
494
  try {
495
- const {source, offsets, functionBodyOffsets} = await promise;
496
- this.rawContent = content = source;
495
+ const {lines, offsets, functionBodyOffsets} = await promise;
496
+ this.rawContent = content = CodeMirror.Text.of(lines);
497
497
  this.wasmDisassemblyInternal = new Common.WasmDisassembly.WasmDisassembly(offsets, functionBodyOffsets);
498
498
  } catch (e) {
499
499
  this.rawContent = content = error = e.message;
@@ -528,8 +528,8 @@ export class SourceFrameImpl extends Common.ObjectWrapper.eventMixin<EventTypes,
528
528
  if (this.formattedContentPromise) {
529
529
  return this.formattedContentPromise;
530
530
  }
531
- this.formattedContentPromise =
532
- Formatter.ScriptFormatter.formatScriptContent(this.contentType, this.rawContent || '');
531
+ const content = this.rawContent instanceof CodeMirror.Text ? this.rawContent.sliceString(0) : this.rawContent || '';
532
+ this.formattedContentPromise = Formatter.ScriptFormatter.formatScriptContent(this.contentType, content);
533
533
  return this.formattedContentPromise;
534
534
  }
535
535
 
@@ -648,7 +648,7 @@ export class SourceFrameImpl extends Common.ObjectWrapper.eventMixin<EventTypes,
648
648
  this.prettyToggle.setEnabled(true);
649
649
  }
650
650
 
651
- private simplifyMimeType(content: string, mimeType: string): string {
651
+ private simplifyMimeType(content: string|CodeMirror.Text, mimeType: string): string {
652
652
  if (!mimeType) {
653
653
  return '';
654
654
  }
@@ -663,8 +663,11 @@ export class SourceFrameImpl extends Common.ObjectWrapper.eventMixin<EventTypes,
663
663
  return 'text/jsx';
664
664
  }
665
665
  // A hack around the fact that files with "php" extension might be either standalone or html embedded php scripts.
666
- if (mimeType === 'text/x-php' && content.match(/\<\?.*\?\>/g)) {
667
- return 'application/x-httpd-php';
666
+ if (mimeType === 'text/x-php') {
667
+ const strContent = typeof content === 'string' ? content : content.sliceString(0);
668
+ if (strContent.match(/\<\?.*\?\>/g)) {
669
+ return 'application/x-httpd-php';
670
+ }
668
671
  }
669
672
  if (mimeType === 'application/wasm') {
670
673
  // text/webassembly is not a proper MIME type, but CodeMirror uses it for WAT syntax highlighting.
@@ -674,7 +677,7 @@ export class SourceFrameImpl extends Common.ObjectWrapper.eventMixin<EventTypes,
674
677
  return mimeType;
675
678
  }
676
679
 
677
- protected async getLanguageSupport(content: string): Promise<CodeMirror.Extension> {
680
+ protected async getLanguageSupport(content: string|CodeMirror.Text): Promise<CodeMirror.Extension> {
678
681
  const mimeType = this.simplifyMimeType(content, this.contentType) || '';
679
682
  const languageDesc = await CodeHighlighter.CodeHighlighter.languageFromMIME(mimeType);
680
683
  if (!languageDesc) {
@@ -694,7 +697,7 @@ export class SourceFrameImpl extends Common.ObjectWrapper.eventMixin<EventTypes,
694
697
  this.textEditor.dispatch({effects: config.language.reconfigure(langExtension)});
695
698
  }
696
699
 
697
- async setContent(content: string): Promise<void> {
700
+ async setContent(content: string|CodeMirror.Text): Promise<void> {
698
701
  this.muteChangeEventsForSetContent = true;
699
702
  const {textEditor} = this;
700
703
  const wasLoaded = this.loadedInternal;
package/package.json CHANGED
@@ -55,5 +55,5 @@
55
55
  "unittest": "scripts/test/run_unittests.py --no-text-coverage",
56
56
  "watch": "vpython third_party/node/node.py --output scripts/watch_build.js"
57
57
  },
58
- "version": "1.0.1010977"
58
+ "version": "1.0.1012379"
59
59
  }