Home | History | Annotate | Download | only in sdk
      1 /*
      2  * Copyright (C) 2012 Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 /**
     32  * @constructor
     33  * @implements {WebInspector.TargetManager.Observer}
     34  * @extends {WebInspector.Object}
     35  * @param {boolean} responsiveDesignAvailable
     36  */
     37 WebInspector.OverridesSupport = function(responsiveDesignAvailable)
     38 {
     39     this._touchEmulationSuspended = false;
     40     this._emulateMobileEnabled = false;
     41     this._userAgent = "";
     42     this._pageResizer = null;
     43     this._deviceScale = 1;
     44     this._fixedDeviceScale = false;
     45     this._initialized = false;
     46     this._deviceMetricsThrottler = new WebInspector.Throttler(0);
     47     this._responsiveDesignAvailable = responsiveDesignAvailable;
     48 
     49     this.settings = {};
     50     this.settings._emulationEnabled = WebInspector.settings.createSetting("emulationEnabled", false);
     51 
     52     this.settings.userAgent = WebInspector.settings.createSetting("userAgent", "");
     53 
     54     this.settings.emulateResolution = WebInspector.settings.createSetting("emulateResolution", true);
     55     this.settings.deviceWidth = WebInspector.settings.createSetting("deviceWidth", 360);
     56     this.settings.deviceHeight = WebInspector.settings.createSetting("deviceHeight", 640);
     57     this.settings.deviceScaleFactor = WebInspector.settings.createSetting("deviceScaleFactor", 0);
     58     this.settings.deviceFitWindow = WebInspector.settings.createSetting("deviceFitWindow", true);
     59     this.settings.emulateMobile = WebInspector.settings.createSetting("emulateMobile", false);
     60     this.settings.customDevicePresets = WebInspector.settings.createSetting("customDevicePresets", []);
     61 
     62     this.settings.emulateTouch = WebInspector.settings.createSetting("emulateTouch", false);
     63 
     64     this.settings.overrideGeolocation = WebInspector.settings.createSetting("overrideGeolocation", false);
     65     this.settings.geolocationOverride = WebInspector.settings.createSetting("geolocationOverride", "");
     66 
     67     this.settings.overrideDeviceOrientation = WebInspector.settings.createSetting("overrideDeviceOrientation", false);
     68     this.settings.deviceOrientationOverride = WebInspector.settings.createSetting("deviceOrientationOverride", "");
     69 
     70     this.settings.overrideCSSMedia = WebInspector.settings.createSetting("overrideCSSMedia", false);
     71     this.settings.emulatedCSSMedia = WebInspector.settings.createSetting("emulatedCSSMedia", "print");
     72 
     73     this.settings.networkConditions = WebInspector.settings.createSetting("networkConditions", {throughput: WebInspector.OverridesSupport.NetworkThroughputUnlimitedValue, latency: 0});
     74 
     75     WebInspector.targetManager.observeTargets(this);
     76 }
     77 
     78 WebInspector.OverridesSupport.Events = {
     79     OverridesWarningUpdated: "OverridesWarningUpdated",
     80     EmulationStateChanged: "EmulationStateChanged"
     81 }
     82 
     83 WebInspector.OverridesSupport.MaxDeviceSize = 3000;
     84 
     85 /**
     86  * @interface
     87  * @extends {WebInspector.EventTarget}
     88  */
     89 WebInspector.OverridesSupport.PageResizer = function()
     90 {
     91 };
     92 
     93 WebInspector.OverridesSupport.PageResizer.Events = {
     94     AvailableSizeChanged: "AvailableSizeChanged",
     95     ResizeRequested: "ResizeRequested",
     96     FixedScaleRequested: "FixedScaleRequested"
     97 };
     98 
     99 WebInspector.OverridesSupport.PageResizer.prototype = {
    100     /**
    101      * Zero width and height mean default size.
    102      * Scale should be applied to page-scale-dependent UI bits. Zero means no scale.
    103      * @param {number} dipWidth
    104      * @param {number} dipHeight
    105      * @param {number} scale
    106      */
    107     update: function(dipWidth, dipHeight, scale) { }
    108 };
    109 
    110 /** @typedef {{title: string, width: number, height: number, deviceScaleFactor: number, userAgent: string, touch: boolean, mobile: boolean}} */
    111 WebInspector.OverridesSupport.Device = {};
    112 
    113 /**
    114  * @constructor
    115  * @param {number} latitude
    116  * @param {number} longitude
    117  * @param {string} error
    118  */
    119 WebInspector.OverridesSupport.GeolocationPosition = function(latitude, longitude, error)
    120 {
    121     this.latitude = latitude;
    122     this.longitude = longitude;
    123     this.error = error;
    124 }
    125 
    126 WebInspector.OverridesSupport.GeolocationPosition.prototype = {
    127     /**
    128      * @return {string}
    129      */
    130     toSetting: function()
    131     {
    132         return (typeof this.latitude === "number" && typeof this.longitude === "number" && typeof this.error === "string") ? this.latitude + "@" + this.longitude + ":" + this.error : "";
    133     }
    134 }
    135 
    136 /**
    137  * @return {!WebInspector.OverridesSupport.GeolocationPosition}
    138  */
    139 WebInspector.OverridesSupport.GeolocationPosition.parseSetting = function(value)
    140 {
    141     if (value) {
    142         var splitError = value.split(":");
    143         if (splitError.length === 2) {
    144             var splitPosition = splitError[0].split("@")
    145             if (splitPosition.length === 2)
    146                 return new WebInspector.OverridesSupport.GeolocationPosition(parseFloat(splitPosition[0]), parseFloat(splitPosition[1]), splitError[1]);
    147         }
    148     }
    149     return new WebInspector.OverridesSupport.GeolocationPosition(0, 0, "");
    150 }
    151 
    152 /**
    153  * @return {?WebInspector.OverridesSupport.GeolocationPosition}
    154  */
    155 WebInspector.OverridesSupport.GeolocationPosition.parseUserInput = function(latitudeString, longitudeString, errorStatus)
    156 {
    157     function isUserInputValid(value)
    158     {
    159         if (!value)
    160             return true;
    161         return /^[-]?[0-9]*[.]?[0-9]*$/.test(value);
    162     }
    163 
    164     if (!latitudeString ^ !latitudeString)
    165         return null;
    166 
    167     var isLatitudeValid = isUserInputValid(latitudeString);
    168     var isLongitudeValid = isUserInputValid(longitudeString);
    169 
    170     if (!isLatitudeValid && !isLongitudeValid)
    171         return null;
    172 
    173     var latitude = isLatitudeValid ? parseFloat(latitudeString) : -1;
    174     var longitude = isLongitudeValid ? parseFloat(longitudeString) : -1;
    175 
    176     return new WebInspector.OverridesSupport.GeolocationPosition(latitude, longitude, errorStatus ? "PositionUnavailable" : "");
    177 }
    178 
    179 WebInspector.OverridesSupport.GeolocationPosition.clearGeolocationOverride = function()
    180 {
    181     GeolocationAgent.clearGeolocationOverride();
    182 }
    183 
    184 /**
    185  * @constructor
    186  * @param {number} alpha
    187  * @param {number} beta
    188  * @param {number} gamma
    189  */
    190 WebInspector.OverridesSupport.DeviceOrientation = function(alpha, beta, gamma)
    191 {
    192     this.alpha = alpha;
    193     this.beta = beta;
    194     this.gamma = gamma;
    195 }
    196 
    197 WebInspector.OverridesSupport.DeviceOrientation.prototype = {
    198     /**
    199      * @return {string}
    200      */
    201     toSetting: function()
    202     {
    203         return JSON.stringify(this);
    204     }
    205 }
    206 
    207 /**
    208  * @return {!WebInspector.OverridesSupport.DeviceOrientation}
    209  */
    210 WebInspector.OverridesSupport.DeviceOrientation.parseSetting = function(value)
    211 {
    212     if (value) {
    213         var jsonObject = JSON.parse(value);
    214         return new WebInspector.OverridesSupport.DeviceOrientation(jsonObject.alpha, jsonObject.beta, jsonObject.gamma);
    215     }
    216     return new WebInspector.OverridesSupport.DeviceOrientation(0, 0, 0);
    217 }
    218 
    219 /**
    220  * @return {?WebInspector.OverridesSupport.DeviceOrientation}
    221  */
    222 WebInspector.OverridesSupport.DeviceOrientation.parseUserInput = function(alphaString, betaString, gammaString)
    223 {
    224     function isUserInputValid(value)
    225     {
    226         if (!value)
    227             return true;
    228         return /^[-]?[0-9]*[.]?[0-9]*$/.test(value);
    229     }
    230 
    231     if (!alphaString ^ !betaString ^ !gammaString)
    232         return null;
    233 
    234     var isAlphaValid = isUserInputValid(alphaString);
    235     var isBetaValid = isUserInputValid(betaString);
    236     var isGammaValid = isUserInputValid(gammaString);
    237 
    238     if (!isAlphaValid && !isBetaValid && !isGammaValid)
    239         return null;
    240 
    241     var alpha = isAlphaValid ? parseFloat(alphaString) : -1;
    242     var beta = isBetaValid ? parseFloat(betaString) : -1;
    243     var gamma = isGammaValid ? parseFloat(gammaString) : -1;
    244 
    245     return new WebInspector.OverridesSupport.DeviceOrientation(alpha, beta, gamma);
    246 }
    247 
    248 WebInspector.OverridesSupport.DeviceOrientation.clearDeviceOrientationOverride = function()
    249 {
    250     PageAgent.clearDeviceOrientationOverride();
    251 }
    252 
    253 /**
    254  * @param {string} value
    255  * @return {string}
    256  */
    257 WebInspector.OverridesSupport.deviceSizeValidator = function(value)
    258 {
    259     if (!value || (/^[\d]+$/.test(value) && value >= 0 && value <= WebInspector.OverridesSupport.MaxDeviceSize))
    260         return "";
    261     return WebInspector.UIString("Value must be non-negative integer");
    262 }
    263 
    264 /**
    265  * @param {string} value
    266  * @return {string}
    267  */
    268 WebInspector.OverridesSupport.deviceScaleFactorValidator = function(value)
    269 {
    270     if (!value || (/^[\d]+(\.\d+)?|\.\d+$/.test(value) && value >= 0 && value <= 10))
    271         return "";
    272     return WebInspector.UIString("Value must be non-negative float");
    273 }
    274 
    275 WebInspector.OverridesSupport.NetworkThroughputUnlimitedValue = -1;
    276 
    277 /** @typedef {{id: string, title: string, throughput: number, latency: number}} */
    278 WebInspector.OverridesSupport.NetworkConditionsPreset;
    279 
    280 WebInspector.OverridesSupport.prototype = {
    281     /**
    282      * @return {boolean}
    283      */
    284     canEmulate: function()
    285     {
    286         return !!this._target && this._target.canEmulate();
    287     },
    288 
    289     /**
    290      * @return {boolean}
    291      */
    292     emulationEnabled: function()
    293     {
    294         return this.canEmulate() && this.settings._emulationEnabled.get();
    295     },
    296 
    297     /**
    298      * @param {boolean} enabled
    299      */
    300     setEmulationEnabled: function(enabled)
    301     {
    302         if (this.canEmulate()) {
    303             this.settings._emulationEnabled.set(enabled);
    304             this.dispatchEventToListeners(WebInspector.OverridesSupport.Events.EmulationStateChanged);
    305             if (enabled && this.settings.emulateResolution.get())
    306                 this._target.pageAgent().resetScrollAndPageScaleFactor();
    307         }
    308     },
    309 
    310     /**
    311      * @return {boolean}
    312      */
    313     responsiveDesignAvailable: function()
    314     {
    315         return this._responsiveDesignAvailable;
    316     },
    317 
    318     /**
    319      * @param {?WebInspector.OverridesSupport.PageResizer} pageResizer
    320      * @param {!Size} availableSize
    321      */
    322     setPageResizer: function(pageResizer, availableSize)
    323     {
    324         if (pageResizer === this._pageResizer)
    325             return;
    326 
    327         if (this._pageResizer) {
    328             this._pageResizer.removeEventListener(WebInspector.OverridesSupport.PageResizer.Events.AvailableSizeChanged, this._onPageResizerAvailableSizeChanged, this);
    329             this._pageResizer.removeEventListener(WebInspector.OverridesSupport.PageResizer.Events.ResizeRequested, this._onPageResizerResizeRequested, this);
    330             this._pageResizer.removeEventListener(WebInspector.OverridesSupport.PageResizer.Events.FixedScaleRequested, this._onPageResizerFixedScaleRequested, this);
    331         }
    332         this._pageResizer = pageResizer;
    333         this._pageResizerAvailableSize = availableSize;
    334         if (this._pageResizer) {
    335             this._pageResizer.addEventListener(WebInspector.OverridesSupport.PageResizer.Events.AvailableSizeChanged, this._onPageResizerAvailableSizeChanged, this);
    336             this._pageResizer.addEventListener(WebInspector.OverridesSupport.PageResizer.Events.ResizeRequested, this._onPageResizerResizeRequested, this);
    337             this._pageResizer.addEventListener(WebInspector.OverridesSupport.PageResizer.Events.FixedScaleRequested, this._onPageResizerFixedScaleRequested, this);
    338         }
    339         if (this._initialized)
    340             this._deviceMetricsChanged();
    341     },
    342 
    343     /**
    344      * @param {!WebInspector.OverridesSupport.Device} device
    345      */
    346     emulateDevice: function(device)
    347     {
    348         this._deviceMetricsChangedListenerMuted = true;
    349         this._userAgentChangedListenerMuted = true;
    350         this.settings.userAgent.set(device.userAgent);
    351         this.settings.emulateResolution.set(true);
    352         this.settings.deviceWidth.set(device.width);
    353         this.settings.deviceHeight.set(device.height);
    354         this.settings.deviceScaleFactor.set(device.deviceScaleFactor);
    355         this.settings.emulateTouch.set(device.touch);
    356         this.settings.emulateMobile.set(device.mobile);
    357         delete this._deviceMetricsChangedListenerMuted;
    358         delete this._userAgentChangedListenerMuted;
    359 
    360         if (this._initialized) {
    361             this._deviceMetricsChanged();
    362             this._userAgentChanged();
    363             this._target.pageAgent().resetScrollAndPageScaleFactor();
    364         }
    365     },
    366 
    367     reset: function()
    368     {
    369         this._deviceMetricsChangedListenerMuted = true;
    370         this._userAgentChangedListenerMuted = true;
    371         this.settings.userAgent.set("");
    372         this.settings.emulateResolution.set(false);
    373         this.settings.deviceScaleFactor.set(0);
    374         this.settings.emulateTouch.set(false);
    375         this.settings.emulateMobile.set(false);
    376         this.settings.overrideDeviceOrientation.set(false);
    377         this.settings.overrideGeolocation.set(false);
    378         this.settings.overrideCSSMedia.set(false);
    379         this.settings.networkConditions.set({throughput: WebInspector.OverridesSupport.NetworkThroughputUnlimitedValue, latency: 0});
    380         delete this._deviceMetricsChangedListenerMuted;
    381         delete this._userAgentChangedListenerMuted;
    382 
    383         if (this._initialized) {
    384             this._deviceMetricsChanged();
    385             this._userAgentChanged();
    386         }
    387     },
    388 
    389     /**
    390      * @param {!WebInspector.OverridesSupport.Device} device
    391      * @return {boolean}
    392      */
    393     isEmulatingDevice: function(device)
    394     {
    395         var sameResolution = this.settings.emulateResolution.get() ?
    396             (this.settings.deviceWidth.get() === device.width && this.settings.deviceHeight.get() === device.height && this.settings.deviceScaleFactor.get() === device.deviceScaleFactor) :
    397             (!device.width && !device.height && !device.deviceScaleFactor);
    398         return this.settings.userAgent.get() === device.userAgent
    399             && this.settings.emulateTouch.get() === device.touch
    400             && this.settings.emulateMobile.get() === device.mobile
    401             && sameResolution;
    402     },
    403 
    404     /**
    405      * @return {!WebInspector.OverridesSupport.Device}
    406      */
    407     deviceFromCurrentSettings: function()
    408     {
    409         var device = {};
    410         if (this.settings.emulateResolution.get()) {
    411             device.width = this.settings.deviceWidth.get();
    412             device.height = this.settings.deviceHeight.get();
    413         } else {
    414             device.width = 0;
    415             device.height = 0;
    416         }
    417         device.deviceScaleFactor = this.settings.deviceScaleFactor.get();
    418         device.touch = this.settings.emulateTouch.get();
    419         device.mobile = this.settings.emulateMobile.get();
    420         device.userAgent = this.settings.userAgent.get();
    421         device.title = "";
    422         return device;
    423     },
    424 
    425     /**
    426      * @param {boolean} suspended
    427      */
    428     setTouchEmulationSuspended: function(suspended)
    429     {
    430         this._touchEmulationSuspended = suspended;
    431         if (this._initialized)
    432             this._emulateTouchEventsChanged();
    433     },
    434 
    435     applyInitialOverrides: function()
    436     {
    437         if (!this._target) {
    438             this._applyInitialOverridesOnTargetAdded = true;
    439             return;
    440         }
    441 
    442         this._initialized = true;
    443 
    444         this.settings._emulationEnabled.addChangeListener(this._userAgentChanged, this);
    445         this.settings.userAgent.addChangeListener(this._userAgentChanged, this);
    446 
    447         this.settings._emulationEnabled.addChangeListener(this._deviceMetricsChanged, this);
    448         this.settings.emulateResolution.addChangeListener(this._deviceMetricsChanged, this);
    449         this.settings.deviceWidth.addChangeListener(this._deviceMetricsChanged, this);
    450         this.settings.deviceHeight.addChangeListener(this._deviceMetricsChanged, this);
    451         this.settings.deviceScaleFactor.addChangeListener(this._deviceMetricsChanged, this);
    452         this.settings.emulateMobile.addChangeListener(this._deviceMetricsChanged, this);
    453         this.settings.deviceFitWindow.addChangeListener(this._deviceMetricsChanged, this);
    454 
    455         this.settings._emulationEnabled.addChangeListener(this._geolocationPositionChanged, this);
    456         this.settings.overrideGeolocation.addChangeListener(this._geolocationPositionChanged, this);
    457         this.settings.geolocationOverride.addChangeListener(this._geolocationPositionChanged, this);
    458 
    459         this.settings._emulationEnabled.addChangeListener(this._deviceOrientationChanged, this);
    460         this.settings.overrideDeviceOrientation.addChangeListener(this._deviceOrientationChanged, this);
    461         this.settings.deviceOrientationOverride.addChangeListener(this._deviceOrientationChanged, this);
    462 
    463         this.settings._emulationEnabled.addChangeListener(this._emulateTouchEventsChanged, this);
    464         this.settings.emulateTouch.addChangeListener(this._emulateTouchEventsChanged, this);
    465 
    466         this.settings._emulationEnabled.addChangeListener(this._cssMediaChanged, this);
    467         this.settings.overrideCSSMedia.addChangeListener(this._cssMediaChanged, this);
    468         this.settings.emulatedCSSMedia.addChangeListener(this._cssMediaChanged, this);
    469 
    470         this.settings._emulationEnabled.addChangeListener(this._networkConditionsChanged, this);
    471         this.settings.networkConditions.addChangeListener(this._networkConditionsChanged, this);
    472 
    473         this.settings._emulationEnabled.addChangeListener(this._showRulersChanged, this);
    474         WebInspector.settings.showMetricsRulers.addChangeListener(this._showRulersChanged, this);
    475         this._showRulersChanged();
    476 
    477         if (!this.emulationEnabled())
    478             return;
    479 
    480         if (this.settings.overrideDeviceOrientation.get())
    481             this._deviceOrientationChanged();
    482 
    483         if (this.settings.overrideGeolocation.get())
    484             this._geolocationPositionChanged();
    485 
    486         if (this.settings.emulateTouch.get())
    487             this._emulateTouchEventsChanged();
    488 
    489         if (this.settings.overrideCSSMedia.get())
    490             this._cssMediaChanged();
    491 
    492         this._deviceMetricsChanged();
    493         if (this.settings.emulateResolution.get())
    494             this._target.pageAgent().resetScrollAndPageScaleFactor();
    495 
    496         this._userAgentChanged();
    497 
    498         if (this.networkThroughputIsLimited())
    499             this._networkConditionsChanged();
    500     },
    501 
    502     _userAgentChanged: function()
    503     {
    504         if (this._userAgentChangedListenerMuted)
    505             return;
    506         var userAgent = this.emulationEnabled() ? this.settings.userAgent.get() : "";
    507         NetworkAgent.setUserAgentOverride(userAgent);
    508         if (this._userAgent !== userAgent)
    509             this._updateUserAgentWarningMessage(WebInspector.UIString("You might need to reload the page for proper user agent spoofing and viewport rendering."));
    510         this._userAgent = userAgent;
    511     },
    512 
    513     /**
    514      * @param {!WebInspector.Event} event
    515      */
    516     _onPageResizerAvailableSizeChanged: function(event)
    517     {
    518         this._pageResizerAvailableSize = /** @type {!Size} */ (event.data);
    519         if (this._initialized)
    520             this._deviceMetricsChanged();
    521     },
    522 
    523     /**
    524      * @param {!WebInspector.Event} event
    525      */
    526     _onPageResizerResizeRequested: function(event)
    527     {
    528         if (typeof event.data.width !== "undefined") {
    529             var width = /** @type {number} */ (event.data.width);
    530             if (width !== this.settings.deviceWidth.get())
    531                 this.settings.deviceWidth.set(width);
    532         }
    533         if (typeof event.data.height !== "undefined") {
    534             var height = /** @type {number} */ (event.data.height);
    535             if (height !== this.settings.deviceHeight.get())
    536                 this.settings.deviceHeight.set(height);
    537         }
    538     },
    539 
    540     /**
    541      * @param {!WebInspector.Event} event
    542      */
    543     _onPageResizerFixedScaleRequested: function(event)
    544     {
    545         this._fixedDeviceScale = /** @type {boolean} */ (event.data);
    546         if (this._initialized)
    547             this._deviceMetricsChanged();
    548     },
    549 
    550     _deviceMetricsChanged: function()
    551     {
    552         this._showRulersChanged();
    553 
    554         if (this._deviceMetricsChangedListenerMuted)
    555             return;
    556 
    557         if (!this.emulationEnabled()) {
    558             this._deviceMetricsThrottler.schedule(clearDeviceMetricsOverride.bind(this));
    559             if (this._pageResizer)
    560                 this._pageResizer.update(0, 0, 1);
    561             return;
    562         }
    563 
    564         var dipWidth = this.settings.emulateResolution.get() ? this.settings.deviceWidth.get() : 0;
    565         var dipHeight = this.settings.emulateResolution.get() ? this.settings.deviceHeight.get() : 0;
    566 
    567         var overrideWidth = dipWidth;
    568         var overrideHeight = dipHeight;
    569         var scale = 1;
    570         if (this._pageResizer) {
    571             var available = this._pageResizerAvailableSize;
    572             if (this.settings.deviceFitWindow.get()) {
    573                 if (this._fixedDeviceScale) {
    574                     scale = this._deviceScale;
    575                 } else {
    576                     scale = 1;
    577                     while (available.width < dipWidth * scale || available.height < dipHeight * scale)
    578                         scale *= 0.8;
    579                 }
    580             }
    581 
    582             this._pageResizer.update(Math.min(dipWidth * scale, available.width), Math.min(dipHeight * scale, available.height), scale);
    583             if (scale === 1 && available.width >= dipWidth && available.height >= dipHeight) {
    584                 // When we have enough space, no page size override is required. This will speed things up and remove lag.
    585                 overrideWidth = 0;
    586                 overrideHeight = 0;
    587             }
    588             if (dipWidth === 0 && dipHeight !== 0)
    589                 overrideWidth = Math.round(available.width / scale);
    590             if (dipHeight === 0 && dipWidth !== 0)
    591                 overrideHeight = Math.round(available.height / scale);
    592         }
    593         this._deviceScale = scale;
    594 
    595         this._deviceMetricsThrottler.schedule(setDeviceMetricsOverride.bind(this));
    596 
    597         /**
    598          * @param {!WebInspector.Throttler.FinishCallback} finishCallback
    599          * @this {WebInspector.OverridesSupport}
    600          */
    601         function setDeviceMetricsOverride(finishCallback)
    602         {
    603             this._target.pageAgent().setDeviceMetricsOverride(
    604                 overrideWidth, overrideHeight, this.settings.emulateResolution.get() ? this.settings.deviceScaleFactor.get() : 0,
    605                 this.settings.emulateMobile.get(), this._pageResizer ? false : this.settings.deviceFitWindow.get(), scale, 0, 0,
    606                 apiCallback.bind(this, finishCallback));
    607         }
    608 
    609         /**
    610          * @param {!WebInspector.Throttler.FinishCallback} finishCallback
    611          * @this {WebInspector.OverridesSupport}
    612          */
    613         function clearDeviceMetricsOverride(finishCallback)
    614         {
    615             this._target.pageAgent().clearDeviceMetricsOverride(apiCallback.bind(this, finishCallback));
    616         }
    617 
    618         /**
    619          * @param {!WebInspector.Throttler.FinishCallback} finishCallback
    620          * @param {?Protocol.Error} error
    621          * @this {WebInspector.OverridesSupport}
    622          */
    623         function apiCallback(finishCallback, error)
    624         {
    625             if (error) {
    626                 this._updateDeviceMetricsWarningMessage(WebInspector.UIString("Screen emulation is not available on this page."));
    627                 this._deviceMetricsOverrideAppliedForTest();
    628                 finishCallback();
    629                 return;
    630             }
    631 
    632             var mobileEnabled = this.emulationEnabled() && this.settings.emulateMobile.get();
    633             if (this._emulateMobileEnabled !== mobileEnabled)
    634                 this._updateDeviceMetricsWarningMessage(WebInspector.UIString("You might need to reload the page for proper user agent spoofing and viewport rendering."));
    635             this._emulateMobileEnabled = mobileEnabled;
    636             this._deviceMetricsOverrideAppliedForTest();
    637             finishCallback();
    638         }
    639     },
    640 
    641     _deviceMetricsOverrideAppliedForTest: function()
    642     {
    643         // Used for sniffing in tests.
    644     },
    645 
    646     _geolocationPositionChanged: function()
    647     {
    648         if (!this.emulationEnabled() || !this.settings.overrideGeolocation.get()) {
    649             GeolocationAgent.clearGeolocationOverride();
    650             return;
    651         }
    652         var geolocation = WebInspector.OverridesSupport.GeolocationPosition.parseSetting(this.settings.geolocationOverride.get());
    653         if (geolocation.error)
    654             GeolocationAgent.setGeolocationOverride();
    655         else
    656             GeolocationAgent.setGeolocationOverride(geolocation.latitude, geolocation.longitude, 150);
    657     },
    658 
    659     _deviceOrientationChanged: function()
    660     {
    661         if (!this.emulationEnabled() || !this.settings.overrideDeviceOrientation.get()) {
    662             PageAgent.clearDeviceOrientationOverride();
    663             return;
    664         }
    665 
    666         var deviceOrientation = WebInspector.OverridesSupport.DeviceOrientation.parseSetting(this.settings.deviceOrientationOverride.get());
    667         PageAgent.setDeviceOrientationOverride(deviceOrientation.alpha, deviceOrientation.beta, deviceOrientation.gamma);
    668     },
    669 
    670     _emulateTouchEventsChanged: function()
    671     {
    672         var emulateTouch = this.emulationEnabled() && this.settings.emulateTouch.get() && !this._touchEmulationSuspended;
    673         var targets = WebInspector.targetManager.targets();
    674         for (var i = 0; i < targets.length; ++i)
    675             targets[i].domModel.emulateTouchEventObjects(emulateTouch);
    676     },
    677 
    678     _cssMediaChanged: function()
    679     {
    680         var enabled = this.emulationEnabled() && this.settings.overrideCSSMedia.get();
    681         PageAgent.setEmulatedMedia(enabled ? this.settings.emulatedCSSMedia.get() : "");
    682         var targets = WebInspector.targetManager.targets();
    683         for (var i = 0; i < targets.length; ++i)
    684             targets[i].cssModel.mediaQueryResultChanged();
    685     },
    686 
    687     _networkConditionsChanged: function()
    688     {
    689         if (!this.emulationEnabled() || !this.networkThroughputIsLimited()) {
    690             NetworkAgent.emulateNetworkConditions(false, 0, 0, 0);
    691         } else {
    692             var conditions = this.settings.networkConditions.get();
    693             var throughput = conditions.throughput;
    694             var latency = conditions.latency;
    695             var offline = !throughput && !latency;
    696             NetworkAgent.emulateNetworkConditions(offline, latency, throughput, throughput);
    697         }
    698     },
    699 
    700     _pageResizerActive: function()
    701     {
    702         return this._pageResizer && this.emulationEnabled();
    703     },
    704 
    705     /**
    706      * @return {boolean}
    707      */
    708     showMetricsRulers: function()
    709     {
    710         return WebInspector.settings.showMetricsRulers.get() && !this._pageResizerActive();
    711     },
    712 
    713     /**
    714      * @return {boolean}
    715      */
    716     showExtensionLines: function()
    717     {
    718         return WebInspector.settings.showMetricsRulers.get();
    719     },
    720 
    721     _showRulersChanged: function()
    722     {
    723         PageAgent.setShowViewportSizeOnResize(!this._pageResizerActive(), WebInspector.settings.showMetricsRulers.get());
    724     },
    725 
    726     _onMainFrameNavigated: function()
    727     {
    728         if (this._initialized)
    729             this._deviceMetricsChanged();
    730         this._updateUserAgentWarningMessage("");
    731         this._updateDeviceMetricsWarningMessage("");
    732     },
    733 
    734     _dispatchWarningChanged: function()
    735     {
    736         this.dispatchEventToListeners(WebInspector.OverridesSupport.Events.OverridesWarningUpdated);
    737     },
    738 
    739     /**
    740      * @param {string} warningMessage
    741      */
    742     _updateDeviceMetricsWarningMessage: function(warningMessage)
    743     {
    744         this._deviceMetricsWarningMessage = warningMessage;
    745         this._dispatchWarningChanged();
    746     },
    747 
    748     /**
    749      * @param {string} warningMessage
    750      */
    751     _updateUserAgentWarningMessage: function(warningMessage)
    752     {
    753         this._userAgentWarningMessage = warningMessage;
    754         this._dispatchWarningChanged();
    755     },
    756 
    757     /**
    758      * @return {string}
    759      */
    760     warningMessage: function()
    761     {
    762         return this._deviceMetricsWarningMessage || this._userAgentWarningMessage || "";
    763     },
    764 
    765     clearWarningMessage: function()
    766     {
    767         this._deviceMetricsWarningMessage = "";
    768         this._userAgentWarningMessage = "";
    769         this._dispatchWarningChanged();
    770     },
    771 
    772     /**
    773      * @param {!WebInspector.Target} target
    774      */
    775     targetAdded: function(target)
    776     {
    777         if (this._target)
    778             return;
    779         this._target = target;
    780         target.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.MainFrameNavigated, this._onMainFrameNavigated, this);
    781 
    782         if (this._applyInitialOverridesOnTargetAdded) {
    783             delete this._applyInitialOverridesOnTargetAdded;
    784             this.applyInitialOverrides();
    785         }
    786         this.dispatchEventToListeners(WebInspector.OverridesSupport.Events.EmulationStateChanged);
    787     },
    788 
    789     swapDimensions: function()
    790     {
    791         var width = WebInspector.overridesSupport.settings.deviceWidth.get();
    792         var height = WebInspector.overridesSupport.settings.deviceHeight.get();
    793         WebInspector.overridesSupport.settings.deviceWidth.set(height);
    794         WebInspector.overridesSupport.settings.deviceHeight.set(width);
    795     },
    796 
    797     /**
    798      * @param {!WebInspector.Target} target
    799      */
    800     targetRemoved: function(target)
    801     {
    802         if (target === this._target) {
    803             target.resourceTreeModel.removeEventListener(WebInspector.ResourceTreeModel.EventTypes.MainFrameNavigated, this._onMainFrameNavigated, this);
    804             delete this._target;
    805             this.dispatchEventToListeners(WebInspector.OverridesSupport.Events.EmulationStateChanged);
    806         }
    807     },
    808 
    809     /**
    810      * @return {boolean}
    811      */
    812     networkThroughputIsLimited: function()
    813     {
    814         var conditions = this.settings.networkConditions.get();
    815         return conditions.throughput !== WebInspector.OverridesSupport.NetworkThroughputUnlimitedValue;
    816     },
    817 
    818     __proto__: WebInspector.Object.prototype
    819 }
    820 
    821 
    822 /**
    823  * @type {!WebInspector.OverridesSupport}
    824  */
    825 WebInspector.overridesSupport;
    826