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