1 /* 2 * Copyright (C) 2012 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 /** 32 * @constructor 33 */ 34 WebInspector.OverridesSupport = function() 35 { 36 this._overridesActive = WebInspector.settings.enableOverridesOnStartup.get(); 37 this._updateAllOverrides(); 38 39 WebInspector.settings.overrideUserAgent.addChangeListener(this._userAgentChanged, this); 40 WebInspector.settings.userAgent.addChangeListener(this._userAgentChanged, this); 41 42 WebInspector.settings.overrideDeviceMetrics.addChangeListener(this._deviceMetricsChanged, this); 43 WebInspector.settings.deviceMetrics.addChangeListener(this._deviceMetricsChanged, this); 44 WebInspector.settings.deviceFitWindow.addChangeListener(this._deviceMetricsChanged, this); 45 46 WebInspector.settings.overrideGeolocation.addChangeListener(this._geolocationPositionChanged, this); 47 WebInspector.settings.geolocationOverride.addChangeListener(this._geolocationPositionChanged, this); 48 49 WebInspector.settings.overrideDeviceOrientation.addChangeListener(this._deviceOrientationChanged, this); 50 WebInspector.settings.deviceOrientationOverride.addChangeListener(this._deviceOrientationChanged, this); 51 52 WebInspector.settings.emulateTouchEvents.addChangeListener(this._emulateTouchEventsChanged, this); 53 54 WebInspector.settings.overrideCSSMedia.addChangeListener(this._cssMediaChanged, this); 55 WebInspector.settings.emulatedCSSMedia.addChangeListener(this._cssMediaChanged, this); 56 } 57 58 /** 59 * @constructor 60 * @param {number} width 61 * @param {number} height 62 * @param {number} fontScaleFactor 63 */ 64 WebInspector.OverridesSupport.DeviceMetrics = function(width, height, fontScaleFactor) 65 { 66 this.width = width; 67 this.height = height; 68 this.fontScaleFactor = fontScaleFactor; 69 } 70 71 /** 72 * @return {WebInspector.OverridesSupport.DeviceMetrics} 73 */ 74 WebInspector.OverridesSupport.DeviceMetrics.parseSetting = function(value) 75 { 76 if (value) { 77 var splitMetrics = value.split("x"); 78 if (splitMetrics.length === 3) 79 return new WebInspector.OverridesSupport.DeviceMetrics(parseInt(splitMetrics[0], 10), parseInt(splitMetrics[1], 10), parseFloat(splitMetrics[2])); 80 } 81 return new WebInspector.OverridesSupport.DeviceMetrics(0, 0, 1); 82 } 83 84 /** 85 * @return {?WebInspector.OverridesSupport.DeviceMetrics} 86 */ 87 WebInspector.OverridesSupport.DeviceMetrics.parseUserInput = function(widthString, heightString, fontScaleFactorString) 88 { 89 function isUserInputValid(value, isInteger) 90 { 91 if (!value) 92 return true; 93 return isInteger ? /^[0]*[1-9][\d]*$/.test(value) : /^[0]*([1-9][\d]*(\.\d+)?|\.\d+)$/.test(value); 94 } 95 96 if (!widthString ^ !heightString) 97 return null; 98 99 var isWidthValid = isUserInputValid(widthString, true); 100 var isHeightValid = isUserInputValid(heightString, true); 101 var isFontScaleFactorValid = isUserInputValid(fontScaleFactorString, false); 102 103 if (!isWidthValid && !isHeightValid && !isFontScaleFactorValid) 104 return null; 105 106 var width = isWidthValid ? parseInt(widthString || "0", 10) : -1; 107 var height = isHeightValid ? parseInt(heightString || "0", 10) : -1; 108 var fontScaleFactor = isFontScaleFactorValid ? parseFloat(fontScaleFactorString) : -1; 109 110 return new WebInspector.OverridesSupport.DeviceMetrics(width, height, fontScaleFactor); 111 } 112 113 WebInspector.OverridesSupport.DeviceMetrics.prototype = { 114 /** 115 * @return {boolean} 116 */ 117 isValid: function() 118 { 119 return this.isWidthValid() && this.isHeightValid() && this.isFontScaleFactorValid(); 120 }, 121 122 /** 123 * @return {boolean} 124 */ 125 isWidthValid: function() 126 { 127 return this.width >= 0; 128 }, 129 130 /** 131 * @return {boolean} 132 */ 133 isHeightValid: function() 134 { 135 return this.height >= 0; 136 }, 137 138 /** 139 * @return {boolean} 140 */ 141 isFontScaleFactorValid: function() 142 { 143 return this.fontScaleFactor > 0; 144 }, 145 146 /** 147 * @return {string} 148 */ 149 toSetting: function() 150 { 151 if (!this.isValid()) 152 return ""; 153 154 return this.width && this.height ? this.width + "x" + this.height + "x" + this.fontScaleFactor : ""; 155 }, 156 157 /** 158 * @return {string} 159 */ 160 widthToInput: function() 161 { 162 return this.isWidthValid() && this.width ? String(this.width) : ""; 163 }, 164 165 /** 166 * @return {string} 167 */ 168 heightToInput: function() 169 { 170 return this.isHeightValid() && this.height ? String(this.height) : ""; 171 }, 172 173 /** 174 * @return {string} 175 */ 176 fontScaleFactorToInput: function() 177 { 178 return this.isFontScaleFactorValid() && this.fontScaleFactor ? String(this.fontScaleFactor) : ""; 179 } 180 } 181 182 /** 183 * @constructor 184 * @param {number} latitude 185 * @param {number} longitude 186 */ 187 WebInspector.OverridesSupport.GeolocationPosition = function(latitude, longitude, error) 188 { 189 this.latitude = latitude; 190 this.longitude = longitude; 191 this.error = error; 192 } 193 194 WebInspector.OverridesSupport.GeolocationPosition.prototype = { 195 /** 196 * @return {string} 197 */ 198 toSetting: function() 199 { 200 return (typeof this.latitude === "number" && typeof this.longitude === "number" && typeof this.error === "string") ? this.latitude + "@" + this.longitude + ":" + this.error : ""; 201 } 202 } 203 204 /** 205 * @return {WebInspector.OverridesSupport.GeolocationPosition} 206 */ 207 WebInspector.OverridesSupport.GeolocationPosition.parseSetting = function(value) 208 { 209 if (value) { 210 var splitError = value.split(":"); 211 if (splitError.length === 2) { 212 var splitPosition = splitError[0].split("@") 213 if (splitPosition.length === 2) 214 return new WebInspector.OverridesSupport.GeolocationPosition(parseFloat(splitPosition[0]), parseFloat(splitPosition[1]), splitError[1]); 215 } 216 } 217 return new WebInspector.OverridesSupport.GeolocationPosition(0, 0, ""); 218 } 219 220 /** 221 * @return {?WebInspector.OverridesSupport.GeolocationPosition} 222 */ 223 WebInspector.OverridesSupport.GeolocationPosition.parseUserInput = function(latitudeString, longitudeString, errorStatus) 224 { 225 function isUserInputValid(value) 226 { 227 if (!value) 228 return true; 229 return /^[-]?[0-9]*[.]?[0-9]*$/.test(value); 230 } 231 232 if (!latitudeString ^ !latitudeString) 233 return null; 234 235 var isLatitudeValid = isUserInputValid(latitudeString); 236 var isLongitudeValid = isUserInputValid(longitudeString); 237 238 if (!isLatitudeValid && !isLongitudeValid) 239 return null; 240 241 var latitude = isLatitudeValid ? parseFloat(latitudeString) : -1; 242 var longitude = isLongitudeValid ? parseFloat(longitudeString) : -1; 243 244 return new WebInspector.OverridesSupport.GeolocationPosition(latitude, longitude, errorStatus ? "PositionUnavailable" : ""); 245 } 246 247 WebInspector.OverridesSupport.GeolocationPosition.clearGeolocationOverride = function() 248 { 249 PageAgent.clearGeolocationOverride(); 250 } 251 252 /** 253 * @constructor 254 * @param {number} alpha 255 * @param {number} beta 256 * @param {number} gamma 257 */ 258 WebInspector.OverridesSupport.DeviceOrientation = function(alpha, beta, gamma) 259 { 260 this.alpha = alpha; 261 this.beta = beta; 262 this.gamma = gamma; 263 } 264 265 WebInspector.OverridesSupport.DeviceOrientation.prototype = { 266 /** 267 * @return {string} 268 */ 269 toSetting: function() 270 { 271 return JSON.stringify(this); 272 } 273 } 274 275 /** 276 * @return {WebInspector.OverridesSupport.DeviceOrientation} 277 */ 278 WebInspector.OverridesSupport.DeviceOrientation.parseSetting = function(value) 279 { 280 if (value) { 281 var jsonObject = JSON.parse(value); 282 return new WebInspector.OverridesSupport.DeviceOrientation(jsonObject.alpha, jsonObject.beta, jsonObject.gamma); 283 } 284 return new WebInspector.OverridesSupport.DeviceOrientation(0, 0, 0); 285 } 286 287 /** 288 * @return {?WebInspector.OverridesSupport.DeviceOrientation} 289 */ 290 WebInspector.OverridesSupport.DeviceOrientation.parseUserInput = function(alphaString, betaString, gammaString) 291 { 292 function isUserInputValid(value) 293 { 294 if (!value) 295 return true; 296 return /^[-]?[0-9]*[.]?[0-9]*$/.test(value); 297 } 298 299 if (!alphaString ^ !betaString ^ !gammaString) 300 return null; 301 302 var isAlphaValid = isUserInputValid(alphaString); 303 var isBetaValid = isUserInputValid(betaString); 304 var isGammaValid = isUserInputValid(gammaString); 305 306 if (!isAlphaValid && !isBetaValid && !isGammaValid) 307 return null; 308 309 var alpha = isAlphaValid ? parseFloat(alphaString) : -1; 310 var beta = isBetaValid ? parseFloat(betaString) : -1; 311 var gamma = isGammaValid ? parseFloat(gammaString) : -1; 312 313 return new WebInspector.OverridesSupport.DeviceOrientation(alpha, beta, gamma); 314 } 315 316 WebInspector.OverridesSupport.DeviceOrientation.clearDeviceOrientationOverride = function() 317 { 318 PageAgent.clearDeviceOrientationOverride(); 319 } 320 321 WebInspector.OverridesSupport.prototype = { 322 setOverridesActive: function(enabled) 323 { 324 if (this._overridesActive === enabled) 325 return; 326 this._overridesActive = enabled; 327 328 this._updateAllOverrides(); 329 }, 330 331 _updateAllOverrides: function() 332 { 333 this._userAgentChanged(); 334 this._deviceMetricsChanged(); 335 this._deviceOrientationChanged(); 336 this._geolocationPositionChanged(); 337 this._emulateTouchEventsChanged(); 338 this._cssMediaChanged(); 339 }, 340 341 _userAgentChanged: function() 342 { 343 NetworkAgent.setUserAgentOverride(this._overridesActive && WebInspector.settings.overrideUserAgent.get() ? WebInspector.settings.userAgent.get() : ""); 344 }, 345 346 _deviceMetricsChanged: function() 347 { 348 var metrics = WebInspector.OverridesSupport.DeviceMetrics.parseSetting(this._overridesActive && WebInspector.settings.overrideDeviceMetrics.get() ? WebInspector.settings.deviceMetrics.get() : ""); 349 if (metrics.isValid()) 350 PageAgent.setDeviceMetricsOverride(metrics.width, metrics.height, metrics.fontScaleFactor, WebInspector.settings.deviceFitWindow.get()); 351 }, 352 353 _geolocationPositionChanged: function() 354 { 355 if (!this._overridesActive || !WebInspector.settings.overrideGeolocation.get()) { 356 PageAgent.clearGeolocationOverride(); 357 return; 358 } 359 var geolocation = WebInspector.OverridesSupport.GeolocationPosition.parseSetting(WebInspector.settings.geolocationOverride.get()); 360 if (geolocation.error) 361 PageAgent.setGeolocationOverride(); 362 else 363 PageAgent.setGeolocationOverride(geolocation.latitude, geolocation.longitude, 150); 364 }, 365 366 _deviceOrientationChanged: function() 367 { 368 if (!this._overridesActive || !WebInspector.settings.overrideDeviceOrientation.get()) { 369 PageAgent.clearDeviceOrientationOverride(); 370 return; 371 } 372 var deviceOrientation = WebInspector.OverridesSupport.DeviceOrientation.parseSetting(WebInspector.settings.deviceOrientationOverride.get()); 373 PageAgent.setDeviceOrientationOverride(deviceOrientation.alpha, deviceOrientation.beta, deviceOrientation.gamma); 374 }, 375 376 _emulateTouchEventsChanged: function() 377 { 378 WebInspector.domAgent.emulateTouchEventObjects(this._overridesActive && WebInspector.settings.emulateTouchEvents.get()); 379 }, 380 381 _cssMediaChanged: function() 382 { 383 PageAgent.setEmulatedMedia(this._overridesActive && WebInspector.settings.overrideCSSMedia.get() ? WebInspector.settings.emulatedCSSMedia.get() : ""); 384 WebInspector.cssModel.mediaQueryResultChanged(); 385 } 386 } 387 388 389 /** 390 * @type {WebInspector.OverridesSupport} 391 */ 392 WebInspector.overridesSupport; 393