Back out 3 changesets (bug 1270357) for test_chrome_ext_native_messaging.html timeouts
authorPhil Ringnalda <philringnalda@gmail.com>
Wed, 08 Jun 2016 20:35:32 -0700
changeset 301202 8987c204d2920c40e6bd6bd79d9626591df58a33
parent 301201 79fdba402322d545a40702e2894c9960685da06e
child 301203 75f65d79283ffe8fbfa797d62e64d7b797d20f6c
push id78249
push userphilringnalda@gmail.com
push dateThu, 09 Jun 2016 03:36:31 +0000
treeherdermozilla-inbound@75f65d79283f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1270357
milestone50.0a1
backs out2e602689376d22362b4bfc556af2c3b6c39dce5f
e1979d928c7c4eb08b6a5303b5533ce5a3318ee1
e0f9b54febc83597415fe8d056ef7b4e8939f03d
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Back out 3 changesets (bug 1270357) for test_chrome_ext_native_messaging.html timeouts CLOSED TREE Backed out changeset 2e602689376d (bug 1270357) Backed out changeset e1979d928c7c (bug 1270357) Backed out changeset e0f9b54febc8 (bug 1270357)
testing/mochitest/mochitest.eslintrc
toolkit/components/extensions/NativeMessaging.jsm
toolkit/components/extensions/ext-runtime.js
toolkit/components/extensions/schemas/runtime.json
toolkit/components/extensions/test/mochitest/chrome.ini
toolkit/components/extensions/test/mochitest/test_chrome_ext_native_messaging.html
toolkit/components/extensions/test/xpcshell/test_native_messaging.js
toolkit/modules/subprocess/subprocess_worker_common.js
--- a/testing/mochitest/mochitest.eslintrc
+++ b/testing/mochitest/mochitest.eslintrc
@@ -16,17 +16,16 @@
     "executeSoon": false,
     "export_assertions": false,
     "finish": false,
     "getRootDirectory": false,
     "getTestFilePath": false,
     "gTestPath": false,
     "info": false,
     "is": false,
-    "isDeeply": false,
     "isnot": false,
     "ok": false,
     "promise": false,
     "registerCleanupFunction": false,
     "requestLongerTimeout": false,
     "SimpleTest": false,
     "SpecialPowers": false,
     "todo": false,
--- a/toolkit/components/extensions/NativeMessaging.jsm
+++ b/toolkit/components/extensions/NativeMessaging.jsm
@@ -1,66 +1,33 @@
 /* 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";
 
-this.EXPORTED_SYMBOLS = ["HostManifestManager", "NativeApp"];
+this.EXPORTED_SYMBOLS = ["HostManifestManager"];
 
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-
-const {EventEmitter} = Cu.import("resource://devtools/shared/event-emitter.js", {});
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/AppConstants.jsm");
+Cu.import("resource://devtools/shared/event-emitter.js");
 
-XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
-                                  "resource://gre/modules/AppConstants.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "AsyncShutdown",
-                                  "resource://gre/modules/AsyncShutdown.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "ExtensionUtils",
-                                  "resource://gre/modules/ExtensionUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "OS",
                                   "resource://gre/modules/osfile.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Schemas",
                                   "resource://gre/modules/Schemas.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "Services",
-                                  "resource://gre/modules/Services.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "Subprocess",
-                                  "resource://gre/modules/Subprocess.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Task",
                                   "resource://gre/modules/Task.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "clearTimeout",
-                                  "resource://gre/modules/Timer.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "setTimeout",
-                                  "resource://gre/modules/Timer.jsm");
 
 const HOST_MANIFEST_SCHEMA = "chrome://extensions/content/schemas/native_host_manifest.json";
 const VALID_APPLICATION = /^\w+(\.\w+)*$/;
 
-// For a graceful shutdown (i.e., when the extension is unloaded or when it
-// explicitly calls disconnect() on a native port), how long we give the native
-// application to exit before we start trying to kill it.  (in milliseconds)
-const GRACEFUL_SHUTDOWN_TIME = 3000;
-
-// Hard limits on maximum message size that can be read/written
-// These are defined in the native messaging documentation, note that
-// the write limit is imposed by the "wire protocol" in which message
-// boundaries are defined by preceding each message with its length as
-// 4-byte unsigned integer so this is the largest value that can be
-// represented.  Good luck generating a serialized message that large,
-// the practical write limit is likely to be dictated by available memory.
-const MAX_READ = 1024 * 1024;
-const MAX_WRITE = 0xffffffff;
-
-// Preferences that can lower the message size limits above,
-// used for testing the limits.
-const PREF_MAX_READ = "webextensions.native-messaging.max-input-message-bytes";
-const PREF_MAX_WRITE = "webextensions.native-messaging.max-output-message-bytes";
-
 this.HostManifestManager = {
   _initializePromise: null,
   _lookup: null,
 
   init() {
     if (!this._initializePromise) {
       let platform = AppConstants.platform;
       if (platform == "win") {
@@ -130,230 +97,13 @@ this.HostManifestManager = {
    *
    * @param {string} application The name of the applciation to search for.
    * @param {object} context A context object as expected by Schemas.normalize.
    * @returns {object} The contents of the validated manifest, or null if
    *                   no valid manifest can be found for this application.
    */
   lookupApplication(application, context) {
     if (!VALID_APPLICATION.test(application)) {
-      throw new Error(`Invalid application "${application}"`);
+      throw new context.cloneScope.Error(`Invalid application "${application}"`);
     }
     return this.init().then(() => this._lookup(application, context));
   },
 };
