chrome-devtools-frontend 1.0.969345 → 1.0.969882

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.
@@ -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);
@@ -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
+ };
@@ -37,8 +37,8 @@ export const markdownLinks = new Map<string, string>([
37
37
  ],
38
38
  ['https://developer.chrome.com/docs/extensions/mv3/', 'https://developer.chrome.com/docs/extensions/mv3/'],
39
39
  [
40
- 'https://developer.chrome.com/blog/immutable-document-domain',
41
- 'https://developer.chrome.com/blog/immutable-document-domain',
40
+ 'https://developer.chrome.com/blog/immutable-document-domain/',
41
+ 'https://developer.chrome.com/blog/immutable-document-domain/',
42
42
  ],
43
43
  ]);
44
44
 
package/package.json CHANGED
@@ -23,7 +23,7 @@
23
23
  "auto-debug-interactionstest": "autoninja -C out/Default && npm run debug-interactionstest --",
24
24
  "auto-debug-unittest": "DEBUG_TEST=1 npm run auto-unittest --",
25
25
  "auto-e2etest": "autoninja -C out/Default && npm run e2etest --",
26
- "auto-interactionstest": "autoninja -C out/Default && npm run interactionstest",
26
+ "auto-interactionstest": "autoninja -C out/Default && npm run interactionstest --",
27
27
  "auto-unittest": "scripts/test/run_auto_unittests.py --no-text-coverage",
28
28
  "build": "autoninja -C out/Default",
29
29
  "build-release": "autoninja -C out/Release",
@@ -54,5 +54,5 @@
54
54
  "unittest": "scripts/test/run_unittests.py --no-text-coverage",
55
55
  "watch": "third_party/node/node.py --output scripts/watch_build.js"
56
56
  },
57
- "version": "1.0.969345"
57
+ "version": "1.0.969882"
58
58
  }
@@ -172,6 +172,11 @@ async function requestHandler(request, response) {
172
172
  headers.set('Content-Type', inferredContentType);
173
173
  }
174
174
  }
175
+ if (!headers.get('Cache-Control')) {
176
+ // Lets reduce Disk I/O by allowing clients to cache resources.
177
+ // This is fine to do given that test invocations run against fresh Chrome profiles.
178
+ headers.set('Cache-Control', 'max-age=3600');
179
+ }
175
180
  headers.forEach((value, header) => {
176
181
  response.setHeader(header, value);
177
182
  });