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