-
-this.NativeApp = class extends EventEmitter {
-  constructor(extension, context, application) {
-    super();
-
-    this.context = context;
-    this.name = application;
-
-    // We want a close() notification when the window is destroyed.
-    this.context.callOnClose(this);
-
-    this.encoder = new TextEncoder();
-    this.proc = null;
-    this.readPromise = null;
-    this.sendQueue = [];
-    this.writePromise = null;
-    this.sentDisconnect = false;
-
-    // Grab these once at startup
-    XPCOMUtils.defineLazyPreferenceGetter(this, "maxRead", PREF_MAX_READ, MAX_READ);
-    XPCOMUtils.defineLazyPreferenceGetter(this, "maxWrite", PREF_MAX_WRITE, MAX_WRITE);
-
-    this.startupPromise = HostManifestManager.lookupApplication(application, context)
-      .then(hostInfo => {
-        if (!hostInfo) {
-          throw new Error(`No such native application ${application}`);
-        }
-
-        if (!hostInfo.manifest.allowed_extensions.includes(extension.id)) {
-          throw new Error(`This extension does not have permission to use native application ${application}`);
-        }
-
-        let subprocessOpts = {
-          command: hostInfo.manifest.path,
-          arguments: [hostInfo.path],
-          workdir: OS.Path.dirname(hostInfo.manifest.path),
-        };
-        return Subprocess.call(subprocessOpts);
-      }).then(proc => {
-        this.startupPromise = null;
-        this.proc = proc;
-        this._startRead();
-        this._startWrite();
-      }).catch(err => {
-        this.startupPromise = null;
-        Cu.reportError(err);
-        this._cleanup(err);
-      });
-  }
-
-  // A port is definitely "alive" if this.proc is non-null.  But we have
-  // to provide a live port object immediately when connecting so we also
-  // need to consider a port alive if proc is null but the startupPromise
-  // is still pending.
-  get _isDisconnected() {
-    return (!this.proc && !this.startupPromise);
-  }
-
-  _startRead() {
-    if (this.readPromise) {
-      throw new Error("Entered _startRead() while readPromise is non-null");
-    }
-    this.readPromise = this.proc.stdout.readUint32()
-      .then(len => {
-        if (len > this.maxRead) {
-          throw new Error(`Native application tried to send a message of ${len} bytes, which exceeds the limit of ${this.maxRead} bytes.`);
-        }
-        return this.proc.stdout.readJSON(len);
-      }).then(msg => {
-        this.emit("message", msg);
-        this.readPromise = null;
-        this._startRead();
-      }).catch(err => {
-        Cu.reportError(err);
-        this._cleanup(err);
-      });
-  }
-
-  _startWrite() {
-    if (this.sendQueue.length == 0) {
-      return;
-    }
-
-    if (this.writePromise) {
-      throw new Error("Entered _startWrite() while writePromise is non-null");
-    }
-
-    let buffer = this.sendQueue.shift();
-    let uintArray = Uint32Array.of(buffer.byteLength);
-
-    this.writePromise = Promise.all([
-      this.proc.stdin.write(uintArray.buffer),
-      this.proc.stdin.write(buffer),
-    ]).then(() => {
-      this.writePromise = null;
-      this._startWrite();
-    }).catch(err => {
-      Cu.reportError(err);
-      this._cleanup(err);
-    });
-  }
-
-  send(msg) {
-    if (this._isDisconnected) {
-      throw new this.context.cloneScope.Error("Attempt to postMessage on disconnected port");
-    }
-
-    let json;
-    try {
-      json = JSON.stringify(msg);
-    } catch (err) {
-      throw new this.context.cloneScope.Error(err.message);
-    }
-    let buffer = this.encoder.encode(json).buffer;
-
-    if (buffer.byteLength > this.maxWrite) {
-      throw new this.context.cloneScope.Error("Write too big");
-    }
-
-    this.sendQueue.push(buffer);
-    if (!this.startupPromise && !this.writePromise) {
-      this._startWrite();
-    }
-  }
-
-  // Shut down the native application and also signal to the extension
-  // that the connect has been disconnected.
-  _cleanup(err) {
-    this.context.forgetOnClose(this);
-
-    let doCleanup = () => {
-      // Set a timer to kill the process gracefully after one timeout
-      // interval and kill it forcefully after two intervals.
-      let timer = setTimeout(() => {
-        this.proc.kill(GRACEFUL_SHUTDOWN_TIME);
-      }, GRACEFUL_SHUTDOWN_TIME);
-
-      let promise = Promise.all([
-        this.proc.stdin.close()
-          .catch(err => {
-            if (err.errorCode != Subprocess.ERROR_END_OF_FILE) {
-              throw err;
-            }
-          }),
-        this.proc.wait(),
-      ]).then(() => {
-        this.proc = null;
-        clearTimeout(timer);
-      });
-
-      AsyncShutdown.profileBeforeChange.addBlocker(
-        `Native Messaging: Wait for application ${this.name} to exit`,
-        promise);
-
-      promise.then(() => {
-        AsyncShutdown.profileBeforeChange.removeBlocker(promise);
-      });
-
-      return promise;
-    };
-
-    if (this.proc) {
-      doCleanup();
-    } else if (this.startupPromise) {
-      this.startupPromise.then(doCleanup);
-    }
-
-    if (!this.sentDisconnect) {
-      this.sentDisconnect = true;
-      this.emit("disconnect", err);
-    }
-  }
-
-  // Called from Context when the extension is shut down.
-  close() {
-    this._cleanup();
-  }
-
-  portAPI() {
-    let api = {
-      name: this.name,
-
-      disconnect: () => {
-        if (this._isDisconnected) {
-          throw new this.context.cloneScope.Error("Attempt to disconnect an already disconnected port");
-        }
-        this._cleanup();
-      },
-
-      postMessage: msg => {
-        this.send(msg);
-      },
-
-      onDisconnect: new ExtensionUtils.SingletonEventManager(this.context, "native.onDisconnect", fire => {
-        let listener = what => {
-          this.context.runSafe(fire);
-        };
-        this.on("disconnect", listener);
-        return () => {
-          this.off("disconnect", listener);
-        };
-      }).api(),
-
-      onMessage: new ExtensionUtils.SingletonEventManager(this.context, "native.onMessage", fire => {
-        let listener = (what, msg) => {
-          this.context.runSafe(fire, msg);
-        };
-        this.on("message", listener);
-        return () => {
-          this.off("message", listener);
-        };
-      }).api(),
-    };
-
-    return Cu.cloneInto(api, this.context.cloneScope, {cloneFunctions: true});
-  }
-};
--- a/toolkit/components/extensions/ext-runtime.js
+++ b/toolkit/components/extensions/ext-runtime.js
@@ -1,23 +1,18 @@
 "use strict";
 
 var {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-
 Cu.import("resource://gre/modules/ExtensionUtils.jsm");
 var {
   EventManager,
   ignoreEvent,
 } = ExtensionUtils;
 
-XPCOMUtils.defineLazyModuleGetter(this, "NativeApp",
-                                  "resource://gre/modules/NativeMessaging.jsm");
-
 extensions.registerSchemaAPI("runtime", null, (extension, context) => {
   return {
     runtime: {
       onStartup: new EventManager(context, "runtime.onStartup", fire => {
         extension.onStartup = fire;
         return () => {
           extension.onStartup = null;
         };
@@ -50,25 +45,16 @@ extensions.registerSchemaAPI("runtime", 
 
         if (!GlobalManager.extensionMap.has(recipient.extensionId)) {
           return context.wrapPromise(Promise.reject({message: "Invalid extension ID"}),
                                      responseCallback);
         }
         return context.messenger.sendMessage(Services.cpmm, message, recipient, responseCallback);
       },
 
-      connectNative(application) {
-        if (!extension.hasPermission("nativeMessaging")) {
-          throw new context.cloneScope.Error("Permission denied because 'nativeMessaging' permission is missing.");
-        }
-
-        let app = new NativeApp(extension, context, application);
-        return app.portAPI();
-      },
-
       get lastError() {
         return context.lastError;
       },
 
       getManifest() {
         return Cu.cloneInto(extension.manifest, context.cloneScope);
       },
 
--- a/toolkit/components/extensions/schemas/runtime.json
+++ b/toolkit/components/extensions/schemas/runtime.json
@@ -1,28 +1,14 @@
 // Copyright 2014 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 [
   {
-    "namespace": "manifest",
-    "types": [
-      {
-        "$extend": "Permission",
-        "choices": [{
-          "type": "string",
-          "enum": [
-            "nativeMessaging"
-          ]
-        }]
-      }
-    ]
-  },
-  {
     "namespace": "runtime",
     "description": "Use the <code>browser.runtime</code> API to retrieve the background page, return details about the manifest, and listen for and respond to events in the app or extension lifecycle. You can also use this API to convert the relative path of URLs to fully-qualified URLs.",
     "types": [
       {
         "id": "Port",
         "type": "object",
         "description": "An object which allows two way communication with other pages.",
         "properties": {
@@ -271,16 +257,17 @@
         ],
         "returns": {
           "$ref": "Port",
           "description": "Port through which messages can be sent and received. The port's $(ref:runtime.Port onDisconnect) event is fired if the extension/app does not exist. "
         }
       },
       {
         "name": "connectNative",
+        "unsupported": true,
         "type": "function",
         "description": "Connects to a native application in the host machine.",
         "parameters": [
           {
             "type": "string",
             "name": "application",
             "description": "The name of the registered application to connect to."
           }
--- a/toolkit/components/extensions/test/mochitest/chrome.ini
+++ b/toolkit/components/extensions/test/mochitest/chrome.ini
@@ -8,19 +8,16 @@ support-files =
 [test_chrome_ext_background_debug_global.html]
 skip-if = (os == 'android') # android doesn't have devtools
 [test_chrome_ext_background_page.html]
 skip-if = (toolkit == 'android') # android doesn't have devtools
 [test_chrome_ext_downloads_download.html]
 [test_chrome_ext_downloads_misc.html]
 [test_chrome_ext_downloads_search.html]
 [test_chrome_ext_eventpage_warning.html]
-[test_chrome_ext_native_messaging.html]
-# Re-enable for Windows with bug 1270359.
-skip-if = os != "mac" && os != "linux"
 [test_chrome_ext_contentscript_unrecognizedprop_warning.html]
 skip-if = (os == 'android') # browser.tabs is undefined. Bug 1258975 on android.
 [test_chrome_ext_webnavigation_resolved_urls.html]
 skip-if = (os == 'android') # browser.tabs is undefined. Bug 1258975 on android.
 [test_chrome_native_messaging_paths.html]
 # Re-enable for Windows with bug 1270359.
 skip-if = os != "mac" && os != "linux"
 [test_ext_cookies_expiry.html]
deleted file mode 100644
--- a/toolkit/components/extensions/test/mochitest/test_chrome_ext_native_messaging.html
+++ /dev/null
@@ -1,516 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>WebExtension test</title>
-  <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="chrome://mochikit/content/tests/SimpleTest/SpawnTask.js"></script>
-  <script src="chrome://mochikit/content/tests/SimpleTest/ExtensionTestUtils.js"></script>
-  <script type="text/javascript" src="head.js"></script>
-  <script type="text/javascript" src="test_constants.js"></script>
-  <link rel="stylesheet" href="chrome://mochikit/contents/tests/SimpleTest/test.css"/>
-</head>
-<body>
-
-<script type="text/javascript">
-"use strict";
-
-/* globals OS */
-
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
-
-Cu.import("resource://gre/modules/FileUtils.jsm");
-Cu.import("resource://gre/modules/osfile.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-let {Subprocess, SubprocessImpl} = Cu.import("resource://gre/modules/Subprocess.jsm");
-
-const PREF_MAX_READ = "webextensions.native-messaging.max-input-message-bytes";
-const PREF_MAX_WRITE = "webextensions.native-messaging.max-output-message-bytes";
-
-function getSubprocessCount() {
-  return SubprocessImpl.Process.getWorker().call("getProcesses", [])
-                       .then(result => result.size);
-}
-function waitForSubprocessExit() {
-  return SubprocessImpl.Process.getWorker().call("waitForNoProcesses", []);
-}
-
-let dir = FileUtils.getDir("TmpD", ["NativeMessaging"]);
-dir.createUnique(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
-info(`Using local directory ${dir.path}\n`);
-
-let dirProvider = {
-  getFile(property) {
-    if (property == "XREUserNativeMessaging") {
-      return dir.clone();
-    }
-    return null;
-  },
-};
-
-Services.dirsvc.registerProvider(dirProvider);
-SimpleTest.registerCleanupFunction(() => {
-  Services.dirsvc.unregisterProvider(dirProvider);
-  dir.remove(true);
-});
-
-function getPath(filename) {
-  return OS.Path.join(dir.path, filename);
-}
-
-// Set up a couple of native applications and their manifests for
-// test to use.
-const ID = "native@tests.mozilla.org";
-
-const ECHO_PATH = getPath("echo.py");
-const ECHO_MANIFEST_PATH = getPath("echo.json");
-const ECHO_MANIFEST = {
-  name: "echo",
-  description: "a native app that echoes back messages it receives",
-  path: ECHO_PATH,
-  type: "stdio",
-  allowed_extensions: [ID],
-};
-
-const INFO_PATH = getPath("info.py");
-const INFO_MANIFEST_PATH = getPath("info.json");
-const INFO_MANIFEST = {
-  name: "info",
-  description: "a native app that gives some info about how it was started",
-  path: INFO_PATH,
-  type: "stdio",
-  allowed_extensions: [ID],
-};
-
-const WONTDIE_PATH = getPath("wontdie.py");
-const WONTDIE_MANIFEST_PATH = getPath("wontdie.json");
-const WONTDIE_MANIFEST = {
-  name: "wontdie",
-  description: "a native app that does not exit when stdin closes or on SIGTERM",
-  path: WONTDIE_PATH,
-  type: "stdio",
-  allowed_extensions: [ID],
-};
-
-add_task(function* setup_scripts() {
-  const PERMS = {unixMode: 0o755};
-  let pythonPath = yield Subprocess.pathSearch("python2.7").catch(err => {
-    if (err.errorCode != Subprocess.ERROR_BAD_EXECUTABLE) {
-      throw err;
-    }
-    return Subprocess.pathSearch("python");
-  });
-
-  const ECHO_SCRIPT = String.raw`#!${pythonPath} -u
-import struct
-import sys
-
-while True:
-    rawlen = sys.stdin.read(4)
-    if len(rawlen) == 0:
-        sys.exit(0)
-    msglen = struct.unpack('@I', rawlen)[0]
-    msg = sys.stdin.read(msglen)
-
-    sys.stdout.write(struct.pack('@I', msglen))
-    sys.stdout.write(msg)
-`;
-
-  yield OS.File.writeAtomic(ECHO_PATH, ECHO_SCRIPT);
-  yield OS.File.setPermissions(ECHO_PATH, PERMS);
-  yield OS.File.writeAtomic(ECHO_MANIFEST_PATH, JSON.stringify(ECHO_MANIFEST));
-
-  const INFO_SCRIPT = String.raw`#!${pythonPath} -u
-import json
-import os
-import struct
-import sys
-
-msg = json.dumps({"args": sys.argv, "cwd": os.getcwd()})
-sys.stdout.write(struct.pack('@I', len(msg)))
-sys.stdout.write(msg)
-sys.exit(0)
-`;
-
-  yield OS.File.writeAtomic(INFO_PATH, INFO_SCRIPT);
-  yield OS.File.setPermissions(INFO_PATH, PERMS);
-  yield OS.File.writeAtomic(INFO_MANIFEST_PATH, JSON.stringify(INFO_MANIFEST));
-
-  const WONTDIE_SCRIPT = String.raw`#!${pythonPath} -u
-import signal
-import struct
-import sys
-
-signal.signal(signal.SIGTERM, signal.SIG_IGN)
-
-while True:
-    rawlen = sys.stdin.read(4)
-    if len(rawlen) == 0:
-        signal.pause()
-    msglen = struct.unpack('@I', rawlen)[0]
-    msg = sys.stdin.read(msglen)
-
-    sys.stdout.write(struct.pack('@I', msglen))
-    sys.stdout.write(msg)
-`;
-
-  yield OS.File.writeAtomic(WONTDIE_PATH, WONTDIE_SCRIPT);
-  yield OS.File.setPermissions(WONTDIE_PATH, PERMS);
-  yield OS.File.writeAtomic(WONTDIE_MANIFEST_PATH, JSON.stringify(WONTDIE_MANIFEST));
-});
-
-// Test the basic operation of native messaging with a simple
-// script that echoes back whatever message is sent to it.
-add_task(function* test_happy_path() {
-  function background() {
-    let port = browser.runtime.connectNative("echo");
-    port.onMessage.addListener(msg => {
-      browser.test.sendMessage("message", msg);
-    });
-    browser.test.onMessage.addListener((what, payload) => {
-      if (what == "send") {
-        if (payload._json) {
-          let json = payload._json;
-          payload.toJSON = () => json;
-          delete payload._json;
-        }
-        port.postMessage(payload);
-      }
-    });
-    browser.test.sendMessage("ready");
-  }
-
-  let extension = ExtensionTestUtils.loadExtension({
-    background: `(${background})()`,
-    manifest: {
-      permissions: ["nativeMessaging"],
-    },
-  }, ID);
-
-  yield extension.startup();
-  yield extension.awaitMessage("ready");
-  const tests = [
-    {
-      data: "this is a string",
-      what: "simple string",
-    },
-    {
-      data: "Это юникода",
-      what: "unicode string",
-    },
-    {
-      data: {test: "hello"},
-      what: "simple object",
-    },
-    {
-      data: {
-        what: "An object with a few properties",
-        number: 123,
-        bool: true,
-        nested: {what: "another object"},
-      },
-      what: "object with several properties",
-    },
-
-    // enable with bug 1274708
-    //{
-    //  data: {
-    //    ignoreme: true,
-    //    _json: {data: "i have a tojson method"},
-    //  },
-    //  expected: {data: "i have a tojson method"},
-    //  what: "object with toJSON() method",
-    //},
-  ];
-  for (let test of tests) {
-    extension.sendMessage("send", test.data);
-    let response = yield extension.awaitMessage("message");
-    let expected = test.expected || test.data;
-    isDeeply(response, expected, `Echoed a message of type ${test.what}`);
-  }
-
-  let procCount = yield getSubprocessCount();
-  is(procCount, 1, "subprocess is still running");
-  let exitPromise = waitForSubprocessExit();
-  yield extension.unload();
-  yield exitPromise;
-});
-
-// Test calling Port.disconnect()
-add_task(function* test_disconnect() {
-  function background() {
-    let port = browser.runtime.connectNative("echo");
-    port.onMessage.addListener(msg => {
-      browser.test.sendMessage("message", msg);
-    });
-    browser.test.onMessage.addListener((what, payload) => {
-      if (what == "send") {
-        if (payload._json) {
-          let json = payload._json;
-          payload.toJSON = () => json;
-          delete payload._json;
-        }
-        port.postMessage(payload);
-      } else if (what == "disconnect") {
-        try {
-          port.disconnect();
-          browser.test.sendMessage("disconnect-result", {success: true});
-        } catch (err) {
-          browser.test.sendMessage("disconnect-result", {
-            success: false,
-            errmsg: err.message,
-          });
-        }
-      }
-    });
-    browser.test.sendMessage("ready");
-  }
-
-  let extension = ExtensionTestUtils.loadExtension({
-    background: `(${background})()`,
-    manifest: {
-      permissions: ["nativeMessaging"],
-    },
-  }, ID);
-
-  yield extension.startup();
-  yield extension.awaitMessage("ready");
-
-  extension.sendMessage("send", "test");
-  let response = yield extension.awaitMessage("message");
-  is(response, "test", "Echoed a string");
-
-  let procCount = yield getSubprocessCount();
-  is(procCount, 1, "subprocess is running");
-
-  extension.sendMessage("disconnect");
-  response = yield extension.awaitMessage("disconnect-result");
-  is(response.success, true, "disconnect succeeded");
-
-  info("waiting for subprocess to exit");
-  yield waitForSubprocessExit();
-  procCount = yield getSubprocessCount();
-  is(procCount, 0, "subprocess is no longer running");
-
-  extension.sendMessage("disconnect");
-  response = yield extension.awaitMessage("disconnect-result");
-  is(response.success, false, "second call to disconnect failed");
-  ok(/already disconnected/.test(response.errmsg), "disconnect error message is reasonable");
-
-  yield extension.unload();
-});
-
-// Test the limit on message size for writing
-add_task(function* test_write_limit() {
-  Services.prefs.setIntPref(PREF_MAX_WRITE, 10);
-  function clearPref() {
-    Services.prefs.clearUserPref(PREF_MAX_WRITE);
-  }
-  SimpleTest.registerCleanupFunction(clearPref);
-
-  function background() {
-    const PAYLOAD = "0123456789A";
-    let port = browser.runtime.connectNative("echo");
-    try {
-      port.postMessage(PAYLOAD);
-      browser.test.sendMessage("result", null);
-    } catch (ex) {
-      browser.test.sendMessage("result", ex.message);
-    }
-  }
-
-  let extension = ExtensionTestUtils.loadExtension({
-    background: `(${background})()`,
-    manifest: {
-      permissions: ["nativeMessaging"],
-    },
-  }, ID);
-
-  yield extension.startup();
-
-  let errmsg = yield extension.awaitMessage("result");
-  isnot(errmsg, null, "native postMessage() failed for overly large message");
-
-  yield extension.unload();
-  yield waitForSubprocessExit();
-
-  clearPref();
-});
-
-// Test the limit on message size for reading
-add_task(function* test_read_limit() {
-  Services.prefs.setIntPref(PREF_MAX_READ, 10);
-  function clearPref() {
-    Services.prefs.clearUserPref(PREF_MAX_READ);
-  }
-  SimpleTest.registerCleanupFunction(clearPref);
-
-  function background() {
-    const PAYLOAD = "0123456789A";
-    let port = browser.runtime.connectNative("echo");
-    port.onDisconnect.addListener(() => {
-      browser.test.sendMessage("result", {
-        what: "disconnected",
-        errmsg: browser.runtime.lastError.message,
-      });
-    });
-    port.onMessage.addListener(msg => {
-      browser.test.sendMessage("result", {what: "got message"});
-    });
-    port.postMessage(PAYLOAD);
-  }
-
-  let extension = ExtensionTestUtils.loadExtension({
-    background: `(${background})()`,
-    manifest: {
-      permissions: ["nativeMessaging"],
-    },
-  }, ID);
-
-  yield extension.startup();
-
-  let result = yield extension.awaitMessage("result");
-  is(result.what, "disconnected", "native port disconnected on receiving large message");
-  ok(/tried to send a message of \d+ bytes, which exceeds the limit of \d+ bytes/.test(result.errmsg), "error message for large read is reasonable");
-
-  yield extension.unload();
-  yield waitForSubprocessExit();
-
-  clearPref();
-});
-
-// Test that an extension without the nativeMessaging permission cannot
-// use native messaging.
-add_task(function* test_ext_permission() {
-  function background() {
-    try {
-      browser.runtime.connectNative("test");
-      browser.test.sendMessage("result", null);
-    } catch (ex) {
-      browser.test.sendMessage("result", ex.message);
-    }
-  }
-
-  let extension = ExtensionTestUtils.loadExtension({
-    background: `(${background})()`,
-    manifest: {},
-  });
-
-  yield extension.startup();
-
-  let errmsg = yield extension.awaitMessage("result");
-  isnot(errmsg, null, "connectNative() failed without nativeMessaging permission");
-  ok(/Permission denied/.test(errmsg), "error message for missing extension permission is reasonable");
-
-  yield extension.unload();
-
-  let procCount = yield getSubprocessCount();
-  is(procCount, 0, "No child process was started");
-});
-
-// Test that an extension that is not listed in allowed_extensions for
-// a native application cannot use that application.
-add_task(function* test_app_permission() {
-  function background() {
-    let port = browser.runtime.connectNative("echo");
-    port.onDisconnect.addListener(() => {
-      browser.test.sendMessage("result", {
-        what: "disconnected",
-        errmsg: browser.runtime.lastError.message,
-      });
-    });
-    port.onMessage.addListener(msg => {
-      browser.test.sendMessage("result", {what: "got message"});
-    });
-    port.postMessage({test: "test"});
-  }
-
-  let extension = ExtensionTestUtils.loadExtension({
-    background: `(${background})()`,
-    manifest: {
-      permissions: ["nativeMessaging"],
-    },
-  }, "somethingelse@tests.mozilla.org");
-
-  yield extension.startup();
-
-  let result = yield extension.awaitMessage("result");
-  is(result.what, "disconnected", "connectNative() failed without native app permission");
-  ok(/This extension does not have permission/.test(result.errmsg), "Error message for missing native app permission is reasonable");
-
-  yield extension.unload();
-
-  let procCount = yield getSubprocessCount();
-  is(procCount, 0, "No child process was started");
-});
-
-// Test that the command-line arguments and working directory for the
-// native application are as expected.
-add_task(function* test_child_process() {
-  function background() {
-    let port = browser.runtime.connectNative("info");
-    port.onMessage.addListener(msg => {
-      browser.test.sendMessage("result", msg);
-    });
-  }
-
-  let extension = ExtensionTestUtils.loadExtension({
-    background: `(${background})()`,
-    manifest: {
-      permissions: ["nativeMessaging"],
-    },
-  }, ID);
-
-  yield extension.startup();
-
-  let msg = yield extension.awaitMessage("result");
-  is(msg.args.length, 2, "Received one command line argument");
-  is(msg.args[1], INFO_MANIFEST_PATH, "Command line argument is the path to the native host manifest");
-  is(msg.cwd, OS.Path.dirname(INFO_PATH), "Working directory is the directory containing the native appliation");
-
-  let exitPromise = waitForSubprocessExit();
-  yield extension.unload();
-  yield exitPromise;
-});
-
-// Test that an unresponsive native application still gets killed eventually
-add_task(function* test_unresponsive_native_app() {
-  // XXX expose GRACEFUL_SHUTDOWN_TIME as a pref and reduce it
-  // just for this test?
-
-  function background() {
-    let port = browser.runtime.connectNative("wontdie");
-
-    const MSG = "echo me";
-    // bounce a message to make sure the process actually starts
-    port.onMessage.addListener(msg => {
-      browser.test.assertEq(msg, MSG, "Received echoed message");
-      browser.test.sendMessage("ready");
-    });
-    port.postMessage(MSG);
-  }
-
-  let extension = ExtensionTestUtils.loadExtension({
-    background: `(${background})()`,
-    manifest: {
-      permissions: ["nativeMessaging"],
-    },
-  }, ID);
-
-  yield extension.startup();
-  yield extension.awaitMessage("ready");
-
-  let procCount = yield getSubprocessCount();
-  is(procCount, 1, "subprocess is running");
-
-  let exitPromise = waitForSubprocessExit();
-  yield extension.unload();
-  yield exitPromise;
-
-  procCount = yield getSubprocessCount();
-  is(procCount, 0, "subprocess was succesfully killed");
-});
-
-</script>
-
-</body>
-</html>
--- a/toolkit/components/extensions/test/xpcshell/test_native_messaging.js
+++ b/toolkit/components/extensions/test/xpcshell/test_native_messaging.js
@@ -1,19 +1,20 @@
 "use strict";
 
-/* global OS, HostManifestManager, NativeApp */
-Cu.import("resource://gre/modules/AsyncShutdown.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/FileUtils.jsm");
-Cu.import("resource://gre/modules/Schemas.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-const {Subprocess, SubprocessImpl} = Cu.import("resource://gre/modules/Subprocess.jsm");
-Cu.import("resource://gre/modules/NativeMessaging.jsm");
+
+/* global OS */
 Cu.import("resource://gre/modules/osfile.jsm");
 
+/* global HostManifestManager */
+Cu.import("resource://gre/modules/NativeMessaging.jsm");
+
+Components.utils.import("resource://gre/modules/Schemas.jsm");
 const BASE_SCHEMA = "chrome://extensions/content/schemas/manifest.json";
 
 let dir = FileUtils.getDir("TmpD", ["NativeMessaging"]);
 dir.createUnique(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
 
 let userDir = dir.clone();
 userDir.append("user");
 userDir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
@@ -42,34 +43,26 @@ do_register_cleanup(() => {
 
 function writeManifest(path, manifest) {
   if (typeof manifest != "string") {
     manifest = JSON.stringify(manifest);
   }
   return OS.File.writeAtomic(path, manifest);
 }
 
-let PYTHON;
 add_task(function* setup() {
   yield Schemas.load(BASE_SCHEMA);
-
-  PYTHON = yield Subprocess.pathSearch("python2.7");
-  if (PYTHON == null) {
-    PYTHON = yield Subprocess.pathSearch("python");
-  }
-  notEqual(PYTHON, null, "Found a suitable python interpreter");
 });
 
 // Test of HostManifestManager.lookupApplication() begin here...
+
 let context = {
   url: null,
   logError() {},
   preprocessors: {},
-  callOnClose: () => {},
-  forgetOnClose: () => {},
 };
 
 let templateManifest = {
   name: "test",
   description: "this is only a test",
   path: "/bin/cat",
   type: "stdio",
   allowed_extensions: ["extension@tests.mozilla.org"],
@@ -165,70 +158,8 @@ add_task(function* test_user_dir_precede
   // test.json is still in the global directory from the previous test
 
   let result = yield HostManifestManager.lookupApplication("test", context);
   notEqual(result, null, "lookupApplication finds a manifest when entries exist in both user-specific and system-wide directories");
   equal(result.path, USER_TEST_JSON, "lookupApplication returns the user-specific path when user-specific and system-wide entries both exist");
   deepEqual(result.manifest, templateManifest, "lookupApplication returns user-specific manifest contents with user-specific and system-wide entries both exist");
 });
 
-// Test shutdown handling in NativeApp
-add_task(function* test_native_app_shutdown() {
-  const SCRIPT = String.raw`#!${PYTHON} -u
-import signal
-import struct
-import sys
-
-signal.signal(signal.SIGTERM, signal.SIG_IGN)
-
-while True:
-    rawlen = sys.stdin.read(4)
-    if len(rawlen) == 0:
-        signal.pause()
-    msglen = struct.unpack('@I', rawlen)[0]
-    msg = sys.stdin.read(msglen)
-
-    sys.stdout.write(struct.pack('@I', msglen))
-    sys.stdout.write(msg)
-`;
-
-  let scriptPath = OS.Path.join(userDir.path, "wontdie.py");
-  yield OS.File.writeAtomic(scriptPath, SCRIPT);
-  yield OS.File.setPermissions(scriptPath, {unixMode: 0o755});
-
-  const ID = "native@tests.mozilla.org";
-  const MANIFEST = {
-    name: "wontdie",
-    description: "test async shutdown of native apps",
-    path: scriptPath,
-    type: "stdio",
-    allowed_extensions: [ID],
-  };
-  yield writeManifest(OS.Path.join(userDir.path, "wontdie.json"), MANIFEST);
-
-  let extension = {id: ID};
-  let app = new NativeApp(extension, context, "wontdie");
-
-  // send a message and wait for the reply to make sure the app is running
-  let MSG = "test";
-  let recvPromise = new Promise(resolve => {
-    let listener = (what, msg) => {
-      equal(msg, MSG, "Received test message");
-      app.off("message", listener);
-      resolve();
-    };
-    app.on("message", listener);
-  });
-
-  app.send(MSG);
-  yield recvPromise;
-
-  app._cleanup();
-
-  do_print("waiting for async shutdown");
-  Services.prefs.setBoolPref("toolkit.asyncshutdown.testing", true);
-  AsyncShutdown.profileBeforeChange._trigger();
-  Services.prefs.clearUserPref("toolkit.asyncshutdown.testing");
-
-  let procs = yield SubprocessImpl.Process.getWorker().call("getProcesses", []);
-  equal(procs.size, 0, "native process exited");
-});
-
--- a/toolkit/modules/subprocess/subprocess_worker_common.js
+++ b/toolkit/modules/subprocess/subprocess_worker_common.js
@@ -144,20 +144,16 @@ let requests = {
     return {data: new Set(io.pipes.keys())};
   },
 
   getProcesses() {
     let data = new Map(Array.from(io.processes.values(),
                                   proc => [proc.id, proc.pid]));
     return {data};
   },
-
-  waitForNoProcesses() {
-    return Promise.all(Array.from(io.processes.values(), proc => proc.exitPromise));
-  },
 };
 
 onmessage = event => {
   let {msg, msgId, args} = event.data;
 
   new Promise(resolve => {
     resolve(requests[msg](...args));
   }).then(result => {