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     WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.MainFrameNavigated, this._onMainFrameNavigated.bind(this), this);
     40     this._emulateViewportEnabled = false;
     41     this._userAgent = "";
     42     this._pageResizer = null;
     43     this._initialized = false;
     44     this._deviceMetricsThrottler = new WebInspector.Throttler(0);
     45     this._responsiveDesignAvailable = responsiveDesignAvailable;
     46     WebInspector.targetManager.observeTargets(this);
     47 }
     48 
     49 WebInspector.OverridesSupport.Events = {
     50     OverridesWarningUpdated: "OverridesWarningUpdated",
     51     EmulationStateChanged: "EmulationStateChanged"
     52 }
     53 
     54 /**
     55  * @interface
     56  * @extends {WebInspector.EventTarget}
     57  */
     58 WebInspector.OverridesSupport.PageResizer = function()
     59 {
     60 };
     61 
     62 WebInspector.OverridesSupport.PageResizer.Events = {
     63     AvailableSizeChanged: "AvailableSizeChanged",  // No data.
     64     ResizeRequested: "ResizeRequested"  // Data is of type {!Size}.
     65 };
     66 
     67 WebInspector.OverridesSupport.PageResizer.prototype = {
     68     /**
     69      * Zero width and height mean default size.
     70      * Scale should be applied to page-scale-dependent UI bits. Zero means no scale.
     71      * @param {number} dipWidth
     72      * @param {number} dipHeight
     73      * @param {number} scale
     74      */
     75     update: function(dipWidth, dipHeight, scale) { }
     76 };
     77 
     78 /**
     79  * @param {string} description
     80  * @param {string} userAgent
     81  * @constructor
     82  */
     83 WebInspector.OverridesSupport.Device = function(description, userAgent)
     84 {
     85     this.width = 800;
     86     this.height = 600;
     87     this.deviceScaleFactor = 1;
     88     this.textAutosizing = true;
     89     this.userAgent = userAgent;
     90     this.touch = true;
     91     this.viewport = true;
     92 
     93     var splitMetrics = description.split("x");
     94     if (splitMetrics.length >= 3) {
     95         this.width = parseInt(splitMetrics[0], 10);
     96         this.height = parseInt(splitMetrics[1], 10);
     97         this.deviceScaleFactor = parseFloat(splitMetrics[2]);
     98     }
     99     if (splitMetrics.length >= 4)
    100         this.touch = splitMetrics[3] == 1;
    101     if (splitMetrics.length >= 5)
    102         this.viewport = splitMetrics[4] == 1;
    103 }
    104 
    105 /**
    106  * @constructor
    107  * @param {number} latitude
    108  * @param {number} longitude
    109  * @param {string} error
    110  */
    111 WebInspector.OverridesSupport.GeolocationPosition = function(latitude, longitude, error)
    112 {
    113     this.latitude = latitude;
    114     this.longitude = longitude;
    115     this.error = error;
    116 }
    117 
    118 WebInspector.OverridesSupport.GeolocationPosition.prototype = {
    119     /**
    120      * @return {string}
    121      */
    122     toSetting: function()
    123     {
    124         return (typeof this.latitude === "number" && typeof this.longitude === "number" && typeof this.error === "string") ? this.latitude + "@" + this.longitude + ":" + this.error : "";
    125     }
    126 }
    127 
    128 /**
    129  * @return {!WebInspector.OverridesSupport.GeolocationPosition}
    130  */
    131 WebInspector.OverridesSupport.GeolocationPosition.parseSetting = function(value)
    132 {
    133     if (value) {
    134         var splitError = value.split(":");
    135         if (splitError.length === 2) {
    136             var splitPosition = splitError[0].split("@")
    137             if (splitPosition.length === 2)
    138                 return new WebInspector.OverridesSupport.GeolocationPosition(parseFloat(splitPosition[0]), parseFloat(splitPosition[1]), splitError[1]);
    139         }
    140     }
    141     return new WebInspector.OverridesSupport.GeolocationPosition(0, 0, "");
    142 }
    143 
    144 /**
    145  * @return {?WebInspector.OverridesSupport.GeolocationPosition}
    146  */
    147 WebInspector.OverridesSupport.GeolocationPosition.parseUserInput = function(latitudeString, longitudeString, errorStatus)
    148 {
    149     function isUserInputValid(value)
    150     {
    151         if (!value)
    152             return true;
    153         return /^[-]?[0-9]*[.]?[0-9]*$/.test(value);
    154     }
    155 
    156     if (!latitudeString ^ !latitudeString)
    157         return null;
    158 
    159     var isLatitudeValid = isUserInputValid(latitudeString);
    160     var isLongitudeValid = isUserInputValid(longitudeString);
    161 
    162     if (!isLatitudeValid && !isLongitudeValid)
    163         return null;
    164 
    165     var latitude = isLatitudeValid ? parseFloat(latitudeString) : -1;
    166     var longitude = isLongitudeValid ? parseFloat(longitudeString) : -1;
    167 
    168     return new WebInspector.OverridesSupport.GeolocationPosition(latitude, longitude, errorStatus ? "PositionUnavailable" : "");
    169 }
    170 
    171 WebInspector.OverridesSupport.GeolocationPosition.clearGeolocationOverride = function()
    172 {
    173     GeolocationAgent.clearGeolocationOverride();
    174 }
    175 
    176 /**
    177  * @constructor
    178  * @param {number} alpha
    179  * @param {number} beta
    180  * @param {number} gamma
    181  */
    182 WebInspector.OverridesSupport.DeviceOrientation = function(alpha, beta, gamma)
    183 {
    184     this.alpha = alpha;
    185     this.beta = beta;
    186     this.gamma = gamma;
    187 }
    188 
    189 WebInspector.OverridesSupport.DeviceOrientation.prototype = {
    190     /**
    191      * @return {string}
    192      */
    193     toSetting: function()
    194     {
    195         return JSON.stringify(this);
    196     }
    197 }
    198 
    199 /**
    200  * @return {!WebInspector.OverridesSupport.DeviceOrientation}
    201  */
    202 WebInspector.OverridesSupport.DeviceOrientation.parseSetting = function(value)
    203 {
    204     if (value) {
    205         var jsonObject = JSON.parse(value);
    206         return new WebInspector.OverridesSupport.DeviceOrientation(jsonObject.alpha, jsonObject.beta, jsonObject.gamma);
    207     }
    208     return new WebInspector.OverridesSupport.DeviceOrientation(0, 0, 0);
    209 }
    210 
    211 /**
    212  * @return {?WebInspector.OverridesSupport.DeviceOrientation}
    213  */
    214 WebInspector.OverridesSupport.DeviceOrientation.parseUserInput = function(alphaString, betaString, gammaString)
    215 {
    216     function isUserInputValid(value)
    217     {
    218         if (!value)
    219             return true;
    220         return /^[-]?[0-9]*[.]?[0-9]*$/.test(value);
    221     }
    222 
    223     if (!alphaString ^ !betaString ^ !gammaString)
    224         return null;
    225 
    226     var isAlphaValid = isUserInputValid(alphaString);
    227     var isBetaValid = isUserInputValid(betaString);
    228     var isGammaValid = isUserInputValid(gammaString);
    229 
    230     if (!isAlphaValid && !isBetaValid && !isGammaValid)
    231         return null;
    232 
    233     var alpha = isAlphaValid ? parseFloat(alphaString) : -1;
    234     var beta = isBetaValid ? parseFloat(betaString) : -1;
    235     var gamma = isGammaValid ? parseFloat(gammaString) : -1;
    236 
    237     return new WebInspector.OverridesSupport.DeviceOrientation(alpha, beta, gamma);
    238 }
    239 
    240 WebInspector.OverridesSupport.DeviceOrientation.clearDeviceOrientationOverride = function()
    241 {
    242     PageAgent.clearDeviceOrientationOverride();
    243 }
    244 
    245 /**
    246  * @param {string} value
    247  * @return {string}
    248  */
    249 WebInspector.OverridesSupport.deviceSizeValidator = function(value)
    250 {
    251     if (!value || (/^[\d]+$/.test(value) && value >= 0 && value <= 10000))
    252         return "";
    253     return WebInspector.UIString("Value must be non-negative integer");
    254 }
    255 
    256 /**
    257  * @param {string} value
    258  * @return {string}
    259  */
    260 WebInspector.OverridesSupport.deviceScaleFactorValidator = function(value)
    261 {
    262     if (!value || (/^[\d]+(\.\d+)?|\.\d+$/.test(value) && value >= 0 && value <= 10))
    263         return "";
    264     return WebInspector.UIString("Value must be non-negative float");
    265 }
    266 
    267 // Second element is user agent value.
    268 // Third element lists device metrics separated by 'x':
    269 // - screen width,
    270 // - screen height,
    271 // - device scale factor,
    272 // - touch (true by default if not present),
    273 // - viewport (true by default if not present).
    274 WebInspector.OverridesSupport._phones = [
    275     ["Apple iPhone 3GS",
    276      "Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_2_1 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5",
    277      "320x480x1"],
    278     ["Apple iPhone 4",
    279      "Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_2_1 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5",
    280      "320x480x2"],
    281     ["Apple iPhone 5",
    282      "Mozilla/5.0 (iPhone; CPU iPhone OS 7_0 like Mac OS X; en-us) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11A465 Safari/9537.53",
    283      "320x568x2"],
    284     ["BlackBerry Z10",
    285      "Mozilla/5.0 (BB10; Touch) AppleWebKit/537.10+ (KHTML, like Gecko) Version/10.0.9.2372 Mobile Safari/537.10+",
    286      "384x640x2"],
    287     ["BlackBerry Z30",
    288      "Mozilla/5.0 (BB10; Touch) AppleWebKit/537.10+ (KHTML, like Gecko) Version/10.0.9.2372 Mobile Safari/537.10+",
    289      "360x640x2"],
    290     ["Google Nexus 4",
    291      "Mozilla/5.0 (Linux; Android 4.2.1; en-us; Nexus 4 Build/JOP40D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Mobile Safari/535.19",
    292      "384x640x2"],
    293     ["Google Nexus 5",
    294      "Mozilla/5.0 (Linux; Android 4.2.1; en-us; Nexus 5 Build/JOP40D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Mobile Safari/535.19",
    295      "360x640x3"],
    296     ["Google Nexus S",
    297      "Mozilla/5.0 (Linux; U; Android 2.3.4; en-us; Nexus S Build/GRJ22) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
    298      "320x533x1.5"],
    299     ["HTC Evo, Touch HD, Desire HD, Desire",
    300      "Mozilla/5.0 (Linux; U; Android 2.2; en-us; Sprint APA9292KT Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
    301      "320x533x1.5"],
    302     ["HTC One X, EVO LTE",
    303      "Mozilla/5.0 (Linux; Android 4.0.3; HTC One X Build/IML74K) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19",
    304      "360x640x2"],
    305     ["HTC Sensation, Evo 3D",
    306      "Mozilla/5.0 (Linux; U; Android 4.0.3; en-us; HTC Sensation Build/IML74K) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
    307      "360x640x1.5"],
    308     ["LG Optimus 2X, Optimus 3D, Optimus Black",
    309      "Mozilla/5.0 (Linux; U; Android 2.2; en-us; LG-P990/V08c Build/FRG83) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1 MMS/LG-Android-MMS-V1.0/1.2",
    310      "320x533x1.5"],
    311     ["LG Optimus G",
    312      "Mozilla/5.0 (Linux; Android 4.0; LG-E975 Build/IMM76L) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Mobile Safari/535.19",
    313      "384x640x2"],
    314     ["LG Optimus LTE, Optimus 4X HD",
    315      "Mozilla/5.0 (Linux; U; Android 2.3; en-us; LG-P930 Build/GRJ90) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
    316      "424x753x1.7"],
    317     ["LG Optimus One",
    318      "Mozilla/5.0 (Linux; U; Android 2.2.1; en-us; LG-MS690 Build/FRG83) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
    319      "213x320x1.5"],
    320     ["Motorola Defy, Droid, Droid X, Milestone",
    321      "Mozilla/5.0 (Linux; U; Android 2.0; en-us; Milestone Build/ SHOLS_U2_01.03.1) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17",
    322      "320x569x1.5"],
    323     ["Motorola Droid 3, Droid 4, Droid Razr, Atrix 4G, Atrix 2",
    324      "Mozilla/5.0 (Linux; U; Android 2.2; en-us; Droid Build/FRG22D) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
    325      "540x960x1"],
    326     ["Motorola Droid Razr HD",
    327      "Mozilla/5.0 (Linux; U; Android 2.3; en-us; DROID RAZR 4G Build/6.5.1-73_DHD-11_M1-29) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
    328      "720x1280x1"],
    329     ["Nokia C5, C6, C7, N97, N8, X7",
    330      "NokiaN97/21.1.107 (SymbianOS/9.4; Series60/5.0 Mozilla/5.0; Profile/MIDP-2.1 Configuration/CLDC-1.1) AppleWebkit/525 (KHTML, like Gecko) BrowserNG/7.1.4",
    331      "360x640x1"],
    332     ["Nokia Lumia 7X0, Lumia 8XX, Lumia 900, N800, N810, N900",
    333      "Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; ARM; Touch; NOKIA; Lumia 820)",
    334      "320x533x1.5"],
    335     ["Samsung Galaxy Note 3",
    336      "Mozilla/5.0 (Linux; U; Android 4.3; en-us; SM-N900T Build/JSS15J) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
    337      "540x960x2"],
    338     ["Samsung Galaxy Note II",
    339      "Mozilla/5.0 (Linux; U; Android 4.1; en-us; GT-N7100 Build/JRO03C) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
    340      "360x640x2"],
    341     ["Samsung Galaxy Note",
    342      "Mozilla/5.0 (Linux; U; Android 2.3; en-us; SAMSUNG-SGH-I717 Build/GINGERBREAD) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
    343      "400x640x2"],
    344     ["Samsung Galaxy S III, Galaxy Nexus",
    345      "Mozilla/5.0 (Linux; U; Android 4.0; en-us; GT-I9300 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
    346      "360x640x2"],
    347     ["Samsung Galaxy S, S II, W",
    348      "Mozilla/5.0 (Linux; U; Android 2.1; en-us; GT-I9000 Build/ECLAIR) AppleWebKit/525.10+ (KHTML, like Gecko) Version/3.0.4 Mobile Safari/523.12.2",
    349      "320x533x1.5"],
    350     ["Samsung Galaxy S4",
    351      "Mozilla/5.0 (Linux; Android 4.2.2; GT-I9505 Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.59 Mobile Safari/537.36",
    352      "360x640x3"],
    353     ["Sony Xperia S, Ion",
    354      "Mozilla/5.0 (Linux; U; Android 4.0; en-us; LT28at Build/6.1.C.1.111) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
    355      "360x640x2"],
    356     ["Sony Xperia Sola, U",
    357      "Mozilla/5.0 (Linux; U; Android 2.3; en-us; SonyEricssonST25i Build/6.0.B.1.564) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
    358      "480x854x1"],
    359     ["Sony Xperia Z, Z1",
    360      "Mozilla/5.0 (Linux; U; Android 4.2; en-us; SonyC6903 Build/14.1.G.1.518) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
    361      "360x640x3"],
    362 ];
    363 
    364 WebInspector.OverridesSupport._tablets = [
    365     ["Amazon Kindle Fire HDX 7\u2033",
    366      "Mozilla/5.0 (Linux; U; en-us; KFTHWI Build/JDQ39) AppleWebKit/535.19 (KHTML, like Gecko) Silk/3.13 Safari/535.19 Silk-Accelerated=true",
    367      "1920x1200x2"],
    368     ["Amazon Kindle Fire HDX 8.9\u2033",
    369      "Mozilla/5.0 (Linux; U; en-us; KFAPWI Build/JDQ39) AppleWebKit/535.19 (KHTML, like Gecko) Silk/3.13 Safari/535.19 Silk-Accelerated=true",
    370      "2560x1600x2"],
    371     ["Amazon Kindle Fire (First Generation)",
    372      "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; en-us; Silk/1.0.141.16-Gen4_11004310) AppleWebkit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16 Silk-Accelerated=true",
    373      "1024x600x1"],
    374     ["Apple iPad 1 / 2 / iPad Mini",
    375      "Mozilla/5.0 (iPad; CPU OS 4_3_5 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8L1 Safari/6533.18.5",
    376      "1024x768x1"],
    377     ["Apple iPad 3 / 4",
    378      "Mozilla/5.0 (iPad; CPU OS 7_0 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11A465 Safari/9537.53",
    379      "1024x768x2"],
    380     ["BlackBerry PlayBook",
    381      "Mozilla/5.0 (PlayBook; U; RIM Tablet OS 2.1.0; en-US) AppleWebKit/536.2+ (KHTML like Gecko) Version/7.2.1.0 Safari/536.2+",
    382      "1024x600x1"],
    383     ["Google Nexus 10",
    384      "Mozilla/5.0 (Linux; Android 4.3; Nexus 10 Build/JSS15Q) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.72 Safari/537.36",
    385      "1280x800x2"],
    386     ["Google Nexus 7 2",
    387      "Mozilla/5.0 (Linux; Android 4.3; Nexus 7 Build/JSS15Q) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.72 Safari/537.36",
    388      "960x600x2"],
    389     ["Google Nexus 7",
    390      "Mozilla/5.0 (Linux; Android 4.3; Nexus 7 Build/JSS15Q) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.72 Safari/537.36",
    391      "966x604x1.325"],
    392     ["Motorola Xoom, Xyboard",
    393      "Mozilla/5.0 (Linux; U; Android 3.0; en-us; Xoom Build/HRI39) AppleWebKit/525.10 (KHTML, like Gecko) Version/3.0.4 Mobile Safari/523.12.2",
    394      "1280x800x1"],
    395     ["Samsung Galaxy Tab 7.7, 8.9, 10.1",
    396      "Mozilla/5.0 (Linux; U; Android 2.2; en-us; SCH-I800 Build/FROYO) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
    397      "1280x800x1"],
    398     ["Samsung Galaxy Tab",
    399      "Mozilla/5.0 (Linux; U; Android 2.2; en-us; SCH-I800 Build/FROYO) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
    400      "1024x600x1"],
    401 ];
    402 
    403 WebInspector.OverridesSupport._notebooks = [
    404     ["Notebook with touch",
    405      "",
    406      "1280x950x1x1x0"],
    407     ["Notebook with HiDPI screen",
    408      "",
    409      "1440x900x2x0x0"],
    410     ["Generic notebook",
    411      "",
    412      "1280x800x1x0x0"],
    413 ];
    414 
    415 WebInspector.OverridesSupport._networkThroughputUnlimitedValue = -1;
    416 WebInspector.OverridesSupport._networkThroughputPresets = [
    417     ["Offline", 0],
    418     ["5 Kbps", 5 * 1024 / 8],
    419     ["10 Kbps (GSM)", 10 * 1024 / 8],
    420     ["20 Kbps", 20 * 1024 / 8],
    421     ["40 Kbps (GPRS)", 40 * 1024 / 8],
    422     ["80 Kbps", 80 * 1024 / 8],
    423     ["160 Kbps (EDGE)", 160 * 1024 / 8],
    424     ["320 Kbps", 320 * 1024 / 8],
    425     ["640 Kbps (3G)", 640 * 1024 / 8],
    426     ["1 Mbps", 1024 * 1024 / 8],
    427     ["2 Mbps (802.11b)", 2048 * 1024 / 8],
    428     ["No throttling", WebInspector.OverridesSupport._networkThroughputUnlimitedValue]
    429 ];
    430 
    431 WebInspector.OverridesSupport.prototype = {
    432     /**
    433      * @return {boolean}
    434      */
    435     canEmulate: function()
    436     {
    437         return !!this._target && !this._target.isMobile();
    438     },
    439 
    440     /**
    441      * @return {boolean}
    442      */
    443     emulationEnabled: function()
    444     {
    445         return this.canEmulate() && this.settings._emulationEnabled.get();
    446     },
    447 
    448     /**
    449      * @param {boolean} enabled
    450      */
    451     setEmulationEnabled: function(enabled)
    452     {
    453         if (this.canEmulate()) {
    454             this.settings._emulationEnabled.set(enabled);
    455             this.dispatchEventToListeners(WebInspector.OverridesSupport.Events.EmulationStateChanged);
    456         }
    457     },
    458 
    459     /**
    460      * @return {boolean}
    461      */
    462     responsiveDesignAvailable: function()
    463     {
    464         return this._responsiveDesignAvailable;
    465     },
    466 
    467     /**
    468      * @param {?WebInspector.OverridesSupport.PageResizer} pageResizer
    469      * @param {!Size} availableSize
    470      */
    471     setPageResizer: function(pageResizer, availableSize)
    472     {
    473         if (pageResizer === this._pageResizer)
    474             return;
    475 
    476         if (this._pageResizer) {
    477             this._pageResizer.removeEventListener(WebInspector.OverridesSupport.PageResizer.Events.AvailableSizeChanged, this._onPageResizerAvailableSizeChanged, this);
    478             this._pageResizer.removeEventListener(WebInspector.OverridesSupport.PageResizer.Events.ResizeRequested, this._onPageResizerResizeRequested, this);
    479         }
    480         this._pageResizer = pageResizer;
    481         this._pageResizerAvailableSize = availableSize;
    482         if (this._pageResizer) {
    483             this._pageResizer.addEventListener(WebInspector.OverridesSupport.PageResizer.Events.AvailableSizeChanged, this._onPageResizerAvailableSizeChanged, this);
    484             this._pageResizer.addEventListener(WebInspector.OverridesSupport.PageResizer.Events.ResizeRequested, this._onPageResizerResizeRequested, this);
    485         }
    486         if (this._initialized)
    487             this._deviceMetricsChanged();
    488     },
    489 
    490     /**
    491      * @param {!WebInspector.OverridesSupport.Device} device
    492      */
    493     emulateDevice: function(device)
    494     {
    495         this._deviceMetricsChangedListenerMuted = true;
    496         this._userAgentChangedListenerMuted = true;
    497         this.settings.userAgent.set(device.userAgent);
    498         this.settings.deviceWidth.set(device.width);
    499         this.settings.deviceHeight.set(device.height);
    500         this.settings.deviceScaleFactor.set(device.deviceScaleFactor);
    501         this.settings.deviceTextAutosizing.set(device.textAutosizing);
    502         this.settings.emulateTouch.set(device.touch);
    503         this.settings.emulateViewport.set(device.viewport);
    504         delete this._deviceMetricsChangedListenerMuted;
    505         delete this._userAgentChangedListenerMuted;
    506 
    507         if (this._initialized) {
    508             this._deviceMetricsChanged();
    509             this._userAgentChanged();
    510         }
    511     },
    512 
    513     reset: function()
    514     {
    515         this._deviceMetricsChangedListenerMuted = true;
    516         this._userAgentChangedListenerMuted = true;
    517         this.settings.userAgent.set("");
    518         this.settings.deviceWidth.set(0);
    519         this.settings.deviceHeight.set(0);
    520         this.settings.deviceScaleFactor.set(0);
    521         this.settings.deviceTextAutosizing.set(false);
    522         this.settings.emulateTouch.set(false);
    523         this.settings.emulateViewport.set(false);
    524         this.settings.overrideDeviceOrientation.set(false);
    525         this.settings.overrideGeolocation.set(false);
    526         this.settings.overrideCSSMedia.set(false);
    527         this.settings.networkConditionsThroughput.set(WebInspector.OverridesSupport._networkThroughputUnlimitedValue);
    528         delete this._deviceMetricsChangedListenerMuted;
    529         delete this._userAgentChangedListenerMuted;
    530 
    531         if (this._initialized) {
    532             this._deviceMetricsChanged();
    533             this._userAgentChanged();
    534         }
    535     },
    536 
    537     /**
    538      * @param {!WebInspector.OverridesSupport.Device} device
    539      * @return {boolean}
    540      */
    541     isEmulatingDevice: function(device)
    542     {
    543         return this.settings.userAgent.get() === device.userAgent
    544             && this.settings.deviceWidth.get() === device.width
    545             && this.settings.deviceHeight.get() === device.height
    546             && this.settings.deviceScaleFactor.get() === device.deviceScaleFactor
    547             && this.settings.deviceTextAutosizing.get() === device.textAutosizing
    548             && this.settings.emulateTouch.get() === device.touch
    549             && this.settings.emulateViewport.get() === device.viewport;
    550     },
    551 
    552     applyInitialOverrides: function()
    553     {
    554         if (!this._target) {
    555             this._applyInitialOverridesOnTargetAdded = true;
    556             return;
    557         }
    558 
    559         this._initialized = true;
    560 
    561         this.settings._emulationEnabled.addChangeListener(this._userAgentChanged, this);
    562         this.settings.userAgent.addChangeListener(this._userAgentChanged, this);
    563 
    564         this.settings._emulationEnabled.addChangeListener(this._deviceMetricsChanged, this);
    565         this.settings.deviceWidth.addChangeListener(this._deviceMetricsChanged, this);
    566         this.settings.deviceHeight.addChangeListener(this._deviceMetricsChanged, this);
    567         this.settings.deviceScaleFactor.addChangeListener(this._deviceMetricsChanged, this);
    568         this.settings.deviceTextAutosizing.addChangeListener(this._deviceMetricsChanged, this);
    569         this.settings.emulateViewport.addChangeListener(this._deviceMetricsChanged, this);
    570         this.settings.deviceFitWindow.addChangeListener(this._deviceMetricsChanged, this);
    571 
    572         this.settings._emulationEnabled.addChangeListener(this._geolocationPositionChanged, this);
    573         this.settings.overrideGeolocation.addChangeListener(this._geolocationPositionChanged, this);
    574         this.settings.geolocationOverride.addChangeListener(this._geolocationPositionChanged, this);
    575 
    576         this.settings._emulationEnabled.addChangeListener(this._deviceOrientationChanged, this);
    577         this.settings.overrideDeviceOrientation.addChangeListener(this._deviceOrientationChanged, this);
    578         this.settings.deviceOrientationOverride.addChangeListener(this._deviceOrientationChanged, this);
    579 
    580         this.settings._emulationEnabled.addChangeListener(this._emulateTouchEventsChanged, this);
    581         this.settings.emulateTouch.addChangeListener(this._emulateTouchEventsChanged, this);
    582 
    583         this.settings._emulationEnabled.addChangeListener(this._cssMediaChanged, this);
    584         this.settings.overrideCSSMedia.addChangeListener(this._cssMediaChanged, this);
    585         this.settings.emulatedCSSMedia.addChangeListener(this._cssMediaChanged, this);
    586 
    587         if (WebInspector.experimentsSettings.networkConditions.isEnabled()) {
    588             this.settings._emulationEnabled.addChangeListener(this._networkConditionsChanged, this);
    589             this.settings.networkConditionsThroughput.addChangeListener(this._networkConditionsChanged, this);
    590         }
    591 
    592         WebInspector.settings.showMetricsRulers.addChangeListener(this._showRulersChanged, this);
    593         this._showRulersChanged();
    594 
    595         if (!this.emulationEnabled())
    596             return;
    597 
    598         if (this.settings.overrideDeviceOrientation.get())
    599             this._deviceOrientationChanged();
    600 
    601         if (this.settings.overrideGeolocation.get())
    602             this._geolocationPositionChanged();
    603 
    604         if (this.settings.emulateTouch.get())
    605             this._emulateTouchEventsChanged();
    606 
    607         if (this.settings.overrideCSSMedia.get())
    608             this._cssMediaChanged();
    609 
    610         this._deviceMetricsChanged();
    611 
    612         this._userAgentChanged();
    613 
    614         if (WebInspector.experimentsSettings.networkConditions.isEnabled() && this.networkThroughputIsLimited())
    615             this._networkConditionsChanged();
    616     },
    617 
    618     _userAgentChanged: function()
    619     {
    620         if (this._userAgentChangedListenerMuted)
    621             return;
    622         var userAgent = this.emulationEnabled() ? this.settings.userAgent.get() : "";
    623         NetworkAgent.setUserAgentOverride(userAgent);
    624         if (this._userAgent !== userAgent)
    625             this._updateUserAgentWarningMessage(WebInspector.UIString("You might need to reload the page for proper user agent spoofing and viewport rendering."));
    626         this._userAgent = userAgent;
    627     },
    628 
    629     /**
    630      * @param {!WebInspector.Event} event
    631      */
    632     _onPageResizerAvailableSizeChanged: function(event)
    633     {
    634         this._pageResizerAvailableSize = /** @type {!Size} */ (event.data);
    635         if (this._initialized)
    636             this._deviceMetricsChanged();
    637     },
    638 
    639     /**
    640      * @param {!WebInspector.Event} event
    641      */
    642     _onPageResizerResizeRequested: function(event)
    643     {
    644         if (typeof event.data.width !== "undefined") {
    645             var width = /** @type {number} */ (event.data.width);
    646             if (width !== this.settings.deviceWidth.get())
    647                 this.settings.deviceWidth.set(width);
    648         }
    649         if (typeof event.data.height !== "undefined") {
    650             var height = /** @type {number} */ (event.data.height);
    651             if (height !== this.settings.deviceHeight.get())
    652                 this.settings.deviceHeight.set(height);
    653         }
    654     },
    655 
    656     _deviceMetricsChanged: function()
    657     {
    658         this._showRulersChanged();
    659 
    660         if (this._deviceMetricsChangedListenerMuted)
    661             return;
    662 
    663         if (!this.emulationEnabled()) {
    664             this._deviceMetricsThrottler.schedule(clearDeviceMetricsOverride.bind(this));
    665             if (this._pageResizer)
    666                 this._pageResizer.update(0, 0, 1);
    667             return;
    668         }
    669 
    670         var dipWidth = this.settings.deviceWidth.get();
    671         var dipHeight = this.settings.deviceHeight.get();
    672 
    673         var overrideWidth = dipWidth;
    674         var overrideHeight = dipHeight;
    675         if (this._pageResizer) {
    676             var available = this._pageResizerAvailableSize;
    677             if (available.width >= dipWidth && available.height >= dipHeight) {
    678                 this._pageResizer.update(dipWidth, dipHeight, 0);
    679                 // When we have enough space, no page size override is required. This will speed things up and remove lag.
    680                 overrideWidth = 0;
    681                 overrideHeight = 0;
    682             } else {
    683                 this._pageResizer.update(Math.min(dipWidth, available.width), Math.min(dipHeight, available.height), 0);
    684             }
    685         }
    686 
    687         this._deviceMetricsThrottler.schedule(setDeviceMetricsOverride.bind(this));
    688 
    689         /**
    690          * @param {!WebInspector.Throttler.FinishCallback} finishCallback
    691          * @this {WebInspector.OverridesSupport}
    692          */
    693         function setDeviceMetricsOverride(finishCallback)
    694         {
    695             PageAgent.setDeviceMetricsOverride(
    696                 overrideWidth, overrideHeight, this.settings.deviceScaleFactor.get(),
    697                 this.settings.emulateViewport.get(), this._pageResizer ? false : this.settings.deviceFitWindow.get(),
    698                 this.settings.deviceTextAutosizing.get(), this._fontScaleFactor(overrideWidth || dipWidth, overrideHeight || dipHeight),
    699                 apiCallback.bind(this, finishCallback));
    700         }
    701 
    702         /**
    703          * @param {!WebInspector.Throttler.FinishCallback} finishCallback
    704          * @this {WebInspector.OverridesSupport}
    705          */
    706         function clearDeviceMetricsOverride(finishCallback)
    707         {
    708             PageAgent.clearDeviceMetricsOverride(apiCallback.bind(this, finishCallback));
    709         }
    710 
    711         /**
    712          * @param {!WebInspector.Throttler.FinishCallback} finishCallback
    713          * @param {?Protocol.Error} error
    714          * @this {WebInspector.OverridesSupport}
    715          */
    716         function apiCallback(finishCallback, error)
    717         {
    718             if (error) {
    719                 this._updateDeviceMetricsWarningMessage(WebInspector.UIString("Screen emulation is not available on this page."));
    720                 this._deviceMetricsOverrideAppliedForTest();
    721                 finishCallback();
    722                 return;
    723             }
    724 
    725             var viewportEnabled = this.emulationEnabled() && this.settings.emulateViewport.get();
    726             if (this._emulateViewportEnabled !== viewportEnabled)
    727                 this._updateDeviceMetricsWarningMessage(WebInspector.UIString("You might need to reload the page for proper user agent spoofing and viewport rendering."));
    728             this._emulateViewportEnabled = viewportEnabled;
    729             this._deviceMetricsOverrideAppliedForTest();
    730             finishCallback();
    731         }
    732     },
    733 
    734     _deviceMetricsOverrideAppliedForTest: function()
    735     {
    736         // Used for sniffing in tests.
    737     },
    738 
    739     _geolocationPositionChanged: function()
    740     {
    741         if (!this.emulationEnabled() || !this.settings.overrideGeolocation.get()) {
    742             GeolocationAgent.clearGeolocationOverride();
    743             return;
    744         }
    745         var geolocation = WebInspector.OverridesSupport.GeolocationPosition.parseSetting(this.settings.geolocationOverride.get());
    746         if (geolocation.error)
    747             GeolocationAgent.setGeolocationOverride();
    748         else
    749             GeolocationAgent.setGeolocationOverride(geolocation.latitude, geolocation.longitude, 150);
    750     },
    751 
    752     _deviceOrientationChanged: function()
    753     {
    754         if (!this.emulationEnabled() || !this.settings.overrideDeviceOrientation.get()) {
    755             PageAgent.clearDeviceOrientationOverride();
    756             return;
    757         }
    758 
    759         var deviceOrientation = WebInspector.OverridesSupport.DeviceOrientation.parseSetting(this.settings.deviceOrientationOverride.get());
    760         PageAgent.setDeviceOrientationOverride(deviceOrientation.alpha, deviceOrientation.beta, deviceOrientation.gamma);
    761     },
    762 
    763     _emulateTouchEventsChanged: function()
    764     {
    765         var emulateTouch = this.emulationEnabled() && this.settings.emulateTouch.get();
    766         var targets = WebInspector.targetManager.targets();
    767         for (var i = 0; i < targets.length; ++i)
    768             targets[i].domModel.emulateTouchEventObjects(emulateTouch);
    769     },
    770 
    771     _cssMediaChanged: function()
    772     {
    773         var enabled = this.emulationEnabled() && this.settings.overrideCSSMedia.get();
    774         PageAgent.setEmulatedMedia(enabled ? this.settings.emulatedCSSMedia.get() : "");
    775         var targets = WebInspector.targetManager.targets();
    776         for (var i = 0; i < targets.length; ++i)
    777             targets[i].cssModel.mediaQueryResultChanged();
    778     },
    779 
    780     _networkConditionsChanged: function()
    781     {
    782         if (!this.emulationEnabled() || !this.networkThroughputIsLimited()) {
    783             NetworkAgent.emulateNetworkConditions(false, 0, 0, 0);
    784         } else {
    785             var throughput = this.settings.networkConditionsThroughput.get();
    786             var offline = !throughput;
    787             NetworkAgent.emulateNetworkConditions(offline, 0, throughput, throughput);
    788         }
    789     },
    790 
    791     /**
    792      * @return {boolean}
    793      */
    794     showMetricsRulers: function()
    795     {
    796         var rulersInPageResizer = this._pageResizer && this.emulationEnabled();
    797         return WebInspector.settings.showMetricsRulers.get() && !rulersInPageResizer;
    798     },
    799 
    800     _showRulersChanged: function()
    801     {
    802         if (WebInspector.experimentsSettings.responsiveDesign.isEnabled())
    803             return;
    804         PageAgent.setShowViewportSizeOnResize(true, this.showMetricsRulers());
    805     },
    806 
    807     _onMainFrameNavigated: function()
    808     {
    809         if (this._initialized)
    810             this._deviceMetricsChanged();
    811         this._updateUserAgentWarningMessage("");
    812         this._updateDeviceMetricsWarningMessage("");
    813     },
    814 
    815     /**
    816      * @param {string} warningMessage
    817      */
    818     _updateDeviceMetricsWarningMessage: function(warningMessage)
    819     {
    820         this._deviceMetricsWarningMessage = warningMessage;
    821         this.dispatchEventToListeners(WebInspector.OverridesSupport.Events.OverridesWarningUpdated);
    822     },
    823 
    824     /**
    825      * @param {string} warningMessage
    826      */
    827     _updateUserAgentWarningMessage: function(warningMessage)
    828     {
    829         this._userAgentWarningMessage = warningMessage;
    830         this.dispatchEventToListeners(WebInspector.OverridesSupport.Events.OverridesWarningUpdated);
    831     },
    832 
    833     /**
    834      * @return {string}
    835      */
    836     warningMessage: function()
    837     {
    838         return this._deviceMetricsWarningMessage || this._userAgentWarningMessage || "";
    839     },
    840 
    841     clearWarningMessage: function()
    842     {
    843         this._deviceMetricsWarningMessage = "";
    844         this._userAgentWarningMessage = "";
    845         this.dispatchEventToListeners(WebInspector.OverridesSupport.Events.OverridesWarningUpdated);
    846     },
    847 
    848     /**
    849      * @param {!WebInspector.Target} target
    850      */
    851     targetAdded: function(target)
    852     {
    853         // FIXME: adapt this to multiple targets.
    854         if (this._target)
    855             return;
    856         this._target = target;
    857 
    858         this.settings = {};
    859         this.settings._emulationEnabled = WebInspector.settings.createSetting("emulationEnabled", false);
    860 
    861         this.settings.userAgent = WebInspector.settings.createSetting("userAgent", "");
    862 
    863         this.settings.deviceWidth = WebInspector.settings.createSetting("deviceWidth", 0);
    864         this.settings.deviceHeight = WebInspector.settings.createSetting("deviceHeight", 0);
    865         this.settings.deviceScaleFactor = WebInspector.settings.createSetting("deviceScaleFactor", 0);
    866         this.settings.deviceTextAutosizing = WebInspector.settings.createSetting("deviceTextAutosizing", true);
    867         this.settings.deviceFitWindow = WebInspector.settings.createSetting("deviceFitWindow", true);
    868         // FIXME: rename viewport to mobile everywhere in the code.
    869         this.settings.emulateViewport = WebInspector.settings.createSetting("emulateViewport", false);
    870 
    871         this.settings.emulateTouch = WebInspector.settings.createSetting("emulateTouch", false);
    872 
    873         this.settings.overrideGeolocation = WebInspector.settings.createSetting("overrideGeolocation", false);
    874         this.settings.geolocationOverride = WebInspector.settings.createSetting("geolocationOverride", "");
    875 
    876         this.settings.overrideDeviceOrientation = WebInspector.settings.createSetting("overrideDeviceOrientation", false);
    877         this.settings.deviceOrientationOverride = WebInspector.settings.createSetting("deviceOrientationOverride", "");
    878 
    879         this.settings.overrideCSSMedia = WebInspector.settings.createSetting("overrideCSSMedia", false);
    880         this.settings.emulatedCSSMedia = WebInspector.settings.createSetting("emulatedCSSMedia", "print");
    881 
    882         this.settings.networkConditionsThroughput = WebInspector.settings.createSetting("networkConditionsThroughput", WebInspector.OverridesSupport._networkThroughputUnlimitedValue);
    883 
    884         if (this._applyInitialOverridesOnTargetAdded) {
    885             delete this._applyInitialOverridesOnTargetAdded;
    886             this.applyInitialOverrides();
    887         }
    888     },
    889 
    890     swapDimensions: function()
    891     {
    892         var width = WebInspector.overridesSupport.settings.deviceWidth.get();
    893         var height = WebInspector.overridesSupport.settings.deviceHeight.get();
    894         WebInspector.overridesSupport.settings.deviceWidth.set(height);
    895         WebInspector.overridesSupport.settings.deviceHeight.set(width);
    896     },
    897 
    898     /**
    899      * @param {!WebInspector.Target} target
    900      */
    901     targetRemoved: function(target)
    902     {
    903         // FIXME: adapt this to multiple targets.
    904     },
    905 
    906     /**
    907      * @return {boolean}
    908      */
    909     hasTouchInputs: function()
    910     {
    911         return !!this._target && this._target.hasTouchInputs;
    912     },
    913 
    914     /**
    915      * @return {boolean}
    916      */
    917     networkThroughputIsLimited: function()
    918     {
    919         return this.settings.networkConditionsThroughput.get() !== WebInspector.OverridesSupport._networkThroughputUnlimitedValue;
    920     },
    921 
    922     /**
    923      * Compute the font scale factor.
    924      *
    925      * Chromium on Android uses a device scale adjustment for fonts used in text autosizing for
    926      * improved legibility. This function computes this adjusted value for text autosizing.
    927      *
    928      * For a description of the Android device scale adjustment algorithm, see:
    929      *     chrome/browser/chrome_content_browser_client.cc, GetFontScaleMultiplier(...)
    930      *
    931      * @param {number} width
    932      * @param {number} height
    933      * @return {number} font scale factor.
    934      */
    935     _fontScaleFactor: function(width, height)
    936     {
    937         if (!this.emulationEnabled())
    938             return 1;
    939         var deviceScaleFactor = this.settings.deviceScaleFactor.get();
    940 
    941         if (!width || !height || !deviceScaleFactor)
    942             return 1;
    943 
    944         var minWidth = Math.min(width, height) / deviceScaleFactor;
    945 
    946         var kMinFSM = 1.05;
    947         var kWidthForMinFSM = 320;
    948         var kMaxFSM = 1.3;
    949         var kWidthForMaxFSM = 800;
    950 
    951         if (minWidth <= kWidthForMinFSM)
    952             return kMinFSM;
    953         if (minWidth >= kWidthForMaxFSM)
    954             return kMaxFSM;
    955 
    956         // The font scale multiplier varies linearly between kMinFSM and kMaxFSM.
    957         var ratio = (minWidth - kWidthForMinFSM) / (kWidthForMaxFSM - kWidthForMinFSM);
    958         return ratio * (kMaxFSM - kMinFSM) + kMinFSM;
    959     },
    960 
    961     /**
    962      * @param {!Document} document
    963      * @return {!Element}
    964      */
    965     createDeviceSelect: function(document)
    966     {
    967         var deviceSelectElement = document.createElement("select");
    968 
    969         var selectDeviceOption = new Option(WebInspector.UIString("<Select model>"), WebInspector.UIString("<Select model>"));
    970         selectDeviceOption.device = new WebInspector.OverridesSupport.Device("", "");
    971         deviceSelectElement.add(selectDeviceOption);
    972 
    973         addGroup(WebInspector.UIString("Devices"), WebInspector.OverridesSupport._phones.concat(WebInspector.OverridesSupport._tablets));
    974         addGroup(WebInspector.UIString("Notebooks"), WebInspector.OverridesSupport._notebooks);
    975 
    976         /**
    977          * @param {string} name
    978          * @param {!Array.<!Array.<string>>} devices
    979          */
    980         function addGroup(name, devices)
    981         {
    982             devices = devices.slice();
    983             devices.sort();
    984             var groupElement = deviceSelectElement.createChild("optgroup");
    985             groupElement.label = name;
    986             for (var i = 0; i < devices.length; ++i) {
    987                 var device = devices[i];
    988                 var option = new Option(device[0], device[0]);
    989                 option.device = new WebInspector.OverridesSupport.Device(device[2], device[1]);
    990                 groupElement.appendChild(option);
    991             }
    992         }
    993 
    994         deviceSelectElement.addEventListener("change", deviceSelected, false);
    995 
    996         var emulatedSettingChangedMuted = false;
    997         WebInspector.overridesSupport.settings.deviceWidth.addChangeListener(emulatedSettingChanged);
    998         WebInspector.overridesSupport.settings.deviceHeight.addChangeListener(emulatedSettingChanged);
    999         WebInspector.overridesSupport.settings.deviceScaleFactor.addChangeListener(emulatedSettingChanged);
   1000         WebInspector.overridesSupport.settings.deviceTextAutosizing.addChangeListener(emulatedSettingChanged);
   1001         WebInspector.overridesSupport.settings.emulateViewport.addChangeListener(emulatedSettingChanged);
   1002         WebInspector.overridesSupport.settings.emulateTouch.addChangeListener(emulatedSettingChanged);
   1003         WebInspector.overridesSupport.settings.userAgent.addChangeListener(emulatedSettingChanged);
   1004         emulatedSettingChanged();
   1005 
   1006         function deviceSelected()
   1007         {
   1008             if (deviceSelectElement.selectedIndex === 0)
   1009                 return;
   1010 
   1011             var option = deviceSelectElement.options[deviceSelectElement.selectedIndex];
   1012             emulatedSettingChangedMuted = true;
   1013             WebInspector.overridesSupport.emulateDevice(option.device);
   1014             emulatedSettingChangedMuted = false;
   1015         }
   1016 
   1017         function emulatedSettingChanged()
   1018         {
   1019             if (emulatedSettingChangedMuted)
   1020                 return;
   1021 
   1022             var index = 0;
   1023             for (var i = 1; i < deviceSelectElement.options.length; ++i) {
   1024                 var option = deviceSelectElement.options[i];
   1025                 if (WebInspector.overridesSupport.isEmulatingDevice(option.device)) {
   1026                     index = i;
   1027                     break;
   1028                 }
   1029             }
   1030             deviceSelectElement.selectedIndex = index;
   1031         }
   1032 
   1033         return deviceSelectElement;
   1034     },
   1035 
   1036     /**
   1037      * @param {!Document} document
   1038      * @return {!Element}
   1039      */
   1040     createNetworkThroughputSelect: function(document)
   1041     {
   1042         var throughputSetting = WebInspector.overridesSupport.settings.networkConditionsThroughput;
   1043         var throughputSelectElement = document.createElement("select");
   1044         var presets = WebInspector.OverridesSupport._networkThroughputPresets;
   1045         for (var i = 0; i < presets.length; ++i)
   1046             throughputSelectElement.add(new Option(presets[i][0], presets[i][1]));
   1047 
   1048         settingChanged();
   1049         throughputSetting.addChangeListener(settingChanged);
   1050         throughputSelectElement.addEventListener("change", throughputSelected, false);
   1051 
   1052         function throughputSelected()
   1053         {
   1054             var value = Number(throughputSelectElement.options[throughputSelectElement.selectedIndex].value);
   1055             throughputSetting.removeChangeListener(settingChanged);
   1056             throughputSetting.set(value);
   1057             throughputSetting.addChangeListener(settingChanged);
   1058         }
   1059 
   1060         function settingChanged()
   1061         {
   1062             var value = String(throughputSetting.get());
   1063             var options = throughputSelectElement.options;
   1064             var selectionRestored = false;
   1065             for (var i = 0; i < options.length; ++i) {
   1066                 if (options[i].value === value) {
   1067                     throughputSelectElement.selectedIndex = i;
   1068                     selectionRestored = true;
   1069                     break;
   1070                 }
   1071             }
   1072             if (!selectionRestored)
   1073                 throughputSelectElement.selectedIndex = options.length - 1;
   1074         }
   1075 
   1076         return throughputSelectElement;
   1077     },
   1078 
   1079     reveal: function()
   1080     {
   1081         WebInspector.Revealer.reveal(this);
   1082     },
   1083 
   1084     __proto__: WebInspector.Object.prototype
   1085 }
   1086 
   1087 
   1088 /**
   1089  * @type {!WebInspector.OverridesSupport}
   1090  */
   1091 WebInspector.overridesSupport;
   1092