chrome-devtools-frontend 1.0.968254 → 1.0.970302

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 (65) hide show
  1. package/config/gni/devtools_grd_files.gni +64 -0
  2. package/front_end/core/i18n/locales/en-US.json +18 -0
  3. package/front_end/core/i18n/locales/en-XL.json +18 -0
  4. package/front_end/core/sdk/CSSMatchedStyles.ts +1 -1
  5. package/front_end/core/sdk/ChildTargetManager.ts +2 -2
  6. package/front_end/core/sdk/Connections.ts +6 -1
  7. package/front_end/core/sdk/NetworkManager.ts +4 -3
  8. package/front_end/entrypoints/lighthouse_worker/LighthouseService.ts +84 -13
  9. package/front_end/models/persistence/NetworkPersistenceManager.ts +205 -41
  10. package/front_end/models/timeline_model/TimelineFrameModel.ts +21 -7
  11. package/front_end/panels/animation/animationTimeline.css +0 -3
  12. package/front_end/panels/application/components/ReportsGrid.ts +19 -4
  13. package/front_end/panels/application/components/trustTokensViewDeleteButton.css +0 -1
  14. package/front_end/panels/console/consolePinPane.css +0 -17
  15. package/front_end/panels/css_overview/cssOverviewCompletedView.css +0 -1
  16. package/front_end/panels/elements/components/adornerSettingsPane.css +0 -1
  17. package/front_end/panels/elements/components/computedStyleTrace.css +1 -1
  18. package/front_end/panels/elements/components/elementsBreadcrumbs.css +0 -1
  19. package/front_end/panels/elements/computedStyleWidgetTree.css +2 -2
  20. package/front_end/panels/elements/elementsTreeOutline.css +0 -2
  21. package/front_end/panels/emulation/deviceModeView.css +0 -1
  22. package/front_end/panels/event_listeners/eventListenersView.css +0 -1
  23. package/front_end/panels/issues/components/hideIssuesMenu.css +0 -1
  24. package/front_end/panels/lighthouse/LighthouseController.ts +25 -1
  25. package/front_end/panels/lighthouse/LighthouseProtocolService.ts +37 -5
  26. package/front_end/panels/lighthouse/LighthouseStartView.ts +1 -0
  27. package/front_end/panels/lighthouse/LighthouseStatusView.ts +5 -5
  28. package/front_end/panels/media/playerListView.css +0 -1
  29. package/front_end/panels/network/networkLogView.css +0 -4
  30. package/front_end/panels/network/requestPayloadTree.css +0 -2
  31. package/front_end/panels/network/signedExchangeInfoTree.css +0 -1
  32. package/front_end/panels/settings/emulation/components/userAgentClientHintsForm.css +0 -4
  33. package/front_end/panels/settings/emulation/devicesSettingsTab.css +0 -1
  34. package/front_end/panels/sources/CSSPlugin.ts +2 -0
  35. package/front_end/panels/sources/ScopeChainSidebarPane.ts +31 -0
  36. package/front_end/panels/sources/SourceMapNamesResolver.ts +15 -9
  37. package/front_end/panels/sources/watchExpressionsSidebarPane.css +0 -1
  38. package/front_end/panels/timeline/TimelineFlameChartDataProvider.ts +75 -3
  39. package/front_end/panels/webauthn/webauthnPane.css +0 -12
  40. package/front_end/services/puppeteer/PuppeteerConnection.ts +107 -0
  41. package/front_end/services/puppeteer/puppeteer.ts +9 -0
  42. package/front_end/third_party/codemirror/package/addon/fold/foldgutter.css +1 -5
  43. package/front_end/third_party/codemirror.next/README.chromium +10 -0
  44. package/front_end/third_party/codemirror.next/chunk/codemirror.js +1 -1
  45. package/front_end/third_party/codemirror.next/chunk/cpp.js +2 -1
  46. package/front_end/third_party/codemirror.next/chunk/markdown.js +2 -2
  47. package/front_end/third_party/codemirror.next/chunk/python.js +2 -1
  48. package/front_end/third_party/codemirror.next/codemirror.next.d.ts +574 -553
  49. package/front_end/third_party/codemirror.next/package.json +11 -11
  50. package/front_end/ui/components/adorners/adorner.css +0 -4
  51. package/front_end/ui/components/buttons/button.css +0 -4
  52. package/front_end/ui/components/data_grid/dataGrid.css +0 -4
  53. package/front_end/ui/components/icon_button/iconButton.css +0 -1
  54. package/front_end/ui/components/markdown_view/MarkdownLinksMap.ts +2 -2
  55. package/front_end/ui/legacy/TabbedPane.ts +1 -1
  56. package/front_end/ui/legacy/components/perf_ui/TimelineOverviewPane.ts +2 -2
  57. package/front_end/ui/legacy/components/source_frame/SourceFrame.ts +4 -2
  58. package/front_end/ui/legacy/tabbedPane.css +0 -4
  59. package/front_end/ui/legacy/textButton.css +0 -1
  60. package/front_end/ui/legacy/toolbar.css +0 -1
  61. package/package.json +2 -2
  62. package/scripts/build/devtools_plugin.js +32 -1
  63. package/scripts/build/esbuild.js +1 -24
  64. package/scripts/build/tests/plugins_test.js +60 -1
  65. package/scripts/hosted_mode/server.js +5 -0
