/* 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(); }; });