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