Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / sdk / OverridesSupport.js
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;