mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 02:14:43 +00:00
Bug 1090949
- Make WebIDE's Firefox OS Simulators configurable. r=ochameau
This commit is contained in:
parent
24089aabe1
commit
7afd2ee815
@ -95,6 +95,7 @@ function close(panel) {
|
||||
// when quitting the host application while a panel is visible. To suppress
|
||||
// these errors, check for "hidePopup" in panel before calling it.
|
||||
// It's not clear if there's an issue or it's expected behavior.
|
||||
// See Bug 1151796.
|
||||
|
||||
return panel.hidePopup && panel.hidePopup();
|
||||
}
|
||||
|
@ -31,3 +31,5 @@ webide.jar:
|
||||
content/project-listing.xhtml (project-listing.xhtml)
|
||||
content/project-listing.js (project-listing.js)
|
||||
content/project-panel.js (project-panel.js)
|
||||
content/simulator.js (simulator.js)
|
||||
content/simulator.xhtml (simulator.xhtml)
|
||||
|
320
browser/devtools/webide/content/simulator.js
Normal file
320
browser/devtools/webide/content/simulator.js
Normal file
@ -0,0 +1,320 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const Cu = Components.utils;
|
||||
|
||||
const { require } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools;
|
||||
const { GetDevices, GetDeviceString } = require("devtools/shared/devices");
|
||||
const { Services } = Cu.import("resource://gre/modules/Services.jsm");
|
||||
const { Simulators, Simulator } = require("devtools/webide/simulators");
|
||||
const EventEmitter = require('devtools/toolkit/event-emitter');
|
||||
const promise = require("promise");
|
||||
const utils = require("devtools/webide/utils");
|
||||
|
||||
const Strings = Services.strings.createBundle("chrome://browser/locale/devtools/webide.properties");
|
||||
|
||||
let SimulatorEditor = {
|
||||
|
||||
// Available Firefox OS Simulator addons (key: `addon.id`).
|
||||
_addons: {},
|
||||
|
||||
// Available device simulation profiles (key: `device.name`).
|
||||
_devices: {},
|
||||
|
||||
// The names of supported simulation options.
|
||||
_deviceOptions: [],
|
||||
|
||||
// The <form> element used to edit Simulator options.
|
||||
_form: null,
|
||||
|
||||
// The Simulator object being edited.
|
||||
_simulator: null,
|
||||
|
||||
// Generate the dynamic form elements.
|
||||
init() {
|
||||
let promises = [];
|
||||
|
||||
// Grab the <form> element.
|
||||
let form = this._form;
|
||||
if (!form) {
|
||||
// This is the first time we run `init()`, bootstrap some things.
|
||||
form = this._form = document.querySelector("#simulator-editor");
|
||||
form.addEventListener("change", this.update.bind(this));
|
||||
Simulators.on("configure", (e, simulator) => { this.edit(simulator) });
|
||||
// Extract the list of device simulation options we'll support.
|
||||
let deviceFields = form.querySelectorAll("*[data-device]");
|
||||
this._deviceOptions = [].map.call(deviceFields, field => field.name);
|
||||
}
|
||||
|
||||
// Append a new <option> to a <select> (or <optgroup>) element.
|
||||
function opt(select, value, text) {
|
||||
let option = document.createElement("option");
|
||||
option.value = value;
|
||||
option.textContent = text;
|
||||
select.appendChild(option);
|
||||
}
|
||||
|
||||
// Generate B2G version selector.
|
||||
promises.push(Simulators.findSimulatorAddons().then(addons => {
|
||||
this._addons = {};
|
||||
form.version.innerHTML = "";
|
||||
form.version.classList.remove("custom");
|
||||
addons.forEach(addon => {
|
||||
this._addons[addon.id] = addon;
|
||||
opt(form.version, addon.id, addon.name);
|
||||
});
|
||||
opt(form.version, "custom", "");
|
||||
opt(form.version, "pick", Strings.GetStringFromName("simulator_custom_binary"));
|
||||
}));
|
||||
|
||||
// Generate profile selector.
|
||||
form.profile.innerHTML = "";
|
||||
form.profile.classList.remove("custom");
|
||||
opt(form.profile, "default", Strings.GetStringFromName("simulator_default_profile"));
|
||||
opt(form.profile, "custom", "");
|
||||
opt(form.profile, "pick", Strings.GetStringFromName("simulator_custom_profile"));
|
||||
|
||||
// Generate example devices list.
|
||||
form.device.innerHTML = "";
|
||||
form.device.classList.remove("custom");
|
||||
opt(form.device, "custom", Strings.GetStringFromName("simulator_custom_device"));
|
||||
promises.push(GetDevices().then(devices => {
|
||||
devices.TYPES.forEach(type => {
|
||||
let b2gDevices = devices[type].filter(d => d.firefoxOS);
|
||||
if (b2gDevices.length < 1) {
|
||||
return;
|
||||
}
|
||||
let optgroup = document.createElement("optgroup");
|
||||
optgroup.label = GetDeviceString(type);
|
||||
b2gDevices.forEach(device => {
|
||||
this._devices[device.name] = device;
|
||||
opt(optgroup, device.name, device.name);
|
||||
});
|
||||
form.device.appendChild(optgroup);
|
||||
});
|
||||
}));
|
||||
|
||||
return promise.all(promises);
|
||||
},
|
||||
|
||||
// Edit the configuration of an existing Simulator, or create a new one.
|
||||
edit(simulator) {
|
||||
// If no Simulator was given to edit, we're creating a new one.
|
||||
if (!simulator) {
|
||||
simulator = new Simulator(); // Default options.
|
||||
Simulators.add(simulator);
|
||||
}
|
||||
|
||||
this._simulator = null;
|
||||
|
||||
return this.init().then(() => {
|
||||
this._simulator = simulator;
|
||||
|
||||
// Update the form fields.
|
||||
this._form.name.value = simulator.name;
|
||||
this.updateVersionSelector();
|
||||
this.updateProfileSelector();
|
||||
this.updateDeviceSelector();
|
||||
this.updateDeviceFields();
|
||||
});
|
||||
},
|
||||
|
||||
// Close the configuration panel.
|
||||
close() {
|
||||
this._simulator = null;
|
||||
window.parent.UI.openProject();
|
||||
},
|
||||
|
||||
// Restore the simulator to its default configuration.
|
||||
restoreDefaults() {
|
||||
let simulator = this._simulator;
|
||||
this.version = simulator.addon.id;
|
||||
this.profile = "default";
|
||||
simulator.restoreDefaults();
|
||||
Simulators.emitUpdated();
|
||||
return this.edit(simulator);
|
||||
},
|
||||
|
||||
// Delete this simulator.
|
||||
deleteSimulator() {
|
||||
Simulators.remove(this._simulator);
|
||||
this.close();
|
||||
},
|
||||
|
||||
// Select an available option, or set the "custom" option.
|
||||
updateSelector(selector, value) {
|
||||
selector.value = value;
|
||||
if (selector[selector.selectedIndex].value !== value) {
|
||||
selector.value = "custom";
|
||||
selector.classList.add("custom");
|
||||
selector[selector.selectedIndex].textContent = value;
|
||||
}
|
||||
},
|
||||
|
||||
// VERSION: Can be an installed `addon.id` or a custom binary path.
|
||||
|
||||
get version() {
|
||||
return this._simulator.options.b2gBinary || this._simulator.addon.id;
|
||||
},
|
||||
|
||||
set version(value) {
|
||||
let form = this._form;
|
||||
let simulator = this._simulator;
|
||||
let oldVer = simulator.version;
|
||||
if (this._addons[value]) {
|
||||
// `value` is a simulator addon ID.
|
||||
simulator.addon = this._addons[value];
|
||||
simulator.options.b2gBinary = null;
|
||||
} else {
|
||||
// `value` is a custom binary path.
|
||||
simulator.options.b2gBinary = value;
|
||||
// TODO (Bug 1146531) Indicate that a custom profile is now required.
|
||||
}
|
||||
// If `form.name` contains the old version, update its last occurrence.
|
||||
if (form.name.value.contains(oldVer) && simulator.version !== oldVer) {
|
||||
let regex = new RegExp("(.*)" + oldVer);
|
||||
let name = form.name.value.replace(regex, "$1" + simulator.version);
|
||||
simulator.options.name = form.name.value = Simulators.uniqueName(name);
|
||||
}
|
||||
},
|
||||
|
||||
updateVersionSelector() {
|
||||
this.updateSelector(this._form.version, this.version);
|
||||
},
|
||||
|
||||
// PROFILE. Can be "default" or a custom profile directory path.
|
||||
|
||||
get profile() {
|
||||
return this._simulator.options.gaiaProfile || "default";
|
||||
},
|
||||
|
||||
set profile(value) {
|
||||
this._simulator.options.gaiaProfile = (value == "default" ? null : value);
|
||||
},
|
||||
|
||||
updateProfileSelector() {
|
||||
this.updateSelector(this._form.profile, this.profile);
|
||||
},
|
||||
|
||||
// DEVICE. Can be an existing `device.name` or "custom".
|
||||
|
||||
get device() {
|
||||
let devices = this._devices;
|
||||
let simulator = this._simulator;
|
||||
|
||||
// Search for the name of a device matching current simulator options.
|
||||
for (let name in devices) {
|
||||
let match = true;
|
||||
for (let option of this._deviceOptions) {
|
||||
if (simulator.options[option] === devices[name][option]) {
|
||||
continue;
|
||||
}
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
if (match) {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
return "custom";
|
||||
},
|
||||
|
||||
set device(name) {
|
||||
let device = this._devices[name];
|
||||
if (!device) {
|
||||
return;
|
||||
}
|
||||
let form = this._form;
|
||||
let simulator = this._simulator;
|
||||
this._deviceOptions.forEach(option => {
|
||||
simulator.options[option] = form[option].value = device[option] || null;
|
||||
});
|
||||
// TODO (Bug 1146531) Indicate when a custom profile is required (e.g. for
|
||||
// tablet, TV…).
|
||||
},
|
||||
|
||||
updateDeviceSelector() {
|
||||
this.updateSelector(this._form.device, this.device);
|
||||
},
|
||||
|
||||
// Erase any current values, trust only the `simulator.options`.
|
||||
updateDeviceFields() {
|
||||
let form = this._form;
|
||||
let simulator = this._simulator;
|
||||
this._deviceOptions.forEach(option => {
|
||||
form[option].value = simulator.options[option];
|
||||
});
|
||||
},
|
||||
|
||||
// Handle a change in our form's fields.
|
||||
update(event) {
|
||||
let simulator = this._simulator;
|
||||
if (!simulator) {
|
||||
return;
|
||||
}
|
||||
let form = this._form;
|
||||
let input = event.target;
|
||||
switch (input.name) {
|
||||
case "name":
|
||||
simulator.options.name = input.value;
|
||||
break;
|
||||
case "version":
|
||||
switch (input.value) {
|
||||
case "pick":
|
||||
let file = utils.getCustomBinary(window);
|
||||
if (file) {
|
||||
this.version = file.path;
|
||||
}
|
||||
// Whatever happens, don't stay on the "pick" option.
|
||||
this.updateVersionSelector();
|
||||
break;
|
||||
case "custom":
|
||||
this.version = input[input.selectedIndex].textContent;
|
||||
break;
|
||||
default:
|
||||
this.version = input.value;
|
||||
}
|
||||
break;
|
||||
case "profile":
|
||||
switch (input.value) {
|
||||
case "pick":
|
||||
let directory = utils.getCustomProfile(window);
|
||||
if (directory) {
|
||||
this.profile = directory.path;
|
||||
}
|
||||
// Whatever happens, don't stay on the "pick" option.
|
||||
this.updateProfileSelector();
|
||||
break;
|
||||
case "custom":
|
||||
this.profile = input[input.selectedIndex].textContent;
|
||||
break;
|
||||
default:
|
||||
this.profile = input.value;
|
||||
}
|
||||
break;
|
||||
case "device":
|
||||
this.device = input.value;
|
||||
break;
|
||||
default:
|
||||
simulator.options[input.name] = input.value || null;
|
||||
this.updateDeviceSelector();
|
||||
}
|
||||
Simulators.emitUpdated();
|
||||
},
|
||||
};
|
||||
|
||||
window.addEventListener("load", function onLoad() {
|
||||
document.querySelector("#close").onclick = e => {
|
||||
SimulatorEditor.close();
|
||||
};
|
||||
document.querySelector("#reset").onclick = e => {
|
||||
SimulatorEditor.restoreDefaults();
|
||||
};
|
||||
document.querySelector("#remove").onclick = e => {
|
||||
SimulatorEditor.deleteSimulator();
|
||||
};
|
||||
|
||||
// We just loaded, so we probably missed the first configure request.
|
||||
SimulatorEditor.edit(Simulators._lastConfiguredSimulator);
|
||||
});
|
82
browser/devtools/webide/content/simulator.xhtml
Normal file
82
browser/devtools/webide/content/simulator.xhtml
Normal file
@ -0,0 +1,82 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!DOCTYPE html [
|
||||
<!ENTITY % webideDTD SYSTEM "chrome://browser/locale/devtools/webide.dtd" >
|
||||
%webideDTD;
|
||||
]>
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta charset="utf8"/>
|
||||
<link rel="stylesheet" href="chrome://webide/skin/deck.css" type="text/css"/>
|
||||
<link rel="stylesheet" href="chrome://webide/skin/simulator.css" type="text/css"/>
|
||||
<script type="application/javascript;version=1.8" src="chrome://webide/content/simulator.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="controls">
|
||||
<a id="remove" class="hidden">&simulator_remove;</a>
|
||||
<a id="reset">&simulator_reset;</a>
|
||||
<a id="close">&deck_close;</a>
|
||||
</div>
|
||||
|
||||
<form id="simulator-editor">
|
||||
|
||||
<h1>&simulator_title;</h1>
|
||||
|
||||
<h2>&simulator_software;</h2>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<label>
|
||||
<span class="label">&simulator_name;</span>
|
||||
<input type="text" name="name"/>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label>
|
||||
<span class="label">&simulator_version;</span>
|
||||
<select name="version"/>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label>
|
||||
<span class="label">&simulator_profile;</span>
|
||||
<select name="profile"/>
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2>&simulator_hardware;</h2>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<label>
|
||||
<span class="label">&simulator_device;</span>
|
||||
<select name="device"/>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label>
|
||||
<span class="label">&simulator_screenSize;</span>
|
||||
<input name="width" data-device="" type="number"/>
|
||||
<span>×</span>
|
||||
<input name="height" data-device="" type="number"/>
|
||||
</label>
|
||||
</li>
|
||||
<li class="hidden">
|
||||
<label>
|
||||
<span class="label">&simulator_pixelRatio;</span>
|
||||
<input name="pixelRatio" data-device="" type="number" step="0.05"/>
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</form>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -25,6 +25,7 @@ const Telemetry = require("devtools/shared/telemetry");
|
||||
const {RuntimeScanners, WiFiScanner} = require("devtools/webide/runtimes");
|
||||
const {showDoorhanger} = require("devtools/shared/doorhanger");
|
||||
const ProjectList = require("devtools/webide/project-list");
|
||||
const {Simulators} = require("devtools/webide/simulators");
|
||||
|
||||
const Strings = Services.strings.createBundle("chrome://browser/locale/devtools/webide.properties");
|
||||
|
||||
@ -37,6 +38,7 @@ const MIN_ZOOM = 0.6;
|
||||
// Download remote resources early
|
||||
getJSON("devtools.webide.addonsURL", true);
|
||||
getJSON("devtools.webide.templatesURL", true);
|
||||
getJSON("devtools.devices.url", true);
|
||||
|
||||
// See bug 989619
|
||||
console.log = console.log.bind(console);
|
||||
@ -117,12 +119,16 @@ let UI = {
|
||||
this.contentViewer.fullZoom = Services.prefs.getCharPref("devtools.webide.zoom");
|
||||
|
||||
gDevToolsBrowser.isWebIDEInitialized.resolve();
|
||||
|
||||
this.configureSimulator = this.configureSimulator.bind(this);
|
||||
Simulators.on("configure", this.configureSimulator);
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
window.removeEventListener("focus", this.onfocus, true);
|
||||
AppManager.off("app-manager-update", this.appManagerUpdate);
|
||||
AppManager.destroy();
|
||||
Simulators.off("configure", this.configureSimulator);
|
||||
projectList = null;
|
||||
window.removeEventListener("message", this.onMessage);
|
||||
this.updateConnectionTelemetry();
|
||||
@ -230,6 +236,10 @@ let UI = {
|
||||
this._updatePromise = promise.resolve();
|
||||
},
|
||||
|
||||
configureSimulator: function(event, simulator) {
|
||||
UI.selectDeckPanel("simulator");
|
||||
},
|
||||
|
||||
openInBrowser: function(url) {
|
||||
// Open a URL in a Firefox window
|
||||
let browserWin = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
@ -255,7 +265,8 @@ let UI = {
|
||||
hidePanels: function() {
|
||||
let panels = document.querySelectorAll("panel");
|
||||
for (let p of panels) {
|
||||
p.hidePopup();
|
||||
// Sometimes in tests, p.hidePopup is not defined - Bug 1151796.
|
||||
p.hidePopup && p.hidePopup();
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -211,11 +211,11 @@
|
||||
<iframe id="deck-panel-devicepreferences" flex="1" lazysrc="devicepreferences.xhtml"/>
|
||||
<iframe id="deck-panel-devicesettings" flex="1" lazysrc="devicesettings.xhtml"/>
|
||||
<iframe id="deck-panel-logs" flex="1" src="logs.xhtml"/>
|
||||
<iframe id="deck-panel-simulator" flex="1" lazysrc="simulator.xhtml"/>
|
||||
</deck>
|
||||
</hbox>
|
||||
<splitter hidden="true" class="devtools-horizontal-splitter" orient="vertical"/>
|
||||
<!-- toolbox iframe will be inserted here -->
|
||||
</notificationbox>
|
||||
|
||||
|
||||
</window>
|
||||
|
@ -212,7 +212,7 @@ let SimulatorScanner = {
|
||||
},
|
||||
|
||||
_updateRuntimes() {
|
||||
Simulators.getAll().then(simulators => {
|
||||
Simulators.findSimulators().then(simulators => {
|
||||
this._runtimes = [];
|
||||
for (let simulator of simulators) {
|
||||
this._runtimes.push(new SimulatorRuntime(simulator));
|
||||
@ -572,6 +572,9 @@ SimulatorRuntime.prototype = {
|
||||
connection.connect();
|
||||
});
|
||||
},
|
||||
configure() {
|
||||
Simulators.emit("configure", this.simulator);
|
||||
},
|
||||
get id() {
|
||||
return this.simulator.id;
|
||||
},
|
||||
|
@ -8,9 +8,9 @@
|
||||
const { Cc, Ci, Cu } = require("chrome");
|
||||
|
||||
const Environment = require("sdk/system/environment").env;
|
||||
const EventEmitter = require("devtools/toolkit/event-emitter");
|
||||
const promise = require("promise");
|
||||
const Subprocess = require("sdk/system/child_process/subprocess");
|
||||
const { EventEmitter } = Cu.import("resource://gre/modules/devtools/event-emitter.js", {});
|
||||
const { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
|
||||
const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
|
||||
|
||||
loader.lazyGetter(this, "OS", () => {
|
||||
@ -46,6 +46,12 @@ SimulatorProcess.prototype = {
|
||||
throw Error("B2G executable not found.");
|
||||
}
|
||||
|
||||
// Ensure Gaia profile exists.
|
||||
let gaia = this.gaiaProfile;
|
||||
if (!gaia || !gaia.exists()) {
|
||||
throw Error("Gaia profile directory not found.");
|
||||
}
|
||||
|
||||
this.once("stdout", function () {
|
||||
if (OS == "mac64") {
|
||||
console.debug("WORKAROUND run osascript to show b2g-desktop window on OS=='mac64'");
|
||||
@ -122,13 +128,19 @@ SimulatorProcess.prototype = {
|
||||
get args() {
|
||||
let args = [];
|
||||
|
||||
let gaia = this.gaiaProfile;
|
||||
if (!gaia || !gaia.exists()) {
|
||||
throw Error("Gaia profile directory not found.");
|
||||
}
|
||||
args.push("-profile", gaia.path);
|
||||
// Gaia profile.
|
||||
args.push("-profile", this.gaiaProfile.path);
|
||||
|
||||
args.push("-start-debugger-server", "" + this.options.port);
|
||||
// Debugger server port.
|
||||
let port = parseInt(this.options.port);
|
||||
args.push("-start-debugger-server", "" + port);
|
||||
|
||||
// Screen size.
|
||||
let width = parseInt(this.options.width);
|
||||
let height = parseInt(this.options.height);
|
||||
if (width && height) {
|
||||
args.push("-screen", width + "x" + height);
|
||||
}
|
||||
|
||||
// Ignore eventual zombie instances of b2g that are left over.
|
||||
args.push("-no-remote");
|
||||
@ -202,15 +214,24 @@ Object.defineProperty(ASPp, "b2gBinary", {
|
||||
Object.defineProperty(ASPp, "gaiaProfile", {
|
||||
get: function() {
|
||||
let file;
|
||||
|
||||
// Custom profile from simulator configuration.
|
||||
if (this.options.gaiaProfile) {
|
||||
file = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsILocalFile);
|
||||
file.initWithPath(this.options.gaiaProfile);
|
||||
return file;
|
||||
}
|
||||
|
||||
// Custom profile from addon prefs.
|
||||
try {
|
||||
let pref = "extensions." + this.addon.id + ".gaiaProfile";
|
||||
file = Services.prefs.getComplexValue(pref, Ci.nsIFile);
|
||||
return file;
|
||||
} catch(e) {}
|
||||
|
||||
if (!file) {
|
||||
file = this.addon.getResourceURI().QueryInterface(Ci.nsIFileURL).file;
|
||||
file.append("profile");
|
||||
}
|
||||
// Default profile from addon.
|
||||
file = this.addon.getResourceURI().QueryInterface(Ci.nsIFileURL).file;
|
||||
file.append("profile");
|
||||
return file;
|
||||
}
|
||||
});
|
||||
@ -258,13 +279,12 @@ Object.defineProperty(OASPp, "args", {
|
||||
get: function() {
|
||||
let args = [];
|
||||
|
||||
let gaia = this.gaiaProfile;
|
||||
if (!gaia || !gaia.exists()) {
|
||||
throw Error("Gaia profile directory not found.");
|
||||
}
|
||||
args.push("-profile", gaia.path);
|
||||
// Gaia profile.
|
||||
args.push("-profile", this.gaiaProfile.path);
|
||||
|
||||
args.push("-dbgport", "" + this.options.port);
|
||||
// Debugger server port.
|
||||
let port = parseInt(this.options.port);
|
||||
args.push("-dbgport", "" + port);
|
||||
|
||||
// Ignore eventual zombie instances of b2g that are left over.
|
||||
args.push("-no-remote");
|
||||
|
@ -4,74 +4,207 @@
|
||||
|
||||
const { Cu } = require("chrome");
|
||||
const { AddonManager } = Cu.import("resource://gre/modules/AddonManager.jsm");
|
||||
const { EventEmitter } = Cu.import("resource://gre/modules/devtools/event-emitter.js");
|
||||
loader.lazyRequireGetter(this, "ConnectionManager", "devtools/client/connection-manager", true);
|
||||
loader.lazyRequireGetter(this, "AddonSimulatorProcess", "devtools/webide/simulator-process", true);
|
||||
loader.lazyRequireGetter(this, "OldAddonSimulatorProcess", "devtools/webide/simulator-process", true);
|
||||
loader.lazyRequireGetter(this, "CustomSimulatorProcess", "devtools/webide/simulator-process", true);
|
||||
const EventEmitter = require("devtools/toolkit/event-emitter");
|
||||
const promise = require("promise");
|
||||
|
||||
const SimulatorRegExp = new RegExp(Services.prefs.getCharPref("devtools.webide.simulatorAddonRegExp"));
|
||||
const LocaleCompare = (a, b) => {
|
||||
return a.name.toLowerCase().localeCompare(b.name.toLowerCase());
|
||||
};
|
||||
|
||||
let Simulators = {
|
||||
// TODO (Bug 1090949) Don't generate this list from installed simulator
|
||||
// addons, but instead implement a persistent list of user-configured
|
||||
// simulators.
|
||||
getAll() {
|
||||
|
||||
// The list of simulator configurations.
|
||||
_simulators: [],
|
||||
|
||||
// List all available simulators.
|
||||
findSimulators() {
|
||||
if (this._loaded) {
|
||||
return promise.resolve(this._simulators);
|
||||
}
|
||||
|
||||
// TODO (Bug 1146519) Load a persistent list of configured simulators first.
|
||||
|
||||
// Add default simulators to the list for each new (unused) addon.
|
||||
return this.findSimulatorAddons().then(addons => {
|
||||
this._loaded = true;
|
||||
addons.forEach(this.addIfUnusedAddon.bind(this));
|
||||
return this._simulators;
|
||||
});
|
||||
},
|
||||
|
||||
// List all installed simulator addons.
|
||||
findSimulatorAddons() {
|
||||
let deferred = promise.defer();
|
||||
AddonManager.getAllAddons(addons => {
|
||||
let simulators = [];
|
||||
for (let addon of addons) {
|
||||
if (SimulatorRegExp.exec(addon.id)) {
|
||||
simulators.push(new Simulator(addon));
|
||||
AddonManager.getAllAddons(all => {
|
||||
let addons = [];
|
||||
for (let addon of all) {
|
||||
if (this.isSimulatorAddon(addon)) {
|
||||
addons.push(addon);
|
||||
}
|
||||
}
|
||||
// Sort simulators alphabetically by name.
|
||||
simulators.sort((a, b) => {
|
||||
return a.name.toLowerCase().localeCompare(b.name.toLowerCase())
|
||||
});
|
||||
deferred.resolve(simulators);
|
||||
// Sort simulator addons by name.
|
||||
addons.sort(LocaleCompare);
|
||||
deferred.resolve(addons);
|
||||
});
|
||||
return deferred.promise;
|
||||
},
|
||||
}
|
||||
EventEmitter.decorate(Simulators);
|
||||
|
||||
// Detect simulator addons, including "unofficial" ones
|
||||
isSimulatorAddon(addon) {
|
||||
return SimulatorRegExp.exec(addon.id);
|
||||
},
|
||||
|
||||
// Get a unique name for a simulator (may add a suffix, e.g. "MyName (1)").
|
||||
uniqueName(name) {
|
||||
let simulators = this._simulators;
|
||||
|
||||
let names = {};
|
||||
simulators.forEach(simulator => names[simulator.name] = true);
|
||||
|
||||
// Strip any previous suffix, add a new suffix if necessary.
|
||||
let stripped = name.replace(/ \(\d+\)$/, "");
|
||||
let unique = stripped;
|
||||
for (let i = 1; names[unique]; i++) {
|
||||
unique = stripped + " (" + i + ")";
|
||||
}
|
||||
return unique;
|
||||
},
|
||||
|
||||
// Add a new simulator to the list. Caution: `simulator.name` may be modified.
|
||||
// @return Promise to added simulator.
|
||||
add(simulator) {
|
||||
let simulators = this._simulators;
|
||||
let uniqueName = this.uniqueName(simulator.options.name);
|
||||
simulator.options.name = uniqueName;
|
||||
simulators.push(simulator);
|
||||
this.emitUpdated();
|
||||
return promise.resolve(simulator);
|
||||
},
|
||||
|
||||
remove(simulator) {
|
||||
let simulators = this._simulators;
|
||||
let remaining = simulators.filter(s => s !== simulator);
|
||||
this._simulators = remaining;
|
||||
if (remaining.length !== simulators.length) {
|
||||
this.emitUpdated();
|
||||
}
|
||||
},
|
||||
|
||||
// Add a new default simulator for `addon` if no other simulator uses it.
|
||||
addIfUnusedAddon(addon) {
|
||||
let simulators = this._simulators;
|
||||
let matching = simulators.filter(s => s.addon && s.addon.id == addon.id);
|
||||
if (matching.length > 0) {
|
||||
return promise.resolve();
|
||||
}
|
||||
let name = addon.name.replace(" Simulator", "");
|
||||
return this.add(new Simulator({name}, addon));
|
||||
},
|
||||
|
||||
// TODO (Bug 1146521) Maybe find a better way to deal with removed addons?
|
||||
removeIfUsingAddon(addon) {
|
||||
let simulators = this._simulators;
|
||||
let remaining = simulators.filter(s => !s.addon || s.addon.id != addon.id);
|
||||
this._simulators = remaining;
|
||||
if (remaining.length !== simulators.length) {
|
||||
this.emitUpdated();
|
||||
}
|
||||
},
|
||||
|
||||
emitUpdated() {
|
||||
this._simulators.sort(LocaleCompare);
|
||||
this.emit("updated");
|
||||
},
|
||||
|
||||
onConfigure(e, simulator) {
|
||||
this._lastConfiguredSimulator = simulator;
|
||||
},
|
||||
|
||||
onInstalled(addon) {
|
||||
if (this.isSimulatorAddon(addon)) {
|
||||
this.addIfUnusedAddon(addon);
|
||||
}
|
||||
},
|
||||
|
||||
onEnabled(addon) {
|
||||
if (this.isSimulatorAddon(addon)) {
|
||||
this.addIfUnusedAddon(addon);
|
||||
}
|
||||
},
|
||||
|
||||
onDisabled(addon) {
|
||||
if (this.isSimulatorAddon(addon)) {
|
||||
this.removeIfUsingAddon(addon);
|
||||
}
|
||||
},
|
||||
|
||||
onUninstalled(addon) {
|
||||
if (this.isSimulatorAddon(addon)) {
|
||||
this.removeIfUsingAddon(addon);
|
||||
}
|
||||
},
|
||||
};
|
||||
exports.Simulators = Simulators;
|
||||
AddonManager.addAddonListener(Simulators);
|
||||
EventEmitter.decorate(Simulators);
|
||||
Simulators.on("configure", Simulators.onConfigure.bind(Simulators));
|
||||
|
||||
function update() {
|
||||
Simulators.emit("updated");
|
||||
}
|
||||
AddonManager.addAddonListener({
|
||||
onEnabled: update,
|
||||
onDisabled: update,
|
||||
onInstalled: update,
|
||||
onUninstalled: update
|
||||
});
|
||||
|
||||
|
||||
function Simulator(addon) {
|
||||
function Simulator(options = {}, addon = null) {
|
||||
this.addon = addon;
|
||||
}
|
||||
this.options = options;
|
||||
|
||||
// Fill `this.options` with default values where needed.
|
||||
let defaults = this._defaults;
|
||||
for (let option in defaults) {
|
||||
if (this.options[option] == null) {
|
||||
this.options[option] = defaults[option];
|
||||
}
|
||||
}
|
||||
}
|
||||
Simulator.prototype = {
|
||||
|
||||
// Default simulation options, based on the Firefox OS Flame.
|
||||
_defaults: {
|
||||
width: 320,
|
||||
height: 570,
|
||||
pixelRatio: 1.5
|
||||
},
|
||||
|
||||
restoreDefaults() {
|
||||
let options = this.options;
|
||||
let defaults = this._defaults;
|
||||
for (let option in defaults) {
|
||||
options[option] = defaults[option];
|
||||
}
|
||||
},
|
||||
|
||||
launch() {
|
||||
// Close already opened simulation.
|
||||
if (this.process) {
|
||||
return this.kill().then(this.launch.bind(this));
|
||||
}
|
||||
|
||||
let options = {
|
||||
port: ConnectionManager.getFreeTCPPort()
|
||||
};
|
||||
this.options.port = ConnectionManager.getFreeTCPPort();
|
||||
|
||||
if (this.version <= "1.3") {
|
||||
// Support older simulator addons.
|
||||
this.process = new OldAddonSimulatorProcess(this.addon, options);
|
||||
// Choose simulator process type.
|
||||
if (this.options.b2gBinary) {
|
||||
// Custom binary.
|
||||
this.process = new CustomSimulatorProcess(this.options);
|
||||
} else if (this.version > "1.3") {
|
||||
// Recent simulator addon.
|
||||
this.process = new AddonSimulatorProcess(this.addon, this.options);
|
||||
} else {
|
||||
this.process = new AddonSimulatorProcess(this.addon, options);
|
||||
// Old simulator addon.
|
||||
this.process = new OldAddonSimulatorProcess(this.addon, this.options);
|
||||
}
|
||||
this.process.run();
|
||||
|
||||
return promise.resolve(options.port);
|
||||
return promise.resolve(this.options.port);
|
||||
},
|
||||
|
||||
kill() {
|
||||
@ -84,14 +217,15 @@ Simulator.prototype = {
|
||||
},
|
||||
|
||||
get id() {
|
||||
return this.addon.id;
|
||||
return this.name;
|
||||
},
|
||||
|
||||
get name() {
|
||||
return this.addon.name.replace(" Simulator", "");
|
||||
return this.options.name;
|
||||
},
|
||||
|
||||
get version() {
|
||||
return this.name.match(/\d+\.\d+/)[0];
|
||||
return this.options.b2gBinary ? "Custom" : this.addon.name.match(/\d+\.\d+/)[0];
|
||||
},
|
||||
};
|
||||
exports.Simulator = Simulator;
|
||||
|
@ -13,19 +13,31 @@ function doesFileExist (location) {
|
||||
}
|
||||
exports.doesFileExist = doesFileExist;
|
||||
|
||||
function getPackagedDirectory (window, location) {
|
||||
let directory;
|
||||
if (!location) {
|
||||
let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
|
||||
fp.init(window, Strings.GetStringFromName("importPackagedApp_title"), Ci.nsIFilePicker.modeGetFolder);
|
||||
let res = fp.show();
|
||||
if (res == Ci.nsIFilePicker.returnCancel) {
|
||||
return null;
|
||||
}
|
||||
return fp.file;
|
||||
} else {
|
||||
function _getFile (location, ...pickerParams) {
|
||||
if (location) {
|
||||
return new FileUtils.File(location);
|
||||
}
|
||||
let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
|
||||
fp.init(...pickerParams);
|
||||
let res = fp.show();
|
||||
if (res == Ci.nsIFilePicker.returnCancel) {
|
||||
return null;
|
||||
}
|
||||
return fp.file;
|
||||
}
|
||||
|
||||
function getCustomBinary (window, location) {
|
||||
return _getFile(location, window, Strings.GetStringFromName("selectCustomBinary_title"), Ci.nsIFilePicker.modeOpen);
|
||||
}
|
||||
exports.getCustomBinary = getCustomBinary;
|
||||
|
||||
function getCustomProfile (window, location) {
|
||||
return _getFile(location, window, Strings.GetStringFromName("selectCustomProfile_title"), Ci.nsIFilePicker.modeGetFolder);
|
||||
}
|
||||
exports.getCustomProfile = getCustomProfile;
|
||||
|
||||
function getPackagedDirectory (window, location) {
|
||||
return _getFile(location, window, Strings.GetStringFromName("importPackagedApp_title"), Ci.nsIFilePicker.modeGetFolder);
|
||||
}
|
||||
exports.getPackagedDirectory = getPackagedDirectory;
|
||||
|
||||
|
@ -38,6 +38,7 @@ support-files =
|
||||
head.js
|
||||
hosted_app.manifest
|
||||
templates.json
|
||||
../../shared/test/browser_devices.json
|
||||
|
||||
[test_basic.html]
|
||||
[test_newapp.html]
|
||||
@ -56,3 +57,4 @@ support-files =
|
||||
[test_fullscreenToolbox.html]
|
||||
[test_zoom.html]
|
||||
[test_build.html]
|
||||
[test_simulators.html]
|
||||
|
@ -23,13 +23,14 @@ if (window.location === "chrome://browser/content/browser.xul") {
|
||||
|
||||
Services.prefs.setBoolPref("devtools.webide.enabled", true);
|
||||
Services.prefs.setBoolPref("devtools.webide.enableLocalRuntime", true);
|
||||
Services.prefs.setBoolPref("devtools.webide.enableRuntimeConfiguration", true);
|
||||
|
||||
Services.prefs.setCharPref("devtools.webide.addonsURL", TEST_BASE + "addons/simulators.json");
|
||||
Services.prefs.setCharPref("devtools.webide.simulatorAddonsURL", TEST_BASE + "addons/fxos_#SLASHED_VERSION#_simulator-#OS#.xpi");
|
||||
Services.prefs.setCharPref("devtools.webide.adbAddonURL", TEST_BASE + "addons/adbhelper-#OS#.xpi");
|
||||
Services.prefs.setCharPref("devtools.webide.adaptersAddonURL", TEST_BASE + "addons/fxdt-adapters-#OS#.xpi");
|
||||
Services.prefs.setCharPref("devtools.webide.templatesURL", TEST_BASE + "templates.json");
|
||||
|
||||
Services.prefs.setCharPref("devtools.devices.url", TEST_BASE + "browser_devices.json");
|
||||
|
||||
SimpleTest.registerCleanupFunction(() => {
|
||||
Services.prefs.clearUserPref("devtools.webide.enabled");
|
||||
|
341
browser/devtools/webide/test/test_simulators.html
Normal file
341
browser/devtools/webide/test/test_simulators.html
Normal file
@ -0,0 +1,341 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf8">
|
||||
<title></title>
|
||||
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/chrome-harness.js"></script>
|
||||
<script type="application/javascript;version=1.8" src="head.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<script type="application/javascript;version=1.8">
|
||||
window.onload = function() {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
const EventEmitter = require("devtools/toolkit/event-emitter");
|
||||
const { GetAvailableAddons } = require("devtools/webide/addons");
|
||||
const { GetDevices } = require("devtools/shared/devices");
|
||||
const { Simulator } = require("devtools/webide/simulators");
|
||||
const { AddonSimulatorProcess,
|
||||
OldAddonSimulatorProcess,
|
||||
CustomSimulatorProcess } = require("devtools/webide/simulator-process");
|
||||
|
||||
function addonStatus(addon, status) {
|
||||
if (addon.status == status) {
|
||||
return promise.resolve();
|
||||
}
|
||||
let deferred = promise.defer();
|
||||
addon.on("update", function onUpdate() {
|
||||
if (addon.status == status) {
|
||||
addon.off("update", onUpdate);
|
||||
deferred.resolve();
|
||||
}
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
Task.spawn(function* () {
|
||||
|
||||
let win = yield openWebIDE(false);
|
||||
|
||||
let find = win.document.querySelector.bind(win.document);
|
||||
let findAll = win.document.querySelectorAll.bind(win.document);
|
||||
|
||||
let simulatorList = find("#runtime-panel-simulator");
|
||||
let simulatorPanel = find("#deck-panel-simulator");
|
||||
|
||||
// Hack SimulatorProcesses to spy on simulation parameters.
|
||||
|
||||
let runPromise;
|
||||
function fakeRun() {
|
||||
runPromise.resolve({
|
||||
path: this.b2gBinary.path,
|
||||
args: this.args
|
||||
});
|
||||
// Don't actually try to connect to the fake simulator.
|
||||
throw new Error("Aborting on purpose before connection.");
|
||||
}
|
||||
|
||||
AddonSimulatorProcess.prototype.run = fakeRun;
|
||||
OldAddonSimulatorProcess.prototype.run = fakeRun;
|
||||
CustomSimulatorProcess.prototype.run = fakeRun;
|
||||
|
||||
function runSimulator(i) {
|
||||
runPromise = promise.defer();
|
||||
findAll(".runtime-panel-item-simulator")[i].click();
|
||||
return runPromise.promise;
|
||||
}
|
||||
|
||||
// Install fake "Firefox OS 1.0" simulator addon.
|
||||
|
||||
let addons = yield GetAvailableAddons();
|
||||
|
||||
let sim10 = addons.simulators.filter(a => a.version == "1.0")[0];
|
||||
|
||||
sim10.install();
|
||||
|
||||
yield addonStatus(sim10, "installed");
|
||||
|
||||
is(findAll(".runtime-panel-item-simulator").length, 1, "One simulator in runtime panel");
|
||||
|
||||
// Install fake "Firefox OS 2.0" simulator addon.
|
||||
|
||||
let sim20 = addons.simulators.filter(a => a.version == "2.0")[0];
|
||||
|
||||
sim20.install();
|
||||
|
||||
yield addonStatus(sim20, "installed");
|
||||
|
||||
is(findAll(".runtime-panel-item-simulator").length, 2, "Two simulators in runtime panel");
|
||||
|
||||
// Dry run a simulator to verify that its parameters look right.
|
||||
|
||||
let params = yield runSimulator(0);
|
||||
|
||||
ok(params.path.contains(sim10.addonID) && params.path.contains("b2g-bin"), "Simulator binary path looks right");
|
||||
|
||||
let pid = params.args.indexOf("-profile");
|
||||
ok(pid > -1, "Simulator process arguments have --profile");
|
||||
|
||||
let profilePath = params.args[pid + 1];
|
||||
ok(profilePath.contains(sim10.addonID) && profilePath.contains("profile"), "Simulator profile path looks right");
|
||||
|
||||
ok(params.args.indexOf("-dbgport") > -1 || params.args.indexOf("-start-debugger-server") > -1, "Simulator process arguments have a debugger port");
|
||||
|
||||
ok(params.args.indexOf("-no-remote") > -1, "Simulator process arguments have --no-remote");
|
||||
|
||||
yield nextTick();
|
||||
|
||||
// Configure the fake 1.0 simulator.
|
||||
|
||||
simulatorList.querySelectorAll(".configure-button")[0].click();
|
||||
|
||||
is(find("#deck").selectedPanel, simulatorPanel, "Simulator deck panel is selected");
|
||||
|
||||
yield lazyIframeIsLoaded(simulatorPanel);
|
||||
|
||||
let doc = simulatorPanel.contentWindow.document;
|
||||
let form = doc.querySelector("#simulator-editor");
|
||||
let change = doc.createEvent("HTMLEvents");
|
||||
change.initEvent("change", true, true);
|
||||
|
||||
function set(input, value) {
|
||||
input.value = value;
|
||||
input.dispatchEvent(change);
|
||||
return nextTick();
|
||||
}
|
||||
|
||||
let MockFilePicker = SpecialPowers.MockFilePicker;
|
||||
MockFilePicker.init(simulatorPanel.contentWindow);
|
||||
|
||||
// Test `name`.
|
||||
|
||||
is(form.name.value, find(".runtime-panel-item-simulator").label, "Original simulator name");
|
||||
|
||||
let customName = "CustomFox ";
|
||||
yield set(form.name, customName + "1.0");
|
||||
|
||||
is(find(".runtime-panel-item-simulator").label, form.name.value, "Updated simulator name");
|
||||
|
||||
// Test `version`.
|
||||
|
||||
is(form.version.value, sim10.addonID, "Original simulator version");
|
||||
ok(!form.version.classList.contains("custom"), "Version selector is not customized");
|
||||
|
||||
yield set(form.version, sim20.addonID);
|
||||
|
||||
ok(!form.version.classList.contains("custom"), "Version selector is not customized after addon change");
|
||||
is(form.name.value, customName + "2.0", "Simulator name was updated to new version");
|
||||
|
||||
// Pick custom binary, but act like the user aborted the file picker.
|
||||
|
||||
MockFilePicker.returnFiles = [];
|
||||
yield set(form.version, "pick");
|
||||
|
||||
is(form.version.value, sim20.addonID, "Version selector reverted to last valid choice after customization abort");
|
||||
ok(!form.version.classList.contains("custom"), "Version selector is not customized after customization abort");
|
||||
|
||||
// Pick custom binary, and actually follow through. (success, verify value = "custom" and textContent = custom path)
|
||||
|
||||
MockFilePicker.useAnyFile();
|
||||
yield set(form.version, "pick");
|
||||
|
||||
let fakeBinary = MockFilePicker.returnFiles[0];
|
||||
|
||||
ok(form.version.value == "custom", "Version selector was set to a new custom binary");
|
||||
ok(form.version.classList.contains("custom"), "Version selector is now customized");
|
||||
is(form.version.selectedOptions[0].textContent, fakeBinary.path, "Custom option textContent is correct");
|
||||
|
||||
yield set(form.version, sim10.addonID);
|
||||
|
||||
ok(form.version.classList.contains("custom"), "Version selector remains customized after change back to addon");
|
||||
is(form.name.value, customName + "1.0", "Simulator name was updated to new version");
|
||||
|
||||
yield set(form.version, "custom");
|
||||
|
||||
ok(form.version.value == "custom", "Version selector is back to custom");
|
||||
|
||||
// Test `profile`.
|
||||
|
||||
is(form.profile.value, "default", "Default simulator profile");
|
||||
ok(!form.profile.classList.contains("custom"), "Profile selector is not customized");
|
||||
|
||||
MockFilePicker.returnFiles = [];
|
||||
yield set(form.profile, "pick");
|
||||
|
||||
is(form.profile.value, "default", "Profile selector reverted to last valid choice after customization abort");
|
||||
ok(!form.profile.classList.contains("custom"), "Profile selector is not customized after customization abort");
|
||||
|
||||
let fakeProfile = FileUtils.getDir("TmpD", []);
|
||||
|
||||
MockFilePicker.returnFiles = [ fakeProfile ];
|
||||
yield set(form.profile, "pick");
|
||||
|
||||
ok(form.profile.value == "custom", "Profile selector was set to a new custom directory");
|
||||
ok(form.profile.classList.contains("custom"), "Profile selector is now customized");
|
||||
is(form.profile.selectedOptions[0].textContent, fakeProfile.path, "Custom option textContent is correct");
|
||||
|
||||
yield set(form.profile, "default");
|
||||
|
||||
is(form.profile.value, "default", "Profile selector back to default");
|
||||
ok(form.profile.classList.contains("custom"), "Profile selector remains customized after change back to default");
|
||||
|
||||
yield set(form.profile, "custom");
|
||||
|
||||
is(form.profile.value, "custom", "Profile selector back to custom");
|
||||
|
||||
params = yield runSimulator(0);
|
||||
|
||||
is(params.path, fakeBinary.path, "Simulator process uses custom binary path");
|
||||
|
||||
pid = params.args.indexOf("-profile");
|
||||
is(params.args[pid + 1], fakeProfile.path, "Simulator process uses custom profile directory");
|
||||
|
||||
yield set(form.version, sim10.addonID);
|
||||
|
||||
is(form.name.value, customName + "1.0", "Simulator restored to 1.0");
|
||||
|
||||
params = yield runSimulator(0);
|
||||
|
||||
pid = params.args.indexOf("-profile");
|
||||
is(params.args[pid + 1], fakeProfile.path, "Simulator process still uses custom profile directory");
|
||||
|
||||
yield set(form.version, "custom");
|
||||
|
||||
// Test `device`.
|
||||
|
||||
let defaults = Simulator.prototype._defaults;
|
||||
|
||||
for (let param in defaults) {
|
||||
is(form[param].value, defaults[param], "Default value for device " + param);
|
||||
}
|
||||
|
||||
let width = 5000, height = 4000;
|
||||
yield set(form.width, width);
|
||||
yield set(form.height, height);
|
||||
|
||||
is(form.device.value, "custom", "Device selector is custom");
|
||||
|
||||
params = yield runSimulator(0);
|
||||
|
||||
let sid = params.args.indexOf("-screen");
|
||||
ok(sid > -1, "Simulator process arguments have --screen");
|
||||
ok(params.args[sid + 1].contains(width + "x" + height), "Simulator screen resolution looks right");
|
||||
|
||||
yield set(form.version, sim10.addonID);
|
||||
|
||||
// Configure the fake 2.0 simulator.
|
||||
|
||||
simulatorList.querySelectorAll(".configure-button")[1].click();
|
||||
yield nextTick();
|
||||
|
||||
// Test `name`.
|
||||
|
||||
is(form.name.value, findAll(".runtime-panel-item-simulator")[1].label, "Original simulator name");
|
||||
|
||||
yield set(form.name, customName + "2.0");
|
||||
|
||||
is(findAll(".runtime-panel-item-simulator")[1].label, form.name.value, "Updated simulator name");
|
||||
|
||||
yield set(form.version, sim10.addonID);
|
||||
|
||||
ok(form.name.value !== customName + "1.0", "Conflicting simulator name was deduplicated");
|
||||
|
||||
is(form.name.value, findAll(".runtime-panel-item-simulator")[1].label, "Deduplicated simulator name stayed consistent");
|
||||
|
||||
yield set(form.version, sim20.addonID);
|
||||
|
||||
is(form.name.value, customName + "2.0", "Name deduplication was undone when possible");
|
||||
|
||||
// Test `device`.
|
||||
|
||||
for (let param in defaults) {
|
||||
is(form[param].value, defaults[param], "Default value for device " + param);
|
||||
}
|
||||
|
||||
let devices = yield GetDevices();
|
||||
devices = devices[devices.TYPES[0]];
|
||||
let device = devices[devices.length - 1];
|
||||
|
||||
yield set(form.device, device.name);
|
||||
|
||||
is(form.device.value, device.name, "Device selector was changed");
|
||||
is(form.width.value, device.width, "New device width is correct");
|
||||
is(form.height.value, device.height, "New device height is correct");
|
||||
|
||||
params = yield runSimulator(1);
|
||||
|
||||
sid = params.args.indexOf("-screen");
|
||||
ok(params.args[sid + 1].contains(device.width + "x" + device.height), "Simulator screen resolution looks right");
|
||||
|
||||
// Restore default simulator options.
|
||||
|
||||
doc.querySelector("#reset").click();
|
||||
yield nextTick();
|
||||
|
||||
for (let param in defaults) {
|
||||
is(form[param].value, defaults[param], "Default value for device " + param);
|
||||
}
|
||||
|
||||
// Uninstall the 2.0 addon and watch its Simulator object disappear.
|
||||
|
||||
sim20.uninstall();
|
||||
|
||||
yield addonStatus(sim20, "uninstalled");
|
||||
|
||||
is(findAll(".runtime-panel-item-simulator").length, 1, "One simulator left in runtime panel");
|
||||
|
||||
// Remove 1.0 simulator.
|
||||
|
||||
simulatorList.querySelectorAll(".configure-button")[0].click();
|
||||
yield nextTick();
|
||||
|
||||
doc.querySelector("#remove").click();
|
||||
yield nextTick();
|
||||
|
||||
is(findAll(".runtime-panel-item-simulator").length, 0, "Last simulator was removed");
|
||||
|
||||
sim10.uninstall();
|
||||
|
||||
MockFilePicker.cleanup();
|
||||
|
||||
doc.querySelector("#close").click();
|
||||
|
||||
ok(!find("#deck").selectedPanel, "No panel selected");
|
||||
|
||||
yield closeWebIDE(win);
|
||||
|
||||
SimpleTest.finish();
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -70,3 +70,12 @@ li > label:hover {
|
||||
li > label > span {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
input, select {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
select {
|
||||
padding-top: 2px;
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
|
@ -18,3 +18,4 @@ webide.jar:
|
||||
skin/wifi-auth.css (wifi-auth.css)
|
||||
skin/logs.css (logs.css)
|
||||
skin/project-listing.css (project-listing.css)
|
||||
skin/simulator.css (simulator.css)
|
||||
|
41
browser/devtools/webide/themes/simulator.css
Normal file
41
browser/devtools/webide/themes/simulator.css
Normal file
@ -0,0 +1,41 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
select:not(.custom) > option[value="custom"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
select, input[type="text"] {
|
||||
width: 13rem;
|
||||
}
|
||||
|
||||
input[name="name"] {
|
||||
height: 1.8rem;
|
||||
}
|
||||
|
||||
input[type="number"] {
|
||||
width: 6rem;
|
||||
}
|
||||
|
||||
input[type="text"], input[type="number"] {
|
||||
padding-left: 0.2rem;
|
||||
}
|
||||
|
||||
li > label:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.label {
|
||||
width: 6rem;
|
||||
padding: 0.2rem;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
@ -195,3 +195,16 @@
|
||||
|
||||
<!-- Logs panel -->
|
||||
<!ENTITY logs_title "Pre-packaging Command Logs">
|
||||
|
||||
<!-- Simulator Options -->
|
||||
<!ENTITY simulator_title "Simulator Options">
|
||||
<!ENTITY simulator_remove "Delete Simulator">
|
||||
<!ENTITY simulator_reset "Restore Defaults">
|
||||
<!ENTITY simulator_name "Name">
|
||||
<!ENTITY simulator_software "Software">
|
||||
<!ENTITY simulator_version "Version">
|
||||
<!ENTITY simulator_profile "Profile">
|
||||
<!ENTITY simulator_hardware "Hardware">
|
||||
<!ENTITY simulator_device "Device">
|
||||
<!ENTITY simulator_screenSize "Screen">
|
||||
<!ENTITY simulator_pixelRatio "Pixel Ratio">
|
||||
|
@ -19,6 +19,9 @@ importPackagedApp_title=Select Directory
|
||||
importHostedApp_title=Open Hosted App
|
||||
importHostedApp_header=Enter Manifest URL
|
||||
|
||||
selectCustomBinary_title=Select custom B2G binary
|
||||
selectCustomProfile_title=Select custom Gaia profile
|
||||
|
||||
notification_showTroubleShooting_label=Troubleshooting
|
||||
notification_showTroubleShooting_accesskey=T
|
||||
|
||||
@ -77,3 +80,9 @@ status_unknown=UNKNOWN
|
||||
|
||||
# Device preferences and settings
|
||||
device_reset_default=Reset to default
|
||||
|
||||
# Simulator options
|
||||
simulator_custom_device=Custom
|
||||
simulator_custom_binary=Custom B2G binary…
|
||||
simulator_custom_profile=Custom Gaia profile…
|
||||
simulator_default_profile=Use default
|
||||
|
Loading…
Reference in New Issue
Block a user