Home | History | Annotate | Download | only in toolbox
      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