1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 /** 6 * @constructor 7 * @extends {WebInspector.VBox} 8 * @implements {WebInspector.OverridesSupport.PageResizer} 9 * @implements {WebInspector.TargetManager.Observer} 10 * @param {!WebInspector.InspectedPagePlaceholder} inspectedPagePlaceholder 11 */ 12 WebInspector.ResponsiveDesignView = function(inspectedPagePlaceholder) 13 { 14 WebInspector.VBox.call(this); 15 this.setMinimumSize(150, 150); 16 this.element.classList.add("overflow-hidden"); 17 18 this._responsiveDesignContainer = new WebInspector.VBox(); 19 this._responsiveDesignContainer.registerRequiredCSS("responsiveDesignView.css"); 20 21 this._createToolbar(); 22 23 this._canvasContainer = new WebInspector.View(); 24 this._canvasContainer.element.classList.add("responsive-design"); 25 this._canvasContainer.show(this._responsiveDesignContainer.element); 26 27 this._canvas = this._canvasContainer.element.createChild("canvas", "fill responsive-design-canvas"); 28 29 this._mediaInspectorContainer = this._canvasContainer.element.createChild("div", "responsive-design-media-container"); 30 this._mediaInspector = new WebInspector.MediaQueryInspector(); 31 this._updateMediaQueryInspector(); 32 33 this._warningMessage = this._canvasContainer.element.createChild("div", "responsive-design-warning hidden"); 34 this._warningMessage.createChild("div", "warning-icon-small"); 35 this._warningMessage.createChild("span"); 36 var warningDisableButton = this._warningMessage.createChild("div", "disable-warning"); 37 warningDisableButton.textContent = WebInspector.UIString("Never show"); 38 warningDisableButton.addEventListener("click", this._disableOverridesWarnings.bind(this), false); 39 var warningCloseButton = this._warningMessage.createChild("div", "close-button"); 40 warningCloseButton.addEventListener("click", WebInspector.overridesSupport.clearWarningMessage.bind(WebInspector.overridesSupport), false); 41 WebInspector.overridesSupport.addEventListener(WebInspector.OverridesSupport.Events.OverridesWarningUpdated, this._overridesWarningUpdated, this); 42 WebInspector.settings.disableOverridesWarning.addChangeListener(this._overridesWarningUpdated, this); 43 44 this._slidersContainer = this._canvasContainer.element.createChild("div", "vbox responsive-design-sliders-container"); 45 var genericDeviceOutline = this._slidersContainer.createChild("div", "responsive-design-generic-outline-container"); 46 genericDeviceOutline.createChild("div", "responsive-design-generic-outline"); 47 var widthSlider = this._slidersContainer.createChild("div", "responsive-design-slider-width"); 48 widthSlider.createChild("div", "responsive-design-thumb-handle"); 49 this._createResizer(widthSlider, false); 50 var heightSlider = this._slidersContainer.createChild("div", "responsive-design-slider-height"); 51 heightSlider.createChild("div", "responsive-design-thumb-handle"); 52 this._createResizer(heightSlider, true); 53 this._pageContainer = this._slidersContainer.createChild("div", "vbox flex-auto"); 54 55 // Page scale controls. 56 this._pageScaleContainer = this._canvasContainer.element.createChild("div", "hbox responsive-design-page-scale-container"); 57 this._decreasePageScaleButton = new WebInspector.StatusBarButton(WebInspector.UIString(""), "responsive-design-page-scale-button responsive-design-page-scale-decrease"); 58 this._decreasePageScaleButton.element.tabIndex = -1; 59 this._decreasePageScaleButton.addEventListener("click", this._pageScaleButtonClicked.bind(this, false), this); 60 this._pageScaleContainer.appendChild(this._decreasePageScaleButton.element); 61 62 this._pageScaleLabel = this._pageScaleContainer.createChild("label", "responsive-design-page-scale-label"); 63 this._pageScaleLabel.title = WebInspector.UIString("For a simpler way to change the current page scale, hold down Shift and drag with your mouse."); 64 this._pageScaleLabel.addEventListener("dblclick", this._resetPageScale.bind(this), false); 65 66 this._increasePageScaleButton = new WebInspector.StatusBarButton(WebInspector.UIString(""), "responsive-design-page-scale-button responsive-design-page-scale-increase"); 67 this._increasePageScaleButton.element.tabIndex = -1; 68 this._increasePageScaleButton.addEventListener("click", this._pageScaleButtonClicked.bind(this, true), this); 69 this._pageScaleContainer.appendChild(this._increasePageScaleButton.element); 70 71 this._inspectedPagePlaceholder = inspectedPagePlaceholder; 72 inspectedPagePlaceholder.show(this.element); 73 74 this._enabled = false; 75 this._viewport = { scrollX: 0, scrollY: 0, contentsWidth: 0, contentsHeight: 0, pageScaleFactor: 1, minimumPageScaleFactor: 1, maximumPageScaleFactor: 1 }; 76 this._drawContentsSize = true; 77 this._viewportChangedThrottler = new WebInspector.Throttler(0); 78 this._pageScaleFactorThrottler = new WebInspector.Throttler(50); 79 80 WebInspector.zoomManager.addEventListener(WebInspector.ZoomManager.Events.ZoomChanged, this._onZoomChanged, this); 81 WebInspector.overridesSupport.addEventListener(WebInspector.OverridesSupport.Events.EmulationStateChanged, this._emulationEnabledChanged, this); 82 this._mediaInspector.addEventListener(WebInspector.MediaQueryInspector.Events.CountUpdated, this._updateMediaQueryInspectorButton, this); 83 this._mediaInspector.addEventListener(WebInspector.MediaQueryInspector.Events.HeightUpdated, this.onResize, this); 84 WebInspector.targetManager.observeTargets(this); 85 86 this._emulationEnabledChanged(); 87 this._overridesWarningUpdated(); 88 }; 89 90 // Measured in DIP. 91 WebInspector.ResponsiveDesignView.RulerWidth = 34; 92 WebInspector.ResponsiveDesignView.RulerHeight = 22; 93 WebInspector.ResponsiveDesignView.RulerTopHeight = 11; 94 WebInspector.ResponsiveDesignView.RulerBottomHeight = 9; 95 96 WebInspector.ResponsiveDesignView.prototype = { 97 98 /** 99 * @param {!WebInspector.Target} target 100 */ 101 targetAdded: function(target) 102 { 103 // FIXME: adapt this to multiple targets. 104 if (this._target) 105 return; 106 this._target = target; 107 target.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.ViewportChanged, this._viewportChanged, this); 108 }, 109 110 /** 111 * @param {!WebInspector.Target} target 112 */ 113 targetRemoved: function(target) 114 { 115 if (target !== this._target) 116 return; 117 target.resourceTreeModel.removeEventListener(WebInspector.ResourceTreeModel.EventTypes.ViewportChanged, this._viewportChanged, this); 118 }, 119 120 _invalidateCache: function() 121 { 122 delete this._cachedScale; 123 delete this._cachedCssCanvasWidth; 124 delete this._cachedCssCanvasHeight; 125 delete this._cachedCssHeight; 126 delete this._cachedCssWidth; 127 delete this._cachedZoomFactor; 128 delete this._cachedViewport; 129 delete this._cachedDrawContentsSize; 130 delete this._cachedMediaInspectorHeight; 131 delete this._availableSize; 132 }, 133 134 _emulationEnabledChanged: function() 135 { 136 var enabled = WebInspector.overridesSupport.emulationEnabled(); 137 this._mediaInspector.setEnabled(enabled); 138 if (enabled && !this._enabled) { 139 this._invalidateCache(); 140 this._ignoreResize = true; 141 this._enabled = true; 142 this._inspectedPagePlaceholder.clearMinimumSizeAndMargins(); 143 this._inspectedPagePlaceholder.show(this._pageContainer); 144 this._responsiveDesignContainer.show(this.element); 145 delete this._ignoreResize; 146 this.onResize(); 147 } else if (!enabled && this._enabled) { 148 this._invalidateCache(); 149 this._ignoreResize = true; 150 this._enabled = false; 151 this._scale = 1; 152 this._inspectedPagePlaceholder.restoreMinimumSizeAndMargins(); 153 this._responsiveDesignContainer.detach(); 154 this._inspectedPagePlaceholder.show(this.element); 155 delete this._ignoreResize; 156 this.onResize(); 157 } 158 }, 159 160 /** 161 * WebInspector.OverridesSupport.PageResizer override. 162 * @param {number} dipWidth 163 * @param {number} dipHeight 164 * @param {number} scale 165 */ 166 update: function(dipWidth, dipHeight, scale) 167 { 168 this._scale = scale; 169 this._dipWidth = dipWidth ? Math.max(dipWidth, 1) : 0; 170 this._dipHeight = dipHeight ? Math.max(dipHeight, 1) : 0; 171 this._updateUI(); 172 }, 173 174 updatePageResizer: function() 175 { 176 WebInspector.overridesSupport.setPageResizer(this, this._availableDipSize()); 177 }, 178 179 /** 180 * @return {!Size} 181 */ 182 _availableDipSize: function() 183 { 184 if (typeof this._availableSize === "undefined") { 185 var zoomFactor = WebInspector.zoomManager.zoomFactor(); 186 var rect = this._canvasContainer.element.getBoundingClientRect(); 187 var rulerTotalHeight = this._rulerTotalHeightDIP(); 188 this._availableSize = new Size(Math.max(rect.width * zoomFactor - WebInspector.ResponsiveDesignView.RulerWidth, 1), 189 Math.max(rect.height * zoomFactor - rulerTotalHeight, 1)); 190 } 191 return this._availableSize; 192 }, 193 194 /** 195 * @param {!Element} element 196 * @param {boolean} vertical 197 * @return {!WebInspector.ResizerWidget} 198 */ 199 _createResizer: function(element, vertical) 200 { 201 var resizer = new WebInspector.ResizerWidget(); 202 resizer.addElement(element); 203 resizer.setVertical(vertical); 204 resizer.addEventListener(WebInspector.ResizerWidget.Events.ResizeStart, this._onResizeStart, this); 205 resizer.addEventListener(WebInspector.ResizerWidget.Events.ResizeUpdate, this._onResizeUpdate, this); 206 resizer.addEventListener(WebInspector.ResizerWidget.Events.ResizeEnd, this._onResizeEnd, this); 207 return resizer; 208 }, 209 210 /** 211 * @param {!WebInspector.Event} event 212 */ 213 _onResizeStart: function(event) 214 { 215 this._drawContentsSize = false; 216 var available = this._availableDipSize(); 217 this._slowPositionStart = null; 218 this._resizeStartSize = event.target.isVertical() ? (this._dipHeight || available.height) : (this._dipWidth || available.width); 219 this.dispatchEventToListeners(WebInspector.OverridesSupport.PageResizer.Events.FixedScaleRequested, true); 220 this._updateUI(); 221 }, 222 223 /** 224 * @param {!WebInspector.Event} event 225 */ 226 _onResizeUpdate: function(event) 227 { 228 if (event.data.shiftKey !== !!this._slowPositionStart) 229 this._slowPositionStart = event.data.shiftKey ? event.data.currentPosition : null; 230 var cssOffset = this._slowPositionStart ? (event.data.currentPosition - this._slowPositionStart) / 10 + this._slowPositionStart - event.data.startPosition : event.data.currentPosition - event.data.startPosition; 231 var dipOffset = Math.round(cssOffset * WebInspector.zoomManager.zoomFactor()); 232 var newSize = this._resizeStartSize + dipOffset; 233 newSize = Math.round(newSize / (this._scale || 1)); 234 newSize = Math.max(Math.min(newSize, WebInspector.OverridesSupport.MaxDeviceSize), 1); 235 var requested = {}; 236 if (event.target.isVertical()) 237 requested.height = newSize; 238 else 239 requested.width = newSize; 240 this.dispatchEventToListeners(WebInspector.OverridesSupport.PageResizer.Events.ResizeRequested, requested); 241 }, 242 243 /** 244 * @param {!WebInspector.Event} event 245 */ 246 _onResizeEnd: function(event) 247 { 248 this._drawContentsSize = true; 249 this.dispatchEventToListeners(WebInspector.OverridesSupport.PageResizer.Events.FixedScaleRequested, false); 250 delete this._resizeStartSize; 251 this._updateUI(); 252 }, 253 254 /** 255 * Draws canvas of the specified css size in DevTools page space. 256 * Canvas contains grid and rulers. 257 * @param {number} cssCanvasWidth 258 * @param {number} cssCanvasHeight 259 * @param {number} rulerHeight 260 */ 261 _drawCanvas: function(cssCanvasWidth, cssCanvasHeight, rulerHeight) 262 { 263 if (!this._enabled) 264 return; 265 266 var canvas = this._canvas; 267 var context = canvas.getContext("2d"); 268 canvas.style.width = cssCanvasWidth + "px"; 269 canvas.style.height = cssCanvasHeight + "px"; 270 271 var zoomFactor = WebInspector.zoomManager.zoomFactor(); 272 var dipCanvasWidth = cssCanvasWidth * zoomFactor; 273 var dipCanvasHeight = cssCanvasHeight * zoomFactor; 274 275 var deviceScaleFactor = window.devicePixelRatio; 276 canvas.width = deviceScaleFactor * cssCanvasWidth; 277 canvas.height = deviceScaleFactor * cssCanvasHeight; 278 context.scale(canvas.width / dipCanvasWidth, canvas.height / dipCanvasHeight); 279 context.font = "11px " + WebInspector.fontFamily(); 280 281 const backgroundColor = "rgb(102, 102, 102)"; 282 const lightLineColor = "rgb(132, 132, 132)"; 283 const darkLineColor = "rgb(114, 114, 114)"; 284 const rulerColor = "rgb(125, 125, 125)"; 285 const textColor = "rgb(186, 186, 186)"; 286 const contentsSizeColor = "rgba(0, 0, 0, 0.3)"; 287 288 var scale = (this._scale || 1) * this._viewport.pageScaleFactor; 289 var rulerScale = 0.5; 290 while (Math.abs(rulerScale * scale - 1) > Math.abs((rulerScale + 0.5) * scale - 1)) 291 rulerScale += 0.5; 292 293 var gridStep = 50 * scale * rulerScale; 294 var gridSubStep = 10 * scale * rulerScale; 295 296 var rulerSubStep = 5 * scale * rulerScale; 297 var rulerStepCount = 20; 298 299 var rulerWidth = WebInspector.ResponsiveDesignView.RulerWidth; 300 301 var dipGridWidth = dipCanvasWidth - rulerWidth; 302 var dipGridHeight = dipCanvasHeight - rulerHeight; 303 var dipScrollX = this._viewport.scrollX * scale; 304 var dipScrollY = this._viewport.scrollY * scale; 305 context.translate(rulerWidth, rulerHeight); 306 307 context.fillStyle = backgroundColor; 308 context.fillRect(0, 0, dipGridWidth, dipGridHeight); 309 310 context.translate(0.5, 0.5); 311 context.strokeStyle = rulerColor; 312 context.fillStyle = textColor; 313 context.lineWidth = 1; 314 315 // Draw horizontal ruler. 316 context.save(); 317 318 var minXIndex = Math.ceil(dipScrollX / rulerSubStep); 319 var maxXIndex = Math.floor((dipScrollX + dipGridWidth) / rulerSubStep); 320 if (minXIndex) { 321 context.beginPath(); 322 context.moveTo(0, -rulerHeight); 323 context.lineTo(0, 0); 324 context.stroke(); 325 } 326 327 context.translate(-dipScrollX, 0); 328 for (var index = minXIndex; index <= maxXIndex; index++) { 329 var x = index * rulerSubStep; 330 var height = WebInspector.ResponsiveDesignView.RulerHeight * 0.25; 331 332 if (!(index % (rulerStepCount / 4))) 333 height = WebInspector.ResponsiveDesignView.RulerHeight * 0.5; 334 335 if (!(index % (rulerStepCount / 2))) 336 height = rulerHeight; 337 338 if (!(index % rulerStepCount)) { 339 context.save(); 340 context.translate(x, 0); 341 context.fillText(Math.round(x / scale), 2, -rulerHeight + 10); 342 context.restore(); 343 height = rulerHeight; 344 } 345 346 context.beginPath(); 347 context.moveTo(x, - height); 348 context.lineTo(x, 0); 349 context.stroke(); 350 } 351 context.restore(); 352 353 // Draw vertical ruler. 354 context.save(); 355 var minYIndex = Math.ceil(dipScrollY / rulerSubStep); 356 var maxYIndex = Math.floor((dipScrollY + dipGridHeight) / rulerSubStep); 357 context.translate(0, -dipScrollY); 358 for (var index = minYIndex; index <= maxYIndex; index++) { 359 var y = index * rulerSubStep; 360 var x = -rulerWidth * 0.25; 361 if (!(index % (rulerStepCount / 4))) 362 x = -rulerWidth * 0.5; 363 if (!(index % (rulerStepCount / 2))) 364 x = -rulerWidth * 0.75; 365 366 if (!(index % rulerStepCount)) { 367 context.save(); 368 context.translate(0, y); 369 context.rotate(-Math.PI / 2); 370 context.fillText(Math.round(y / scale), 2, -rulerWidth + 10); 371 context.restore(); 372 x = -rulerWidth; 373 } 374 375 context.beginPath(); 376 context.moveTo(x, y); 377 context.lineTo(0, y); 378 context.stroke(); 379 } 380 context.restore(); 381 382 // Draw grid. 383 drawGrid(dipScrollX, dipScrollY, darkLineColor, gridSubStep); 384 drawGrid(dipScrollX, dipScrollY, lightLineColor, gridStep); 385 386 /** 387 * @param {number} scrollX 388 * @param {number} scrollY 389 * @param {string} color 390 * @param {number} step 391 */ 392 function drawGrid(scrollX, scrollY, color, step) 393 { 394 context.strokeStyle = color; 395 var minX = Math.ceil(scrollX / step) * step; 396 var maxX = Math.floor((scrollX + dipGridWidth) / step) * step - minX; 397 var minY = Math.ceil(scrollY / step) * step; 398 var maxY = Math.floor((scrollY + dipGridHeight) / step) * step - minY; 399 400 context.save(); 401 context.translate(minX - scrollX, 0); 402 for (var x = 0; x <= maxX; x += step) { 403 context.beginPath(); 404 context.moveTo(x, 0); 405 context.lineTo(x, dipGridHeight); 406 context.stroke(); 407 } 408 context.restore(); 409 410 context.save(); 411 context.translate(0, minY - scrollY); 412 for (var y = 0; y <= maxY; y += step) { 413 context.beginPath(); 414 context.moveTo(0, y); 415 context.lineTo(dipGridWidth, y); 416 context.stroke(); 417 } 418 context.restore(); 419 } 420 421 context.translate(-0.5, -0.5); 422 423 // Draw contents size. 424 var pageScaleAvailable = WebInspector.overridesSupport.settings.emulateMobile.get() || WebInspector.overridesSupport.settings.emulateTouch.get(); 425 if (this._drawContentsSize && pageScaleAvailable) { 426 context.fillStyle = contentsSizeColor; 427 var visibleContentsWidth = Math.max(0, Math.min(dipGridWidth, this._viewport.contentsWidth * scale - dipScrollX)); 428 var visibleContentsHeight = Math.max(0, Math.min(dipGridHeight, this._viewport.contentsHeight * scale - dipScrollY)); 429 context.fillRect(0, 0, visibleContentsWidth, visibleContentsHeight); 430 } 431 }, 432 433 /** 434 * @return {number} 435 */ 436 _rulerTotalHeightDIP: function() 437 { 438 var mediaInspectorHeight = this._mediaInspector.isShowing() ? this._mediaInspector.element.offsetHeight : 0; 439 if (!mediaInspectorHeight) 440 return WebInspector.ResponsiveDesignView.RulerHeight; 441 return WebInspector.ResponsiveDesignView.RulerTopHeight + WebInspector.ResponsiveDesignView.RulerBottomHeight + mediaInspectorHeight * WebInspector.zoomManager.zoomFactor(); 442 }, 443 444 _updateUI: function() 445 { 446 if (!this._enabled || !this.isShowing()) 447 return; 448 449 var zoomFactor = WebInspector.zoomManager.zoomFactor(); 450 var rect = this._canvas.parentElement.getBoundingClientRect(); 451 var availableDip = this._availableDipSize(); 452 var cssCanvasWidth = rect.width; 453 var cssCanvasHeight = rect.height; 454 var mediaInspectorHeight = this._mediaInspector.isShowing() ? this._mediaInspector.element.offsetHeight : 0; 455 var rulerTotalHeight = this._rulerTotalHeightDIP(); 456 457 this._mediaInspector.setAxisTransform(this._viewport.scrollX, this._scale * this._viewport.pageScaleFactor); 458 459 if (this._cachedZoomFactor !== zoomFactor || this._cachedMediaInspectorHeight !== mediaInspectorHeight) { 460 var cssRulerWidth = WebInspector.ResponsiveDesignView.RulerWidth / zoomFactor + "px"; 461 var cssRulerHeight = (mediaInspectorHeight ? WebInspector.ResponsiveDesignView.RulerTopHeight : WebInspector.ResponsiveDesignView.RulerHeight) / zoomFactor + "px"; 462 var cssCanvasOffset = rulerTotalHeight / zoomFactor + "px"; 463 this._slidersContainer.style.left = cssRulerWidth; 464 this._slidersContainer.style.top = cssCanvasOffset; 465 this._warningMessage.style.height = cssCanvasOffset; 466 this._pageScaleContainer.style.top = cssCanvasOffset; 467 this._mediaInspectorContainer.style.left = cssRulerWidth; 468 this._mediaInspectorContainer.style.marginTop = cssRulerHeight; 469 } 470 471 var cssWidth = (this._dipWidth ? this._dipWidth : availableDip.width) / zoomFactor; 472 var cssHeight = (this._dipHeight ? this._dipHeight : availableDip.height) / zoomFactor; 473 if (this._cachedCssWidth !== cssWidth || this._cachedCssHeight !== cssHeight) { 474 this._slidersContainer.style.width = cssWidth + "px"; 475 this._slidersContainer.style.height = cssHeight + "px"; 476 this._inspectedPagePlaceholder.onResize(); 477 } 478 479 var pageScaleVisible = cssWidth + this._pageScaleContainerWidth + WebInspector.ResponsiveDesignView.RulerWidth / zoomFactor <= rect.width; 480 this._pageScaleContainer.classList.toggle("hidden", !pageScaleVisible); 481 482 var viewportChanged = !this._cachedViewport 483 || this._cachedViewport.scrollX !== this._viewport.scrollX || this._cachedViewport.scrollY !== this._viewport.scrollY 484 || this._cachedViewport.contentsWidth !== this._viewport.contentsWidth || this._cachedViewport.contentsHeight !== this._viewport.contentsHeight 485 || this._cachedViewport.pageScaleFactor !== this._viewport.pageScaleFactor 486 || this._cachedViewport.minimumPageScaleFactor !== this._viewport.minimumPageScaleFactor 487 || this._cachedViewport.maximumPageScaleFactor !== this._viewport.maximumPageScaleFactor; 488 489 var canvasInvalidated = viewportChanged || this._drawContentsSize !== this._cachedDrawContentsSize || this._cachedScale !== this._scale || 490 this._cachedCssCanvasWidth !== cssCanvasWidth || this._cachedCssCanvasHeight !== cssCanvasHeight || this._cachedZoomFactor !== zoomFactor || 491 this._cachedMediaInspectorHeight !== mediaInspectorHeight; 492 493 if (canvasInvalidated) 494 this._drawCanvas(cssCanvasWidth, cssCanvasHeight, rulerTotalHeight); 495 496 if (viewportChanged) { 497 this._pageScaleLabel.textContent = WebInspector.UIString("%.1f", this._viewport.pageScaleFactor); 498 this._decreasePageScaleButton.title = WebInspector.UIString("Scale down (minimum %.1f)", this._viewport.minimumPageScaleFactor); 499 this._decreasePageScaleButton.setEnabled(this._viewport.pageScaleFactor > this._viewport.minimumPageScaleFactor); 500 this._increasePageScaleButton.title = WebInspector.UIString("Scale up (maximum %.1f)", this._viewport.maximumPageScaleFactor); 501 this._increasePageScaleButton.setEnabled(this._viewport.pageScaleFactor < this._viewport.maximumPageScaleFactor); 502 } 503 504 this._cachedScale = this._scale; 505 this._cachedCssCanvasWidth = cssCanvasWidth; 506 this._cachedCssCanvasHeight = cssCanvasHeight; 507 this._cachedCssHeight = cssHeight; 508 this._cachedCssWidth = cssWidth; 509 this._cachedZoomFactor = zoomFactor; 510 this._cachedViewport = this._viewport; 511 this._cachedDrawContentsSize = this._drawContentsSize; 512 this._cachedMediaInspectorHeight = mediaInspectorHeight; 513 }, 514 515 onResize: function() 516 { 517 if (!this._enabled || this._ignoreResize) 518 return; 519 var oldSize = this._availableSize; 520 521 this._pageScaleContainer.classList.remove("hidden"); 522 this._pageScaleContainerWidth = this._pageScaleContainer.offsetWidth; 523 524 delete this._availableSize; 525 var newSize = this._availableDipSize(); 526 if (!newSize.isEqual(oldSize)) 527 this.dispatchEventToListeners(WebInspector.OverridesSupport.PageResizer.Events.AvailableSizeChanged, newSize); 528 this._updateUI(); 529 this._inspectedPagePlaceholder.onResize(); 530 }, 531 532 _onZoomChanged: function() 533 { 534 this._updateUI(); 535 }, 536 537 _createToolbar: function() 538 { 539 this._toolbarElement = this._responsiveDesignContainer.element.createChild("div", "responsive-design-toolbar"); 540 this._createButtonsSection(); 541 this._createDeviceSection(); 542 this._toolbarElement.createChild("div", "responsive-design-separator"); 543 this._createNetworkSection(); 544 this._toolbarElement.createChild("div", "responsive-design-separator"); 545 546 var moreButtonContainer = this._toolbarElement.createChild("div", "responsive-design-more-button-container"); 547 var moreButton = moreButtonContainer.createChild("button", "responsive-design-more-button"); 548 moreButton.title = WebInspector.UIString("More overrides"); 549 moreButton.addEventListener("click", this._showEmulationInDrawer.bind(this), false); 550 moreButton.textContent = "\u2026"; 551 }, 552 553 _createButtonsSection: function() 554 { 555 var buttonsSection = this._toolbarElement.createChild("div", "responsive-design-section responsive-design-section-buttons"); 556 557 var resetButton = new WebInspector.StatusBarButton(WebInspector.UIString("Reset all overrides."), "clear-status-bar-item"); 558 buttonsSection.appendChild(resetButton.element); 559 resetButton.addEventListener("click", WebInspector.overridesSupport.reset, WebInspector.overridesSupport); 560 561 // Media Query Inspector. 562 this._toggleMediaInspectorButton = new WebInspector.StatusBarButton(WebInspector.UIString("Media queries not found"), "responsive-design-toggle-media-inspector"); 563 this._toggleMediaInspectorButton.toggled = WebInspector.settings.showMediaQueryInspector.get(); 564 this._toggleMediaInspectorButton.setEnabled(false); 565 this._toggleMediaInspectorButton.addEventListener("click", this._onToggleMediaInspectorButtonClick, this); 566 WebInspector.settings.showMediaQueryInspector.addChangeListener(this._updateMediaQueryInspector, this); 567 buttonsSection.appendChild(this._toggleMediaInspectorButton.element); 568 }, 569 570 _createDeviceSection: function() 571 { 572 var deviceSection = this._toolbarElement.createChild("div", "responsive-design-section responsive-design-section-device"); 573 574 var separator = deviceSection.createChild("div", "responsive-design-section-decorator"); 575 576 // Device. 577 var deviceElement = deviceSection.createChild("div", "responsive-design-suite responsive-design-suite-top").createChild("div"); 578 579 var fieldsetElement = deviceElement.createChild("fieldset"); 580 fieldsetElement.createChild("label").textContent = WebInspector.UIString("Device"); 581 var deviceSelectElement = WebInspector.OverridesUI.createDeviceSelect(document); 582 fieldsetElement.appendChild(deviceSelectElement); 583 deviceSelectElement.classList.add("responsive-design-device-select"); 584 585 var detailsElement = deviceSection.createChild("div", "responsive-design-suite"); 586 587 // Dimensions. 588 var screenElement = detailsElement.createChild("div", ""); 589 fieldsetElement = screenElement.createChild("fieldset"); 590 591 var emulateResolutionCheckbox = WebInspector.SettingsUI.createSettingCheckbox("", WebInspector.overridesSupport.settings.emulateResolution, true, undefined, WebInspector.UIString("Emulate screen resolution")); 592 fieldsetElement.appendChild(emulateResolutionCheckbox); 593 594 var resolutionButton = new WebInspector.StatusBarButton(WebInspector.UIString("Screen resolution"), "responsive-design-icon responsive-design-icon-resolution"); 595 resolutionButton.setEnabled(false); 596 fieldsetElement.appendChild(resolutionButton.element); 597 var resolutionFieldset = WebInspector.SettingsUI.createSettingFieldset(WebInspector.overridesSupport.settings.emulateResolution); 598 fieldsetElement.appendChild(resolutionFieldset); 599 600 resolutionFieldset.appendChild(WebInspector.SettingsUI.createSettingInputField("", WebInspector.overridesSupport.settings.deviceWidth, true, 4, "3em", WebInspector.OverridesSupport.deviceSizeValidator, true, true, WebInspector.UIString("\u2013"))); 601 resolutionFieldset.createTextChild("\u00D7"); 602 resolutionFieldset.appendChild(WebInspector.SettingsUI.createSettingInputField("", WebInspector.overridesSupport.settings.deviceHeight, true, 4, "3em", WebInspector.OverridesSupport.deviceSizeValidator, true, true, WebInspector.UIString("\u2013"))); 603 604 var swapButton = new WebInspector.StatusBarButton(WebInspector.UIString("Swap dimensions"), "responsive-design-icon responsive-design-icon-swap"); 605 swapButton.element.tabIndex = -1; 606 swapButton.addEventListener("click", WebInspector.overridesSupport.swapDimensions, WebInspector.overridesSupport); 607 resolutionFieldset.appendChild(swapButton.element); 608 609 // Device pixel ratio. 610 detailsElement.createChild("div", "responsive-design-suite-separator"); 611 612 var dprElement = detailsElement.createChild("div", ""); 613 var resolutionFieldset2 = WebInspector.SettingsUI.createSettingFieldset(WebInspector.overridesSupport.settings.emulateResolution); 614 dprElement.appendChild(resolutionFieldset2); 615 var dprButton = new WebInspector.StatusBarButton(WebInspector.UIString("Device pixel ratio"), "responsive-design-icon responsive-design-icon-dpr"); 616 dprButton.setEnabled(false); 617 resolutionFieldset2.appendChild(dprButton.element); 618 resolutionFieldset2.appendChild(WebInspector.SettingsUI.createSettingInputField("", WebInspector.overridesSupport.settings.deviceScaleFactor, true, 4, "1.9em", WebInspector.OverridesSupport.deviceScaleFactorValidator, true, true, WebInspector.UIString("\u2013"))); 619 620 // Fit to window. 621 detailsElement.createChild("div", "responsive-design-suite-separator"); 622 var fitToWindowElement = detailsElement.createChild("div", ""); 623 fieldsetElement = fitToWindowElement.createChild("fieldset"); 624 fieldsetElement.appendChild(WebInspector.SettingsUI.createSettingCheckbox(WebInspector.UIString("Fit"), WebInspector.overridesSupport.settings.deviceFitWindow, true, undefined, WebInspector.UIString("Zoom to fit available space"))); 625 }, 626 627 _createNetworkSection: function() 628 { 629 var networkSection = this._toolbarElement.createChild("div", "responsive-design-section responsive-design-section-network"); 630 631 var separator = networkSection.createChild("div", "responsive-design-section-decorator"); 632 633 // Bandwidth. 634 var bandwidthElement = networkSection.createChild("div", "responsive-design-suite responsive-design-suite-top").createChild("div"); 635 var fieldsetElement = bandwidthElement.createChild("fieldset"); 636 var networkCheckbox = fieldsetElement.createChild("label"); 637 networkCheckbox.textContent = WebInspector.UIString("Network"); 638 fieldsetElement.appendChild(WebInspector.OverridesUI.createNetworkConditionsSelect(document)); 639 640 // User agent. 641 var userAgentElement = networkSection.createChild("div", "responsive-design-suite").createChild("div"); 642 fieldsetElement = userAgentElement.createChild("fieldset"); 643 fieldsetElement.appendChild(WebInspector.SettingsUI.createSettingInputField("UA", WebInspector.overridesSupport.settings.userAgent, false, 0, "", undefined, false, false, WebInspector.UIString("No override"))); 644 }, 645 646 _onToggleMediaInspectorButtonClick: function() 647 { 648 WebInspector.settings.showMediaQueryInspector.set(!this._toggleMediaInspectorButton.toggled); 649 }, 650 651 _updateMediaQueryInspector: function() 652 { 653 this._toggleMediaInspectorButton.toggled = WebInspector.settings.showMediaQueryInspector.get(); 654 if (this._mediaInspector.isShowing() === WebInspector.settings.showMediaQueryInspector.get()) 655 return; 656 if (this._mediaInspector.isShowing()) 657 this._mediaInspector.detach(); 658 else 659 this._mediaInspector.show(this._mediaInspectorContainer); 660 this.onResize(); 661 }, 662 663 /** 664 * @param {!WebInspector.Event} event 665 */ 666 _updateMediaQueryInspectorButton: function(event) 667 { 668 var count = /** @type {number} */ (event.data); 669 this._toggleMediaInspectorButton.setEnabled(!!count); 670 this._toggleMediaInspectorButton.title = !count ? WebInspector.UIString("Media queries not found") : 671 WebInspector.UIString((count === 1 ? "%d media query found" : "%d media queries found"), count); 672 }, 673 674 _overridesWarningUpdated: function() 675 { 676 var message = WebInspector.settings.disableOverridesWarning.get() ? "" : WebInspector.overridesSupport.warningMessage(); 677 if (this._warning === message) 678 return; 679 this._warning = message; 680 this._warningMessage.classList.toggle("hidden", !message); 681 this._warningMessage.querySelector("span").textContent = message; 682 this._invalidateCache(); 683 this.onResize(); 684 }, 685 686 _disableOverridesWarnings: function() 687 { 688 WebInspector.settings.disableOverridesWarning.set(true); 689 }, 690 691 _showEmulationInDrawer: function() 692 { 693 WebInspector.Revealer.reveal(WebInspector.overridesSupport); 694 }, 695 696 /** 697 * @param {!WebInspector.Event} event 698 */ 699 _viewportChanged: function(event) 700 { 701 var viewport = /** @type {?PageAgent.Viewport} */ (event.data); 702 if (viewport) { 703 this._viewport = viewport; 704 this._viewport.minimumPageScaleFactor = Math.max(0.1, this._viewport.minimumPageScaleFactor); 705 this._viewport.minimumPageScaleFactor = Math.min(this._viewport.minimumPageScaleFactor, this._viewport.pageScaleFactor); 706 this._viewport.maximumPageScaleFactor = Math.min(10, this._viewport.maximumPageScaleFactor); 707 this._viewport.maximumPageScaleFactor = Math.max(this._viewport.maximumPageScaleFactor, this._viewport.pageScaleFactor); 708 this._viewportChangedThrottler.schedule(this._updateUIThrottled.bind(this)); 709 } 710 }, 711 712 /** 713 * @param {!WebInspector.Throttler.FinishCallback} finishCallback 714 */ 715 _updateUIThrottled: function(finishCallback) 716 { 717 this._updateUI(); 718 finishCallback(); 719 }, 720 721 /** 722 * @param {boolean} increase 723 * @param {!WebInspector.Event} event 724 */ 725 _pageScaleButtonClicked: function(increase, event) 726 { 727 this._pageScaleFactorThrottler.schedule(updatePageScaleFactor.bind(this)); 728 729 /** 730 * @param {!WebInspector.Throttler.FinishCallback} finishCallback 731 * @this {WebInspector.ResponsiveDesignView} 732 */ 733 function updatePageScaleFactor(finishCallback) 734 { 735 if (this._target && this._viewport) { 736 var value = this._viewport.pageScaleFactor; 737 value = increase ? value * 1.1 : value / 1.1; 738 value = Math.min(this._viewport.maximumPageScaleFactor, value); 739 value = Math.max(this._viewport.minimumPageScaleFactor, value) 740 this._target.pageAgent().setPageScaleFactor(value); 741 } 742 finishCallback(); 743 } 744 }, 745 746 _resetPageScale: function() 747 { 748 this._pageScaleFactorThrottler.schedule(updatePageScaleFactor.bind(this)); 749 750 /** 751 * @param {!WebInspector.Throttler.FinishCallback} finishCallback 752 * @this {WebInspector.ResponsiveDesignView} 753 */ 754 function updatePageScaleFactor(finishCallback) 755 { 756 if (this._target && this._viewport && this._viewport.minimumPageScaleFactor <= 1 && this._viewport.maximumPageScaleFactor >= 1) 757 this._target.pageAgent().setPageScaleFactor(1); 758 finishCallback(); 759 } 760 }, 761 762 __proto__: WebInspector.VBox.prototype 763 }; 764