Upstream version 7.35.144.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / OverridesView.js
1 /*
2  * Copyright (C) 2014 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.VBox}
34  */
35 WebInspector.OverridesView = function()
36 {
37     WebInspector.VBox.call(this);
38     this.registerRequiredCSS("overrides.css");
39     this.registerRequiredCSS("helpScreen.css");
40     this.element.classList.add("overrides-view");
41
42     this._tabbedPane = new WebInspector.TabbedPane();
43     this._tabbedPane.shrinkableTabs = false;
44     this._tabbedPane.verticalTabLayout = true;
45
46     new WebInspector.OverridesView.DeviceTab().appendAsTab(this._tabbedPane);
47     new WebInspector.OverridesView.ViewportTab().appendAsTab(this._tabbedPane);
48     new WebInspector.OverridesView.UserAgentTab().appendAsTab(this._tabbedPane);
49     new WebInspector.OverridesView.SensorsTab().appendAsTab(this._tabbedPane);
50
51     this._lastSelectedTabSetting = WebInspector.settings.createSetting("lastSelectedEmulateTab", "device");
52     this._tabbedPane.selectTab(this._lastSelectedTabSetting.get());
53     this._tabbedPane.addEventListener(WebInspector.TabbedPane.EventTypes.TabSelected, this._tabSelected, this);
54     this._tabbedPane.show(this.element);
55
56     this._warningFooter = this.element.createChild("div", "overrides-footer");
57     this._overridesWarningUpdated();
58     WebInspector.overridesSupport.addEventListener(WebInspector.OverridesSupport.Events.OverridesWarningUpdated, this._overridesWarningUpdated, this);
59 }
60
61 WebInspector.OverridesView.prototype = {
62     /**
63      * @param {!WebInspector.Event} event
64      */
65     _tabSelected: function(event)
66     {
67         this._lastSelectedTabSetting.set(this._tabbedPane.selectedTabId);
68     },
69
70     _overridesWarningUpdated: function()
71     {
72         var message = WebInspector.overridesSupport.warningMessage();
73         this._warningFooter.classList.toggle("hidden", !message);
74         this._warningFooter.textContent = message;
75     },
76
77     __proto__: WebInspector.VBox.prototype
78 }
79
80 /**
81  * @constructor
82  * @extends {WebInspector.VBox}
83  * @param {string} id
84  * @param {string} name
85  * @param {!Array.<!WebInspector.Setting>} settings
86  */
87 WebInspector.OverridesView.Tab = function(id, name, settings)
88 {
89     WebInspector.VBox.call(this);
90     this._id = id;
91     this._name = name;
92     this._settings = settings;
93     for (var i = 0; i < settings.length; ++i)
94         settings[i].addChangeListener(this._updateActiveState, this);
95 }
96
97 WebInspector.OverridesView.Tab.prototype = {
98     /**
99      * @param {!WebInspector.TabbedPane} tabbedPane
100      */
101     appendAsTab: function(tabbedPane)
102     {
103         this._tabbedPane = tabbedPane;
104         tabbedPane.appendTab(this._id, this._name, this);
105         this._updateActiveState();
106     },
107
108     _updateActiveState: function()
109     {
110         var active = false;
111         for (var i = 0; !active && i < this._settings.length; ++i)
112             active = this._settings[i].get();
113         this._tabbedPane.element.classList.toggle("overrides-activate-" + this._id, active);
114         this._tabbedPane.changeTabTitle(this._id, active ? this._name + " \u2713" : this._name);
115     },
116
117     /**
118      * Creates an input element under the parentElement with the given id and defaultText.
119      * It also sets an onblur event listener.
120      * @param {!Element} parentElement
121      * @param {string} id
122      * @param {string} defaultText
123      * @param {function(*)} eventListener
124      * @param {boolean=} numeric
125      * @return {!Element} element
126      */
127     _createInput: function(parentElement, id, defaultText, eventListener, numeric)
128     {
129         var element = parentElement.createChild("input");
130         element.id = id;
131         element.type = "text";
132         element.maxLength = 12;
133         element.style.width = "80px";
134         element.value = defaultText;
135         element.align = "right";
136         if (numeric)
137             element.className = "numeric";
138         element.addEventListener("input", eventListener, false);
139         element.addEventListener("keydown", keyDownListener, false);
140         function keyDownListener(event)
141         {
142             if (isEnterKey(event))
143                 eventListener(event);
144         }
145         return element;
146     },
147
148     /**
149      * @param {string} title
150      * @param {function(boolean)} callback
151      */
152     _createNonPersistedCheckbox: function(title, callback)
153     {
154         var labelElement = document.createElement("label");
155         var checkboxElement = labelElement.createChild("input");
156         checkboxElement.type = "checkbox";
157         checkboxElement.checked = false;
158         checkboxElement.addEventListener("click", onclick, false);
159         labelElement.appendChild(document.createTextNode(title));
160         return labelElement;
161
162         function onclick()
163         {
164             callback(checkboxElement.checked);
165         }
166     },
167
168     /**
169      * @param {string} name
170      * @param {!WebInspector.Setting} setting
171      * @param {function(boolean)=} callback
172      */
173     _createSettingCheckbox: function(name, setting, callback)
174     {
175         var checkbox = WebInspector.SettingsUI.createCheckbox(name, setting.get.bind(setting), listener, true);
176
177         function listener(value)
178         {
179             if (setting.get() === value)
180                 return;
181
182             setting.set(value);
183             if (callback)
184                 callback(value);
185         }
186
187         setting.addChangeListener(changeListener);
188
189         function changeListener()
190         {
191             if (checkbox.firstChild.checked !== setting.get())
192                 checkbox.firstChild.checked = setting.get();
193         }
194         return checkbox;
195     },
196
197     __proto__: WebInspector.VBox.prototype
198 }
199
200 /**
201  * @constructor
202  * @extends {WebInspector.OverridesView.Tab}
203  */
204 WebInspector.OverridesView.DeviceTab = function()
205 {
206     WebInspector.OverridesView.Tab.call(this, "device", WebInspector.UIString("Device"), []);
207     this.element.classList.add("overrides-device");
208
209     this._emulatedDeviceSetting = WebInspector.settings.createSetting("emulatedDevice", "Google Nexus 4");
210     this._emulateDeviceViewportSetting = WebInspector.settings.overrideDeviceMetrics;
211     this._emulateDeviceUserAgentSetting = WebInspector.settings.overrideUserAgent;
212
213     this._deviceSelectElement = this.element.createChild("select");
214
215     var devices = WebInspector.OverridesView.DeviceTab._phones.concat(WebInspector.OverridesView.DeviceTab._tablets);
216     devices.sort();
217     var selectionRestored = false;
218     for (var i = 0; i < devices.length; ++i) {
219         var device = devices[i];
220         var option = new Option(device[0], device[0]);
221         option._userAgent = device[1];
222         option._metrics = device[2];
223         this._deviceSelectElement.add(option);
224         if (this._emulatedDeviceSetting.get() === device[0]) {
225             this._deviceSelectElement.selectedIndex = i;
226             selectionRestored = true;
227         }
228     }
229
230     if (!selectionRestored)
231         this._deviceSelectElement.selectedIndex = devices.length - 1;
232
233     this._deviceSelectElement.addEventListener("change", this._deviceSelected.bind(this), false);
234     this._deviceSelectElement.addEventListener("keypress", this._keyPressed.bind(this), false);
235     this._deviceSelectElement.disabled = WebInspector.OverridesSupport.isInspectingDevice();
236
237     var buttonsBar = this.element.createChild("div");
238     var emulateButton = buttonsBar.createChild("button", "settings-tab-text-button");
239     emulateButton.textContent = WebInspector.UIString("Emulate");
240     emulateButton.addEventListener("click", this._emulateButtonClicked.bind(this), false);
241     emulateButton.disabled = WebInspector.OverridesSupport.isInspectingDevice();
242
243     var resetButton = buttonsBar.createChild("button", "settings-tab-text-button");
244     resetButton.textContent = WebInspector.UIString("Reset");
245     resetButton.addEventListener("click", this._resetButtonClicked.bind(this), false);
246     this._resetButton = resetButton;
247
248     this._viewportValueLabel = this.element.createChild("div", "overrides-device-value-label");
249     this._viewportValueLabel.textContent = WebInspector.UIString("Viewport:");
250     this._viewportValueElement = this._viewportValueLabel.createChild("span", "overrides-device-value");
251
252     this._userAgentLabel = this.element.createChild("div", "overrides-device-value-label");
253     this._userAgentLabel.textContent = WebInspector.UIString("User agent:");
254     this._userAgentValueElement = this._userAgentLabel.createChild("span", "overrides-device-value");
255
256     this._updateValueLabels();
257     WebInspector.overridesSupport.addEventListener(WebInspector.OverridesSupport.Events.HasActiveOverridesChanged, this._hasActiveOverridesChanged, this);
258     this._hasActiveOverridesChanged();
259 }
260
261 // Third element lists device metrics separated by 'x':
262 // - screen width,
263 // - screen height,
264 // - device scale factor,
265 // - use text autosizing.
266 WebInspector.OverridesView.DeviceTab._phones = [
267     ["Apple iPhone 3GS",
268      "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",
269      "320x480x1"],
270     ["Apple iPhone 4",
271      "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",
272      "640x960x2"],
273     ["Apple iPhone 5",
274      "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",
275      "640x1136x2"],
276     ["BlackBerry Z10",
277      "Mozilla/5.0 (BB10; Touch) AppleWebKit/537.10+ (KHTML, like Gecko) Version/10.0.9.2372 Mobile Safari/537.10+",
278      "768x1280x2"],
279     ["BlackBerry Z30",
280      "Mozilla/5.0 (BB10; Touch) AppleWebKit/537.10+ (KHTML, like Gecko) Version/10.0.9.2372 Mobile Safari/537.10+",
281      "720x1280x2"],
282     ["Google Nexus 4",
283      "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",
284      "768x1280x2"],
285     ["Google Nexus 5",
286      "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",
287      "1080x1920x3"],
288     ["Google Nexus S",
289      "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",
290      "480x800x1.5"],
291     ["HTC Evo, Touch HD, Desire HD, Desire",
292      "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",
293      "480x800x1.5"],
294     ["HTC One X, EVO LTE",
295      "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",
296      "720x1280x2"],
297     ["HTC Sensation, Evo 3D",
298      "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",
299      "540x960x1.5"],
300     ["LG Optimus 2X, Optimus 3D, Optimus Black",
301      "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",
302      "480x800x1.5"],
303     ["LG Optimus G",
304      "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",
305      "768x1280x2"],
306     ["LG Optimus LTE, Optimus 4X HD",
307      "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",
308      "720x1280x1.7"],
309     ["LG Optimus One",
310      "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",
311      "320x480x1.5"],
312     ["Motorola Defy, Droid, Droid X, Milestone",
313      "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",
314      "480x854x1.5"],
315     ["Motorola Droid 3, Droid 4, Droid Razr, Atrix 4G, Atrix 2",
316      "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",
317      "540x960x1"],
318     ["Motorola Droid Razr HD",
319      "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",
320      "720x1280x1"],
321     ["Nokia C5, C6, C7, N97, N8, X7",
322      "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",
323      "360x640x1"],
324     ["Nokia Lumia 7X0, Lumia 8XX, Lumia 900, N800, N810, N900",
325      "Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; ARM; Touch; NOKIA; Lumia 820)",
326      "480x800x1.5"],
327     ["Samsung Galaxy Note 3",
328      "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",
329      "1080x1920x2"],
330     ["Samsung Galaxy Note II",
331      "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",
332      "720x1280x2"],
333     ["Samsung Galaxy Note",
334      "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",
335      "800x1280x2"],
336     ["Samsung Galaxy S III, Galaxy Nexus",
337      "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",
338      "720x1280x2"],
339     ["Samsung Galaxy S, S II, W",
340      "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",
341      "480x800x1.5"],
342     ["Samsung Galaxy S4",
343      "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",
344      "1080x1920x3"],
345     ["Sony Xperia S, Ion",
346      "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",
347      "720x1280x2"],
348     ["Sony Xperia Sola, U",
349      "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",
350      "480x854x1"],
351     ["Sony Xperia Z, Z1",
352      "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",
353      "1080x1920x3"],
354 ];
355
356 WebInspector.OverridesView.DeviceTab._tablets = [
357     ["Amazon Amazon Kindle Fire HD 7\u2033",
358      "Mozilla/5.0 (Linux; U; Android 2.3.4; en-us; Kindle Fire HD Build/GINGERBREAD) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
359      "1280x800x1.5"],
360     ["Amazon Amazon Kindle Fire HD 8.9\u2033",
361      "Mozilla/5.0 (Linux; U; Android 2.3.4; en-us; Kindle Fire HD Build/GINGERBREAD) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
362      "1920x1200x1.5"],
363     ["Amazon Amazon Kindle Fire",
364      "Mozilla/5.0 (Linux; U; Android 2.3.4; en-us; Kindle Fire Build/GINGERBREAD) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
365      "1024x600x1"],
366     ["Apple iPad 1 / 2 / iPad Mini",
367      "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",
368      "1024x768x1"],
369     ["Apple iPad 3 / 4",
370      "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",
371      "2048x1536x2"],
372     ["BlackBerry PlayBook",
373      "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+",
374      "1024x600x1"],
375     ["Google Nexus 10",
376      "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",
377      "2560x1600x2"],
378     ["Google Nexus 7 2",
379      "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",
380      "1920x1200x2"],
381     ["Google Nexus 7",
382      "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",
383      "1280x800x1.325"],
384     ["Motorola Xoom, Xyboard",
385      "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",
386      "1280x800x1"],
387     ["Samsung Galaxy Tab 7.7, 8.9, 10.1",
388      "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",
389      "1280x800x1"],
390     ["Samsung Galaxy Tab",
391      "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",
392      "1024x600x1"],
393 ];
394
395 WebInspector.OverridesView.DeviceTab.prototype = {
396     /**
397      * @param {!Event} e
398      */
399     _keyPressed: function(e)
400     {
401         if (e.keyCode === WebInspector.KeyboardShortcut.Keys.Enter.code)
402             this._emulateButtonClicked();
403     },
404
405     _emulateButtonClicked: function()
406     {
407         var option = this._deviceSelectElement.options[this._deviceSelectElement.selectedIndex];
408         WebInspector.overridesSupport.emulateDevice(option._metrics, option._userAgent);
409     },
410
411     _resetButtonClicked: function()
412     {
413         WebInspector.overridesSupport.reset();
414     },
415
416     _hasActiveOverridesChanged: function()
417     {
418         this._resetButton.disabled = !WebInspector.overridesSupport.hasActiveOverrides();
419     },
420
421     _deviceSelected: function()
422     {
423         var option = this._deviceSelectElement.options[this._deviceSelectElement.selectedIndex];
424         this._emulatedDeviceSetting.set(option.value);
425         this._updateValueLabels();
426     },
427
428     _updateValueLabels: function()
429     {
430         var option = this._deviceSelectElement.options[this._deviceSelectElement.selectedIndex];
431         var metrics;
432         if (option._metrics && (metrics = WebInspector.OverridesSupport.DeviceMetrics.parseSetting(option._metrics)))
433             this._viewportValueElement.textContent = WebInspector.UIString("%s \xD7 %s, devicePixelRatio = %s", metrics.width, metrics.height, metrics.deviceScaleFactor);
434         else
435             this._viewportValueElement.textContent = "";
436         this._userAgentValueElement.textContent = option._userAgent || "";
437     },
438
439     __proto__: WebInspector.OverridesView.Tab.prototype
440 }
441
442
443 /**
444  * @constructor
445  * @extends {WebInspector.OverridesView.Tab}
446  */
447 WebInspector.OverridesView.ViewportTab = function()
448 {
449     WebInspector.OverridesView.Tab.call(this, "viewport", WebInspector.UIString("Screen"), [WebInspector.settings.overrideDeviceMetrics, WebInspector.settings.overrideCSSMedia]);
450     this.element.classList.add("overrides-viewport");
451
452     const metricsSetting = WebInspector.settings.deviceMetrics.get();
453     var metrics = WebInspector.OverridesSupport.DeviceMetrics.parseSetting(metricsSetting);
454     var checkbox = this._createSettingCheckbox(WebInspector.UIString("Emulate screen"), WebInspector.settings.overrideDeviceMetrics, this._onMetricsCheckboxClicked.bind(this));
455     checkbox.firstChild.disabled = WebInspector.OverridesSupport.isInspectingDevice();
456     WebInspector.settings.deviceMetrics.addChangeListener(this._updateDeviceMetricsElement, this);
457
458     this.element.appendChild(checkbox);
459     this.element.appendChild(this._createDeviceMetricsElement(metrics));
460     this.element.appendChild(this._createMediaEmulationFragment());
461
462     var footnote = this.element.createChild("p", "help-footnote");
463     var footnoteLink = footnote.createChild("a");
464     footnoteLink.href = "https://developers.google.com/chrome-developer-tools/docs/mobile-emulation";
465     footnoteLink.target = "_blank";
466     footnoteLink.createTextChild(WebInspector.UIString("More information about screen emulation"));
467
468     this._onMetricsCheckboxClicked(WebInspector.settings.overrideDeviceMetrics.get());
469 }
470
471 WebInspector.OverridesView.ViewportTab.prototype = {
472     /**
473      * @param {boolean} enabled
474      */
475     _onMetricsCheckboxClicked: function(enabled)
476     {
477         if (enabled && !this._widthOverrideElement.value)
478             this._widthOverrideElement.focus();
479         this._applyDeviceMetricsUserInput();
480     },
481
482     _applyDeviceMetricsUserInput: function()
483     {
484         this._muteRangeListener = true;
485         this._widthRangeInput.value = this._widthOverrideElement.value;
486         delete this._muteRangeListener;
487         if (this._applyDeviceMetricsTimer)
488             clearTimeout(this._applyDeviceMetricsTimer);
489         this._applyDeviceMetricsTimer = setTimeout(this._doApplyDeviceMetricsUserInput.bind(this), 50);
490     },
491
492     _doApplyDeviceMetricsUserInput: function()
493     {
494         delete this._applyDeviceMetricsTimer;
495         this._setDeviceMetricsOverride(WebInspector.OverridesSupport.DeviceMetrics.parseUserInput(this._widthOverrideElement.value.trim(), this._heightOverrideElement.value.trim(), this._deviceScaleFactorOverrideElement.value.trim(), this._textAutosizingOverrideCheckbox.checked), true);
496     },
497
498     /**
499      * @param {?WebInspector.OverridesSupport.DeviceMetrics} metrics
500      * @param {boolean} userInputModified
501      */
502     _setDeviceMetricsOverride: function(metrics, userInputModified)
503     {
504         function setValid(condition, element)
505         {
506             if (condition)
507                 element.classList.remove("error-input");
508             else
509                 element.classList.add("error-input");
510         }
511
512         setValid(metrics && metrics.isWidthValid(), this._widthOverrideElement);
513         setValid(metrics && metrics.isHeightValid(), this._heightOverrideElement);
514         setValid(metrics && metrics.isDeviceScaleFactorValid(), this._deviceScaleFactorOverrideElement);
515
516         if (!metrics)
517             return;
518
519         if (!userInputModified) {
520             this._widthOverrideElement.value = metrics.widthToInput();
521             this._heightOverrideElement.value = metrics.heightToInput();
522             this._deviceScaleFactorOverrideElement.value = metrics.deviceScaleFactorToInput();
523             this._textAutosizingOverrideCheckbox.checked = metrics.textAutosizing;
524         }
525
526         if (metrics.isValid()) {
527             var value = metrics.toSetting();
528             if (value !== WebInspector.settings.deviceMetrics.get())
529                 WebInspector.settings.deviceMetrics.set(value);
530         }
531     },
532
533     /**
534      * @param {!WebInspector.OverridesSupport.DeviceMetrics} metrics
535      */
536     _createDeviceMetricsElement: function(metrics)
537     {
538         var fieldsetElement = WebInspector.SettingsUI.createSettingFieldset(WebInspector.settings.overrideDeviceMetrics);
539         if (WebInspector.OverridesSupport.isInspectingDevice())
540             fieldsetElement.disabled = true;
541         fieldsetElement.id = "metrics-override-section";
542
543         /**
544          * @this {WebInspector.OverridesView.ViewportTab}
545          */
546         function swapDimensionsClicked()
547         {
548             var widthValue = this._widthOverrideElement.value;
549             this._widthOverrideElement.value = this._heightOverrideElement.value;
550             this._heightOverrideElement.value = widthValue;
551             this._applyDeviceMetricsUserInput();
552         }
553
554         var tableElement = fieldsetElement.createChild("table", "nowrap");
555
556         var rowElement = tableElement.createChild("tr");
557         var cellElement = rowElement.createChild("td");
558         cellElement.appendChild(document.createTextNode(WebInspector.UIString("Resolution:")));
559         cellElement = rowElement.createChild("td");
560         this._widthOverrideElement = this._createInput(cellElement, "metrics-override-width", String(metrics.width || screen.width), this._applyDeviceMetricsUserInput.bind(this), true);
561         this._swapDimensionsElement = cellElement.createChild("button", "overrides-swap");
562         this._swapDimensionsElement.appendChild(document.createTextNode(" \u21C4 ")); // RIGHTWARDS ARROW OVER LEFTWARDS ARROW.
563         this._swapDimensionsElement.title = WebInspector.UIString("Swap dimensions");
564         this._swapDimensionsElement.addEventListener("click", swapDimensionsClicked.bind(this), false);
565         this._swapDimensionsElement.tabIndex = -1;
566         this._heightOverrideElement = this._createInput(cellElement, "metrics-override-height", String(metrics.height || screen.height), this._applyDeviceMetricsUserInput.bind(this), true);
567
568         rowElement = tableElement.createChild("tr");
569         cellElement = rowElement.createChild("td");
570         cellElement.colSpan = 4;
571         this._widthRangeInput = cellElement.createChild("input");
572         this._widthRangeInput.type = "range";
573         this._widthRangeInput.min = 100;
574         this._widthRangeInput.max = 2000;
575         this._widthRangeInput.addEventListener("change", this._rangeValueChanged.bind(this), false);
576         this._widthRangeInput.addEventListener("input", this._rangeValueChanged.bind(this), false);
577         this._widthRangeInput.value = this._widthOverrideElement.value;
578
579         rowElement = tableElement.createChild("tr");
580         rowElement.title = WebInspector.UIString("Ratio between a device's physical pixels and device-independent pixels.");
581         cellElement = rowElement.createChild("td");
582         cellElement.appendChild(document.createTextNode(WebInspector.UIString("Device pixel ratio:")));
583         cellElement = rowElement.createChild("td");
584         this._deviceScaleFactorOverrideElement = this._createInput(cellElement, "metrics-override-device-scale", String(metrics.deviceScaleFactor || 1), this._applyDeviceMetricsUserInput.bind(this), true);
585
586         var textAutosizingOverrideElement = this._createNonPersistedCheckbox(WebInspector.UIString("Enable text autosizing "), this._applyDeviceMetricsUserInput.bind(this));
587         textAutosizingOverrideElement.title = WebInspector.UIString("Text autosizing is the feature that boosts font sizes on mobile devices.");
588         this._textAutosizingOverrideCheckbox = textAutosizingOverrideElement.firstChild;
589         this._textAutosizingOverrideCheckbox.checked = metrics.textAutosizing;
590         fieldsetElement.appendChild(textAutosizingOverrideElement);
591
592         var checkbox = this._createSettingCheckbox(WebInspector.UIString("Emulate viewport"), WebInspector.settings.emulateViewport);
593         fieldsetElement.appendChild(checkbox);
594
595         checkbox = this._createSettingCheckbox(WebInspector.UIString("Shrink to fit"), WebInspector.settings.deviceFitWindow);
596         fieldsetElement.appendChild(checkbox);
597
598         return fieldsetElement;
599     },
600
601     _updateDeviceMetricsElement: function()
602     {
603         const metricsSetting = WebInspector.settings.deviceMetrics.get();
604         var metrics = WebInspector.OverridesSupport.DeviceMetrics.parseSetting(metricsSetting);
605
606         if (this._widthOverrideElement.value !== metrics.width)
607             this._widthOverrideElement.value  = metrics.width || screen.width;
608         this._muteRangeListener = true;
609         if (this._widthRangeInput.value != metrics.width)
610             this._widthRangeInput.value = metrics.width || screen.width;
611         delete this._muteRangeListener;
612         if (this._heightOverrideElement.value !== metrics.height)
613             this._heightOverrideElement.value = metrics.height || screen.height;
614         if (this._deviceScaleFactorOverrideElement.value !== metrics.deviceScaleFactor)
615             this._deviceScaleFactorOverrideElement.value = metrics.deviceScaleFactor || 1;
616         if (this._textAutosizingOverrideCheckbox.checked !== metrics.textAutosizing)
617             this._textAutosizingOverrideCheckbox.checked = metrics.textAutosizing || false;
618     },
619
620     _createMediaEmulationFragment: function()
621     {
622         var checkbox = WebInspector.SettingsUI.createSettingCheckbox(WebInspector.UIString("CSS media"), WebInspector.settings.overrideCSSMedia, true);
623         var fieldsetElement = WebInspector.SettingsUI.createSettingFieldset(WebInspector.settings.overrideCSSMedia);
624         if (WebInspector.OverridesSupport.isInspectingDevice())
625             fieldsetElement.disabled = true;
626
627         var mediaSelectElement = fieldsetElement.createChild("select");
628         var mediaTypes = WebInspector.CSSStyleModel.MediaTypes;
629         var defaultMedia = WebInspector.settings.emulatedCSSMedia.get();
630         for (var i = 0; i < mediaTypes.length; ++i) {
631             var mediaType = mediaTypes[i];
632             if (mediaType === "all") {
633                 // "all" is not a device-specific media type.
634                 continue;
635             }
636             var option = document.createElement("option");
637             option.text = mediaType;
638             option.value = mediaType;
639             mediaSelectElement.add(option);
640             if (mediaType === defaultMedia)
641                 mediaSelectElement.selectedIndex = mediaSelectElement.options.length - 1;
642         }
643
644         mediaSelectElement.addEventListener("change", this._emulateMediaChanged.bind(this, mediaSelectElement), false);
645         var fragment = document.createDocumentFragment();
646         fragment.appendChild(checkbox);
647         fragment.appendChild(fieldsetElement);
648         return fragment;
649     },
650
651     _emulateMediaChanged: function(select)
652     {
653         var media = select.options[select.selectedIndex].value;
654         WebInspector.settings.emulatedCSSMedia.set(media);
655     },
656
657     _rangeValueChanged: function()
658     {
659         if (this._muteRangeListener)
660             return;
661         this._widthOverrideElement.value = this._widthRangeInput.value;
662         this._applyDeviceMetricsUserInput();
663     },
664
665     __proto__: WebInspector.OverridesView.Tab.prototype
666 }
667
668
669 /**
670  * @constructor
671  * @extends {WebInspector.OverridesView.Tab}
672  */
673 WebInspector.OverridesView.UserAgentTab = function()
674 {
675     WebInspector.OverridesView.Tab.call(this, "user-agent", WebInspector.UIString("User Agent"), [WebInspector.settings.overrideUserAgent]);
676     this.element.classList.add("overrides-user-agent");
677     var checkbox = this._createSettingCheckbox(WebInspector.UIString("Spoof user agent"), WebInspector.settings.overrideUserAgent);
678     checkbox.firstChild.disabled = WebInspector.OverridesSupport.isInspectingDevice();
679     this.element.appendChild(checkbox);
680     this.element.appendChild(this._createUserAgentSelectRowElement());
681 }
682
683 WebInspector.OverridesView.UserAgentTab._userAgents = [
684     ["Android 4.0.2 \u2014 Galaxy Nexus", "Mozilla/5.0 (Linux; U; Android 4.0.2; en-us; Galaxy Nexus Build/ICL53F) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30"],
685     ["Android 2.3 \u2014 Nexus S", "Mozilla/5.0 (Linux; U; Android 2.3.6; en-us; Nexus S Build/GRK39F) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1"],
686     ["BlackBerry \u2014 BB10", "Mozilla/5.0 (BB10; Touch) AppleWebKit/537.1+ (KHTML, like Gecko) Version/10.0.0.1337 Mobile Safari/537.1+"],
687     ["BlackBerry \u2014 PlayBook 2.1", "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+"],
688     ["BlackBerry \u2014 9900", "Mozilla/5.0 (BlackBerry; U; BlackBerry 9900; en-US) AppleWebKit/534.11+ (KHTML, like Gecko) Version/7.0.0.187 Mobile Safari/534.11+"],
689     ["Chrome 31 \u2014 Mac", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36"],
690     ["Chrome 31 \u2014 Windows", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.16 Safari/537.36"],
691     ["Chrome \u2014 Android Tablet", "Mozilla/5.0 (Linux; Android 4.1.2; Nexus 7 Build/JZ054K) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Safari/535.19"],
692     ["Chrome \u2014 Android Mobile", "Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19"],
693     ["Firefox 14 \u2014 Android Mobile", "Mozilla/5.0 (Android; Mobile; rv:14.0) Gecko/14.0 Firefox/14.0"],
694     ["Firefox 14 \u2014 Android Tablet", "Mozilla/5.0 (Android; Tablet; rv:14.0) Gecko/14.0 Firefox/14.0"],
695     ["Firefox 4 \u2014 Mac", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0.1) Gecko/20100101 Firefox/4.0.1"],
696     ["Firefox 4 \u2014 Windows", "Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1"],
697     ["Firefox 7 \u2014 Mac", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:7.0.1) Gecko/20100101 Firefox/7.0.1"],
698     ["Firefox 7 \u2014 Windows", "Mozilla/5.0 (Windows NT 6.1; Intel Mac OS X 10.6; rv:7.0.1) Gecko/20100101 Firefox/7.0.1"],
699     ["Internet Explorer 10", "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0)"],
700     ["Internet Explorer 7", "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)"],
701     ["Internet Explorer 8", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)"],
702     ["Internet Explorer 9", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)"],
703     ["iPad \u2014 iOS 7", "Mozilla/5.0 (iPad; CPU OS 7_0_2 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11A501 Safari/9537.53"],
704     ["iPad \u2014 iOS 6", "Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5376e Safari/8536.25"],
705     ["iPhone \u2014 iOS 7", "Mozilla/5.0 (iPhone; CPU iPhone OS 7_0_2 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11A4449d Safari/9537.53"],
706     ["iPhone \u2014 iOS 6", "Mozilla/5.0 (iPhone; CPU iPhone OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5376e Safari/8536.25"],
707     ["MeeGo \u2014 Nokia N9", "Mozilla/5.0 (MeeGo; NokiaN9) AppleWebKit/534.13 (KHTML, like Gecko) NokiaBrowser/8.5.0 Mobile Safari/534.13"],
708     ["Opera 18 \u2014 Mac", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36 OPR/18.0.1284.68"],
709     ["Opera 18 \u2014 Windows", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36 OPR/18.0.1284.68"],
710     ["Opera 12 \u2014 Mac", "Opera/9.80 (Macintosh; Intel Mac OS X 10.9.1) Presto/2.12.388 Version/12.16"],
711     ["Opera 12 \u2014 Windows", "Opera/9.80 (Windows NT 6.1) Presto/2.12.388 Version/12.16"],
712 ];
713
714 WebInspector.OverridesView.UserAgentTab.prototype = {
715     /**
716      * @return {!Element}
717      */
718     _createUserAgentSelectRowElement: function()
719     {
720         var userAgent = WebInspector.settings.userAgent.get();
721         var userAgents = WebInspector.OverridesView.UserAgentTab._userAgents.concat([[WebInspector.UIString("Other"), "Other"]]);
722
723         var fieldsetElement = WebInspector.SettingsUI.createSettingFieldset(WebInspector.settings.overrideUserAgent);
724         if (WebInspector.OverridesSupport.isInspectingDevice())
725             fieldsetElement.disabled = true;
726
727         this._selectElement = fieldsetElement.createChild("select");
728         fieldsetElement.createChild("br");
729         this._otherUserAgentElement = fieldsetElement.createChild("input");
730         this._otherUserAgentElement.type = "text";
731         this._otherUserAgentElement.value = userAgent;
732         this._otherUserAgentElement.title = userAgent;
733
734         var selectionRestored = false;
735         for (var i = 0; i < userAgents.length; ++i) {
736             var agent = userAgents[i];
737             var option = new Option(agent[0], agent[1]);
738             option._metrics = agent[2] ? agent[2] : "";
739             this._selectElement.add(option);
740             if (userAgent === agent[1]) {
741                 this._selectElement.selectedIndex = i;
742                 selectionRestored = true;
743             }
744         }
745
746         if (!selectionRestored) {
747             if (!userAgent)
748                 this._selectElement.selectedIndex = 0;
749             else
750                 this._selectElement.selectedIndex = userAgents.length - 1;
751         }
752
753         this._selectElement.addEventListener("change", this._userAgentChanged.bind(this, true), false);
754         WebInspector.settings.userAgent.addChangeListener(this._userAgentSettingChanged, this);
755
756         fieldsetElement.addEventListener("dblclick", textDoubleClicked.bind(this), false);
757         this._otherUserAgentElement.addEventListener("blur", textChanged.bind(this), false);
758
759         /**
760          * @this {WebInspector.OverridesView.UserAgentTab}
761          */
762         function textDoubleClicked()
763         {
764             this._selectElement.selectedIndex = userAgents.length - 1;
765             this._userAgentChanged();
766         }
767
768         /**
769          * @this {WebInspector.OverridesView.UserAgentTab}
770          */
771         function textChanged()
772         {
773             if (WebInspector.settings.userAgent.get() !== this._otherUserAgentElement.value)
774                 WebInspector.settings.userAgent.set(this._otherUserAgentElement.value);
775         }
776
777         return fieldsetElement;
778     },
779
780     /**
781      * @param {boolean=} isUserGesture
782      */
783     _userAgentChanged: function(isUserGesture)
784     {
785         var value = this._selectElement.options[this._selectElement.selectedIndex].value;
786         if (value !== "Other") {
787             WebInspector.settings.userAgent.set(value);
788             this._otherUserAgentElement.value = value;
789             this._otherUserAgentElement.title = value;
790             this._otherUserAgentElement.disabled = true;
791         } else {
792             this._otherUserAgentElement.disabled = false;
793             this._otherUserAgentElement.focus();
794         }
795     },
796
797     _userAgentSettingChanged: function()
798     {
799         var value = WebInspector.settings.userAgent.get();
800         var options = this._selectElement.options;
801         var foundMatch = false;
802         for (var i = 0; i < options.length; ++i) {
803             if (options[i].value === value) {
804                 if (this._selectElement.selectedIndex !== i)
805                     this._selectElement.selectedIndex = i;
806                 foundMatch = true;
807                 break;
808             }
809         }
810
811         this._otherUserAgentElement.disabled = foundMatch;
812         if (!foundMatch)
813             this._selectElement.selectedIndex = options.length - 1;
814
815         if (this._otherUserAgentElement.value !== value) {
816             this._otherUserAgentElement.value = value;
817             this._otherUserAgentElement.title = value;
818         }
819     },
820
821     __proto__: WebInspector.OverridesView.Tab.prototype
822 }
823
824
825 /**
826  * @constructor
827  * @extends {WebInspector.OverridesView.Tab}
828  */
829 WebInspector.OverridesView.SensorsTab = function()
830 {
831     WebInspector.OverridesView.Tab.call(this, "sensors", WebInspector.UIString("Sensors"), [WebInspector.settings.emulateTouchEvents, WebInspector.settings.overrideGeolocation, WebInspector.settings.overrideDeviceOrientation]);
832     this.element.classList.add("overrides-sensors");
833     this.registerRequiredCSS("accelerometer.css");
834     if (!WebInspector.OverridesSupport.isInspectingDevice())
835         this.element.appendChild(this._createSettingCheckbox(WebInspector.UIString("Emulate touch screen"), WebInspector.settings.emulateTouchEvents));
836     this._appendGeolocationOverrideControl();
837     this._apendDeviceOrientationOverrideControl();
838 }
839
840 WebInspector.OverridesView.SensorsTab.prototype = {
841     _appendGeolocationOverrideControl: function()
842     {
843         const geolocationSetting = WebInspector.settings.geolocationOverride.get();
844         var geolocation = WebInspector.OverridesSupport.GeolocationPosition.parseSetting(geolocationSetting);
845         this.element.appendChild(this._createSettingCheckbox(WebInspector.UIString("Emulate geolocation coordinates"), WebInspector.settings.overrideGeolocation, this._geolocationOverrideCheckboxClicked.bind(this)));
846         this.element.appendChild(this._createGeolocationOverrideElement(geolocation));
847         this._geolocationOverrideCheckboxClicked(WebInspector.settings.overrideGeolocation.get());
848     },
849
850     /**
851      * @param {boolean} enabled
852      */
853     _geolocationOverrideCheckboxClicked: function(enabled)
854     {
855         if (enabled && !this._latitudeElement.value)
856             this._latitudeElement.focus();
857     },
858
859     _applyGeolocationUserInput: function()
860     {
861         this._setGeolocationPosition(WebInspector.OverridesSupport.GeolocationPosition.parseUserInput(this._latitudeElement.value.trim(), this._longitudeElement.value.trim(), this._geolocationErrorElement.checked), true);
862     },
863
864     /**
865      * @param {?WebInspector.OverridesSupport.GeolocationPosition} geolocation
866      * @param {boolean} userInputModified
867      */
868     _setGeolocationPosition: function(geolocation, userInputModified)
869     {
870         if (!geolocation)
871             return;
872
873         if (!userInputModified) {
874             this._latitudeElement.value = geolocation.latitude;
875             this._longitudeElement.value = geolocation.longitude;
876         }
877
878         var value = geolocation.toSetting();
879         WebInspector.settings.geolocationOverride.set(value);
880     },
881
882     /**
883      * @param {!WebInspector.OverridesSupport.GeolocationPosition} geolocation
884      * @return {!Element}
885      */
886     _createGeolocationOverrideElement: function(geolocation)
887     {
888         var fieldsetElement = WebInspector.SettingsUI.createSettingFieldset(WebInspector.settings.overrideGeolocation);
889         fieldsetElement.id = "geolocation-override-section";
890
891         var tableElement = fieldsetElement.createChild("table");
892         var rowElement = tableElement.createChild("tr");
893         var cellElement = rowElement.createChild("td");
894         cellElement = rowElement.createChild("td");
895         cellElement.appendChild(document.createTextNode(WebInspector.UIString("Lat = ")));
896         this._latitudeElement = this._createInput(cellElement, "geolocation-override-latitude", String(geolocation.latitude), this._applyGeolocationUserInput.bind(this), true);
897         cellElement.appendChild(document.createTextNode(" , "));
898         cellElement.appendChild(document.createTextNode(WebInspector.UIString("Lon = ")));
899         this._longitudeElement = this._createInput(cellElement, "geolocation-override-longitude", String(geolocation.longitude), this._applyGeolocationUserInput.bind(this), true);
900         rowElement = tableElement.createChild("tr");
901         cellElement = rowElement.createChild("td");
902         cellElement.colSpan = 2;
903         var geolocationErrorLabelElement = document.createElement("label");
904         var geolocationErrorCheckboxElement = geolocationErrorLabelElement.createChild("input");
905         geolocationErrorCheckboxElement.id = "geolocation-error";
906         geolocationErrorCheckboxElement.type = "checkbox";
907         geolocationErrorCheckboxElement.checked = !geolocation || geolocation.error;
908         geolocationErrorCheckboxElement.addEventListener("click", this._applyGeolocationUserInput.bind(this), false);
909         geolocationErrorLabelElement.appendChild(document.createTextNode(WebInspector.UIString("Emulate position unavailable")));
910         this._geolocationErrorElement = geolocationErrorCheckboxElement;
911         cellElement.appendChild(geolocationErrorLabelElement);
912
913         return fieldsetElement;
914     },
915
916     _apendDeviceOrientationOverrideControl: function()
917     {
918         const deviceOrientationSetting = WebInspector.settings.deviceOrientationOverride.get();
919         var deviceOrientation = WebInspector.OverridesSupport.DeviceOrientation.parseSetting(deviceOrientationSetting);
920         this.element.appendChild(this._createSettingCheckbox(WebInspector.UIString("Accelerometer"), WebInspector.settings.overrideDeviceOrientation, this._deviceOrientationOverrideCheckboxClicked.bind(this)));
921         this.element.appendChild(this._createDeviceOrientationOverrideElement(deviceOrientation));
922         this._deviceOrientationOverrideCheckboxClicked(WebInspector.settings.overrideDeviceOrientation.get());
923     },
924
925     /**
926      * @param {boolean} enabled
927      */
928     _deviceOrientationOverrideCheckboxClicked: function(enabled)
929     {
930         if (enabled && !this._alphaElement.value)
931             this._alphaElement.focus();
932     },
933
934     _applyDeviceOrientationUserInput: function()
935     {
936         this._setDeviceOrientation(WebInspector.OverridesSupport.DeviceOrientation.parseUserInput(this._alphaElement.value.trim(), this._betaElement.value.trim(), this._gammaElement.value.trim()), WebInspector.OverridesView.SensorsTab.DeviceOrientationModificationSource.UserInput);
937     },
938
939     _resetDeviceOrientation: function()
940     {
941         this._setDeviceOrientation(new WebInspector.OverridesSupport.DeviceOrientation(0, 0, 0), WebInspector.OverridesView.SensorsTab.DeviceOrientationModificationSource.ResetButton);
942     },
943
944     /**
945      * @param {?WebInspector.OverridesSupport.DeviceOrientation} deviceOrientation
946      * @param {!WebInspector.OverridesView.SensorsTab.DeviceOrientationModificationSource} modificationSource
947      */
948     _setDeviceOrientation: function(deviceOrientation, modificationSource)
949     {
950         if (!deviceOrientation)
951             return;
952
953         if (modificationSource != WebInspector.OverridesView.SensorsTab.DeviceOrientationModificationSource.UserInput) {
954             this._alphaElement.value = deviceOrientation.alpha;
955             this._betaElement.value = deviceOrientation.beta;
956             this._gammaElement.value = deviceOrientation.gamma;
957         }
958
959         if (modificationSource != WebInspector.OverridesView.SensorsTab.DeviceOrientationModificationSource.UserDrag)
960             this._setBoxOrientation(deviceOrientation);
961
962         var value = deviceOrientation.toSetting();
963         WebInspector.settings.deviceOrientationOverride.set(value);
964     },
965
966     /**
967      * @param {!Element} parentElement
968      * @param {string} id
969      * @param {string} label
970      * @param {string} defaultText
971      * @return {!Element}
972      */
973     _createAxisInput: function(parentElement, id, label, defaultText)
974     {
975         var div = parentElement.createChild("div", "accelerometer-axis-input-container");
976         div.appendChild(document.createTextNode(label));
977         return this._createInput(div, id, defaultText, this._applyDeviceOrientationUserInput.bind(this), true);
978     },
979
980     /**
981      * @param {!WebInspector.OverridesSupport.DeviceOrientation} deviceOrientation
982      */
983     _createDeviceOrientationOverrideElement: function(deviceOrientation)
984     {
985         var fieldsetElement = WebInspector.SettingsUI.createSettingFieldset(WebInspector.settings.overrideDeviceOrientation);
986         fieldsetElement.id = "device-orientation-override-section";
987         var tableElement = fieldsetElement.createChild("table");
988         var rowElement = tableElement.createChild("tr");
989         var cellElement = rowElement.createChild("td", "accelerometer-inputs-cell");
990
991         this._alphaElement = this._createAxisInput(cellElement, "device-orientation-override-alpha", "\u03B1: ", String(deviceOrientation.alpha));
992         this._betaElement = this._createAxisInput(cellElement, "device-orientation-override-beta", "\u03B2: ", String(deviceOrientation.beta));
993         this._gammaElement = this._createAxisInput(cellElement, "device-orientation-override-gamma", "\u03B3: ", String(deviceOrientation.gamma));
994
995         var resetButton = cellElement.createChild("button", "settings-tab-text-button accelerometer-reset-button");
996         resetButton.textContent = WebInspector.UIString("Reset");
997         resetButton.addEventListener("click", this._resetDeviceOrientation.bind(this), false);
998
999         this._stageElement = rowElement.createChild("td","accelerometer-stage");
1000         this._boxElement = this._stageElement.createChild("section", "accelerometer-box");
1001
1002         this._boxElement.createChild("section", "front");
1003         this._boxElement.createChild("section", "top");
1004         this._boxElement.createChild("section", "back");
1005         this._boxElement.createChild("section", "left");
1006         this._boxElement.createChild("section", "right");
1007         this._boxElement.createChild("section", "bottom");
1008
1009         WebInspector.installDragHandle(this._stageElement, this._onBoxDragStart.bind(this), this._onBoxDrag.bind(this), this._onBoxDragEnd.bind(this), "move");
1010         this._setBoxOrientation(deviceOrientation);
1011         return fieldsetElement;
1012     },
1013
1014     /**
1015      * @param {!WebInspector.OverridesSupport.DeviceOrientation} deviceOrientation
1016      */
1017     _setBoxOrientation: function(deviceOrientation)
1018     {
1019         var matrix = new WebKitCSSMatrix();
1020         this._boxMatrix = matrix.rotate(-deviceOrientation.beta, deviceOrientation.gamma, -deviceOrientation.alpha);
1021         this._boxElement.style.webkitTransform = this._boxMatrix.toString();
1022     },
1023
1024     /**
1025      * @param {!MouseEvent} event
1026      * @return {boolean}
1027      */
1028     _onBoxDrag: function(event)
1029     {
1030         var mouseMoveVector = this._calculateRadiusVector(event.x, event.y);
1031         if (!mouseMoveVector)
1032             return true;
1033
1034         event.consume(true);
1035         var axis = WebInspector.Geometry.crossProduct(this._mouseDownVector, mouseMoveVector);
1036         axis.normalize();
1037         var angle = WebInspector.Geometry.calculateAngle(this._mouseDownVector, mouseMoveVector);
1038         var matrix = new WebKitCSSMatrix();
1039         var rotationMatrix = matrix.rotateAxisAngle(axis.x, axis.y, axis.z, angle);
1040         this._currentMatrix = rotationMatrix.multiply(this._boxMatrix)
1041         this._boxElement.style.webkitTransform = this._currentMatrix;
1042         var eulerAngles = WebInspector.Geometry.EulerAngles.fromRotationMatrix(this._currentMatrix);
1043         var newOrientation = new WebInspector.OverridesSupport.DeviceOrientation(-eulerAngles.alpha, -eulerAngles.beta, eulerAngles.gamma);
1044         this._setDeviceOrientation(newOrientation, WebInspector.OverridesView.SensorsTab.DeviceOrientationModificationSource.UserDrag);
1045         return false;
1046     },
1047
1048     /**
1049      * @param {!MouseEvent} event
1050      * @return {boolean}
1051      */
1052     _onBoxDragStart: function(event)
1053     {
1054         if (!WebInspector.settings.overrideDeviceOrientation.get())
1055             return false;
1056
1057         this._mouseDownVector = this._calculateRadiusVector(event.x, event.y);
1058
1059         if (!this._mouseDownVector)
1060             return false;
1061
1062         event.consume(true);
1063         return true;
1064     },
1065
1066     _onBoxDragEnd: function()
1067     {
1068         this._boxMatrix = this._currentMatrix;
1069     },
1070
1071     /**
1072      * @param {number} x
1073      * @param {number} y
1074      * @return {?WebInspector.Geometry.Vector}
1075      */
1076     _calculateRadiusVector: function(x, y)
1077     {
1078         var rect = this._stageElement.getBoundingClientRect();
1079         var radius = Math.max(rect.width, rect.height) / 2;
1080         var sphereX = (x - rect.left - rect.width / 2) / radius;
1081         var sphereY = (y - rect.top - rect.height / 2) / radius;
1082         var sqrSum = sphereX * sphereX + sphereY * sphereY;
1083         if (sqrSum > 0.5)
1084             return new WebInspector.Geometry.Vector(sphereX, sphereY, 0.5 / Math.sqrt(sqrSum));
1085
1086         return new WebInspector.Geometry.Vector(sphereX, sphereY, Math.sqrt(1 - sqrSum));
1087     },
1088
1089     __proto__ : WebInspector.OverridesView.Tab.prototype
1090 }
1091
1092 /** @enum {string} */
1093 WebInspector.OverridesView.SensorsTab.DeviceOrientationModificationSource = {
1094     UserInput: "userInput",
1095     UserDrag: "userDrag",
1096     ResetButton: "resetButton"
1097 }