Home | History | Annotate | Download | only in front_end
      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  */
     35 WebInspector.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 
     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.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  */
     87 WebInspector.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 
     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.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 
    198 WebInspector.OverridesView.Tab.prototype.__proto__ = WebInspector.View.prototype;
    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("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.
    265 WebInspector.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 
    355 WebInspector.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 
    394 WebInspector.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 
    434 WebInspector.OverridesView.DeviceTab.prototype.__proto__ = WebInspector.OverridesView.Tab.prototype;
    435 
    436 
    437 /**
    438  * @constructor
    439  * @extends {WebInspector.OverridesView.Tab}
    440  */
    441 WebInspector.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 
    465 WebInspector.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 
    657 WebInspector.OverridesView.ViewportTab.prototype.__proto__ = WebInspector.OverridesView.Tab.prototype;
    658 
    659 
    660 /**
    661  * @constructor
    662  * @extends {WebInspector.OverridesView.Tab}
    663  */
    664 WebInspector.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 
    674 WebInspector.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 
    705 WebInspector.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 
    813 WebInspector.OverridesView.UserAgentTab.prototype.__proto__ = WebInspector.OverridesView.Tab.prototype;
    814 
    815 
    816 /**
    817  * @constructor
    818  * @extends {WebInspector.OverridesView.Tab}
    819  */
    820 WebInspector.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 
    832 WebInspector.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} */
   1085 WebInspector.OverridesView.SensorsTab.DeviceOrientationModificationSource = {
   1086     UserInput: "userInput",
   1087     UserDrag: "userDrag",
   1088     ResetButton: "resetButton"
   1089 }
   1090