@@ -125,6 +125,14 @@ const UIStrings = {
125
125
  *@description Text of checkbox to reset storage features prior to running audits in Lighthouse
126
126
  */
127
127
  clearStorage: 'Clear storage',
128
+ /**
129
+ * @description Text of checkbox to use the legacy Lighthouse navigation mode
130
+ */
131
+ legacyNavigation: 'Legacy navigation',
132
+ /**
133
+ * @description Tooltip text that appears when hovering over the 'Legacy navigation' checkbox in the settings pane opened by clicking the setting cog in the start view of the audits panel
134
+ */
135
+ useLegacyNavigation: 'Audit the page using classic Lighthouse when in navigation mode.',
128
136
  /**
129
137
  * @description Tooltip text of checkbox to reset storage features prior to running audits in
130
138
  * Lighthouse. Resetting the storage clears/empties it to a neutral state.
@@ -277,7 +285,11 @@ export class LighthouseController extends Common.ObjectWrapper.ObjectWrapper<Eve
277
285
  return navigationEntry.url;
278
286
  }
279
287
 
280
- getFlags(): {internalDisableDeviceScreenEmulation: boolean, emulatedFormFactor: (string|undefined)} {
288
+ getFlags(): {
289
+ internalDisableDeviceScreenEmulation: boolean,
290
+ emulatedFormFactor: (string|undefined),
291
+ legacyNavigation: boolean,
292
+ } {
281
293
  const flags = {
282
294
  // DevTools handles all the emulation. This tells Lighthouse to not bother with emulation.
283
295
  internalDisableDeviceScreenEmulation: true,
@@ -288,6 +300,7 @@ export class LighthouseController extends Common.ObjectWrapper.ObjectWrapper<Eve
288
300
  return flags as {
289
301
  internalDisableDeviceScreenEmulation: boolean,
290
302
  emulatedFormFactor: (string | undefined),
303
+ legacyNavigation: boolean,
291
304
  };
292
305
  }
293
306
 
@@ -433,6 +446,17 @@ export const RuntimeSettings: RuntimeSetting[] = [
433
446
  options: undefined,
434
447
  learnMore: undefined,
435
448
  },
449
+ {
450
+ setting: Common.Settings.Settings.instance().createSetting(
451
+ 'lighthouse.legacy_navigation', true, Common.Settings.SettingStorageType.Synced),
452
+ title: i18nLazyString(UIStrings.legacyNavigation),
453
+ description: i18nLazyString(UIStrings.useLegacyNavigation),
454
+ setFlags: (flags: Flags, value: string|boolean): void => {
455
+ flags.legacyNavigation = value;
456
+ },
457
+ options: undefined,
458
+ learnMore: undefined,
459
+ },
436
460
  ];
437
461
 
438
462
  // TODO(crbug.com/1167717): Make this a const enum again
@@ -11,6 +11,11 @@ import type * as ReportRenderer from './LighthouseReporterTypes.js';
11
11
  let lastId = 1;
12
12
 
13
13
  export class ProtocolService {
14
+ private targetInfo?: {
15
+ mainSessionId: string,
16
+ mainTargetId: string,
17
+ mainFrameId: string,
18
+ };
14
19
  private rawConnection?: ProtocolClient.InspectorBackend.Connection;
15
20
  private lighthouseWorkerPromise?: Promise<Worker>;
16
21
  private lighthouseMessageUpdateCallback?: ((arg0: string) => void);
@@ -19,26 +24,53 @@ export class ProtocolService {
19
24
  await SDK.TargetManager.TargetManager.instance().suspendAllTargets();
20
25
  const mainTarget = SDK.TargetManager.TargetManager.instance().mainTarget();
21
26
  if (!mainTarget) {
22
- throw new Error('Unable to find main target required for LightHouse');
27
+ throw new Error('Unable to find main target required for Lighthouse');
23
28
  }
24
29
  const childTargetManager = mainTarget.model(SDK.ChildTargetManager.ChildTargetManager);
25
30
  if (!childTargetManager) {
26
- throw new Error('Unable to find child target manager required for LightHouse');
31
+ throw new Error('Unable to find child target manager required for Lighthouse');
27
32
  }
28
- this.rawConnection = await childTargetManager.createParallelConnection(message => {
33
+ const resourceTreeModel = mainTarget.model(SDK.ResourceTreeModel.ResourceTreeModel);
34
+ if (!resourceTreeModel) {
35
+ throw new Error('Unable to find resource tree model required for Lighthouse');
36
+ }
37
+ const mainFrame = resourceTreeModel.mainFrame;
38
+ if (!mainFrame) {
39
+ throw new Error('Unable to find main frame required for Lighthouse');
40
+ }
41
+
42
+ const {connection, sessionId} = await childTargetManager.createParallelConnection(message => {
29
43
  if (typeof message === 'string') {
30
44
  message = JSON.parse(message);
31
45
  }
32
46
  this.dispatchProtocolMessage(message);
33
47
  });
48
+
49
+ this.rawConnection = connection;
50
+ this.targetInfo = {
51
+ mainTargetId: await childTargetManager.getParentTargetId(),
52
+ mainFrameId: mainFrame.id,
53
+ mainSessionId: sessionId,
54
+ };
34
55
  }
35
56
 
36
57
  getLocales(): readonly string[] {
37
58
  return [i18n.DevToolsLocale.DevToolsLocale.instance().locale];
38
59
  }
39
60
 
40
- startLighthouse(auditURL: string, categoryIDs: string[], flags: Object): Promise<ReportRenderer.RunnerResult> {
41
- return this.sendWithResponse('start', {url: auditURL, categoryIDs, flags, locales: this.getLocales()});
61
+ async startLighthouse(auditURL: string, categoryIDs: string[], flags: Record<string, Object|undefined>):
62
+ Promise<ReportRenderer.RunnerResult> {
63
+ if (!this.targetInfo) {
64
+ throw new Error('Unable to get target info required for Lighthouse');
65
+ }
66
+ const mode = flags.legacyNavigation ? 'start' : 'navigate';
67
+ return this.sendWithResponse(mode, {
68
+ url: auditURL,
69
+ categoryIDs,
70
+ flags,
71
+ locales: this.getLocales(),
72
+ target: this.targetInfo,
73
+ });
42
74
  }
43
75
 
44
76
  async detach(): Promise<void> {
@@ -113,6 +113,7 @@ export class StartView extends UI.Widget.Widget {
113
113
  }
114
114
 
115
115
  private render(): void {
116
+ this.populateRuntimeSettingAsToolbarCheckbox('lighthouse.legacy_navigation', this.settingsToolbarInternal);
116
117
  this.populateRuntimeSettingAsToolbarCheckbox('lighthouse.clear_storage', this.settingsToolbarInternal);
117
118
  this.populateRuntimeSettingAsToolbarCheckbox('lighthouse.throttling', this.settingsToolbarInternal);
118
119
 
@@ -321,7 +321,7 @@ export class StatusView {
321
321
  }
322
322
 
323
323
  private getPhaseForMessage(message: string): StatusPhase|null {
324
- return StatusPhases.find(phase => message.startsWith(phase.statusMessagePrefix)) || null;
324
+ return StatusPhases.find(phase => phase.statusMessageRegex.test(message)) || null;
325
325
  }
326
326
 
327
327
  private resetProgressBarClasses(): void {
@@ -457,7 +457,7 @@ export interface StatusPhase {
457
457
  id: string;
458
458
  progressBarClass: string;
459
459
  message: () => Common.UIString.LocalizedString;
460
- statusMessagePrefix: string;
460
+ statusMessageRegex: RegExp;
461
461
  }
462
462
 
463
463
  export const StatusPhases: StatusPhase[] = [
@@ -465,19 +465,19 @@ export const StatusPhases: StatusPhase[] = [
465
465
  id: 'loading',
466
466
  progressBarClass: 'loading',
467
467
  message: i18nLazyString(UIStrings.lighthouseIsLoadingThePage),
468
- statusMessagePrefix: 'Loading page',
468
+ statusMessageRegex: /^(Loading page|Navigating to)/,
469
469
  },
470
470
  {
471
471
  id: 'gathering',
472
472
  progressBarClass: 'gathering',
473
473
  message: i18nLazyString(UIStrings.lighthouseIsGatheringInformation),
474
- statusMessagePrefix: 'Gathering',
474
+ statusMessageRegex: /^(Gathering|Computing artifact)/,
475
475
  },
476
476
  {
477
477
  id: 'auditing',
478
478
  progressBarClass: 'auditing',
479
479
  message: i18nLazyString(UIStrings.almostThereLighthouseIsNow),
480
- statusMessagePrefix: 'Auditing',
480
+ statusMessageRegex: /^Auditing/,
481
481
  },
482
482
  ];
483
483
 
@@ -61,7 +61,6 @@ li.storage-group-list-item::before {
61
61
  }
62
62
 
63
63
  .player-entry-row:hover {
64
- cursor: pointer;
65
64
  background: var(--color-background-hover-overlay);
66
65
  }
67
66
 
@@ -124,10 +124,6 @@
124
124
  color: inherit;
125
125
  }
126
126
 
127
- .network-log-grid.data-grid .name-column {
128
- cursor: pointer;
129
- }
130
-
131
127
  .network-log-grid.data-grid .waterfall-column {
132
128
  padding: 1px 0;
133
129
  }
@@ -71,7 +71,6 @@
71
71
 
72
72
  .tree-outline li .header-toggle:hover {
73
73
  color: var(--color-text-secondary);
74
- cursor: pointer;
75
74
  }
76
75
 
77
76
  .tree-outline .payload-name {
@@ -105,7 +104,6 @@
105
104
  display: inline-block;
106
105
  font-size: 12px;
107
106
  font-family: sans-serif;
108
- cursor: pointer;
109
107
  margin: 0 4px;
110
108
  padding: 2px 4px;
111
109
  }
@@ -69,7 +69,6 @@
69
69
  --override-header-hover-color: rgb(20% 20% 45%);
70
70
 
71
71
  color: var(--override-header-hover-color);
72
- cursor: pointer;
73
72
  }
74
73
 
75
74
  .-theme-with-dark-background .tree-outline .header-toggle:hover,
@@ -75,10 +75,6 @@
75
75
  margin-right: 5px;
76
76
  }
77
77
 
78
- .delete-icon {
79
- cursor: pointer;
80
- }
81
-
82
78
  .brand-row {
83
79
  display: flex;
84
80
  align-items: center;
@@ -34,7 +34,6 @@
34
34
  display: flex;
35
35
  align-items: center;
36
36
  flex: auto 1 1;
37
- cursor: pointer;
38
37
  overflow: hidden;
39
38
  color: var(--color-text-primary);
40
39
  user-select: none;
@@ -238,12 +238,14 @@ function createCSSTooltip(active: ActiveTooltip): CodeMirror.Tooltip {
238
238
  changes: text === active.text ? undefined :
239
239
  {from: active.pos, to: active.pos + text.length, insert: active.text},
240
240
  });
241
+ widget.hideWidget();
241
242
  view.focus();
242
243
  }
243
244
  });
244
245
  widget.element.addEventListener('focusout', event => {
245
246
  if (event.relatedTarget && !widget.element.contains(event.relatedTarget as Node)) {
246
247
  view.dispatch({effects: setTooltip.of(null)});
248
+ widget.hideWidget();
247
249
  }
248
250
  }, false);
249
251
  widget.element.addEventListener('mousedown', event => event.consume());
@@ -90,6 +90,8 @@ export class ScopeChainSidebarPane extends UI.Widget.VBox implements UI.ContextF
90
90
  private readonly expandController: ObjectUI.ObjectPropertiesSection.ObjectPropertiesSectionsTreeExpandController;
91
91
  private readonly linkifier: Components.Linkifier.Linkifier;
92
92
  private infoElement: HTMLDivElement;
93
+ #scopesScript: SDK.Script.Script|null = null;
94
+
93
95
  private constructor() {
94
96
  super(true);
95
97
 
@@ -126,6 +128,34 @@ export class ScopeChainSidebarPane extends UI.Widget.VBox implements UI.ContextF
126
128
  }
127
129
  }
128
130
 
131
+ private sourceMapAttached(
132
+ event: Common.EventTarget.EventTargetEvent<{client: SDK.Script.Script, sourceMap: SDK.SourceMap.SourceMap}>):
133
+ void {
134
+ if (event.data.client === this.#scopesScript) {
135
+ void this.update();
136
+ }
137
+ }
138
+
139
+ private setScopeSourceMapSubscription(callFrame: SDK.DebuggerModel.CallFrame|null): void {
140
+ const oldScript = this.#scopesScript;
141
+ this.#scopesScript = callFrame?.script ?? null;
142
+
143
+ // Shortcut for the case when we are listening to the same model.
144
+ if (oldScript?.debuggerModel === this.#scopesScript?.debuggerModel) {
145
+ return;
146
+ }
147
+
148
+ if (oldScript) {
149
+ oldScript.debuggerModel.sourceMapManager().removeEventListener(
150
+ SDK.SourceMapManager.Events.SourceMapAttached, this.sourceMapAttached, this);
151
+ }
152
+
153
+ if (this.#scopesScript) {
154
+ this.#scopesScript.debuggerModel.sourceMapManager().addEventListener(
155
+ SDK.SourceMapManager.Events.SourceMapAttached, this.sourceMapAttached, this);
156
+ }
157
+ }
158
+
129
159
  private async update(): Promise<void> {
130
160
  // The `resolveThisObject(callFrame)` and `resolveScopeChain(callFrame)` calls
131
161
  // below may take a while to complete, so indicate to the user that something
@@ -137,6 +167,7 @@ export class ScopeChainSidebarPane extends UI.Widget.VBox implements UI.ContextF
137
167
  this.linkifier.reset();
138
168
 
139
169
  const callFrame = UI.Context.Context.instance().flavor(SDK.DebuggerModel.CallFrame);
170
+ this.setScopeSourceMapSubscription(callFrame);
140
171
  const [thisObject, scopeChain] = await Promise.all([resolveThisObject(callFrame), resolveScopeChain(callFrame)]);
141
172
  // By now the developer might have moved on, and we don't want to show stale
142
173
  // scope information, so check again that we're still on the same CallFrame.
@@ -10,9 +10,14 @@ import * as TextUtils from '../../models/text_utils/text_utils.js';
10
10
  import type * as Workspace from '../../models/workspace/workspace.js';
11
11
  import * as Protocol from '../../generated/protocol.js';
12
12
 
13
- const scopeToCachedIdentifiersMap = new WeakMap<SDK.DebuggerModel.ScopeChainEntry, Promise<Map<string, string>>>();
13
+ interface CachedScopeMap {
14
+ sourceMap: SDK.SourceMap.SourceMap|null;
15
+ identifiersPromise: Promise<Map<string, string>>;
16
+ }
14
17
 
18
+ const scopeToCachedIdentifiersMap = new WeakMap<SDK.DebuggerModel.ScopeChainEntry, CachedScopeMap>();
15
19
  const cachedMapByCallFrame = new WeakMap<SDK.DebuggerModel.CallFrame, Map<string, string>>();
20
+
16
21
  export class Identifier {
17
22
  name: string;
18
23
  lineNumber: number;
@@ -79,17 +84,17 @@ export const resolveScopeChain =
79
84
  };
80
85
 
81
86
  export const resolveScope = async(scope: SDK.DebuggerModel.ScopeChainEntry): Promise<Map<string, string>> => {
82
- let identifiersPromise = scopeToCachedIdentifiersMap.get(scope);
83
- if (!identifiersPromise) {
87
+ let cachedScopeMap = scopeToCachedIdentifiersMap.get(scope);
88
+ const script = scope.callFrame().script;
89
+ const sourceMap = Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance().sourceMapForScript(script);
90
+
91
+ if (!cachedScopeMap || cachedScopeMap.sourceMap !== sourceMap) {
84
92
  // TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration)
85
93
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
86
94
  // TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration)
87
95
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
88
- identifiersPromise = (async(): Promise<Map<any, any>> => {
96
+ const identifiersPromise = (async(): Promise<Map<any, any>> => {
89
97
  const namesMapping = new Map<string, string>();
90
- const script = scope.callFrame().script;
91
- const sourceMap =
92
- Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance().sourceMapForScript(script);
93
98
  if (sourceMap) {
94
99
  const textCache = new Map<string, TextUtils.Text.Text>();
95
100
  // Extract as much as possible from SourceMap and resolve
@@ -111,9 +116,10 @@ export const resolveScope = async(scope: SDK.DebuggerModel.ScopeChainEntry): Pro
111
116
  }
112
117
  return namesMapping;
113
118
  })();
114
- scopeToCachedIdentifiersMap.set(scope, identifiersPromise);
119
+ cachedScopeMap = {sourceMap, identifiersPromise};
120
+ scopeToCachedIdentifiersMap.set(scope, {sourceMap, identifiersPromise});
115
121
  }
116
- return await identifiersPromise;
122
+ return await cachedScopeMap.identifiersPromise;
117
123
 
118
124
  async function resolveSourceName(
119
125
  script: SDK.Script.Script, sourceMap: SDK.SourceMap.SourceMap, id: Identifier,
@@ -7,7 +7,6 @@
7
7
  .watch-expression-delete-button {
8
8
  position: absolute;
9
9
  top: 5px;
10
- cursor: pointer;
11
10
  opacity: 0%;
12
11
  }
13
12
 
@@ -144,6 +144,10 @@ const UIStrings = {
144
144
  */
145
145
  droppedFrame: 'Dropped Frame',
146
146
  /**
147
+ *@description Text in Timeline Frame Chart Data Provider of the Performance panel
148
+ */
149
+ partiallyPresentedFrame: 'Partially Presented Frame',
150
+ /**
147
151
  *@description Text for a rendering frame
148
152
  */
149
153
  frame: 'Frame',
@@ -164,6 +168,8 @@ const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
164
168
  export class TimelineFlameChartDataProvider extends Common.ObjectWrapper.ObjectWrapper<EventTypes> implements
165
169
  PerfUI.FlameChart.FlameChartDataProvider {
166
170
  private readonly font: string;
171
+ private droppedFramePatternCanvas: HTMLCanvasElement;
172
+ private partialFramePatternCanvas: HTMLCanvasElement;
167
173
  private timelineDataInternal: PerfUI.FlameChart.TimelineData|null;
168
174
  private currentLevel: number;
169
175
  private performanceModel: PerformanceModel|null;
@@ -206,6 +212,9 @@ export class TimelineFlameChartDataProvider extends Common.ObjectWrapper.ObjectW
206
212
  super();
207
213
  this.reset();
208
214
  this.font = '11px ' + Host.Platform.fontFamily();
215
+ this.droppedFramePatternCanvas = document.createElement('canvas');
216
+ this.partialFramePatternCanvas = document.createElement('canvas');
217
+ this.preparePatternCanvas();
209
218
  this.timelineDataInternal = null;
210
219
  this.currentLevel = 0;
211
220
  this.performanceModel = null;
@@ -1002,7 +1011,11 @@ export class TimelineFlameChartDataProvider extends Common.ObjectWrapper.ObjectW
1002
1011
  if (frame.idle) {
1003
1012
  title = i18nString(UIStrings.idleFrame);
1004
1013
  } else if (frame.dropped) {
1005
- title = i18nString(UIStrings.droppedFrame);
1014
+ if (frame.isPartial) {
1015
+ title = i18nString(UIStrings.partiallyPresentedFrame);
1016
+ } else {
1017
+ title = i18nString(UIStrings.droppedFrame);
1018
+ }
1006
1019
  nameSpanTimelineInfoTime = 'timeline-info-warning';
1007
1020
  } else {
1008
1021
  title = i18nString(UIStrings.frame);
@@ -1093,6 +1106,42 @@ export class TimelineFlameChartDataProvider extends Common.ObjectWrapper.ObjectW
1093
1106
  return key ? `hsl(${Platform.StringUtilities.hashCode(key) % 300 + 30}, 40%, 70%)` : '#ccc';
1094
1107
  }
1095
1108
 
1109
+ private preparePatternCanvas(): void {
1110
+ // Set the candy stripe pattern to 17px so it repeats well.
1111
+ const size = 17;
1112
+ this.droppedFramePatternCanvas.width = size;
1113
+ this.droppedFramePatternCanvas.height = size;
1114
+
1115
+ this.partialFramePatternCanvas.width = size;
1116
+ this.partialFramePatternCanvas.height = size;
1117
+
1118
+ const ctx = this.droppedFramePatternCanvas.getContext('2d');
1119
+ if (ctx) {
1120
+ // Make a dense solid-line pattern.
1121
+ ctx.translate(size * 0.5, size * 0.5);
1122
+ ctx.rotate(Math.PI * 0.25);
1123
+ ctx.translate(-size * 0.5, -size * 0.5);
1124
+
1125
+ ctx.fillStyle = 'rgb(255, 255, 255)';
1126
+ for (let x = -size; x < size * 2; x += 3) {
1127
+ ctx.fillRect(x, -size, 1, size * 3);
1128
+ }
1129
+ }
1130
+
1131
+ const ctx2 = this.partialFramePatternCanvas.getContext('2d');
1132
+ if (ctx2) {
1133
+ // Make a sparse dashed-line pattern.
1134
+ ctx2.strokeStyle = 'rgb(255, 255, 255)';
1135
+ ctx2.lineWidth = 2;
1136
+ ctx2.beginPath();
1137
+ ctx2.moveTo(17, 0);
1138
+ ctx2.lineTo(10, 7);
1139
+ ctx2.moveTo(8, 9);
1140
+ ctx2.lineTo(2, 15);
1141
+ ctx2.stroke();
1142
+ }
1143
+ }
1144
+
1096
1145
  private drawFrame(
1097
1146
  entryIndex: number, context: CanvasRenderingContext2D, text: string|null, barX: number, barY: number,
1098
1147
  barWidth: number, barHeight: number): void {
@@ -1100,8 +1149,31 @@ export class TimelineFlameChartDataProvider extends Common.ObjectWrapper.ObjectW
1100
1149
  const frame = (this.entryData[entryIndex] as TimelineModel.TimelineFrameModel.TimelineFrame);
1101
1150
  barX += hPadding;
1102
1151
  barWidth -= 2 * hPadding;
1103
- context.fillStyle =
1104
- frame.idle ? 'white' : frame.dropped ? '#f0b7b1' : (frame.hasWarnings() ? '#fad1d1' : '#d7f0d1');
1152
+ if (frame.idle) {
1153
+ context.fillStyle = 'white';
1154
+ } else if (frame.dropped) {
1155
+ if (frame.isPartial) {
1156
+ // For partially presented frame boxes, paint a yellow background with
1157
+ // a sparse white dashed-line pattern overlay.
1158
+ context.fillStyle = '#f0e442';
1159
+ context.fillRect(barX, barY, barWidth, barHeight);
1160
+
1161
+ const overlay = context.createPattern(this.partialFramePatternCanvas, 'repeat');
1162
+ context.fillStyle = overlay || context.fillStyle;
1163
+ } else {
1164
+ // For dropped frame boxes, paint a red background with a dense white
1165
+ // solid-line pattern overlay.
1166
+ context.fillStyle = '#f08080';
1167
+ context.fillRect(barX, barY, barWidth, barHeight);
1168
+
1169
+ const overlay = context.createPattern(this.droppedFramePatternCanvas, 'repeat');
1170
+ context.fillStyle = overlay || context.fillStyle;
1171
+ }
1172
+ } else if (frame.hasWarnings()) {
1173
+ context.fillStyle = '#fad1d1';
1174
+ } else {
1175
+ context.fillStyle = '#d7f0d1';
1176
+ }
1105
1177
  context.fillRect(barX, barY, barWidth, barHeight);
1106
1178
 
1107
1179
  const frameDurationText = i18n.TimeUtilities.preciseMillisToString(frame.duration, 1);
@@ -120,18 +120,6 @@
120
120
  padding: 5px 10px 0 0;
121
121
  }
122
122
 
123
- .text-button {
124
- float: right;
125
- white-space: nowrap;
126
- overflow: hidden;
127
- min-width: 28px;
128
- background: transparent;
129
- border: none;
130
- color: var(--color-link);
131
- text-decoration: underline;
132
- cursor: pointer;
133
- }
134
-
135
123
  td .text-button {
136
124
  min-width: 20px;
137
125
  margin: auto;
@@ -0,0 +1,107 @@
1
+ // Copyright (c) 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
+ import * as puppeteer from '../../third_party/puppeteer/puppeteer.js';
6
+ import type * as Protocol from '../../generated/protocol.js';
7
+ import type * as SDK from '../../core/sdk/sdk.js';
8
+
9
+ export class Transport implements puppeteer.ConnectionTransport {
10
+ #connection: SDK.Connections.ParallelConnectionInterface;
11
+ #knownIds = new Set<number>();
12
+
13
+ constructor(connection: SDK.Connections.ParallelConnectionInterface) {
14
+ this.#connection = connection;
15
+ }
16
+
17
+ send(message: string): void {
18
+ const data = JSON.parse(message);
19
+ this.#knownIds.add(data.id);
20
+ this.#connection.sendRawMessage(JSON.stringify(data));
21
+ }
22
+
23
+ close(): void {
24
+ void this.#connection.disconnect();
25
+ }
26
+
27
+ set onmessage(cb: (message: string) => void) {
28
+ this.#connection.setOnMessage((message: Object) => {
29
+ if (!cb) {
30
+ return;
31
+ }
32
+ const data = (message) as {id: number, method: string, params: unknown, sessionId?: string};
33
+ if (data.id && !this.#knownIds.has(data.id)) {
34
+ return;
35
+ }
36
+ this.#knownIds.delete(data.id);
37
+ if (!data.sessionId) {
38
+ return;
39
+ }
40
+ return cb(JSON.stringify({
41
+ ...data,
42
+ // Puppeteer is expecting to use the default session, but we give it a non-default session in #connection.
43
+ // Replace that sessionId with undefined so Puppeteer treats it as default.
44
+ sessionId: data.sessionId === this.#connection.getSessionId() ? undefined : data.sessionId,
45
+ }));
46
+ });
47
+ }
48
+
49
+ set onclose(cb: () => void) {
50
+ const prev = this.#connection.getOnDisconnect();
51
+ this.#connection.setOnDisconnect(reason => {
52
+ if (prev) {
53
+ prev(reason);
54
+ }
55
+ if (cb) {
56
+ cb();
57
+ }
58
+ });
59
+ }
60
+ }
61
+
62
+ export class PuppeteerConnection extends puppeteer.Connection {
63
+ // Overriding Puppeteer's API here.
64
+ // eslint-disable-next-line rulesdir/no_underscored_properties
65
+ async _onMessage(message: string): Promise<void> {
66
+ const msgObj = JSON.parse(message) as {id: number, method: string, params: unknown, sessionId?: string};
67
+ if (msgObj.sessionId && !this._sessions.has(msgObj.sessionId)) {
68
+ return;
69
+ }
70
+ void super._onMessage(message);
71
+ }
72
+ }
73
+
74
+ export async function getPuppeteerConnection(
75
+ rawConnection: SDK.Connections.ParallelConnectionInterface,
76
+ mainFrameId: string,
77
+ mainTargetId: string,
78
+ ): Promise<{page: puppeteer.Page | null, browser: puppeteer.Browser}> {
79
+ const transport = new Transport(rawConnection);
80
+
81
+ // url is an empty string in this case parallel to:
82
+ // https://github.com/puppeteer/puppeteer/blob/f63a123ecef86693e6457b07437a96f108f3e3c5/src/common/BrowserConnector.ts#L72
83
+ const connection = new PuppeteerConnection('', transport);
84
+
85
+ const targetFilterCallback = (targetInfo: Protocol.Target.TargetInfo): boolean => {
86
+ if (targetInfo.type !== 'page' && targetInfo.type !== 'iframe') {
87
+ return false;
88
+ }
89
+ // TODO only connect to iframes that are related to the main target. This requires refactoring in Puppeteer: https://github.com/puppeteer/puppeteer/issues/3667.
90
+ return targetInfo.targetId === mainTargetId || targetInfo.openerId === mainTargetId || targetInfo.type === 'iframe';
91
+ };
92
+
93
+ const browser = await puppeteer.Browser.create(
94
+ connection,
95
+ [] /* contextIds */,
96
+ false /* ignoreHTTPSErrors */,
97
+ undefined /* defaultViewport */,
98
+ undefined /* process */,
99
+ undefined /* closeCallback */,
100
+ targetFilterCallback,
101
+ );
102
+
103
+ const pages = await browser.pages();
104
+ const page = pages.find(p => p.mainFrame()._id === mainFrameId) || null;
105
+
106
+ return {page, browser};
107
+ }
@@ -0,0 +1,9 @@
1
+ // Copyright (c) 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
+ import * as PuppeteerConnection from './PuppeteerConnection.js';
6
+
7
+ export {
8
+ PuppeteerConnection,
9
+ };
@@ -3,15 +3,11 @@
3
3
  text-shadow: #b9f 1px 1px 2px, #b9f -1px -1px 2px, #b9f 1px -1px 2px, #b9f -1px 1px 2px;
4
4
  font-family: arial;
5
5
  line-height: .3;
6
- cursor: pointer;
7
6
  }
8
7
  .CodeMirror-foldgutter {
9
8
  width: .7em;
10
9
  }
11
- .CodeMirror-foldgutter-open,
12
- .CodeMirror-foldgutter-folded {
13
- cursor: pointer;
14
- }
10
+
15
11
  .CodeMirror-foldgutter-open:after {
16
12
  content: "\25BE";
17
13
  }
@@ -16,3 +16,13 @@ To update this package, adjust the version ranges in package.json if necessary,
16
16
  ```
17
17
 
18
18
  It will use npm to locally install the necessary packages and their dependencies, and bundle them into a single file (codemirror.js and codemirror.d.ts) with rollup.
19
+
20
+ If you want to easily obtain all the latest versions of the packages, run the following:
21
+
22
+ ```
23
+ npm i
24
+ npm outdated
25
+ ```
26
+
27
+ This will show you a table with all the latest versions of each package.
28
+ You will need to manually update the versions in the `package.json` file.