blob: cf8233addd6ba8fbd0eea2e4d643f8e7c157c2dc [file] [log] [blame]
Blink Reformat4c46d092018-04-07 15:32:371/*
2 * Copyright (C) 2013 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
Tim van der Lippe082ae0d2020-01-15 14:34:3430
Tim van der Lippe3760f902020-02-12 15:24:4931import * as Common from '../common/common.js';
32import * as Host from '../host/host.js';
33import * as SDK from '../sdk/sdk.js';
34import * as UI from '../ui/ui.js';
35
Tim van der Lippe082ae0d2020-01-15 14:34:3436import {InputModel} from './InputModel.js';
37
Blink Reformat4c46d092018-04-07 15:32:3738/**
39 * @implements {SDK.OverlayModel.Highlighter}
40 * @unrestricted
41 */
Tim van der Lippe3760f902020-02-12 15:24:4942export class ScreencastView extends UI.Widget.VBox {
Blink Reformat4c46d092018-04-07 15:32:3743 /**
Tim van der Lippe3760f902020-02-12 15:24:4944 * @param {!SDK.ScreenCaptureModel.ScreenCaptureModel} screenCaptureModel
Blink Reformat4c46d092018-04-07 15:32:3745 */
46 constructor(screenCaptureModel) {
47 super();
48 this._screenCaptureModel = screenCaptureModel;
Tim van der Lippe3760f902020-02-12 15:24:4949 this._domModel = screenCaptureModel.target().model(SDK.DOMModel.DOMModel);
50 this._overlayModel = screenCaptureModel.target().model(SDK.OverlayModel.OverlayModel);
51 this._resourceTreeModel = screenCaptureModel.target().model(SDK.ResourceTreeModel.ResourceTreeModel);
52 this._networkManager = screenCaptureModel.target().model(SDK.NetworkManager.NetworkManager);
Tim van der Lippe082ae0d2020-01-15 14:34:3453 this._inputModel = screenCaptureModel.target().model(InputModel);
Blink Reformat4c46d092018-04-07 15:32:3754
55 this.setMinimumSize(150, 150);
56 this.registerRequiredCSS('screencast/screencastView.css');
57 }
58
59 initialize() {
60 this.element.classList.add('screencast');
61
62 this._createNavigationBar();
63
64 this._viewportElement = this.element.createChild('div', 'screencast-viewport hidden');
65 this._canvasContainerElement = this._viewportElement.createChild('div', 'screencast-canvas-container');
66 this._glassPaneElement = this._canvasContainerElement.createChild('div', 'screencast-glasspane fill hidden');
67
68 this._canvasElement = this._canvasContainerElement.createChild('canvas');
Joel Einbinder83fc76e2018-06-11 23:19:4769 this._canvasElement.tabIndex = 0;
Blink Reformat4c46d092018-04-07 15:32:3770 this._canvasElement.addEventListener('mousedown', this._handleMouseEvent.bind(this), false);
71 this._canvasElement.addEventListener('mouseup', this._handleMouseEvent.bind(this), false);
72 this._canvasElement.addEventListener('mousemove', this._handleMouseEvent.bind(this), false);
73 this._canvasElement.addEventListener('mousewheel', this._handleMouseEvent.bind(this), false);
74 this._canvasElement.addEventListener('click', this._handleMouseEvent.bind(this), false);
75 this._canvasElement.addEventListener('contextmenu', this._handleContextMenuEvent.bind(this), false);
76 this._canvasElement.addEventListener('keydown', this._handleKeyEvent.bind(this), false);
77 this._canvasElement.addEventListener('keyup', this._handleKeyEvent.bind(this), false);
78 this._canvasElement.addEventListener('keypress', this._handleKeyEvent.bind(this), false);
79 this._canvasElement.addEventListener('blur', this._handleBlurEvent.bind(this), false);
80
81 this._titleElement = this._canvasContainerElement.createChild('div', 'screencast-element-title monospace hidden');
82 this._tagNameElement = this._titleElement.createChild('span', 'screencast-tag-name');
83 this._nodeIdElement = this._titleElement.createChild('span', 'screencast-node-id');
84 this._classNameElement = this._titleElement.createChild('span', 'screencast-class-name');
85 this._titleElement.createTextChild(' ');
86 this._nodeWidthElement = this._titleElement.createChild('span');
87 this._titleElement.createChild('span', 'screencast-px').textContent = 'px';
Mathias Bynens23ee1aa2020-03-02 12:06:3888 this._titleElement.createTextChild(' × ');
Blink Reformat4c46d092018-04-07 15:32:3789 this._nodeHeightElement = this._titleElement.createChild('span');
90 this._titleElement.createChild('span', 'screencast-px').textContent = 'px';
91 this._titleElement.style.top = '0';
92 this._titleElement.style.left = '0';
93
94 this._imageElement = new Image();
95 this._isCasting = false;
96 this._context = this._canvasElement.getContext('2d');
97 this._checkerboardPattern = this._createCheckerboardPattern(this._context);
98
99 this._shortcuts = /** !Object.<number, function(Event=):boolean> */ ({});
Tim van der Lippe3760f902020-02-12 15:24:49100 this._shortcuts[UI.KeyboardShortcut.KeyboardShortcut.makeKey('l', UI.KeyboardShortcut.Modifiers.Ctrl)] =
Blink Reformat4c46d092018-04-07 15:32:37101 this._focusNavigationBar.bind(this);
102
Paul Lewisdaac1062020-03-05 14:37:10103 SDK.SDKModel.TargetManager.instance().addEventListener(
104 SDK.SDKModel.Events.SuspendStateChanged, this._onSuspendStateChange, this);
Blink Reformat4c46d092018-04-07 15:32:37105 this._updateGlasspane();
106 }
107
108 /**
109 * @override
110 */
111 wasShown() {
112 this._startCasting();
113 }
114
115 /**
116 * @override
117 */
118 willHide() {
119 this._stopCasting();
120 }
121
122 _startCasting() {
Paul Lewisdaac1062020-03-05 14:37:10123 if (SDK.SDKModel.TargetManager.instance().allTargetsSuspended()) {
Blink Reformat4c46d092018-04-07 15:32:37124 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34125 }
126 if (this._isCasting) {
Blink Reformat4c46d092018-04-07 15:32:37127 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34128 }
Blink Reformat4c46d092018-04-07 15:32:37129 this._isCasting = true;
130
131 const maxImageDimension = 2048;
132 const dimensions = this._viewportDimensions();
133 if (dimensions.width < 0 || dimensions.height < 0) {
134 this._isCasting = false;
135 return;
136 }
137 dimensions.width *= window.devicePixelRatio;
138 dimensions.height *= window.devicePixelRatio;
139 // Note: startScreencast width and height are expected to be integers so must be floored.
140 this._screenCaptureModel.startScreencast(
141 'jpeg', 80, Math.floor(Math.min(maxImageDimension, dimensions.width)),
142 Math.floor(Math.min(maxImageDimension, dimensions.height)), undefined, this._screencastFrame.bind(this),
143 this._screencastVisibilityChanged.bind(this));
Paul Lewisdaac1062020-03-05 14:37:10144 for (const emulationModel of SDK.SDKModel.TargetManager.instance().models(SDK.EmulationModel.EmulationModel)) {
Blink Reformat4c46d092018-04-07 15:32:37145 emulationModel.overrideEmulateTouch(true);
Tim van der Lippe1d6e57a2019-09-30 11:55:34146 }
147 if (this._overlayModel) {
Blink Reformat4c46d092018-04-07 15:32:37148 this._overlayModel.setHighlighter(this);
Tim van der Lippe1d6e57a2019-09-30 11:55:34149 }
Blink Reformat4c46d092018-04-07 15:32:37150 }
151
152 _stopCasting() {
Tim van der Lippe1d6e57a2019-09-30 11:55:34153 if (!this._isCasting) {
Blink Reformat4c46d092018-04-07 15:32:37154 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34155 }
Blink Reformat4c46d092018-04-07 15:32:37156 this._isCasting = false;
157 this._screenCaptureModel.stopScreencast();
Paul Lewisdaac1062020-03-05 14:37:10158 for (const emulationModel of SDK.SDKModel.TargetManager.instance().models(SDK.EmulationModel.EmulationModel)) {
Blink Reformat4c46d092018-04-07 15:32:37159 emulationModel.overrideEmulateTouch(false);
Tim van der Lippe1d6e57a2019-09-30 11:55:34160 }
161 if (this._overlayModel) {
Blink Reformat4c46d092018-04-07 15:32:37162 this._overlayModel.setHighlighter(null);
Tim van der Lippe1d6e57a2019-09-30 11:55:34163 }
Blink Reformat4c46d092018-04-07 15:32:37164 }
165
166 /**
167 * @param {string} base64Data
168 * @param {!Protocol.Page.ScreencastFrameMetadata} metadata
169 */
170 _screencastFrame(base64Data, metadata) {
171 this._imageElement.onload = () => {
172 this._pageScaleFactor = metadata.pageScaleFactor;
173 this._screenOffsetTop = metadata.offsetTop;
174 this._scrollOffsetX = metadata.scrollOffsetX;
175 this._scrollOffsetY = metadata.scrollOffsetY;
176
177 const deviceSizeRatio = metadata.deviceHeight / metadata.deviceWidth;
178 const dimensionsCSS = this._viewportDimensions();
179
180 this._imageZoom = Math.min(
181 dimensionsCSS.width / this._imageElement.naturalWidth,
182 dimensionsCSS.height / (this._imageElement.naturalWidth * deviceSizeRatio));
183 this._viewportElement.classList.remove('hidden');
Paul Lewis8f4d1202019-12-04 13:05:55184 const bordersSize = _bordersSize;
Tim van der Lippe1d6e57a2019-09-30 11:55:34185 if (this._imageZoom < 1.01 / window.devicePixelRatio) {
Blink Reformat4c46d092018-04-07 15:32:37186 this._imageZoom = 1 / window.devicePixelRatio;
Tim van der Lippe1d6e57a2019-09-30 11:55:34187 }
Blink Reformat4c46d092018-04-07 15:32:37188 this._screenZoom = this._imageElement.naturalWidth * this._imageZoom / metadata.deviceWidth;
189 this._viewportElement.style.width = metadata.deviceWidth * this._screenZoom + bordersSize + 'px';
190 this._viewportElement.style.height = metadata.deviceHeight * this._screenZoom + bordersSize + 'px';
191
Pavel Feldmanfc99cdc2019-01-19 02:38:14192 this.highlightInOverlay({node: this._highlightNode}, this._highlightConfig);
Blink Reformat4c46d092018-04-07 15:32:37193 };
194 this._imageElement.src = 'data:image/jpg;base64,' + base64Data;
195 }
196
197 _isGlassPaneActive() {
198 return !this._glassPaneElement.classList.contains('hidden');
199 }
200
201 /**
202 * @param {boolean} visible
203 */
204 _screencastVisibilityChanged(visible) {
205 this._targetInactive = !visible;
206 this._updateGlasspane();
207 }
208
209 /**
Tim van der Lippec02a97c2020-02-14 14:39:27210 * @param {!Common.EventTarget.EventTargetEvent} event
Blink Reformat4c46d092018-04-07 15:32:37211 */
212 _onSuspendStateChange(event) {
Paul Lewisdaac1062020-03-05 14:37:10213 if (SDK.SDKModel.TargetManager.instance().allTargetsSuspended()) {
Blink Reformat4c46d092018-04-07 15:32:37214 this._stopCasting();
Tim van der Lippe1d6e57a2019-09-30 11:55:34215 } else {
Blink Reformat4c46d092018-04-07 15:32:37216 this._startCasting();
Tim van der Lippe1d6e57a2019-09-30 11:55:34217 }
Blink Reformat4c46d092018-04-07 15:32:37218 this._updateGlasspane();
219 }
220
221 _updateGlasspane() {
222 if (this._targetInactive) {
Tim van der Lippe3760f902020-02-12 15:24:49223 this._glassPaneElement.textContent = Common.UIString.UIString('The tab is inactive');
Blink Reformat4c46d092018-04-07 15:32:37224 this._glassPaneElement.classList.remove('hidden');
Paul Lewisdaac1062020-03-05 14:37:10225 } else if (SDK.SDKModel.TargetManager.instance().allTargetsSuspended()) {
Tim van der Lippe3760f902020-02-12 15:24:49226 this._glassPaneElement.textContent = Common.UIString.UIString('Profiling in progress');
Blink Reformat4c46d092018-04-07 15:32:37227 this._glassPaneElement.classList.remove('hidden');
228 } else {
229 this._glassPaneElement.classList.add('hidden');
230 }
231 }
232
233 /**
234 * @param {!Event} event
235 */
236 async _handleMouseEvent(event) {
237 if (this._isGlassPaneActive()) {
238 event.consume();
239 return;
240 }
241
Tim van der Lippe1d6e57a2019-09-30 11:55:34242 if (!this._pageScaleFactor || !this._domModel) {
Blink Reformat4c46d092018-04-07 15:32:37243 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34244 }
Blink Reformat4c46d092018-04-07 15:32:37245
246 if (!this._inspectModeConfig || event.type === 'mousewheel') {
Tim van der Lippe1d6e57a2019-09-30 11:55:34247 if (this._inputModel) {
Blink Reformat4c46d092018-04-07 15:32:37248 this._inputModel.emitTouchFromMouseEvent(event, this._screenOffsetTop, this._screenZoom);
Tim van der Lippe1d6e57a2019-09-30 11:55:34249 }
Blink Reformat4c46d092018-04-07 15:32:37250 event.preventDefault();
Tim van der Lippe1d6e57a2019-09-30 11:55:34251 if (event.type === 'mousedown') {
Blink Reformat4c46d092018-04-07 15:32:37252 this._canvasElement.focus();
Tim van der Lippe1d6e57a2019-09-30 11:55:34253 }
Blink Reformat4c46d092018-04-07 15:32:37254 return;
255 }
256
257 const position = this._convertIntoScreenSpace(event);
258
259 const node = await this._domModel.nodeForLocation(
260 Math.floor(position.x / this._pageScaleFactor + this._scrollOffsetX),
261 Math.floor(position.y / this._pageScaleFactor + this._scrollOffsetY),
Paul Lewis2d7d65c2020-03-16 17:26:30262 Common.Settings.Settings.instance().moduleSetting('showUAShadowDOM').get());
Blink Reformat4c46d092018-04-07 15:32:37263
Tim van der Lippe1d6e57a2019-09-30 11:55:34264 if (!node) {
Blink Reformat4c46d092018-04-07 15:32:37265 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34266 }
Blink Reformat4c46d092018-04-07 15:32:37267 if (event.type === 'mousemove') {
Pavel Feldmanfc99cdc2019-01-19 02:38:14268 this.highlightInOverlay({node}, this._inspectModeConfig);
Blink Reformat4c46d092018-04-07 15:32:37269 this._domModel.overlayModel().nodeHighlightRequested(node.id);
270 } else if (event.type === 'click') {
Pavel Feldmanfc99cdc2019-01-19 02:38:14271 this._domModel.overlayModel().inspectNodeRequested(node.backendNodeId());
Blink Reformat4c46d092018-04-07 15:32:37272 }
273 }
274
275 /**
276 * @param {!Event} event
277 */
278 _handleKeyEvent(event) {
279 if (this._isGlassPaneActive()) {
280 event.consume();
281 return;
282 }
283
Tim van der Lippe3760f902020-02-12 15:24:49284 const shortcutKey = UI.KeyboardShortcut.KeyboardShortcut.makeKeyFromEvent(/** @type {!KeyboardEvent} */ (event));
Blink Reformat4c46d092018-04-07 15:32:37285 const handler = this._shortcuts[shortcutKey];
286 if (handler && handler(event)) {
287 event.consume();
288 return;
289 }
290
Tim van der Lippe1d6e57a2019-09-30 11:55:34291 if (this._inputModel) {
Blink Reformat4c46d092018-04-07 15:32:37292 this._inputModel.emitKeyEvent(event);
Tim van der Lippe1d6e57a2019-09-30 11:55:34293 }
Blink Reformat4c46d092018-04-07 15:32:37294 event.consume();
295 this._canvasElement.focus();
296 }
297
298 /**
299 * @param {!Event} event
300 */
301 _handleContextMenuEvent(event) {
302 event.consume(true);
303 }
304
305 /**
306 * @param {!Event} event
307 */
308 _handleBlurEvent(event) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34309 if (this._inputModel) {
Blink Reformat4c46d092018-04-07 15:32:37310 this._inputModel.cancelTouch();
Tim van der Lippe1d6e57a2019-09-30 11:55:34311 }
Blink Reformat4c46d092018-04-07 15:32:37312 }
313
314 /**
315 * @param {!Event} event
316 * @return {!{x: number, y: number}}
317 */
318 _convertIntoScreenSpace(event) {
319 const position = {};
320 position.x = Math.round(event.offsetX / this._screenZoom);
321 position.y = Math.round(event.offsetY / this._screenZoom - this._screenOffsetTop);
322 return position;
323 }
324
325 /**
326 * @override
327 */
328 onResize() {
329 if (this._deferredCasting) {
330 clearTimeout(this._deferredCasting);
331 delete this._deferredCasting;
332 }
333
334 this._stopCasting();
335 this._deferredCasting = setTimeout(this._startCasting.bind(this), 100);
336 }
337
338 /**
339 * @override
Pavel Feldmanfc99cdc2019-01-19 02:38:14340 * @param {!SDK.OverlayModel.HighlightData} data
Blink Reformat4c46d092018-04-07 15:32:37341 * @param {?Protocol.Overlay.HighlightConfig} config
Blink Reformat4c46d092018-04-07 15:32:37342 */
Pavel Feldmanfc99cdc2019-01-19 02:38:14343 highlightInOverlay(data, config) {
344 this._highlightInOverlay(data, config);
345 }
346
347 /**
348 * @param {!SDK.OverlayModel.HighlightData} data
349 * @param {?Protocol.Overlay.HighlightConfig} config
350 */
351 async _highlightInOverlay(data, config) {
352 const {node: n, deferredNode, object} = data;
353 let node = n;
Tim van der Lippe1d6e57a2019-09-30 11:55:34354 if (!node && deferredNode) {
Pavel Feldmanfc99cdc2019-01-19 02:38:14355 node = await deferredNode.resolvePromise();
Tim van der Lippe1d6e57a2019-09-30 11:55:34356 }
Pavel Feldmanfc99cdc2019-01-19 02:38:14357 if (!node && object) {
Tim van der Lippe3760f902020-02-12 15:24:49358 const domModel = object.runtimeModel().target().model(SDK.DOMModel.DOMModel);
Tim van der Lippe1d6e57a2019-09-30 11:55:34359 if (domModel) {
Pavel Feldmanfc99cdc2019-01-19 02:38:14360 node = await domModel.pushObjectAsNodeToFrontend(object);
Tim van der Lippe1d6e57a2019-09-30 11:55:34361 }
Pavel Feldmanfc99cdc2019-01-19 02:38:14362 }
363
Blink Reformat4c46d092018-04-07 15:32:37364 this._highlightNode = node;
365 this._highlightConfig = config;
366 if (!node) {
367 this._model = null;
368 this._config = null;
369 this._node = null;
370 this._titleElement.classList.add('hidden');
371 this._repaint();
372 return;
373 }
374
375 this._node = node;
376 node.boxModel().then(model => {
377 if (!model || !this._pageScaleFactor) {
378 this._repaint();
379 return;
380 }
381 this._model = this._scaleModel(model);
382 this._config = config;
383 this._repaint();
384 });
385 }
386
387 /**
388 * @param {!Protocol.DOM.BoxModel} model
389 * @return {!Protocol.DOM.BoxModel}
390 */
391 _scaleModel(model) {
392 /**
393 * @param {!Protocol.DOM.Quad} quad
Tim van der Lippe082ae0d2020-01-15 14:34:34394 * @this {ScreencastView}
Blink Reformat4c46d092018-04-07 15:32:37395 */
396 function scaleQuad(quad) {
397 for (let i = 0; i < quad.length; i += 2) {
398 quad[i] = quad[i] * this._pageScaleFactor * this._screenZoom;
399 quad[i + 1] = (quad[i + 1] * this._pageScaleFactor + this._screenOffsetTop) * this._screenZoom;
400 }
401 }
402
403 scaleQuad.call(this, model.content);
404 scaleQuad.call(this, model.padding);
405 scaleQuad.call(this, model.border);
406 scaleQuad.call(this, model.margin);
407 return model;
408 }
409
410 _repaint() {
411 const model = this._model;
412 const config = this._config;
413
414 const canvasWidth = this._canvasElement.getBoundingClientRect().width;
415 const canvasHeight = this._canvasElement.getBoundingClientRect().height;
416 this._canvasElement.width = window.devicePixelRatio * canvasWidth;
417 this._canvasElement.height = window.devicePixelRatio * canvasHeight;
418
419 this._context.save();
420 this._context.scale(window.devicePixelRatio, window.devicePixelRatio);
421
422 // Paint top and bottom gutter.
423 this._context.save();
424 this._context.fillStyle = this._checkerboardPattern;
425 this._context.fillRect(0, 0, canvasWidth, this._screenOffsetTop * this._screenZoom);
426 this._context.fillRect(
427 0, this._screenOffsetTop * this._screenZoom + this._imageElement.naturalHeight * this._imageZoom, canvasWidth,
428 canvasHeight);
429 this._context.restore();
430
431 if (model && config) {
432 this._context.save();
433 const transparentColor = 'rgba(0, 0, 0, 0)';
434 const quads = [];
Tim van der Lippe1d6e57a2019-09-30 11:55:34435 if (model.content && config.contentColor !== transparentColor) {
Blink Reformat4c46d092018-04-07 15:32:37436 quads.push({quad: model.content, color: config.contentColor});
Tim van der Lippe1d6e57a2019-09-30 11:55:34437 }
438 if (model.padding && config.paddingColor !== transparentColor) {
Blink Reformat4c46d092018-04-07 15:32:37439 quads.push({quad: model.padding, color: config.paddingColor});
Tim van der Lippe1d6e57a2019-09-30 11:55:34440 }
441 if (model.border && config.borderColor !== transparentColor) {
Blink Reformat4c46d092018-04-07 15:32:37442 quads.push({quad: model.border, color: config.borderColor});
Tim van der Lippe1d6e57a2019-09-30 11:55:34443 }
444 if (model.margin && config.marginColor !== transparentColor) {
Blink Reformat4c46d092018-04-07 15:32:37445 quads.push({quad: model.margin, color: config.marginColor});
Tim van der Lippe1d6e57a2019-09-30 11:55:34446 }
Blink Reformat4c46d092018-04-07 15:32:37447
Tim van der Lippe1d6e57a2019-09-30 11:55:34448 for (let i = quads.length - 1; i > 0; --i) {
Blink Reformat4c46d092018-04-07 15:32:37449 this._drawOutlinedQuadWithClip(quads[i].quad, quads[i - 1].quad, quads[i].color);
Tim van der Lippe1d6e57a2019-09-30 11:55:34450 }
451 if (quads.length > 0) {
Blink Reformat4c46d092018-04-07 15:32:37452 this._drawOutlinedQuad(quads[0].quad, quads[0].color);
Tim van der Lippe1d6e57a2019-09-30 11:55:34453 }
Blink Reformat4c46d092018-04-07 15:32:37454 this._context.restore();
455
456 this._drawElementTitle();
457
458 this._context.globalCompositeOperation = 'destination-over';
459 }
460
461 this._context.drawImage(
462 this._imageElement, 0, this._screenOffsetTop * this._screenZoom,
463 this._imageElement.naturalWidth * this._imageZoom, this._imageElement.naturalHeight * this._imageZoom);
464 this._context.restore();
465 }
466
467 /**
468 * @param {!Protocol.DOM.RGBA} color
469 * @return {string}
470 */
471 _cssColor(color) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34472 if (!color) {
Blink Reformat4c46d092018-04-07 15:32:37473 return 'transparent';
Tim van der Lippe1d6e57a2019-09-30 11:55:34474 }
Tim van der Lippe3760f902020-02-12 15:24:49475 return Common.Color.Color.fromRGBA([color.r, color.g, color.b, color.a]).asString(Common.Color.Format.RGBA) || '';
Blink Reformat4c46d092018-04-07 15:32:37476 }
477
478 /**
479 * @param {!Protocol.DOM.Quad} quad
480 * @return {!CanvasRenderingContext2D}
481 */
482 _quadToPath(quad) {
483 this._context.beginPath();
484 this._context.moveTo(quad[0], quad[1]);
485 this._context.lineTo(quad[2], quad[3]);
486 this._context.lineTo(quad[4], quad[5]);
487 this._context.lineTo(quad[6], quad[7]);
488 this._context.closePath();
489 return this._context;
490 }
491
492 /**
493 * @param {!Protocol.DOM.Quad} quad
494 * @param {!Protocol.DOM.RGBA} fillColor
495 */
496 _drawOutlinedQuad(quad, fillColor) {
497 this._context.save();
498 this._context.lineWidth = 2;
499 this._quadToPath(quad).clip();
500 this._context.fillStyle = this._cssColor(fillColor);
501 this._context.fill();
502 this._context.restore();
503 }
504
505 /**
506 * @param {!Protocol.DOM.Quad} quad
507 * @param {!Protocol.DOM.Quad} clipQuad
508 * @param {!Protocol.DOM.RGBA} fillColor
509 */
510 _drawOutlinedQuadWithClip(quad, clipQuad, fillColor) {
511 this._context.fillStyle = this._cssColor(fillColor);
512 this._context.save();
513 this._context.lineWidth = 0;
514 this._quadToPath(quad).fill();
515 this._context.globalCompositeOperation = 'destination-out';
516 this._context.fillStyle = 'red';
517 this._quadToPath(clipQuad).fill();
518 this._context.restore();
519 }
520
521 _drawElementTitle() {
Tim van der Lippe1d6e57a2019-09-30 11:55:34522 if (!this._node) {
Blink Reformat4c46d092018-04-07 15:32:37523 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34524 }
Blink Reformat4c46d092018-04-07 15:32:37525
526 const canvasWidth = this._canvasElement.getBoundingClientRect().width;
527 const canvasHeight = this._canvasElement.getBoundingClientRect().height;
528
529 const lowerCaseName = this._node.localName() || this._node.nodeName().toLowerCase();
530 this._tagNameElement.textContent = lowerCaseName;
531 this._nodeIdElement.textContent = this._node.getAttribute('id') ? '#' + this._node.getAttribute('id') : '';
532 this._nodeIdElement.textContent = this._node.getAttribute('id') ? '#' + this._node.getAttribute('id') : '';
533 let className = this._node.getAttribute('class');
Tim van der Lippe1d6e57a2019-09-30 11:55:34534 if (className && className.length > 50) {
Mathias Bynens23ee1aa2020-03-02 12:06:38535 className = className.substring(0, 50) + '…';
Tim van der Lippe1d6e57a2019-09-30 11:55:34536 }
Blink Reformat4c46d092018-04-07 15:32:37537 this._classNameElement.textContent = className || '';
538 this._nodeWidthElement.textContent = this._model.width;
539 this._nodeHeightElement.textContent = this._model.height;
540
541 this._titleElement.classList.remove('hidden');
542 const titleWidth = this._titleElement.offsetWidth + 6;
543 const titleHeight = this._titleElement.offsetHeight + 4;
544
545 const anchorTop = this._model.margin[1];
546 const anchorBottom = this._model.margin[7];
547
548 const arrowHeight = 7;
549 let renderArrowUp = false;
550 let renderArrowDown = false;
551
552 let boxX = Math.max(2, this._model.margin[0]);
Tim van der Lippe1d6e57a2019-09-30 11:55:34553 if (boxX + titleWidth > canvasWidth) {
Blink Reformat4c46d092018-04-07 15:32:37554 boxX = canvasWidth - titleWidth - 2;
Tim van der Lippe1d6e57a2019-09-30 11:55:34555 }
Blink Reformat4c46d092018-04-07 15:32:37556
557 let boxY;
558 if (anchorTop > canvasHeight) {
559 boxY = canvasHeight - titleHeight - arrowHeight;
560 renderArrowDown = true;
561 } else if (anchorBottom < 0) {
562 boxY = arrowHeight;
563 renderArrowUp = true;
564 } else if (anchorBottom + titleHeight + arrowHeight < canvasHeight) {
565 boxY = anchorBottom + arrowHeight - 4;
566 renderArrowUp = true;
567 } else if (anchorTop - titleHeight - arrowHeight > 0) {
568 boxY = anchorTop - titleHeight - arrowHeight + 3;
569 renderArrowDown = true;
570 } else {
571 boxY = arrowHeight;
572 }
573
574 this._context.save();
575 this._context.translate(0.5, 0.5);
576 this._context.beginPath();
577 this._context.moveTo(boxX, boxY);
578 if (renderArrowUp) {
579 this._context.lineTo(boxX + 2 * arrowHeight, boxY);
580 this._context.lineTo(boxX + 3 * arrowHeight, boxY - arrowHeight);
581 this._context.lineTo(boxX + 4 * arrowHeight, boxY);
582 }
583 this._context.lineTo(boxX + titleWidth, boxY);
584 this._context.lineTo(boxX + titleWidth, boxY + titleHeight);
585 if (renderArrowDown) {
586 this._context.lineTo(boxX + 4 * arrowHeight, boxY + titleHeight);
587 this._context.lineTo(boxX + 3 * arrowHeight, boxY + titleHeight + arrowHeight);
588 this._context.lineTo(boxX + 2 * arrowHeight, boxY + titleHeight);
589 }
590 this._context.lineTo(boxX, boxY + titleHeight);
591 this._context.closePath();
592 this._context.fillStyle = 'rgb(255, 255, 194)';
593 this._context.fill();
594 this._context.strokeStyle = 'rgb(128, 128, 128)';
595 this._context.stroke();
596
597 this._context.restore();
598
599 this._titleElement.style.top = (boxY + 3) + 'px';
600 this._titleElement.style.left = (boxX + 3) + 'px';
601 }
602
603 /**
604 * @return {!{width: number, height: number}}
605 */
606 _viewportDimensions() {
607 const gutterSize = 30;
Paul Lewis8f4d1202019-12-04 13:05:55608 const bordersSize = _bordersSize;
Blink Reformat4c46d092018-04-07 15:32:37609 const width = this.element.offsetWidth - bordersSize - gutterSize;
Paul Lewis8f4d1202019-12-04 13:05:55610 const height = this.element.offsetHeight - bordersSize - gutterSize - _navBarHeight;
Blink Reformat4c46d092018-04-07 15:32:37611 return {width: width, height: height};
612 }
613
614 /**
615 * @override
616 * @param {!Protocol.Overlay.InspectMode} mode
617 * @param {!Protocol.Overlay.HighlightConfig} config
618 * @return {!Promise}
619 */
620 setInspectMode(mode, config) {
621 this._inspectModeConfig = mode !== Protocol.Overlay.InspectMode.None ? config : null;
622 return Promise.resolve();
623 }
624
625 /**
626 * @override
627 * @param {!Protocol.Page.FrameId} frameId
628 */
629 highlightFrame(frameId) {
630 }
631
632 /**
633 * @param {!CanvasRenderingContext2D} context
634 */
635 _createCheckerboardPattern(context) {
636 const pattern = /** @type {!HTMLCanvasElement} */ (createElement('canvas'));
637 const size = 32;
638 pattern.width = size * 2;
639 pattern.height = size * 2;
640 const pctx = pattern.getContext('2d');
641
642 pctx.fillStyle = 'rgb(195, 195, 195)';
643 pctx.fillRect(0, 0, size * 2, size * 2);
644
645 pctx.fillStyle = 'rgb(225, 225, 225)';
646 pctx.fillRect(0, 0, size, size);
647 pctx.fillRect(size, size, size, size);
648 return context.createPattern(pattern, 'repeat');
649 }
650
651 _createNavigationBar() {
652 this._navigationBar = this.element.createChild('div', 'screencast-navigation');
653 this._navigationBack = this._navigationBar.createChild('button', 'back');
654 this._navigationBack.disabled = true;
655 this._navigationForward = this._navigationBar.createChild('button', 'forward');
656 this._navigationForward.disabled = true;
657 this._navigationReload = this._navigationBar.createChild('button', 'reload');
Tim van der Lippe3760f902020-02-12 15:24:49658 this._navigationUrl = UI.UIUtils.createInput();
Blink Reformat4c46d092018-04-07 15:32:37659 this._navigationBar.appendChild(this._navigationUrl);
660 this._navigationUrl.type = 'text';
Tim van der Lippe082ae0d2020-01-15 14:34:34661 this._navigationProgressBar = new ProgressTracker(
Blink Reformat4c46d092018-04-07 15:32:37662 this._resourceTreeModel, this._networkManager, this._navigationBar.createChild('div', 'progress'));
663
664 if (this._resourceTreeModel) {
665 this._navigationBack.addEventListener('click', this._navigateToHistoryEntry.bind(this, -1), false);
666 this._navigationForward.addEventListener('click', this._navigateToHistoryEntry.bind(this, 1), false);
667 this._navigationReload.addEventListener('click', this._navigateReload.bind(this), false);
668 this._navigationUrl.addEventListener('keyup', this._navigationUrlKeyUp.bind(this), true);
669 this._requestNavigationHistory();
670 this._resourceTreeModel.addEventListener(
Tim van der Lippe37a35ff2020-03-03 13:49:02671 SDK.ResourceTreeModel.Events.MainFrameNavigated, this._requestNavigationHistoryEvent, this);
Blink Reformat4c46d092018-04-07 15:32:37672 this._resourceTreeModel.addEventListener(
Tim van der Lippe37a35ff2020-03-03 13:49:02673 SDK.ResourceTreeModel.Events.CachedResourcesLoaded, this._requestNavigationHistoryEvent, this);
Blink Reformat4c46d092018-04-07 15:32:37674 }
675 }
676
677 /**
678 * @param {number} offset
679 */
680 _navigateToHistoryEntry(offset) {
681 const newIndex = this._historyIndex + offset;
Tim van der Lippe1d6e57a2019-09-30 11:55:34682 if (newIndex < 0 || newIndex >= this._historyEntries.length) {
Blink Reformat4c46d092018-04-07 15:32:37683 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34684 }
Blink Reformat4c46d092018-04-07 15:32:37685 this._resourceTreeModel.navigateToHistoryEntry(this._historyEntries[newIndex]);
686 this._requestNavigationHistory();
687 }
688
689 _navigateReload() {
690 this._resourceTreeModel.reloadPage();
691 }
692
693 /**
694 * @param {!Event} event
695 */
696 _navigationUrlKeyUp(event) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34697 if (event.key !== 'Enter') {
Blink Reformat4c46d092018-04-07 15:32:37698 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34699 }
Blink Reformat4c46d092018-04-07 15:32:37700 let url = this._navigationUrl.value;
Tim van der Lippe1d6e57a2019-09-30 11:55:34701 if (!url) {
Blink Reformat4c46d092018-04-07 15:32:37702 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34703 }
Paul Lewis8f4d1202019-12-04 13:05:55704 if (!url.match(_SchemeRegex)) {
Blink Reformat4c46d092018-04-07 15:32:37705 url = 'http://' + url;
Tim van der Lippe1d6e57a2019-09-30 11:55:34706 }
Blink Reformat4c46d092018-04-07 15:32:37707 this._resourceTreeModel.navigate(url);
708 this._canvasElement.focus();
709 }
710
Tim van der Lippe37a35ff2020-03-03 13:49:02711 /**
712 * @param {!Common.EventTarget.EventTargetEvent} event
713 */
714 _requestNavigationHistoryEvent(event) {
715 this._requestNavigationHistory();
716 }
717
Blink Reformat4c46d092018-04-07 15:32:37718 async _requestNavigationHistory() {
719 const history = await this._resourceTreeModel.navigationHistory();
Tim van der Lippe1d6e57a2019-09-30 11:55:34720 if (!history) {
Blink Reformat4c46d092018-04-07 15:32:37721 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34722 }
Blink Reformat4c46d092018-04-07 15:32:37723
724 this._historyIndex = history.currentIndex;
725 this._historyEntries = history.entries;
726
727 this._navigationBack.disabled = this._historyIndex === 0;
728 this._navigationForward.disabled = this._historyIndex === (this._historyEntries.length - 1);
729
730 let url = this._historyEntries[this._historyIndex].url;
Paul Lewis8f4d1202019-12-04 13:05:55731 const match = url.match(_HttpRegex);
Tim van der Lippe1d6e57a2019-09-30 11:55:34732 if (match) {
Blink Reformat4c46d092018-04-07 15:32:37733 url = match[1];
Tim van der Lippe1d6e57a2019-09-30 11:55:34734 }
Tim van der Lippe3760f902020-02-12 15:24:49735 Host.InspectorFrontendHost.InspectorFrontendHostInstance.inspectedURLChanged(url);
Blink Reformat4c46d092018-04-07 15:32:37736 this._navigationUrl.value = url;
737 }
738
739 _focusNavigationBar() {
740 this._navigationUrl.focus();
741 this._navigationUrl.select();
742 return true;
743 }
Paul Lewis8f4d1202019-12-04 13:05:55744}
Blink Reformat4c46d092018-04-07 15:32:37745
Paul Lewis8f4d1202019-12-04 13:05:55746export const _bordersSize = 44;
747export const _navBarHeight = 29;
748export const _HttpRegex = /^http:\/\/(.+)/;
749export const _SchemeRegex = /^(https?|about|chrome):/;
Blink Reformat4c46d092018-04-07 15:32:37750
751/**
752 * @unrestricted
753 */
Paul Lewis8f4d1202019-12-04 13:05:55754export class ProgressTracker {
Blink Reformat4c46d092018-04-07 15:32:37755 /**
Tim van der Lippe3760f902020-02-12 15:24:49756 * @param {?SDK.ResourceTreeModel.ResourceTreeModel} resourceTreeModel
757 * @param {?SDK.NetworkManager.NetworkManager} networkManager
Blink Reformat4c46d092018-04-07 15:32:37758 * @param {!Element} element
759 */
760 constructor(resourceTreeModel, networkManager, element) {
761 this._element = element;
762 if (resourceTreeModel) {
763 resourceTreeModel.addEventListener(
764 SDK.ResourceTreeModel.Events.MainFrameNavigated, this._onMainFrameNavigated, this);
765 resourceTreeModel.addEventListener(SDK.ResourceTreeModel.Events.Load, this._onLoad, this);
766 }
767 if (networkManager) {
768 networkManager.addEventListener(SDK.NetworkManager.Events.RequestStarted, this._onRequestStarted, this);
769 networkManager.addEventListener(SDK.NetworkManager.Events.RequestFinished, this._onRequestFinished, this);
770 }
771 }
772
773 _onMainFrameNavigated() {
774 this._requestIds = {};
775 this._startedRequests = 0;
776 this._finishedRequests = 0;
777 this._maxDisplayedProgress = 0;
778 this._updateProgress(0.1); // Display first 10% on navigation start.
779 }
780
781 _onLoad() {
782 delete this._requestIds;
783 this._updateProgress(1); // Display 100% progress on load, hide it in 0.5s.
784 setTimeout(function() {
Tim van der Lippe1d6e57a2019-09-30 11:55:34785 if (!this._navigationProgressVisible()) {
Blink Reformat4c46d092018-04-07 15:32:37786 this._displayProgress(0);
Tim van der Lippe1d6e57a2019-09-30 11:55:34787 }
Blink Reformat4c46d092018-04-07 15:32:37788 }.bind(this), 500);
789 }
790
791 _navigationProgressVisible() {
792 return !!this._requestIds;
793 }
794
795 _onRequestStarted(event) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34796 if (!this._navigationProgressVisible()) {
Blink Reformat4c46d092018-04-07 15:32:37797 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34798 }
Tim van der Lippe3760f902020-02-12 15:24:49799 const request = /** @type {!SDK.NetworkRequest.NetworkRequest} */ (event.data);
Blink Reformat4c46d092018-04-07 15:32:37800 // Ignore long-living WebSockets for the sake of progress indicator, as we won't be waiting them anyway.
Tim van der Lippe3760f902020-02-12 15:24:49801 if (request.type === Common.ResourceType.resourceTypes.WebSocket) {
Blink Reformat4c46d092018-04-07 15:32:37802 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34803 }
Blink Reformat4c46d092018-04-07 15:32:37804 this._requestIds[request.requestId()] = request;
805 ++this._startedRequests;
806 }
807
808 _onRequestFinished(event) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34809 if (!this._navigationProgressVisible()) {
Blink Reformat4c46d092018-04-07 15:32:37810 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34811 }
Tim van der Lippe3760f902020-02-12 15:24:49812 const request = /** @type {!SDK.NetworkRequest.NetworkRequest} */ (event.data);
Tim van der Lippe1d6e57a2019-09-30 11:55:34813 if (!(request.requestId() in this._requestIds)) {
Blink Reformat4c46d092018-04-07 15:32:37814 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34815 }
Blink Reformat4c46d092018-04-07 15:32:37816 ++this._finishedRequests;
817 setTimeout(function() {
818 this._updateProgress(
819 this._finishedRequests / this._startedRequests * 0.9); // Finished requests drive the progress up to 90%.
820 }.bind(this), 500); // Delay to give the new requests time to start. This makes the progress smoother.
821 }
822
823 _updateProgress(progress) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34824 if (!this._navigationProgressVisible()) {
Blink Reformat4c46d092018-04-07 15:32:37825 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34826 }
827 if (this._maxDisplayedProgress >= progress) {
Blink Reformat4c46d092018-04-07 15:32:37828 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34829 }
Blink Reformat4c46d092018-04-07 15:32:37830 this._maxDisplayedProgress = progress;
831 this._displayProgress(progress);
832 }
833
834 _displayProgress(progress) {
835 this._element.style.width = (100 * progress) + '%';
836 }
Paul Lewis8f4d1202019-12-04 13:05:55837}