diff options
Diffstat (limited to 'devtools/client/webide/modules/simulator-process.js')
-rw-r--r-- | devtools/client/webide/modules/simulator-process.js | 325 |
1 files changed, 325 insertions, 0 deletions
diff --git a/devtools/client/webide/modules/simulator-process.js b/devtools/client/webide/modules/simulator-process.js new file mode 100644 index 000000000..7d0b57cc6 --- /dev/null +++ b/devtools/client/webide/modules/simulator-process.js @@ -0,0 +1,325 @@ +/* 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/. + */ + +"use strict"; + +const { Cc, Ci, Cu } = require("chrome"); + +const Environment = require("sdk/system/environment").env; +const EventEmitter = require("devtools/shared/event-emitter"); +const promise = require("promise"); +const Subprocess = require("sdk/system/child_process/subprocess"); +const Services = require("Services"); + +loader.lazyGetter(this, "OS", () => { + const Runtime = require("sdk/system/runtime"); + switch (Runtime.OS) { + case "Darwin": + return "mac64"; + case "Linux": + if (Runtime.XPCOMABI.indexOf("x86_64") === 0) { + return "linux64"; + } else { + return "linux32"; + } + case "WINNT": + return "win32"; + default: + return ""; + } +}); + +function SimulatorProcess() {} +SimulatorProcess.prototype = { + + // Check if B2G is running. + get isRunning() { + return !!this.process; + }, + + // Start the process and connect the debugger client. + run() { + + // Resolve B2G binary. + let b2g = this.b2gBinary; + if (!b2g || !b2g.exists()) { + 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'"); + // Escape double quotes and escape characters for use in AppleScript. + let path = b2g.path.replace(/\\/g, "\\\\").replace(/\"/g, '\\"'); + + Subprocess.call({ + command: "/usr/bin/osascript", + arguments: ["-e", 'tell application "' + path + '" to activate'], + }); + } + }); + + let logHandler = (e, data) => this.log(e, data.trim()); + this.on("stdout", logHandler); + this.on("stderr", logHandler); + this.once("exit", () => { + this.off("stdout", logHandler); + this.off("stderr", logHandler); + }); + + let environment; + if (OS.indexOf("linux") > -1) { + environment = ["TMPDIR=" + Services.dirsvc.get("TmpD", Ci.nsIFile).path]; + ["DISPLAY", "XAUTHORITY"].forEach(key => { + if (key in Environment) { + environment.push(key + "=" + Environment[key]); + } + }); + } + + // Spawn a B2G instance. + this.process = Subprocess.call({ + command: b2g, + arguments: this.args, + environment: environment, + stdout: data => this.emit("stdout", data), + stderr: data => this.emit("stderr", data), + // On B2G instance exit, reset tracked process, remote debugger port and + // shuttingDown flag, then finally emit an exit event. + done: result => { + console.log("B2G terminated with " + result.exitCode); + this.process = null; + this.emit("exit", result.exitCode); + } + }); + }, + + // Request a B2G instance kill. + kill() { + let deferred = promise.defer(); + if (this.process) { + this.once("exit", (e, exitCode) => { + this.shuttingDown = false; + deferred.resolve(exitCode); + }); + if (!this.shuttingDown) { + this.shuttingDown = true; + this.emit("kill", null); + this.process.kill(); + } + return deferred.promise; + } else { + return promise.resolve(undefined); + } + }, + + // Maybe log output messages. + log(level, message) { + if (!Services.prefs.getBoolPref("devtools.webide.logSimulatorOutput")) { + return; + } + if (level === "stderr" || level === "error") { + console.error(message); + return; + } + console.log(message); + }, + + // Compute B2G CLI arguments. + get args() { + let args = []; + + // Gaia profile. + args.push("-profile", this.gaiaProfile.path); + + // 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"); + + // If we are running a simulator based on Mulet, + // we have to override the default chrome URL + // in order to prevent the Browser UI to appear. + if (this.b2gBinary.leafName.includes("firefox")) { + args.push("-chrome", "chrome://b2g/content/shell.html"); + } + + return args; + }, +}; + +EventEmitter.decorate(SimulatorProcess.prototype); + + +function CustomSimulatorProcess(options) { + this.options = options; +} + +var CSPp = CustomSimulatorProcess.prototype = Object.create(SimulatorProcess.prototype); + +// Compute B2G binary file handle. +Object.defineProperty(CSPp, "b2gBinary", { + get: function () { + let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile); + file.initWithPath(this.options.b2gBinary); + return file; + } +}); + +// Compute Gaia profile file handle. +Object.defineProperty(CSPp, "gaiaProfile", { + get: function () { + let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile); + file.initWithPath(this.options.gaiaProfile); + return file; + } +}); + +exports.CustomSimulatorProcess = CustomSimulatorProcess; + + +function AddonSimulatorProcess(addon, options) { + this.addon = addon; + this.options = options; +} + +var ASPp = AddonSimulatorProcess.prototype = Object.create(SimulatorProcess.prototype); + +// Compute B2G binary file handle. +Object.defineProperty(ASPp, "b2gBinary", { + get: function () { + let file; + try { + let pref = "extensions." + this.addon.id + ".customRuntime"; + file = Services.prefs.getComplexValue(pref, Ci.nsIFile); + } catch (e) {} + + if (!file) { + file = this.addon.getResourceURI().QueryInterface(Ci.nsIFileURL).file; + file.append("b2g"); + let binaries = { + win32: "b2g-bin.exe", + mac64: "B2G.app/Contents/MacOS/b2g-bin", + linux32: "b2g-bin", + linux64: "b2g-bin", + }; + binaries[OS].split("/").forEach(node => file.append(node)); + } + // If the binary doesn't exists, it may be because of a simulator + // based on mulet, which has a different binary name. + if (!file.exists()) { + file = this.addon.getResourceURI().QueryInterface(Ci.nsIFileURL).file; + file.append("firefox"); + let binaries = { + win32: "firefox.exe", + mac64: "FirefoxNightly.app/Contents/MacOS/firefox-bin", + linux32: "firefox-bin", + linux64: "firefox-bin", + }; + binaries[OS].split("/").forEach(node => file.append(node)); + } + return file; + } +}); + +// Compute Gaia profile file handle. +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) {} + + // Default profile from addon. + file = this.addon.getResourceURI().QueryInterface(Ci.nsIFileURL).file; + file.append("profile"); + return file; + } +}); + +exports.AddonSimulatorProcess = AddonSimulatorProcess; + + +function OldAddonSimulatorProcess(addon, options) { + this.addon = addon; + this.options = options; +} + +var OASPp = OldAddonSimulatorProcess.prototype = Object.create(AddonSimulatorProcess.prototype); + +// Compute B2G binary file handle. +Object.defineProperty(OASPp, "b2gBinary", { + get: function () { + let file; + try { + let pref = "extensions." + this.addon.id + ".customRuntime"; + file = Services.prefs.getComplexValue(pref, Ci.nsIFile); + } catch (e) {} + + if (!file) { + file = this.addon.getResourceURI().QueryInterface(Ci.nsIFileURL).file; + let version = this.addon.name.match(/\d+\.\d+/)[0].replace(/\./, "_"); + file.append("resources"); + file.append("fxos_" + version + "_simulator"); + file.append("data"); + file.append(OS == "linux32" ? "linux" : OS); + let binaries = { + win32: "b2g/b2g-bin.exe", + mac64: "B2G.app/Contents/MacOS/b2g-bin", + linux32: "b2g/b2g-bin", + linux64: "b2g/b2g-bin", + }; + binaries[OS].split("/").forEach(node => file.append(node)); + } + return file; + } +}); + +// Compute B2G CLI arguments. +Object.defineProperty(OASPp, "args", { + get: function () { + let args = []; + + // Gaia profile. + args.push("-profile", this.gaiaProfile.path); + + // 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"); + + return args; + } +}); + +exports.OldAddonSimulatorProcess = OldAddonSimulatorProcess; |