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