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