Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / 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  * @extends {WebInspector.Object}
34  */
35 WebInspector.OverridesSupport = function()
36 {
37     WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.MainFrameNavigated, this._onMainFrameNavigated.bind(this), this);
38     this._deviceMetricsOverrideEnabled = false;
39     this._emulateViewportEnabled = false;
40     this._userAgent = "";
41     this.maybeHasActiveOverridesChanged();
42
43     WebInspector.settings.overrideUserAgent.addChangeListener(this._userAgentChanged, this);
44     WebInspector.settings.userAgent.addChangeListener(this._userAgentChanged, this);
45
46     WebInspector.settings.overrideDeviceMetrics.addChangeListener(this._deviceMetricsChanged, this);
47     WebInspector.settings.deviceMetrics.addChangeListener(this._deviceMetricsChanged, this);
48     WebInspector.settings.emulateViewport.addChangeListener(this._deviceMetricsChanged, this);
49     WebInspector.settings.deviceFitWindow.addChangeListener(this._deviceMetricsChanged, this);
50
51     WebInspector.settings.overrideGeolocation.addChangeListener(this._geolocationPositionChanged, this);
52     WebInspector.settings.geolocationOverride.addChangeListener(this._geolocationPositionChanged, this);
53
54     WebInspector.settings.overrideDeviceOrientation.addChangeListener(this._deviceOrientationChanged, this);
55     WebInspector.settings.deviceOrientationOverride.addChangeListener(this._deviceOrientationChanged, this);
56
57     WebInspector.settings.emulateTouchEvents.addChangeListener(this._emulateTouchEventsChanged, this);
58
59     WebInspector.settings.overrideCSSMedia.addChangeListener(this._cssMediaChanged, this);
60     WebInspector.settings.emulatedCSSMedia.addChangeListener(this._cssMediaChanged, this);
61 }
62
63 WebInspector.OverridesSupport.Events = {
64     OverridesWarningUpdated: "OverridesWarningUpdated",
65     HasActiveOverridesChanged: "HasActiveOverridesChanged",
66 }
67
68 /**
69  * @constructor
70  * @param {number} width
71  * @param {number} height
72  * @param {number} deviceScaleFactor
73  * @param {boolean} textAutosizing
74  */
75 WebInspector.OverridesSupport.DeviceMetrics = function(width, height, deviceScaleFactor, textAutosizing)
76 {
77     this.width = width;
78     this.height = height;
79     this.deviceScaleFactor = deviceScaleFactor;
80     this.textAutosizing = textAutosizing;
81 }
82
83 /**
84  * @return {!WebInspector.OverridesSupport.DeviceMetrics}
85  */
86 WebInspector.OverridesSupport.DeviceMetrics.parseSetting = function(value)
87 {
88     var width = 0;
89     var height = 0;
90     var deviceScaleFactor = 1;
91     var textAutosizing = true;
92     if (value) {
93         var splitMetrics = value.split("x");
94         if (splitMetrics.length >= 3) {
95             width = parseInt(splitMetrics[0], 10);
96             height = parseInt(splitMetrics[1], 10);
97             deviceScaleFactor = parseFloat(splitMetrics[2]);
98             if (splitMetrics.length == 4)
99                 textAutosizing = splitMetrics[3] == 1;
100         }
101     }
102     return new WebInspector.OverridesSupport.DeviceMetrics(width, height, deviceScaleFactor, textAutosizing);
103 }
104
105 /**
106  * @return {?WebInspector.OverridesSupport.DeviceMetrics}
107  */
108 WebInspector.OverridesSupport.DeviceMetrics.parseUserInput = function(widthString, heightString, deviceScaleFactorString, textAutosizing)
109 {
110     function isUserInputValid(value, isInteger)
111     {
112         if (!value)
113             return true;
114         return isInteger ? /^[0]*[1-9][\d]*$/.test(value) : /^[0]*([1-9][\d]*(\.\d+)?|\.\d+)$/.test(value);
115     }
116
117     if (!widthString ^ !heightString)
118         return null;
119
120     var isWidthValid = isUserInputValid(widthString, true);
121     var isHeightValid = isUserInputValid(heightString, true);
122     var isDeviceScaleFactorValid = isUserInputValid(deviceScaleFactorString, false);
123
124     if (!isWidthValid && !isHeightValid && !isDeviceScaleFactorValid)
125         return null;
126
127     var width = isWidthValid ? parseInt(widthString || "0", 10) : -1;
128     var height = isHeightValid ? parseInt(heightString || "0", 10) : -1;
129     var deviceScaleFactor = isDeviceScaleFactorValid ? parseFloat(deviceScaleFactorString) : -1;
130
131     return new WebInspector.OverridesSupport.DeviceMetrics(width, height, deviceScaleFactor, textAutosizing);
132 }
133
134 WebInspector.OverridesSupport.DeviceMetrics.prototype = {
135     /**
136      * @return {boolean}
137      */
138     isValid: function()
139     {
140         return this.isWidthValid() && this.isHeightValid() && this.isDeviceScaleFactorValid();
141     },
142
143     /**
144      * @return {boolean}
145      */
146     isWidthValid: function()
147     {
148         return this.width >= 0;
149     },
150
151     /**
152      * @return {boolean}
153      */
154     isHeightValid: function()
155     {
156         return this.height >= 0;
157     },
158
159     /**
160      * @return {boolean}
161      */
162     isDeviceScaleFactorValid: function()
163     {
164         return this.deviceScaleFactor > 0;
165     },
166
167     /**
168      * @return {string}
169      */
170     toSetting: function()
171     {
172         if (!this.isValid())
173             return "";
174
175         return this.width && this.height ? this.width + "x" + this.height + "x" + this.deviceScaleFactor + "x" + (this.textAutosizing ? "1" : "0") : "";
176     },
177
178     /**
179      * @return {string}
180      */
181     widthToInput: function()
182     {
183         return this.isWidthValid() && this.width ? String(this.width) : "";
184     },
185
186     /**
187      * @return {string}
188      */
189     heightToInput: function()
190     {
191         return this.isHeightValid() && this.height ? String(this.height) : "";
192     },
193
194     /**
195      * @return {string}
196      */
197     deviceScaleFactorToInput: function()
198     {
199         return this.isDeviceScaleFactorValid() && this.deviceScaleFactor ? String(this.deviceScaleFactor) : "";
200     },
201
202     /**
203      * Compute the font scale factor.
204      *
205      * Chromium on Android uses a device scale adjustment for fonts used in text autosizing for
206      * improved legibility. This function computes this adjusted value for text autosizing.
207      *
208      * For a description of the Android device scale adjustment algorithm, see:
209      *     chrome/browser/chrome_content_browser_client.cc, GetFontScaleMultiplier(...)
210      *
211      * @return {number} font scale factor.
212      */
213     fontScaleFactor: function()
214     {
215         if (this.isValid()) {
216             var minWidth = Math.min(this.width, this.height) / this.deviceScaleFactor;
217
218             var kMinFSM = 1.05;
219             var kWidthForMinFSM = 320;
220             var kMaxFSM = 1.3;
221             var kWidthForMaxFSM = 800;
222
223             if (minWidth <= kWidthForMinFSM)
224                 return kMinFSM;
225             if (minWidth >= kWidthForMaxFSM)
226                 return kMaxFSM;
227
228             // The font scale multiplier varies linearly between kMinFSM and kMaxFSM.
229             var ratio = (minWidth - kWidthForMinFSM) / (kWidthForMaxFSM - kWidthForMinFSM);
230
231             return ratio * (kMaxFSM - kMinFSM) + kMinFSM;
232         }
233
234         return 1;
235     }
236 }
237
238 /**
239  * @constructor
240  * @param {number} latitude
241  * @param {number} longitude
242  */
243 WebInspector.OverridesSupport.GeolocationPosition = function(latitude, longitude, error)
244 {
245     this.latitude = latitude;
246     this.longitude = longitude;
247     this.error = error;
248 }
249
250 WebInspector.OverridesSupport.GeolocationPosition.prototype = {
251     /**
252      * @return {string}
253      */
254     toSetting: function()
255     {
256         return (typeof this.latitude === "number" && typeof this.longitude === "number" && typeof this.error === "string") ? this.latitude + "@" + this.longitude + ":" + this.error : "";
257     }
258 }
259
260 /**
261  * @return {!WebInspector.OverridesSupport.GeolocationPosition}
262  */
263 WebInspector.OverridesSupport.GeolocationPosition.parseSetting = function(value)
264 {
265     if (value) {
266         var splitError = value.split(":");
267         if (splitError.length === 2) {
268             var splitPosition = splitError[0].split("@")
269             if (splitPosition.length === 2)
270                 return new WebInspector.OverridesSupport.GeolocationPosition(parseFloat(splitPosition[0]), parseFloat(splitPosition[1]), splitError[1]);
271         }
272     }
273     return new WebInspector.OverridesSupport.GeolocationPosition(0, 0, "");
274 }
275
276 /**
277  * @return {?WebInspector.OverridesSupport.GeolocationPosition}
278  */
279 WebInspector.OverridesSupport.GeolocationPosition.parseUserInput = function(latitudeString, longitudeString, errorStatus)
280 {
281     function isUserInputValid(value)
282     {
283         if (!value)
284             return true;
285         return /^[-]?[0-9]*[.]?[0-9]*$/.test(value);
286     }
287
288     if (!latitudeString ^ !latitudeString)
289         return null;
290
291     var isLatitudeValid = isUserInputValid(latitudeString);
292     var isLongitudeValid = isUserInputValid(longitudeString);
293
294     if (!isLatitudeValid && !isLongitudeValid)
295         return null;
296
297     var latitude = isLatitudeValid ? parseFloat(latitudeString) : -1;
298     var longitude = isLongitudeValid ? parseFloat(longitudeString) : -1;
299
300     return new WebInspector.OverridesSupport.GeolocationPosition(latitude, longitude, errorStatus ? "PositionUnavailable" : "");
301 }
302
303 WebInspector.OverridesSupport.GeolocationPosition.clearGeolocationOverride = function()
304 {
305     GeolocationAgent.clearGeolocationOverride();
306 }
307
308 /**
309  * @constructor
310  * @param {number} alpha
311  * @param {number} beta
312  * @param {number} gamma
313  */
314 WebInspector.OverridesSupport.DeviceOrientation = function(alpha, beta, gamma)
315 {
316     this.alpha = alpha;
317     this.beta = beta;
318     this.gamma = gamma;
319 }
320
321 WebInspector.OverridesSupport.DeviceOrientation.prototype = {
322     /**
323      * @return {string}
324      */
325     toSetting: function()
326     {
327         return JSON.stringify(this);
328     }
329 }
330
331 /**
332  * @return {!WebInspector.OverridesSupport.DeviceOrientation}
333  */
334 WebInspector.OverridesSupport.DeviceOrientation.parseSetting = function(value)
335 {
336     if (value) {
337         var jsonObject = JSON.parse(value);
338         return new WebInspector.OverridesSupport.DeviceOrientation(jsonObject.alpha, jsonObject.beta, jsonObject.gamma);
339     }
340     return new WebInspector.OverridesSupport.DeviceOrientation(0, 0, 0);
341 }
342
343 /**
344  * @return {?WebInspector.OverridesSupport.DeviceOrientation}
345  */
346 WebInspector.OverridesSupport.DeviceOrientation.parseUserInput = function(alphaString, betaString, gammaString)
347 {
348     function isUserInputValid(value)
349     {
350         if (!value)
351             return true;
352         return /^[-]?[0-9]*[.]?[0-9]*$/.test(value);
353     }
354
355     if (!alphaString ^ !betaString ^ !gammaString)
356         return null;
357
358     var isAlphaValid = isUserInputValid(alphaString);
359     var isBetaValid = isUserInputValid(betaString);
360     var isGammaValid = isUserInputValid(gammaString);
361
362     if (!isAlphaValid && !isBetaValid && !isGammaValid)
363         return null;
364
365     var alpha = isAlphaValid ? parseFloat(alphaString) : -1;
366     var beta = isBetaValid ? parseFloat(betaString) : -1;
367     var gamma = isGammaValid ? parseFloat(gammaString) : -1;
368
369     return new WebInspector.OverridesSupport.DeviceOrientation(alpha, beta, gamma);
370 }
371
372 WebInspector.OverridesSupport.DeviceOrientation.clearDeviceOrientationOverride = function()
373 {
374     PageAgent.clearDeviceOrientationOverride();
375 }
376
377 WebInspector.OverridesSupport.prototype = {
378     /**
379      * @param {string} deviceMetrics
380      * @param {string} userAgent
381      */
382     emulateDevice: function(deviceMetrics, userAgent)
383     {
384         this._deviceMetricsChangedListenerMuted = true;
385         this._userAgentChangedListenerMuted = true;
386         WebInspector.settings.deviceMetrics.set(deviceMetrics);
387         WebInspector.settings.userAgent.set(userAgent);
388         WebInspector.settings.overrideDeviceMetrics.set(true);
389         WebInspector.settings.overrideUserAgent.set(true);
390         WebInspector.settings.emulateTouchEvents.set(true);
391         WebInspector.settings.emulateViewport.set(true);
392         delete this._deviceMetricsChangedListenerMuted;
393         delete this._userAgentChangedListenerMuted;
394         this._deviceMetricsChanged();
395         this._userAgentChanged();
396     },
397
398     reset: function()
399     {
400         this._deviceMetricsChangedListenerMuted = true;
401         this._userAgentChangedListenerMuted = true;
402         WebInspector.settings.overrideDeviceMetrics.set(false);
403         WebInspector.settings.overrideUserAgent.set(false);
404         WebInspector.settings.emulateTouchEvents.set(false);
405         WebInspector.settings.overrideDeviceOrientation.set(false);
406         WebInspector.settings.overrideGeolocation.set(false);
407         WebInspector.settings.overrideCSSMedia.set(false);
408         WebInspector.settings.emulateViewport.set(false);
409         WebInspector.settings.deviceMetrics.set("");
410         delete this._deviceMetricsChangedListenerMuted;
411         delete this._userAgentChangedListenerMuted;
412         this._deviceMetricsChanged();
413         this._userAgentChanged();
414     },
415
416     applyInitialOverrides: function()
417     {
418         this._deviceMetricsChangedListenerMuted = true;
419         this._userAgentChangedListenerMuted = true;
420         this._userAgentChanged();
421         this._deviceMetricsChanged();
422         this._deviceOrientationChanged();
423         this._geolocationPositionChanged();
424         this._emulateTouchEventsChanged();
425         this._cssMediaChanged();
426         delete this._deviceMetricsChangedListenerMuted;
427         delete this._userAgentChangedListenerMuted;
428         this._deviceMetricsChanged();
429         this._userAgentChanged();
430     },
431
432     _userAgentChanged: function()
433     {
434         if (WebInspector.isInspectingDevice() || this._userAgentChangedListenerMuted)
435             return;
436         var userAgent = WebInspector.settings.overrideUserAgent.get() ? WebInspector.settings.userAgent.get() : "";
437         NetworkAgent.setUserAgentOverride(userAgent);
438         this._updateUserAgentWarningMessage(this._userAgent !== userAgent ? WebInspector.UIString("You might need to reload the page for proper user agent spoofing and viewport rendering.") : "");
439         this._userAgent = userAgent;
440         this.maybeHasActiveOverridesChanged();
441     },
442
443     _deviceMetricsChanged: function()
444     {
445         if (this._deviceMetricsChangedListenerMuted)
446             return;
447         var metrics = WebInspector.OverridesSupport.DeviceMetrics.parseSetting(WebInspector.settings.overrideDeviceMetrics.get() ? WebInspector.settings.deviceMetrics.get() : "");
448         if (!metrics.isValid())
449             return;
450
451         var dipWidth = Math.round(metrics.width / metrics.deviceScaleFactor);
452         var dipHeight = Math.round(metrics.height / metrics.deviceScaleFactor);
453
454         // Disable override without checks.
455         if (dipWidth && dipHeight && WebInspector.isInspectingDevice()) {
456             this._updateDeviceMetricsWarningMessage(WebInspector.UIString("Screen emulation on the device is not available."));
457             return;
458         }
459
460         PageAgent.setDeviceMetricsOverride(dipWidth, dipHeight, metrics.deviceScaleFactor, WebInspector.settings.emulateViewport.get(), WebInspector.settings.deviceFitWindow.get(), metrics.textAutosizing, metrics.fontScaleFactor(), apiCallback.bind(this));
461         this.maybeHasActiveOverridesChanged();
462
463         /**
464          * @param {?Protocol.Error} error
465          * @this {WebInspector.OverridesSupport}
466          */
467         function apiCallback(error)
468         {
469             if (error) {
470                 this._updateDeviceMetricsWarningMessage(WebInspector.UIString("Screen emulation is not available on this page."));
471                 return;
472             }
473
474             var metricsOverrideEnabled = !!(dipWidth && dipHeight);
475             var viewportEnabled =  WebInspector.settings.emulateViewport.get();
476             this._updateDeviceMetricsWarningMessage(this._deviceMetricsOverrideEnabled !== metricsOverrideEnabled || (metricsOverrideEnabled && this._emulateViewportEnabled != viewportEnabled) ?
477                 WebInspector.UIString("You might need to reload the page for proper user agent spoofing and viewport rendering.") : "");
478             this._deviceMetricsOverrideEnabled = metricsOverrideEnabled;
479             this._emulateViewportEnabled = viewportEnabled;
480             this._deviceMetricsOverrideAppliedForTest();
481         }
482     },
483
484     _deviceMetricsOverrideAppliedForTest: function()
485     {
486         // Used for sniffing in tests.
487     },
488
489     _geolocationPositionChanged: function()
490     {
491         if (!WebInspector.settings.overrideGeolocation.get()) {
492             GeolocationAgent.clearGeolocationOverride();
493             return;
494         }
495         var geolocation = WebInspector.OverridesSupport.GeolocationPosition.parseSetting(WebInspector.settings.geolocationOverride.get());
496         if (geolocation.error)
497             GeolocationAgent.setGeolocationOverride();
498         else
499             GeolocationAgent.setGeolocationOverride(geolocation.latitude, geolocation.longitude, 150);
500         this.maybeHasActiveOverridesChanged();
501     },
502
503     _deviceOrientationChanged: function()
504     {
505         if (!WebInspector.settings.overrideDeviceOrientation.get()) {
506             PageAgent.clearDeviceOrientationOverride();
507             return;
508         }
509         if (WebInspector.isInspectingDevice())
510             return;
511
512         var deviceOrientation = WebInspector.OverridesSupport.DeviceOrientation.parseSetting(WebInspector.settings.deviceOrientationOverride.get());
513         PageAgent.setDeviceOrientationOverride(deviceOrientation.alpha, deviceOrientation.beta, deviceOrientation.gamma);
514         this.maybeHasActiveOverridesChanged();
515     },
516
517     _emulateTouchEventsChanged: function()
518     {
519         if (WebInspector.isInspectingDevice() && WebInspector.settings.emulateTouchEvents.get())
520             return;
521
522         WebInspector.domAgent.emulateTouchEventObjects(WebInspector.settings.emulateTouchEvents.get());
523         this.maybeHasActiveOverridesChanged();
524     },
525
526     _cssMediaChanged: function()
527     {
528         PageAgent.setEmulatedMedia(WebInspector.settings.overrideCSSMedia.get() ? WebInspector.settings.emulatedCSSMedia.get() : "");
529         WebInspector.cssModel.mediaQueryResultChanged();
530         this.maybeHasActiveOverridesChanged();
531     },
532
533     /**
534      * @return {boolean}
535      */
536     hasActiveOverrides: function()
537     {
538         return this._hasActiveOverrides;
539     },
540
541     maybeHasActiveOverridesChanged: function()
542     {
543         var hasActiveOverrides = WebInspector.settings.overrideUserAgent.get() || WebInspector.settings.overrideDeviceMetrics.get() ||
544             WebInspector.settings.overrideGeolocation.get() || WebInspector.settings.overrideDeviceOrientation.get() ||
545             WebInspector.settings.emulateTouchEvents.get() || WebInspector.settings.overrideCSSMedia.get();
546         if (this._hasActiveOverrides !== hasActiveOverrides) {
547             this._hasActiveOverrides = hasActiveOverrides;
548             this.dispatchEventToListeners(WebInspector.OverridesSupport.Events.HasActiveOverridesChanged);
549         }
550     },
551
552     _onMainFrameNavigated: function()
553     {
554         this._deviceMetricsChanged();
555         this._updateUserAgentWarningMessage("");
556     },
557
558     /**
559      * @param {string} warningMessage
560      */
561     _updateDeviceMetricsWarningMessage: function(warningMessage)
562     {
563         this._deviceMetricsWarningMessage = warningMessage;
564         this.dispatchEventToListeners(WebInspector.OverridesSupport.Events.OverridesWarningUpdated);
565     },
566
567     /**
568      * @param {string} warningMessage
569      */
570     _updateUserAgentWarningMessage: function(warningMessage)
571     {
572         this._userAgentWarningMessage = warningMessage;
573         this.dispatchEventToListeners(WebInspector.OverridesSupport.Events.OverridesWarningUpdated);
574     },
575
576     /**
577      * @return {string}
578      */
579     warningMessage: function()
580     {
581         return this._deviceMetricsWarningMessage || this._userAgentWarningMessage || "";
582     },
583
584     __proto__: WebInspector.Object.prototype
585 }
586
587
588 /**
589  * @type {!WebInspector.OverridesSupport}
590  */
591 WebInspector.overridesSupport;