Home | History | Annotate | Download | only in main
      1 /*
      2  * Copyright (C) 2014 Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 /**
     32  * @constructor
     33  * @extends {WebInspector.VBox}
     34  */
     35 WebInspector.OverridesView = function()
     36 {
     37     WebInspector.VBox.call(this);
     38     this.registerRequiredCSS("overrides.css");
     39     this.element.classList.add("overrides-view");
     40 
     41     this._tabbedPane = new WebInspector.TabbedPane();
     42     this._tabbedPane.shrinkableTabs = false;
     43     this._tabbedPane.verticalTabLayout = true;
     44 
     45     new WebInspector.OverridesView.DeviceTab().appendAsTab(this._tabbedPane);
     46     new WebInspector.OverridesView.MediaTab().appendAsTab(this._tabbedPane);
     47     new WebInspector.OverridesView.NetworkTab().appendAsTab(this._tabbedPane);
     48     new WebInspector.OverridesView.SensorsTab().appendAsTab(this._tabbedPane);
     49 
     50     this._lastSelectedTabSetting = WebInspector.settings.createSetting("lastSelectedEmulateTab", "device");
     51     this._tabbedPane.selectTab(this._lastSelectedTabSetting.get());
     52     this._tabbedPane.addEventListener(WebInspector.TabbedPane.EventTypes.TabSelected, this._tabSelected, this);
     53     this._tabbedPane.show(this.element);
     54 
     55     var resetButtonElement = this._tabbedPane.headerElement().createChild("button", "text-button");
     56     resetButtonElement.id = "overrides-reset-button";
     57     resetButtonElement.textContent = WebInspector.UIString("Reset");
     58     resetButtonElement.addEventListener("click", WebInspector.overridesSupport.reset.bind(WebInspector.overridesSupport), false);
     59 
     60     if (!WebInspector.overridesSupport.responsiveDesignAvailable()) {
     61         var disableButtonElement = this._tabbedPane.headerElement().createChild("button", "text-button overrides-disable-button");
     62         disableButtonElement.id = "overrides-disable-button";
     63         disableButtonElement.textContent = WebInspector.UIString("Disable");
     64         disableButtonElement.addEventListener("click", this._toggleEmulationEnabled.bind(this), false);
     65     }
     66 
     67     this._splashScreenElement = this.element.createChild("div", "overrides-splash-screen");
     68     this._unavailableSplashScreenElement = this.element.createChild("div", "overrides-splash-screen");
     69     this._unavailableSplashScreenElement.createTextChild(WebInspector.UIString("Emulation is not available."));
     70 
     71     if (WebInspector.overridesSupport.responsiveDesignAvailable()) {
     72         this._splashScreenElement.createTextChild(WebInspector.UIString("Emulation is currently disabled. Toggle "));
     73         var toggleEmulationButton = new WebInspector.StatusBarButton("", "emulation-status-bar-item");
     74         toggleEmulationButton.addEventListener("click", this._toggleEmulationEnabled, this);
     75         this._splashScreenElement.appendChild(toggleEmulationButton.element);
     76         this._splashScreenElement.createTextChild(WebInspector.UIString("in the main toolbar to enable it."));
     77     } else {
     78         var toggleEmulationButton = this._splashScreenElement.createChild("button", "text-button overrides-enable-button");
     79         toggleEmulationButton.textContent = WebInspector.UIString("Enable emulation");
     80         toggleEmulationButton.addEventListener("click", this._toggleEmulationEnabled.bind(this), false);
     81     }
     82 
     83     this._warningFooter = this.element.createChild("div", "overrides-footer");
     84     this._overridesWarningUpdated();
     85 
     86     WebInspector.overridesSupport.addEventListener(WebInspector.OverridesSupport.Events.OverridesWarningUpdated, this._overridesWarningUpdated, this);
     87     WebInspector.overridesSupport.addEventListener(WebInspector.OverridesSupport.Events.EmulationStateChanged, this._emulationStateChanged, this);
     88     this._emulationStateChanged();
     89 }
     90 
     91 WebInspector.OverridesView.prototype = {
     92     /**
     93      * @param {!WebInspector.Event} event
     94      */
     95     _tabSelected: function(event)
     96     {
     97         this._lastSelectedTabSetting.set(this._tabbedPane.selectedTabId);
     98     },
     99 
    100     _overridesWarningUpdated: function()
    101     {
    102         var message = WebInspector.overridesSupport.warningMessage();
    103         this._warningFooter.classList.toggle("hidden", !message);
    104         this._warningFooter.textContent = message;
    105     },
    106 
    107     _toggleEmulationEnabled: function()
    108     {
    109         WebInspector.overridesSupport.setEmulationEnabled(!WebInspector.overridesSupport.emulationEnabled());
    110     },
    111 
    112     _emulationStateChanged: function()
    113     {
    114         this._unavailableSplashScreenElement.classList.toggle("hidden", WebInspector.overridesSupport.canEmulate());
    115         this._tabbedPane.element.classList.toggle("hidden", !WebInspector.overridesSupport.emulationEnabled());
    116         this._splashScreenElement.classList.toggle("hidden", WebInspector.overridesSupport.emulationEnabled() || !WebInspector.overridesSupport.canEmulate());
    117     },
    118 
    119     __proto__: WebInspector.VBox.prototype
    120 }
    121 
    122 /**
    123  * @constructor
    124  * @extends {WebInspector.VBox}
    125  * @param {string} id
    126  * @param {string} name
    127  * @param {!Array.<!WebInspector.Setting>} settings
    128  * @param {!Array.<function():boolean>=} predicates
    129  */
    130 WebInspector.OverridesView.Tab = function(id, name, settings, predicates)
    131 {
    132     WebInspector.VBox.call(this);
    133     this._id = id;
    134     this._name = name;
    135     this._settings = settings;
    136     this._predicates = predicates || [];
    137     for (var i = 0; i < settings.length; ++i)
    138         settings[i].addChangeListener(this.updateActiveState, this);
    139 }
    140 
    141 WebInspector.OverridesView.Tab.prototype = {
    142     /**
    143      * @param {!WebInspector.TabbedPane} tabbedPane
    144      */
    145     appendAsTab: function(tabbedPane)
    146     {
    147         this._tabbedPane = tabbedPane;
    148         tabbedPane.appendTab(this._id, this._name, this);
    149         this.updateActiveState();
    150     },
    151 
    152     updateActiveState: function()
    153     {
    154         if (!this._tabbedPane)
    155             return;
    156         var active = false;
    157         for (var i = 0; !active && i < this._settings.length; ++i)
    158             active = this._settings[i].get();
    159         for (var i = 0; !active && i < this._predicates.length; ++i)
    160             active = this._predicates[i]();
    161         this._tabbedPane.toggleTabClass(this._id, "overrides-activate", active);
    162     },
    163 
    164     /**
    165      * @param {string} name
    166      * @param {!WebInspector.Setting} setting
    167      * @param {function(boolean)=} callback
    168      */
    169     _createSettingCheckbox: function(name, setting, callback)
    170     {
    171         var checkbox = WebInspector.SettingsUI.createSettingCheckbox(name, setting, true);
    172 
    173         function changeListener(value)
    174         {
    175             callback(setting.get());
    176         }
    177 
    178         if (callback)
    179             setting.addChangeListener(changeListener);
    180 
    181         return checkbox;
    182     },
    183 
    184     __proto__: WebInspector.VBox.prototype
    185 }
    186 
    187 /**
    188  * @constructor
    189  * @extends {WebInspector.OverridesView.Tab}
    190  */
    191 WebInspector.OverridesView.DeviceTab = function()
    192 {
    193     WebInspector.OverridesView.Tab.call(this, "device", WebInspector.UIString("Device"),  [
    194         WebInspector.overridesSupport.settings.emulateResolution,
    195         WebInspector.overridesSupport.settings.deviceScaleFactor,
    196         WebInspector.overridesSupport.settings.emulateMobile
    197     ]);
    198     this.element.classList.add("overrides-device");
    199 
    200     this.element.appendChild(this._createDeviceElement());
    201 
    202     var footnote = this.element.createChild("p", "help-footnote");
    203     var footnoteLink = footnote.createChild("a");
    204     footnoteLink.href = "https://developers.google.com/chrome-developer-tools/docs/mobile-emulation";
    205     footnoteLink.target = "_blank";
    206     footnoteLink.createTextChild(WebInspector.UIString("More information about screen emulation"));
    207 }
    208 
    209 WebInspector.OverridesView.DeviceTab.prototype = {
    210     _createDeviceElement: function()
    211     {
    212         var fieldsetElement = document.createElement("fieldset");
    213         fieldsetElement.id = "metrics-override-section";
    214 
    215         var deviceModelElement = fieldsetElement.createChild("p", "overrides-device-model-section");
    216         deviceModelElement.createChild("span").textContent = WebInspector.UIString("Model:");
    217 
    218         var deviceSelectElement = WebInspector.OverridesUI.createDeviceSelect(document, this._showTitleDialog.bind(this));
    219         var buttons = deviceSelectElement.querySelectorAll("button");
    220         for (var i = 0; i < buttons.length; ++i)
    221             buttons[i].classList.add("text-button");
    222         deviceModelElement.appendChild(deviceSelectElement);
    223 
    224         var emulateResolutionCheckbox = WebInspector.SettingsUI.createSettingCheckbox(WebInspector.UIString("Emulate screen resolution"), WebInspector.overridesSupport.settings.emulateResolution, true);
    225         fieldsetElement.appendChild(emulateResolutionCheckbox);
    226         var resolutionFieldset = WebInspector.SettingsUI.createSettingFieldset(WebInspector.overridesSupport.settings.emulateResolution);
    227         fieldsetElement.appendChild(resolutionFieldset);
    228 
    229         var tableElement = resolutionFieldset.createChild("table", "nowrap");
    230         var rowElement = tableElement.createChild("tr");
    231         var cellElement = rowElement.createChild("td");
    232         cellElement.createTextChild(WebInspector.UIString("Resolution:"));
    233         cellElement = rowElement.createChild("td");
    234 
    235         var widthOverrideInput = WebInspector.SettingsUI.createSettingInputField("", WebInspector.overridesSupport.settings.deviceWidth, true, 4, "80px", WebInspector.OverridesSupport.deviceSizeValidator, true, true, WebInspector.UIString("\u2013"));
    236         cellElement.appendChild(widthOverrideInput);
    237         this._swapDimensionsElement = cellElement.createChild("button", "overrides-swap");
    238         this._swapDimensionsElement.createTextChild(" \u21C4 "); // RIGHTWARDS ARROW OVER LEFTWARDS ARROW.
    239         this._swapDimensionsElement.title = WebInspector.UIString("Swap dimensions");
    240         this._swapDimensionsElement.addEventListener("click", WebInspector.overridesSupport.swapDimensions.bind(WebInspector.overridesSupport), false);
    241         this._swapDimensionsElement.tabIndex = -1;
    242         var heightOverrideInput = WebInspector.SettingsUI.createSettingInputField("", WebInspector.overridesSupport.settings.deviceHeight, true, 4, "80px", WebInspector.OverridesSupport.deviceSizeValidator, true, true, WebInspector.UIString("\u2013"));
    243         cellElement.appendChild(heightOverrideInput);
    244 
    245         rowElement = tableElement.createChild("tr");
    246         cellElement = rowElement.createChild("td");
    247         cellElement.colSpan = 4;
    248 
    249         rowElement = tableElement.createChild("tr");
    250         rowElement.title = WebInspector.UIString("Ratio between a device's physical pixels and device-independent pixels.");
    251         rowElement.createChild("td").createTextChild(WebInspector.UIString("Device pixel ratio:"));
    252         rowElement.createChild("td").appendChild(WebInspector.SettingsUI.createSettingInputField("", WebInspector.overridesSupport.settings.deviceScaleFactor, true, 4, "80px", WebInspector.OverridesSupport.deviceScaleFactorValidator, true, true, WebInspector.UIString("\u2013")));
    253 
    254         var mobileCheckbox = this._createSettingCheckbox(WebInspector.UIString("Emulate mobile"), WebInspector.overridesSupport.settings.emulateMobile);
    255         mobileCheckbox.title = WebInspector.UIString("Enable meta viewport, overlay scrollbars, text autosizing and default 980px body width");
    256         fieldsetElement.appendChild(mobileCheckbox);
    257 
    258         fieldsetElement.appendChild(this._createSettingCheckbox(WebInspector.UIString("Shrink to fit"), WebInspector.overridesSupport.settings.deviceFitWindow));
    259 
    260         return fieldsetElement;
    261     },
    262 
    263     /**
    264      * @param {!function(string)} callback
    265      */
    266     _showTitleDialog: function(callback)
    267     {
    268         WebInspector.Dialog.show(this.element, new WebInspector.OverridesView.DeviceTab.CustomDeviceTitleDialog(callback));
    269     },
    270 
    271     __proto__: WebInspector.OverridesView.Tab.prototype
    272 }
    273 
    274 /**
    275  * @constructor
    276  * @extends {WebInspector.DialogDelegate}
    277  * @param {!function(string)} callback
    278  */
    279 WebInspector.OverridesView.DeviceTab.CustomDeviceTitleDialog = function(callback)
    280 {
    281     WebInspector.DialogDelegate.call(this);
    282 
    283     this.element = document.createElementWithClass("div", "custom-device-title-dialog");
    284     this.element.createChild("label").textContent = WebInspector.UIString("Save as: ");
    285 
    286     this._input = this.element.createChild("input");
    287     this._input.setAttribute("type", "text");
    288     this._input.placeholder = WebInspector.UIString("device model name");
    289     this._input.addEventListener("input", this._onInput.bind(this), false);
    290 
    291     this._saveButton = this.element.createChild("button");
    292     this._saveButton.textContent = WebInspector.UIString("Save");
    293     this._saveButton.addEventListener("click", this._onSaveClick.bind(this), false);
    294 
    295     this._callback = callback;
    296     this._result = "";
    297     this._onInput();
    298 }
    299 
    300 WebInspector.OverridesView.DeviceTab.CustomDeviceTitleDialog.prototype = {
    301     focus: function()
    302     {
    303         WebInspector.setCurrentFocusElement(this._input);
    304         this._input.select();
    305     },
    306 
    307     _onSaveClick: function()
    308     {
    309         this._result = this._input.value.trim();
    310         WebInspector.Dialog.hide();
    311     },
    312 
    313     _onInput: function()
    314     {
    315         this._saveButton.disabled = !this._input.value.trim();
    316     },
    317 
    318     /**
    319      * @param {!Event} event
    320      */
    321     onEnter: function(event)
    322     {
    323         if (this._input.value.trim()) {
    324             this._result = this._input.value.trim();
    325         } else {
    326             event.consume();
    327         }
    328     },
    329 
    330     willHide: function()
    331     {
    332         this._callback(this._result);
    333     },
    334 
    335     __proto__: WebInspector.DialogDelegate.prototype
    336 }
    337 
    338 /**
    339  * @constructor
    340  * @extends {WebInspector.OverridesView.Tab}
    341  */
    342 WebInspector.OverridesView.MediaTab = function()
    343 {
    344     var settings = [WebInspector.overridesSupport.settings.overrideCSSMedia];
    345     WebInspector.OverridesView.Tab.call(this, "media", WebInspector.UIString("Media"), settings);
    346     this.element.classList.add("overrides-media");
    347 
    348     this._createMediaEmulationFragment();
    349 }
    350 
    351 WebInspector.OverridesView.MediaTab.prototype = {
    352     _createMediaEmulationFragment: function()
    353     {
    354         var checkbox = WebInspector.SettingsUI.createSettingCheckbox(WebInspector.UIString("CSS media"), WebInspector.overridesSupport.settings.overrideCSSMedia, true);
    355         var fieldsetElement = WebInspector.SettingsUI.createSettingFieldset(WebInspector.overridesSupport.settings.overrideCSSMedia);
    356         var mediaSelectElement = fieldsetElement.createChild("select");
    357         var mediaTypes = WebInspector.CSSStyleModel.MediaTypes;
    358         var defaultMedia = WebInspector.overridesSupport.settings.emulatedCSSMedia.get();
    359         for (var i = 0; i < mediaTypes.length; ++i) {
    360             var mediaType = mediaTypes[i];
    361             if (mediaType === "all") {
    362                 // "all" is not a device-specific media type.
    363                 continue;
    364             }
    365             var option = document.createElement("option");
    366             option.text = mediaType;
    367             option.value = mediaType;
    368             mediaSelectElement.add(option);
    369             if (mediaType === defaultMedia)
    370                 mediaSelectElement.selectedIndex = mediaSelectElement.options.length - 1;
    371         }
    372 
    373         mediaSelectElement.addEventListener("change", this._emulateMediaChanged.bind(this, mediaSelectElement), false);
    374         var fragment = document.createDocumentFragment();
    375         fragment.appendChild(checkbox);
    376         fragment.appendChild(fieldsetElement);
    377         this.element.appendChild(fragment);
    378     },
    379 
    380     _emulateMediaChanged: function(select)
    381     {
    382         var media = select.options[select.selectedIndex].value;
    383         WebInspector.overridesSupport.settings.emulatedCSSMedia.set(media);
    384     },
    385 
    386     __proto__: WebInspector.OverridesView.Tab.prototype
    387 }
    388 
    389 
    390 /**
    391  * @constructor
    392  * @extends {WebInspector.OverridesView.Tab}
    393  */
    394 WebInspector.OverridesView.NetworkTab = function()
    395 {
    396     WebInspector.OverridesView.Tab.call(this, "network", WebInspector.UIString("Network"), [], [this._userAgentOverrideEnabled.bind(this), this._networkThroughputIsLimited.bind(this)]);
    397     this.element.classList.add("overrides-network");
    398     this._createNetworkConditionsElement();
    399     this._createUserAgentSection();
    400 }
    401 
    402 WebInspector.OverridesView.NetworkTab.prototype = {
    403     /**
    404      * @return {boolean}
    405      */
    406     _networkThroughputIsLimited: function()
    407     {
    408         return WebInspector.overridesSupport.networkThroughputIsLimited();
    409     },
    410 
    411     _createNetworkConditionsElement: function()
    412     {
    413         var fieldsetElement = this.element.createChild("fieldset");
    414         fieldsetElement.createChild("span").textContent = WebInspector.UIString("Limit network throughput:");
    415         fieldsetElement.createChild("br");
    416         fieldsetElement.appendChild(WebInspector.OverridesUI.createNetworkConditionsSelect(document));
    417 
    418         WebInspector.overridesSupport.settings.networkConditions.addChangeListener(this.updateActiveState, this);
    419     },
    420 
    421     /**
    422      * @return {boolean}
    423      */
    424     _userAgentOverrideEnabled: function()
    425     {
    426         return !!WebInspector.overridesSupport.settings.userAgent.get();
    427     },
    428 
    429     _createUserAgentSection: function()
    430     {
    431         var fieldsetElement = this.element.createChild("fieldset");
    432         fieldsetElement.createChild("label").textContent = WebInspector.UIString("Spoof user agent:");
    433         var selectAndInput = WebInspector.OverridesUI.createUserAgentSelectAndInput(document);
    434         fieldsetElement.appendChild(selectAndInput.select);
    435         fieldsetElement.appendChild(selectAndInput.input);
    436 
    437         WebInspector.overridesSupport.settings.userAgent.addChangeListener(this.updateActiveState, this);
    438     },
    439 
    440     __proto__: WebInspector.OverridesView.Tab.prototype
    441 }
    442 
    443 
    444 /**
    445  * @constructor
    446  * @extends {WebInspector.OverridesView.Tab}
    447  */
    448 WebInspector.OverridesView.SensorsTab = function()
    449 {
    450     WebInspector.OverridesView.Tab.call(this, "sensors", WebInspector.UIString("Sensors"), [
    451         WebInspector.overridesSupport.settings.overrideGeolocation,
    452         WebInspector.overridesSupport.settings.overrideDeviceOrientation,
    453         WebInspector.overridesSupport.settings.emulateTouch
    454     ]);
    455 
    456     this.element.classList.add("overrides-sensors");
    457     this.registerRequiredCSS("accelerometer.css");
    458     this.element.appendChild(this._createSettingCheckbox(WebInspector.UIString("Emulate touch screen"), WebInspector.overridesSupport.settings.emulateTouch, undefined));
    459     this._appendGeolocationOverrideControl();
    460     this._apendDeviceOrientationOverrideControl();
    461 }
    462 
    463 WebInspector.OverridesView.SensorsTab.prototype = {
    464     _appendGeolocationOverrideControl: function()
    465     {
    466         const geolocationSetting = WebInspector.overridesSupport.settings.geolocationOverride.get();
    467         var geolocation = WebInspector.OverridesSupport.GeolocationPosition.parseSetting(geolocationSetting);
    468         this.element.appendChild(this._createSettingCheckbox(WebInspector.UIString("Emulate geolocation coordinates"), WebInspector.overridesSupport.settings.overrideGeolocation, this._geolocationOverrideCheckboxClicked.bind(this)));
    469         this.element.appendChild(this._createGeolocationOverrideElement(geolocation));
    470         this._geolocationOverrideCheckboxClicked(WebInspector.overridesSupport.settings.overrideGeolocation.get());
    471     },
    472 
    473     /**
    474      * @param {boolean} enabled
    475      */
    476     _geolocationOverrideCheckboxClicked: function(enabled)
    477     {
    478         if (enabled && !this._latitudeElement.value)
    479             this._latitudeElement.focus();
    480     },
    481 
    482     _applyGeolocationUserInput: function()
    483     {
    484         this._setGeolocationPosition(WebInspector.OverridesSupport.GeolocationPosition.parseUserInput(this._latitudeElement.value.trim(), this._longitudeElement.value.trim(), this._geolocationErrorElement.checked), true);
    485     },
    486 
    487     /**
    488      * @param {?WebInspector.OverridesSupport.GeolocationPosition} geolocation
    489      * @param {boolean} userInputModified
    490      */
    491     _setGeolocationPosition: function(geolocation, userInputModified)
    492     {
    493         if (!geolocation)
    494             return;
    495 
    496         if (!userInputModified) {
    497             this._latitudeElement.value = geolocation.latitude;
    498             this._longitudeElement.value = geolocation.longitude;
    499         }
    500 
    501         var value = geolocation.toSetting();
    502         WebInspector.overridesSupport.settings.geolocationOverride.set(value);
    503     },
    504 
    505     /**
    506      * @param {!WebInspector.OverridesSupport.GeolocationPosition} geolocation
    507      * @return {!Element}
    508      */
    509     _createGeolocationOverrideElement: function(geolocation)
    510     {
    511         var fieldsetElement = WebInspector.SettingsUI.createSettingFieldset(WebInspector.overridesSupport.settings.overrideGeolocation);
    512         fieldsetElement.id = "geolocation-override-section";
    513 
    514         var tableElement = fieldsetElement.createChild("table");
    515         var rowElement = tableElement.createChild("tr");
    516         var cellElement = rowElement.createChild("td");
    517         cellElement = rowElement.createChild("td");
    518         cellElement.createTextChild(WebInspector.UIString("Lat = "));
    519         this._latitudeElement = WebInspector.SettingsUI.createInput(cellElement, "geolocation-override-latitude", String(geolocation.latitude), this._applyGeolocationUserInput.bind(this), true);
    520         cellElement.createTextChild(" , ");
    521         cellElement.createTextChild(WebInspector.UIString("Lon = "));
    522         this._longitudeElement = WebInspector.SettingsUI.createInput(cellElement, "geolocation-override-longitude", String(geolocation.longitude), this._applyGeolocationUserInput.bind(this), true);
    523         rowElement = tableElement.createChild("tr");
    524         cellElement = rowElement.createChild("td");
    525         cellElement.colSpan = 2;
    526         var geolocationErrorLabelElement = document.createElement("label");
    527         var geolocationErrorCheckboxElement = geolocationErrorLabelElement.createChild("input");
    528         geolocationErrorCheckboxElement.id = "geolocation-error";
    529         geolocationErrorCheckboxElement.type = "checkbox";
    530         geolocationErrorCheckboxElement.checked = !geolocation || geolocation.error;
    531         geolocationErrorCheckboxElement.addEventListener("click", this._applyGeolocationUserInput.bind(this), false);
    532         geolocationErrorLabelElement.createTextChild(WebInspector.UIString("Emulate position unavailable"));
    533         this._geolocationErrorElement = geolocationErrorCheckboxElement;
    534         cellElement.appendChild(geolocationErrorLabelElement);
    535 
    536         return fieldsetElement;
    537     },
    538 
    539     _apendDeviceOrientationOverrideControl: function()
    540     {
    541         const deviceOrientationSetting = WebInspector.overridesSupport.settings.deviceOrientationOverride.get();
    542         var deviceOrientation = WebInspector.OverridesSupport.DeviceOrientation.parseSetting(deviceOrientationSetting);
    543         this.element.appendChild(this._createSettingCheckbox(WebInspector.UIString("Accelerometer"), WebInspector.overridesSupport.settings.overrideDeviceOrientation, this._deviceOrientationOverrideCheckboxClicked.bind(this)));
    544         this.element.appendChild(this._createDeviceOrientationOverrideElement(deviceOrientation));
    545         this._deviceOrientationOverrideCheckboxClicked(WebInspector.overridesSupport.settings.overrideDeviceOrientation.get());
    546     },
    547 
    548     /**
    549      * @param {boolean} enabled
    550      */
    551     _deviceOrientationOverrideCheckboxClicked: function(enabled)
    552     {
    553         if (enabled && !this._alphaElement.value)
    554             this._alphaElement.focus();
    555     },
    556 
    557     _applyDeviceOrientationUserInput: function()
    558     {
    559         this._setDeviceOrientation(WebInspector.OverridesSupport.DeviceOrientation.parseUserInput(this._alphaElement.value.trim(), this._betaElement.value.trim(), this._gammaElement.value.trim()), WebInspector.OverridesView.SensorsTab.DeviceOrientationModificationSource.UserInput);
    560     },
    561 
    562     _resetDeviceOrientation: function()
    563     {
    564         this._setDeviceOrientation(new WebInspector.OverridesSupport.DeviceOrientation(0, 0, 0), WebInspector.OverridesView.SensorsTab.DeviceOrientationModificationSource.ResetButton);
    565     },
    566 
    567     /**
    568      * @param {?WebInspector.OverridesSupport.DeviceOrientation} deviceOrientation
    569      * @param {!WebInspector.OverridesView.SensorsTab.DeviceOrientationModificationSource} modificationSource
    570      */
    571     _setDeviceOrientation: function(deviceOrientation, modificationSource)
    572     {
    573         if (!deviceOrientation)
    574             return;
    575 
    576         if (modificationSource != WebInspector.OverridesView.SensorsTab.DeviceOrientationModificationSource.UserInput) {
    577             this._alphaElement.value = deviceOrientation.alpha;
    578             this._betaElement.value = deviceOrientation.beta;
    579             this._gammaElement.value = deviceOrientation.gamma;
    580         }
    581 
    582         if (modificationSource != WebInspector.OverridesView.SensorsTab.DeviceOrientationModificationSource.UserDrag)
    583             this._setBoxOrientation(deviceOrientation);
    584 
    585         var value = deviceOrientation.toSetting();
    586         WebInspector.overridesSupport.settings.deviceOrientationOverride.set(value);
    587     },
    588 
    589     /**
    590      * @param {!Element} parentElement
    591      * @param {string} id
    592      * @param {string} label
    593      * @param {string} defaultText
    594      * @return {!Element}
    595      */
    596     _createAxisInput: function(parentElement, id, label, defaultText)
    597     {
    598         var div = parentElement.createChild("div", "accelerometer-axis-input-container");
    599         div.createTextChild(label);
    600         return WebInspector.SettingsUI.createInput(div, id, defaultText, this._applyDeviceOrientationUserInput.bind(this), true);
    601     },
    602 
    603     /**
    604      * @param {!WebInspector.OverridesSupport.DeviceOrientation} deviceOrientation
    605      */
    606     _createDeviceOrientationOverrideElement: function(deviceOrientation)
    607     {
    608         var fieldsetElement = WebInspector.SettingsUI.createSettingFieldset(WebInspector.overridesSupport.settings.overrideDeviceOrientation);
    609         fieldsetElement.id = "device-orientation-override-section";
    610         var tableElement = fieldsetElement.createChild("table");
    611         var rowElement = tableElement.createChild("tr");
    612         var cellElement = rowElement.createChild("td", "accelerometer-inputs-cell");
    613 
    614         this._alphaElement = this._createAxisInput(cellElement, "device-orientation-override-alpha", "\u03B1: ", String(deviceOrientation.alpha));
    615         this._betaElement = this._createAxisInput(cellElement, "device-orientation-override-beta", "\u03B2: ", String(deviceOrientation.beta));
    616         this._gammaElement = this._createAxisInput(cellElement, "device-orientation-override-gamma", "\u03B3: ", String(deviceOrientation.gamma));
    617 
    618         var resetButton = cellElement.createChild("button", "text-button accelerometer-reset-button");
    619         resetButton.textContent = WebInspector.UIString("Reset");
    620         resetButton.addEventListener("click", this._resetDeviceOrientation.bind(this), false);
    621 
    622         this._stageElement = rowElement.createChild("td","accelerometer-stage");
    623         this._boxElement = this._stageElement.createChild("section", "accelerometer-box");
    624 
    625         this._boxElement.createChild("section", "front");
    626         this._boxElement.createChild("section", "top");
    627         this._boxElement.createChild("section", "back");
    628         this._boxElement.createChild("section", "left");
    629         this._boxElement.createChild("section", "right");
    630         this._boxElement.createChild("section", "bottom");
    631 
    632         WebInspector.installDragHandle(this._stageElement, this._onBoxDragStart.bind(this), this._onBoxDrag.bind(this), this._onBoxDragEnd.bind(this), "move");
    633         this._setBoxOrientation(deviceOrientation);
    634         return fieldsetElement;
    635     },
    636 
    637     /**
    638      * @param {!WebInspector.OverridesSupport.DeviceOrientation} deviceOrientation
    639      */
    640     _setBoxOrientation: function(deviceOrientation)
    641     {
    642         var matrix = new WebKitCSSMatrix();
    643         this._boxMatrix = matrix.rotate(-deviceOrientation.beta, deviceOrientation.gamma, -deviceOrientation.alpha);
    644         this._boxElement.style.webkitTransform = this._boxMatrix.toString();
    645     },
    646 
    647     /**
    648      * @param {!MouseEvent} event
    649      * @return {boolean}
    650      */
    651     _onBoxDrag: function(event)
    652     {
    653         var mouseMoveVector = this._calculateRadiusVector(event.x, event.y);
    654         if (!mouseMoveVector)
    655             return true;
    656 
    657         event.consume(true);
    658         var axis = WebInspector.Geometry.crossProduct(this._mouseDownVector, mouseMoveVector);
    659         axis.normalize();
    660         var angle = WebInspector.Geometry.calculateAngle(this._mouseDownVector, mouseMoveVector);
    661         var matrix = new WebKitCSSMatrix();
    662         var rotationMatrix = matrix.rotateAxisAngle(axis.x, axis.y, axis.z, angle);
    663         this._currentMatrix = rotationMatrix.multiply(this._boxMatrix)
    664         this._boxElement.style.webkitTransform = this._currentMatrix;
    665         var eulerAngles = WebInspector.Geometry.EulerAngles.fromRotationMatrix(this._currentMatrix);
    666         var newOrientation = new WebInspector.OverridesSupport.DeviceOrientation(-eulerAngles.alpha, -eulerAngles.beta, eulerAngles.gamma);
    667         this._setDeviceOrientation(newOrientation, WebInspector.OverridesView.SensorsTab.DeviceOrientationModificationSource.UserDrag);
    668         return false;
    669     },
    670 
    671     /**
    672      * @param {!MouseEvent} event
    673      * @return {boolean}
    674      */
    675     _onBoxDragStart: function(event)
    676     {
    677         if (!WebInspector.overridesSupport.settings.overrideDeviceOrientation.get())
    678             return false;
    679 
    680         this._mouseDownVector = this._calculateRadiusVector(event.x, event.y);
    681 
    682         if (!this._mouseDownVector)
    683             return false;
    684 
    685         event.consume(true);
    686         return true;
    687     },
    688 
    689     _onBoxDragEnd: function()
    690     {
    691         this._boxMatrix = this._currentMatrix;
    692     },
    693 
    694     /**
    695      * @param {number} x
    696      * @param {number} y
    697      * @return {?WebInspector.Geometry.Vector}
    698      */
    699     _calculateRadiusVector: function(x, y)
    700     {
    701         var rect = this._stageElement.getBoundingClientRect();
    702         var radius = Math.max(rect.width, rect.height) / 2;
    703         var sphereX = (x - rect.left - rect.width / 2) / radius;
    704         var sphereY = (y - rect.top - rect.height / 2) / radius;
    705         var sqrSum = sphereX * sphereX + sphereY * sphereY;
    706         if (sqrSum > 0.5)
    707             return new WebInspector.Geometry.Vector(sphereX, sphereY, 0.5 / Math.sqrt(sqrSum));
    708 
    709         return new WebInspector.Geometry.Vector(sphereX, sphereY, Math.sqrt(1 - sqrSum));
    710     },
    711 
    712     __proto__ : WebInspector.OverridesView.Tab.prototype
    713 }
    714 
    715 /** @enum {string} */
    716 WebInspector.OverridesView.SensorsTab.DeviceOrientationModificationSource = {
    717     UserInput: "userInput",
    718     UserDrag: "userDrag",
    719     ResetButton: "resetButton"
    720 }
    721 
    722 /**
    723  * @constructor
    724  * @implements {WebInspector.Revealer}
    725  */
    726 WebInspector.OverridesView.Revealer = function()
    727 {
    728 }
    729 
    730 WebInspector.OverridesView.Revealer.prototype = {
    731     /**
    732      * @param {!Object} overridesSupport
    733      */
    734     reveal: function(overridesSupport)
    735     {
    736         WebInspector.inspectorView.showViewInDrawer("emulation");
    737     }
    738 }
    739