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