blob: fa3753315366dcbe76c3b967633acf4aea9c2a60 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @constructor
*/
WebInspector.EmulatedDevice = function()
{
/** @type {string} */
this.title = "";
/** @type {string} */
this.type = WebInspector.EmulatedDevice.Type.Unknown;
/** @type {!WebInspector.EmulatedDevice.Orientation} */
this.vertical = {width: 0, height: 0, outlineInsets: null, outlineImages: null};
/** @type {!WebInspector.EmulatedDevice.Orientation} */
this.horizontal = {width: 0, height: 0, outlineInsets: null, outlineImages: null};
/** @type {number} */
this.deviceScaleFactor = 1;
/** @type {!Array.<string>} */
this.capabilities = [WebInspector.EmulatedDevice.Capability.Touch, WebInspector.EmulatedDevice.Capability.Mobile];
/** @type {string} */
this.userAgent = "";
/** @type {!Array.<!WebInspector.EmulatedDevice.Mode>} */
this.modes = [];
/** @type {string} */
this._show = WebInspector.EmulatedDevice._Show.Default;
/** @type {boolean} */
this._showByDefault = true;
/** @type {?Runtime.Extension} */
this._extension = null;
}
/** @typedef {!{title: string, orientation: string, insets: !Insets, images: ?WebInspector.EmulatedDevice.Images}} */
WebInspector.EmulatedDevice.Mode;
/** @typedef {!{width: number, height: number, outlineInsets: ?Insets, outlineImages: ?WebInspector.EmulatedDevice.Images}} */
WebInspector.EmulatedDevice.Orientation;
WebInspector.EmulatedDevice.Horizontal = "horizontal";
WebInspector.EmulatedDevice.Vertical = "vertical";
WebInspector.EmulatedDevice.Type = {
Phone: "phone",
Tablet: "tablet",
Notebook: "notebook",
Desktop: "desktop",
Unknown: "unknown"
}
WebInspector.EmulatedDevice.Capability = {
Touch: "touch",
Mobile: "mobile"
}
WebInspector.EmulatedDevice._Show = {
Always: "Always",
Default: "Default",
Never: "Never"
}
/**
* @param {*} json
* @return {?WebInspector.EmulatedDevice}
*/
WebInspector.EmulatedDevice.fromJSONV1 = function(json)
{
try {
/**
* @param {*} object
* @param {string} key
* @param {string} type
* @param {*=} defaultValue
* @return {*}
*/
function parseValue(object, key, type, defaultValue)
{
if (typeof object !== "object" || object === null || !object.hasOwnProperty(key)) {
if (typeof defaultValue !== "undefined")
return defaultValue;
throw new Error("Emulated device is missing required property '" + key + "'");
}
var value = object[key];
if (typeof value !== type || value === null)
throw new Error("Emulated device property '" + key + "' has wrong type '" + typeof value + "'");
return value;
}
/**
* @param {*} object
* @param {string} key
* @return {number}
*/
function parseIntValue(object, key)
{
var value = /** @type {number} */ (parseValue(object, key, "number"));
if (value !== Math.abs(value))
throw new Error("Emulated device value '" + key + "' must be integer");
return value;
}
/**
* @param {*} json
* @return {!Insets}
*/
function parseInsets(json)
{
return new Insets(parseIntValue(json, "left"), parseIntValue(json, "top"), parseIntValue(json, "right"), parseIntValue(json, "bottom"));
}
/**
* @param {*} json
* @return {!WebInspector.EmulatedDevice.Images}
*/
function parseImages(json)
{
if (!Array.isArray(json))
throw new Error("Emulated device images is not an array");
var result = new WebInspector.EmulatedDevice.Images();
for (var i = 0; i < json.length; ++i) {
var src = /** @type {string} */ (parseValue(json[i], "src", "string"));
var scale = /** @type {number} */ (parseValue(json[i], "scale", "number"));
if (scale <= 0)
throw new Error("Emulated device property image scale must be positive");
result.addSource(src, scale);
}
return result;
}
/**
* @param {*} json
* @return {!WebInspector.EmulatedDevice.Orientation}
*/
function parseOrientation(json)
{
var result = {};
result.width = parseIntValue(json, "width");
if (result.width < 0 || result.width > WebInspector.OverridesSupport.MaxDeviceSize)
throw new Error("Emulated device has wrong width: " + result.width);
result.height = parseIntValue(json, "height");
if (result.height < 0 || result.height > WebInspector.OverridesSupport.MaxDeviceSize)
throw new Error("Emulated device has wrong height: " + result.height);
var outlineInsets = parseValue(json["outline"], "insets", "object", null);
if (outlineInsets) {
result.outlineInsets = parseInsets(outlineInsets);
if (result.outlineInsets.left < 0 || result.outlineInsets.top < 0)
throw new Error("Emulated device has wrong outline insets");
result.outlineImages = parseImages(parseValue(json["outline"], "images", "object"));
}
return /** @type {!WebInspector.EmulatedDevice.Orientation} */ (result);
}
var result = new WebInspector.EmulatedDevice();
result.title = /** @type {string} */ (parseValue(json, "title", "string"));
result.type = /** @type {string} */ (parseValue(json, "type", "string"));
result.userAgent = /** @type {string} */ (parseValue(json, "user-agent", "string"));
var capabilities = parseValue(json, "capabilities", "object", []);
if (!Array.isArray(capabilities))
throw new Error("Emulated device capabilities must be an array");
result.capabilities = [];
for (var i = 0; i < capabilities.length; ++i) {
if (typeof capabilities[i] !== "string")
throw new Error("Emulated device capability must be a string");
result.capabilities.push(capabilities[i]);
}
result.deviceScaleFactor = /** @type {number} */ (parseValue(json["screen"], "device-pixel-ratio", "number"));
if (result.deviceScaleFactor < 0 || result.deviceScaleFactor > 100)
throw new Error("Emulated device has wrong deviceScaleFactor: " + result.deviceScaleFactor);
result.vertical = parseOrientation(parseValue(json["screen"], "vertical", "object"));
result.horizontal = parseOrientation(parseValue(json["screen"], "horizontal", "object"));
var modes = parseValue(json, "modes", "object", []);
if (!Array.isArray(modes))
throw new Error("Emulated device modes must be an array");
result.modes = [];
for (var i = 0; i < modes.length; ++i) {
var mode = {};
mode.title = /** @type {string} */ (parseValue(modes[i], "title", "string"));
mode.orientation = /** @type {string} */ (parseValue(modes[i], "orientation", "string"));
if (mode.orientation !== WebInspector.EmulatedDevice.Vertical && mode.orientation !== WebInspector.EmulatedDevice.Horizontal)
throw new Error("Emulated device mode has wrong orientation '" + mode.orientation + "'");
var orientation = result.orientationByName(mode.orientation);
mode.insets = parseInsets(parseValue(modes[i], "insets", "object"));
if (mode.insets.top < 0 || mode.insets.left < 0 || mode.insets.right < 0 || mode.insets.bottom < 0 ||
mode.insets.top + mode.insets.bottom > orientation.height || mode.insets.left + mode.insets.right > orientation.width) {
throw new Error("Emulated device mode '" + mode.title + "'has wrong mode insets");
}
if (modes[i].hasOwnProperty("images"))
mode.images = parseImages(parseValue(modes[i], "images", "object"));
result.modes.push(mode);
}
result._showByDefault = /** @type {boolean} */ (parseValue(json, "show-by-default", "boolean", true));
result._show = /** @type {string} */ (parseValue(json, "show", "string", WebInspector.EmulatedDevice._Show.Default));
return result;
} catch (e) {
WebInspector.console.error("Failed to update emulated device list. " + String(e));
return null;
}
}
/**
* @param {!WebInspector.OverridesSupport.Device} device
* @param {string} title
* @param {string=} type
* @return {!WebInspector.EmulatedDevice}
*/
WebInspector.EmulatedDevice.fromOverridesDevice = function(device, title, type)
{
var result = new WebInspector.EmulatedDevice();
result.title = title;
result.type = type || WebInspector.EmulatedDevice.Type.Unknown;
result.vertical.width = device.width;
result.vertical.height = device.height;
result.horizontal.width = device.height;
result.horizontal.height = device.width;
result.deviceScaleFactor = device.deviceScaleFactor;
result.userAgent = device.userAgent;
result.capabilities = [];
if (device.touch)
result.capabilities.push(WebInspector.EmulatedDevice.Capability.Touch);
if (device.mobile)
result.capabilities.push(WebInspector.EmulatedDevice.Capability.Mobile);
return result;
}
/**
* @param {!WebInspector.EmulatedDevice} device1
* @param {!WebInspector.EmulatedDevice} device2
* @return {number}
*/
WebInspector.EmulatedDevice.compareByTitle = function(device1, device2)
{
return device1.title < device2.title ? -1 : (device1.title > device2.title ? 1 : 0);
}
WebInspector.EmulatedDevice.prototype = {
/**
* @return {?Runtime.Extension}
*/
extension: function()
{
return this._extension;
},
/**
* @param {?Runtime.Extension} extension
*/
setExtension: function(extension)
{
this._extension = extension;
},
/**
* @param {string} orientation
* @return {!Array.<!WebInspector.EmulatedDevice.Mode>}
*/
modesForOrientation: function(orientation)
{
var result = [];
for (var index = 0; index < this.modes.length; index++) {
if (this.modes[index].orientation === orientation)
result.push(this.modes[index]);
}
return result;
},
/**
* @return {*}
*/
_toJSON: function()
{
var json = {};
json["title"] = this.title;
json["type"] = this.type;
json["user-agent"] = this.userAgent;
json["capabilities"] = this.capabilities;
json["screen"] = {};
json["screen"]["device-pixel-ratio"] = this.deviceScaleFactor;
json["screen"]["vertical"] = this._orientationToJSON(this.vertical);
json["screen"]["horizontal"] = this._orientationToJSON(this.horizontal);
json["modes"] = [];
for (var i = 0; i < this.modes.length; ++i) {
var mode = {};
mode["title"] = this.modes[i].title;
mode["orientation"] = this.modes[i].orientation;
mode["insets"] = {};
mode["insets"]["left"] = this.modes[i].insets.left;
mode["insets"]["top"] = this.modes[i].insets.top;
mode["insets"]["right"] = this.modes[i].insets.right;
mode["insets"]["bottom"] = this.modes[i].insets.bottom;
if (this.modes[i].images)
mode["images"] = this.modes[i].images._toJSON();
json["modes"].push(mode);
}
json["show-by-default"] = this._showByDefault;
json["show"] = this._show;
return json;
},
/**
* @param {!WebInspector.EmulatedDevice.Orientation} orientation
* @return {*}
*/
_orientationToJSON: function(orientation)
{
var json = {};
json["width"] = orientation.width;
json["height"] = orientation.height;
if (orientation.outlineInsets) {
json["outline"] = {};
json["outline"]["insets"] = {};
json["outline"]["insets"]["left"] = orientation.outlineInsets.left;
json["outline"]["insets"]["top"] = orientation.outlineInsets.top;
json["outline"]["insets"]["right"] = orientation.outlineInsets.right;
json["outline"]["insets"]["bottom"] = orientation.outlineInsets.bottom;
json["outline"]["images"] = orientation.outlineImages._toJSON();
}
return json;
},
/**
* @param {!WebInspector.EmulatedDevice.Mode} mode
* @return {!WebInspector.OverridesSupport.Device}
*/
modeToOverridesDevice: function(mode)
{
var result = {};
var orientation = this.orientationByName(mode.orientation);
result.width = orientation.width - mode.insets.left - mode.insets.right;
result.height = orientation.height - mode.insets.top - mode.insets.bottom;
result.deviceScaleFactor = this.deviceScaleFactor;
result.userAgent = this.userAgent;
result.touch = this.touch();
result.mobile = this.mobile();
return result;
},
/**
* @param {string} name
* @return {!WebInspector.EmulatedDevice.Orientation}
*/
orientationByName: function(name)
{
return name === WebInspector.EmulatedDevice.Vertical ? this.vertical : this.horizontal;
},
/**
* @return {boolean}
*/
show: function()
{
if (this._show === WebInspector.EmulatedDevice._Show.Default)
return this._showByDefault;
return this._show === WebInspector.EmulatedDevice._Show.Always;
},
/**
* @param {boolean} show
*/
setShow: function(show)
{
this._show = show ? WebInspector.EmulatedDevice._Show.Always : WebInspector.EmulatedDevice._Show.Never;
},
/**
* @param {!WebInspector.EmulatedDevice} other
*/
copyShowFrom: function(other)
{
this._show = other._show;
},
/**
* @return {boolean}
*/
touch: function()
{
return this.capabilities.indexOf(WebInspector.EmulatedDevice.Capability.Touch) !== -1;
},
/**
* @return {boolean}
*/
mobile: function()
{
return this.capabilities.indexOf(WebInspector.EmulatedDevice.Capability.Mobile) !== -1;
}
}
/**
* @constructor
* @extends {WebInspector.Object}
*/
WebInspector.EmulatedDevice.Images = function()
{
WebInspector.Object.call(this);
this._sources = [];
this._scales = [];
}
WebInspector.EmulatedDevice.Images.prototype = {
/**
* @return {*}
*/
_toJSON: function()
{
var result = [];
for (var i = 0; i < this._sources.length; ++i)
result.push({src: this._sources[i], scale: this._scales[i]});
return result;
},
/**
* @param {string} src
* @param {number} scale
*/
addSource: function(src, scale)
{
this._sources.push(src);
this._scales.push(scale);
},
__proto__: WebInspector.Object.prototype
}
/**
* @constructor
* @extends {WebInspector.Object}
*/
WebInspector.EmulatedDevicesList = function()
{
WebInspector.Object.call(this);
WebInspector.settings.createSetting("standardEmulatedDeviceList", []).remove();
/** @type {!WebInspector.Setting} */
this._standardSetting = WebInspector.settings.createSetting("standardEmulatedDeviceList", []);
/** @type {!Array.<!WebInspector.EmulatedDevice>} */
this._standard = this._listFromJSONV1(this._standardSetting.get());
this._updateStandardDevices();
/** @type {!WebInspector.Setting} */
this._customSetting = WebInspector.settings.createSetting("customEmulatedDeviceList", []);
/** @type {!Array.<!WebInspector.EmulatedDevice>} */
this._custom = this._listFromJSONV1(this._customSetting.get());
}
WebInspector.EmulatedDevicesList.Events = {
CustomDevicesUpdated: "CustomDevicesUpdated",
StandardDevicesUpdated: "StandardDevicesUpdated"
}
WebInspector.EmulatedDevicesList.prototype = {
_updateStandardDevices: function()
{
var devices = [];
var extensions = self.runtime.extensions("emulated-device");
for (var i = 0; i < extensions.length; ++i) {
var device = WebInspector.EmulatedDevice.fromJSONV1(extensions[i].descriptor()["device"]);
device.setExtension(extensions[i]);
devices.push(device);
}
this._copyShowValues(this._standard, devices);
this._standard = devices;
this.saveStandardDevices();
},
/**
* @param {!Array.<*>} jsonArray
* @return {!Array.<!WebInspector.EmulatedDevice>}
*/
_listFromJSONV1: function(jsonArray)
{
var result = [];
if (!Array.isArray(jsonArray))
return result;
for (var i = 0; i < jsonArray.length; ++i) {
var device = WebInspector.EmulatedDevice.fromJSONV1(jsonArray[i]);
if (device) {
result.push(device);
if (!device.modes.length) {
device.modes.push({title: "", orientation: WebInspector.EmulatedDevice.Horizontal, insets: new Insets(0, 0, 0, 0), images: null});
device.modes.push({title: "", orientation: WebInspector.EmulatedDevice.Vertical, insets: new Insets(0, 0, 0, 0), images: null});
}
}
}
return result;
},
/**
* @return {!Array.<!WebInspector.EmulatedDevice>}
*/
standard: function()
{
return this._standard;
},
/**
* @return {!Array.<!WebInspector.EmulatedDevice>}
*/
custom: function()
{
return this._custom;
},
/**
* @param {!WebInspector.EmulatedDevice} device
*/
addCustomDevice: function(device)
{
this._custom.push(device);
this.saveCustomDevices();
},
/**
* @param {!WebInspector.EmulatedDevice} device
*/
removeCustomDevice: function(device)
{
this._custom.remove(device);
this.saveCustomDevices();
},
saveCustomDevices: function()
{
var json = this._custom.map(/** @param {!WebInspector.EmulatedDevice} device */ function(device) { return device._toJSON(); });
this._customSetting.set(json);
this.dispatchEventToListeners(WebInspector.EmulatedDevicesList.Events.CustomDevicesUpdated);
},
saveStandardDevices: function()
{
var json = this._standard.map(/** @param {!WebInspector.EmulatedDevice} device */ function(device) { return device._toJSON(); });
this._standardSetting.set(json);
this.dispatchEventToListeners(WebInspector.EmulatedDevicesList.Events.StandardDevicesUpdated);
},
/**
* @param {!Array.<!WebInspector.EmulatedDevice>} from
* @param {!Array.<!WebInspector.EmulatedDevice>} to
*/
_copyShowValues: function(from, to)
{
var deviceById = new Map();
for (var i = 0; i < from.length; ++i)
deviceById.set(from[i].title, from[i]);
for (var i = 0; i < to.length; ++i) {
var title = to[i].title;
if (deviceById.has(title))
to[i].copyShowFrom(/** @type {!WebInspector.EmulatedDevice} */ (deviceById.get(title)));
}
},
__proto__: WebInspector.Object.prototype
}
/** @type {!WebInspector.EmulatedDevicesList} */
WebInspector.emulatedDevicesList;