Backed out changeset b117a76d0a25 (bug 1171638) for making Windows M(JP) basically permafail.
authorRyan VanderMeulen <ryanvm@gmail.com>
Fri, 05 Jun 2015 11:49:54 -0400
changeset 247417 a60028c73618d3b385997b4910e3ae25481bc9d5
parent 247398 920ded6a1f77189943310aa1fda0f38a20e5f824
child 247418 cffc40f56130f6b0614ba4e44e6047051fa66056
child 247472 6609b3b2ffc15c8fa14d0f88b156d9df5c25942c
child 247480 b0315d00af9e700a23e3b9b8b5387d3908ce8ea0
child 247489 970330c5d6f3b4caa9c0be0a2d3b470b33329bb0
push id60699
push userryanvm@gmail.com
push dateFri, 05 Jun 2015 15:51:07 +0000
treeherdermozilla-inbound@cffc40f56130 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1171638
milestone41.0a1
backs outb117a76d0a25cc13806cce4061b0c8de101a2403
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
Backed out changeset b117a76d0a25 (bug 1171638) for making Windows M(JP) basically permafail.
addon-sdk/moz.build
addon-sdk/source/.travis.yml
addon-sdk/source/bin/jpm-test.js
addon-sdk/source/bin/node-scripts/test.firefox-bin.js
addon-sdk/source/bin/node-scripts/test.ini.js
addon-sdk/source/bin/node-scripts/update-ini.js
addon-sdk/source/bin/node-scripts/utils.js
addon-sdk/source/gulpfile.js
addon-sdk/source/lib/sdk/console/traceback.js
addon-sdk/source/lib/sdk/content/sandbox.js
addon-sdk/source/lib/sdk/content/sandbox/events.js
addon-sdk/source/lib/sdk/deprecated/memory.js
addon-sdk/source/lib/sdk/deprecated/unit-test-finder.js
addon-sdk/source/lib/sdk/deprecated/unit-test.js
addon-sdk/source/lib/sdk/io/text-streams.js
addon-sdk/source/lib/sdk/places/host/host-query.js
addon-sdk/source/lib/sdk/places/utils.js
addon-sdk/source/lib/sdk/test/harness.js
addon-sdk/source/lib/sdk/test/memory.js
addon-sdk/source/lib/sdk/util/bond.js
addon-sdk/source/lib/sdk/zip/utils.js
addon-sdk/source/mapping.json
addon-sdk/source/package.json
addon-sdk/source/python-lib/cuddlefish/__init__.py
addon-sdk/source/python-lib/cuddlefish/tests/addons/simplest-test/main.js
addon-sdk/source/python-lib/cuddlefish/tests/addons/simplest-test/manifest-overload.json
addon-sdk/source/python-lib/cuddlefish/tests/addons/simplest-test/package.json
addon-sdk/source/python-lib/cuddlefish/tests/addons/simplest-test/tests/test-minimal.js
addon-sdk/source/python-lib/cuddlefish/tests/test_init.py
addon-sdk/source/test/addons/chrome/data/panel.js
addon-sdk/source/test/addons/chrome/main.js
addon-sdk/source/test/addons/layout-change/lib/test-cuddlefish-loader.js
addon-sdk/source/test/addons/places/lib/places-helper.js
addon-sdk/source/test/addons/places/lib/test-places-bookmarks.js
addon-sdk/source/test/addons/places/lib/test-places-events.js
addon-sdk/source/test/addons/places/lib/test-places-history.js
addon-sdk/source/test/addons/places/lib/test-places-host.js
addon-sdk/source/test/addons/require/list.js
addon-sdk/source/test/addons/require/main.js
addon-sdk/source/test/addons/require/memory.js
addon-sdk/source/test/fixtures/index.html
addon-sdk/source/test/fixtures/test-addon-extras-window.html
addon-sdk/source/test/fixtures/test-addon-extras.html
addon-sdk/source/test/fixtures/test.html
addon-sdk/source/test/jetpack-package.ini
addon-sdk/source/test/loader/user-global.js
addon-sdk/source/test/private-browsing/windows.js
addon-sdk/source/test/tabs/test-firefox-tabs.js
addon-sdk/source/test/test-addon-extras.js
addon-sdk/source/test/test-bond.js
addon-sdk/source/test/test-file.js
addon-sdk/source/test/test-loader.js
addon-sdk/source/test/test-memory.js
addon-sdk/source/test/test-mpl2-license-header.js
addon-sdk/source/test/test-unit-test.js
addon-sdk/source/test/test-weak-set.js
addon-sdk/source/test/test-windows-common.js
addon-sdk/source/test/test-xul-app.js
addon-sdk/source/test/windows/test-firefox-windows.js
--- a/addon-sdk/moz.build
+++ b/addon-sdk/moz.build
@@ -24,16 +24,17 @@ EXTRA_JS_MODULES.sdk.system += [
 if CONFIG['MOZ_WIDGET_TOOLKIT'] != "gonk":
     EXTRA_JS_MODULES.commonjs.method.test += [
         'source/lib/method/test/browser.js',
         'source/lib/method/test/common.js',
     ]
 
     EXTRA_JS_MODULES.commonjs.sdk.deprecated += [
         'source/lib/sdk/deprecated/api-utils.js',
+        'source/lib/sdk/deprecated/memory.js',
         'source/lib/sdk/deprecated/sync-worker.js',
         'source/lib/sdk/deprecated/unit-test-finder.js',
         'source/lib/sdk/deprecated/unit-test.js',
         'source/lib/sdk/deprecated/window-utils.js',
     ]
 
     EXTRA_JS_MODULES.commonjs.sdk.frame += [
         'source/lib/sdk/frame/hidden-frame.js',
@@ -252,20 +253,16 @@ EXTRA_JS_MODULES.commonjs.sdk.content +=
     'source/lib/sdk/content/sandbox.js',
     'source/lib/sdk/content/tab-events.js',
     'source/lib/sdk/content/thumbnail.js',
     'source/lib/sdk/content/utils.js',
     'source/lib/sdk/content/worker-child.js',
     'source/lib/sdk/content/worker.js',
 ]
 
-EXTRA_JS_MODULES.commonjs.sdk.content.sandbox += [
-    'source/lib/sdk/content/sandbox/events.js',
-]
-
 EXTRA_JS_MODULES.commonjs.sdk['context-menu'] += [
     'source/lib/sdk/context-menu/context.js',
     'source/lib/sdk/context-menu/core.js',
     'source/lib/sdk/context-menu/readers.js',
 ]
 
 EXTRA_JS_MODULES.commonjs.sdk.core += [
     'source/lib/sdk/core/disposable.js',
@@ -451,16 +448,17 @@ EXTRA_JS_MODULES.commonjs.sdk.uri += [
 ]
 
 EXTRA_JS_MODULES.commonjs.sdk.url += [
     'source/lib/sdk/url/utils.js',
 ]
 
 EXTRA_JS_MODULES.commonjs.sdk.util += [
     'source/lib/sdk/util/array.js',
+    'source/lib/sdk/util/bond.js',
     'source/lib/sdk/util/collection.js',
     'source/lib/sdk/util/contract.js',
     'source/lib/sdk/util/deprecate.js',
     'source/lib/sdk/util/dispatcher.js',
     'source/lib/sdk/util/list.js',
     'source/lib/sdk/util/match-pattern.js',
     'source/lib/sdk/util/object.js',
     'source/lib/sdk/util/rules.js',
--- a/addon-sdk/source/.travis.yml
+++ b/addon-sdk/source/.travis.yml
@@ -1,12 +1,12 @@
 sudo: false
 language: node_js
 node_js:
-  - "0.12"
+  - "0.10"
 
 env:
   - JPM_FX_DEBUG=0
   - JPM_FX_DEBUG=1
 
 notifications:
   irc: "irc.mozilla.org#jetpack"
 
--- a/addon-sdk/source/bin/jpm-test.js
+++ b/addon-sdk/source/bin/jpm-test.js
@@ -12,23 +12,22 @@ var mocha = new Mocha({
 });
 
 var isDebug = require("./node-scripts/utils").isDebug;
 
 exports.run = function(type) {
   return new Promise(function(resolve) {
     type = type || "";
     [
-      (!isDebug && /^(firefox-bin)?$/.test(type)) && require.resolve("../bin/node-scripts/test.firefox-bin"),
+      (!isDebug && /^(modules)?$/.test(type)) && require.resolve("../bin/node-scripts/test.modules"),
+      (!isDebug && /^(addons)?$/.test(type)) && require.resolve("../bin/node-scripts/test.addons"),
+      (/^(examples)?$/.test(type)) && require.resolve("../bin/node-scripts/test.examples"),
       (!isDebug && /^(docs)?$/.test(type)) && require.resolve("../bin/node-scripts/test.docs"),
       (!isDebug && /^(ini)?$/.test(type)) && require.resolve("../bin/node-scripts/test.ini"),
-      (/^(examples)?$/.test(type)) && require.resolve("../bin/node-scripts/test.examples"),
-      (!isDebug && /^(addons)?$/.test(type)) && require.resolve("../bin/node-scripts/test.addons"),
-      (!isDebug && /^(modules)?$/.test(type)) && require.resolve("../bin/node-scripts/test.modules"),
-    ].forEach(function(filepath) {
+    ].sort().forEach(function(filepath) {
       filepath && mocha.addFile(filepath);
     })
 
     mocha.run(function(failures) {
       resolve(failures);
     });
   });
 }
deleted file mode 100644
--- a/addon-sdk/source/bin/node-scripts/test.firefox-bin.js
+++ /dev/null
@@ -1,37 +0,0 @@
-/* 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";
-
-var fs = require("fs");
-var Promise = require("promise");
-var chai = require("chai");
-var expect = chai.expect;
-var normalizeBinary = require("fx-runner/lib/utils").normalizeBinary;
-
-//var firefox_binary = process.env["JPM_FIREFOX_BINARY"] || normalizeBinary("nightly");
-
-describe("Checking Firefox binary", function () {
-
-  it("using matching fx-runner version with jpm", function () {
-    var sdkPackageJSON = require("../../package.json");
-    var jpmPackageINI = require("jpm/package.json");
-    expect(sdkPackageJSON.devDependencies["fx-runner"]).to.be.equal(jpmPackageINI.dependencies["fx-runner"]);
-  });
-
-  it("exists", function (done) {
-    var useEnvVar = new Promise(function(resolve) {
-      resolve(process.env["JPM_FIREFOX_BINARY"]);
-    });
-
-    var firefox_binary = process.env["JPM_FIREFOX_BINARY"] ? useEnvVar : normalizeBinary("nightly");
-    firefox_binary.then(function(path) {
-      expect(path).to.be.ok;
-      fs.exists(path, function (exists) {
-        expect(exists).to.be.ok;
-        done();
-      });
-    })
-  });
-
-});
--- a/addon-sdk/source/bin/node-scripts/test.ini.js
+++ b/addon-sdk/source/bin/node-scripts/test.ini.js
@@ -6,63 +6,34 @@
 var fs = require("fs");
 var path = require("path");
 var Promise = require("promise");
 var chai = require("chai");
 var expect = chai.expect;
 var ini = require("./update-ini");
 
 var addonINI = path.resolve("./test/addons/jetpack-addon.ini");
-var packageINI = path.resolve("./test/jetpack-package.ini");
 
 describe("Checking ini files", function () {
 
   it("Check test/addons/jetpack-addon.ini", function (done) {
 
     fs.readFile(addonINI, function (err, data) {
       if (err) {
         throw err;
       }
-      // filter comments
-      var text = data.toString().split("\n").filter(function(line) {
-        return !/^\s*#/.test(line);
-      }).join("\n");
+      var text = data.toString();
       var expected = "";
 
       ini.makeAddonIniContent()
       .then(function(contents) {
         expected = contents;
 
         setTimeout(function end() {
-          expect(text.trim()).to.be.equal(expected.trim());
-          done();
-        });
-      });
-    });
-
-  });
-
-  it("Check test/jetpack-package.ini", function (done) {
-
-    fs.readFile(packageINI, function (err, data) {
-      if (err) {
-        throw err;
-      }
-      // filter comments
-      var text = data.toString().split("\n").filter(function(line) {
-        return !/^\s*#/.test(line);
-      }).join("\n");
-      var expected = "";
-
-      ini.makePackageIniContent()
-      .then(function(contents) {
-        expected = contents;
-
-        setTimeout(function end() {
-          expect(text.trim()).to.be.equal(expected.trim());
+          expect(expected.trim()).to.be.equal(text.trim());
           done();
         });
       });
     });
 
   });
 
 });
--- a/addon-sdk/source/bin/node-scripts/update-ini.js
+++ b/addon-sdk/source/bin/node-scripts/update-ini.js
@@ -6,25 +6,16 @@
 var path = require("path");
 var cp = require("child_process");
 var fs = require("fs");
 var Promise = require("promise");
 var parser = require("ini-parser");
 
 var addonINI = path.resolve("./test/addons/jetpack-addon.ini");
 var addonsDir = path.resolve("./test/addons/");
-var packageINI = path.resolve("./test/jetpack-package.ini");
-var packageDir = path.resolve("./test/");
-var packageIgnorables = [ "addons", "preferences" ];
-var packageSupportFiles = [
-  "fixtures.js",
-  "pagemod-test-helpers.js",
-  "test-context-menu.html",
-  "util.js"
-]
 
 function updateAddonINI() {
   return new Promise(function(resolve) {
     console.log("Start updating " + addonINI);
 
     makeAddonIniContent().
     then(function(contents) {
       fs.writeFileSync(addonINI, contents, { encoding: "utf8" });
@@ -36,107 +27,32 @@ function updateAddonINI() {
 exports.updateAddonINI = updateAddonINI;
 
 function makeAddonIniContent() {
   return new Promise(function(resolve) {
     var data = parser.parse(fs.readFileSync(addonINI, { encoding: "utf8" }).toString());
     var result = {};
 
     fs.readdir(addonsDir, function(err, files) {
-      // get a list of folders
       var folders = files.filter(function(file) {
         return fs.statSync(path.resolve(addonsDir, file)).isDirectory();
       }).sort();
 
-      // copy any related data from the existing ini
       folders.forEach(function(folder) {
         var oldData = data[folder + ".xpi"];
         result[folder] = oldData ? oldData : {};
       });
 
-      // build a new ini file
+      // build ini file
       var contents = [];
       Object.keys(result).sort().forEach(function(key) {
         contents.push("[" + key + ".xpi]");
         Object.keys(result[key]).forEach(function(dataKey) {
           contents.push(dataKey + " = " + result[key][dataKey]);
         });
       });
       contents = contents.join("\n") + "\n";
 
       return resolve(contents);
     });
   });
 }
 exports.makeAddonIniContent = makeAddonIniContent;
-
-function makePackageIniContent() {
-  return new Promise(function(resolve) {
-    var data = parser.parse(fs.readFileSync(packageINI, { encoding: "utf8" }).toString());
-    var result = {};
-
-    fs.readdir(packageDir, function(err, files) {
-      // get a list of folders
-      var folders = files.filter(function(file) {
-        var ignore = (packageIgnorables.indexOf(file) >= 0);
-        var isDir = fs.statSync(path.resolve(packageDir, file)).isDirectory();
-        return (isDir && !ignore);
-      }).sort();
-
-      // get a list of "test-"" files
-      var files = files.filter(function(file) {
-        var ignore = !/^test\-.*\.js$/i.test(file);
-        var isDir = fs.statSync(path.resolve(packageDir, file)).isDirectory();
-        return (!isDir && !ignore);
-      }).sort();
-
-      // get a list of the support files
-      var support_files = packageSupportFiles.map(function(file) {
-        return "  " + file;
-      });
-      folders.forEach(function(folder) {
-        support_files.push("  " + folder + "/**");
-      });
-      support_files = support_files.sort();
-
-      // copy any related data from the existing ini
-      files.forEach(function(file) {
-        var oldData = data[file];
-        result[file] = oldData ? oldData : {};
-      });
-
-      // build a new ini file
-      var contents = [
-        "[DEFAULT]",
-        "support-files ="
-      ];
-      support_files.forEach(function(support_file) {
-        contents.push(support_file);
-      });
-      contents.push("");
-
-      Object.keys(result).sort().forEach(function(key) {
-        contents.push("[" + key + "]");
-        Object.keys(result[key]).forEach(function(dataKey) {
-          contents.push(dataKey + " = " + result[key][dataKey]);
-        });
-      });
-      contents = contents.join("\n") + "\n";
-
-      return resolve(contents);
-    });
-  });
-}
-exports.makePackageIniContent = makePackageIniContent;
-
-function updatePackageINI() {
-  return new Promise(function(resolve) {
-    console.log("Start updating " + packageINI);
-
-    makeAddonIniContent().
-    then(function(contents) {
-      fs.writeFileSync(packageINI, contents, { encoding: "utf8" });
-      console.log("Done updating " + packageINI);
-      resolve();
-    });
-  })
-}
-exports.updatePackageINI = updatePackageINI;
--- a/addon-sdk/source/bin/node-scripts/utils.js
+++ b/addon-sdk/source/bin/node-scripts/utils.js
@@ -60,19 +60,16 @@ function run (cmd, options, p) {
       }
       output.push(data);
       return null;
     });
 
     if (p) {
       proc.stdout.pipe(p.stdout);
     }
-    else if (!isDebug) {
-      proc.stdout.pipe(DEFAULT_PROCESS.stdout);
-    }
     else {
       proc.stdout.on("data", function (data) {
         data = (data || "") + "";
         if (/TEST-/.test(data)) {
           DEFAULT_PROCESS.stdout.write(data.replace(/[\s\n]+$/, "") + "\n");
         }
       });
     }
--- a/addon-sdk/source/gulpfile.js
+++ b/addon-sdk/source/gulpfile.js
@@ -23,22 +23,22 @@ gulp.task('test:examples', function(done
   require("./bin/jpm-test").run("examples").catch(console.error).then(done);
 });
 
 gulp.task('test:modules', function(done) {
   require("./bin/jpm-test").run("modules").catch(console.error).then(done);
 });
 
 gulp.task('test:ini', function(done) {
-  require("./bin/jpm-test").run("ini").catch(console.error).then(done);
-});
-
-gulp.task('test:firefox-bin', function(done) {
-  require("./bin/jpm-test").run("firefox-bin").catch(console.error).then(done);
+  test("ini").catch(console.error).then(done);
 });
 
 gulp.task('patch:clean', function(done) {
   patch.clean().catch(console.error).then(done);
 });
 
 gulp.task('patch:apply', function(done) {
   patch.apply().catch(console.error).then(done);
 });
+
+gulp.task('update:ini', function(done) {
+  ini.updateAddonINI().catch(console.error).then(done);
+});
--- a/addon-sdk/source/lib/sdk/console/traceback.js
+++ b/addon-sdk/source/lib/sdk/console/traceback.js
@@ -1,21 +1,24 @@
 /* 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";
 
 module.metadata = {
   "stability": "experimental"
 };
 
-const { Ci, components } = require("chrome");
+const { Cc, Ci, components } = require("chrome");
 const { parseStack, sourceURI } = require("toolkit/loader");
 const { readURISync } = require("../net/url");
 
+exports.sourceURI = sourceURI
+
 function safeGetFileLine(path, line) {
   try {
     var scheme = require("../url").URL(path).scheme;
     // TODO: There should be an easier, more accurate way to figure out
     // what's the case here.
     if (!(scheme == "http" || scheme == "https"))
       return readURISync(path).split("\n")[line - 1];
   } catch (e) {}
--- a/addon-sdk/source/lib/sdk/content/sandbox.js
+++ b/addon-sdk/source/lib/sdk/content/sandbox.js
@@ -5,28 +5,28 @@
 
 module.metadata = {
   'stability': 'unstable'
 };
 
 const { Class } = require('../core/heritage');
 const { EventTarget } = require('../event/target');
 const { on, off, emit } = require('../event/core');
-const { events } = require('./sandbox/events');
 const { requiresAddonGlobal } = require('./utils');
 const { delay: async } = require('../lang/functional');
 const { Ci, Cu, Cc } = require('chrome');
 const timer = require('../timers');
 const { URL } = require('../url');
 const { sandbox, evaluate, load } = require('../loader/sandbox');
 const { merge } = require('../util/object');
 const { getTabForContentWindow } = require('../tabs/utils');
 const { getInnerId } = require('../window/utils');
 const { PlainTextConsole } = require('../console/plain-text');
-const { data } = require('../self');const { isChildLoader } = require('../remote/core');
+const { data } = require('../self');
+const { isChildLoader } = require('../remote/core');
 // WeakMap of sandboxes so we can access private values
 const sandboxes = new WeakMap();
 
 /* Trick the linker in order to ensure shipping these files in the XPI.
   require('./content-worker.js');
   Then, retrieve URL of these files in the XPI:
 */
 let prefix = module.uri.split('sandbox.js')[0];
@@ -161,17 +161,16 @@ const WorkerSandbox = Class({
     let top = window.top === window ? content : content.top;
     let parent = window.parent === window ? content : content.parent;
     merge(content, {
       // We need 'this === window === top' to be true in toplevel scope:
       get window() content,
       get top() top,
       get parent() parent
     });
-
     // Use the Greasemonkey naming convention to provide access to the
     // unwrapped window object so the content script can access document
     // JavaScript values.
     // NOTE: this functionality is experimental and may change or go away
     // at any time!
     //
     // Note that because waivers aren't propagated between origins, we
     // need the unsafeWindow getter to live in the sandbox.
@@ -257,34 +256,28 @@ const WorkerSandbox = Class({
       };
 
       Object.defineProperties(con, properties);
       Cu.makeObjectPropsNormal(con);
 
       win.console = con;
     };
 
-    emit(events, "content-script-before-inserted", {
-      window: window,
-      worker: worker
-    });
-
     // The order of `contentScriptFile` and `contentScript` evaluation is
     // intentional, so programs can load libraries like jQuery from script URLs
     // and use them in scripts.
     let contentScriptFile = ('contentScriptFile' in worker)
           ? worker.contentScriptFile
           : null,
         contentScript = ('contentScript' in worker)
           ? worker.contentScript
           : null;
 
     if (contentScriptFile)
       importScripts.apply(null, [this].concat(contentScriptFile));
-
     if (contentScript) {
       evaluateIn(
         this,
         Array.isArray(contentScript) ? contentScript.join(';\n') : contentScript
       );
     }
   },
   destroy: function destroy(reason) {
deleted file mode 100644
--- a/addon-sdk/source/lib/sdk/content/sandbox/events.js
+++ /dev/null
@@ -1,12 +0,0 @@
-/* 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";
-
-module.metadata = {
-  "stability": "experimental"
-};
-
-const events = {};
-exports.events = events;
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/lib/sdk/deprecated/memory.js
@@ -0,0 +1,129 @@
+/* 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";
+
+module.metadata = {
+  "stability": "deprecated"
+};
+
+const { Cc, Ci, Cu, components } = require("chrome");
+const { when: unload } = require("../system/unload")
+
+var trackedObjects = {};
+const Compacter = {
+  notify: function() {
+    var newTrackedObjects = {};
+
+    for (let name in trackedObjects) {
+      let oldBin = trackedObjects[name];
+      let newBin = [];
+      let strongRefs = [];
+
+      for (let i = 0, l = oldBin.length; i < l; i++) {
+        let strongRef = oldBin[i].weakref.get();
+
+        if (strongRef && strongRefs.indexOf(strongRef) == -1) {
+          strongRefs.push(strongRef);
+          newBin.push(oldBin[i]);
+        }
+      }
+
+      if (newBin.length)
+        newTrackedObjects[name] = newBin;
+    }
+
+    trackedObjects = newTrackedObjects;
+  }
+};
+
+var timer = Cc["@mozilla.org/timer;1"]
+            .createInstance(Ci.nsITimer);
+timer.initWithCallback(Compacter,
+                       5000,
+                       Ci.nsITimer.TYPE_REPEATING_SLACK);
+
+function track(object, bin, stackFrameNumber) {
+  var frame = components.stack.caller;
+  var weakref = Cu.getWeakReference(object);
+
+  if (!bin && 'constructor' in object)
+    bin = object.constructor.name;
+  if (bin == "Object")
+    bin = frame.name;
+  if (!bin)
+    bin = "generic";
+  if (!(bin in trackedObjects))
+    trackedObjects[bin] = [];
+
+  if (stackFrameNumber > 0)
+    for (var i = 0; i < stackFrameNumber; i++)
+      frame = frame.caller;
+
+  trackedObjects[bin].push({weakref: weakref,
+                            created: new Date(),
+                            filename: frame.filename,
+                            lineNo: frame.lineNumber,
+                            bin: bin});
+}
+exports.track = track;
+
+var getBins = exports.getBins = function getBins() {
+  var names = [];
+  for (let name in trackedObjects)
+    names.push(name);
+  return names;
+};
+
+function getObjects(bin) {
+  var results = [];
+
+  function getLiveObjectsInBin(bin) {
+    for (let i = 0, l = bin.length; i < l; i++) {
+      let object = bin[i].weakref.get();
+
+      if (object) {
+        results.push(bin[i]);
+      }
+    }
+  }
+
+  if (bin) {
+    if (bin in trackedObjects)
+      getLiveObjectsInBin(trackedObjects[bin]);
+  }
+  else {
+    for (let name in trackedObjects)
+      getLiveObjectsInBin(trackedObjects[name]);
+  }
+
+  return results;
+}
+exports.getObjects = getObjects;
+
+function gc() {
+  // Components.utils.forceGC() doesn't currently perform
+  // cycle collection, which means that e.g. DOM elements
+  // won't be collected by it. Fortunately, there are
+  // other ways...
+  var test_utils = Cc["@mozilla.org/appshell/appShellService;1"]
+               .getService(Ci.nsIAppShellService)
+               .hiddenDOMWindow
+               .QueryInterface(Ci.nsIInterfaceRequestor)
+               .getInterface(Ci.nsIDOMWindowUtils);
+  test_utils.garbageCollect();
+  // Clean metadata for dead objects
+  Compacter.notify();
+  // Not sure why, but sometimes it appears that we don't get
+  // them all with just one CC, so let's do it again.
+  test_utils.garbageCollect();
+};
+exports.gc = gc;
+
+unload(_ => {
+  trackedObjects = {};
+  if (timer) {
+    timer.cancel();
+    timer = null;
+  }
+});
--- a/addon-sdk/source/lib/sdk/deprecated/unit-test-finder.js
+++ b/addon-sdk/source/lib/sdk/deprecated/unit-test-finder.js
@@ -3,16 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 module.metadata = {
   "stability": "deprecated"
 };
 
 const file = require("../io/file");
+const memory = require('./memory');
 const { Loader } = require("../test/loader");
 
 const { isNative } = require('@loader/options');
 
 const cuddlefish = isNative ? require("toolkit/loader") : require("../loader/cuddlefish");
 
 const { defer, resolve } = require("../core/promise");
 const { getAddon } = require("../addon/installer");
@@ -126,16 +127,17 @@ const makeFilters = function makeFilters
   };
 }
 exports.makeFilters = makeFilters;
 
 let loader = Loader(module);
 const NOT_TESTS = ['setup', 'teardown'];
 
 var TestFinder = exports.TestFinder = function TestFinder(options) {
+  memory.track(this);
   this.filter = options.filter;
   this.testInProcess = options.testInProcess === false ? false : true;
   this.testOutOfProcess = options.testOutOfProcess === true ? true : false;
 };
 
 TestFinder.prototype = {
   findTests: function findTests() {
     let { fileFilter, testFilter } = makeFilters({ filter: this.filter });
--- a/addon-sdk/source/lib/sdk/deprecated/unit-test.js
+++ b/addon-sdk/source/lib/sdk/deprecated/unit-test.js
@@ -2,16 +2,17 @@
  * 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";
 
 module.metadata = {
   "stability": "deprecated"
 };
 
+const memory = require("./memory");
 const timer = require("../timers");
 const cfxArgs = require("../test/options");
 const { getTabs, closeTab, getURI, getTabId, getSelectedTab } = require("../tabs/utils");
 const { windows, isBrowser, getMostRecentBrowserWindow } = require("../window/utils");
 const { defer, all, Debugging: PromiseDebugging, resolve } = require("../core/promise");
 const { getInnerId } = require("../window/utils");
 const { cleanUI } = require("../test/utils");
 
@@ -41,16 +42,17 @@ const TestRunner = function TestRunner(o
 
   // remember the id's for the open window and tab
   let window = getMostRecentBrowserWindow();
   runnerWindows.set(this, getInnerId(window));
   runnerTabs.set(this, getTabId(getSelectedTab(window)));
 
   this.fs = options.fs;
   this.console = options.console || console;
+  memory.track(this);
   this.passed = 0;
   this.failed = 0;
   this.testRunSummary = [];
   this.expectFailNesting = 0;
   this.done = TestRunner.prototype.done.bind(this);
 };
 
 TestRunner.prototype = {
@@ -276,56 +278,50 @@ TestRunner.prototype = {
   },
 
   done: function done() {
     if (this.isDone) {
       return resolve();
     }
 
     this.isDone = true;
-    this.pass("This test is done.");
-
     if (this.test.teardown) {
       this.test.teardown(this);
     }
-
     if (this.waitTimeout !== null) {
       timer.clearTimeout(this.waitTimeout);
       this.waitTimeout = null;
     }
-
     // Do not leave any callback set when calling to `waitUntil`
     this.waitUntilCallback = null;
     if (this.test.passed == 0 && this.test.failed == 0) {
       this._logTestFailed("empty test");
-
       if ("testMessage" in this.console) {
         this.console.testMessage(false, false, this.test.name, "Empty test");
       }
       else {
         this.console.error("fail:", "Empty test")
       }
-
       this.failed++;
       this.test.failed++;
     }
 
     let wins = windows(null, { includePrivate: true });
-    let winPromises = wins.map(win => {
-      return new Promise(resolve => {
-        if (["interactive", "complete"].indexOf(win.document.readyState) >= 0) {
-          resolve()
-        }
-        else {
-          win.addEventListener("DOMContentLoaded", function onLoad() {
-            win.removeEventListener("DOMContentLoaded", onLoad, false);
-            resolve();
-          }, false);
-        }
-      });
+    let winPromises = wins.map(win =>  {
+      let { promise, resolve } = defer();
+      if (["interactive", "complete"].indexOf(win.document.readyState) >= 0) {
+        resolve()
+      }
+      else {
+        win.addEventListener("DOMContentLoaded", function onLoad() {
+          win.removeEventListener("DOMContentLoaded", onLoad, false);
+          resolve();
+        }, false);
+      }
+      return promise;
     });
 
     PromiseDebugging.flushUncaughtErrors();
     PromiseDebugging.removeUncaughtErrorObserver(this._uncaughtErrorObserver);
 
 
     return all(winPromises).then(() => {
       let browserWins = wins.filter(isBrowser);
@@ -357,27 +353,19 @@ TestRunner.prototype = {
             console.log(win.location + " - " + tabs.map(getURI).join(", "));
           }
           else {
             console.log(win.location);
           }
         }
       }
 
-      return failure;
+      return null;
     }).
-    then(failure => {
-      if (!failure) {
-        this.pass("There was a clean UI.");
-        return null;
-      }
-      return cleanUI().then(() => {
-        this.pass("There is a clean UI.");
-      });
-    }).
+    then(cleanUI).
     then(() => {
       this.testRunSummary.push({
         name: this.test.name,
         passed: this.test.passed,
         failed: this.test.failed,
         errors: [error for (error in this.test.errors)].join(", ")
       });
 
--- a/addon-sdk/source/lib/sdk/io/text-streams.js
+++ b/addon-sdk/source/lib/sdk/io/text-streams.js
@@ -1,41 +1,46 @@
 /* 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";
 
 module.metadata = {
   "stability": "experimental"
 };
 
-const { Cc, Ci, Cu, components } = require("chrome");
-const { ensure } = require("../system/unload");
-const { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {});
+const {Cc,Ci,Cu,components} = require("chrome");
+var NetUtil = {};
+Cu.import("resource://gre/modules/NetUtil.jsm", NetUtil);
+NetUtil = NetUtil.NetUtil;
 
 // NetUtil.asyncCopy() uses this buffer length, and since we call it, for best
 // performance we use it, too.
 const BUFFER_BYTE_LEN = 0x8000;
 const PR_UINT32_MAX = 0xffffffff;
 const DEFAULT_CHARSET = "UTF-8";
 
+exports.TextReader = TextReader;
+exports.TextWriter = TextWriter;
 
 /**
  * An input stream that reads text from a backing stream using a given text
  * encoding.
  *
  * @param inputStream
  *        The stream is backed by this nsIInputStream.  It must already be
  *        opened.
  * @param charset
  *        Text in inputStream is expected to be in this character encoding.  If
  *        not given, "UTF-8" is assumed.  See nsICharsetConverterManager.idl for
  *        documentation on how to determine other valid values for this.
  */
 function TextReader(inputStream, charset) {
+  const self = this;
   charset = checkCharset(charset);
 
   let stream = Cc["@mozilla.org/intl/converter-input-stream;1"].
                createInstance(Ci.nsIConverterInputStream);
   stream.init(inputStream, charset, BUFFER_BYTE_LEN,
               Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
 
   let manager = new StreamManager(this, stream);
@@ -79,31 +84,31 @@ function TextReader(inputStream, charset
       chunkRead = stream.readString(toRead, chunk);
       str += chunk.value;
       totalRead += chunkRead;
     }
 
     return str;
   };
 }
-exports.TextReader = TextReader;
 
 /**
  * A buffered output stream that writes text to a backing stream using a given
  * text encoding.
  *
  * @param outputStream
  *        The stream is backed by this nsIOutputStream.  It must already be
  *        opened.
  * @param charset
  *        Text will be written to outputStream using this character encoding.
  *        If not given, "UTF-8" is assumed.  See nsICharsetConverterManager.idl
  *        for documentation on how to determine other valid values for this.
  */
 function TextWriter(outputStream, charset) {
+  const self = this;
   charset = checkCharset(charset);
 
   let stream = outputStream;
 
   // Buffer outputStream if it's not already.
   let ioUtils = Cc["@mozilla.org/io-util;1"].getService(Ci.nsIIOUtil);
   if (!ioUtils.outputStreamIsBuffered(outputStream)) {
     stream = Cc["@mozilla.org/network/buffered-output-stream;1"].
@@ -159,62 +164,64 @@ function TextWriter(outputStream, charse
    * @param callback
    *        An optional function.  If given, it's called as callback(error) when
    *        the write completes.  error is an Error object or undefined if there
    *        was no error.  Inside callback, |this| is the stream object.
    */
   this.writeAsync = function TextWriter_writeAsync(str, callback) {
     manager.ensureOpened();
     let istream = uconv.convertToInputStream(str);
-    NetUtil.asyncCopy(istream, stream, (result) => {
+    NetUtil.asyncCopy(istream, stream, function (result) {
         let err = components.isSuccessCode(result) ? undefined :
         new Error("An error occured while writing to the stream: " + result);
       if (err)
         console.error(err);
 
       // asyncCopy() closes its output (and input) stream.
       manager.opened = false;
 
       if (typeof(callback) === "function") {
         try {
-          callback.call(this, err);
+          callback.call(self, err);
         }
         catch (exc) {
           console.exception(exc);
         }
       }
     });
   };
 }
-exports.TextWriter = TextWriter;
 
 // This manages the lifetime of stream, a TextReader or TextWriter.  It defines
 // closed and close() on stream and registers an unload listener that closes
 // rawStream if it's still opened.  It also provides ensureOpened(), which
 // throws an exception if the stream is closed.
 function StreamManager(stream, rawStream) {
+  const self = this;
   this.rawStream = rawStream;
   this.opened = true;
 
   /**
    * True iff the stream is closed.
    */
-  stream.__defineGetter__("closed", () => !this.opened);
+  stream.__defineGetter__("closed", function stream_closed() {
+    return !self.opened;
+  });
 
   /**
    * Closes both the stream and its backing stream.  If the stream is already
    * closed, an exception is thrown.  For TextWriters, this first flushes the
    * backing stream's buffer.
    */
-  stream.close = () => {
-    this.ensureOpened();
-    this.unload();
+  stream.close = function stream_close() {
+    self.ensureOpened();
+    self.unload();
   };
 
-  ensure(this);
+  require("../system/unload").ensure(this);
 }
 
 StreamManager.prototype = {
   ensureOpened: function StreamManager_ensureOpened() {
     if (!this.opened)
       throw new Error("The stream is closed and cannot be used.");
   },
   unload: function StreamManager_unload() {
--- a/addon-sdk/source/lib/sdk/places/host/host-query.js
+++ b/addon-sdk/source/lib/sdk/places/host/host-query.js
@@ -1,23 +1,24 @@
 /* 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";
 
 module.metadata = {
   "stability": "experimental",
   "engines": {
     "Firefox": "*",
     "SeaMonkey": "*"
   }
 };
 
 const { Cc, Ci } = require('chrome');
-const { all } = require('../../core/promise');
+const { defer, all, resolve } = require('../../core/promise');
 const { safeMerge, omit } = require('../../util/object');
 const historyService = Cc['@mozilla.org/browser/nav-history-service;1']
                      .getService(Ci.nsINavHistoryService);
 const bookmarksService = Cc['@mozilla.org/browser/nav-bookmarks-service;1']
                          .getService(Ci.nsINavBookmarksService);
 const { request, response } = require('../../addon/host');
 const { newURI } = require('../../url/utils');
 const { send } = require('../../addon/events');
@@ -39,21 +40,23 @@ const MANUAL_QUERY_PROPERTIES = [
   'uri', 'folder', 'tags', 'url', 'folder'
 ];
 
 const PLACES_PROPERTIES = [
   'uri', 'title', 'accessCount', 'time'
 ];
 
 function execute (queries, options) {
-  return new Promise(resolve => {
-    let root = historyService
-        .executeQueries(queries, queries.length, options).root;
-    resolve(collect([], root));
-  });
+  let deferred = defer();
+  let root = historyService
+    .executeQueries(queries, queries.length, options).root;
+
+  let items = collect([], root);
+  deferred.resolve(items);
+  return deferred.promise;
 }
 
 function collect (acc, node) {
   node.containerOpen = true;
   for (let i = 0; i < node.childCount; i++) {
     let child = node.getChild(i);
     acc.push(child);
     if (child.type === child.RESULT_TYPE_FOLDER) {
@@ -61,45 +64,50 @@ function collect (acc, node) {
       collect(acc, container);
     }
   }
   node.containerOpen = false;
   return acc;
 }
 
 function query (queries, options) {
-  return new Promise((resolve, reject) => {
-    queries = queries || [];
-    options = options || {};
-    let optionsObj, queryObjs;
+  queries = queries || [];
+  options = options || {}; 
+  let deferred = defer();
+  let optionsObj, queryObjs;
 
+  try {
     optionsObj = historyService.getNewQueryOptions();
     queryObjs = [].concat(queries).map(createQuery);
     if (!queryObjs.length) {
       queryObjs = [historyService.getNewQuery()];
     }
     safeMerge(optionsObj, options);
+  } catch (e) {
+    deferred.reject(e);
+    return deferred.promise;
+  }
 
-    /*
-     * Currently `places:` queries are not supported
-     */
-    optionsObj.excludeQueries = true;
+  /*
+   * Currently `places:` queries are not supported
+   */
+  optionsObj.excludeQueries = true;
 
-    execute(queryObjs, optionsObj).then((results) => {
-      if (optionsObj.queryType === 0) {
-        return results.map(normalize);
-      }
-      else if (optionsObj.queryType === 1) {
-        // Formats query results into more standard
-        // data structures for returning
-        return all(results.map(({itemId}) =>
-          send('sdk-places-bookmarks-get', { id: itemId })));
-      }
-    }).then(resolve, reject);
-  });
+  execute(queryObjs, optionsObj).then(function (results) {
+    if (optionsObj.queryType === 0) {
+      return results.map(normalize);
+    } else if (optionsObj.queryType === 1) {
+      // Formats query results into more standard
+      // data structures for returning
+      return all(results.map(({itemId}) =>
+        send('sdk-places-bookmarks-get', { id: itemId })));
+    }
+  }).then(deferred.resolve, deferred.reject);
+  
+  return deferred.promise;
 }
 exports.query = query;
 
 function createQuery (query) {
   query = query || {};
   let queryObj = historyService.getNewQuery();
 
   safeMerge(queryObj, omit(query, MANUAL_QUERY_PROPERTIES));
@@ -127,28 +135,27 @@ function queryReceiver (message) {
   }, reason => {
     resData.error = reason;
     respond(resData);
   });
 }
 
 /*
  * Converts a nsINavHistoryResultNode into a plain object
- *
+ * 
  * https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/nsINavHistoryResultNode
  */
 function normalize (historyObj) {
   return PLACES_PROPERTIES.reduce((obj, prop) => {
     if (prop === 'uri')
       obj.url = historyObj.uri;
     else if (prop === 'time') {
       // Cast from microseconds to milliseconds
       obj.time = Math.floor(historyObj.time / 1000)
-    }
-    else if (prop === 'accessCount')
+    } else if (prop === 'accessCount')
       obj.visitCount = historyObj[prop];
     else
       obj[prop] = historyObj[prop];
     return obj;
   }, {});
 }
 
 /*
--- a/addon-sdk/source/lib/sdk/places/utils.js
+++ b/addon-sdk/source/lib/sdk/places/utils.js
@@ -46,24 +46,24 @@ let TreeNode = Class({
   walk: method(walk),
   toString: function () '[object TreeNode]'
 });
 exports.TreeNode = TreeNode;
 
 /*
  * Descends down from `node` applying `fn` to each in order.
  * `fn` can return values or promises -- if promise returned,
- * children are not processed until resolved. `fn` is passed
+ * children are not processed until resolved. `fn` is passed 
  * one argument, the current node, `curr`.
  */
 function walk (curr, fn) {
   return promised(fn)(curr).then(val => {
     return all(curr.children.map(child => walk(child, fn)));
   });
-}
+} 
 
 /*
  * Descends from the TreeNode `node`, returning
  * the node with value `value` if found or `null`
  * otherwise
  */
 function get (node, value) {
   if (node.value === value) return node;
@@ -117,17 +117,17 @@ function isRootGroup (id) {
     bmsrv.unfiledBookmarksFolder
   ].indexOf(id);
 }
 exports.isRootGroup = isRootGroup;
 
 /*
  * Merges appropriate options into query based off of url
  * 4 scenarios:
- *
+ * 
  * 'moz.com' // domain: moz.com, domainIsHost: true
  *    --> 'http://moz.com', 'http://moz.com/thunderbird'
  * '*.moz.com' // domain: moz.com, domainIsHost: false
  *    --> 'http://moz.com', 'http://moz.com/index', 'http://ff.moz.com/test'
  * 'http://moz.com' // url: http://moz.com/, urlIsPrefix: false
  *    --> 'http://moz.com/'
  * 'http://moz.com/*' // url: http://moz.com/, urlIsPrefix: true
  *    --> 'http://moz.com/', 'http://moz.com/thunderbird'
@@ -172,34 +172,34 @@ exports.promisedEmitter = promisedEmitte
 
 
 // https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/nsINavHistoryQueryOptions
 function createQuery (type, query) {
   query = query || {};
   let qObj = {
     searchTerms: query.query
   };
-
+     
   urlQueryParser(qObj, query.url);
-
+  
   // 0 === history
   if (type === 0) {
     // PRTime used by query is in microseconds, not milliseconds
     qObj.beginTime = (query.from || 0) * 1000;
     qObj.endTime = (query.to || new Date()) * 1000;
 
     // Set reference time to Epoch
     qObj.beginTimeReference = 0;
     qObj.endTimeReference = 0;
   }
   // 1 === bookmarks
   else if (type === 1) {
     qObj.tags = query.tags;
     qObj.folder = query.group && query.group.id;
-  }
+  } 
   // 2 === unified (not implemented on platform)
   else if (type === 2) {
 
   }
 
   return qObj;
 }
 exports.createQuery = createQuery;
--- a/addon-sdk/source/lib/sdk/test/harness.js
+++ b/addon-sdk/source/lib/sdk/test/harness.js
@@ -10,16 +10,17 @@ module.metadata = {
 const { Cc, Ci, Cu } = require("chrome");
 const { Loader } = require('./loader');
 const { serializeStack, parseStack  } = require("toolkit/loader");
 const { setTimeout } = require('../timers');
 const { PlainTextConsole } = require("../console/plain-text");
 const { when: unload } = require("../system/unload");
 const { format, fromException }  = require("../console/traceback");
 const system = require("../system");
+const memory = require('../deprecated/memory');
 const { gc: gcPromise } = require('./memory');
 const { defer } = require('../core/promise');
 const { extend } = require('../core/heritage');
 
 // Trick manifest builder to make it think we need these modules ?
 const unit = require("../deprecated/unit-test");
 const test = require("../../test");
 const url = require("../url");
@@ -144,24 +145,29 @@ function dictDiff(last, curr) {
   return diff;
 }
 
 function reportMemoryUsage() {
   if (!profileMemory) {
     return emptyPromise();
   }
 
-  return gcPromise().then((() => {
+  return gcPromise().then((function () {
     var mgr = Cc["@mozilla.org/memory-reporter-manager;1"]
               .getService(Ci.nsIMemoryReporterManager);
     let count = 0;
     function logReporter(process, path, kind, units, amount, description) {
       print(((++count == 1) ? "\n" : "") + description + ": " + amount + "\n");
     }
     mgr.getReportsForThisProcess(logReporter, null, /* anonymize = */ false);
+
+    var weakrefs = [info.weakref.get()
+                    for (info of memory.getObjects())];
+    weakrefs = [weakref for (weakref of weakrefs) if (weakref)];
+    print("Tracked memory objects in testing sandbox: " + weakrefs.length + "\n");
   }));
 }
 
 var gWeakrefInfo;
 
 function checkMemory() {
   return gcPromise().then(_ => {
     let leaks = getPotentialLeaks();
@@ -205,16 +211,26 @@ function showResults() {
 
   resolve();
   return promise;
 }
 
 function cleanup() {
   let coverObject = {};
   try {
+    for (let name in loader.modules)
+      memory.track(loader.modules[name],
+                           "module global scope: " + name);
+      memory.track(loader, "Cuddlefish Loader");
+
+    if (profileMemory) {
+      gWeakrefInfo = [{ weakref: info.weakref, bin: info.bin }
+                      for (info of memory.getObjects())];
+    }
+
     loader.unload();
 
     if (loader.globals.console.errorsLogged && !results.failed) {
       results.failed++;
       console.error("warnings and/or errors were logged.");
     }
 
     if (consoleListener.errorsLogged && !results.failed) {
@@ -230,17 +246,17 @@ function cleanup() {
       coverObject = loader.globals.global['__$coverObject'] || {};
     }
 
     consoleListener.errorsLogged = 0;
     loader = null;
 
     consoleListener.unregister();
 
-    Cu.forceGC();
+    memory.gc();
   }
   catch (e) {
     results.failed++;
     console.error("unload.send() threw an exception.");
     console.exception(e);
   };
 
   setTimeout(require("./options").checkMemory ? checkMemory : showResults, 1);
@@ -257,17 +273,17 @@ function cleanup() {
     let outfh = file.open(out,'w');
     outfh.write(JSON.stringify(coverObject,null,2));
     outfh.flush();
     outfh.close();
   }
 }
 
 function getPotentialLeaks() {
-  Cu.forceGC();
+  memory.gc();
 
   // Things we can assume are part of the platform and so aren't leaks
   let GOOD_BASE_URLS = [
     "chrome://",
     "resource:///",
     "resource://app/",
     "resource://gre/",
     "resource://gre-resources/",
--- a/addon-sdk/source/lib/sdk/test/memory.js
+++ b/addon-sdk/source/lib/sdk/test/memory.js
@@ -1,11 +1,20 @@
 /* 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 { Cu } = require("chrome");
+const memory = require('../deprecated/memory');
+const { defer } = require('../core/promise');
 
 function gc() {
-  return new Promise(resolve => Cu.schedulePreciseGC(resolve));
+  let { promise, resolve } = defer();
+
+  Cu.forceGC();
+  memory.gc();
+
+  Cu.schedulePreciseGC(_ => resolve());
+
+  return promise;
 }
 exports.gc = gc;
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/lib/sdk/util/bond.js
@@ -0,0 +1,36 @@
+/* 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";
+
+module.metadata = {
+  "stability": "experimental"
+};
+
+const makeDescriptor = (name, method) => ({
+  get() {
+    if (!Object.hasOwnProperty.call(this, name)) {
+      Object.defineProperty(this, name, {value: method.bind(this)});
+      return this[name];
+    } else {
+      return method;
+    }
+  }
+});
+
+const Bond = function(methods) {
+  let descriptor = {};
+  let members = [...Object.getOwnPropertyNames(methods),
+                 ...Object.getOwnPropertySymbols(methods)];
+
+  for (let name of members) {
+    let method = methods[name];
+    if (typeof(method) !== "function") {
+      throw new TypeError(`Property named "${name}" passed to Bond must be a function`);
+    }
+    descriptor[name] = makeDescriptor(name, method);
+  }
+
+  return Object.create(Bond.prototype, descriptor);
+}
+exports.Bond = Bond;
--- a/addon-sdk/source/lib/sdk/zip/utils.js
+++ b/addon-sdk/source/lib/sdk/zip/utils.js
@@ -1,16 +1,22 @@
 /* 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";
+'use strict';
 
-const { Cc, Ci } = require("chrome");
+const { Cc, Ci, Cu } = require("chrome");
+const { defer } = require("../core/promise");
 
-function getZipReader(aFile) {
-  return new Promise(resolve => {
-    let zipReader = Cc["@mozilla.org/libjar/zip-reader;1"].
-                        createInstance(Ci.nsIZipReader);
+const getZipReader = function getZipReader(aFile) {
+  let { promise, resolve, reject } = defer();
+  let zipReader = Cc["@mozilla.org/libjar/zip-reader;1"].
+                      createInstance(Ci.nsIZipReader);
+  try {
     zipReader.open(aFile);
-    resolve(zipReader);
-  });
+  }
+  catch(e){
+    reject(e);
+  }
+  resolve(zipReader);
+  return promise;
 };
 exports.getZipReader = getZipReader;
--- a/addon-sdk/source/mapping.json
+++ b/addon-sdk/source/mapping.json
@@ -8,16 +8,17 @@
   "functional": "sdk/core/functional",
   "l10n/core": "sdk/l10n/json/core",
   "l10n/html": "sdk/l10n/html",
   "l10n/loader": "sdk/l10n/loader",
   "l10n/locale": "sdk/l10n/locale",
   "l10n/prefs": "sdk/l10n/prefs",
   "list": "sdk/util/list",
   "loader": "sdk/loader/loader",
+  "memory": "sdk/deprecated/memory",
   "namespace": "sdk/core/namespace",
   "preferences-service": "sdk/preferences/service",
   "promise": "sdk/core/promise",
   "system": "sdk/system",
   "system/events": "sdk/system/events",
   "tabs/tab": "sdk/tabs/tab",
   "tabs/utils": "sdk/tabs/utils",
   "timer": "sdk/timers",
--- a/addon-sdk/source/package.json
+++ b/addon-sdk/source/package.json
@@ -17,17 +17,16 @@
   },
   "version": "0.1.18",
   "main": "./lib/index.js",
   "loader": "lib/sdk/loader/cuddlefish.js",
   "devDependencies": {
     "async": "0.9.0",
     "chai": "2.1.1",
     "fs-extra": "0.18.2",
-    "fx-runner": "0.0.7",
     "glob": "4.4.2",
     "gulp": "3.8.11",
     "ini-parser": "0.0.2",
     "jpm": "0.0.29",
     "lodash": "3.3.1",
     "mocha": "2.1.0",
     "patch-editor": "0.0.1",
     "promise": "6.1.0",
--- a/addon-sdk/source/python-lib/cuddlefish/__init__.py
+++ b/addon-sdk/source/python-lib/cuddlefish/__init__.py
@@ -231,16 +231,20 @@ parser_groups = (
                                        action="store_true",
                                        default=False,
                                        cmds=['test', 'testpkgs', 'testaddons',
                                              'testall'])),
         (("", "--output-file",), dict(dest="output_file",
                                       help="Where to put the finished .xpi",
                                       default=None,
                                       cmds=['xpi'])),
+        (("", "--manifest-overload",), dict(dest="manifest_overload",
+                                      help="JSON file to overload package.json properties",
+                                      default=None,
+                                      cmds=['xpi'])),
         (("", "--abort-on-missing-module",), dict(dest="abort_on_missing",
                                       help="Abort if required module is missing",
                                       action="store_true",
                                       default=False,
                                       cmds=['test', 'run', 'xpi', 'testpkgs'])),
         (("", "--no-connections",), dict(dest="no_connections",
                                       help="disable/enable remote connections (on for cfx run only by default)",
                                       type="choice",
@@ -652,16 +656,20 @@ def run(arguments=sys.argv[1:], target_c
         if not os.path.exists(os.path.join(options.pkgdir, 'package.json')):
             print >>sys.stderr, ("cannot find 'package.json' in"
                                  " %s." % options.pkgdir)
             sys.exit(1)
 
         target_cfg_json = os.path.join(options.pkgdir, 'package.json')
         target_cfg = packaging.get_config_in_dir(options.pkgdir)
 
+    if options.manifest_overload:
+        for k, v in packaging.load_json_file(options.manifest_overload).items():
+            target_cfg[k] = v
+
     # At this point, we're either building an XPI or running Jetpack code in
     # a Mozilla application (which includes running tests).
 
     use_main = False
     inherited_options = ['verbose', 'enable_e10s', 'parseable', 'check_memory',
                          'no_quit', 'abort_on_missing']
     enforce_timeouts = False
 
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/python-lib/cuddlefish/tests/addons/simplest-test/main.js
@@ -0,0 +1,17 @@
+/* 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/. */
+
+const { Cc, Ci } = require("chrome");
+
+exports.main = function(options, callbacks) {
+  // Close Firefox window. Firefox should quit.
+  require("sdk/deprecated/window-utils").activeBrowserWindow.close();
+
+  // But not on Mac where it stay alive! We have to request application quit.
+  if (require("sdk/system/runtime").OS == "Darwin") {
+    let appStartup = Cc['@mozilla.org/toolkit/app-startup;1'].
+                     getService(Ci.nsIAppStartup);
+    appStartup.quit(appStartup.eAttemptQuit);
+  }
+}
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/python-lib/cuddlefish/tests/addons/simplest-test/manifest-overload.json
@@ -0,0 +1,3 @@
+{
+  "version": "1.0-nightly"
+}
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/python-lib/cuddlefish/tests/addons/simplest-test/package.json
@@ -0,0 +1,6 @@
+{
+  "id": "simplest-test",
+  "directories": {
+    "lib": "."
+  }
+}
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/python-lib/cuddlefish/tests/addons/simplest-test/tests/test-minimal.js
@@ -0,0 +1,7 @@
+/* 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/. */
+
+exports.minimalTest = function(test) {
+  test.assert(true);
+};
--- a/addon-sdk/source/python-lib/cuddlefish/tests/test_init.py
+++ b/addon-sdk/source/python-lib/cuddlefish/tests/test_init.py
@@ -177,16 +177,45 @@ class TestCfxQuits(unittest.TestCase):
     # implements our own
     def assertIn(self, member, container):
         """Just like self.assertTrue(a in b), but with a nicer default message."""
         if member not in container:
             standardMsg = '"%s" not found in "%s"' % (member,
                                                   container)
             self.fail(standardMsg)
 
+    def test_cfx_run(self):
+        addon_path = os.path.join(tests_path,
+                                  "addons", "simplest-test")
+        rc, out, err = self.run_cfx(addon_path, ["run"])
+        self.assertEqual(rc, 0)
+        self.assertIn("Program terminated successfully.", err)
+
+    def test_cfx_test(self):
+        addon_path = os.path.join(tests_path, "addons", "simplest-test")
+        rc, out, err = self.run_cfx(addon_path, ["test"])
+        self.assertEqual(rc, 0)
+        self.assertIn("1 of 1 tests passed.", err)
+        self.assertIn("Program terminated successfully.", err)
+
+    def test_cfx_xpi(self):
+        addon_path = os.path.join(tests_path,
+                                  "addons", "simplest-test")
+        rc, out, err = self.run_cfx(addon_path, \
+          ["xpi", "--manifest-overload", "manifest-overload.json"])
+        self.assertEqual(rc, 0)
+        # Ensure that the addon version from our manifest overload is used
+        # in install.rdf
+        xpi_path = os.path.join(addon_path, "simplest-test.xpi")
+        xpi = zipfile.ZipFile(xpi_path, "r")
+        manifest = xpi.read("install.rdf")
+        self.assertIn("<em:version>1.0-nightly</em:version>", manifest)
+        xpi.close()
+        os.remove(xpi_path)
+
     def test_cfx_init(self):
         # Create an empty test directory
         addon_path = os.path.abspath(os.path.join(".test_tmp", "test-cfx-init"))
         if os.path.isdir(addon_path):
             shutil.rmtree(addon_path)
         os.makedirs(addon_path)
 
         # Fake a call to cfx init
@@ -198,14 +227,14 @@ class TestCfxQuits(unittest.TestCase):
         out, err = out.getvalue(), err.getvalue()
         self.assertEqual(rc["result"], 0)
         self.assertTrue("Have fun!" in out)
         self.assertEqual(err,"")
 
         # run cfx test
         rc, out, err = self.run_cfx(addon_path, ["test"])
         self.assertEqual(rc, 0)
-        self.assertIn("6 of 6 tests passed.", err)
+        self.assertIn("2 of 2 tests passed.", err)
         self.assertIn("Program terminated successfully.", err)
 
 
 if __name__ == "__main__":
     unittest.main()
--- a/addon-sdk/source/test/addons/chrome/data/panel.js
+++ b/addon-sdk/source/test/addons/chrome/data/panel.js
@@ -1,10 +1,8 @@
 /* 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';
 
 self.port.on('echo', _ => {
   self.port.emit('echo', '');
 });
-
-self.port.emit('start', '');
--- a/addon-sdk/source/test/addons/chrome/main.js
+++ b/addon-sdk/source/test/addons/chrome/main.js
@@ -62,36 +62,28 @@ exports.testChromeLocale = function(asse
                'locales ja-JP folder was copied correctly');
 
   let enStringBundle = Services.strings.createBundle(enLocalePath);
   assert.equal(enStringBundle.GetStringFromName('test'),
                'Test',
                'locales en-US folder was copied correctly');
 }
 
-exports.testChromeInPanel = function*(assert) {
+exports.testChromeInPanel = function(assert, done) {
   let panel = Panel({
     contentURL: 'chrome://test/content/panel.html',
-    contentScriptWhen: 'end',
+    contentScriptWhen: 'start',
     contentScriptFile: data.url('panel.js')
   });
-
-  yield new Promise(resolve => panel.port.once('start', resolve));
-  assert.pass('start was emitted');
-
-  yield new Promise(resolve => {
-    panel.once('show', resolve);
-    panel.show();
-  });
-  assert.pass('panel shown');
-
-  yield new Promise(resolve => {
-    panel.port.once('echo', resolve);
+  panel.once('show', _ => {
+    assert.pass('panel shown');
+    panel.port.once('echo', _ => {
+      assert.pass('got echo');
+      panel.destroy();
+      assert.pass('panel is destroyed');
+      done();
+    });
     panel.port.emit('echo');
   });
-
-  assert.pass('got echo');
-
-  panel.destroy();
-  assert.pass('panel is destroyed');
+  panel.show();
 }
 
 require('sdk/test/runner').runTestsFromModule(module);
--- a/addon-sdk/source/test/addons/layout-change/lib/test-cuddlefish-loader.js
+++ b/addon-sdk/source/test/addons/layout-change/lib/test-cuddlefish-loader.js
@@ -142,16 +142,19 @@ exports["test compatibility"] = function
                require("sdk/tabs/utils"), "sdk/tabs/utils -> tabs/utils");
 
   assert.equal(require("dom/events"),
                require("sdk/dom/events"), "sdk/dom/events -> dom/events");
 
   assert.equal(require("tabs/tab.js"),
                require("sdk/tabs/tab"), "sdk/tabs/tab -> tabs/tab.js");
 
+  assert.equal(require("memory"),
+               require("sdk/deprecated/memory"), "sdk/deprecated/memory -> memory");
+
   assert.equal(require("environment"),
                require("sdk/system/environment"), "sdk/system/environment -> environment");
 
   if (app.is("Firefox")) {
     // This module fails on fennec because of favicon xpcom component
     // being not implemented on it.
     assert.equal(require("utils/data"),
                  require("sdk/io/data"), "sdk/io/data -> utils/data");
--- a/addon-sdk/source/test/addons/places/lib/places-helper.js
+++ b/addon-sdk/source/test/addons/places/lib/places-helper.js
@@ -28,16 +28,23 @@ const {
 
 function invalidResolve (assert) {
   return function (e) {
     assert.fail('Resolve state should not be called: ' + e);
   };
 }
 exports.invalidResolve = invalidResolve;
 
+function invalidReject (assert) {
+  return function (e) {
+    assert.fail('Reject state should not be called: ' + e);
+  };
+}
+exports.invalidReject = invalidReject;
+
 // Removes all children of group
 function clearBookmarks (group) {
   group
    ? bmsrv.removeFolderChildren(group.id)
    : clearAllBookmarks();
 }
 
 function clearAllBookmarks () {
--- a/addon-sdk/source/test/addons/places/lib/test-places-bookmarks.js
+++ b/addon-sdk/source/test/addons/places/lib/test-places-bookmarks.js
@@ -19,17 +19,17 @@ const { defer, all, resolve } = require(
 const { before, after } = require('sdk/test/utils');
 
 const {
   Bookmark, Group, Separator,
   save, search, remove,
   MENU, TOOLBAR, UNSORTED
 } = require('sdk/places/bookmarks');
 const {
-  invalidResolve, createTree,
+  invalidResolve, invalidReject, createTree,
   compareWithHost, createBookmark, createBookmarkItem,
   createBookmarkTree, addVisits, resetPlaces
 } = require('./places-helper');
 const { promisedEmitter } = require('sdk/places/utils');
 const bmsrv = Cc['@mozilla.org/browser/nav-bookmarks-service;1'].
                     getService(Ci.nsINavBookmarksService);
 const tagsrv = Cc['@mozilla.org/browser/tagging-service;1'].
                     getService(Ci.nsITaggingService);
@@ -364,37 +364,38 @@ exports.testPromisedSave = function (ass
     return saveP(first);
   }).then(() => {
     assert.equal(bmsrv.getItemIndex(first.id), 2, 'properly moved bookmark');
     assert.equal(bmsrv.getItemIndex(second.id), 0, 'other bookmarks adjusted');
     assert.equal(bmsrv.getItemIndex(third.id), 1, 'other bookmarks adjusted');
   }).then(done).catch(assert.fail);
 };
 
-exports.testPromisedErrorSave = function*(assert) {
+exports.testPromisedErrorSave = function (assert, done) {
   let bookmarks = [
     { title: 'moz1', url: 'http://moz1.com', type: 'bookmark'},
     { title: 'moz2', url: 'invalidurl', type: 'bookmark'},
     { title: 'moz3', url: 'http://moz3.com', type: 'bookmark'}
   ];
 
-  yield saveP(bookmarks).then(() => {
-    assert.fail("should not resolve");
-  }, reason => {
+  saveP(bookmarks).then(invalidResolve, reason => {
     assert.ok(
       /The `url` property must be a valid URL/.test(reason),
       'Error event called with correct reason');
-  });
 
-  bookmarks[1].url = 'http://moz2.com';
-  yield saveP(bookmarks);
-
-  let res = yield searchP({ query: 'moz' });
-  assert.equal(res.length, 3, 'all 3 should be saved upon retry');
-  res.map(item => assert.ok(/moz\d\.com/.test(item.url), 'correct item'));
+    bookmarks[1].url = 'http://moz2.com';
+    return saveP(bookmarks);
+  }).
+  then(res => searchP({ query: 'moz' })).
+  then(res => {
+    assert.equal(res.length, 3, 'all 3 should be saved upon retry');
+    res.map(item => assert.ok(/moz\d\.com/.test(item.url), 'correct item'));
+    done();
+  }, invalidReject).
+  catch(assert.fail);
 };
 
 exports.testMovingChildren = function (assert, done) {
   let topFolder = Group({ title: 'top', group: MENU });
   let midFolder = Group({ title: 'middle', group: topFolder });
   let bookmarks = [
     Bookmark({ title: 'moz1', url: 'http://moz1.com', group: midFolder}),
     Bookmark({ title: 'moz2', url: 'http://moz2.com', group: midFolder}),
@@ -686,73 +687,71 @@ exports.testSearchTags = function (asser
 
 /*
  * Tests 4 scenarios
  * '*.mozilla.com'
  * 'mozilla.com'
  * 'http://mozilla.com/'
  * 'http://mozilla.com/*'
  */
-exports.testSearchURLForBookmarks = function*(assert) {
-  yield createBookmarkTree()
-  let data = yield searchP({ url: 'mozilla.org' });
-
-  assert.equal(data.length, 2, 'only URLs with host domain');
-  assert.equal(data[0].url, 'http://mozilla.org/');
-  assert.equal(data[1].url, 'http://mozilla.org/thunderbird/');
-
-  data = yield searchP({ url: '*.mozilla.org' });
-
-  assert.equal(data.length, 3, 'returns domain and when host is other than domain');
-  assert.equal(data[0].url, 'http://mozilla.org/');
-  assert.equal(data[1].url, 'http://mozilla.org/thunderbird/');
-  assert.equal(data[2].url, 'http://developer.mozilla.org/en-US/');
-
-  data = yield searchP({ url: 'http://mozilla.org' });
-
-  assert.equal(data.length, 1, 'only exact URL match');
-  assert.equal(data[0].url, 'http://mozilla.org/');
-
-  data = yield searchP({ url: 'http://mozilla.org/*' });
-
-  assert.equal(data.length, 2, 'only URLs that begin with query');
-  assert.equal(data[0].url, 'http://mozilla.org/');
-  assert.equal(data[1].url, 'http://mozilla.org/thunderbird/');
-
-  data = yield searchP([{ url: 'mozilla.org' }, { url: 'component.fm' }]);
-
-  assert.equal(data.length, 3, 'returns URLs that match EITHER query');
-  assert.equal(data[0].url, 'http://mozilla.org/');
-  assert.equal(data[1].url, 'http://mozilla.org/thunderbird/');
-  assert.equal(data[2].url, 'http://component.fm/');
+exports.testSearchURL = function (assert, done) {
+  createBookmarkTree().then(() => {
+    return searchP({ url: 'mozilla.org' });
+  }).then(data => {
+    assert.equal(data.length, 2, 'only URLs with host domain');
+    assert.equal(data[0].url, 'http://mozilla.org/');
+    assert.equal(data[1].url, 'http://mozilla.org/thunderbird/');
+    return searchP({ url: '*.mozilla.org' });
+  }).then(data => {
+    assert.equal(data.length, 3, 'returns domain and when host is other than domain');
+    assert.equal(data[0].url, 'http://mozilla.org/');
+    assert.equal(data[1].url, 'http://mozilla.org/thunderbird/');
+    assert.equal(data[2].url, 'http://developer.mozilla.org/en-US/');
+    return searchP({ url: 'http://mozilla.org' });
+  }).then(data => {
+    assert.equal(data.length, 1, 'only exact URL match');
+    assert.equal(data[0].url, 'http://mozilla.org/');
+    return searchP({ url: 'http://mozilla.org/*' });
+  }).then(data => {
+    assert.equal(data.length, 2, 'only URLs that begin with query');
+    assert.equal(data[0].url, 'http://mozilla.org/');
+    assert.equal(data[1].url, 'http://mozilla.org/thunderbird/');
+    return searchP([{ url: 'mozilla.org' }, { url: 'component.fm' }]);
+  }).then(data => {
+    assert.equal(data.length, 3, 'returns URLs that match EITHER query');
+    assert.equal(data[0].url, 'http://mozilla.org/');
+    assert.equal(data[1].url, 'http://mozilla.org/thunderbird/');
+    assert.equal(data[2].url, 'http://component.fm/');
+  }).then(done).catch(assert.fail);
 };
 
 /*
  * Searches url, title, tags
  */
-exports.testSearchQueryForBookmarks = function*(assert) {
-  yield createBookmarkTree();
-
-  let data = yield searchP({ query: 'thunder' });
-  assert.equal(data.length, 3);
-  assert.equal(data[0].title, 'mozilla.com', 'query matches tag, url, or title');
-  assert.equal(data[1].title, 'mozilla.org', 'query matches tag, url, or title');
-  assert.equal(data[2].title, 'thunderbird', 'query matches tag, url, or title');
-
-  data = yield searchP([{ query: 'rust' }, { query: 'component' }]);
-  // rust OR component
-  assert.equal(data.length, 3);
-  assert.equal(data[0].title, 'mozilla.com', 'query matches tag, url, or title');
-  assert.equal(data[1].title, 'mozilla.org', 'query matches tag, url, or title');
-  assert.equal(data[2].title, 'web audio components', 'query matches tag, url, or title');
-
-  data = yield searchP([{ query: 'moz', tags: ['javascript']}]);
-  assert.equal(data.length, 1);
-  assert.equal(data[0].title, 'mdn',
-    'only one item matches moz query AND has a javascript tag');
+exports.testSearchQuery = function (assert, done) {
+  createBookmarkTree().then(() => {
+    return searchP({ query: 'thunder' });
+  }).then(data => {
+    assert.equal(data.length, 3);
+    assert.equal(data[0].title, 'mozilla.com', 'query matches tag, url, or title');
+    assert.equal(data[1].title, 'mozilla.org', 'query matches tag, url, or title');
+    assert.equal(data[2].title, 'thunderbird', 'query matches tag, url, or title');
+    return searchP([{ query: 'rust' }, { query: 'component' }]);
+  }).then(data => {
+    // rust OR component
+    assert.equal(data.length, 3);
+    assert.equal(data[0].title, 'mozilla.com', 'query matches tag, url, or title');
+    assert.equal(data[1].title, 'mozilla.org', 'query matches tag, url, or title');
+    assert.equal(data[2].title, 'web audio components', 'query matches tag, url, or title');
+    return searchP([{ query: 'moz', tags: ['javascript']}]);
+  }).then(data => {
+    assert.equal(data.length, 1);
+    assert.equal(data[0].title, 'mdn',
+      'only one item matches moz query AND has a javascript tag');
+  }).then(done).catch(assert.fail);
 };
 
 /*
  * Test caching on bulk calls.
  * Each construction of a bookmark item snapshot results in
  * the recursive lookup of parent groups up to the root groups --
  * ensure that the appropriate instances equal each other, and no duplicate
  * fetches are called
@@ -823,17 +822,17 @@ exports.testSearchCount = function (asse
         if (n > max) n = max;
         assert.equal(results.length, n,
           'count ' + n + ' returns ' + n + ' results');
       });
     };
   }
 };
 
-exports.testSearchSortForBookmarks = function (assert, done) {
+exports.testSearchSort = function (assert, done) {
   let urls = [
     'http://mozilla.com/', 'http://webaud.io/', 'http://mozilla.com/webfwd/',
     'http://developer.mozilla.com/', 'http://bandcamp.com/'
   ];
 
   saveP(
     urls.map(url =>
       Bookmark({ url: url, title: url.replace(/http:\/\/|\//g,'')}))
--- a/addon-sdk/source/test/addons/places/lib/test-places-events.js
+++ b/addon-sdk/source/test/addons/places/lib/test-places-events.js
@@ -22,17 +22,17 @@ const { release, platform } = require('n
 
 const isOSX10_6 = (() => {
   let vString = release();
   return vString && /darwin/.test(platform()) && /10\.6/.test(vString);
 })();
 
 const { search } = require('sdk/places/history');
 const {
-  invalidResolve, createTree, createBookmark,
+  invalidResolve, invalidReject, createTree, createBookmark,
   compareWithHost, addVisits, resetPlaces, createBookmarkItem,
   removeVisits, historyBatch
 } = require('./places-helper');
 const { save, MENU, UNSORTED } = require('sdk/places/bookmarks');
 const { promisedEmitter } = require('sdk/places/utils');
 
 exports['test bookmark-item-added'] = function (assert, done) {
   events.on('data', function handler ({type, data}) {
--- a/addon-sdk/source/test/addons/places/lib/test-places-history.js
+++ b/addon-sdk/source/test/addons/places/lib/test-places-history.js
@@ -14,80 +14,82 @@ const { defer, all } = require('sdk/core
 const { has } = require('sdk/util/array');
 const { setTimeout } = require('sdk/timers');
 const { before, after } = require('sdk/test/utils');
 const { set } = require('sdk/preferences/service');
 const {
   search
 } = require('sdk/places/history');
 const {
-  invalidResolve, createTree,
+  invalidResolve, invalidReject, createTree,
   compareWithHost, addVisits, resetPlaces
 } = require('./places-helper');
 const { promisedEmitter } = require('sdk/places/utils');
 
-exports.testEmptyQuery = function*(assert) {
+exports.testEmptyQuery = function (assert, done) {
   let within = toBeWithin();
-  yield addVisits([
+  addVisits([
     'http://simplequery-1.com', 'http://simplequery-2.com'
-  ]);
-
-  let results = yield searchP();
-  assert.equal(results.length, 2, 'Correct number of entries returned');
-  assert.equal(results[0].url, 'http://simplequery-1.com/',
-    'matches url');
-  assert.equal(results[1].url, 'http://simplequery-2.com/',
-    'matches url');
-  assert.equal(results[0].title, 'Test visit for ' + results[0].url,
-    'title matches');
-  assert.equal(results[1].title, 'Test visit for ' + results[1].url,
-    'title matches');
-  assert.equal(results[0].visitCount, 1, 'matches access');
-  assert.equal(results[1].visitCount, 1, 'matches access');
-  assert.ok(within(results[0].time), 'accurate access time');
-  assert.ok(within(results[1].time), 'accurate access time');
-  assert.equal(Object.keys(results[0]).length, 4,
-    'no addition exposed properties on history result');
+  ]).then(searchP).then(results => {
+    assert.equal(results.length, 2, 'Correct number of entries returned');
+    assert.equal(results[0].url, 'http://simplequery-1.com/',
+      'matches url');
+    assert.equal(results[1].url, 'http://simplequery-2.com/',
+      'matches url');
+    assert.equal(results[0].title, 'Test visit for ' + results[0].url,
+      'title matches');
+    assert.equal(results[1].title, 'Test visit for ' + results[1].url,
+      'title matches');
+    assert.equal(results[0].visitCount, 1, 'matches access');
+    assert.equal(results[1].visitCount, 1, 'matches access');
+    assert.ok(within(results[0].time), 'accurate access time');
+    assert.ok(within(results[1].time), 'accurate access time');
+    assert.equal(Object.keys(results[0]).length, 4,
+      'no addition exposed properties on history result');
+    done();
+  }, invalidReject);
 };
 
-exports.testVisitCount = function*(assert) {
-  yield addVisits([
+exports.testVisitCount = function (assert, done) {
+  addVisits([
     'http://simplequery-1.com', 'http://simplequery-1.com',
     'http://simplequery-1.com', 'http://simplequery-1.com'
-  ]);
-  let results = yield searchP();
-  assert.equal(results.length, 1, 'Correct number of entries returned');
-  assert.equal(results[0].url, 'http://simplequery-1.com/', 'correct url');
-  assert.equal(results[0].visitCount, 4, 'matches access count');
+  ]).then(searchP).then(results => {
+    assert.equal(results.length, 1, 'Correct number of entries returned');
+    assert.equal(results[0].url, 'http://simplequery-1.com/', 'correct url');
+    assert.equal(results[0].visitCount, 4, 'matches access count');
+    done();
+  }, invalidReject);
 };
 
 /*
  * Tests 4 scenarios
  * '*.mozilla.org'
  * 'mozilla.org'
  * 'http://mozilla.org/'
  * 'http://mozilla.org/*'
  */
-exports.testSearchURLForHistory = function*(assert) {
-  yield addVisits([
+exports.testSearchURL = function (assert, done) {
+  addVisits([
     'http://developer.mozilla.org', 'http://mozilla.org',
     'http://mozilla.org/index', 'https://mozilla.org'
-  ]);
-
-  let results = yield searchP({ url: 'http://mozilla.org/' });
-  assert.equal(results.length, 1, 'should just be an exact match');
-
-  results = yield searchP({ url: '*.mozilla.org' });
-  assert.equal(results.length, 4, 'returns all entries');
-
-  results = yield searchP({ url: 'mozilla.org' });
-  assert.equal(results.length, 3, 'returns entries where mozilla.org is host');
-
-  results = yield searchP({ url: 'http://mozilla.org/*' });
-  assert.equal(results.length, 2, 'should match anything starting with substring');
+  ]).then(() => searchP({ url: '*.mozilla.org' }))
+  .then(results => {
+    assert.equal(results.length, 4, 'returns all entries');
+    return searchP({ url: 'mozilla.org' });
+  }).then(results => {
+    assert.equal(results.length, 3, 'returns entries where mozilla.org is host');
+    return searchP({ url: 'http://mozilla.org/' });
+  }).then(results => {
+    assert.equal(results.length, 1, 'should just be an exact match');
+    return searchP({ url: 'http://mozilla.org/*' });
+  }).then(results => {
+    assert.equal(results.length, 2, 'should match anything starting with substring');
+    done();
+  });
 };
 
 // Disabling due to intermittent Bug 892619
 // TODO solve this
 /*
 exports.testSearchTimeRange = function (assert, done) {
   let firstTime, secondTime;
   addVisits([
@@ -117,31 +119,33 @@ exports.testSearchTimeRange = function (
     assert.equal(results.length, 2, 'should return only last entries');
     results.map(item => {
       assert.ok(/newvisit/.test(item.url), 'correct entry');
     });
     done();
   });
 };
 */
-exports.testSearchQueryForHistory = function*(assert) {
-  yield addVisits([
+exports.testSearchQuery = function (assert, done) {
+  addVisits([
     'http://mozilla.com', 'http://webaud.io', 'http://mozilla.com/webfwd'
-  ]);
-
-  let results = yield searchP({ query: 'moz' });
-  assert.equal(results.length, 2, 'should return urls that match substring');
-  results.map(({url}) => {
-    assert.ok(/moz/.test(url), 'correct item');
-  });
-
-  results = yield searchP([{ query: 'webfwd' }, { query: 'aud.io' }]);
-  assert.equal(results.length, 2, 'should OR separate queries');
-  results.map(({url}) => {
-    assert.ok(/webfwd|aud\.io/.test(url), 'correct item');
+  ]).then(() => {
+    return searchP({ query: 'moz' });
+  }).then(results => {
+    assert.equal(results.length, 2, 'should return urls that match substring');
+    results.map(({url}) => {
+      assert.ok(/moz/.test(url), 'correct item');
+    });
+    return searchP([{ query: 'webfwd' }, { query: 'aud.io' }]);
+  }).then(results => {
+    assert.equal(results.length, 2, 'should OR separate queries');
+    results.map(({url}) => {
+      assert.ok(/webfwd|aud\.io/.test(url), 'correct item');
+    });
+    done();
   });
 };
 
 /*
  * Query Options
  */
 
 exports.testSearchCount = function (assert, done) {
@@ -159,60 +163,61 @@ exports.testSearchCount = function (asse
       return searchP({}, { count: n }).then(results => {
         assert.equal(results.length, n,
           'count ' + n + ' returns ' + n + ' results');
       });
     };
   }
 };
 
-exports.testSearchSortForHistory = function*(assert) {
+exports.testSearchSort = function (assert, done) {
+  let places = [
+    'http://mozilla.com/', 'http://webaud.io/', 'http://mozilla.com/webfwd/',
+    'http://developer.mozilla.com/', 'http://bandcamp.com/'
+  ];
+  addVisits(places).then(() => {
+    return searchP({}, { sort: 'title' });
+  }).then(results => {
+    checkOrder(results, [4,3,0,2,1]);
+    return searchP({}, { sort: 'title', descending: true });
+  }).then(results => {
+    checkOrder(results, [1,2,0,3,4]);
+    return searchP({}, { sort: 'url' });
+  }).then(results => {
+    checkOrder(results, [4,3,0,2,1]);
+    return searchP({}, { sort: 'url', descending: true });
+  }).then(results => {
+    checkOrder(results, [1,2,0,3,4]);
+    return addVisits('http://mozilla.com') // for visit conut
+      .then(() => addVisits('http://github.com')); // for checking date
+  }).then(() => {
+    return searchP({}, { sort: 'visitCount' });
+  }).then(results => {
+    assert.equal(results[5].url, 'http://mozilla.com/',
+      'last entry is the highest visit count');
+    return searchP({}, { sort: 'visitCount', descending: true });
+  }).then(results => {
+    assert.equal(results[0].url, 'http://mozilla.com/',
+      'first entry is the highest visit count');
+    return searchP({}, { sort: 'date' });
+  }).then(results => {
+    assert.equal(results[5].url, 'http://github.com/',
+      'latest visited should be first');
+    return searchP({}, { sort: 'date', descending: true });
+  }).then(results => {
+    assert.equal(results[0].url, 'http://github.com/',
+      'latest visited should be at the end');
+  }).then(done);
+
   function checkOrder (results, nums) {
     assert.equal(results.length, nums.length, 'expected return count');
     for (let i = 0; i < nums.length; i++) {
       assert.equal(results[i].url, places[nums[i]], 'successful order');
     }
   }
-
-  let places = [
-    'http://mozilla.com/', 'http://webaud.io/', 'http://mozilla.com/webfwd/',
-    'http://developer.mozilla.com/', 'http://bandcamp.com/'
-  ];
-  yield addVisits(places);
-
-  let results = yield searchP({}, { sort: 'title' });
-  checkOrder(results, [4,3,0,2,1]);
-
-  results = yield searchP({}, { sort: 'title', descending: true });
-  checkOrder(results, [1,2,0,3,4]);
-
-  results = yield searchP({}, { sort: 'url' });
-  checkOrder(results, [4,3,0,2,1]);
-
-  results = yield searchP({}, { sort: 'url', descending: true });
-  checkOrder(results, [1,2,0,3,4]);
-
-  yield addVisits('http://mozilla.com'); // for visit conut
-  yield addVisits('http://github.com'); // for checking date
-
-  results = yield searchP({}, { sort: 'visitCount' });
-  assert.equal(results[5].url, 'http://mozilla.com/',
-    'last entry is the highest visit count');
-
-  results = yield  searchP({}, { sort: 'visitCount', descending: true });
-  assert.equal(results[0].url, 'http://mozilla.com/',
-    'first entry is the highest visit count');
-
-  results = yield  searchP({}, { sort: 'date' });
-  assert.equal(results[5].url, 'http://github.com/',
-    'latest visited should be first');
-
-  results = yield  searchP({}, { sort: 'date', descending: true });
-  assert.equal(results[0].url, 'http://github.com/',
-    'latest visited should be at the end');
 };
 
 exports.testEmitters = function (assert, done) {
   let urls = [
     'http://mozilla.com/', 'http://webaud.io/', 'http://mozilla.com/webfwd/',
     'http://developer.mozilla.com/', 'http://bandcamp.com/'
   ];
   addVisits(urls).then(() => {
--- a/addon-sdk/source/test/addons/places/lib/test-places-host.js
+++ b/addon-sdk/source/test/addons/places/lib/test-places-host.js
@@ -16,47 +16,49 @@ const { newURI } = require('sdk/url/util
 const { send } = require('sdk/addon/events');
 const { set } = require('sdk/preferences/service');
 const { before, after } = require('sdk/test/utils');
 
 require('sdk/places/host/host-bookmarks');
 require('sdk/places/host/host-tags');
 require('sdk/places/host/host-query');
 const {
-  invalidResolve, createTree,
+  invalidResolve, invalidReject, createTree,
   compareWithHost, createBookmark, createBookmarkTree, resetPlaces
 } = require('./places-helper');
 
 const bmsrv = Cc['@mozilla.org/browser/nav-bookmarks-service;1'].
                     getService(Ci.nsINavBookmarksService);
 const hsrv = Cc['@mozilla.org/browser/nav-history-service;1'].
               getService(Ci.nsINavHistoryService);
 const tagsrv = Cc['@mozilla.org/browser/tagging-service;1'].
               getService(Ci.nsITaggingService);
 
-exports.testBookmarksCreate = function*(assert) {
+exports.testBookmarksCreate = function (assert, done) {
   let items = [{
     title: 'my title',
     url: 'http://test-places-host.com/testBookmarksCreate/',
     tags: ['some', 'tags', 'yeah'],
     type: 'bookmark'
   }, {
     title: 'my folder',
     type: 'group',
     group: bmsrv.bookmarksMenuFolder
   }, {
     type: 'separator',
     group: bmsrv.unfiledBookmarksFolder
   }];
 
-  yield all(items.map((item) => {
-    return send('sdk-places-bookmarks-create', item).then((data) => {
+  all(items.map(function (item) {
+    return send('sdk-places-bookmarks-create', item).then(function (data) {
       compareWithHost(assert, data);
-    });
-  }));
+    }, invalidReject(assert));
+  })).then(function () {
+    done();
+  }, invalidReject(assert));
 };
 
 exports.testBookmarksCreateFail = function (assert, done) {
   let items = [{
     title: 'my title',
     url: 'not-a-url',
     type: 'bookmark'
   }, {
deleted file mode 100644
--- a/addon-sdk/source/test/addons/require/list.js
+++ /dev/null
@@ -1,6 +0,0 @@
-/* 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";
-
-exports.local = true;
--- a/addon-sdk/source/test/addons/require/main.js
+++ b/addon-sdk/source/test/addons/require/main.js
@@ -1,20 +1,20 @@
 /* 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";
 
 var { isNative } = require("@loader/options");
 
 exports["test local vs sdk module"] = function (assert) {
-  assert.notEqual(require("list"),
-                  require("sdk/util/list"),
+  assert.notEqual(require("memory"),
+                  require("sdk/deprecated/memory"),
                   "Local module takes the priority over sdk modules");
-  assert.ok(require("list").local,
+  assert.ok(require("memory").local,
             "this module is really the local one");
 }
 
 if (!isNative) {
   exports["test 3rd party vs sdk module"] = function (assert) {
     // We are testing with a 3rd party package called `tabs` with 3 modules
     // main, page-mod and third-party
 
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/test/addons/require/memory.js
@@ -0,0 +1,5 @@
+/* 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/. */
+
+exports.local = true;
--- a/addon-sdk/source/test/fixtures/index.html
+++ b/addon-sdk/source/test/fixtures/index.html
@@ -4,15 +4,10 @@
 
 <html>
   <head>
     <meta charset="UTF-8">
     <title>Add-on Page</title>
   </head>
   <body>
     <p>This is an add-on page test!</p>
-    <script>
-    function getTestURL() {
-      return window.document.documentURI + "";
-    }
-    </script>
   </body>
 </html>
deleted file mode 100644
--- a/addon-sdk/source/test/fixtures/test-addon-extras-window.html
+++ /dev/null
@@ -1,21 +0,0 @@
-<!-- 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/. -->
-
-<html>
-<head>
-    <meta charset="UTF-8">
-    <title>Worker test</title>
-</head>
-<body>
-  <p id="paragraph">Lorem ipsum dolor sit amet.</p>
-  <script>
-    if ("addon" in window) {
-      var count = 1;
-      addon.port.on("get-result", () => {
-        addon.port.emit("result" + count++, extras.test().getTestURL())
-      });
-    }
-  </script>
-</body>
-</html>
deleted file mode 100644
--- a/addon-sdk/source/test/fixtures/test-addon-extras.html
+++ /dev/null
@@ -1,31 +0,0 @@
-<!-- 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/. -->
-
-<html>
-<head>
-    <meta charset="UTF-8">
-    <title>Worker test</title>
-</head>
-<body>
-  <p id="paragraph">Lorem ipsum dolor sit amet.</p>
-  <script>
-    if ("addon" in window) {
-      var count = 1;
-      addon.port.on("get-result", () => {
-        console.log("get-result recieved");
-        addon.port.emit("result" + count++, extras && extras.test())
-      });
-    }
-
-    window.addEventListener("message", function getMessage({ data }) {
-      if (data.name == "start") {
-        window.postMessage({
-          name: "extras",
-          result: window.extras === undefined
-        }, '*');
-      }
-    }, false);
-  </script>
-</body>
-</html>
--- a/addon-sdk/source/test/fixtures/test.html
+++ b/addon-sdk/source/test/fixtures/test.html
@@ -4,15 +4,10 @@
 
 <html>
   <head>
     <meta charset="UTF-8">
     <title>foo</title>
   </head>
   <body>
     <p>bar</p>
-    <script>
-    function getTestURL() {
-      return window.document.documentURI + "";
-    }
-    </script>
   </body>
 </html>
--- a/addon-sdk/source/test/jetpack-package.ini
+++ b/addon-sdk/source/test/jetpack-package.ini
@@ -1,44 +1,44 @@
 [DEFAULT]
 support-files =
   buffers/**
   commonjs-test-adapter/**
   context-menu/**
   event/**
-  fixtures.js
   fixtures/**
   framescript-manager/**
   framescript-util/**
+  loader/**
   lib/**
-  loader/**
   modules/**
-  pagemod-test-helpers.js
   path/**
   private-browsing/**
   querystring/**
   sidebar/**
   tabs/**
-  test-context-menu.html
   traits/**
-  util.js
   windows/**
   zip/**
+  fixtures.js
+  pagemod-test-helpers.js
+  test-context-menu.html
+  util.js
 
 [test-addon-bootstrap.js]
-[test-addon-extras.js]
 [test-addon-installer.js]
 [test-addon-window.js]
 [test-api-utils.js]
 [test-array.js]
 [test-base64.js]
 [test-bootstrap.js]
 [test-browser-events.js]
 [test-buffer.js]
 [test-byte-streams.js]
+[test-bond.js]
 [test-child_process.js]
 [test-chrome.js]
 [test-clipboard.js]
 [test-collection.js]
 [test-commonjs-test-adapter.js]
 [test-content-events.js]
 [test-content-script.js]
 [test-content-sync-worker.js]
@@ -67,30 +67,29 @@ skip-if = true
 [test-functional.js]
 [test-globals.js]
 [test-heritage.js]
 [test-hidden-frame.js]
 [test-host-events.js]
 [test-hotkeys.js]
 [test-httpd.js]
 [test-indexed-db.js]
-[test-jetpack-id.js]
 [test-keyboard-observer.js]
 [test-keyboard-utils.js]
+[test-lang-type.js]
 [test-l10n-locale.js]
 [test-l10n-plural-rules.js]
-[test-lang-type.js]
 [test-libxul.js]
 [test-list.js]
 [test-loader.js]
 [test-match-pattern.js]
+[test-memory.js]
 [test-method.js]
 [test-module.js]
 [test-modules.js]
-[test-mozilla-toolkit-versioning.js]
 [test-mpl2-license-header.js]
 skip-if = true
 [test-namespace.js]
 [test-native-loader.js]
 [test-native-options.js]
 [test-net-url.js]
 [test-node-os.js]
 [test-notifications.js]
@@ -112,17 +111,16 @@ skip-if = true
 [test-request.js]
 [test-require.js]
 [test-rules.js]
 [test-sandbox.js]
 [test-selection.js]
 [test-self.js]
 [test-sequence.js]
 [test-set-exports.js]
-[test-shared-require.js]
 [test-simple-prefs.js]
 [test-simple-storage.js]
 [test-system-events.js]
 [test-system-input-output.js]
 [test-system-runtime.js]
 [test-system-startup.js]
 [test-system.js]
 [test-tab-events.js]
deleted file mode 100644
--- a/addon-sdk/source/test/loader/user-global.js
+++ /dev/null
@@ -1,11 +0,0 @@
-/* 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";
-
-// Test module to check presense of user defined globals.
-// Related to bug 827792.
-
-exports.getCom = function() {
-  return com;
-};
--- a/addon-sdk/source/test/private-browsing/windows.js
+++ b/addon-sdk/source/test/private-browsing/windows.js
@@ -6,17 +6,16 @@
 const { onFocus, openDialog, open } = require('sdk/window/utils');
 const { open: openPromise, close, focus, promise } = require('sdk/window/helpers');
 const { isPrivate } = require('sdk/private-browsing');
 const { getMode } = require('sdk/private-browsing/utils');
 const { browserWindows: windows } = require('sdk/windows');
 const { defer } = require('sdk/core/promise');
 const tabs = require('sdk/tabs');
 const { getMostRecentBrowserWindow } = require('sdk/window/utils');
-const { cleanUI } = require("sdk/test/utils");
 
 // test openDialog() from window/utils with private option
 // test isActive state in pwpb case
 // test isPrivate on ChromeWindow
 exports.testPerWindowPrivateBrowsingGetter = function*(assert) {
   let win = openDialog({ private: true });
 
   yield promise(win, 'DOMContentLoaded');
@@ -38,28 +37,25 @@ exports.testPerWindowPrivateBrowsingGett
   });
 
   yield promise(win, 'DOMContentLoaded');
   assert.equal(getMode(win), true, 'Newly opened window is in PB mode');
   assert.ok(isPrivate(win), 'isPrivate(window) is true');
   yield close(win)
 }
 
-exports.testIsPrivateOnWindowOpen = function*(assert) {
-  let window = yield new Promise(resolve => {
-    windows.open({
-      isPrivate: true,
-      onOpen: resolve
-    });
+exports.testIsPrivateOnWindowOpen = function(assert, done) {
+  windows.open({
+    isPrivate: true,
+    onOpen: function(window) {
+      assert.equal(isPrivate(window), false, 'isPrivate for a window is true when it should be');
+      assert.equal(isPrivate(window.tabs[0]), false, 'isPrivate for a tab is false when it should be');
+      window.close(done);
+    }
   });
-
-  assert.equal(isPrivate(window), false, 'isPrivate for a window is true when it should be');
-  assert.equal(isPrivate(window.tabs[0]), false, 'isPrivate for a tab is false when it should be');
-
-  yield cleanUI();
 }
 
 exports.testIsPrivateOnWindowOpenFromPrivate = function(assert, done) {
     // open a private window
     openPromise(null, {
       features: {
         private: true,
         chrome: true,
--- a/addon-sdk/source/test/tabs/test-firefox-tabs.js
+++ b/addon-sdk/source/test/tabs/test-firefox-tabs.js
@@ -13,17 +13,16 @@ const { getOwnerWindow } = require('sdk/
 const { windows, onFocus, getMostRecentBrowserWindow } = require('sdk/window/utils');
 const { open, focus, close } = require('sdk/window/helpers');
 const tabs = require('sdk/tabs');
 const { browserWindows } = require('sdk/windows');
 const { set: setPref } = require("sdk/preferences/service");
 const DEPRECATE_PREF = "devtools.errorconsole.deprecation_warnings";
 const fixtures = require("../fixtures");
 const { base64jpeg } = fixtures;
-const { cleanUI, after } = require("sdk/test/utils");
 
 // Bug 682681 - tab.title should never be empty
 exports.testBug682681_aboutURI = function(assert, done) {
   let url = 'chrome://browser/locale/tabbrowser.properties';
   let stringBundle = Cc["@mozilla.org/intl/stringbundle;1"].
                         getService(Ci.nsIStringBundleService).
                         createBundle(url);
   let emptyTabTitle = stringBundle.GetStringFromName('tabs.emptyTabTitle');
@@ -368,38 +367,41 @@ exports.testTabMove = function(assert, d
         tab.index = 0;
         assert.equal(tab.index, 0, "tab index after move matches");
         close(window).then(done).then(null, assert.fail);
       }
     });
   }).then(null, assert.fail);
 };
 
-exports.testIgnoreClosing = function*(assert) {
-  let url = "data:text/html;charset=utf-8,foobar";
-  let originalWindow = getMostRecentBrowserWindow();
+exports.testIgnoreClosing = function(assert, done) {
+  let originalWindow = viewFor(browserWindows.activeWindow);
+  openBrowserWindow(function(window, browser) {
+    onFocus(window).then(() => {
+      let url = "data:text/html;charset=utf-8,foobar";
 
-  let window = yield open().then(focus);
+      assert.equal(tabs.length, 2, "should be two windows open each with one tab");
 
-  assert.equal(tabs.length, 2, "should be two windows open each with one tab");
+      tabs.on('ready', function onReady(tab) {
+        tabs.removeListener('ready', onReady);
 
-  yield new Promise(resolve => {
-    tabs.once("ready", (tab) => {
-      let win = tab.window;
-      assert.equal(win.tabs.length, 2, "should be two tabs in the new window");
-      assert.equal(tabs.length, 3, "should be three tabs in total");
+        let win = tab.window;
+        assert.equal(win.tabs.length, 2, "should be two tabs in the new window");
+        assert.equal(tabs.length, 3, "should be three tabs in total");
 
-      tab.close(() => {
-        assert.equal(win.tabs.length, 1, "should be one tab in the new window");
-        assert.equal(tabs.length, 2, "should be two tabs in total");
-        resolve();
+        tab.close(function() {
+          assert.equal(win.tabs.length, 1, "should be one tab in the new window");
+          assert.equal(tabs.length, 2, "should be two tabs in total");
+
+          close(window).then(onFocus(originalWindow)).then(done).then(null, assert.fail);
+        });
       });
+
+      tabs.open(url);
     });
-
-    tabs.open(url);
   });
 };
 
 // TEST: open tab with default options
 exports.testOpen = function(assert, done) {
   let url = "data:text/html;charset=utf-8,default";
   tabs.open({
     url: url,
@@ -530,48 +532,42 @@ exports.testTabsEvent_onOpen = function(
       close(window).then(done).then(null, assert.fail);
     });
 
     tabs.open(url);
   }).then(null, assert.fail);
 };
 
 // TEST: onClose event handler
-exports.testTabsEvent_onClose = function*(assert) {
-  let window = yield open().then(focus);
-  let url = "data:text/html;charset=utf-8,onclose";
-  let eventCount = 0;
+exports.testTabsEvent_onClose = function(assert, done) {
+  open().then(focus).then(window => {
+    let url = "data:text/html;charset=utf-8,onclose";
+    let eventCount = 0;
 
-  // add listener via property assignment
-  function listener1(tab) {
-    eventCount++;
-  }
-  tabs.on("close", listener1);
+    // add listener via property assignment
+    function listener1(tab) {
+      eventCount++;
+    }
+    tabs.on('close', listener1);
 
-  yield new Promise(resolve => {
     // add listener via collection add
-    tabs.on("close", function listener2(tab) {
+    tabs.on('close', function listener2(tab) {
       assert.equal(++eventCount, 2, "both listeners notified");
-      tabs.removeListener("close", listener2);
-      resolve();
+      tabs.removeListener('close', listener1);
+      tabs.removeListener('close', listener2);
+      close(window).then(done).then(null, assert.fail);
     });
 
     tabs.on('ready', function onReady(tab) {
       tabs.removeListener('ready', onReady);
       tab.close();
     });
 
     tabs.open(url);
-  });
-
-  tabs.removeListener("close", listener1);
-  assert.pass("done test!");
-
-  yield close(window);
-  assert.pass("window was closed!");
+  }).then(null, assert.fail);
 };
 
 // TEST: onClose event handler when a window is closed
 exports.testTabsEvent_onCloseWindow = function(assert, done) {
   let closeCount = 0;
   let individualCloseCount = 0;
 
   open().then(focus).then(window => {
@@ -670,48 +666,42 @@ exports.testTabsEvent_onActivate = funct
       close(window).then(done).then(null, assert.fail);
     });
 
     tabs.open(url);
   }).then(null, assert.fail);
 };
 
 // onDeactivate event handler
-exports.testTabsEvent_onDeactivate = function*(assert) {
-  let window = yield open().then(focus);
-
-  let url = "data:text/html;charset=utf-8,ondeactivate";
-  let eventCount = 0;
+exports.testTabsEvent_onDeactivate = function(assert, done) {
+  open().then(focus).then(window => {
+    let url = "data:text/html;charset=utf-8,ondeactivate";
+    let eventCount = 0;
 
-  // add listener via property assignment
-  function listener1(tab) {
-    eventCount++;
-    assert.pass("listener1 was called " + eventCount);
-  };
-  tabs.on('deactivate', listener1);
+    // add listener via property assignment
+    function listener1(tab) {
+      eventCount++;
+    };
+    tabs.on('deactivate', listener1);
 
-  yield new Promise(resolve => {
     // add listener via collection add
     tabs.on('deactivate', function listener2(tab) {
       assert.equal(++eventCount, 2, "both listeners notified");
+      tabs.removeListener('deactivate', listener1);
       tabs.removeListener('deactivate', listener2);
-      resolve();
+      close(window).then(done).then(null, assert.fail);
     });
 
     tabs.on('open', function onOpen(tab) {
-      assert.pass("tab opened");
       tabs.removeListener('open', onOpen);
       tabs.open("data:text/html;charset=utf-8,foo");
     });
 
     tabs.open(url);
-  });
-
-  tabs.removeListener('deactivate', listener1);
-  assert.pass("listeners were removed");
+  }).then(null, assert.fail);
 };
 
 // pinning
 exports.testTabsEvent_pinning = function(assert, done) {
   open().then(focus).then(window => {
     let url = "data:text/html;charset=utf-8,1";
 
     tabs.on('open', function onOpen(tab) {
@@ -731,46 +721,39 @@ exports.testTabsEvent_pinning = function
       close(window).then(done).then(null, assert.fail);
     });
 
     tabs.open(url);
   }).then(null, assert.fail);
 };
 
 // TEST: per-tab event handlers
-exports.testPerTabEvents = function*(assert) {
-  let window = yield open().then(focus);
-  let eventCount = 0;
+exports.testPerTabEvents = function(assert, done) {
+  open().then(focus).then(window => {
+    let eventCount = 0;
 
-  let tab = yield new Promise(resolve => {
     tabs.open({
       url: "data:text/html;charset=utf-8,foo",
-      onOpen: (tab) => {
-        assert.pass("the tab was opened");
-
+      onOpen: function(tab) {
         // add listener via property assignment
         function listener1() {
           eventCount++;
         };
         tab.on('ready', listener1);
 
         // add listener via collection add
         tab.on('ready', function listener2() {
-          assert.equal(eventCount, 1, "listener1 called before listener2");
+          assert.equal(eventCount, 1, "both listeners notified");
           tab.removeListener('ready', listener1);
           tab.removeListener('ready', listener2);
-          assert.pass("removed listeners");
-          eventCount++;
-          resolve();
+          close(window).then(done).then(null, assert.fail);
         });
       }
     });
-  });
-
-  assert.equal(eventCount, 2, "both listeners were notified.");
+  }).then(null, assert.fail);
 };
 
 exports.testAttachOnOpen = function (assert, done) {
   // Take care that attach has to be called on tab ready and not on tab open.
   open().then(focus).then(window => {
     tabs.open({
       url: "data:text/html;charset=utf-8,foobar",
       onOpen: function (tab) {
@@ -1215,20 +1198,16 @@ exports.testTabDestroy = function(assert
           tab.activate();
         });
       }));
       myFirstTab.activate();
     })
   })
 };
 
-after(exports, function*(name, assert) {
-  yield cleanUI();
-});
-
 /******************* helpers *********************/
 
 // Utility function to open a new browser window.
 function openBrowserWindow(callback, url) {
   let ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
            getService(Ci.nsIWindowWatcher);
   let urlString = Cc["@mozilla.org/supports-string;1"].
                   createInstance(Ci.nsISupportsString);
deleted file mode 100644
--- a/addon-sdk/source/test/test-addon-extras.js
+++ /dev/null
@@ -1,171 +0,0 @@
-/* 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 { Ci, Cu, Cc, components } = require("chrome");
-const self = require("sdk/self");
-const { before, after } = require("sdk/test/utils");
-const fixtures = require("./fixtures");
-const { Loader } = require("sdk/test/loader");
-const { merge } = require("sdk/util/object");
-
-exports["test changing result from addon extras in panel"] = function(assert, done) {
-  let loader = Loader(module, null, null, {
-    modules: {
-      "sdk/self": merge({}, self, {
-        data: merge({}, self.data, {url: fixtures.url})
-      })
-    }
-  });
-
-  const { Panel } = loader.require("sdk/panel");
-  const { events } = loader.require("sdk/content/sandbox/events");
-  const { on } = loader.require("sdk/event/core");
-  const { isAddonContent } = loader.require("sdk/content/utils");
-
-  var result = 1;
-  var extrasVal = {
-    test: function() {
-      return result;
-    }
-  };
-
-  on(events, "content-script-before-inserted", ({ window, worker }) => {
-    assert.pass("content-script-before-inserted");
-
-    if (isAddonContent({ contentURL: window.location.href })) {
-      let extraStuff = Cu.cloneInto(extrasVal, window, {
-        cloneFunctions: true
-      });
-      getUnsafeWindow(window).extras = extraStuff;
-
-      assert.pass("content-script-before-inserted done!");
-    }
-  });
-
-  let panel = Panel({
-    contentURL: "./test-addon-extras.html"
-  });
-
-  panel.port.once("result1", (result) => {
-    assert.equal(result, 1, "result is a number");
-    result = true;
-    panel.port.emit("get-result");
-  });
-
-  panel.port.once("result2", (result) => {
-    assert.equal(result, true, "result is a boolean");
-    loader.unload();
-    done();
-  });
-
-  panel.port.emit("get-result");
-}
-
-exports["test window result from addon extras in panel"] = function*(assert) {
-  let loader = Loader(module, null, null, {
-    modules: {
-      "sdk/self": merge({}, self, {
-        data: merge({}, self.data, {url: fixtures.url})
-      })
-    }
-  });
-
-  const { Panel } = loader.require('sdk/panel');
-  const { Page } = loader.require('sdk/page-worker');
-  const { getActiveView } = loader.require("sdk/view/core");
-  const { getDocShell } = loader.require('sdk/frame/utils');
-  const { events } = loader.require("sdk/content/sandbox/events");
-  const { on } = loader.require("sdk/event/core");
-  const { isAddonContent } = loader.require("sdk/content/utils");
-
-  // make a page worker and wait for it to load
-  var page = yield new Promise(resolve => {
-    assert.pass("Creating the background page");
-
-    let page = Page({
-      contentURL: "./test.html",
-      contentScriptWhen: "end",
-      contentScript: "self.port.emit('end', unsafeWindow.getTestURL() + '')"
-    });
-
-    page.port.once("end", (url) => {
-      assert.equal(url, fixtures.url("./test.html"), "url is correct");
-      resolve(page);
-    });
-  });
-  assert.pass("Created the background page");
-
-  var extrasVal = {
-    test: function() {
-      assert.pass("start test function");
-      let frame = getActiveView(page);
-      let window = getUnsafeWindow(frame.contentWindow);
-      assert.equal(typeof window.getTestURL, "function", "window.getTestURL is a function");
-      return window;
-    }
-  };
-
-  on(events, "content-script-before-inserted", ({ window, worker }) => {
-    let url = window.location.href;
-    assert.pass("content-script-before-inserted " + url);
-
-    if (isAddonContent({ contentURL: url })) {
-      let extraStuff = Cu.cloneInto(extrasVal, window, {
-        cloneFunctions: true
-      });
-      getUnsafeWindow(window).extras = extraStuff;
-
-      assert.pass("content-script-before-inserted done!");
-    }
-  });
-
-  let panel = Panel({
-    contentURL: "./test-addon-extras-window.html"
-  });
-
-
-  yield new Promise(resolve => {
-    panel.port.once("result1", (result) => {
-      assert.equal(result, fixtures.url("./test.html"), "result1 is a window");
-      resolve();
-    });
-
-    assert.pass("emit get-result");
-    panel.port.emit("get-result");
-  });
-
-  page.destroy();
-
-
-  page = yield new Promise(resolve => {
-    let page = Page({
-      contentURL: "./index.html",
-      contentScriptWhen: "end",
-      contentScript: "self.port.emit('end')"
-    });
-    page.port.once("end", () => resolve(page));
-  });
-
-
-  yield new Promise(resolve => {
-    panel.port.once("result2", (result) => {
-      assert.equal(result, fixtures.url("./index.html"), "result2 is a window");
-      resolve();
-    });
-
-    assert.pass("emit get-result");
-    panel.port.emit("get-result");
-  });
-
-  loader.unload();
-}
-
-
-
-function getUnsafeWindow (win) {
-  return win.wrappedJSObject || win;
-}
-
-require("sdk/test").run(exports);
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/test/test-bond.js
@@ -0,0 +1,169 @@
+/* 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 { Bond } = require("sdk/util/bond");
+const { Class } = require("sdk/core/heritage");
+
+exports["test bonds on constructors"] = assert => {
+  const MyClass = function(name) {
+    this.name = name;
+  }
+  MyClass.prototype = Bond({
+    hello() {
+      return `Hello my name is ${this.name}`
+    }
+  })
+  Object.assign(MyClass.prototype, {
+    constructor: MyClass,
+    readName() {
+      return this.name
+    }
+  });
+
+  const i1 = new MyClass("James Bond");
+
+  assert.equal(i1.hello(), "Hello my name is James Bond");
+  assert.equal(i1.hello.call({name: "Hack"}), "Hello my name is James Bond");
+  assert.equal(i1.readName(), "James Bond");
+  assert.equal(i1.readName.call({name: "Hack"}), "Hack");
+
+  const hello = i1.hello
+  assert.equal(hello(), "Hello my name is James Bond");
+};
+
+exports["test subclassing"] = assert => {
+  const MyClass = function(name) {
+    this.name = name;
+  }
+  MyClass.prototype = Bond({
+    hello() {
+      return `Hello my name is ${this.name}`
+    }
+  });
+
+  const i1 = new MyClass("James Bond");
+
+  assert.equal(i1.hello(), "Hello my name is James Bond");
+  assert.equal(i1.hello.call({name: "Hack"}), "Hello my name is James Bond");
+  const i1Hello = i1.hello
+  assert.equal(i1Hello(), "Hello my name is James Bond");
+
+  const MySubClass = function(...args) {
+    MyClass.call(this, ...args)
+  }
+  MySubClass.prototype = Object.create(MyClass.prototype)
+
+  const i2 = new MySubClass("Your father");
+
+  assert.equal(i2.hello(), "Hello my name is Your father");
+  assert.equal(i2.hello.call({name: "Hack"}), "Hello my name is Your father");
+  const i2Hello = i2.hello
+  assert.equal(i2Hello(), "Hello my name is Your father");
+};
+
+exports["test access on prototype"] = assert => {
+  const MyClass = function(name) {
+    this.name = name;
+  }
+  MyClass.prototype = Bond({
+    hello() {
+      return `Hello my name is ${this.name}`
+    }
+  });
+
+  assert.equal(MyClass.prototype.hello(), "Hello my name is undefined");
+  assert.ok(Object.getOwnPropertyDescriptor(MyClass.prototype, "hello").get,
+            "hello is still a getter");
+  assert.equal(MyClass.prototype.hello.call({name: "this"}),
+               "Hello my name is this",
+               "passing `this` on prototype methods work");
+
+  const i1 = new MyClass("James Bond");
+  assert.equal(i1.hello(), "Hello my name is James Bond");
+
+  assert.ok(!Object.getOwnPropertyDescriptor(i1, "hello").get,
+            "hello is not a getter on instance");
+
+  assert.equal(i1.hello.call({name: "Hack"}), "Hello my name is James Bond");
+  const i1Hello = i1.hello
+  assert.equal(i1Hello(), "Hello my name is James Bond");
+};
+
+
+exports["test bonds with Class"] = assert => {
+  const MyClass = Class({
+    extends: Bond({
+      hello() {
+        return `Hello my name is ${this.name}`
+      }
+    }),
+    initialize(name) {
+      this.name = name;
+    }
+  });
+
+  const i1 = new MyClass("James Bond");
+
+  assert.equal(i1.hello(), "Hello my name is James Bond");
+  assert.equal(i1.hello.call({name: "Hack"}), "Hello my name is James Bond");
+
+  const hello = i1.hello
+  assert.equal(hello(), "Hello my name is James Bond");
+};
+
+
+exports["test with mixin"] = assert => {
+  const MyClass = Class({
+    implements: [
+      Bond({
+        hello() {
+          return `Hello my name is ${this.name}`
+        }
+      })
+    ],
+    initialize(name) {
+      this.name = name;
+    }
+  });
+
+  const i1 = new MyClass("James Bond");
+
+  assert.equal(i1.hello(), "Hello my name is James Bond");
+  assert.equal(i1.hello.call({name: "Hack"}), "Hello my name is James Bond");
+
+  const hello = i1.hello
+  assert.equal(hello(), "Hello my name is James Bond");
+
+  const MyComposedClass = Class({
+    implements: [
+      MyClass,
+      Bond({
+        bye() {
+          return `Bye ${this.name}`
+        }
+      })
+    ],
+    initialize(name) {
+      this.name = name;
+    }
+  });
+
+  const i2 = new MyComposedClass("Major Tom");
+
+  assert.equal(i2.hello(), "Hello my name is Major Tom");
+  assert.equal(i2.hello.call({name: "Hack"}), "Hello my name is Major Tom");
+
+  const i2Hello = i2.hello
+  assert.equal(i2Hello(), "Hello my name is Major Tom");
+
+  assert.equal(i2.bye(), "Bye Major Tom");
+  assert.equal(i2.bye.call({name: "Hack"}), "Bye Major Tom");
+
+  const i2Bye = i2.bye
+  assert.equal(i2Bye(), "Bye Major Tom");
+};
+
+
+require("sdk/test").run(exports);
--- a/addon-sdk/source/test/test-file.js
+++ b/addon-sdk/source/test/test-file.js
@@ -1,11 +1,12 @@
 /* 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 { pathFor } = require('sdk/system');
 const file = require("sdk/io/file");
 const url = require("sdk/url");
 
 const byteStreams = require("sdk/io/byte-streams");
 const textStreams = require("sdk/io/text-streams");
--- a/addon-sdk/source/test/test-loader.js
+++ b/addon-sdk/source/test/test-loader.js
@@ -519,27 +519,9 @@ exports['test lazy globals'] = function 
   let loader = Loader({ paths: { '': uri }, modules: modules});
   assert.ok(!gotFoo, "foo hasn't been accessed during loader instanciation");
   let program = main(loader, 'main');
   assert.ok(!gotFoo, "foo hasn't been accessed during module loading");
   assert.equal(program.useFoo(), foo, "foo mock works");
   assert.ok(gotFoo, "foo has been accessed only when we first try to use it");
 };
 
-exports['test user global'] = function(assert) {
-  // Test case for bug 827792
-  let com = {};
-  let loader = require('toolkit/loader');
-  let loadOptions = require('@loader/options');
-  let options = loader.override(loadOptions,
-                                {globals: loader.override(loadOptions.globals,
-                                                          {com: com,
-                                                           console: console,
-                                                           dump: dump})});
-  let subloader = loader.Loader(options);
-  let userRequire = loader.Require(subloader, module);
-  let userModule = userRequire("./loader/user-global");
-
-  assert.equal(userModule.getCom(), com,
-               "user module returns expected `com` global");
-};
-
 require('sdk/test').run(exports);
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/test/test-memory.js
@@ -0,0 +1,22 @@
+/* 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 memory = require("sdk/deprecated/memory");
+const { gc } = require("sdk/test/memory");
+
+exports.testMemory = function(assert) {
+  var obj = {};
+  memory.track(obj, "testMemory.testObj");
+
+  var objs = memory.getObjects("testMemory.testObj");
+  assert.equal(objs[0].weakref.get(), obj);
+  obj = null;
+
+  gc().then(function() {
+    assert.equal(objs[0].weakref.get(), null);
+  });
+};
+
+require('sdk/test').run(exports);
--- a/addon-sdk/source/test/test-mpl2-license-header.js
+++ b/addon-sdk/source/test/test-mpl2-license-header.js
@@ -5,18 +5,16 @@
 "use strict";
 
 const { Cc, Ci, Cu } = require("chrome");
 const options = require('@loader/options');
 const { id } = require("sdk/self");
 const { getAddonByID } = require("sdk/addon/manager");
 const { mapcat, map, filter, fromEnumerator } = require("sdk/util/sequence");
 const { readURISync } = require('sdk/net/url');
-const { Request } = require('sdk/request');
-const { defer } = require("sdk/core/promise");
 
 const ios = Cc['@mozilla.org/network/io-service;1'].
               getService(Ci.nsIIOService);
 
 const MIT_LICENSE_HEADER = [];
 
 const MPL2_LICENSE_TEST = new RegExp([
   "^\\/\\* This Source Code Form is subject to the terms of the Mozilla Public",
@@ -54,52 +52,36 @@ const getEntries = directory => mapcat(e
     return getEntries(entry);
   }
   else if (isTestFile(entry)) {
     return [ entry ];
   }
   return [];
 }, filter(() => true, getDirectoryEntries(directory)));
 
-function readURL(url) {
-  let { promise, resolve } = defer();
-
-  Request({
-    url: url,
-    overrideMimeType: "text/plain",
-    onComplete: (response) => resolve(response.text)
-  }).get();
-
-  return promise;
-}
 
 exports["test MPL2 license header"] = function*(assert) {
   let addon = yield getAddonByID(id);
   let xpiURI = addon.getResourceURI();
   let rootURL = xpiURI.spec;
-  assert.ok(rootURL, rootURL);
   let files = [...getEntries(xpiURI.QueryInterface(Ci.nsIFileURL).file)];
 
   assert.ok(files.length > 1, files.length + " files found.");
   let failures = [];
   let success = 0;
 
-  for (let i = 0, len = files.length; i < len; i++) {
-    let file = files[i];
-    assert.ok(file.path, "Trying " + file.path);
-
+  files.forEach(file => {
     const URI = ios.newFileURI(file);
+    let leafName = URI.spec.replace(rootURL, "");
+    let contents = readURISync(URI);
 
-    let leafName = URI.spec.replace(rootURL, "");
-
-    let contents = yield readURL(URI.spec);
     if (!MPL2_LICENSE_TEST.test(contents)) {
       failures.push(leafName);
     }
-  }
+  });
 
   assert.equal(1, failures.length, "we expect one failure");
   assert.ok(/test-mpl2-license-header\.js$/.test(failures[0]), "the only failure is this file");
   failures.shift();
   assert.equal("", failures.join(",\n"), failures.length + " files found missing the required mpl 2 header");
 }
 
 require("sdk/test").run(exports);
--- a/addon-sdk/source/test/test-unit-test.js
+++ b/addon-sdk/source/test/test-unit-test.js
@@ -118,37 +118,36 @@ exports.testWaitUntilTimeoutInCallback =
   test.waitUntilDone();
 
   let expected = [];
   let message = 0;
   if (require("sdk/test/options").parseable) {
     expected.push(["print", "TEST-START | wait4ever\n"]);
     expected.push(["error", "fail:", "Timed out (after: START)"]);
     expected.push(["error", "test assertion never became true:\n", "assertion failed, value is false\n"]);
+    expected.push(["print", "TEST-END | wait4ever\n"]);
   }
   else {
     expected.push(["info",  "executing 'wait4ever'"]);
     expected.push(["error", "fail:", "Timed out (after: START)"]);
     expected.push(["error", "test assertion never became true:\n", "assertion failed, value is false\n"]);
   }
 
   function checkExpected(name, args) {
-    var index = message;
-    if (message++ >= expected.length) {
+    if (expected.length == 0 || expected[0][0] != name) {
+      test.fail("Saw an unexpected console." + name + "() call " + args);
       return;
     }
 
-    let expectedArgs = expected[index].slice(1);
-    for (let i = 0; i < expectedArgs.length; i++) {
+    message++;
+    let expectedArgs = expected.shift().slice(1);
+    for (let i = 0; i < expectedArgs.length; i++)
       test.assertEqual(args[i], expectedArgs[i], "Should have seen the right message in argument " + i + " of message " + message);
-    }
-
-    if (message >= expected.length) {
+    if (expected.length == 0)
       test.done();
-    }
   }
 
   let runner = new (require("sdk/deprecated/unit-test").TestRunner)({
     console: {
       error: function() {
         checkExpected("error", Array.slice(arguments));
       },
       info: function () {
--- a/addon-sdk/source/test/test-weak-set.js
+++ b/addon-sdk/source/test/test-weak-set.js
@@ -1,86 +1,123 @@
 /* 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";
+'use strict';
+
+const { Cu } = require('chrome');
+const { Loader } = require('sdk/test/loader');
+const { gc } = require("sdk/test/memory");
+
+exports['test adding item'] = function*(assert) {
+  let loader = Loader(module);
+  let { add, remove, has, clear, iterator } = loader.require('sdk/lang/weak-set');
 
-const { Cu } = require("chrome");
-const memory = require("sdk/test/memory");
-const { add, remove, has, clear, iterator } = require("sdk/lang/weak-set");
-const { setInterval, clearInterval } = require("sdk/timers");
+  let items = {};
+  let item = {};
+
+  add(items, item);
 
-function gc(assert) {
-  let wait = 1;
-  let interval = setInterval(function() {
-    assert.pass("waited " + (wait++ * 0.250) + "secs for gc()..");
-  }, 250);
+  yield gc();
+
+  assert.ok(has(items, item), 'the item is in the weak set');
+
+  loader.unload();
+};
 
-  return memory.gc().then(() => {
-    assert.pass("gc completed!");
-    clearInterval(interval);
-  });
-}
+exports['test remove item'] = function*(assert) {
+  let loader = Loader(module);
+  let { add, remove, has, clear, iterator } = loader.require('sdk/lang/weak-set');
+
+  let items = {};
+  let item = {};
+
+  add(items, item);
+
+  remove(items, item);
 
-exports['test add/remove/iterate/clear item'] = function*(assert) {
-  let addItems = {};
-  let removeItems = {};
-  let iterateItems = {};
-  let clearItems = {};
-  let nonReferencedItems = {};
+  yield gc();
+
+  assert.ok(!has(items, item), 'the item is not in weak set');
 
-  let item = {};
+  loader.unload();
+};
+
+exports['test iterate'] = function*(assert) {
+  let loader = Loader(module);
+  let { add, remove, has, clear, iterator } = loader.require('sdk/lang/weak-set');
+
+  let items = {};
   let addedItems = [{}, {}];
 
-  assert.pass("adding things to items");
-  add(addItems, item);
-  add(removeItems, item);
-  add(iterateItems, addedItems[0]);
-  add(iterateItems, addedItems[1]);
-  add(iterateItems, addedItems[0]); // weak set shouldn't add this twice
-  add(clearItems, addedItems[0]);
-  add(clearItems, addedItems[1]);
-  add(nonReferencedItems, {});
+  add(items, addedItems[0]);
+  add(items, addedItems[1]);
+  add(items, addedItems[0]); // weak set shouldn't add this twice
 
-  assert.pass("removing things from removeItems");
-  remove(removeItems, item);
-
-  assert.pass("clear things from clearItems");
-  clear(clearItems);
-
-  assert.pass("starting gc..");
-  yield gc(assert);
+  yield gc();
   let count = 0;
 
-  assert.equal(has(addItems, item), true, 'the addItems is in the weak set');
-  assert.equal(has(removeItems, item), false, 'the removeItems is not in weak set');
+  for (let item of iterator(items)) {
+    assert.equal(item, addedItems[count],
+      'item in the expected order');
 
-  assert.pass("iterating iterateItems..");
-  for (let item of iterator(iterateItems)) {
-    assert.equal(item, addedItems[count], "item in the expected order");
     count++;
   }
 
   assert.equal(count, 2, 'items in the expected number');
+  loader.unload();
+};
 
-  assert.pass("iterating clearItems..");
-  for (let item of iterator(clearItems)) {
-    assert.fail("the loop should not be executed");
-    count++
+exports['test clear'] = function*(assert) {
+  let loader = Loader(module);
+  let { add, remove, has, clear, iterator } = loader.require('sdk/lang/weak-set');
+
+  let items = {};
+  let addedItems = [{}, {}];
+
+  add(items, addedItems[0]);
+  add(items, addedItems[1]);
+
+  clear(items)
+
+  yield gc();
+  let count = 0;
+
+  for (let item of iterator(items)) {
+    assert.fail('the loop should not be executed');
   }
 
-  for (let item of iterator(nonReferencedItems)) {
-    assert.fail("the loop should not be executed");
-    count++
+  assert.equal(count, 0, 'no items in the weak set');
+  loader.unload();
+};
+
+exports['test adding item without reference'] = function*(assert) {
+  let loader = Loader(module);
+  let { add, remove, has, clear, iterator } = loader.require('sdk/lang/weak-set');
+
+  let items = {};
+
+  add(items, {});
+
+  yield gc();
+  let count = 0;
+
+  for (let item of iterator(items)) {
+    assert.fail('the loop should not be executed');
   }
 
-  assert.equal(count, 2, 'items in the expected number');
+  assert.equal(count, 0, 'no items in the weak set');
+
+  loader.unload();
 };
 
 exports['test adding non object or null item'] = function(assert) {
+  let loader = Loader(module);
+  let { add, remove, has, clear, iterator } = loader.require('sdk/lang/weak-set');
+
   let items = {};
 
   assert.throws(() => {
     add(items, 'foo');
   },
   /^\w+ is not a non-null object/,
   'only non-null object are allowed');
 
@@ -105,16 +142,19 @@ exports['test adding non object or null 
   assert.throws(() => {
     add(items, true);
   },
   /^\w+ is not a non-null object/,
   'only non-null object are allowed');
 };
 
 exports['test adding to non object or null item'] = function(assert) {
+  let loader = Loader(module);
+  let { add, remove, has, clear, iterator } = loader.require('sdk/lang/weak-set');
+
   let item = {};
 
   assert.throws(() => {
     add('foo', item);
   },
   /^\w+ is not a non-null object/,
   'only non-null object are allowed');
 
@@ -138,9 +178,9 @@ exports['test adding to non object or nu
 
   assert.throws(() => {
     add(true, item);
   },
   /^\w+ is not a non-null object/,
   'only non-null object are allowed');
 };
 
-require("sdk/test").run(exports);
+require('sdk/test').run(exports);
--- a/addon-sdk/source/test/test-windows-common.js
+++ b/addon-sdk/source/test/test-windows-common.js
@@ -5,17 +5,16 @@
 
 const { Loader } = require('sdk/test/loader');
 const { browserWindows } = require('sdk/windows');
 const { isFocused } = require('sdk/window/utils');
 const { viewFor } = require('sdk/view/core');
 const { modelFor } = require('sdk/model/core');
 const { Ci } = require("chrome");
 const { isBrowser, getWindowTitle } = require("sdk/window/utils");
-const { after, cleanUI } = require("sdk/test/utils");
 
 // TEST: browserWindows Iterator
 exports.testBrowserWindowsIterator = function(assert) {
   let activeWindowCount = 0;
   let windows = [];
   let i = 0;
   for (let window of browserWindows) {
     if (window === browserWindows.activeWindow)
@@ -61,28 +60,30 @@ exports.testWindowActivateMethod_simple 
 
   assert.equal(browserWindows.activeWindow, window,
                'Active window is active after window.activate() call');
   assert.equal(window.tabs.activeTab, tab,
                'Active tab is active after window.activate() call');
 };
 
 
-exports["test getView(window)"] = function*(assert) {
-  let window = yield new Promise(resolve => {
-    browserWindows.once("open", resolve);
-    browserWindows.open({ url: "data:text/html;charset=utf-8,<title>yo</title>" });
+exports["test getView(window)"] = function(assert, done) {
+  browserWindows.once("open", window => {
+    const view = viewFor(window);
+
+    assert.ok(view instanceof Ci.nsIDOMWindow, "view is a window");
+    assert.ok(isBrowser(view), "view is a browser window");
+    assert.equal(getWindowTitle(view), window.title,
+                 "window has a right title");
+
+    window.close(done);
   });
 
-  const view = viewFor(window);
 
-  assert.ok(view instanceof Ci.nsIDOMWindow, "view is a window");
-  assert.ok(isBrowser(view), "view is a browser window");
-  assert.equal(getWindowTitle(view), window.title,
-               "window has a right title");
+  browserWindows.open({ url: "data:text/html;charset=utf-8,<title>yo</title>" });
 };
 
 
 exports["test modelFor(window)"] = function(assert, done) {
   browserWindows.once("open", window => {
     const view = viewFor(window);
 
     assert.ok(view instanceof Ci.nsIDOMWindow, "view is a window");
@@ -91,14 +92,9 @@ exports["test modelFor(window)"] = funct
 
     window.close(done);
   });
 
 
   browserWindows.open({ url: "data:text/html;charset=utf-8,<title>yo</title>" });
 };
 
-after(exports, function*(name, assert) {
-  assert.pass("cleaning the ui.");
-  yield cleanUI();
-});
-
 require('sdk/test').run(exports);
--- a/addon-sdk/source/test/test-xul-app.js
+++ b/addon-sdk/source/test/test-xul-app.js
@@ -1,45 +1,44 @@
 /* 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";
 
 var xulApp = require("sdk/system/xul-app");
 
-exports["test xulapp"] = function (assert) {
+exports["test xulapp"] = function(assert) {
   assert.equal(typeof(xulApp.ID), "string",
                    "ID is a string");
   assert.equal(typeof(xulApp.name), "string",
                    "name is a string");
   assert.equal(typeof(xulApp.version), "string",
                    "version is a string");
   assert.equal(typeof(xulApp.platformVersion), "string",
                    "platformVersion is a string");
 
-  assert.throws(() => xulApp.is("blargy"),
-      /Unkown Mozilla Application: blargy/,
-      "is() throws error on bad app name");
-
-  assert.throws(() => xulApp.isOneOf(["blargy"]),
-      /Unkown Mozilla Application: blargy/,
-      "isOneOf() throws error on bad app name");
+  assert.throws(function() { xulApp.is("blargy"); },
+                    /Unkown Mozilla Application: blargy/,
+                    "is() throws error on bad app name");
+  assert.throws(function() { xulApp.isOneOf(["blargy"]); },
+                    /Unkown Mozilla Application: blargy/,
+                    "isOneOf() throws error on bad app name");
 
   function testSupport(name) {
     var item = xulApp.is(name);
     assert.ok(item === true || item === false,
-                  "is('" + name + "') is true or false.");
+                "is('" + name + "') is true or false.");
   }
 
   var apps = ["Firefox", "Mozilla", "SeaMonkey", "Fennec", "Thunderbird"];
 
   apps.forEach(testSupport);
 
-  assert.ok(xulApp.isOneOf(apps) == true || xulApp.isOneOf(apps) == false,
-                "isOneOf() returns true or false.");
+  assert.ok(xulApp.isOneOf(apps) == true ||
+              xulApp.isOneOf(apps) == false,
+              "isOneOf() returns true or false.");
 
   assert.equal(xulApp.versionInRange(xulApp.platformVersion, "1.9", "*"),
                    true, "platformVersion in range [1.9, *)");
   assert.equal(xulApp.versionInRange("3.6.4", "3.6.4", "3.6.*"),
                    true, "3.6.4 in [3.6.4, 3.6.*)");
   assert.equal(xulApp.versionInRange("1.9.3", "1.9.2", "1.9.3"),
                    false, "1.9.3 not in [1.9.2, 1.9.3)");
 };
--- a/addon-sdk/source/test/windows/test-firefox-windows.js
+++ b/addon-sdk/source/test/windows/test-firefox-windows.js
@@ -322,16 +322,17 @@ exports.testTrackWindows = function(asse
   }
 
   // listen to global activate events
   browserWindows.on("activate", windowsActivation);
 
   // listen to global deactivate events
   browserWindows.on("deactivate", windowsDeactivation);
 
+
   function openWindow() {
     windows.push(browserWindows.open({
       url: "data:text/html;charset=utf-8,<i>testTrackWindows</i>",
       onActivate: function(window) {
         let index = windows.indexOf(window);
 
         // Guard against windows that have already been removed.
         // See bug 874502 comment 32.
@@ -344,20 +345,18 @@ exports.testTrackWindows = function(asse
         actions.push("activate " + index);
 
         if (windows.length < 3) {
           openWindow()
         }
         else {
           (function closeWindows(windows) {
             if (!windows.length) {
-              assert.pass('the last window was closed');
               browserWindows.removeListener("activate", windowsActivation);
               browserWindows.removeListener("deactivate", windowsDeactivation);
-              assert.pass('removed listeners');
               return done();
             }
 
             return windows.pop().close(function() {
               assert.pass('window was closed');
               closeWindows(windows);
             });
           })(windows)