summaryrefslogtreecommitdiffstats
path: root/devtools/client/webide/content/simulator.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/webide/content/simulator.js')
-rw-r--r--devtools/client/webide/content/simulator.js352
1 files changed, 352 insertions, 0 deletions
diff --git a/devtools/client/webide/content/simulator.js b/devtools/client/webide/content/simulator.js
new file mode 100644
index 000000000..ddc1cbed1
--- /dev/null
+++ b/devtools/client/webide/content/simulator.js
@@ -0,0 +1,352 @@
+/* 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/. */
+
+var Cu = Components.utils;
+var Ci = Components.interfaces;
+
+const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
+const { getDevices, getDeviceString } = require("devtools/client/shared/devices");
+const { Simulators, Simulator } = require("devtools/client/webide/modules/simulators");
+const Services = require("Services");
+const EventEmitter = require("devtools/shared/event-emitter");
+const promise = require("promise");
+const utils = require("devtools/client/webide/modules/utils");
+
+const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties");
+
+var 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 = Array.map(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();
+
+ // Change visibility of 'TV Simulator Menu'.
+ let tvSimMenu = document.querySelector("#tv_simulator_menu");
+ tvSimMenu.style.visibility = (this._simulator.type === "television") ?
+ "visible" : "hidden";
+
+ // Trigger any listener waiting for this update
+ let change = document.createEvent("HTMLEvents");
+ change.initEvent("change", true, true);
+ this._form.dispatchEvent(change);
+ });
+ },
+
+ // Open the directory of TV Simulator config.
+ showTVConfigDirectory() {
+ let profD = Services.dirsvc.get("ProfD", Ci.nsIFile);
+ profD.append("extensions");
+ profD.append(this._simulator.addon.id);
+ profD.append("profile");
+ profD.append("dummy");
+ let profileDir = profD.path;
+
+ // Show the profile directory.
+ let nsLocalFile = Components.Constructor("@mozilla.org/file/local;1",
+ "nsILocalFile", "initWithPath");
+ new nsLocalFile(profileDir).reveal();
+ },
+
+ // 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.selectedIndex == -1) {
+ 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.includes(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);
+
+ document.querySelector("#open-tv-dummy-directory").onclick = e => {
+ SimulatorEditor.showTVConfigDirectory();
+ e.preventDefault();
+ };
+});