chrome-devtools-frontend 1.0.1667564 → 1.0.1668390
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.
- package/.agents/skills/devtools-setting-migration/SKILL.md +290 -0
- package/.agents/skills/ui-eng-vision-widget-promoter/SKILL.md +11 -4
- package/.agents/skills/ui-widgets/SKILL.md +25 -1
- package/docs/ui_engineering.md +44 -1
- package/front_end/core/common/Settings.ts +11 -4
- package/front_end/core/root/Runtime.ts +5 -0
- package/front_end/core/sdk/CSSModel.test.api.ts +102 -0
- package/front_end/core/sdk/CSSModel.ts +3 -2
- package/front_end/core/sdk/DebuggerModel.ts +3 -2
- package/front_end/core/sdk/SDKSettings.ts +19 -0
- package/front_end/core/sdk/sdk-meta.ts +0 -62
- package/front_end/core/sdk/sdk.ts +2 -0
- package/front_end/entrypoints/main/MainImpl.ts +10 -3
- package/front_end/foundation/Universe.ts +7 -0
- package/front_end/models/ai_assistance/AiAgent2.ts +1 -1
- package/front_end/models/ai_assistance/BuiltInAi.ts +11 -6
- package/front_end/models/ai_assistance/ChangeManager.ts +17 -1
- package/front_end/models/javascript_metadata/NativeFunctions.js +22 -9
- package/front_end/models/source_map_scopes/NamesResolver.ts +23 -17
- package/front_end/panels/application/ServiceWorkerUpdateCycleView.ts +78 -118
- package/front_end/panels/application/preloading/components/preloadingDetailsReportView.css +13 -13
- package/front_end/panels/console/ConsoleInsightTeaser.ts +3 -2
- package/front_end/panels/emulation/DeviceModeToolbar.ts +2 -2
- package/front_end/panels/emulation/DeviceModeView.ts +63 -104
- package/front_end/panels/sensors/LocationsSettingsTab.ts +244 -34
- package/front_end/panels/sensors/locationsSettingsTab.css +74 -0
- package/front_end/panels/sources/DebuggerPlugin.ts +2 -2
- package/front_end/panels/sources/sources-meta.ts +55 -0
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/ui/legacy/Dialog.ts +93 -4
- package/front_end/ui/legacy/InspectorView.ts +1 -5
- package/front_end/ui/legacy/PopoverHelper.ts +5 -5
- package/front_end/ui/visual_logging/KnownContextValues.ts +1 -0
- package/package.json +1 -1
|
@@ -10,21 +10,21 @@
|
|
|
10
10
|
|
|
11
11
|
devtools-report {
|
|
12
12
|
flex-grow: 1;
|
|
13
|
-
}
|
|
14
13
|
|
|
15
|
-
button.link {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
14
|
+
button.link {
|
|
15
|
+
color: var(--sys-color-primary);
|
|
16
|
+
text-decoration: underline;
|
|
17
|
+
padding: 0;
|
|
18
|
+
border: none;
|
|
19
|
+
background: none;
|
|
20
|
+
font-family: inherit;
|
|
21
|
+
font-size: inherit;
|
|
22
|
+
height: 16px;
|
|
23
|
+
}
|
|
25
24
|
|
|
26
|
-
button.link devtools-icon {
|
|
27
|
-
|
|
25
|
+
button.link devtools-icon {
|
|
26
|
+
vertical-align: sub;
|
|
27
|
+
}
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
.link {
|
|
@@ -420,7 +420,8 @@ export class ConsoleInsightTeaser extends UI.Widget.Widget {
|
|
|
420
420
|
#callShowTooltip = false;
|
|
421
421
|
#startTime = 0;
|
|
422
422
|
|
|
423
|
-
constructor(uuid: string, consoleViewMessage: ConsoleViewMessage, element?: HTMLElement, view?: View
|
|
423
|
+
constructor(uuid: string, consoleViewMessage: ConsoleViewMessage, element?: HTMLElement, view?: View,
|
|
424
|
+
builtInAi: AiAssistanceModel.BuiltInAi.BuiltInAi = AiAssistanceModel.BuiltInAi.BuiltInAi.instance()) {
|
|
424
425
|
super(element);
|
|
425
426
|
this.#view = view ?? DEFAULT_VIEW;
|
|
426
427
|
this.#uuid = uuid;
|
|
@@ -430,7 +431,7 @@ export class ConsoleInsightTeaser extends UI.Widget.Widget {
|
|
|
430
431
|
this.#boundOnAidaAvailabilityChange = this.#onAidaAvailabilityChange.bind(this);
|
|
431
432
|
this.#boundOnDownloadProgressChange = this.#onDownloadProgressChange.bind(this);
|
|
432
433
|
this.#boundOnSessionCreation = this.#onSessionCreation.bind(this);
|
|
433
|
-
this.#builtInAi =
|
|
434
|
+
this.#builtInAi = builtInAi;
|
|
434
435
|
this.#state = this.#builtInAi.hasSession() ? State.READY : State.NO_MODEL;
|
|
435
436
|
this.#callShowTooltip = true;
|
|
436
437
|
this.requestUpdate();
|
|
@@ -512,8 +512,8 @@ export class DeviceModeToolbar extends UI.Widget.Widget {
|
|
|
512
512
|
super.wasShown();
|
|
513
513
|
// TODO(crbug.com/407750803): Revisit once DeviceModeView is migrated.
|
|
514
514
|
this.performUpdate(); // Trigger a manual update eagerly, DeviceModeView needs to measure our height.
|
|
515
|
+
this.restore();
|
|
515
516
|
}
|
|
516
|
-
|
|
517
517
|
override performUpdate(): void {
|
|
518
518
|
if (!this.model) {
|
|
519
519
|
return;
|
|
@@ -1078,7 +1078,7 @@ export class DeviceModeToolbar extends UI.Widget.Widget {
|
|
|
1078
1078
|
return !this.model ? '' : `${(this.model.scale() * 100).toFixed(0)}`;
|
|
1079
1079
|
}
|
|
1080
1080
|
|
|
1081
|
-
restore(): void {
|
|
1081
|
+
private restore(): void {
|
|
1082
1082
|
if (!this.model) {
|
|
1083
1083
|
return;
|
|
1084
1084
|
}
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
// Copyright 2015 The Chromium Authors
|
|
2
2
|
// Use of this source code is governed by a BSD-style license that can be
|
|
3
3
|
// found in the LICENSE file.
|
|
4
|
-
/* eslint-disable @devtools/no-imperative-dom-api */
|
|
5
4
|
|
|
6
5
|
import * as Common from '../../core/common/common.js';
|
|
7
6
|
import * as Host from '../../core/host/host.js';
|
|
8
7
|
import * as i18n from '../../core/i18n/i18n.js';
|
|
9
8
|
import * as Platform from '../../core/platform/platform.js';
|
|
9
|
+
import * as TextUtils from '../../core/text_utils/text_utils.js';
|
|
10
10
|
import type * as Protocol from '../../generated/protocol.js';
|
|
11
11
|
import * as EmulationModel from '../../models/emulation/emulation.js';
|
|
12
12
|
import * as Geometry from '../../models/geometry/geometry.js';
|
|
13
|
+
import * as Workspace from '../../models/workspace/workspace.js';
|
|
13
14
|
import * as UI from '../../ui/legacy/legacy.js';
|
|
14
15
|
import {Directives, html, nothing, render} from '../../ui/lit/lit.js';
|
|
15
16
|
import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
|
|
@@ -60,16 +61,6 @@ const UIStrings = {
|
|
|
60
61
|
const str_ = i18n.i18n.registerUIStrings('panels/emulation/DeviceModeView.ts', UIStrings);
|
|
61
62
|
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
62
63
|
|
|
63
|
-
export interface DeviceModeViewRefs {
|
|
64
|
-
toolbar: DeviceModeToolbar;
|
|
65
|
-
bottomRightResizerElement: HTMLElement;
|
|
66
|
-
bottomLeftResizerElement: HTMLElement;
|
|
67
|
-
rightResizerElement: HTMLElement;
|
|
68
|
-
leftResizerElement: HTMLElement;
|
|
69
|
-
bottomResizerElement: HTMLElement;
|
|
70
|
-
pageArea: HTMLElement;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
64
|
export interface DeviceModeViewInput {
|
|
74
65
|
model: EmulationModel.DeviceModeModel.DeviceModeModel;
|
|
75
66
|
showMediaInspectorSetting: Common.Settings.Setting<boolean>;
|
|
@@ -78,10 +69,12 @@ export interface DeviceModeViewInput {
|
|
|
78
69
|
outlineImageLoaded: boolean;
|
|
79
70
|
screenImage: string;
|
|
80
71
|
screenImageLoaded: boolean;
|
|
72
|
+
resizable: boolean;
|
|
81
73
|
showRulers: boolean;
|
|
82
74
|
showMediaInspector: boolean;
|
|
83
75
|
scale: number;
|
|
84
76
|
cachedCssScreenRect?: EmulationModel.DeviceModeModel.Rect;
|
|
77
|
+
cachedCssVisiblePageRect?: EmulationModel.DeviceModeModel.Rect;
|
|
85
78
|
cachedOutlineRect?: EmulationModel.DeviceModeModel.Rect;
|
|
86
79
|
onApplyPresetSize: (size: number, e: Event) => void;
|
|
87
80
|
bottomRightResizer: UI.ResizerWidget.ResizerWidget;
|
|
@@ -101,7 +94,7 @@ export interface DeviceModeViewInput {
|
|
|
101
94
|
|
|
102
95
|
export type DeviceModeViewView = (
|
|
103
96
|
input: DeviceModeViewInput,
|
|
104
|
-
output:
|
|
97
|
+
output: undefined,
|
|
105
98
|
target: HTMLElement,
|
|
106
99
|
) => void;
|
|
107
100
|
|
|
@@ -130,7 +123,7 @@ function resizerRef(
|
|
|
130
123
|
|
|
131
124
|
export const DEFAULT_DEVICE_MODE_VIEW: DeviceModeViewView = (
|
|
132
125
|
input: DeviceModeViewInput,
|
|
133
|
-
|
|
126
|
+
_output: undefined,
|
|
134
127
|
target: HTMLElement,
|
|
135
128
|
): void => {
|
|
136
129
|
const sizes = [320, 375, 425, 768, 1024, 1440, 2560];
|
|
@@ -145,8 +138,7 @@ export const DEFAULT_DEVICE_MODE_VIEW: DeviceModeViewView = (
|
|
|
145
138
|
];
|
|
146
139
|
// clang-format off
|
|
147
140
|
render(html`
|
|
148
|
-
|
|
149
|
-
${UI.Widget.widgetRef(DeviceModeToolbar, t => { output.toolbar = t; })}></devtools-widget>
|
|
141
|
+
${UI.Widget.widget(DeviceModeToolbar, {model: input.model})}
|
|
150
142
|
<div class=${classMap({
|
|
151
143
|
'device-mode-content-clip': true,
|
|
152
144
|
vbox: true,
|
|
@@ -199,33 +191,44 @@ export const DEFAULT_DEVICE_MODE_VIEW: DeviceModeViewView = (
|
|
|
199
191
|
@load=${(): void => input.onScreenImageLoaded(true)}
|
|
200
192
|
@error=${(): void => input.onScreenImageLoaded(false)}>
|
|
201
193
|
<div class="device-mode-resizer device-mode-bottom-right-resizer"
|
|
194
|
+
?hidden=${!input.resizable}
|
|
202
195
|
jslog=${VisualLogging.slider('device-mode-resizer').track({drag: true})}
|
|
203
196
|
${ref(input.bottomRightResizerRef)}>
|
|
204
197
|
<div></div>
|
|
205
198
|
</div>
|
|
206
199
|
<div class="device-mode-resizer device-mode-bottom-left-resizer"
|
|
200
|
+
?hidden=${!input.resizable}
|
|
207
201
|
jslog=${VisualLogging.slider('device-mode-resizer').track({drag: true})}
|
|
208
202
|
${ref(input.bottomLeftResizerRef)}>
|
|
209
203
|
<div></div>
|
|
210
204
|
</div>
|
|
211
205
|
<div class="device-mode-resizer device-mode-right-resizer"
|
|
206
|
+
?hidden=${!input.resizable}
|
|
212
207
|
jslog=${VisualLogging.slider('device-mode-resizer').track({drag: true})}
|
|
213
208
|
${ref(input.rightResizerRef)}>
|
|
214
209
|
<div></div>
|
|
215
210
|
</div>
|
|
216
211
|
<div class="device-mode-resizer device-mode-left-resizer"
|
|
212
|
+
?hidden=${!input.resizable}
|
|
217
213
|
jslog=${VisualLogging.slider('device-mode-resizer').track({drag: true})}
|
|
218
214
|
${ref(input.leftResizerRef)}>
|
|
219
215
|
<div></div>
|
|
220
216
|
</div>
|
|
221
217
|
<div class="device-mode-resizer device-mode-bottom-resizer"
|
|
218
|
+
?hidden=${!input.resizable}
|
|
222
219
|
jslog=${VisualLogging.slider('device-mode-resizer').track({drag: true})}
|
|
223
220
|
title=${i18nString(UIStrings.doubleclickForFullHeight)}
|
|
224
221
|
${ref(input.bottomResizerRef)}
|
|
225
222
|
@dblclick=${input.onDoubleclickBottomResizer}>
|
|
226
223
|
<div></div>
|
|
227
224
|
</div>
|
|
228
|
-
<div class="device-mode-page-area"
|
|
225
|
+
<div class="device-mode-page-area"
|
|
226
|
+
style=${styleMap(input.cachedCssVisiblePageRect ? {
|
|
227
|
+
left: `${input.cachedCssVisiblePageRect.left}px`,
|
|
228
|
+
top: `${input.cachedCssVisiblePageRect.top}px`,
|
|
229
|
+
width: `${input.cachedCssVisiblePageRect.width}px`,
|
|
230
|
+
height: `${input.cachedCssVisiblePageRect.height}px`,
|
|
231
|
+
} : {})}><slot></slot></div>
|
|
229
232
|
</div>
|
|
230
233
|
${input.showRulers ? html`
|
|
231
234
|
<devtools-widget class="device-mode-ruler-top device-mode-ruler"
|
|
@@ -247,7 +250,11 @@ export const DEFAULT_DEVICE_MODE_VIEW: DeviceModeViewView = (
|
|
|
247
250
|
` : nothing}
|
|
248
251
|
</div>
|
|
249
252
|
</div>
|
|
250
|
-
`, target
|
|
253
|
+
`, target, {
|
|
254
|
+
container: {
|
|
255
|
+
classes: ['device-mode-view'],
|
|
256
|
+
},
|
|
257
|
+
});
|
|
251
258
|
// clang-format on
|
|
252
259
|
};
|
|
253
260
|
|
|
@@ -256,34 +263,16 @@ export class DeviceModeView extends UI.Widget.VBox {
|
|
|
256
263
|
private model: EmulationModel.DeviceModeModel.DeviceModeModel;
|
|
257
264
|
private showMediaInspectorSetting: Common.Settings.Setting<boolean>;
|
|
258
265
|
private showRulersSetting: Common.Settings.Setting<boolean>;
|
|
259
|
-
pageArea!: HTMLElement;
|
|
260
|
-
rightResizerElement!: HTMLElement;
|
|
261
|
-
leftResizerElement!: HTMLElement;
|
|
262
|
-
bottomResizerElement!: HTMLElement;
|
|
263
|
-
bottomRightResizerElement!: HTMLElement;
|
|
264
|
-
bottomLeftResizerElement!: HTMLElement;
|
|
265
266
|
private readonly bottomRightResizer = this.createResizer(2, 1);
|
|
266
267
|
private readonly bottomLeftResizer = this.createResizer(-2, 1);
|
|
267
268
|
private readonly rightResizer = this.createResizer(2, 0);
|
|
268
269
|
private readonly leftResizer = this.createResizer(-2, 0);
|
|
269
270
|
private readonly bottomResizer = this.createResizer(0, 1);
|
|
270
|
-
private readonly bottomRightResizerRef = resizerRef(this.bottomRightResizer
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
private readonly
|
|
274
|
-
|
|
275
|
-
});
|
|
276
|
-
private readonly rightResizerRef = resizerRef(this.rightResizer, el => {
|
|
277
|
-
this.rightResizerElement = el;
|
|
278
|
-
});
|
|
279
|
-
private readonly leftResizerRef = resizerRef(this.leftResizer, el => {
|
|
280
|
-
this.leftResizerElement = el;
|
|
281
|
-
});
|
|
282
|
-
private readonly bottomResizerRef = resizerRef(this.bottomResizer, el => {
|
|
283
|
-
this.bottomResizerElement = el;
|
|
284
|
-
});
|
|
285
|
-
private cachedResizable!: boolean|undefined;
|
|
286
|
-
toolbar!: DeviceModeToolbar;
|
|
271
|
+
private readonly bottomRightResizerRef = resizerRef(this.bottomRightResizer);
|
|
272
|
+
private readonly bottomLeftResizerRef = resizerRef(this.bottomLeftResizer);
|
|
273
|
+
private readonly rightResizerRef = resizerRef(this.rightResizer);
|
|
274
|
+
private readonly leftResizerRef = resizerRef(this.leftResizer);
|
|
275
|
+
private readonly bottomResizerRef = resizerRef(this.bottomResizer);
|
|
287
276
|
private slowPositionStart?: {
|
|
288
277
|
x: number,
|
|
289
278
|
y: number,
|
|
@@ -295,8 +284,6 @@ export class DeviceModeView extends UI.Widget.VBox {
|
|
|
295
284
|
private cachedMediaInspectorVisible?: boolean;
|
|
296
285
|
private cachedShowRulers?: boolean;
|
|
297
286
|
private cachedScale?: number;
|
|
298
|
-
private handleWidth?: number;
|
|
299
|
-
private handleHeight?: number;
|
|
300
287
|
#outlineImageLoaded = false;
|
|
301
288
|
#lastOutlineImageSrc?: string;
|
|
302
289
|
#screenImageLoaded = false;
|
|
@@ -308,7 +295,6 @@ export class DeviceModeView extends UI.Widget.VBox {
|
|
|
308
295
|
this.#view = view;
|
|
309
296
|
|
|
310
297
|
this.setMinimumSize(150, 150);
|
|
311
|
-
this.element.classList.add('device-mode-view');
|
|
312
298
|
this.registerRequiredCSS(deviceModeViewStyles);
|
|
313
299
|
|
|
314
300
|
this.model = EmulationModel.DeviceModeModel.DeviceModeModel.instance();
|
|
@@ -339,11 +325,13 @@ export class DeviceModeView extends UI.Widget.VBox {
|
|
|
339
325
|
outlineImageLoaded: this.#outlineImageLoaded,
|
|
340
326
|
screenImage: this.model.screenImage(),
|
|
341
327
|
screenImageLoaded: this.#screenImageLoaded,
|
|
328
|
+
resizable: this.model.type() === EmulationModel.DeviceModeModel.Type.Responsive,
|
|
342
329
|
showRulers: this.showRulersSetting.get() && this.model.type() !== EmulationModel.DeviceModeModel.Type.None,
|
|
343
330
|
showMediaInspector:
|
|
344
331
|
this.showMediaInspectorSetting.get() && this.model.type() !== EmulationModel.DeviceModeModel.Type.None,
|
|
345
332
|
scale: this.model.scale(),
|
|
346
333
|
cachedCssScreenRect: this.cachedCssScreenRect,
|
|
334
|
+
cachedCssVisiblePageRect: this.cachedCssVisiblePageRect,
|
|
347
335
|
cachedOutlineRect: this.cachedOutlineRect,
|
|
348
336
|
onApplyPresetSize: (width: number, e: Event): void => {
|
|
349
337
|
this.model.emulate(EmulationModel.DeviceModeModel.Type.Responsive, null, null);
|
|
@@ -364,7 +352,7 @@ export class DeviceModeView extends UI.Widget.VBox {
|
|
|
364
352
|
onOutlineImageLoaded: (success: boolean): void => this.onOutlineImageLoaded(success),
|
|
365
353
|
onScreenImageLoaded: (success: boolean): void => this.onScreenImageLoaded(success),
|
|
366
354
|
};
|
|
367
|
-
this.#view(input,
|
|
355
|
+
this.#view(input, undefined, this.contentElement);
|
|
368
356
|
}
|
|
369
357
|
|
|
370
358
|
private onOutlineImageLoaded(success: boolean): void {
|
|
@@ -380,6 +368,7 @@ export class DeviceModeView extends UI.Widget.VBox {
|
|
|
380
368
|
this.requestUpdate();
|
|
381
369
|
}
|
|
382
370
|
}
|
|
371
|
+
|
|
383
372
|
private createResizer(widthFactor: number, heightFactor: number): UI.ResizerWidget.ResizerWidget {
|
|
384
373
|
const resizer = new UI.ResizerWidget.ResizerWidget();
|
|
385
374
|
let cursor: 'nwse-resize'|'nesw-resize'|('ew-resize' | 'ns-resize') = widthFactor ? 'ew-resize' : 'ns-resize';
|
|
@@ -450,13 +439,6 @@ export class DeviceModeView extends UI.Widget.VBox {
|
|
|
450
439
|
}
|
|
451
440
|
|
|
452
441
|
private updateUI(): void {
|
|
453
|
-
function applyRect(element: HTMLElement, rect: EmulationModel.DeviceModeModel.Rect): void {
|
|
454
|
-
element.style.left = rect.left + 'px';
|
|
455
|
-
element.style.top = rect.top + 'px';
|
|
456
|
-
element.style.width = rect.width + 'px';
|
|
457
|
-
element.style.height = rect.height + 'px';
|
|
458
|
-
}
|
|
459
|
-
|
|
460
442
|
if (!this.isShowing()) {
|
|
461
443
|
return;
|
|
462
444
|
}
|
|
@@ -474,7 +456,6 @@ export class DeviceModeView extends UI.Widget.VBox {
|
|
|
474
456
|
|
|
475
457
|
const cssVisiblePageRect = this.model.visiblePageRect().scale(1 / zoomFactor);
|
|
476
458
|
if (!this.cachedCssVisiblePageRect || !cssVisiblePageRect.isEqual(this.cachedCssVisiblePageRect)) {
|
|
477
|
-
applyRect(this.pageArea, cssVisiblePageRect);
|
|
478
459
|
callDoResize = true;
|
|
479
460
|
this.cachedCssVisiblePageRect = cssVisiblePageRect;
|
|
480
461
|
}
|
|
@@ -488,16 +469,6 @@ export class DeviceModeView extends UI.Widget.VBox {
|
|
|
488
469
|
}
|
|
489
470
|
}
|
|
490
471
|
|
|
491
|
-
const resizable = this.model.type() === EmulationModel.DeviceModeModel.Type.Responsive;
|
|
492
|
-
if (resizable !== this.cachedResizable) {
|
|
493
|
-
this.rightResizerElement.classList.toggle('hidden', !resizable);
|
|
494
|
-
this.leftResizerElement.classList.toggle('hidden', !resizable);
|
|
495
|
-
this.bottomResizerElement.classList.toggle('hidden', !resizable);
|
|
496
|
-
this.bottomRightResizerElement.classList.toggle('hidden', !resizable);
|
|
497
|
-
this.bottomLeftResizerElement.classList.toggle('hidden', !resizable);
|
|
498
|
-
this.cachedResizable = resizable;
|
|
499
|
-
}
|
|
500
|
-
|
|
501
472
|
const mediaInspectorVisible =
|
|
502
473
|
this.showMediaInspectorSetting.get() && this.model.type() !== EmulationModel.DeviceModeModel.Type.None;
|
|
503
474
|
if (mediaInspectorVisible !== this.cachedMediaInspectorVisible) {
|
|
@@ -518,7 +489,6 @@ export class DeviceModeView extends UI.Widget.VBox {
|
|
|
518
489
|
}
|
|
519
490
|
|
|
520
491
|
this.requestUpdate();
|
|
521
|
-
this.toolbar.requestUpdate();
|
|
522
492
|
if (callDoResize) {
|
|
523
493
|
this.doResize();
|
|
524
494
|
}
|
|
@@ -545,29 +515,18 @@ export class DeviceModeView extends UI.Widget.VBox {
|
|
|
545
515
|
}
|
|
546
516
|
const zoomFactor = UI.ZoomManager.ZoomManager.instance().zoomFactor();
|
|
547
517
|
const rect = contentArea.getBoundingClientRect();
|
|
518
|
+
const handleWidth = this.contentElement.querySelector<HTMLElement>('.device-mode-right-resizer')?.offsetWidth || 20;
|
|
519
|
+
const handleHeight =
|
|
520
|
+
this.contentElement.querySelector<HTMLElement>('.device-mode-bottom-resizer')?.offsetHeight || 20;
|
|
548
521
|
const availableSize =
|
|
549
522
|
new Geometry.Size(Math.max(rect.width * zoomFactor, 1), Math.max(rect.height * zoomFactor, 1));
|
|
550
|
-
const preferredSize = new Geometry.Size(
|
|
551
|
-
|
|
552
|
-
Math.max((rect.height - (this.handleHeight || 0)) * zoomFactor, 1));
|
|
523
|
+
const preferredSize = new Geometry.Size(Math.max((rect.width - 2 * handleWidth) * zoomFactor, 1),
|
|
524
|
+
Math.max((rect.height - handleHeight) * zoomFactor, 1));
|
|
553
525
|
this.model.setAvailableSize(availableSize, preferredSize);
|
|
554
526
|
}
|
|
555
527
|
|
|
556
|
-
private measureHandles(): void {
|
|
557
|
-
const hidden = this.rightResizerElement.classList.contains('hidden');
|
|
558
|
-
this.rightResizerElement.classList.toggle('hidden', false);
|
|
559
|
-
this.bottomResizerElement.classList.toggle('hidden', false);
|
|
560
|
-
this.handleWidth = this.rightResizerElement.offsetWidth;
|
|
561
|
-
this.handleHeight = this.bottomResizerElement.offsetHeight;
|
|
562
|
-
this.rightResizerElement.classList.toggle('hidden', hidden);
|
|
563
|
-
this.bottomResizerElement.classList.toggle('hidden', hidden);
|
|
564
|
-
}
|
|
565
|
-
|
|
566
528
|
private zoomChanged(): void {
|
|
567
|
-
delete this.handleWidth;
|
|
568
|
-
delete this.handleHeight;
|
|
569
529
|
if (this.isShowing()) {
|
|
570
|
-
this.measureHandles();
|
|
571
530
|
this.contentAreaResized();
|
|
572
531
|
}
|
|
573
532
|
}
|
|
@@ -580,8 +539,6 @@ export class DeviceModeView extends UI.Widget.VBox {
|
|
|
580
539
|
|
|
581
540
|
override wasShown(): void {
|
|
582
541
|
super.wasShown();
|
|
583
|
-
this.measureHandles();
|
|
584
|
-
this.toolbar.restore();
|
|
585
542
|
}
|
|
586
543
|
|
|
587
544
|
override willHide(): void {
|
|
@@ -609,11 +566,10 @@ export class DeviceModeView extends UI.Widget.VBox {
|
|
|
609
566
|
const contentLeft = screenRect.left + visiblePageRect.left - outlineRect.left;
|
|
610
567
|
const contentTop = screenRect.top + visiblePageRect.top - outlineRect.top;
|
|
611
568
|
|
|
612
|
-
const canvas =
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
canvas.height = Math.min((1 << 14), Math.floor(outlineRect.height));
|
|
569
|
+
const canvas = new OffscreenCanvas(Math.floor(outlineRect.width),
|
|
570
|
+
// Cap the height to not hit the GPU limit.
|
|
571
|
+
// https://crbug.com/1260828
|
|
572
|
+
Math.min((1 << 14), Math.floor(outlineRect.height)));
|
|
617
573
|
const ctx = canvas.getContext('2d', {willReadFrequently: true});
|
|
618
574
|
if (!ctx) {
|
|
619
575
|
throw new Error('Could not get 2d context from canvas.');
|
|
@@ -627,7 +583,7 @@ export class DeviceModeView extends UI.Widget.VBox {
|
|
|
627
583
|
await this.paintImage(ctx, this.model.screenImage(), screenRect.relativeTo(outlineRect));
|
|
628
584
|
}
|
|
629
585
|
ctx.drawImage(pageImage, Math.floor(contentLeft), Math.floor(contentTop));
|
|
630
|
-
this.saveScreenshot(
|
|
586
|
+
void this.saveScreenshot(canvas);
|
|
631
587
|
};
|
|
632
588
|
}
|
|
633
589
|
|
|
@@ -651,23 +607,22 @@ export class DeviceModeView extends UI.Widget.VBox {
|
|
|
651
607
|
const pageImage = new Image();
|
|
652
608
|
pageImage.src = 'data:image/png;base64,' + screenshot;
|
|
653
609
|
pageImage.onload = () => {
|
|
654
|
-
const canvas =
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
canvas.height = Math.min((1 << 14), Math.floor(pageImage.naturalHeight));
|
|
610
|
+
const canvas = new OffscreenCanvas(pageImage.naturalWidth,
|
|
611
|
+
// Cap the height to not hit the GPU limit.
|
|
612
|
+
// https://crbug.com/1260828
|
|
613
|
+
Math.min((1 << 14), Math.floor(pageImage.naturalHeight)));
|
|
659
614
|
const ctx = canvas.getContext('2d', {willReadFrequently: true});
|
|
660
615
|
if (!ctx) {
|
|
661
616
|
throw new Error('Could not get 2d context for base64 screenshot.');
|
|
662
617
|
}
|
|
663
618
|
ctx.imageSmoothingEnabled = false;
|
|
664
619
|
ctx.drawImage(pageImage, 0, 0);
|
|
665
|
-
this.saveScreenshot(
|
|
620
|
+
void this.saveScreenshot(canvas);
|
|
666
621
|
};
|
|
667
622
|
}
|
|
668
623
|
|
|
669
|
-
private paintImage(ctx: CanvasRenderingContext2D, src: string,
|
|
670
|
-
|
|
624
|
+
private paintImage(ctx: OffscreenCanvasRenderingContext2D|CanvasRenderingContext2D, src: string,
|
|
625
|
+
rect: EmulationModel.DeviceModeModel.Rect): Promise<void> {
|
|
671
626
|
return new Promise(resolve => {
|
|
672
627
|
const image = new Image();
|
|
673
628
|
image.crossOrigin = 'Anonymous';
|
|
@@ -680,7 +635,7 @@ export class DeviceModeView extends UI.Widget.VBox {
|
|
|
680
635
|
});
|
|
681
636
|
}
|
|
682
637
|
|
|
683
|
-
private saveScreenshot(canvas:
|
|
638
|
+
private async saveScreenshot(canvas: OffscreenCanvas): Promise<void> {
|
|
684
639
|
const url = this.model.inspectedURL();
|
|
685
640
|
let fileName = '';
|
|
686
641
|
if (url) {
|
|
@@ -692,15 +647,19 @@ export class DeviceModeView extends UI.Widget.VBox {
|
|
|
692
647
|
if (device && this.model.type() === EmulationModel.DeviceModeModel.Type.Device) {
|
|
693
648
|
fileName += `(${device.title})`;
|
|
694
649
|
}
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
link.click();
|
|
650
|
+
fileName += '.png';
|
|
651
|
+
const blob = await canvas.convertToBlob({type: 'image/png'});
|
|
652
|
+
const dataUrl = await new Promise<string>((resolve, reject) => {
|
|
653
|
+
const reader = new FileReader();
|
|
654
|
+
reader.onloadend = () => resolve(reader.result as string);
|
|
655
|
+
reader.onerror = reject;
|
|
656
|
+
reader.readAsDataURL(blob);
|
|
703
657
|
});
|
|
658
|
+
const base64 = dataUrl.slice(dataUrl.indexOf(',') + 1);
|
|
659
|
+
const contentData = new TextUtils.ContentData.ContentData(base64, /* isBase64=*/ true, 'image/png');
|
|
660
|
+
await Workspace.FileManager.FileManager.instance().save(fileName as Platform.DevToolsPath.RawPathString,
|
|
661
|
+
contentData, /* forceSaveAs=*/ true);
|
|
662
|
+
Workspace.FileManager.FileManager.instance().close(fileName as Platform.DevToolsPath.RawPathString);
|
|
704
663
|
}
|
|
705
664
|
}
|
|
706
665
|
|