Bug 1321637: Fix execution of batch files with spaces in path. r?mhowell,aswan draft
authorKris Maglione <maglione.k@gmail.com>
Sun, 22 Jan 2017 16:23:33 -0800
changeset 464861 2637a04c079c50be07ab3af6bf7d05e03dac1f5a
parent 464860 0cdd083de64d2c7d9cf742ca15546a00e80ec989
child 543017 d167d9c61cb1b3f2916b287dcc0c00e1b4236816
push id42457
push usermaglione.k@gmail.com
push dateMon, 23 Jan 2017 04:22:37 +0000
reviewersmhowell, aswan
bugs1321637
milestone53.0a1
Bug 1321637: Fix execution of batch files with spaces in path. r?mhowell,aswan MozReview-Commit-ID: 8Q3KFLnXEkl
toolkit/components/extensions/test/xpcshell/head_native_messaging.js
toolkit/components/extensions/test/xpcshell/test_native_messaging.js
toolkit/modules/subprocess/subprocess_win.jsm
toolkit/modules/subprocess/subprocess_worker_win.js
--- a/toolkit/components/extensions/test/xpcshell/head_native_messaging.js
+++ b/toolkit/components/extensions/test/xpcshell/head_native_messaging.js
@@ -10,17 +10,17 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "OS",
                                   "resource://gre/modules/osfile.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "setTimeout",
                                   "resource://gre/modules/Timer.jsm");
 
 let {Subprocess, SubprocessImpl} = Cu.import("resource://gre/modules/Subprocess.jsm", {});
 
 
-let tmpDir = FileUtils.getDir("TmpD", ["NativeMessaging"]);
+let tmpDir = FileUtils.getDir("TmpD", ["Native Messaging"]);
 tmpDir.createUnique(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
 
 do_register_cleanup(() => {
   tmpDir.remove(true);
 });
 
 function getPath(filename) {
   return OS.Path.join(tmpDir.path, filename);
@@ -85,20 +85,20 @@ function* setupHosts(scripts) {
       const REGKEY = String.raw`Software\Mozilla\NativeMessagingHosts`;
 
       let registry = new MockRegistry();
       do_register_cleanup(() => {
         registry.shutdown();
       });
 
       for (let script of scripts) {
-        let batPath = getPath(`${script.name}.bat`);
+        let batPath = getPath(`batch ${script.name}.bat`);
         let scriptPath = getPath(`${script.name}.py`);
 
-        let batBody = `@ECHO OFF\n${pythonPath} -u ${scriptPath} %*\n`;
+        let batBody = `@ECHO OFF\n${pythonPath} -u "${scriptPath}" %*\n`;
         yield OS.File.writeAtomic(batPath, batBody);
 
         // Create absolute and relative path versions of the entry.
         for (let [name, path] of [[script.name, batPath],
                                   [`relative.${script.name}`, OS.Path.basename(batPath)]]) {
           script.name = name;
           let manifestPath = yield writeManifest(script, scriptPath, path);
 
--- a/toolkit/components/extensions/test/xpcshell/test_native_messaging.js
+++ b/toolkit/components/extensions/test/xpcshell/test_native_messaging.js
@@ -251,17 +251,17 @@ while True:
     type: "stdio",
     allowed_extensions: [ID],
   };
 
   if (AppConstants.platform == "win") {
     yield OS.File.writeAtomic(scriptPath, SCRIPT);
 
     let batPath = OS.Path.join(userDir.path, "wontdie.bat");
-    let batBody = `@ECHO OFF\n${PYTHON} -u ${scriptPath} %*\n`;
+    let batBody = `@ECHO OFF\n${PYTHON} -u "${scriptPath}" %*\n`;
     yield OS.File.writeAtomic(batPath, batBody);
     yield OS.File.setPermissions(batPath, {unixMode: 0o755});
 
     manifest.path = batPath;
     yield writeManifest(manifestPath, manifest);
 
     registry.setValue(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
                       `${REGPATH}\\wontdie`, "", manifestPath);
--- a/toolkit/modules/subprocess/subprocess_win.jsm
+++ b/toolkit/modules/subprocess/subprocess_win.jsm
@@ -15,29 +15,33 @@ var {classes: Cc, interfaces: Ci, utils:
 
 var EXPORTED_SYMBOLS = ["SubprocessImpl"];
 
 Cu.import("resource://gre/modules/AppConstants.jsm");
 Cu.import("resource://gre/modules/ctypes.jsm");
 Cu.import("resource://gre/modules/osfile.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/subprocess/subprocess_common.jsm");
 
+XPCOMUtils.defineLazyServiceGetter(this, "env", "@mozilla.org/process/environment;1", "nsIEnvironment");
+
 Services.scriptloader.loadSubScript("resource://gre/modules/subprocess/subprocess_shared.js", this);
 Services.scriptloader.loadSubScript("resource://gre/modules/subprocess/subprocess_shared_win.js", this);
 
 class WinPromiseWorker extends PromiseWorker {
   constructor(...args) {
     super(...args);
 
     this.signalEvent = libc.CreateSemaphoreW(null, 0, 32, null);
 
     this.call("init", [{
       breakAwayFromJob: !AppConstants.isPlatformAndVersionAtLeast("win", "6.2"),
+      comspec: env.get("COMSPEC"),
       signalEvent: String(ctypes.cast(this.signalEvent, ctypes.uintptr_t).value),
     }]);
   }
 
   signalWorker() {
     libc.ReleaseSemaphore(this.signalEvent, 1, null);
   }
 
--- a/toolkit/modules/subprocess/subprocess_worker_win.js
+++ b/toolkit/modules/subprocess/subprocess_worker_win.js
@@ -442,16 +442,21 @@ class Process extends BaseProcess {
 
     if (/\\cmd\.exe$/i.test(command) && args.length == 3 && /^(\/S)?\/C$/i.test(args[1])) {
       // cmd.exe is insane and requires special treatment.
       args = [this.quoteString(args[0]), "/S/C", `"${args[2]}"`];
     } else {
       args = args.map(arg => this.quoteString(arg));
     }
 
+    if (/\.bat$/i.test(command)) {
+      command = io.comspec;
+      args = ["cmd.exe", "/s/c", `"${args.join(" ")}"`];
+    }
+
     let envp = this.stringList(options.environment);
 
     let handles = this.initPipes(options);
 
     let processFlags = win32.CREATE_NO_WINDOW
                      | win32.CREATE_SUSPENDED
                      | win32.CREATE_UNICODE_ENVIRONMENT;
 
@@ -593,16 +598,18 @@ io = {
 
   processes: new Map(),
 
   messageCount: 0,
 
   running: true,
 
   init(details) {
+    this.comspec = details.comspec;
+
     let signalEvent = ctypes.cast(ctypes.uintptr_t(details.signalEvent),
                                   win32.HANDLE);
     this.signal = new Signal(signalEvent);
     this.updatePollEvents();
 
     this.breakAwayFromJob = details.breakAwayFromJob;
 
     setTimeout(this.loop.bind(this), 0);