OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2013 Google Inc. All rights reserved. | 2 * Copyright (C) 2013 Google Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
6 * met: | 6 * met: |
7 * | 7 * |
8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
11 * copyright notice, this list of conditions and the following disclaimer | 11 * copyright notice, this list of conditions and the following disclaimer |
12 * in the documentation and/or other materials provided with the | 12 * in the documentation and/or other materials provided with the |
13 * distribution. | 13 * distribution. |
14 * * Neither the name of Google Inc. nor the names of its | 14 * * Neither the name of Google Inc. nor the names of its |
15 * contributors may be used to endorse or promote products derived from | 15 * contributors may be used to endorse or promote products derived from |
16 * this software without specific prior written permission. | 16 * this software without specific prior written permission. |
17 * | 17 * |
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 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. | 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
29 */ | 29 */ |
30 | |
31 /** | 30 /** |
32 * @constructor | |
33 * @extends {WebInspector.VBox} | |
34 * @implements {WebInspector.DOMNodeHighlighter} | 31 * @implements {WebInspector.DOMNodeHighlighter} |
35 * @param {!WebInspector.Target} target | 32 * @unrestricted |
36 * @param {!WebInspector.ResourceTreeModel} resourceTreeModel | |
37 */ | 33 */ |
38 WebInspector.ScreencastView = function(target, resourceTreeModel) | 34 WebInspector.ScreencastView = class extends WebInspector.VBox { |
39 { | 35 /** |
40 WebInspector.VBox.call(this); | 36 * @param {!WebInspector.Target} target |
| 37 * @param {!WebInspector.ResourceTreeModel} resourceTreeModel |
| 38 */ |
| 39 constructor(target, resourceTreeModel) { |
| 40 super(); |
41 this._target = target; | 41 this._target = target; |
42 this._domModel = WebInspector.DOMModel.fromTarget(target); | 42 this._domModel = WebInspector.DOMModel.fromTarget(target); |
43 this._resourceTreeModel = resourceTreeModel; | 43 this._resourceTreeModel = resourceTreeModel; |
44 | 44 |
45 this.setMinimumSize(150, 150); | 45 this.setMinimumSize(150, 150); |
46 this.registerRequiredCSS("screencast/screencastView.css"); | 46 this.registerRequiredCSS('screencast/screencastView.css'); |
47 }; | 47 } |
48 | 48 |
49 WebInspector.ScreencastView._bordersSize = 44; | 49 initialize() { |
50 | 50 this.element.classList.add('screencast'); |
51 WebInspector.ScreencastView._navBarHeight = 29; | 51 |
52 | 52 this._createNavigationBar(); |
53 WebInspector.ScreencastView._HttpRegex = /^http:\/\/(.+)/; | 53 |
54 | 54 this._viewportElement = this.element.createChild('div', 'screencast-viewport
hidden'); |
55 WebInspector.ScreencastView._SchemeRegex = /^(https?|about|chrome):/; | 55 this._canvasContainerElement = this._viewportElement.createChild('div', 'scr
eencast-canvas-container'); |
56 | 56 this._glassPaneElement = this._canvasContainerElement.createChild('div', 'sc
reencast-glasspane fill hidden'); |
57 WebInspector.ScreencastView.prototype = { | 57 |
58 initialize: function() | 58 this._canvasElement = this._canvasContainerElement.createChild('canvas'); |
59 { | 59 this._canvasElement.tabIndex = 1; |
60 this.element.classList.add("screencast"); | 60 this._canvasElement.addEventListener('mousedown', this._handleMouseEvent.bin
d(this), false); |
61 | 61 this._canvasElement.addEventListener('mouseup', this._handleMouseEvent.bind(
this), false); |
62 this._createNavigationBar(); | 62 this._canvasElement.addEventListener('mousemove', this._handleMouseEvent.bin
d(this), false); |
63 | 63 this._canvasElement.addEventListener('mousewheel', this._handleMouseEvent.bi
nd(this), false); |
64 this._viewportElement = this.element.createChild("div", "screencast-view
port hidden"); | 64 this._canvasElement.addEventListener('click', this._handleMouseEvent.bind(th
is), false); |
65 this._canvasContainerElement = this._viewportElement.createChild("div",
"screencast-canvas-container"); | 65 this._canvasElement.addEventListener('contextmenu', this._handleContextMenuE
vent.bind(this), false); |
66 this._glassPaneElement = this._canvasContainerElement.createChild("div",
"screencast-glasspane fill hidden"); | 66 this._canvasElement.addEventListener('keydown', this._handleKeyEvent.bind(th
is), false); |
67 | 67 this._canvasElement.addEventListener('keyup', this._handleKeyEvent.bind(this
), false); |
68 this._canvasElement = this._canvasContainerElement.createChild("canvas")
; | 68 this._canvasElement.addEventListener('keypress', this._handleKeyEvent.bind(t
his), false); |
69 this._canvasElement.tabIndex = 1; | 69 this._canvasElement.addEventListener('blur', this._handleBlurEvent.bind(this
), false); |
70 this._canvasElement.addEventListener("mousedown", this._handleMouseEvent
.bind(this), false); | 70 |
71 this._canvasElement.addEventListener("mouseup", this._handleMouseEvent.b
ind(this), false); | 71 this._titleElement = this._canvasContainerElement.createChild('div', 'screen
cast-element-title monospace hidden'); |
72 this._canvasElement.addEventListener("mousemove", this._handleMouseEvent
.bind(this), false); | 72 this._tagNameElement = this._titleElement.createChild('span', 'screencast-ta
g-name'); |
73 this._canvasElement.addEventListener("mousewheel", this._handleMouseEven
t.bind(this), false); | 73 this._nodeIdElement = this._titleElement.createChild('span', 'screencast-nod
e-id'); |
74 this._canvasElement.addEventListener("click", this._handleMouseEvent.bin
d(this), false); | 74 this._classNameElement = this._titleElement.createChild('span', 'screencast-
class-name'); |
75 this._canvasElement.addEventListener("contextmenu", this._handleContextM
enuEvent.bind(this), false); | 75 this._titleElement.createTextChild(' '); |
76 this._canvasElement.addEventListener("keydown", this._handleKeyEvent.bin
d(this), false); | 76 this._nodeWidthElement = this._titleElement.createChild('span'); |
77 this._canvasElement.addEventListener("keyup", this._handleKeyEvent.bind(
this), false); | 77 this._titleElement.createChild('span', 'screencast-px').textContent = 'px'; |
78 this._canvasElement.addEventListener("keypress", this._handleKeyEvent.bi
nd(this), false); | 78 this._titleElement.createTextChild(' \u00D7 '); |
79 this._canvasElement.addEventListener("blur", this._handleBlurEvent.bind(
this), false); | 79 this._nodeHeightElement = this._titleElement.createChild('span'); |
80 | 80 this._titleElement.createChild('span', 'screencast-px').textContent = 'px'; |
81 this._titleElement = this._canvasContainerElement.createChild("div", "sc
reencast-element-title monospace hidden"); | 81 this._titleElement.style.top = '0'; |
82 this._tagNameElement = this._titleElement.createChild("span", "screencas
t-tag-name"); | 82 this._titleElement.style.left = '0'; |
83 this._nodeIdElement = this._titleElement.createChild("span", "screencast
-node-id"); | 83 |
84 this._classNameElement = this._titleElement.createChild("span", "screenc
ast-class-name"); | 84 this._imageElement = new Image(); |
85 this._titleElement.createTextChild(" "); | 85 this._isCasting = false; |
86 this._nodeWidthElement = this._titleElement.createChild("span"); | 86 this._context = this._canvasElement.getContext('2d'); |
87 this._titleElement.createChild("span", "screencast-px").textContent = "p
x"; | 87 this._checkerboardPattern = this._createCheckerboardPattern(this._context); |
88 this._titleElement.createTextChild(" \u00D7 "); | 88 |
89 this._nodeHeightElement = this._titleElement.createChild("span"); | 89 this._shortcuts = /** !Object.<number, function(Event=):boolean> */ ({}); |
90 this._titleElement.createChild("span", "screencast-px").textContent = "p
x"; | 90 this._shortcuts[WebInspector.KeyboardShortcut.makeKey('l', WebInspector.Keyb
oardShortcut.Modifiers.Ctrl)] = |
91 this._titleElement.style.top = "0"; | 91 this._focusNavigationBar.bind(this); |
92 this._titleElement.style.left = "0"; | 92 |
93 | 93 this._resourceTreeModel.addEventListener( |
94 this._imageElement = new Image(); | 94 WebInspector.ResourceTreeModel.Events.ScreencastFrame, this._screencastF
rame, this); |
95 this._isCasting = false; | 95 this._resourceTreeModel.addEventListener( |
96 this._context = this._canvasElement.getContext("2d"); | 96 WebInspector.ResourceTreeModel.Events.ScreencastVisibilityChanged, this.
_screencastVisibilityChanged, this); |
97 this._checkerboardPattern = this._createCheckerboardPattern(this._contex
t); | 97 |
98 | 98 WebInspector.targetManager.addEventListener( |
99 this._shortcuts = /** !Object.<number, function(Event=):boolean> */ ({})
; | 99 WebInspector.TargetManager.Events.SuspendStateChanged, this._onSuspendSt
ateChange, this); |
100 this._shortcuts[WebInspector.KeyboardShortcut.makeKey("l", WebInspector.
KeyboardShortcut.Modifiers.Ctrl)] = this._focusNavigationBar.bind(this); | 100 this._updateGlasspane(); |
101 | 101 } |
102 this._resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.
Events.ScreencastFrame, this._screencastFrame, this); | 102 |
103 this._resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.
Events.ScreencastVisibilityChanged, this._screencastVisibilityChanged, this); | 103 /** |
104 | 104 * @override |
105 WebInspector.targetManager.addEventListener(WebInspector.TargetManager.E
vents.SuspendStateChanged, this._onSuspendStateChange, this); | 105 */ |
106 this._updateGlasspane(); | 106 wasShown() { |
107 }, | 107 this._startCasting(); |
108 | 108 } |
109 wasShown: function() | 109 |
110 { | 110 /** |
111 this._startCasting(); | 111 * @override |
112 }, | 112 */ |
113 | 113 willHide() { |
114 willHide: function() | 114 this._stopCasting(); |
115 { | 115 } |
116 this._stopCasting(); | 116 |
117 }, | 117 _startCasting() { |
118 | 118 if (WebInspector.targetManager.allTargetsSuspended()) |
119 _startCasting: function() | 119 return; |
120 { | 120 if (this._isCasting) |
121 if (WebInspector.targetManager.allTargetsSuspended()) | 121 return; |
122 return; | 122 this._isCasting = true; |
123 if (this._isCasting) | 123 |
124 return; | 124 const maxImageDimension = 2048; |
125 this._isCasting = true; | 125 var dimensions = this._viewportDimensions(); |
126 | 126 if (dimensions.width < 0 || dimensions.height < 0) { |
127 const maxImageDimension = 2048; | 127 this._isCasting = false; |
128 var dimensions = this._viewportDimensions(); | 128 return; |
129 if (dimensions.width < 0 || dimensions.height < 0) { | 129 } |
130 this._isCasting = false; | 130 dimensions.width *= window.devicePixelRatio; |
131 return; | 131 dimensions.height *= window.devicePixelRatio; |
132 } | 132 this._target.pageAgent().startScreencast( |
133 dimensions.width *= window.devicePixelRatio; | 133 'jpeg', 80, Math.min(maxImageDimension, dimensions.width), Math.min(maxI
mageDimension, dimensions.height)); |
134 dimensions.height *= window.devicePixelRatio; | 134 this._target.emulationAgent().setTouchEmulationEnabled(true); |
135 this._target.pageAgent().startScreencast("jpeg", 80, Math.min(maxImageDi
mension, dimensions.width), Math.min(maxImageDimension, dimensions.height)); | 135 this._domModel.setHighlighter(this); |
136 this._target.emulationAgent().setTouchEmulationEnabled(true); | 136 } |
137 this._domModel.setHighlighter(this); | 137 |
138 }, | 138 _stopCasting() { |
139 | 139 if (!this._isCasting) |
140 _stopCasting: function() | 140 return; |
141 { | 141 this._isCasting = false; |
142 if (!this._isCasting) | 142 this._target.pageAgent().stopScreencast(); |
143 return; | 143 this._target.emulationAgent().setTouchEmulationEnabled(false); |
144 this._isCasting = false; | 144 this._domModel.setHighlighter(null); |
145 this._target.pageAgent().stopScreencast(); | 145 } |
146 this._target.emulationAgent().setTouchEmulationEnabled(false); | 146 |
147 this._domModel.setHighlighter(null); | 147 /** |
148 }, | 148 * @param {!WebInspector.Event} event |
| 149 */ |
| 150 _screencastFrame(event) { |
| 151 var metadata = /** type {PageAgent.ScreencastFrameMetadata} */ (event.data.m
etadata); |
| 152 var base64Data = /** type {string} */ (event.data.data); |
| 153 this._imageElement.src = 'data:image/jpg;base64,' + base64Data; |
| 154 this._pageScaleFactor = metadata.pageScaleFactor; |
| 155 this._screenOffsetTop = metadata.offsetTop; |
| 156 this._scrollOffsetX = metadata.scrollOffsetX; |
| 157 this._scrollOffsetY = metadata.scrollOffsetY; |
| 158 |
| 159 var deviceSizeRatio = metadata.deviceHeight / metadata.deviceWidth; |
| 160 var dimensionsCSS = this._viewportDimensions(); |
| 161 |
| 162 this._imageZoom = Math.min( |
| 163 dimensionsCSS.width / this._imageElement.naturalWidth, |
| 164 dimensionsCSS.height / (this._imageElement.naturalWidth * deviceSizeRati
o)); |
| 165 this._viewportElement.classList.remove('hidden'); |
| 166 var bordersSize = WebInspector.ScreencastView._bordersSize; |
| 167 if (this._imageZoom < 1.01 / window.devicePixelRatio) |
| 168 this._imageZoom = 1 / window.devicePixelRatio; |
| 169 this._screenZoom = this._imageElement.naturalWidth * this._imageZoom / metad
ata.deviceWidth; |
| 170 this._viewportElement.style.width = metadata.deviceWidth * this._screenZoom
+ bordersSize + 'px'; |
| 171 this._viewportElement.style.height = metadata.deviceHeight * this._screenZoo
m + bordersSize + 'px'; |
| 172 |
| 173 this.highlightDOMNode(this._highlightNode, this._highlightConfig); |
| 174 } |
| 175 |
| 176 _isGlassPaneActive() { |
| 177 return !this._glassPaneElement.classList.contains('hidden'); |
| 178 } |
| 179 |
| 180 /** |
| 181 * @param {!WebInspector.Event} event |
| 182 */ |
| 183 _screencastVisibilityChanged(event) { |
| 184 this._targetInactive = !event.data.visible; |
| 185 this._updateGlasspane(); |
| 186 } |
| 187 |
| 188 /** |
| 189 * @param {!WebInspector.Event} event |
| 190 */ |
| 191 _onSuspendStateChange(event) { |
| 192 if (WebInspector.targetManager.allTargetsSuspended()) |
| 193 this._stopCasting(); |
| 194 else |
| 195 this._startCasting(); |
| 196 this._updateGlasspane(); |
| 197 } |
| 198 |
| 199 _updateGlasspane() { |
| 200 if (this._targetInactive) { |
| 201 this._glassPaneElement.textContent = WebInspector.UIString('The tab is ina
ctive'); |
| 202 this._glassPaneElement.classList.remove('hidden'); |
| 203 } else if (WebInspector.targetManager.allTargetsSuspended()) { |
| 204 this._glassPaneElement.textContent = WebInspector.UIString('Profiling in p
rogress'); |
| 205 this._glassPaneElement.classList.remove('hidden'); |
| 206 } else { |
| 207 this._glassPaneElement.classList.add('hidden'); |
| 208 } |
| 209 } |
| 210 |
| 211 /** |
| 212 * @param {!Event} event |
| 213 */ |
| 214 _handleMouseEvent(event) { |
| 215 if (this._isGlassPaneActive()) { |
| 216 event.consume(); |
| 217 return; |
| 218 } |
| 219 |
| 220 if (!this._pageScaleFactor) |
| 221 return; |
| 222 |
| 223 if (!this._inspectModeConfig || event.type === 'mousewheel') { |
| 224 this._simulateTouchForMouseEvent(event); |
| 225 event.preventDefault(); |
| 226 if (event.type === 'mousedown') |
| 227 this._canvasElement.focus(); |
| 228 return; |
| 229 } |
| 230 |
| 231 var position = this._convertIntoScreenSpace(event); |
| 232 this._domModel.nodeForLocation( |
| 233 position.x / this._pageScaleFactor + this._scrollOffsetX, |
| 234 position.y / this._pageScaleFactor + this._scrollOffsetY, callback.bind(
this)); |
149 | 235 |
150 /** | 236 /** |
151 * @param {!WebInspector.Event} event | 237 * @param {?WebInspector.DOMNode} node |
| 238 * @this {WebInspector.ScreencastView} |
152 */ | 239 */ |
153 _screencastFrame: function(event) | 240 function callback(node) { |
154 { | 241 if (!node) |
155 var metadata = /** type {PageAgent.ScreencastFrameMetadata} */(event.dat
a.metadata); | 242 return; |
156 var base64Data = /** type {string} */(event.data.data); | 243 if (event.type === 'mousemove') { |
157 this._imageElement.src = "data:image/jpg;base64," + base64Data; | 244 this.highlightDOMNode(node, this._inspectModeConfig); |
158 this._pageScaleFactor = metadata.pageScaleFactor; | 245 this._domModel.nodeHighlightRequested(node.id); |
159 this._screenOffsetTop = metadata.offsetTop; | 246 } else if (event.type === 'click') { |
160 this._scrollOffsetX = metadata.scrollOffsetX; | 247 WebInspector.Revealer.reveal(node); |
161 this._scrollOffsetY = metadata.scrollOffsetY; | 248 } |
162 | 249 } |
163 var deviceSizeRatio = metadata.deviceHeight / metadata.deviceWidth; | 250 } |
164 var dimensionsCSS = this._viewportDimensions(); | 251 |
165 | 252 /** |
166 this._imageZoom = Math.min(dimensionsCSS.width / this._imageElement.natu
ralWidth, dimensionsCSS.height / (this._imageElement.naturalWidth * deviceSizeRa
tio)); | 253 * @param {!Event} event |
167 this._viewportElement.classList.remove("hidden"); | 254 */ |
168 var bordersSize = WebInspector.ScreencastView._bordersSize; | 255 _handleKeyEvent(event) { |
169 if (this._imageZoom < 1.01 / window.devicePixelRatio) | 256 if (this._isGlassPaneActive()) { |
170 this._imageZoom = 1 / window.devicePixelRatio; | 257 event.consume(); |
171 this._screenZoom = this._imageElement.naturalWidth * this._imageZoom / m
etadata.deviceWidth; | 258 return; |
172 this._viewportElement.style.width = metadata.deviceWidth * this._screenZ
oom + bordersSize + "px"; | 259 } |
173 this._viewportElement.style.height = metadata.deviceHeight * this._scree
nZoom + bordersSize + "px"; | 260 |
174 | 261 var shortcutKey = WebInspector.KeyboardShortcut.makeKeyFromEvent(/** @type {
!KeyboardEvent} */ (event)); |
175 this.highlightDOMNode(this._highlightNode, this._highlightConfig); | 262 var handler = this._shortcuts[shortcutKey]; |
176 }, | 263 if (handler && handler(event)) { |
177 | 264 event.consume(); |
178 _isGlassPaneActive: function() | 265 return; |
179 { | 266 } |
180 return !this._glassPaneElement.classList.contains("hidden"); | 267 |
181 }, | 268 var type; |
| 269 switch (event.type) { |
| 270 case 'keydown': |
| 271 type = 'keyDown'; |
| 272 break; |
| 273 case 'keyup': |
| 274 type = 'keyUp'; |
| 275 break; |
| 276 case 'keypress': |
| 277 type = 'char'; |
| 278 break; |
| 279 default: |
| 280 return; |
| 281 } |
| 282 |
| 283 var text = event.type === 'keypress' ? String.fromCharCode(event.charCode) :
undefined; |
| 284 this._target.inputAgent().invoke_dispatchKeyEvent({ |
| 285 type: type, |
| 286 modifiers: this._modifiersForEvent(event), |
| 287 timestamp: event.timeStamp / 1000, |
| 288 text: text, |
| 289 unmodifiedText: text ? text.toLowerCase() : undefined, |
| 290 keyIdentifier: event.keyIdentifier, |
| 291 code: event.code, |
| 292 key: event.key, |
| 293 windowsVirtualKeyCode: event.keyCode, |
| 294 nativeVirtualKeyCode: event.keyCode, |
| 295 autoRepeat: false, |
| 296 isKeypad: false, |
| 297 isSystemKey: false |
| 298 }); |
| 299 event.consume(); |
| 300 this._canvasElement.focus(); |
| 301 } |
| 302 |
| 303 /** |
| 304 * @param {!Event} event |
| 305 */ |
| 306 _handleContextMenuEvent(event) { |
| 307 event.consume(true); |
| 308 } |
| 309 |
| 310 /** |
| 311 * @param {!Event} event |
| 312 */ |
| 313 _simulateTouchForMouseEvent(event) { |
| 314 const buttons = {0: 'none', 1: 'left', 2: 'middle', 3: 'right'}; |
| 315 const types = { |
| 316 'mousedown': 'mousePressed', |
| 317 'mouseup': 'mouseReleased', |
| 318 'mousemove': 'mouseMoved', |
| 319 'mousewheel': 'mouseWheel' |
| 320 }; |
| 321 if (!(event.type in types) || !(event.which in buttons)) |
| 322 return; |
| 323 if (event.type !== 'mousewheel' && buttons[event.which] === 'none') |
| 324 return; |
| 325 |
| 326 if (event.type === 'mousedown' || typeof this._eventScreenOffsetTop === 'und
efined') |
| 327 this._eventScreenOffsetTop = this._screenOffsetTop; |
| 328 |
| 329 var modifiers = |
| 330 (event.altKey ? 1 : 0) | (event.ctrlKey ? 2 : 0) | (event.metaKey ? 4 :
0) | (event.shiftKey ? 8 : 0); |
| 331 |
| 332 var convertedPosition = this._zoomIntoScreenSpace(event); |
| 333 convertedPosition.y = Math.round(convertedPosition.y - this._eventScreenOffs
etTop); |
| 334 var params = { |
| 335 type: types[event.type], |
| 336 x: convertedPosition.x, |
| 337 y: convertedPosition.y, |
| 338 modifiers: modifiers, |
| 339 timestamp: event.timeStamp / 1000, |
| 340 button: buttons[event.which], |
| 341 clickCount: 0 |
| 342 }; |
| 343 if (event.type === 'mousewheel') { |
| 344 params.deltaX = event.wheelDeltaX / this._screenZoom; |
| 345 params.deltaY = event.wheelDeltaY / this._screenZoom; |
| 346 } else { |
| 347 this._eventParams = params; |
| 348 } |
| 349 if (event.type === 'mouseup') |
| 350 delete this._eventScreenOffsetTop; |
| 351 this._target.inputAgent().invoke_emulateTouchFromMouseEvent(params); |
| 352 } |
| 353 |
| 354 /** |
| 355 * @param {!Event} event |
| 356 */ |
| 357 _handleBlurEvent(event) { |
| 358 if (typeof this._eventScreenOffsetTop !== 'undefined') { |
| 359 var params = this._eventParams; |
| 360 delete this._eventParams; |
| 361 params.type = 'mouseReleased'; |
| 362 this._target.inputAgent().invoke_emulateTouchFromMouseEvent(params); |
| 363 } |
| 364 } |
| 365 |
| 366 /** |
| 367 * @param {!Event} event |
| 368 * @return {!{x: number, y: number}} |
| 369 */ |
| 370 _zoomIntoScreenSpace(event) { |
| 371 var position = {}; |
| 372 position.x = Math.round(event.offsetX / this._screenZoom); |
| 373 position.y = Math.round(event.offsetY / this._screenZoom); |
| 374 return position; |
| 375 } |
| 376 |
| 377 /** |
| 378 * @param {!Event} event |
| 379 * @return {!{x: number, y: number}} |
| 380 */ |
| 381 _convertIntoScreenSpace(event) { |
| 382 var position = this._zoomIntoScreenSpace(event); |
| 383 position.y = Math.round(position.y - this._screenOffsetTop); |
| 384 return position; |
| 385 } |
| 386 |
| 387 /** |
| 388 * @param {!Event} event |
| 389 * @return {number} |
| 390 */ |
| 391 _modifiersForEvent(event) { |
| 392 var modifiers = 0; |
| 393 if (event.altKey) |
| 394 modifiers = 1; |
| 395 if (event.ctrlKey) |
| 396 modifiers += 2; |
| 397 if (event.metaKey) |
| 398 modifiers += 4; |
| 399 if (event.shiftKey) |
| 400 modifiers += 8; |
| 401 return modifiers; |
| 402 } |
| 403 |
| 404 /** |
| 405 * @override |
| 406 */ |
| 407 onResize() { |
| 408 if (this._deferredCasting) { |
| 409 clearTimeout(this._deferredCasting); |
| 410 delete this._deferredCasting; |
| 411 } |
| 412 |
| 413 this._stopCasting(); |
| 414 this._deferredCasting = setTimeout(this._startCasting.bind(this), 100); |
| 415 } |
| 416 |
| 417 /** |
| 418 * @override |
| 419 * @param {?WebInspector.DOMNode} node |
| 420 * @param {?DOMAgent.HighlightConfig} config |
| 421 * @param {!DOMAgent.BackendNodeId=} backendNodeId |
| 422 * @param {!RuntimeAgent.RemoteObjectId=} objectId |
| 423 */ |
| 424 highlightDOMNode(node, config, backendNodeId, objectId) { |
| 425 this._highlightNode = node; |
| 426 this._highlightConfig = config; |
| 427 if (!node) { |
| 428 this._model = null; |
| 429 this._config = null; |
| 430 this._node = null; |
| 431 this._titleElement.classList.add('hidden'); |
| 432 this._repaint(); |
| 433 return; |
| 434 } |
| 435 |
| 436 this._node = node; |
| 437 node.boxModel(callback.bind(this)); |
182 | 438 |
183 /** | 439 /** |
184 * @param {!WebInspector.Event} event | 440 * @param {?DOMAgent.BoxModel} model |
| 441 * @this {WebInspector.ScreencastView} |
185 */ | 442 */ |
186 _screencastVisibilityChanged: function(event) | 443 function callback(model) { |
187 { | 444 if (!model || !this._pageScaleFactor) { |
188 this._targetInactive = !event.data.visible; | 445 this._repaint(); |
189 this._updateGlasspane(); | 446 return; |
190 }, | 447 } |
191 | 448 this._model = this._scaleModel(model); |
192 /** | 449 this._config = config; |
193 * @param {!WebInspector.Event} event | 450 this._repaint(); |
194 */ | 451 } |
195 _onSuspendStateChange: function(event) | 452 } |
196 { | 453 |
197 if (WebInspector.targetManager.allTargetsSuspended()) | 454 /** |
198 this._stopCasting(); | 455 * @param {!DOMAgent.BoxModel} model |
199 else | 456 * @return {!DOMAgent.BoxModel} |
200 this._startCasting(); | 457 */ |
201 this._updateGlasspane(); | 458 _scaleModel(model) { |
202 }, | |
203 | |
204 _updateGlasspane: function() | |
205 { | |
206 if (this._targetInactive) { | |
207 this._glassPaneElement.textContent = WebInspector.UIString("The tab
is inactive"); | |
208 this._glassPaneElement.classList.remove("hidden"); | |
209 } else if (WebInspector.targetManager.allTargetsSuspended()) { | |
210 this._glassPaneElement.textContent = WebInspector.UIString("Profilin
g in progress"); | |
211 this._glassPaneElement.classList.remove("hidden"); | |
212 } else { | |
213 this._glassPaneElement.classList.add("hidden"); | |
214 } | |
215 }, | |
216 | |
217 /** | |
218 * @param {!Event} event | |
219 */ | |
220 _handleMouseEvent: function(event) | |
221 { | |
222 if (this._isGlassPaneActive()) { | |
223 event.consume(); | |
224 return; | |
225 } | |
226 | |
227 if (!this._pageScaleFactor) | |
228 return; | |
229 | |
230 if (!this._inspectModeConfig || event.type === "mousewheel") { | |
231 this._simulateTouchForMouseEvent(event); | |
232 event.preventDefault(); | |
233 if (event.type === "mousedown") | |
234 this._canvasElement.focus(); | |
235 return; | |
236 } | |
237 | |
238 var position = this._convertIntoScreenSpace(event); | |
239 this._domModel.nodeForLocation(position.x / this._pageScaleFactor + this
._scrollOffsetX, position.y / this._pageScaleFactor + this._scrollOffsetY, callb
ack.bind(this)); | |
240 | |
241 /** | |
242 * @param {?WebInspector.DOMNode} node | |
243 * @this {WebInspector.ScreencastView} | |
244 */ | |
245 function callback(node) | |
246 { | |
247 if (!node) | |
248 return; | |
249 if (event.type === "mousemove") { | |
250 this.highlightDOMNode(node, this._inspectModeConfig); | |
251 this._domModel.nodeHighlightRequested(node.id); | |
252 } else if (event.type === "click") { | |
253 WebInspector.Revealer.reveal(node); | |
254 } | |
255 } | |
256 }, | |
257 | |
258 /** | |
259 * @param {!Event} event | |
260 */ | |
261 _handleKeyEvent: function(event) | |
262 { | |
263 if (this._isGlassPaneActive()) { | |
264 event.consume(); | |
265 return; | |
266 } | |
267 | |
268 var shortcutKey = WebInspector.KeyboardShortcut.makeKeyFromEvent(/** @ty
pe {!KeyboardEvent} */ (event)); | |
269 var handler = this._shortcuts[shortcutKey]; | |
270 if (handler && handler(event)) { | |
271 event.consume(); | |
272 return; | |
273 } | |
274 | |
275 var type; | |
276 switch (event.type) { | |
277 case "keydown": type = "keyDown"; break; | |
278 case "keyup": type = "keyUp"; break; | |
279 case "keypress": type = "char"; break; | |
280 default: return; | |
281 } | |
282 | |
283 var text = event.type === "keypress" ? String.fromCharCode(event.charCod
e) : undefined; | |
284 this._target.inputAgent().invoke_dispatchKeyEvent({ | |
285 type: type, | |
286 modifiers: this._modifiersForEvent(event), | |
287 timestamp: event.timeStamp / 1000, | |
288 text: text, | |
289 unmodifiedText: text ? text.toLowerCase() : undefined, | |
290 keyIdentifier: event.keyIdentifier, | |
291 code: event.code, | |
292 key: event.key, | |
293 windowsVirtualKeyCode: event.keyCode, | |
294 nativeVirtualKeyCode: event.keyCode, | |
295 autoRepeat: false, | |
296 isKeypad: false, | |
297 isSystemKey: false}); | |
298 event.consume(); | |
299 this._canvasElement.focus(); | |
300 }, | |
301 | |
302 /** | |
303 * @param {!Event} event | |
304 */ | |
305 _handleContextMenuEvent: function(event) | |
306 { | |
307 event.consume(true); | |
308 }, | |
309 | |
310 /** | |
311 * @param {!Event} event | |
312 */ | |
313 _simulateTouchForMouseEvent: function(event) | |
314 { | |
315 const buttons = {0: "none", 1: "left", 2: "middle", 3: "right"}; | |
316 const types = {"mousedown" : "mousePressed", "mouseup": "mouseReleased",
"mousemove": "mouseMoved", "mousewheel": "mouseWheel"}; | |
317 if (!(event.type in types) || !(event.which in buttons)) | |
318 return; | |
319 if (event.type !== "mousewheel" && buttons[event.which] === "none") | |
320 return; | |
321 | |
322 if (event.type === "mousedown" || typeof this._eventScreenOffsetTop ===
"undefined") | |
323 this._eventScreenOffsetTop = this._screenOffsetTop; | |
324 | |
325 var modifiers = (event.altKey ? 1 : 0) | (event.ctrlKey ? 2 : 0) | (even
t.metaKey ? 4 : 0) | (event.shiftKey ? 8 : 0); | |
326 | |
327 var convertedPosition = this._zoomIntoScreenSpace(event); | |
328 convertedPosition.y = Math.round(convertedPosition.y - this._eventScreen
OffsetTop); | |
329 var params = {type: types[event.type], x: convertedPosition.x, y: conver
tedPosition.y, modifiers: modifiers, timestamp: event.timeStamp / 1000, button:
buttons[event.which], clickCount: 0}; | |
330 if (event.type === "mousewheel") { | |
331 params.deltaX = event.wheelDeltaX / this._screenZoom; | |
332 params.deltaY = event.wheelDeltaY / this._screenZoom; | |
333 } else { | |
334 this._eventParams = params; | |
335 } | |
336 if (event.type === "mouseup") | |
337 delete this._eventScreenOffsetTop; | |
338 this._target.inputAgent().invoke_emulateTouchFromMouseEvent(params); | |
339 }, | |
340 | |
341 /** | |
342 * @param {!Event} event | |
343 */ | |
344 _handleBlurEvent: function(event) | |
345 { | |
346 if (typeof this._eventScreenOffsetTop !== "undefined") { | |
347 var params = this._eventParams; | |
348 delete this._eventParams; | |
349 params.type = "mouseReleased"; | |
350 this._target.inputAgent().invoke_emulateTouchFromMouseEvent(params); | |
351 } | |
352 }, | |
353 | |
354 /** | |
355 * @param {!Event} event | |
356 * @return {!{x: number, y: number}} | |
357 */ | |
358 _zoomIntoScreenSpace: function(event) | |
359 { | |
360 var position = {}; | |
361 position.x = Math.round(event.offsetX / this._screenZoom); | |
362 position.y = Math.round(event.offsetY / this._screenZoom); | |
363 return position; | |
364 }, | |
365 | |
366 /** | |
367 * @param {!Event} event | |
368 * @return {!{x: number, y: number}} | |
369 */ | |
370 _convertIntoScreenSpace: function(event) | |
371 { | |
372 var position = this._zoomIntoScreenSpace(event); | |
373 position.y = Math.round(position.y - this._screenOffsetTop); | |
374 return position; | |
375 }, | |
376 | |
377 /** | |
378 * @param {!Event} event | |
379 * @return {number} | |
380 */ | |
381 _modifiersForEvent: function(event) | |
382 { | |
383 var modifiers = 0; | |
384 if (event.altKey) | |
385 modifiers = 1; | |
386 if (event.ctrlKey) | |
387 modifiers += 2; | |
388 if (event.metaKey) | |
389 modifiers += 4; | |
390 if (event.shiftKey) | |
391 modifiers += 8; | |
392 return modifiers; | |
393 }, | |
394 | |
395 onResize: function() | |
396 { | |
397 if (this._deferredCasting) { | |
398 clearTimeout(this._deferredCasting); | |
399 delete this._deferredCasting; | |
400 } | |
401 | |
402 this._stopCasting(); | |
403 this._deferredCasting = setTimeout(this._startCasting.bind(this), 100); | |
404 }, | |
405 | |
406 /** | |
407 * @override | |
408 * @param {?WebInspector.DOMNode} node | |
409 * @param {?DOMAgent.HighlightConfig} config | |
410 * @param {!DOMAgent.BackendNodeId=} backendNodeId | |
411 * @param {!RuntimeAgent.RemoteObjectId=} objectId | |
412 */ | |
413 highlightDOMNode: function(node, config, backendNodeId, objectId) | |
414 { | |
415 this._highlightNode = node; | |
416 this._highlightConfig = config; | |
417 if (!node) { | |
418 this._model = null; | |
419 this._config = null; | |
420 this._node = null; | |
421 this._titleElement.classList.add("hidden"); | |
422 this._repaint(); | |
423 return; | |
424 } | |
425 | |
426 this._node = node; | |
427 node.boxModel(callback.bind(this)); | |
428 | |
429 /** | |
430 * @param {?DOMAgent.BoxModel} model | |
431 * @this {WebInspector.ScreencastView} | |
432 */ | |
433 function callback(model) | |
434 { | |
435 if (!model || !this._pageScaleFactor) { | |
436 this._repaint(); | |
437 return; | |
438 } | |
439 this._model = this._scaleModel(model); | |
440 this._config = config; | |
441 this._repaint(); | |
442 } | |
443 }, | |
444 | |
445 /** | |
446 * @param {!DOMAgent.BoxModel} model | |
447 * @return {!DOMAgent.BoxModel} | |
448 */ | |
449 _scaleModel: function(model) | |
450 { | |
451 /** | |
452 * @param {!DOMAgent.Quad} quad | |
453 * @this {WebInspector.ScreencastView} | |
454 */ | |
455 function scaleQuad(quad) | |
456 { | |
457 for (var i = 0; i < quad.length; i += 2) { | |
458 quad[i] = quad[i] * this._screenZoom; | |
459 quad[i + 1] = (quad[i + 1] + this._screenOffsetTop) * this._scre
enZoom; | |
460 } | |
461 } | |
462 | |
463 scaleQuad.call(this, model.content); | |
464 scaleQuad.call(this, model.padding); | |
465 scaleQuad.call(this, model.border); | |
466 scaleQuad.call(this, model.margin); | |
467 return model; | |
468 }, | |
469 | |
470 _repaint: function() | |
471 { | |
472 var model = this._model; | |
473 var config = this._config; | |
474 | |
475 var canvasWidth = this._canvasElement.getBoundingClientRect().width; | |
476 var canvasHeight = this._canvasElement.getBoundingClientRect().height; | |
477 this._canvasElement.width = window.devicePixelRatio * canvasWidth; | |
478 this._canvasElement.height = window.devicePixelRatio * canvasHeight; | |
479 | |
480 this._context.save(); | |
481 this._context.scale(window.devicePixelRatio, window.devicePixelRatio); | |
482 | |
483 // Paint top and bottom gutter. | |
484 this._context.save(); | |
485 this._context.fillStyle = this._checkerboardPattern; | |
486 this._context.fillRect(0, 0, canvasWidth, this._screenOffsetTop * this._
screenZoom); | |
487 this._context.fillRect(0, this._screenOffsetTop * this._screenZoom + thi
s._imageElement.naturalHeight * this._imageZoom, canvasWidth, canvasHeight); | |
488 this._context.restore(); | |
489 | |
490 if (model && config) { | |
491 this._context.save(); | |
492 const transparentColor = "rgba(0, 0, 0, 0)"; | |
493 var quads = []; | |
494 if (model.content && config.contentColor !== transparentColor) | |
495 quads.push({quad: model.content, color: config.contentColor}); | |
496 if (model.padding && config.paddingColor !== transparentColor) | |
497 quads.push({quad: model.padding, color: config.paddingColor}); | |
498 if (model.border && config.borderColor !== transparentColor) | |
499 quads.push({quad: model.border, color: config.borderColor}); | |
500 if (model.margin && config.marginColor !== transparentColor) | |
501 quads.push({quad: model.margin, color: config.marginColor}); | |
502 | |
503 for (var i = quads.length - 1; i > 0; --i) | |
504 this._drawOutlinedQuadWithClip(quads[i].quad, quads[i - 1].quad,
quads[i].color); | |
505 if (quads.length > 0) | |
506 this._drawOutlinedQuad(quads[0].quad, quads[0].color); | |
507 this._context.restore(); | |
508 | |
509 this._drawElementTitle(); | |
510 | |
511 this._context.globalCompositeOperation = "destination-over"; | |
512 } | |
513 | |
514 this._context.drawImage(this._imageElement, 0, this._screenOffsetTop * t
his._screenZoom, this._imageElement.naturalWidth * this._imageZoom, this._imageE
lement.naturalHeight * this._imageZoom); | |
515 this._context.restore(); | |
516 | |
517 }, | |
518 | |
519 /** | |
520 * @param {!DOMAgent.RGBA} color | |
521 * @return {string} | |
522 */ | |
523 _cssColor: function(color) | |
524 { | |
525 if (!color) | |
526 return "transparent"; | |
527 return WebInspector.Color.fromRGBA([color.r, color.g, color.b, color.a])
.asString(WebInspector.Color.Format.RGBA) || ""; | |
528 }, | |
529 | |
530 /** | 459 /** |
531 * @param {!DOMAgent.Quad} quad | 460 * @param {!DOMAgent.Quad} quad |
532 * @return {!CanvasRenderingContext2D} | 461 * @this {WebInspector.ScreencastView} |
533 */ | 462 */ |
534 _quadToPath: function(quad) | 463 function scaleQuad(quad) { |
535 { | 464 for (var i = 0; i < quad.length; i += 2) { |
536 this._context.beginPath(); | 465 quad[i] = quad[i] * this._screenZoom; |
537 this._context.moveTo(quad[0], quad[1]); | 466 quad[i + 1] = (quad[i + 1] + this._screenOffsetTop) * this._screenZoom; |
538 this._context.lineTo(quad[2], quad[3]); | 467 } |
539 this._context.lineTo(quad[4], quad[5]); | 468 } |
540 this._context.lineTo(quad[6], quad[7]); | 469 |
541 this._context.closePath(); | 470 scaleQuad.call(this, model.content); |
542 return this._context; | 471 scaleQuad.call(this, model.padding); |
543 }, | 472 scaleQuad.call(this, model.border); |
544 | 473 scaleQuad.call(this, model.margin); |
545 /** | 474 return model; |
546 * @param {!DOMAgent.Quad} quad | 475 } |
547 * @param {!DOMAgent.RGBA} fillColor | 476 |
548 */ | 477 _repaint() { |
549 _drawOutlinedQuad: function(quad, fillColor) | 478 var model = this._model; |
550 { | 479 var config = this._config; |
551 this._context.save(); | 480 |
552 this._context.lineWidth = 2; | 481 var canvasWidth = this._canvasElement.getBoundingClientRect().width; |
553 this._quadToPath(quad).clip(); | 482 var canvasHeight = this._canvasElement.getBoundingClientRect().height; |
554 this._context.fillStyle = this._cssColor(fillColor); | 483 this._canvasElement.width = window.devicePixelRatio * canvasWidth; |
555 this._context.fill(); | 484 this._canvasElement.height = window.devicePixelRatio * canvasHeight; |
556 this._context.restore(); | 485 |
557 }, | 486 this._context.save(); |
558 | 487 this._context.scale(window.devicePixelRatio, window.devicePixelRatio); |
559 /** | 488 |
560 * @param {!DOMAgent.Quad} quad | 489 // Paint top and bottom gutter. |
561 * @param {!DOMAgent.Quad} clipQuad | 490 this._context.save(); |
562 * @param {!DOMAgent.RGBA} fillColor | 491 this._context.fillStyle = this._checkerboardPattern; |
563 */ | 492 this._context.fillRect(0, 0, canvasWidth, this._screenOffsetTop * this._scre
enZoom); |
564 _drawOutlinedQuadWithClip: function(quad, clipQuad, fillColor) | 493 this._context.fillRect( |
565 { | 494 0, this._screenOffsetTop * this._screenZoom + this._imageElement.natural
Height * this._imageZoom, canvasWidth, |
566 this._context.fillStyle = this._cssColor(fillColor); | 495 canvasHeight); |
567 this._context.save(); | 496 this._context.restore(); |
568 this._context.lineWidth = 0; | 497 |
569 this._quadToPath(quad).fill(); | 498 if (model && config) { |
570 this._context.globalCompositeOperation = "destination-out"; | 499 this._context.save(); |
571 this._context.fillStyle = "red"; | 500 const transparentColor = 'rgba(0, 0, 0, 0)'; |
572 this._quadToPath(clipQuad).fill(); | 501 var quads = []; |
573 this._context.restore(); | 502 if (model.content && config.contentColor !== transparentColor) |
574 }, | 503 quads.push({quad: model.content, color: config.contentColor}); |
575 | 504 if (model.padding && config.paddingColor !== transparentColor) |
576 _drawElementTitle: function() | 505 quads.push({quad: model.padding, color: config.paddingColor}); |
577 { | 506 if (model.border && config.borderColor !== transparentColor) |
578 if (!this._node) | 507 quads.push({quad: model.border, color: config.borderColor}); |
579 return; | 508 if (model.margin && config.marginColor !== transparentColor) |
580 | 509 quads.push({quad: model.margin, color: config.marginColor}); |
581 var canvasWidth = this._canvasElement.getBoundingClientRect().width; | 510 |
582 var canvasHeight = this._canvasElement.getBoundingClientRect().height; | 511 for (var i = quads.length - 1; i > 0; --i) |
583 | 512 this._drawOutlinedQuadWithClip(quads[i].quad, quads[i - 1].quad, quads[i
].color); |
584 var lowerCaseName = this._node.localName() || this._node.nodeName().toLo
werCase(); | 513 if (quads.length > 0) |
585 this._tagNameElement.textContent = lowerCaseName; | 514 this._drawOutlinedQuad(quads[0].quad, quads[0].color); |
586 this._nodeIdElement.textContent = this._node.getAttribute("id") ? "#" +
this._node.getAttribute("id") : ""; | 515 this._context.restore(); |
587 this._nodeIdElement.textContent = this._node.getAttribute("id") ? "#" +
this._node.getAttribute("id") : ""; | 516 |
588 var className = this._node.getAttribute("class"); | 517 this._drawElementTitle(); |
589 if (className && className.length > 50) | 518 |
590 className = className.substring(0, 50) + "\u2026"; | 519 this._context.globalCompositeOperation = 'destination-over'; |
591 this._classNameElement.textContent = className || ""; | 520 } |
592 this._nodeWidthElement.textContent = this._model.width; | 521 |
593 this._nodeHeightElement.textContent = this._model.height; | 522 this._context.drawImage( |
594 | 523 this._imageElement, 0, this._screenOffsetTop * this._screenZoom, |
595 this._titleElement.classList.remove("hidden"); | 524 this._imageElement.naturalWidth * this._imageZoom, this._imageElement.na
turalHeight * this._imageZoom); |
596 var titleWidth = this._titleElement.offsetWidth + 6; | 525 this._context.restore(); |
597 var titleHeight = this._titleElement.offsetHeight + 4; | 526 } |
598 | 527 |
599 var anchorTop = this._model.margin[1]; | 528 /** |
600 var anchorBottom = this._model.margin[7]; | 529 * @param {!DOMAgent.RGBA} color |
601 | 530 * @return {string} |
602 const arrowHeight = 7; | 531 */ |
603 var renderArrowUp = false; | 532 _cssColor(color) { |
604 var renderArrowDown = false; | 533 if (!color) |
605 | 534 return 'transparent'; |
606 var boxX = Math.max(2, this._model.margin[0]); | 535 return WebInspector.Color.fromRGBA([color.r, color.g, color.b, color.a]).asS
tring(WebInspector.Color.Format.RGBA) || |
607 if (boxX + titleWidth > canvasWidth) | 536 ''; |
608 boxX = canvasWidth - titleWidth - 2; | 537 } |
609 | 538 |
610 var boxY; | 539 /** |
611 if (anchorTop > canvasHeight) { | 540 * @param {!DOMAgent.Quad} quad |
612 boxY = canvasHeight - titleHeight - arrowHeight; | 541 * @return {!CanvasRenderingContext2D} |
613 renderArrowDown = true; | 542 */ |
614 } else if (anchorBottom < 0) { | 543 _quadToPath(quad) { |
615 boxY = arrowHeight; | 544 this._context.beginPath(); |
616 renderArrowUp = true; | 545 this._context.moveTo(quad[0], quad[1]); |
617 } else if (anchorBottom + titleHeight + arrowHeight < canvasHeight) { | 546 this._context.lineTo(quad[2], quad[3]); |
618 boxY = anchorBottom + arrowHeight - 4; | 547 this._context.lineTo(quad[4], quad[5]); |
619 renderArrowUp = true; | 548 this._context.lineTo(quad[6], quad[7]); |
620 } else if (anchorTop - titleHeight - arrowHeight > 0) { | 549 this._context.closePath(); |
621 boxY = anchorTop - titleHeight - arrowHeight + 3; | 550 return this._context; |
622 renderArrowDown = true; | 551 } |
623 } else | 552 |
624 boxY = arrowHeight; | 553 /** |
625 | 554 * @param {!DOMAgent.Quad} quad |
626 this._context.save(); | 555 * @param {!DOMAgent.RGBA} fillColor |
627 this._context.translate(0.5, 0.5); | 556 */ |
628 this._context.beginPath(); | 557 _drawOutlinedQuad(quad, fillColor) { |
629 this._context.moveTo(boxX, boxY); | 558 this._context.save(); |
630 if (renderArrowUp) { | 559 this._context.lineWidth = 2; |
631 this._context.lineTo(boxX + 2 * arrowHeight, boxY); | 560 this._quadToPath(quad).clip(); |
632 this._context.lineTo(boxX + 3 * arrowHeight, boxY - arrowHeight); | 561 this._context.fillStyle = this._cssColor(fillColor); |
633 this._context.lineTo(boxX + 4 * arrowHeight, boxY); | 562 this._context.fill(); |
634 } | 563 this._context.restore(); |
635 this._context.lineTo(boxX + titleWidth, boxY); | 564 } |
636 this._context.lineTo(boxX + titleWidth, boxY + titleHeight); | 565 |
637 if (renderArrowDown) { | 566 /** |
638 this._context.lineTo(boxX + 4 * arrowHeight, boxY + titleHeight); | 567 * @param {!DOMAgent.Quad} quad |
639 this._context.lineTo(boxX + 3 * arrowHeight, boxY + titleHeight + ar
rowHeight); | 568 * @param {!DOMAgent.Quad} clipQuad |
640 this._context.lineTo(boxX + 2 * arrowHeight, boxY + titleHeight); | 569 * @param {!DOMAgent.RGBA} fillColor |
641 } | 570 */ |
642 this._context.lineTo(boxX, boxY + titleHeight); | 571 _drawOutlinedQuadWithClip(quad, clipQuad, fillColor) { |
643 this._context.closePath(); | 572 this._context.fillStyle = this._cssColor(fillColor); |
644 this._context.fillStyle = "rgb(255, 255, 194)"; | 573 this._context.save(); |
645 this._context.fill(); | 574 this._context.lineWidth = 0; |
646 this._context.strokeStyle = "rgb(128, 128, 128)"; | 575 this._quadToPath(quad).fill(); |
647 this._context.stroke(); | 576 this._context.globalCompositeOperation = 'destination-out'; |
648 | 577 this._context.fillStyle = 'red'; |
649 this._context.restore(); | 578 this._quadToPath(clipQuad).fill(); |
650 | 579 this._context.restore(); |
651 this._titleElement.style.top = (boxY + 3) + "px"; | 580 } |
652 this._titleElement.style.left = (boxX + 3) + "px"; | 581 |
653 }, | 582 _drawElementTitle() { |
654 | 583 if (!this._node) |
655 /** | 584 return; |
656 * @return {!{width: number, height: number}} | 585 |
657 */ | 586 var canvasWidth = this._canvasElement.getBoundingClientRect().width; |
658 _viewportDimensions: function() | 587 var canvasHeight = this._canvasElement.getBoundingClientRect().height; |
659 { | 588 |
660 const gutterSize = 30; | 589 var lowerCaseName = this._node.localName() || this._node.nodeName().toLowerC
ase(); |
661 const bordersSize = WebInspector.ScreencastView._bordersSize; | 590 this._tagNameElement.textContent = lowerCaseName; |
662 var width = this.element.offsetWidth - bordersSize - gutterSize; | 591 this._nodeIdElement.textContent = this._node.getAttribute('id') ? '#' + this
._node.getAttribute('id') : ''; |
663 var height = this.element.offsetHeight - bordersSize - gutterSize - WebI
nspector.ScreencastView._navBarHeight; | 592 this._nodeIdElement.textContent = this._node.getAttribute('id') ? '#' + this
._node.getAttribute('id') : ''; |
664 return { width: width, height: height }; | 593 var className = this._node.getAttribute('class'); |
665 }, | 594 if (className && className.length > 50) |
666 | 595 className = className.substring(0, 50) + '\u2026'; |
667 /** | 596 this._classNameElement.textContent = className || ''; |
668 * @override | 597 this._nodeWidthElement.textContent = this._model.width; |
669 * @param {!DOMAgent.InspectMode} mode | 598 this._nodeHeightElement.textContent = this._model.height; |
670 * @param {!DOMAgent.HighlightConfig} config | 599 |
671 * @param {function(?Protocol.Error)=} callback | 600 this._titleElement.classList.remove('hidden'); |
672 */ | 601 var titleWidth = this._titleElement.offsetWidth + 6; |
673 setInspectMode: function(mode, config, callback) | 602 var titleHeight = this._titleElement.offsetHeight + 4; |
674 { | 603 |
675 this._inspectModeConfig = mode !== DOMAgent.InspectMode.None ? config :
null; | 604 var anchorTop = this._model.margin[1]; |
676 if (callback) | 605 var anchorBottom = this._model.margin[7]; |
677 callback(null); | 606 |
678 }, | 607 const arrowHeight = 7; |
679 | 608 var renderArrowUp = false; |
680 /** | 609 var renderArrowDown = false; |
681 * @override | 610 |
682 * @param {!PageAgent.FrameId} frameId | 611 var boxX = Math.max(2, this._model.margin[0]); |
683 */ | 612 if (boxX + titleWidth > canvasWidth) |
684 highlightFrame: function(frameId) | 613 boxX = canvasWidth - titleWidth - 2; |
685 { | 614 |
686 }, | 615 var boxY; |
687 | 616 if (anchorTop > canvasHeight) { |
688 /** | 617 boxY = canvasHeight - titleHeight - arrowHeight; |
689 * @param {!CanvasRenderingContext2D} context | 618 renderArrowDown = true; |
690 */ | 619 } else if (anchorBottom < 0) { |
691 _createCheckerboardPattern: function(context) | 620 boxY = arrowHeight; |
692 { | 621 renderArrowUp = true; |
693 var pattern = /** @type {!HTMLCanvasElement} */(createElement("canvas"))
; | 622 } else if (anchorBottom + titleHeight + arrowHeight < canvasHeight) { |
694 const size = 32; | 623 boxY = anchorBottom + arrowHeight - 4; |
695 pattern.width = size * 2; | 624 renderArrowUp = true; |
696 pattern.height = size * 2; | 625 } else if (anchorTop - titleHeight - arrowHeight > 0) { |
697 var pctx = pattern.getContext("2d"); | 626 boxY = anchorTop - titleHeight - arrowHeight + 3; |
698 | 627 renderArrowDown = true; |
699 pctx.fillStyle = "rgb(195, 195, 195)"; | 628 } else |
700 pctx.fillRect(0, 0, size * 2, size * 2); | 629 boxY = arrowHeight; |
701 | 630 |
702 pctx.fillStyle = "rgb(225, 225, 225)"; | 631 this._context.save(); |
703 pctx.fillRect(0, 0, size, size); | 632 this._context.translate(0.5, 0.5); |
704 pctx.fillRect(size, size, size, size); | 633 this._context.beginPath(); |
705 return context.createPattern(pattern, "repeat"); | 634 this._context.moveTo(boxX, boxY); |
706 }, | 635 if (renderArrowUp) { |
707 | 636 this._context.lineTo(boxX + 2 * arrowHeight, boxY); |
708 _createNavigationBar: function() | 637 this._context.lineTo(boxX + 3 * arrowHeight, boxY - arrowHeight); |
709 { | 638 this._context.lineTo(boxX + 4 * arrowHeight, boxY); |
710 this._navigationBar = this.element.createChild("div", "screencast-naviga
tion"); | 639 } |
711 | 640 this._context.lineTo(boxX + titleWidth, boxY); |
712 this._navigationBack = this._navigationBar.createChild("button", "back")
; | 641 this._context.lineTo(boxX + titleWidth, boxY + titleHeight); |
713 this._navigationBack.disabled = true; | 642 if (renderArrowDown) { |
714 this._navigationBack.addEventListener("click", this._navigateToHistoryEn
try.bind(this, -1), false); | 643 this._context.lineTo(boxX + 4 * arrowHeight, boxY + titleHeight); |
715 | 644 this._context.lineTo(boxX + 3 * arrowHeight, boxY + titleHeight + arrowHei
ght); |
716 this._navigationForward = this._navigationBar.createChild("button", "for
ward"); | 645 this._context.lineTo(boxX + 2 * arrowHeight, boxY + titleHeight); |
717 this._navigationForward.disabled = true; | 646 } |
718 this._navigationForward.addEventListener("click", this._navigateToHistor
yEntry.bind(this, 1), false); | 647 this._context.lineTo(boxX, boxY + titleHeight); |
719 | 648 this._context.closePath(); |
720 this._navigationReload = this._navigationBar.createChild("button", "relo
ad"); | 649 this._context.fillStyle = 'rgb(255, 255, 194)'; |
721 this._navigationReload.addEventListener("click", this._navigateReload.bi
nd(this), false); | 650 this._context.fill(); |
722 | 651 this._context.strokeStyle = 'rgb(128, 128, 128)'; |
723 this._navigationUrl = this._navigationBar.createChild("input"); | 652 this._context.stroke(); |
724 this._navigationUrl.type = "text"; | 653 |
725 this._navigationUrl.addEventListener("keyup", this._navigationUrlKeyUp.b
ind(this), true); | 654 this._context.restore(); |
726 | 655 |
727 this._navigationProgressBar = new WebInspector.ScreencastView.ProgressTr
acker(this._navigationBar.createChild("div", "progress")); | 656 this._titleElement.style.top = (boxY + 3) + 'px'; |
728 | 657 this._titleElement.style.left = (boxX + 3) + 'px'; |
729 this._requestNavigationHistory(); | 658 } |
730 WebInspector.targetManager.addEventListener(WebInspector.TargetManager.E
vents.InspectedURLChanged, this._requestNavigationHistory, this); | 659 |
731 }, | 660 /** |
732 | 661 * @return {!{width: number, height: number}} |
733 _navigateToHistoryEntry: function(offset) | 662 */ |
734 { | 663 _viewportDimensions() { |
735 var newIndex = this._historyIndex + offset; | 664 const gutterSize = 30; |
736 if (newIndex < 0 || newIndex >= this._historyEntries.length) | 665 const bordersSize = WebInspector.ScreencastView._bordersSize; |
737 return; | 666 var width = this.element.offsetWidth - bordersSize - gutterSize; |
738 this._target.pageAgent().navigateToHistoryEntry(this._historyEntries[new
Index].id); | 667 var height = this.element.offsetHeight - bordersSize - gutterSize - WebInspe
ctor.ScreencastView._navBarHeight; |
739 this._requestNavigationHistory(); | 668 return {width: width, height: height}; |
740 }, | 669 } |
741 | 670 |
742 _navigateReload: function() | 671 /** |
743 { | 672 * @override |
744 this._resourceTreeModel.reloadPage(); | 673 * @param {!DOMAgent.InspectMode} mode |
745 }, | 674 * @param {!DOMAgent.HighlightConfig} config |
746 | 675 * @param {function(?Protocol.Error)=} callback |
747 _navigationUrlKeyUp: function(event) | 676 */ |
748 { | 677 setInspectMode(mode, config, callback) { |
749 if (event.key !== "Enter") | 678 this._inspectModeConfig = mode !== DOMAgent.InspectMode.None ? config : null
; |
750 return; | 679 if (callback) |
751 var url = this._navigationUrl.value; | 680 callback(null); |
752 if (!url) | 681 } |
753 return; | 682 |
754 if (!url.match(WebInspector.ScreencastView._SchemeRegex)) | 683 /** |
755 url = "http://" + url; | 684 * @override |
756 this._target.pageAgent().navigate(url); | 685 * @param {!PageAgent.FrameId} frameId |
757 this._canvasElement.focus(); | 686 */ |
758 }, | 687 highlightFrame(frameId) { |
759 | 688 } |
760 _requestNavigationHistory: function() | 689 |
761 { | 690 /** |
762 this._target.pageAgent().getNavigationHistory(this._onNavigationHistory.
bind(this)); | 691 * @param {!CanvasRenderingContext2D} context |
763 }, | 692 */ |
764 | 693 _createCheckerboardPattern(context) { |
765 _onNavigationHistory: function(error, currentIndex, entries) | 694 var pattern = /** @type {!HTMLCanvasElement} */ (createElement('canvas')); |
766 { | 695 const size = 32; |
767 if (error) | 696 pattern.width = size * 2; |
768 return; | 697 pattern.height = size * 2; |
769 | 698 var pctx = pattern.getContext('2d'); |
770 this._historyIndex = currentIndex; | 699 |
771 this._historyEntries = entries; | 700 pctx.fillStyle = 'rgb(195, 195, 195)'; |
772 | 701 pctx.fillRect(0, 0, size * 2, size * 2); |
773 this._navigationBack.disabled = currentIndex === 0; | 702 |
774 this._navigationForward.disabled = currentIndex === (entries.length - 1)
; | 703 pctx.fillStyle = 'rgb(225, 225, 225)'; |
775 | 704 pctx.fillRect(0, 0, size, size); |
776 var url = entries[currentIndex].url; | 705 pctx.fillRect(size, size, size, size); |
777 var match = url.match(WebInspector.ScreencastView._HttpRegex); | 706 return context.createPattern(pattern, 'repeat'); |
778 if (match) | 707 } |
779 url = match[1]; | 708 |
780 InspectorFrontendHost.inspectedURLChanged(url); | 709 _createNavigationBar() { |
781 this._navigationUrl.value = url; | 710 this._navigationBar = this.element.createChild('div', 'screencast-navigation
'); |
782 }, | 711 |
783 | 712 this._navigationBack = this._navigationBar.createChild('button', 'back'); |
784 _focusNavigationBar: function() | 713 this._navigationBack.disabled = true; |
785 { | 714 this._navigationBack.addEventListener('click', this._navigateToHistoryEntry.
bind(this, -1), false); |
786 this._navigationUrl.focus(); | 715 |
787 this._navigationUrl.select(); | 716 this._navigationForward = this._navigationBar.createChild('button', 'forward
'); |
788 return true; | 717 this._navigationForward.disabled = true; |
789 }, | 718 this._navigationForward.addEventListener('click', this._navigateToHistoryEnt
ry.bind(this, 1), false); |
790 | 719 |
791 __proto__: WebInspector.VBox.prototype | 720 this._navigationReload = this._navigationBar.createChild('button', 'reload')
; |
| 721 this._navigationReload.addEventListener('click', this._navigateReload.bind(t
his), false); |
| 722 |
| 723 this._navigationUrl = this._navigationBar.createChild('input'); |
| 724 this._navigationUrl.type = 'text'; |
| 725 this._navigationUrl.addEventListener('keyup', this._navigationUrlKeyUp.bind(
this), true); |
| 726 |
| 727 this._navigationProgressBar = |
| 728 new WebInspector.ScreencastView.ProgressTracker(this._navigationBar.crea
teChild('div', 'progress')); |
| 729 |
| 730 this._requestNavigationHistory(); |
| 731 WebInspector.targetManager.addEventListener( |
| 732 WebInspector.TargetManager.Events.InspectedURLChanged, this._requestNavi
gationHistory, this); |
| 733 } |
| 734 |
| 735 _navigateToHistoryEntry(offset) { |
| 736 var newIndex = this._historyIndex + offset; |
| 737 if (newIndex < 0 || newIndex >= this._historyEntries.length) |
| 738 return; |
| 739 this._target.pageAgent().navigateToHistoryEntry(this._historyEntries[newInde
x].id); |
| 740 this._requestNavigationHistory(); |
| 741 } |
| 742 |
| 743 _navigateReload() { |
| 744 this._resourceTreeModel.reloadPage(); |
| 745 } |
| 746 |
| 747 _navigationUrlKeyUp(event) { |
| 748 if (event.key !== 'Enter') |
| 749 return; |
| 750 var url = this._navigationUrl.value; |
| 751 if (!url) |
| 752 return; |
| 753 if (!url.match(WebInspector.ScreencastView._SchemeRegex)) |
| 754 url = 'http://' + url; |
| 755 this._target.pageAgent().navigate(url); |
| 756 this._canvasElement.focus(); |
| 757 } |
| 758 |
| 759 _requestNavigationHistory() { |
| 760 this._target.pageAgent().getNavigationHistory(this._onNavigationHistory.bind
(this)); |
| 761 } |
| 762 |
| 763 _onNavigationHistory(error, currentIndex, entries) { |
| 764 if (error) |
| 765 return; |
| 766 |
| 767 this._historyIndex = currentIndex; |
| 768 this._historyEntries = entries; |
| 769 |
| 770 this._navigationBack.disabled = currentIndex === 0; |
| 771 this._navigationForward.disabled = currentIndex === (entries.length - 1); |
| 772 |
| 773 var url = entries[currentIndex].url; |
| 774 var match = url.match(WebInspector.ScreencastView._HttpRegex); |
| 775 if (match) |
| 776 url = match[1]; |
| 777 InspectorFrontendHost.inspectedURLChanged(url); |
| 778 this._navigationUrl.value = url; |
| 779 } |
| 780 |
| 781 _focusNavigationBar() { |
| 782 this._navigationUrl.focus(); |
| 783 this._navigationUrl.select(); |
| 784 return true; |
| 785 } |
792 }; | 786 }; |
793 | 787 |
| 788 WebInspector.ScreencastView._bordersSize = 44; |
| 789 |
| 790 WebInspector.ScreencastView._navBarHeight = 29; |
| 791 |
| 792 WebInspector.ScreencastView._HttpRegex = /^http:\/\/(.+)/; |
| 793 |
| 794 WebInspector.ScreencastView._SchemeRegex = /^(https?|about|chrome):/; |
| 795 |
794 /** | 796 /** |
795 * @param {!Element} element | 797 * @unrestricted |
796 * @constructor | |
797 */ | 798 */ |
798 WebInspector.ScreencastView.ProgressTracker = function(element) | 799 WebInspector.ScreencastView.ProgressTracker = class { |
799 { | 800 /** |
| 801 * @param {!Element} element |
| 802 */ |
| 803 constructor(element) { |
800 this._element = element; | 804 this._element = element; |
801 | 805 |
802 WebInspector.targetManager.addModelListener(WebInspector.ResourceTreeModel,
WebInspector.ResourceTreeModel.Events.MainFrameNavigated, this._onMainFrameNavig
ated, this); | 806 WebInspector.targetManager.addModelListener( |
803 WebInspector.targetManager.addModelListener(WebInspector.ResourceTreeModel,
WebInspector.ResourceTreeModel.Events.Load, this._onLoad, this); | 807 WebInspector.ResourceTreeModel, WebInspector.ResourceTreeModel.Events.Ma
inFrameNavigated, |
804 WebInspector.targetManager.addModelListener(WebInspector.NetworkManager, Web
Inspector.NetworkManager.Events.RequestStarted, this._onRequestStarted, this); | 808 this._onMainFrameNavigated, this); |
805 WebInspector.targetManager.addModelListener(WebInspector.NetworkManager, Web
Inspector.NetworkManager.Events.RequestFinished, this._onRequestFinished, this); | 809 WebInspector.targetManager.addModelListener( |
| 810 WebInspector.ResourceTreeModel, WebInspector.ResourceTreeModel.Events.Lo
ad, this._onLoad, this); |
| 811 WebInspector.targetManager.addModelListener( |
| 812 WebInspector.NetworkManager, WebInspector.NetworkManager.Events.RequestS
tarted, this._onRequestStarted, this); |
| 813 WebInspector.targetManager.addModelListener( |
| 814 WebInspector.NetworkManager, WebInspector.NetworkManager.Events.RequestF
inished, this._onRequestFinished, this); |
| 815 } |
| 816 |
| 817 _onMainFrameNavigated() { |
| 818 this._requestIds = {}; |
| 819 this._startedRequests = 0; |
| 820 this._finishedRequests = 0; |
| 821 this._maxDisplayedProgress = 0; |
| 822 this._updateProgress(0.1); // Display first 10% on navigation start. |
| 823 } |
| 824 |
| 825 _onLoad() { |
| 826 delete this._requestIds; |
| 827 this._updateProgress(1); // Display 100% progress on load, hide it in 0.5s. |
| 828 setTimeout(function() { |
| 829 if (!this._navigationProgressVisible()) |
| 830 this._displayProgress(0); |
| 831 }.bind(this), 500); |
| 832 } |
| 833 |
| 834 _navigationProgressVisible() { |
| 835 return !!this._requestIds; |
| 836 } |
| 837 |
| 838 _onRequestStarted(event) { |
| 839 if (!this._navigationProgressVisible()) |
| 840 return; |
| 841 var request = /** @type {!WebInspector.NetworkRequest} */ (event.data); |
| 842 // Ignore long-living WebSockets for the sake of progress indicator, as we w
on't be waiting them anyway. |
| 843 if (request.type === WebInspector.resourceTypes.WebSocket) |
| 844 return; |
| 845 this._requestIds[request.requestId] = request; |
| 846 ++this._startedRequests; |
| 847 } |
| 848 |
| 849 _onRequestFinished(event) { |
| 850 if (!this._navigationProgressVisible()) |
| 851 return; |
| 852 var request = /** @type {!WebInspector.NetworkRequest} */ (event.data); |
| 853 if (!(request.requestId in this._requestIds)) |
| 854 return; |
| 855 ++this._finishedRequests; |
| 856 setTimeout(function() { |
| 857 this._updateProgress( |
| 858 this._finishedRequests / this._startedRequests * 0.9); // Finished re
quests drive the progress up to 90%. |
| 859 }.bind(this), 500); // Delay to give the new requests time to start. This m
akes the progress smoother. |
| 860 } |
| 861 |
| 862 _updateProgress(progress) { |
| 863 if (!this._navigationProgressVisible()) |
| 864 return; |
| 865 if (this._maxDisplayedProgress >= progress) |
| 866 return; |
| 867 this._maxDisplayedProgress = progress; |
| 868 this._displayProgress(progress); |
| 869 } |
| 870 |
| 871 _displayProgress(progress) { |
| 872 this._element.style.width = (100 * progress) + '%'; |
| 873 } |
806 }; | 874 }; |
807 | |
808 WebInspector.ScreencastView.ProgressTracker.prototype = { | |
809 _onMainFrameNavigated: function() | |
810 { | |
811 this._requestIds = {}; | |
812 this._startedRequests = 0; | |
813 this._finishedRequests = 0; | |
814 this._maxDisplayedProgress = 0; | |
815 this._updateProgress(0.1); // Display first 10% on navigation start. | |
816 }, | |
817 | |
818 _onLoad: function() | |
819 { | |
820 delete this._requestIds; | |
821 this._updateProgress(1); // Display 100% progress on load, hide it in 0
.5s. | |
822 setTimeout(function() { | |
823 if (!this._navigationProgressVisible()) | |
824 this._displayProgress(0); | |
825 }.bind(this), 500); | |
826 }, | |
827 | |
828 _navigationProgressVisible: function() | |
829 { | |
830 return !!this._requestIds; | |
831 }, | |
832 | |
833 _onRequestStarted: function(event) | |
834 { | |
835 if (!this._navigationProgressVisible()) | |
836 return; | |
837 var request = /** @type {!WebInspector.NetworkRequest} */ (event.data); | |
838 // Ignore long-living WebSockets for the sake of progress indicator, as
we won't be waiting them anyway. | |
839 if (request.type === WebInspector.resourceTypes.WebSocket) | |
840 return; | |
841 this._requestIds[request.requestId] = request; | |
842 ++this._startedRequests; | |
843 }, | |
844 | |
845 _onRequestFinished: function(event) | |
846 { | |
847 if (!this._navigationProgressVisible()) | |
848 return; | |
849 var request = /** @type {!WebInspector.NetworkRequest} */ (event.data); | |
850 if (!(request.requestId in this._requestIds)) | |
851 return; | |
852 ++this._finishedRequests; | |
853 setTimeout(function() { | |
854 this._updateProgress(this._finishedRequests / this._startedRequests
* 0.9); // Finished requests drive the progress up to 90%. | |
855 }.bind(this), 500); // Delay to give the new requests time to start. Th
is makes the progress smoother. | |
856 }, | |
857 | |
858 _updateProgress: function(progress) | |
859 { | |
860 if (!this._navigationProgressVisible()) | |
861 return; | |
862 if (this._maxDisplayedProgress >= progress) | |
863 return; | |
864 this._maxDisplayedProgress = progress; | |
865 this._displayProgress(progress); | |
866 }, | |
867 | |
868 _displayProgress: function(progress) | |
869 { | |
870 this._element.style.width = (100 * progress) + "%"; | |
871 } | |
872 }; | |
OLD | NEW |