Merge latest green inbound changeset and mozilla-central
authorEd Morley <emorley@mozilla.com>
Wed, 21 Aug 2013 12:59:33 +0100
changeset 143664 ba6c02fc1fe65230f289ca9eeed9c42af3edde6a
parent 143663 8ab3659863d7d2d97f4e4a5a40a028938be53327 (current diff)
parent 143597 e78c725d45bd197d87c8732f8877925f2967a5d4 (diff)
child 143665 3d355303f4da4e5682d1c0df2bc04d154c4a0f4f
child 143683 d4754ae964dc8906971d73eec888fd90a18577bd
child 143704 ab6bc4d9e4c0fe907bcef53ff725fc83c62a1800
push id2298
push useremorley@mozilla.com
push dateWed, 21 Aug 2013 12:10:39 +0000
treeherderfx-team@3d355303f4da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone26.0a1
Merge latest green inbound changeset and mozilla-central
CLOBBER
addon-sdk/source/python-lib/cuddlefish/tests/preferences-files/packages/curly-id/lib/main.js
addon-sdk/source/python-lib/cuddlefish/tests/preferences-files/packages/curly-id/package.json
addon-sdk/source/python-lib/cuddlefish/tests/preferences-files/packages/preferences-branch/lib/main.js
addon-sdk/source/python-lib/cuddlefish/tests/preferences-files/packages/preferences-branch/package.json
addon-sdk/source/test/addons/curly-id/lib/main.js
addon-sdk/source/test/addons/curly-id/package.json
addon-sdk/source/test/addons/predefined-id-with-at/lib/main.js
addon-sdk/source/test/addons/predefined-id-with-at/package.json
addon-sdk/source/test/addons/preferences-branch/lib/main.js
addon-sdk/source/test/addons/preferences-branch/package.json
addon-sdk/source/test/addons/standard-id/lib/main.js
addon-sdk/source/test/addons/standard-id/package.json
mobile/android/base/AwesomeBar.java
mobile/android/base/AwesomeBarTabs.java
mobile/android/base/AwesomebarResultHandler.java
mobile/android/base/SearchEngine.java
mobile/android/base/SearchEngineRow.java
mobile/android/base/SuggestClient.java
mobile/android/base/awesomebar/AllPagesTab.java
mobile/android/base/awesomebar/AwesomeBarTab.java
mobile/android/base/awesomebar/BookmarksTab.java
mobile/android/base/awesomebar/HistoryTab.java
mobile/android/base/resources/anim/awesomebar_fade_in.xml
mobile/android/base/resources/anim/awesomebar_fade_out.xml
mobile/android/base/resources/anim/awesomebar_hold_still.xml
mobile/android/base/resources/color/abouthome_section_more_text.xml
mobile/android/base/resources/color/abouthome_section_subtitle.xml
mobile/android/base/resources/color/abouthome_section_title.xml
mobile/android/base/resources/color/awesome_bar_title.xml
mobile/android/base/resources/color/awesome_bar_title_hint.xml
mobile/android/base/resources/drawable-hdpi/abouthome_icon.png
mobile/android/base/resources/drawable-hdpi/abouthome_logo_dark.png
mobile/android/base/resources/drawable-hdpi/abouthome_logo_light.png
mobile/android/base/resources/drawable-hdpi/abouthome_promo_box_bg.9.png
mobile/android/base/resources/drawable-hdpi/abouthome_promo_box_pressed_bg.9.png
mobile/android/base/resources/drawable-hdpi/abouthome_promo_logo_apps.png
mobile/android/base/resources/drawable-hdpi/abouthome_promo_logo_sync.png
mobile/android/base/resources/drawable-hdpi/abouthome_thumbnail_add.png
mobile/android/base/resources/drawable-hdpi/abouthome_thumbnail_bg.png
mobile/android/base/resources/drawable-hdpi/address_bar_bg_shadow.png
mobile/android/base/resources/drawable-hdpi/address_bar_url_default.9.png
mobile/android/base/resources/drawable-hdpi/address_bar_url_default_pb.9.png
mobile/android/base/resources/drawable-hdpi/address_bar_url_pressed.9.png
mobile/android/base/resources/drawable-hdpi/address_bar_url_pressed_pb.9.png
mobile/android/base/resources/drawable-hdpi/ic_addons_empty.png
mobile/android/base/resources/drawable-hdpi/ic_awesomebar_go.png
mobile/android/base/resources/drawable-hdpi/ic_awesomebar_reader.png
mobile/android/base/resources/drawable-hdpi/ic_awesomebar_search.png
mobile/android/base/resources/drawable-hdpi/ic_awesomebar_star.png
mobile/android/base/resources/drawable-hdpi/ic_awesomebar_tab.png
mobile/android/base/resources/drawable-mdpi/abouthome_icon.png
mobile/android/base/resources/drawable-mdpi/abouthome_logo_dark.png
mobile/android/base/resources/drawable-mdpi/abouthome_logo_light.png
mobile/android/base/resources/drawable-mdpi/abouthome_promo_box_bg.9.png
mobile/android/base/resources/drawable-mdpi/abouthome_promo_box_pressed_bg.9.png
mobile/android/base/resources/drawable-mdpi/abouthome_promo_logo_apps.png
mobile/android/base/resources/drawable-mdpi/abouthome_promo_logo_sync.png
mobile/android/base/resources/drawable-mdpi/abouthome_thumbnail_add.png
mobile/android/base/resources/drawable-mdpi/abouthome_thumbnail_bg.png
mobile/android/base/resources/drawable-mdpi/address_bar_bg_shadow.png
mobile/android/base/resources/drawable-mdpi/address_bar_url_default.9.png
mobile/android/base/resources/drawable-mdpi/address_bar_url_default_pb.9.png
mobile/android/base/resources/drawable-mdpi/address_bar_url_pressed.9.png
mobile/android/base/resources/drawable-mdpi/address_bar_url_pressed_pb.9.png
mobile/android/base/resources/drawable-mdpi/ic_addons_empty.png
mobile/android/base/resources/drawable-mdpi/ic_awesomebar_go.png
mobile/android/base/resources/drawable-mdpi/ic_awesomebar_reader.png
mobile/android/base/resources/drawable-mdpi/ic_awesomebar_search.png
mobile/android/base/resources/drawable-mdpi/ic_awesomebar_star.png
mobile/android/base/resources/drawable-mdpi/ic_awesomebar_tab.png
mobile/android/base/resources/drawable-xhdpi/abouthome_icon.png
mobile/android/base/resources/drawable-xhdpi/abouthome_logo_dark.png
mobile/android/base/resources/drawable-xhdpi/abouthome_logo_light.png
mobile/android/base/resources/drawable-xhdpi/abouthome_promo_box_bg.9.png
mobile/android/base/resources/drawable-xhdpi/abouthome_promo_box_pressed_bg.9.png
mobile/android/base/resources/drawable-xhdpi/abouthome_promo_logo_apps.png
mobile/android/base/resources/drawable-xhdpi/abouthome_promo_logo_sync.png
mobile/android/base/resources/drawable-xhdpi/abouthome_thumbnail_add.png
mobile/android/base/resources/drawable-xhdpi/abouthome_thumbnail_bg.png
mobile/android/base/resources/drawable-xhdpi/address_bar_bg_shadow.png
mobile/android/base/resources/drawable-xhdpi/address_bar_url_default.9.png
mobile/android/base/resources/drawable-xhdpi/address_bar_url_default_pb.9.png
mobile/android/base/resources/drawable-xhdpi/address_bar_url_pressed.9.png
mobile/android/base/resources/drawable-xhdpi/address_bar_url_pressed_pb.9.png
mobile/android/base/resources/drawable-xhdpi/ic_addons_empty.png
mobile/android/base/resources/drawable-xhdpi/ic_awesomebar_go.png
mobile/android/base/resources/drawable-xhdpi/ic_awesomebar_reader.png
mobile/android/base/resources/drawable-xhdpi/ic_awesomebar_search.png
mobile/android/base/resources/drawable-xhdpi/ic_awesomebar_star.png
mobile/android/base/resources/drawable-xhdpi/ic_awesomebar_tab.png
mobile/android/base/resources/drawable/abouthome_logo.xml
mobile/android/base/resources/drawable/abouthome_promo_box.xml
mobile/android/base/resources/drawable/address_bar_bg.xml
mobile/android/base/resources/drawable/address_bar_bg_shadow_repeat.xml
mobile/android/base/resources/drawable/address_bar_nav_button.xml
mobile/android/base/resources/drawable/address_bar_right_edge.xml
mobile/android/base/resources/drawable/address_bar_url.xml
mobile/android/base/resources/drawable/awesomebar_header_row.xml
mobile/android/base/resources/drawable/awesomebar_listview_divider.xml
mobile/android/base/resources/drawable/awesomebar_tab_indicator.xml
mobile/android/base/resources/drawable/awesomebar_tab_selected.xml
mobile/android/base/resources/drawable/awesomebar_tab_unselected.xml
mobile/android/base/resources/layout-large-v11/awesomebar_search.xml
mobile/android/base/resources/layout-xlarge-land-v11/abouthome_content.xml
mobile/android/base/resources/layout-xlarge-v11/awesomebar_search.xml
mobile/android/base/resources/layout/abouthome_addon_row.xml
mobile/android/base/resources/layout/abouthome_content.xml
mobile/android/base/resources/layout/abouthome_last_tabs_row.xml
mobile/android/base/resources/layout/abouthome_remote_tab_row.xml
mobile/android/base/resources/layout/abouthome_section.xml
mobile/android/base/resources/layout/abouthome_topsite_item.xml
mobile/android/base/resources/layout/awesomebar.xml
mobile/android/base/resources/layout/awesomebar_allpages_list.xml
mobile/android/base/resources/layout/awesomebar_folder_row.xml
mobile/android/base/resources/layout/awesomebar_header_row.xml
mobile/android/base/resources/layout/awesomebar_row.xml
mobile/android/base/resources/layout/awesomebar_search.xml
mobile/android/base/resources/layout/awesomebar_suggestion_prompt.xml
mobile/android/base/resources/layout/awesomebar_tab_indicator.xml
mobile/android/base/resources/layout/awesomebar_tabs.xml
mobile/android/base/resources/menu/abouthome_topsites_contextmenu.xml
mobile/android/base/resources/menu/awesomebar_contextmenu.xml
mobile/android/base/tests/testAllPagesTab.java.in
mobile/android/base/tests/testBookmarksTab.java.in
mobile/android/base/tests/testHistoryTab.java.in
mobile/android/base/tests/testInputAwesomeBar.java.in
mobile/android/base/widget/AboutHome.java
mobile/android/base/widget/AboutHomeSection.java
mobile/android/base/widget/AboutHomeView.java
mobile/android/base/widget/AddonsSection.java
mobile/android/base/widget/LastTabsSection.java
mobile/android/base/widget/LinkTextView.java
mobile/android/base/widget/PromoBox.java
mobile/android/base/widget/RemoteTabsSection.java
mobile/android/base/widget/TopSitesView.java
--- a/addon-sdk/source/app-extension/bootstrap.js
+++ b/addon-sdk/source/app-extension/bootstrap.js
@@ -1,9 +1,8 @@
-/* vim:set ts=2 sw=2 sts=2 expandtab */
 /* 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/. */
 
 // @see http://mxr.mozilla.org/mozilla-central/source/js/src/xpconnect/loader/mozJSComponentLoader.cpp
 
 'use strict';
 
@@ -217,18 +216,16 @@ function startup(data, reasonCode) {
       prefixURI: prefixURI,
       // Add-on URI.
       rootURI: rootURI,
       // options used by system module.
       // File to write 'OK' or 'FAIL' (exit code emulation).
       resultFile: options.resultFile,
       // Arguments passed as --static-args
       staticArgs: options.staticArgs,
-      // Add-on preferences branch name
-      preferencesBranch: options.preferencesBranch,
 
       // Arguments related to test runner.
       modules: {
         '@test/options': {
           allTestModules: options.allTestModules,
           iterations: options.iterations,
           filter: options.filter,
           profileMemory: options.profileMemory,
--- a/addon-sdk/source/lib/method/core.js
+++ b/addon-sdk/source/lib/method/core.js
@@ -94,17 +94,17 @@ function Method(hint) {
                  ((type = builtin[(value.constructor || "").name]) &&
                   type[name]) ||
                  // Otherwise assume it's a host object. For host objects
                  // actual method implementations are stored in the `host`
                  // array and only index for the implementation is stored
                  // in the host object's prototype chain. This avoids memory
                  // leaks that otherwise could happen when saving JS objects
                  // on host object.
-                 host[value["!" + name]] ||
+                 host[value["!" + name] || void(0)] ||
                  // Otherwise attempt to lookup implementation for builtins by
                  // a type of the value. This basically makes sure that all
                  // non primitive values will delegate to an `Object`.
                  ((type = builtin[types[typeof(value)]]) && type[name])
 
 
     // If method implementation for the type is still not found then
     // just fallback for default implementation.
--- a/addon-sdk/source/lib/sdk/addon/installer.js
+++ b/addon-sdk/source/lib/sdk/addon/installer.js
@@ -1,18 +1,14 @@
 /* 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/. */
 
 module.metadata = {
-  "stability": "experimental",
-  "engines": {
-    // TODO Fennec Support in bug 894515
-    "Firefox": "*"
-  }
+  "stability": "experimental"
 };
 
 const { Cc, Ci, Cu } = require("chrome");
 const { AddonManager } = Cu.import("resource://gre/modules/AddonManager.jsm");
 const { defer } = require("../core/promise");
 const { setTimeout } = require("../timers");
 
 /**
--- a/addon-sdk/source/lib/sdk/addon/runner.js
+++ b/addon-sdk/source/lib/sdk/addon/runner.js
@@ -1,25 +1,24 @@
-/* vim:set ts=2 sw=2 sts=2 expandtab */
 /* 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/.
- */
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 module.metadata = {
   "stability": "experimental"
 };
 
 const { Cc, Ci } = require('chrome');
 const { descriptor, Sandbox, evaluate, main, resolveURI } = require('toolkit/loader');
 const { once } = require('../system/events');
 const { exit, env, staticArgs } = require('../system');
 const { when: unload } = require('../system/unload');
 const { loadReason } = require('../self');
 const { rootURI } = require("@loader/options");
+const cfxArgs = require("@test/options");
 const globals = require('../system/globals');
 const xulApp = require('../system/xul-app');
 const appShellService = Cc['@mozilla.org/appshell/appShellService;1'].
                         getService(Ci.nsIAppShellService);
 
 const NAME2TOPIC = {
   'Firefox': 'sessionstore-windows-restored',
   'Fennec': 'sessionstore-windows-restored',
@@ -98,25 +97,33 @@ function startup(reason, options) {
 
   // NOTE: Module is intentionally required only now because it relies
   // on existence of hidden window, which does not exists until startup.
   let { ready } = require('../addon/window');
   // Load localization manifest and .properties files.
   // Run the addon even in case of error (best effort approach)
   require('../l10n/loader').
     load(rootURI).
-    then(null, function failure(error) {
+    then(function l10nSuccess() {
+      if (cfxArgs.parseable) {
+        console.info("localization information has loaded successfully.");
+      }
+    }, function l10nFailure(error) {
       console.info("Error while loading localization: " + error.message);
     }).
     then(function onLocalizationReady(data) {
       // Exports data to a pseudo module so that api-utils/l10n/core
       // can get access to it
       definePseudo(options.loader, '@l10n/data', data ? data : null);
       return ready;
     }).then(function() {
+      if (cfxArgs.parseable) {
+        console.info("addon window has loaded successfully.");
+      }
+
       run(options);
     }).then(null, console.exception);
 }
 
 function run(options) {
   try {
     // Try initializing HTML localization before running main module. Just print
     // an exception in case of error, instead of preventing addon to be run.
@@ -125,16 +132,17 @@ function run(options) {
       // disable. Because unit tests are evaluated in a another Loader who
       // doesn't have access to this current loader.
       if (options.main !== 'test-harness/run-tests')
         require('../l10n/html').enable();
     }
     catch(error) {
       console.exception(error);
     }
+
     // Initialize inline options localization, without preventing addon to be
     // run in case of error
     try {
       require('../l10n/prefs');
     }
     catch(error) {
       console.exception(error);
     }
@@ -154,17 +162,18 @@ function run(options) {
       program.main({
         loadReason: loadReason,
         staticArgs: staticArgs
       }, {
         print: function print(_) { dump(_ + '\n') },
         quit: exit
       });
     }
-  } catch (error) {
+  }
+  catch (error) {
     console.exception(error);
     throw error;
   }
 }
 exports.startup = startup;
 
 // If add-on is lunched via `cfx run` we need to use `system.exit` to let
 // cfx know we're done (`cfx test` will take care of exit so we don't do
--- a/addon-sdk/source/lib/sdk/addon/window.js
+++ b/addon-sdk/source/lib/sdk/addon/window.js
@@ -1,13 +1,11 @@
 /* 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/.
- */
-
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 module.metadata = {
   "stability": "experimental"
 };
 
 const { Ci, Cc } = require("chrome");
 const { make: makeWindow, getHiddenWindow } = require("../window/utils");
--- a/addon-sdk/source/lib/sdk/clipboard.js
+++ b/addon-sdk/source/lib/sdk/clipboard.js
@@ -1,10 +1,8 @@
-/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et: */
 /* 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": "stable",
--- a/addon-sdk/source/lib/sdk/console/plain-text.js
+++ b/addon-sdk/source/lib/sdk/console/plain-text.js
@@ -15,17 +15,17 @@ const { merge } = require("../util/objec
 const { ConsoleAPI } = Cu.import("resource://gre/modules/devtools/Console.jsm");
 
 const DEFAULT_LOG_LEVEL = "error";
 const ADDON_LOG_LEVEL_PREF = "extensions." + self.id + ".sdk.console.logLevel";
 const SDK_LOG_LEVEL_PREF = "extensions.sdk.console.logLevel";
 
 let logLevel = DEFAULT_LOG_LEVEL;
 function setLogLevel() {
-  logLevel = prefs.get(ADDON_LOG_LEVEL_PREF, 
+  logLevel = prefs.get(ADDON_LOG_LEVEL_PREF,
                            prefs.get(SDK_LOG_LEVEL_PREF,
                                      DEFAULT_LOG_LEVEL));
 }
 setLogLevel();
 
 let logLevelObserver = {
   QueryInterface: function(iid) {
     if (!iid.equals(Ci.nsIObserver) &&
--- a/addon-sdk/source/lib/sdk/content/content.js
+++ b/addon-sdk/source/lib/sdk/content/content.js
@@ -1,10 +1,8 @@
-/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et: */
 /* 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": "unstable"
 };
--- a/addon-sdk/source/lib/sdk/content/loader.js
+++ b/addon-sdk/source/lib/sdk/content/loader.js
@@ -1,10 +1,8 @@
-/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et: */
 /* 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": "unstable"
--- a/addon-sdk/source/lib/sdk/content/mod.js
+++ b/addon-sdk/source/lib/sdk/content/mod.js
@@ -1,10 +1,8 @@
-/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et: */
 /* 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"
 };
--- a/addon-sdk/source/lib/sdk/content/symbiont.js
+++ b/addon-sdk/source/lib/sdk/content/symbiont.js
@@ -1,10 +1,8 @@
-/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et: */
 /* 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": "unstable"
 };
--- a/addon-sdk/source/lib/sdk/content/utils.js
+++ b/addon-sdk/source/lib/sdk/content/utils.js
@@ -1,10 +1,8 @@
-/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et: */
 /* 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": "unstable"
 };
--- a/addon-sdk/source/lib/sdk/content/worker.js
+++ b/addon-sdk/source/lib/sdk/content/worker.js
@@ -1,10 +1,8 @@
-/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et: */
 /* 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": "unstable"
 };
--- a/addon-sdk/source/lib/sdk/core/heritage.js
+++ b/addon-sdk/source/lib/sdk/core/heritage.js
@@ -1,14 +1,11 @@
-/* vim:set ts=2 sw=2 sts=2 expandtab */
 /* 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/.
- */
-
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 'use strict';
 
 module.metadata = {
   "stability": "unstable"
 };
 
 var getPrototypeOf = Object.getPrototypeOf;
 var getNames = Object.getOwnPropertyNames;
--- a/addon-sdk/source/lib/sdk/core/promise.js
+++ b/addon-sdk/source/lib/sdk/core/promise.js
@@ -1,12 +1,8 @@
-/* vim:set ts=2 sw=2 sts=2 expandtab */
-/*jshint undef: true es5: true node: true browser: true devel: true
-         forin: true latedef: false */
-/*global define: true, Cu: true, __URI__: true */
 ;(function(id, factory) { // Module boilerplate :(
   if (typeof(define) === 'function') { // RequireJS
     define(factory);
   } else if (typeof(require) === 'function') { // CommonJS
     factory.call(this, require, exports, module);
   } else if (String(this).indexOf('BackstagePass') >= 0) { // JSM
     this[factory.name] = {};
     try {
--- a/addon-sdk/source/lib/sdk/deprecated/api-utils.js
+++ b/addon-sdk/source/lib/sdk/deprecated/api-utils.js
@@ -1,10 +1,8 @@
-/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et: */
 /* 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"
 };
--- a/addon-sdk/source/lib/sdk/deprecated/cortex.js
+++ b/addon-sdk/source/lib/sdk/deprecated/cortex.js
@@ -1,10 +1,9 @@
-/* vim:set ts=2 sw=2 sts=2
- * This Source Code Form is subject to the terms of the Mozilla Public
+/* 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"
 };
--- a/addon-sdk/source/lib/sdk/deprecated/events/assembler.js
+++ b/addon-sdk/source/lib/sdk/deprecated/events/assembler.js
@@ -1,9 +1,8 @@
-/* vim:set ts=2 sw=2 sts=2 et: */
 /* 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 { Trait } = require("../light-traits");
 const { removeListener, on } = require("../../dom/events");
--- a/addon-sdk/source/lib/sdk/deprecated/light-traits.js
+++ b/addon-sdk/source/lib/sdk/deprecated/light-traits.js
@@ -1,10 +1,9 @@
-/* vim:ts=2:sts=2:sw=2:
- * This Source Code Form is subject to the terms of the Mozilla Public
+/* 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"
 };
--- a/addon-sdk/source/lib/sdk/deprecated/unit-test.js
+++ b/addon-sdk/source/lib/sdk/deprecated/unit-test.js
@@ -1,10 +1,9 @@
-/* vim:st=2:sts=2:sw=2:
- * This Source Code Form is subject to the terms of the Mozilla Public
+/* 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"
 };
--- a/addon-sdk/source/lib/sdk/dom/events.js
+++ b/addon-sdk/source/lib/sdk/dom/events.js
@@ -1,9 +1,8 @@
-/* vim:set ts=2 sw=2 sts=2 et: */
 /* 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": "unstable"
--- a/addon-sdk/source/lib/sdk/dom/events/keys.js
+++ b/addon-sdk/source/lib/sdk/dom/events/keys.js
@@ -1,9 +1,8 @@
-/* vim:set ts=2 sw=2 sts=2 et: */
 /* 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": "unstable"
--- a/addon-sdk/source/lib/sdk/event/core.js
+++ b/addon-sdk/source/lib/sdk/event/core.js
@@ -1,10 +1,8 @@
-/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et: */
 /* 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": "unstable"
--- a/addon-sdk/source/lib/sdk/event/target.js
+++ b/addon-sdk/source/lib/sdk/event/target.js
@@ -1,10 +1,8 @@
-/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et: */
 /* 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": "stable"
--- a/addon-sdk/source/lib/sdk/frame/hidden-frame.js
+++ b/addon-sdk/source/lib/sdk/frame/hidden-frame.js
@@ -1,10 +1,8 @@
-/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et: */
 /* 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"
--- a/addon-sdk/source/lib/sdk/hotkeys.js
+++ b/addon-sdk/source/lib/sdk/hotkeys.js
@@ -1,9 +1,8 @@
-/* vim:set ts=2 sw=2 sts=2 et: */
 /* 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": "stable"
--- a/addon-sdk/source/lib/sdk/io/buffer.js
+++ b/addon-sdk/source/lib/sdk/io/buffer.js
@@ -1,12 +1,11 @@
 /* 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/.
- */
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 module.metadata = {
   "stability": "experimental"
 };
 
 
 const { Cu } = require("chrome");
--- a/addon-sdk/source/lib/sdk/io/byte-streams.js
+++ b/addon-sdk/source/lib/sdk/io/byte-streams.js
@@ -1,11 +1,9 @@
-/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * vim:set ts=2 sw=2 sts=2 et filetype=javascript
- * This Source Code Form is subject to the terms of the Mozilla Public
+/* 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"
 };
--- a/addon-sdk/source/lib/sdk/io/fs.js
+++ b/addon-sdk/source/lib/sdk/io/fs.js
@@ -1,12 +1,11 @@
 /* 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/.
- */
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 module.metadata = {
   "stability": "experimental"
 };
 
 const { Cc, Ci, CC } = require("chrome");
 
@@ -922,9 +921,9 @@ exports.watchFile = watchFile;
 function unwatchFile(path, listener) {
   throw Error("Not implemented");
 }
 exports.unwatchFile = unwatchFile;
 
 function watch(path, options, listener) {
   throw Error("Not implemented");
 }
-exports.watch = watch;
\ No newline at end of file
+exports.watch = watch;
--- a/addon-sdk/source/lib/sdk/io/stream.js
+++ b/addon-sdk/source/lib/sdk/io/stream.js
@@ -1,12 +1,11 @@
 /* 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/.
- */
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 module.metadata = {
   "stability": "experimental"
 };
 
 const { CC, Cc, Ci, Cu, Cr, components } = require("chrome");
 const { EventTarget } = require("../event/target");
--- a/addon-sdk/source/lib/sdk/io/text-streams.js
+++ b/addon-sdk/source/lib/sdk/io/text-streams.js
@@ -1,11 +1,9 @@
-/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * vim:set ts=2 sw=2 sts=2 et filetype=javascript
- * This Source Code Form is subject to the terms of the Mozilla Public
+/* 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"
 };
--- a/addon-sdk/source/lib/sdk/keyboard/hotkeys.js
+++ b/addon-sdk/source/lib/sdk/keyboard/hotkeys.js
@@ -1,9 +1,8 @@
-/* vim:set ts=2 sw=2 sts=2 et: */
 /* 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": "unstable"
--- a/addon-sdk/source/lib/sdk/keyboard/observer.js
+++ b/addon-sdk/source/lib/sdk/keyboard/observer.js
@@ -1,9 +1,8 @@
-/* vim:set ts=2 sw=2 sts=2 et: */
 /* 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": "unstable"
--- a/addon-sdk/source/lib/sdk/keyboard/utils.js
+++ b/addon-sdk/source/lib/sdk/keyboard/utils.js
@@ -1,9 +1,8 @@
-/* vim:set ts=2 sw=2 sts=2 et: */
 /* 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": "unstable"
--- a/addon-sdk/source/lib/sdk/lang/type.js
+++ b/addon-sdk/source/lib/sdk/lang/type.js
@@ -1,10 +1,9 @@
-/* vim:ts=2:sts=2:sw=2:
- * This Source Code Form is subject to the terms of the Mozilla Public
+/* 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": "unstable"
 };
--- a/addon-sdk/source/lib/sdk/loader/cuddlefish.js
+++ b/addon-sdk/source/lib/sdk/loader/cuddlefish.js
@@ -1,14 +1,11 @@
-/* vim:set ts=2 sw=2 sts=2 expandtab */
 /* 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/.
- */
-
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 'use strict';
 
 module.metadata = {
   "stability": "unstable"
 };
 
 // This module is manually loaded by bootstrap.js in a sandbox and immediatly
 // put in module cache so that it is never loaded in any other way.
--- a/addon-sdk/source/lib/sdk/loader/sandbox.js
+++ b/addon-sdk/source/lib/sdk/loader/sandbox.js
@@ -1,10 +1,8 @@
-/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et: */
 /* 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"
 };
--- a/addon-sdk/source/lib/sdk/notifications.js
+++ b/addon-sdk/source/lib/sdk/notifications.js
@@ -1,11 +1,9 @@
-/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * vim:set ts=2 sw=2 sts=2 et filetype=javascript
- * This Source Code Form is subject to the terms of the Mozilla Public
+/* 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": "stable"
 };
--- a/addon-sdk/source/lib/sdk/page-mod.js
+++ b/addon-sdk/source/lib/sdk/page-mod.js
@@ -1,10 +1,8 @@
-/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et: */
 /* 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": "stable"
 };
--- a/addon-sdk/source/lib/sdk/page-worker.js
+++ b/addon-sdk/source/lib/sdk/page-worker.js
@@ -1,10 +1,8 @@
-/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et: */
 /* 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": "stable"
 };
--- a/addon-sdk/source/lib/sdk/passwords.js
+++ b/addon-sdk/source/lib/sdk/passwords.js
@@ -1,9 +1,8 @@
-/* vim:set ts=2 sw=2 sts=2 et: */
 /* 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": "stable"
 };
--- a/addon-sdk/source/lib/sdk/passwords/utils.js
+++ b/addon-sdk/source/lib/sdk/passwords/utils.js
@@ -1,9 +1,8 @@
-/* vim:set ts=2 sw=2 sts=2 et: */
 /* 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": "unstable"
--- a/addon-sdk/source/lib/sdk/places/contract.js
+++ b/addon-sdk/source/lib/sdk/places/contract.js
@@ -1,10 +1,8 @@
-/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et: */
 /* 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": "unstable"
--- a/addon-sdk/source/lib/sdk/self.js
+++ b/addon-sdk/source/lib/sdk/self.js
@@ -1,10 +1,9 @@
-/* vim:st=2:sts=2:sw=2:
- * This Source Code Form is subject to the terms of the Mozilla Public
+/* 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": "stable"
 };
 
--- a/addon-sdk/source/lib/sdk/simple-prefs.js
+++ b/addon-sdk/source/lib/sdk/simple-prefs.js
@@ -6,21 +6,19 @@
 module.metadata = {
   "stability": "experimental"
 };
 
 const { emit, off } = require("./event/core");
 const { when: unload } = require("./system/unload");
 const { PrefsTarget } = require("./preferences/event-target");
 const { id } = require("./self");
-const { preferencesBranch } = require('@loader/options');
-
 const observers = require("./deprecated/observer-service");
 
-const ADDON_BRANCH = "extensions." + preferencesBranch + ".";
+const ADDON_BRANCH = "extensions." + id + ".";
 const BUTTON_PRESSED = id + "-cmdPressed";
 
 const target = PrefsTarget({ branchName: ADDON_BRANCH });
 
 // Listen to clicks on buttons
 function buttonClick(subject, data) {
   emit(target, data);
 }
--- a/addon-sdk/source/lib/sdk/simple-storage.js
+++ b/addon-sdk/source/lib/sdk/simple-storage.js
@@ -1,11 +1,9 @@
-/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * vim:set ts=2 sw=2 sts=2 et filetype=javascript
- * This Source Code Form is subject to the terms of the Mozilla Public
+/* 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": "stable"
 };
--- a/addon-sdk/source/lib/sdk/stylesheet/style.js
+++ b/addon-sdk/source/lib/sdk/stylesheet/style.js
@@ -1,10 +1,8 @@
-/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et: */
 /* 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"
 };
--- a/addon-sdk/source/lib/sdk/system.js
+++ b/addon-sdk/source/lib/sdk/system.js
@@ -1,9 +1,8 @@
-/* vim:set ts=2 sw=2 sts=2 expandtab */
 /* 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": "unstable"
--- a/addon-sdk/source/lib/sdk/system/environment.js
+++ b/addon-sdk/source/lib/sdk/system/environment.js
@@ -1,9 +1,8 @@
-/* vim:set ts=2 sw=2 sts=2 expandtab */
 /* 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": "stable"
--- a/addon-sdk/source/lib/sdk/system/globals.js
+++ b/addon-sdk/source/lib/sdk/system/globals.js
@@ -1,9 +1,8 @@
-/* vim:set ts=2 sw=2 sts=2 expandtab */
 /* 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": "unstable"
--- a/addon-sdk/source/lib/sdk/system/runtime.js
+++ b/addon-sdk/source/lib/sdk/system/runtime.js
@@ -1,9 +1,8 @@
-/* vim:set ts=2 sw=2 sts=2 et: */
 /* 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": "unstable"
--- a/addon-sdk/source/lib/sdk/tabs/observer.js
+++ b/addon-sdk/source/lib/sdk/tabs/observer.js
@@ -1,9 +1,8 @@
-/* vim:set ts=2 sw=2 sts=2 et: */
 /* 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": "unstable"
 };
--- a/addon-sdk/source/lib/sdk/tabs/utils.js
+++ b/addon-sdk/source/lib/sdk/tabs/utils.js
@@ -1,9 +1,8 @@
-/* vim:set ts=2 sw=2 sts=2 et: */
 /* 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': 'unstable'
 };
--- a/addon-sdk/source/lib/sdk/test.js
+++ b/addon-sdk/source/lib/sdk/test.js
@@ -1,10 +1,9 @@
-/* vim:ts=2:sts=2:sw=2:
- * This Source Code Form is subject to the terms of the Mozilla Public
+/* 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": "unstable"
 };
--- a/addon-sdk/source/lib/sdk/test/assert.js
+++ b/addon-sdk/source/lib/sdk/test/assert.js
@@ -1,10 +1,9 @@
-/* vim:ts=2:sts=2:sw=2:
- * This Source Code Form is subject to the terms of the Mozilla Public
+/* 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": "unstable"
 };
 
--- a/addon-sdk/source/lib/sdk/test/harness.js
+++ b/addon-sdk/source/lib/sdk/test/harness.js
@@ -421,17 +421,17 @@ function nextIteration(tests) {
     setTimeout(cleanup, 0);
   }
 }
 
 var POINTLESS_ERRORS = [
   'Invalid chrome URI:',
   'OpenGL LayerManager Initialized Succesfully.',
   '[JavaScript Error: "TelemetryStopwatch:',
-  '[JavaScript Warning: "ReferenceError: reference to undefined property',
+  'reference to undefined property',
   '[JavaScript Error: "The character encoding of the HTML document was ' +
     'not declared.',
   '[Javascript Warning: "Error: Failed to preserve wrapper of wrapped ' +
     'native weak map key',
   '[JavaScript Warning: "Duplicate resource declaration for',
   'file: "chrome://browser/content/',
   'file: "chrome://global/content/',
   '[JavaScript Warning: "The character encoding of a framed document was ' +
--- a/addon-sdk/source/lib/sdk/test/httpd.js
+++ b/addon-sdk/source/lib/sdk/test/httpd.js
@@ -1,10 +1,8 @@
-/* -*- Mode: JavaScript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et: */
 /* 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/. */
 
 /*
 * An implementation of an HTTP server both as a loadable script and as an XPCOM
 * component. See the accompanying README file for user documentation on
 * httpd.js.
--- a/addon-sdk/source/lib/sdk/test/utils.js
+++ b/addon-sdk/source/lib/sdk/test/utils.js
@@ -1,10 +1,9 @@
-/* vim:ts=2:sts=2:sw=2:
- * This Source Code Form is subject to the terms of the Mozilla Public
+/* 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': 'unstable'
 };
--- a/addon-sdk/source/lib/sdk/util/array.js
+++ b/addon-sdk/source/lib/sdk/util/array.js
@@ -1,9 +1,8 @@
-/* vim:set ts=2 sw=2 sts=2 et: */
 /* 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"
 };
--- a/addon-sdk/source/lib/sdk/util/collection.js
+++ b/addon-sdk/source/lib/sdk/util/collection.js
@@ -1,10 +1,8 @@
-/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et: */
 /* 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"
--- a/addon-sdk/source/lib/sdk/util/match-pattern.js
+++ b/addon-sdk/source/lib/sdk/util/match-pattern.js
@@ -1,10 +1,8 @@
-/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et: */
 /* 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": "unstable"
--- a/addon-sdk/source/lib/sdk/util/registry.js
+++ b/addon-sdk/source/lib/sdk/util/registry.js
@@ -1,10 +1,8 @@
-/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et: */
 /* 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": "unstable"
 };
--- a/addon-sdk/source/lib/sdk/util/rules.js
+++ b/addon-sdk/source/lib/sdk/util/rules.js
@@ -1,10 +1,8 @@
-/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et: */
 /* 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": "unstable"
--- a/addon-sdk/source/lib/sdk/view/core.js
+++ b/addon-sdk/source/lib/sdk/view/core.js
@@ -1,9 +1,8 @@
-/* vim:set ts=2 sw=2 sts=2 et: */
 /* 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": "unstable"
--- a/addon-sdk/source/lib/sdk/widget.js
+++ b/addon-sdk/source/lib/sdk/widget.js
@@ -1,10 +1,8 @@
-/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et: */
 /* 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";
 
 // The widget module currently supports only Firefox.
 // See: https://bugzilla.mozilla.org/show_bug.cgi?id=560716
--- a/addon-sdk/source/lib/sdk/windows/observer.js
+++ b/addon-sdk/source/lib/sdk/windows/observer.js
@@ -1,9 +1,8 @@
-/* vim:set ts=2 sw=2 sts=2 et: */
 /* 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": "unstable"
 };
--- a/addon-sdk/source/lib/test.js
+++ b/addon-sdk/source/lib/test.js
@@ -1,10 +1,9 @@
-/* vim:ts=2:sts=2:sw=2:
- * This Source Code Form is subject to the terms of the Mozilla Public
+/* 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": "unstable"
 };
--- a/addon-sdk/source/lib/toolkit/loader.js
+++ b/addon-sdk/source/lib/toolkit/loader.js
@@ -1,13 +1,12 @@
-/* vim:set ts=2 sw=2 sts=2 expandtab */
 /* 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/.
- */
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
 ;(function(id, factory) { // Module boilerplate :(
   if (typeof(define) === 'function') { // RequireJS
     define(factory);
   } else if (typeof(require) === 'function') { // CommonJS
     factory.call(this, require, exports, module);
   } else if (~String(this).indexOf('BackstagePass')) { // JSM
     this[factory.name] = {};
     factory(function require(uri) {
--- a/addon-sdk/source/python-lib/cuddlefish/options_defaults.py
+++ b/addon-sdk/source/python-lib/cuddlefish/options_defaults.py
@@ -1,13 +1,13 @@
 # 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/.
 
-def parse_options_defaults(options, preferencesBranch):
+def parse_options_defaults(options, jetpack_id):
     # this returns a unicode string
     pref_list = []
 
     for pref in options:
         if ('value' in pref):
             value = pref["value"]
 
             if isinstance(value, float):
@@ -16,11 +16,11 @@ def parse_options_defaults(options, pref
                 value = str(pref["value"]).lower()
             elif isinstance(value, str): # presumably ASCII
                 value = "\"" + unicode(pref["value"]) + "\""
             elif isinstance(value, unicode):
                 value = "\"" + pref["value"] + "\""
             else:
                 value = str(pref["value"])
 
-            pref_list.append("pref(\"extensions." + preferencesBranch + "." + pref["name"] + "\", " + value + ");")
+            pref_list.append("pref(\"extensions." + jetpack_id + "." + pref["name"] + "\", " + value + ");")
 
     return "\n".join(pref_list) + "\n"
--- a/addon-sdk/source/python-lib/cuddlefish/options_xul.py
+++ b/addon-sdk/source/python-lib/cuddlefish/options_xul.py
@@ -40,30 +40,30 @@ def validate_prefs(options):
             for item in pref["options"]:
                 if ("value" not in item):
                     raise MissingPrefAttr("'options' requires a 'value'")
                 if ("label" not in item):
                     raise MissingPrefAttr("'options' requires a 'label'")
 
         # TODO: Check that pref["type"] matches default value type
 
-def parse_options(options, jetpack_id, preferencesBranch):
+def parse_options(options, jetpack_id):
     doc = Document()
     root = doc.createElement("vbox")
     root.setAttribute("xmlns", "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul")
     doc.appendChild(root)
 
     for pref in options:
         if ("hidden" in pref and pref["hidden"] == True):
             continue;
 
         setting = doc.createElement("setting")
         setting.setAttribute("pref-name", pref["name"])
         setting.setAttribute("data-jetpack-id", jetpack_id)
-        setting.setAttribute("pref", "extensions." + preferencesBranch + "." + pref["name"])
+        setting.setAttribute("pref", "extensions." + jetpack_id + "." + pref["name"])
         setting.setAttribute("type", pref["type"])
         setting.setAttribute("title", pref["title"])
 
         if ("description" in pref):
             setting.appendChild(doc.createTextNode(pref["description"]))
 
         if (pref["type"] == "control"):
             button = doc.createElement("button")
--- a/addon-sdk/source/python-lib/cuddlefish/packaging.py
+++ b/addon-sdk/source/python-lib/cuddlefish/packaging.py
@@ -391,31 +391,16 @@ def generate_build_for_target(pkg_cfg, t
 
     if 'icon64' in target_cfg:
         build['icon64'] = os.path.join(target_cfg.root_dir, target_cfg.icon64)
         del target_cfg['icon64']
 
     if ('preferences' in target_cfg):
         build['preferences'] = target_cfg.preferences
 
-    if 'id' in target_cfg:
-        # NOTE: logic duplicated from buildJID()
-        jid = target_cfg['id']
-        if not ('@' in jid or jid.startswith('{')):
-            jid += '@jetpack'
-        build['preferencesBranch'] = jid
-
-    if 'preferences-branch' in target_cfg:
-        # check it's a non-empty, valid branch name
-        preferencesBranch = target_cfg['preferences-branch']
-        if re.match('^[\w{@}-]+$', preferencesBranch):
-            build['preferencesBranch'] = preferencesBranch
-        elif not is_running_tests:
-            print >>sys.stderr, "IGNORING preferences-branch (not a valid branch name)"
-
     return build
 
 def _get_files_in_dir(path):
     data = {}
     files = os.listdir(path)
     for filename in files:
         fullpath = os.path.join(path, filename)
         if os.path.isdir(fullpath):
deleted file mode 100644
--- a/addon-sdk/source/python-lib/cuddlefish/tests/preferences-files/packages/curly-id/lib/main.js
+++ /dev/null
@@ -1,4 +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/. */
-
deleted file mode 100644
--- a/addon-sdk/source/python-lib/cuddlefish/tests/preferences-files/packages/curly-id/package.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-    "id": "{34a1eae1-c20a-464f-9b0e-000000000000}",
-    "fullName": "curly ID test",
-    "author": "Tomislav Jovanovic",
-
-    "preferences": [{
-        "name": "test13",
-        "type": "integer",
-        "title": "test13",
-        "value": 26
-    }],
-
-    "preferences-branch": "invalid^branch*name"
-}
deleted file mode 100644
--- a/addon-sdk/source/python-lib/cuddlefish/tests/preferences-files/packages/preferences-branch/lib/main.js
+++ /dev/null
@@ -1,4 +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/. */
-
deleted file mode 100644
--- a/addon-sdk/source/python-lib/cuddlefish/tests/preferences-files/packages/preferences-branch/package.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-    "id": "test-preferences-branch",
-    "fullName": "preferences-branch test",
-    "author": "Tomislav Jovanovic",
-
-    "preferences": [{
-        "name": "test42",
-        "type": "bool",
-        "title": "test42",
-        "value": true
-    }],
-
-    "preferences-branch": "human-readable"
-}
--- a/addon-sdk/source/python-lib/cuddlefish/tests/test_packaging.py
+++ b/addon-sdk/source/python-lib/cuddlefish/tests/test_packaging.py
@@ -17,18 +17,17 @@ def get_configs(pkg_name, dirname='stati
     if not (os.path.exists(pkg_path) and os.path.isdir(pkg_path)):
         raise Exception('path does not exist: %s' % pkg_path)
     target_cfg = packaging.get_config_in_dir(pkg_path)
     pkg_cfg = packaging.build_config(root_path, target_cfg)
     deps = packaging.get_deps_for_targets(pkg_cfg, [pkg_name])
     build = packaging.generate_build_for_target(
         pkg_cfg=pkg_cfg,
         target=pkg_name,
-        deps=deps,
-        is_running_tests=True,
+        deps=deps
         )
     return Bunch(target_cfg=target_cfg, pkg_cfg=pkg_cfg, build=build)
 
 class PackagingTests(unittest.TestCase):
     def test_bug_588661(self):
         configs = get_configs('foo', 'bug-588661-files')
         self.assertEqual(configs.build.loader,
                          'foo/lib/foo-loader.js')
--- a/addon-sdk/source/python-lib/cuddlefish/tests/test_xpi.py
+++ b/addon-sdk/source/python-lib/cuddlefish/tests/test_xpi.py
@@ -35,30 +35,30 @@ class PrefsTests(unittest.TestCase):
     def tearDown(self):
         if self.xpi:
             self.xpi.close()
         if self.xpiname and os.path.exists(self.xpiname):
             os.remove(self.xpiname)
 
     def testPackageWithSimplePrefs(self):
         self.makexpi('simple-prefs')
-        packageName = 'jid1-fZHqN9JfrDBa8A@jetpack'
         self.failUnless('options.xul' in self.xpi.namelist())
         optsxul = self.xpi.read('options.xul').decode("utf-8")
-        self.failUnlessEqual(self.xpi_harness_options["jetpackID"], packageName)
-        self.failUnlessEqual(self.xpi_harness_options["preferencesBranch"], packageName)
+        self.failUnlessEqual(self.xpi_harness_options["jetpackID"],
+                             "jid1-fZHqN9JfrDBa8A@jetpack")
 
         root = ElementTree.XML(optsxul.encode('utf-8'))
 
         xulNamespacePrefix = \
             "{http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul}"
         
         settings = root.findall(xulNamespacePrefix + 'setting')
 
         def assertPref(setting, name, prefType, title):
+            packageName = 'jid1-fZHqN9JfrDBa8A@jetpack'
             self.failUnlessEqual(setting.get('data-jetpack-id'), packageName)
             self.failUnlessEqual(setting.get('pref'),
                                  'extensions.' + packageName + '.' + name)
             self.failUnlessEqual(setting.get('pref-name'), name)
             self.failUnlessEqual(setting.get('type'), prefType)
             self.failUnlessEqual(setting.get('title'), title)
 
         assertPref(settings[0], 'test', 'bool', u't\u00EBst')
@@ -83,70 +83,24 @@ class PrefsTests(unittest.TestCase):
         prefsjs = self.xpi.read('defaults/preferences/prefs.js').decode("utf-8")
         exp = [u'pref("extensions.jid1-fZHqN9JfrDBa8A@jetpack.test", false);',
                u'pref("extensions.jid1-fZHqN9JfrDBa8A@jetpack.test2", "\u00FCnic\u00F8d\u00E9");',
                u'pref("extensions.jid1-fZHqN9JfrDBa8A@jetpack.test3", "1");',
                u'pref("extensions.jid1-fZHqN9JfrDBa8A@jetpack.test4", "red");',
                ]
         self.failUnlessEqual(prefsjs, "\n".join(exp)+"\n")
 
-    def testPackageWithPreferencesBranch(self):
-        self.makexpi('preferences-branch')
-        self.failUnless('options.xul' in self.xpi.namelist())
-        optsxul = self.xpi.read('options.xul').decode("utf-8")
-        self.failUnlessEqual(self.xpi_harness_options["preferencesBranch"], 
-                             "human-readable")
-
-        root = ElementTree.XML(optsxul.encode('utf-8'))
-        xulNamespacePrefix = \
-            "{http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul}"
-        
-        setting = root.find(xulNamespacePrefix + 'setting')
-        self.failUnlessEqual(setting.get('pref'),
-                             'extensions.human-readable.test42')
-
-        prefsjs = self.xpi.read('defaults/preferences/prefs.js').decode("utf-8")
-        self.failUnlessEqual(prefsjs, 
-                            'pref("extensions.human-readable.test42", true);\n')
-
     def testPackageWithNoPrefs(self):
         self.makexpi('no-prefs')
         self.failIf('options.xul' in self.xpi.namelist())
         self.failUnlessEqual(self.xpi_harness_options["jetpackID"],
                              "jid1-fZHqN9JfrDBa8A@jetpack")
         prefsjs = self.xpi.read('defaults/preferences/prefs.js').decode("utf-8")
         self.failUnlessEqual(prefsjs, "")
 
-    def testPackageWithInvalidPreferencesBranch(self):
-        self.makexpi('curly-id')
-        self.failIfEqual(self.xpi_harness_options["preferencesBranch"], 
-                         "invalid^branch*name")
-        self.failUnlessEqual(self.xpi_harness_options["preferencesBranch"], 
-                             "{34a1eae1-c20a-464f-9b0e-000000000000}")
-
-    def testPackageWithCurlyID(self):
-        self.makexpi('curly-id')
-        self.failUnlessEqual(self.xpi_harness_options["jetpackID"], 
-                             "{34a1eae1-c20a-464f-9b0e-000000000000}")
-
-        self.failUnless('options.xul' in self.xpi.namelist())
-        optsxul = self.xpi.read('options.xul').decode("utf-8")
-
-        root = ElementTree.XML(optsxul.encode('utf-8'))
-        xulNamespacePrefix = \
-            "{http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul}"
-        
-        setting = root.find(xulNamespacePrefix + 'setting')
-        self.failUnlessEqual(setting.get('pref'),
-                             'extensions.{34a1eae1-c20a-464f-9b0e-000000000000}.test13')
-
-        prefsjs = self.xpi.read('defaults/preferences/prefs.js').decode("utf-8")
-        self.failUnlessEqual(prefsjs, 
-                            'pref("extensions.{34a1eae1-c20a-464f-9b0e-000000000000}.test13", 26);\n')
-
 
 class Bug588119Tests(unittest.TestCase):
     def makexpi(self, pkg_name):
         self.xpiname = "%s.xpi" % pkg_name
         create_xpi(self.xpiname, pkg_name, 'bug-588119-files')
         self.xpi = zipfile.ZipFile(self.xpiname, 'r')
         options = self.xpi.read('harness-options.json')
         self.xpi_harness_options = json.loads(options)
--- a/addon-sdk/source/python-lib/cuddlefish/xpi.py
+++ b/addon-sdk/source/python-lib/cuddlefish/xpi.py
@@ -72,25 +72,24 @@ def build_xpi(template_root_dir, manifes
 
     # Handle simple-prefs
     if 'preferences' in harness_options:
         from options_xul import parse_options, validate_prefs
 
         validate_prefs(harness_options["preferences"])
 
         opts_xul = parse_options(harness_options["preferences"],
-                                 harness_options["jetpackID"],
-                                 harness_options["preferencesBranch"])
+                                 harness_options["jetpackID"])
         open('.options.xul', 'wb').write(opts_xul.encode("utf-8"))
         zf.write('.options.xul', 'options.xul')
         os.remove('.options.xul')
 
         from options_defaults import parse_options_defaults
         prefs_js = parse_options_defaults(harness_options["preferences"],
-                                          harness_options["preferencesBranch"])
+                                          harness_options["jetpackID"])
         open('.prefs.js', 'wb').write(prefs_js.encode("utf-8"))
 
     else:
         open('.prefs.js', 'wb').write("")
 
     zf.write('.prefs.js', 'defaults/preferences/prefs.js')
     os.remove('.prefs.js')
 
deleted file mode 100644
--- a/addon-sdk/source/test/addons/curly-id/lib/main.js
+++ /dev/null
@@ -1,45 +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 { id } = require('sdk/self');
-const simple = require('sdk/simple-prefs');
-const service = require('sdk/preferences/service');
-const { preferencesBranch } = require('@loader/options');
-const { AddonManager } = require('chrome').Cu.import('resource://gre/modules/AddonManager.jsm');
-
-exports.testCurlyID = function(assert) {
-  assert.equal(id, '{34a1eae1-c20a-464f-9b0e-000000000000}', 'curly ID is curly');
-
-  assert.equal(simple.prefs.test13, 26, 'test13 is 26');
-
-  simple.prefs.test14 = '15';
-  assert.equal(service.get('extensions.{34a1eae1-c20a-464f-9b0e-000000000000}.test14'), '15', 'test14 is 15');
-
-  assert.equal(service.get('extensions.{34a1eae1-c20a-464f-9b0e-000000000000}.test14'), simple.prefs.test14, 'simple test14 also 15');
-  
-}
-
-exports.testInvalidPreferencesBranch = function(assert) {
-  assert.notEqual(preferencesBranch, 'invalid^branch*name', 'invalid preferences-branch value ignored');
-
-  assert.equal(preferencesBranch, '{34a1eae1-c20a-464f-9b0e-000000000000}', 'preferences-branch is {34a1eae1-c20a-464f-9b0e-000000000000}');
-
-}
-
-// from `/test/test-self.js`, adapted to `sdk/test/assert` API
-exports.testSelfID = function(assert, done) {
-
-  assert.equal(typeof(id), 'string', 'self.id is a string');
-  assert.ok(id.length > 0, 'self.id not empty');
-
-  AddonManager.getAddonByID(id, function(addon) {
-    assert.ok(addon, 'found addon with self.id');
-    done();
-  });
-
-}
-
-require('sdk/test/runner').runTestsFromModule(module);
deleted file mode 100644
--- a/addon-sdk/source/test/addons/curly-id/package.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-    "id": "{34a1eae1-c20a-464f-9b0e-000000000000}",
-    "fullName": "curly ID test",
-    "author": "Tomislav Jovanovic",
-
-    "preferences": [{
-        "name": "test13",
-        "type": "integer",
-        "title": "test13",
-        "value": 26
-    }],
-
-    "preferences-branch": "invalid^branch*name"
-}
deleted file mode 100644
--- a/addon-sdk/source/test/addons/predefined-id-with-at/lib/main.js
+++ /dev/null
@@ -1,35 +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 { id } = require('sdk/self');
-const simple = require('sdk/simple-prefs');
-const service = require('sdk/preferences/service');
-const { preferencesBranch } = require('@loader/options');
-const { AddonManager } = require('chrome').Cu.import('resource://gre/modules/AddonManager.jsm', {});
-
-const expected_id = 'predefined-id@test';
-
-exports.testExpectedID = function(assert) {
-  assert.equal(id, expected_id, 'ID is as expected');
-  assert.equal(preferencesBranch, expected_id, 'preferences-branch is ' + expected_id);
-
-  assert.equal(simple.prefs.test, 5, 'test pref is 5');
-
-  simple.prefs.test2 = '25';
-  assert.equal(service.get('extensions.'+expected_id+'.test2'), '25', 'test pref is 25');
-  assert.equal(service.get('extensions.'+expected_id+'.test2'), simple.prefs.test2, 'test pref is 25');
-}
-
-exports.testSelfID = function(assert, done) {
-  assert.equal(typeof(id), 'string', 'self.id is a string');
-  assert.ok(id.length > 0, 'self.id not empty');
-
-  AddonManager.getAddonByID(id, function(addon) {
-    assert.equal(addon.id, id, 'found addon with self.id');
-    done();
-  });
-}
-
-require('sdk/test/runner').runTestsFromModule(module);
deleted file mode 100644
--- a/addon-sdk/source/test/addons/predefined-id-with-at/package.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-    "id": "predefined-id@test",
-    "fullName": "predefined ID test",
-    "author": "Erik Vold",
-    "preferences": [{
-        "name": "test",
-        "type": "integer",
-        "title": "test",
-        "value": 5
-    }]
-}
deleted file mode 100644
--- a/addon-sdk/source/test/addons/preferences-branch/lib/main.js
+++ /dev/null
@@ -1,36 +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 { id } = require('sdk/self');
-const simple = require('sdk/simple-prefs');
-const service = require('sdk/preferences/service');
-const { preferencesBranch } = require('@loader/options');
-const { AddonManager } = require('chrome').Cu.import('resource://gre/modules/AddonManager.jsm');
-
-exports.testPreferencesBranch = function(assert) {
-  assert.equal(preferencesBranch, 'human-readable', 'preferencesBranch is human-readable');
-
-  assert.equal(simple.prefs.test42, true, 'test42 is true');
-
-  simple.prefs.test43 = 'movie';
-  assert.equal(service.get('extensions.human-readable.test43'), 'movie', 'test43 is a movie');
-  
-}
-
-// from `/test/test-self.js`, adapted to `sdk/test/assert` API
-exports.testSelfID = function(assert, done) {
-
-  assert.equal(typeof(id), 'string', 'self.id is a string');
-  assert.ok(id.length > 0, 'self.id not empty');
-
-  AddonManager.getAddonByID(id, function(addon) {
-    assert.ok(addon, 'found addon with self.id');
-    done();
-  });
-
-}
-
-require('sdk/test/runner').runTestsFromModule(module);
deleted file mode 100644
--- a/addon-sdk/source/test/addons/preferences-branch/package.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-    "id": "test-preferences-branch",
-    "fullName": "preferences-branch test",
-    "author": "Tomislav Jovanovic",
-
-    "preferences": [{
-        "name": "test42",
-        "type": "bool",
-        "title": "test42",
-        "value": true
-    }],
-
-    "preferences-branch": "human-readable"
-}
--- a/addon-sdk/source/test/addons/simple-prefs/lib/main.js
+++ b/addon-sdk/source/test/addons/simple-prefs/lib/main.js
@@ -3,17 +3,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 'use strict';
 
 const { Cu } = require('chrome');
 const sp = require('sdk/simple-prefs');
 const app = require('sdk/system/xul-app');
 const self = require('sdk/self');
 const tabs = require('sdk/tabs');
-const { preferencesBranch } = require('@loader/options');
 
 const { AddonManager } = Cu.import('resource://gre/modules/AddonManager.jsm', {});
 
 exports.testDefaultValues = function (assert) {
   assert.equal(sp.prefs.myHiddenInt, 5, 'myHiddenInt default is 5');
   assert.equal(sp.prefs.myInteger, 8, 'myInteger default is 8');
   assert.equal(sp.prefs.somePreference, 'TEST', 'somePreference default is correct');
 }
@@ -85,13 +84,9 @@ if (app.is('Firefox')) {
               tab.close(done);
             }
           });
       	}
       });
   }
 }
 
-exports.testDefaultPreferencesBranch = function(assert) {
-  assert.equal(preferencesBranch, self.id, 'preferencesBranch default the same as self.id');
-}
-
 require('sdk/test/runner').runTestsFromModule(module);
deleted file mode 100644
--- a/addon-sdk/source/test/addons/standard-id/lib/main.js
+++ /dev/null
@@ -1,45 +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 { id } = require('sdk/self');
-const simple = require('sdk/simple-prefs');
-const service = require('sdk/preferences/service');
-const { preferencesBranch } = require('@loader/options');
-const { AddonManager } = require('chrome').Cu.import('resource://gre/modules/AddonManager.jsm');
-
-exports.testStandardID = function(assert) {
-  assert.equal(id, 'standard-id@jetpack', 'standard ID is standard');
-
-  assert.equal(simple.prefs.test13, 26, 'test13 is 26');
-
-  simple.prefs.test14 = '15';
-  assert.equal(service.get('extensions.standard-id@jetpack.test14'), '15', 'test14 is 15');
-
-  assert.equal(service.get('extensions.standard-id@jetpack.test14'), simple.prefs.test14, 'simple test14 also 15');
-  
-}
-
-exports.testInvalidPreferencesBranch = function(assert) {
-  assert.notEqual(preferencesBranch, 'invalid^branch*name', 'invalid preferences-branch value ignored');
-
-  assert.equal(preferencesBranch, 'standard-id@jetpack', 'preferences-branch is standard-id@jetpack');
-
-}
-
-// from `/test/test-self.js`, adapted to `sdk/test/assert` API
-exports.testSelfID = function(assert, done) {
-
-  assert.equal(typeof(id), 'string', 'self.id is a string');
-  assert.ok(id.length > 0, 'self.id not empty');
-
-  AddonManager.getAddonByID(id, function(addon) {
-    assert.ok(addon, 'found addon with self.id');
-    done();
-  });
-
-}
-
-require('sdk/test/runner').runTestsFromModule(module);
deleted file mode 100644
--- a/addon-sdk/source/test/addons/standard-id/package.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-    "id": "standard-id",
-    "fullName": "standard ID test",
-    "author": "Tomislav Jovanovic",
-
-    "preferences": [{
-        "name": "test13",
-        "type": "integer",
-        "title": "test13",
-        "value": 26
-    }],
-
-    "preferences-branch": "invalid^branch*name"
-}
index c7cfccd7abd114c59a8b12f1fc9e8caf5803b081..48945af0577a41d56f7b789fb64331bcacf8b388
GIT binary patch
literal 1692
zc${^VdpOg39LImmWsEHMOLTCK!pvwz=r;Flm6j<)rjlEmC?zVl(L$CI&84ssB9B`r
zj!VSJu*PVktVm->Xs*Yhp65Judd~Oze4p><`^V?~{p0g~y`3;1F+~6XzyO^nVU1j=
zeVGRWfOIeb$N-xFZxZQT$hlxb5Zpfm?<@s?klrG=^>h9r1P~FM69Is4-NpFEV2%T{
zZbr$&A_{`3I_TcXX3GoAox#<K<?zk+xEQ_dG-hWa)zM(Jeo!|`q`Wd5p|Y2?vN)H~
zS3E4!IS<-Qn$p{rexUVfd5cj*X6MV}oLgF%jmLenkc;Z|Zx8ZJ>#>ucmJI409!?}<
zq~BM+6xH6w$@{vYLq0-V6ZQZFRc`F%U+A#9Xe|jo&X6m(9l2zIH;pOiaIzd=dz%-E
zUEYvX+wA*UWXHoeT;N^}x$P}~vK!K54CQbN@ntyNCz+g^^<J<zzLWRGNd$IGoD4xf
z+F``=Py}~{k%hhIQ;_==(u{ch{dg4WcMle%d{$PNh8Zo!1|;w7v9J#Z>|sKXOlmJQ
zNg3g4LzUh#NM&a0CEpu;r^WM|{fwaP+TFaYkm%F(`IPB1HBp>dY=UvBt-ZWEbwK~4
z2@EP6H(|f7P&`Jydn~0D{Rwa60yZqSD|+A=m0A=H$9&p!8}>jY`^AMP%W9{KxNnWR
z^bh*ycm;mx<|<sD+xNl-W2`?U(f0^z>zh?i!ddDDnGR@h<=bG=Z2iGl`cvP>^XVl<
zNML0eD1pPTa6RWT=2h*`jn>==kVQoRuK0tcURPX#N!64@-}1GlX|i*UfzZy)lsko=
zzag%<Tw_3XHdzoPZ}6sD>7&OE)iDr3=DVZ>&lKO(F2Qq#Q$T8k&rV*6Fk6zi@Q8^s
z%pVmi=@-9VQ8xIv+rchId)J_U<8Oz{AhydY%CZ9+IThp+Fxu^hR9BI^LB)Lr0qoc0
zr7?J#aNlTcsmA;r8(U?D4Vr<Ou)3bfL`Cy<V%if;uy+U*8hq)uP?SC^R>X|g&{V4%
zcK;#MzuA|RYDJx<_9FVTRN6|YyrcH3AXWT|Fl7sxe7?0BH`-G`VyPMAw?m-AA2umn
z%C6DnM*~qQEochN*FBD*graO6RLDDv^{Tx`oSt&y^c~viADSOXM7@d<C)S<C#?ZAJ
zur4W?8qXK^yy%afn1#}+3R*#a2M&$<<X;92b`lTNT^)k)=X87;Uu~OV#(R>mOIe@&
zs@`)jBi@|o3iHK{XKZt6+Ghl*Ep%j9arYk(T4+jl%R3LpDXJ&Qbv#ihLy#UHFT4ja
zj_aujvXdJ+E$5YX0;e*g1T&jKK9(I{tab4R-)sxX>w42OoI67c?X0mM)wLLltl>PV
zyW|nL>fwJ*E;*n&_iK&c?2_V}T+Jgn_l1H}l6~w2NvD57zq<xHJTJnsa*f`dVgLYH
z1Kp1pa*hxf2oFB#^Nn+zHO6&*<(%Q%cq0R`&N)21XxK}JD`wMQLi>0J^Wf|5hSC5j
zZd<w@w{X?dTU3fs89yR6^H!rPT>aLrtdrJPa`~|??t6}ztm+G&3%JCC5;o`ES9>`-
zWTQV!S6kQ_p7D%EIHp8SM~3(p(n3jpHBkyD8-;BPXPXp9P=<5HOyrPyQ0seO#C2t8
zG-fN8a73NSAxMjoCnwlbNoeC-=SQ6=mXj2^r{8sp`pK<_4~;nC-Ix@XA)!Fi;a<Kj
z#;Dwj-dUC~5)mXQ<%jF_KUEHyL<lWFWOh}#1ZRMzrO9*qV#kq0w|lBSo})NjVkve&
zIHOf(Y@|YWlhhiVGgekwgPV0+p7^z^b<ZC@#-e*UG=p^<5U%za^Ic8v>32{o>6sQy
z`w5P*Eaz2o`6pz*)*)h>Np|?*KE(~SP}#qep+N2zlVR=R`$I^dHohJ#s$}E*i6mRK
zI6+oishOMW7d(DAQaRL%u<@EGWnm_a(te849(L8^cs!$H?4w|+IondJpi(=*axcAP
z(Pw}$MjXgFsxDTv|B5OJypX%UC@F(W(OVYqHotU5ENcW$89QM_M72Qw7xJpW`dS+>
yK#y9levW+c?~(r>8~mvFtDAmQ<gfknlefgbd+QfLaf$UN(Y2koc7G6GfBge0?DkLq
--- a/addon-sdk/source/test/fixtures/chrome-worker/addEventListener.js
+++ b/addon-sdk/source/test/fixtures/chrome-worker/addEventListener.js
@@ -1,8 +1,6 @@
 /* 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/.
- */
-
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 'use strict';
 
 postMessage('Hello');
--- a/addon-sdk/source/test/fixtures/chrome-worker/jsctypes.js
+++ b/addon-sdk/source/test/fixtures/chrome-worker/jsctypes.js
@@ -1,8 +1,6 @@
 /* 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/.
- */
-
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 'use strict';
 
 postMessage(typeof ctypes.open);
--- a/addon-sdk/source/test/fixtures/chrome-worker/onerror.js
+++ b/addon-sdk/source/test/fixtures/chrome-worker/onerror.js
@@ -1,8 +1,6 @@
 /* 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/.
- */
-
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 'use strict';
 
 throw new Error('ok');
--- a/addon-sdk/source/test/fixtures/chrome-worker/onmessage.js
+++ b/addon-sdk/source/test/fixtures/chrome-worker/onmessage.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/.
- */
-
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 'use strict';
 
 onmessage = function (event) {
   postMessage(event.data);
 };
--- a/addon-sdk/source/test/fixtures/chrome-worker/setTimeout.js
+++ b/addon-sdk/source/test/fixtures/chrome-worker/setTimeout.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/.
- */
-
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 'use strict';
 
 setTimeout(function () {
   postMessage('ok');
 }, 0);
--- a/addon-sdk/source/test/fixtures/chrome-worker/xhr.js
+++ b/addon-sdk/source/test/fixtures/chrome-worker/xhr.js
@@ -1,13 +1,11 @@
 /* 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/.
- */
-
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 'use strict';
 
 let xhr = XMLHttpRequest();
 xhr.open("GET", "data:text/plain,ok", true);
 xhr.onload = function () {
   postMessage(xhr.responseText);
 };
 xhr.send(null);
--- a/addon-sdk/source/test/tabs/test-firefox-tabs.js
+++ b/addon-sdk/source/test/tabs/test-firefox-tabs.js
@@ -8,20 +8,17 @@ const { Loader } = require('sdk/test/loa
 const timer = require('sdk/timers');
 const { getOwnerWindow } = require('sdk/private-browsing/window/utils');
 const { windows, onFocus, getMostRecentBrowserWindow } = require('sdk/window/utils');
 const { open, focus, close } = require('sdk/window/helpers');
 const { StringBundle } = require('sdk/deprecated/app-strings');
 const tabs = require('sdk/tabs');
 const { browserWindows } = require('sdk/windows');
 
-const base64png = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYA" +
-                  "AABzenr0AAAASUlEQVRYhe3O0QkAIAwD0eyqe3Q993AQ3cBSUKpygfsNTy" +
-                  "N5ugbQpK0BAADgP0BRDWXWlwEAAAAAgPsA3rzDaAAAAHgPcGrpgAnzQ2FG" +
-                  "bWRR9AAAAABJRU5ErkJggg%3D%3D";
+const base64png = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAA";
 
 // Bug 682681 - tab.title should never be empty
 exports.testBug682681_aboutURI = function(assert, done) {
   let tabStrings = StringBundle('chrome://browser/locale/tabbrowser.properties');
 
   tabs.on('ready', function onReady(tab) {
     tabs.removeListener('ready', onReady);
 
@@ -859,60 +856,55 @@ exports['test unique tab ids'] = functio
     results[0].win.close();
     results[1].win.close();
     done();
   });
 }
 
 // related to Bug 671305
 exports.testOnLoadEventWithDOM = function(assert, done) {
-  openBrowserWindow(function(window, browser) {
-    let count = 0;
-    tabs.on('load', function onLoad(tab) {
-      assert.equal(tab.title, 'tab', 'tab passed in as arg, load called');
-      if (!count++) {
-        tab.reload();
+  let count = 0;
+  let title = 'testOnLoadEventWithDOM';
+
+  // open a about: url
+  tabs.open({
+    url: 'data:text/html;charset=utf-8,<title>' + title + '</title>',
+    inBackground: true,
+    onLoad: function(tab) {
+      assert.equal(tab.title, title, 'tab passed in as arg, load called');
+
+      if (++count > 1) {
+        assert.pass('onLoad event called on reload');
+        tab.close(done);
       }
       else {
-        // end of test
-        tabs.removeListener('load', onLoad);
-        assert.pass('onLoad event called on reload');
-        close(window).then(done);
+        assert.pass('first onLoad event occured');
+        tab.reload();
       }
-    });
-
-    // open a about: url
-    tabs.open({
-      url: 'data:text/html;charset=utf-8,<title>tab</title>',
-      inBackground: true
-    });
+    }
   });
 };
 
 // related to Bug 671305
 exports.testOnLoadEventWithImage = function(assert, done) {
-  openBrowserWindow(function(window, browser) {
-    let count = 0;
-    tabs.on('load', function onLoad(tab) {
-      if (!count++) {
-        tab.reload();
+  let count = 0;
+
+  tabs.open({
+    url: base64png,
+    inBackground: true,
+    onLoad: function(tab) {
+      if (++count > 1) {
+        assert.pass('onLoad event called on reload with image');
+        tab.close(done);
       }
       else {
-        // end of test
-        tabs.removeListener('load', onLoad);
-        assert.pass('onLoad event called on reload with image');
-        close(window).then(done);
+        assert.pass('first onLoad event occured');
+        tab.reload();
       }
-    });
-
-    // open a image url
-    tabs.open({
-      url: base64png,
-      inBackground: true
-    });
+    }
   });
 };
 
 exports.testFaviconGetterDeprecation = function (assert, done) {
   const { LoaderWithHookedConsole } = require("sdk/test/loader");
   let { loader, messages } = LoaderWithHookedConsole(module);
   let tabs = loader.require('sdk/tabs');
 
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/test/test-addon-window.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';
+
+let { Loader } = require('sdk/test/loader');
+
+exports.testReady = function(assert, done) {
+  let loader = Loader(module);
+  let { ready, window } = loader.require('sdk/addon/window');
+  let windowIsReady = false;
+
+  ready.then(function() {
+    assert.equal(windowIsReady, false, 'ready promise was resolved only once');
+    windowIsReady = true;
+
+    loader.unload();
+    done();
+  }).then(null, assert.fail);
+}
+
+require('sdk/test').run(exports);
--- a/addon-sdk/source/test/test-api-utils.js
+++ b/addon-sdk/source/test/test-api-utils.js
@@ -1,10 +1,8 @@
-/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et: */
 /* 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 apiUtils = require("sdk/deprecated/api-utils");
 
 exports.testPublicConstructor = function (assert) {
   function PrivateCtor() {}
--- a/addon-sdk/source/test/test-byte-streams.js
+++ b/addon-sdk/source/test/test-byte-streams.js
@@ -1,11 +1,9 @@
-/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * vim:set ts=2 sw=2 sts=2 et filetype=javascript
- * This Source Code Form is subject to the terms of the Mozilla Public
+/* 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 byteStreams = require("sdk/io/byte-streams");
 const file = require("sdk/io/file");
 const { pathFor } = require("sdk/system");
 const { Loader } = require("sdk/test/loader");
 
--- a/addon-sdk/source/test/test-chrome.js
+++ b/addon-sdk/source/test/test-chrome.js
@@ -1,13 +1,11 @@
 /* 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/.
- */
-
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 'use strict';
 
 let chrome = require('chrome');
 
 const FIXTURES_URL = module.uri.substr(0, module.uri.lastIndexOf('/') + 1) +
                      'fixtures/chrome-worker/'
 
 exports['test addEventListener'] = function(assert, done) {
@@ -67,20 +65,20 @@ exports['test XMLHttpRequest'] = functio
 };
 
 exports['test onerror'] = function(assert, done) {
   let uri = FIXTURES_URL + 'onerror.js';
 
   let worker = new chrome.ChromeWorker(uri);
   worker.onerror = function(event) {
     assert.equal(event.filename, uri, 'event reports the correct uri');
-    assert.equal(event.lineno, 8, 'event reports the correct line number');
+    assert.equal(event.lineno, 6, 'event reports the correct line number');
     assert.equal(event.target, worker, 'event reports the correct worker');
     assert.ok(event.message.match(/ok/),
                 'event contains the exception message');
     // Call preventDefault in order to avoid being displayed in JS console.
     event.preventDefault();
     worker.terminate();
     done();
   };
 };
 
-require('test').run(exports);
+require('sdk/test').run(exports);
--- a/addon-sdk/source/test/test-clipboard.js
+++ b/addon-sdk/source/test/test-clipboard.js
@@ -1,12 +1,11 @@
 /* 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";
 
 require("sdk/clipboard");
 
 const { Cc, Ci } = require("chrome");
 
 const imageTools = Cc["@mozilla.org/image/tools;1"].
                     getService(Ci.imgITools);
--- a/addon-sdk/source/test/test-collection.js
+++ b/addon-sdk/source/test/test-collection.js
@@ -1,127 +1,128 @@
-/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et: */
 /* 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 collection = require("sdk/util/collection");
 
-exports.testAddRemove = function (test) {
+exports.testAddRemove = function (assert) {
   let coll = new collection.Collection();
-  compare(test, coll, []);
-  addRemove(test, coll, [], false);
+  compare(assert, coll, []);
+  addRemove(assert, coll, [], false);
 };
 
-exports.testAddRemoveBackingArray = function (test) {
+exports.testAddRemoveBackingArray = function (assert) {
   let items = ["foo"];
   let coll = new collection.Collection(items);
-  compare(test, coll, items);
-  addRemove(test, coll, items, true);
+  compare(assert, coll, items);
+  addRemove(assert, coll, items, true);
 
   items = ["foo", "bar"];
   coll = new collection.Collection(items);
-  compare(test, coll, items);
-  addRemove(test, coll, items, true);
+  compare(assert, coll, items);
+  addRemove(assert, coll, items, true);
 };
 
-exports.testProperty = function (test) {
+exports.testProperty = function (assert) {
   let obj = makeObjWithCollProp();
-  compare(test, obj.coll, []);
-  addRemove(test, obj.coll, [], false);
+  compare(assert, obj.coll, []);
+  addRemove(assert, obj.coll, [], false);
 
   // Test single-value set.
   let items = ["foo"];
   obj.coll = items[0];
-  compare(test, obj.coll, items);
-  addRemove(test, obj.coll, items, false);
+  compare(assert, obj.coll, items);
+  addRemove(assert, obj.coll, items, false);
 
   // Test array set.
   items = ["foo", "bar"];
   obj.coll = items;
-  compare(test, obj.coll, items);
-  addRemove(test, obj.coll, items, false);
+  compare(assert, obj.coll, items);
+  addRemove(assert, obj.coll, items, false);
 };
 
-exports.testPropertyBackingArray = function (test) {
+exports.testPropertyBackingArray = function (assert) {
   let items = ["foo"];
   let obj = makeObjWithCollProp(items);
-  compare(test, obj.coll, items);
-  addRemove(test, obj.coll, items, true);
+  compare(assert, obj.coll, items);
+  addRemove(assert, obj.coll, items, true);
 
   items = ["foo", "bar"];
   obj = makeObjWithCollProp(items);
-  compare(test, obj.coll, items);
-  addRemove(test, obj.coll, items, true);
+  compare(assert, obj.coll, items);
+  addRemove(assert, obj.coll, items, true);
 };
 
 // Adds some values to coll and then removes them.  initialItems is an array
 // containing coll's initial items.  isBacking is true if initialItems is coll's
 // backing array; the point is that updates to coll should affect initialItems
 // if that's the case.
-function addRemove(test, coll, initialItems, isBacking) {
+function addRemove(assert, coll, initialItems, isBacking) {
   let items = isBacking ? initialItems : initialItems.slice(0);
   let numInitialItems = items.length;
 
   // Test add(val).
   let numInsertions = 5;
   for (let i = 0; i < numInsertions; i++) {
-    compare(test, coll, items);
+    compare(assert, coll, items);
     coll.add(i);
     if (!isBacking)
       items.push(i);
   }
-  compare(test, coll, items);
+  compare(assert, coll, items);
 
   // Add the items we just added to make sure duplicates aren't added.
   for (let i = 0; i < numInsertions; i++)
     coll.add(i);
-  compare(test, coll, items);
+  compare(assert, coll, items);
 
   // Test remove(val).  Do a kind of shuffled remove.  Remove item 1, then
   // item 0, 3, 2, 5, 4, ...
   for (let i = 0; i < numInsertions; i++) {
     let val = i % 2 ? i - 1 :
               i === numInsertions - 1 ? i : i + 1;
     coll.remove(val);
     if (!isBacking)
       items.splice(items.indexOf(val), 1);
-    compare(test, coll, items);
+    compare(assert, coll, items);
   }
-  test.assertEqual(coll.length, numInitialItems,
-                   "All inserted items should be removed");
+  assert.equal(coll.length, numInitialItems,
+               "All inserted items should be removed");
 
   // Remove the items we just removed.  coll should be unchanged.
   for (let i = 0; i < numInsertions; i++)
     coll.remove(i);
-  compare(test, coll, items);
+  compare(assert, coll, items);
 
   // Test add and remove([val1, val2]).
   let newItems = [0, 1];
   coll.add(newItems);
-  compare(test, coll, isBacking ? items : items.concat(newItems));
+  compare(assert, coll, isBacking ? items : items.concat(newItems));
   coll.remove(newItems);
-  compare(test, coll, items);
-  test.assertEqual(coll.length, numInitialItems,
-                   "All inserted items should be removed");
+  compare(assert, coll, items);
+  assert.equal(coll.length, numInitialItems,
+               "All inserted items should be removed");
 }
 
 // Asserts that the items in coll are the items of array.
-function compare(test, coll, array) {
-  test.assertEqual(coll.length, array.length,
-                   "Collection length should be correct");
+function compare(assert, coll, array) {
+  assert.equal(coll.length, array.length,
+               "Collection length should be correct");
   let numItems = 0;
   for (let item in coll) {
-    test.assertEqual(item, array[numItems], "Items should be equal");
+    assert.equal(item, array[numItems], "Items should be equal");
     numItems++;
   }
-  test.assertEqual(numItems, array.length,
-                   "Number of items in iteration should be correct");
+  assert.equal(numItems, array.length,
+               "Number of items in iteration should be correct");
 }
 
 // Returns a new object with a collection property named "coll".  backingArray,
 // if defined, will create the collection with that backing array.
 function makeObjWithCollProp(backingArray) {
   let obj = {};
   collection.addCollectionProperty(obj, "coll", backingArray);
   return obj;
 }
+
+require("sdk/test").run(exports);
--- a/addon-sdk/source/test/test-context-menu.js
+++ b/addon-sdk/source/test/test-context-menu.js
@@ -1,10 +1,8 @@
-/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et: */
 /* 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';
 
 let { Cc, Ci } = require("chrome");
 
 require("sdk/context-menu");
--- a/addon-sdk/source/test/test-cortex.js
+++ b/addon-sdk/source/test/test-cortex.js
@@ -1,14 +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/. */
 
-// vim:set ts=2 sw=2 sts=2
-
 "use strict";
 
 var Cortex = require("sdk/deprecated/cortex").Cortex;
 
 exports["test property changes propagate"] = function (assert) {
   var source = {
     _foo: "secret",
     get foo() {
--- a/addon-sdk/source/test/test-events.js
+++ b/addon-sdk/source/test/test-events.js
@@ -1,145 +1,144 @@
 /* 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 { LoaderWithHookedConsole } = require("sdk/test/loader");
 
 // Exposing private methods as public in order to test
 const EventEmitter = require('sdk/deprecated/events').EventEmitter.compose({
   listeners: function(type) this._listeners(type),
   emit: function() this._emit.apply(this, arguments),
   emitOnObject: function() this._emitOnObject.apply(this, arguments),
   removeAllListeners: function(type) this._removeAllListeners(type)
 });
 
-exports['test:add listeners'] = function(test) {
+exports['test:add listeners'] = function(assert) {
   let e = new EventEmitter();
 
   let events_new_listener_emited = [];
   let times_hello_emited = 0;
 
   e.on("newListener", function (event, listener) {
     events_new_listener_emited.push(event)
   })
 
   e.on("hello", function (a, b) {
     times_hello_emited += 1
-    test.assertEqual("a", a)
-    test.assertEqual("b", b)
-    test.assertEqual(this, e, '`this` pseudo-variable is bound to instance');
+    assert.equal("a", a)
+    assert.equal("b", b)
+    assert.equal(this, e, '`this` pseudo-variable is bound to instance');
   })
 
   e.emit("hello", "a", "b")
 };
 
-exports['test:removeListener'] = function(test) {
+exports['test:removeListener'] = function(assert) {
   let count = 0;
 
   function listener1 () {
     count++;
   }
   function listener2 () {
     count++;
   }
 
   // test adding and removing listener
   let e1 = new EventEmitter();
-  test.assertEqual(0, e1.listeners('hello').length);
+  assert.equal(0, e1.listeners('hello').length);
   e1.on("hello", listener1);
-  test.assertEqual(1, e1.listeners('hello').length);
-  test.assertEqual(listener1, e1.listeners('hello')[0]);
+  assert.equal(1, e1.listeners('hello').length);
+  assert.equal(listener1, e1.listeners('hello')[0]);
   e1.removeListener("hello", listener1);
-  test.assertEqual(0, e1.listeners('hello').length);
+  assert.equal(0, e1.listeners('hello').length);
   e1.emit("hello", "");
-  test.assertEqual(0, count);
+  assert.equal(0, count);
 
   // test adding one listener and removing another which was not added
   let e2 = new EventEmitter();
-  test.assertEqual(0, e2.listeners('hello').length);
+  assert.equal(0, e2.listeners('hello').length);
   e2.on("hello", listener1);
-  test.assertEqual(1, e2.listeners('hello').length);
+  assert.equal(1, e2.listeners('hello').length);
   e2.removeListener("hello", listener2);
-  test.assertEqual(1, e2.listeners('hello').length);
-  test.assertEqual(listener1, e2.listeners('hello')[0]);
+  assert.equal(1, e2.listeners('hello').length);
+  assert.equal(listener1, e2.listeners('hello')[0]);
   e2.emit("hello", "");
-  test.assertEqual(1, count);
+  assert.equal(1, count);
 
   // test adding 2 listeners, and removing one
   let e3 = new EventEmitter();
-  test.assertEqual(0, e3.listeners('hello').length);
+  assert.equal(0, e3.listeners('hello').length);
   e3.on("hello", listener1);
-  test.assertEqual(1, e3.listeners('hello').length);
+  assert.equal(1, e3.listeners('hello').length);
   e3.on("hello", listener2);
-  test.assertEqual(2, e3.listeners('hello').length);
+  assert.equal(2, e3.listeners('hello').length);
   e3.removeListener("hello", listener1);
-  test.assertEqual(1, e3.listeners('hello').length);
-  test.assertEqual(listener2, e3.listeners('hello')[0]);
+  assert.equal(1, e3.listeners('hello').length);
+  assert.equal(listener2, e3.listeners('hello')[0]);
   e3.emit("hello", "");
-  test.assertEqual(2, count);
+  assert.equal(2, count);
 };
 
-exports['test:removeAllListeners'] = function(test) {
+exports['test:removeAllListeners'] = function(assert) {
   let count = 0;
 
   function listener1 () {
     count++;
   }
   function listener2 () {
     count++;
   }
 
   // test adding a listener and removing all of that type
   let e1 = new EventEmitter();
   e1.on("hello", listener1);
-  test.assertEqual(1, e1.listeners('hello').length);
+  assert.equal(1, e1.listeners('hello').length);
   e1.removeAllListeners("hello");
-  test.assertEqual(0, e1.listeners('hello').length);
+  assert.equal(0, e1.listeners('hello').length);
   e1.emit("hello", "");
-  test.assertEqual(0, count);
+  assert.equal(0, count);
 
   // test adding a listener and removing all of another type
   let e2 = new EventEmitter();
   e2.on("hello", listener1);
-  test.assertEqual(1, e2.listeners('hello').length);
+  assert.equal(1, e2.listeners('hello').length);
   e2.removeAllListeners('goodbye');
-  test.assertEqual(1, e2.listeners('hello').length);
+  assert.equal(1, e2.listeners('hello').length);
   e2.emit("hello", "");
-  test.assertEqual(1, count);
+  assert.equal(1, count);
 
   // test adding 1+ listeners and removing all of that type
   let e3 = new EventEmitter();
   e3.on("hello", listener1);
-  test.assertEqual(1, e3.listeners('hello').length);
+  assert.equal(1, e3.listeners('hello').length);
   e3.on("hello", listener2);
-  test.assertEqual(2, e3.listeners('hello').length);
+  assert.equal(2, e3.listeners('hello').length);
   e3.removeAllListeners("hello");
-  test.assertEqual(0, e3.listeners('hello').length);
+  assert.equal(0, e3.listeners('hello').length);
   e3.emit("hello", "");
-  test.assertEqual(1, count);
+  assert.equal(1, count);
 
   // test adding 2 listeners for 2 types and removing all listeners
   let e4 = new EventEmitter();
   e4.on("hello", listener1);
-  test.assertEqual(1, e4.listeners('hello').length);
+  assert.equal(1, e4.listeners('hello').length);
   e4.on('goodbye', listener2);
-  test.assertEqual(1, e4.listeners('goodbye').length);
+  assert.equal(1, e4.listeners('goodbye').length);
   e4.emit("goodbye", "");
   e4.removeAllListeners();
-  test.assertEqual(0, e4.listeners('hello').length);
-  test.assertEqual(0, e4.listeners('goodbye').length);
+  assert.equal(0, e4.listeners('hello').length);
+  assert.equal(0, e4.listeners('goodbye').length);
   e4.emit("hello", "");
   e4.emit("goodbye", "");
-  test.assertEqual(2, count);
+  assert.equal(2, count);
 };
 
-exports['test: modify in emit'] = function(test) {
+exports['test: modify in emit'] = function(assert) {
   let callbacks_called = [ ];
   let e = new EventEmitter();
 
   function callback1() {
     callbacks_called.push("callback1");
     e.on("foo", callback2);
     e.on("foo", callback3);
     e.removeListener("foo", callback1);
@@ -149,116 +148,116 @@ exports['test: modify in emit'] = functi
     e.removeListener("foo", callback2);
   }
   function callback3() {
     callbacks_called.push("callback3");
     e.removeListener("foo", callback3);
   }
 
   e.on("foo", callback1);
-  test.assertEqual(1, e.listeners("foo").length);
+  assert.equal(1, e.listeners("foo").length);
 
   e.emit("foo");
-  test.assertEqual(2, e.listeners("foo").length);
-  test.assertEqual(1, callbacks_called.length);
-  test.assertEqual('callback1', callbacks_called[0]);
+  assert.equal(2, e.listeners("foo").length);
+  assert.equal(1, callbacks_called.length);
+  assert.equal('callback1', callbacks_called[0]);
 
   e.emit("foo");
-  test.assertEqual(0, e.listeners("foo").length);
-  test.assertEqual(3, callbacks_called.length);
-  test.assertEqual('callback1', callbacks_called[0]);
-  test.assertEqual('callback2', callbacks_called[1]);
-  test.assertEqual('callback3', callbacks_called[2]);
+  assert.equal(0, e.listeners("foo").length);
+  assert.equal(3, callbacks_called.length);
+  assert.equal('callback1', callbacks_called[0]);
+  assert.equal('callback2', callbacks_called[1]);
+  assert.equal('callback3', callbacks_called[2]);
 
   e.emit("foo");
-  test.assertEqual(0, e.listeners("foo").length);
-  test.assertEqual(3, callbacks_called.length);
-  test.assertEqual('callback1', callbacks_called[0]);
-  test.assertEqual('callback2', callbacks_called[1]);
-  test.assertEqual('callback3', callbacks_called[2]);
+  assert.equal(0, e.listeners("foo").length);
+  assert.equal(3, callbacks_called.length);
+  assert.equal('callback1', callbacks_called[0]);
+  assert.equal('callback2', callbacks_called[1]);
+  assert.equal('callback3', callbacks_called[2]);
 
   e.on("foo", callback1);
   e.on("foo", callback2);
-  test.assertEqual(2, e.listeners("foo").length);
+  assert.equal(2, e.listeners("foo").length);
   e.removeAllListeners("foo");
-  test.assertEqual(0, e.listeners("foo").length);
+  assert.equal(0, e.listeners("foo").length);
 
   // Verify that removing callbacks while in emit allows emits to propagate to
   // all listeners
   callbacks_called = [ ];
 
   e.on("foo", callback2);
   e.on("foo", callback3);
-  test.assertEqual(2, e.listeners("foo").length);
+  assert.equal(2, e.listeners("foo").length);
   e.emit("foo");
-  test.assertEqual(2, callbacks_called.length);
-  test.assertEqual('callback2', callbacks_called[0]);
-  test.assertEqual('callback3', callbacks_called[1]);
-  test.assertEqual(0, e.listeners("foo").length);
+  assert.equal(2, callbacks_called.length);
+  assert.equal('callback2', callbacks_called[0]);
+  assert.equal('callback3', callbacks_called[1]);
+  assert.equal(0, e.listeners("foo").length);
 };
 
-exports['test:adding same listener'] = function(test) {
+exports['test:adding same listener'] = function(assert) {
   function foo() {}
   let e = new EventEmitter();
   e.on("foo", foo);
   e.on("foo", foo);
-  test.assertEqual(
+  assert.equal(
     1,
     e.listeners("foo").length,
     "listener reregistration is ignored"
  );
 }
 
-exports['test:errors are reported if listener throws'] = function(test) {
+exports['test:errors are reported if listener throws'] = function(assert) {
   let e = new EventEmitter(),
       reported = false;
   e.on('error', function(e) reported = true)
   e.on('boom', function() { throw new Error('Boom!') });
   e.emit('boom', 3);
-  test.assert(reported, 'error should be reported through event');
+  assert.ok(reported, 'error should be reported through event');
 };
 
-exports['test:emitOnObject'] = function(test) {
+exports['test:emitOnObject'] = function(assert) {
   let e = new EventEmitter();
 
   e.on("foo", function() {
-    test.assertEqual(this, e, "`this` should be emitter");
+    assert.equal(this, e, "`this` should be emitter");
   });
   e.emitOnObject(e, "foo");
 
   e.on("bar", function() {
-    test.assertEqual(this, obj, "`this` should be other object");
+    assert.equal(this, obj, "`this` should be other object");
   });
   let obj = {};
   e.emitOnObject(obj, "bar");
 };
 
-exports['test:once'] = function(test) {
+exports['test:once'] = function(assert) {
   let e = new EventEmitter();
   let called = false;
 
   e.once('foo', function(value) {
-    test.assert(!called, "listener called only once");
-    test.assertEqual(value, "bar", "correct argument was passed");
+    assert.ok(!called, "listener called only once");
+    assert.equal(value, "bar", "correct argument was passed");
   });
 
   e.emit('foo', 'bar');
   e.emit('foo', 'baz');
 };
 
-exports["test:removing once"] = function(test) {
+exports["test:removing once"] = function(assert) {
   let e = require("sdk/deprecated/events").EventEmitterTrait.create();
-  e.once("foo", function() { test.pass("listener was called"); });
-  e.once("error", function() { test.fail("error event was emitted"); });
+  e.once("foo", function() { assert.pass("listener was called"); });
+  e.once("error", function() { assert.fail("error event was emitted"); });
   e._emit("foo", "bug-656684");
 };
 
 // Bug 726967: Ensure that `emit` doesn't do an infinite loop when `error`
 // listener throws an exception
-exports['test:emitLoop'] = function(test) {
+exports['test:emitLoop'] = function(assert) {
   // Override the console for this test so it doesn't log the exception to the
   // test output
   let { loader } = LoaderWithHookedConsole(module);
 
   let EventEmitter = loader.require('sdk/deprecated/events').EventEmitter.compose({
     listeners: function(type) this._listeners(type),
     emit: function() this._emit.apply(this, arguments),
     emitOnObject: function() this._emitOnObject.apply(this, arguments),
@@ -271,10 +270,12 @@ exports['test:emitLoop'] = function(test
     throw new Error("foo");
   });
 
   e.on("error", function() {
     throw new Error("error");
   });
   e.emit("foo");
 
-  test.pass("emit didn't looped");
+  assert.pass("emit didn't looped");
 };
+
+require('sdk/test').run(exports);
--- a/addon-sdk/source/test/test-globals.js
+++ b/addon-sdk/source/test/test-globals.js
@@ -1,22 +1,23 @@
 /* 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';
 
-Object.defineProperty(this, "global", { value: this });
+Object.defineProperty(this, 'global', { value: this });
 
-exports.testGlobals = function(test) {
+exports.testGlobals = function(assert) {
   // the only globals in module scope should be:
-  //   module, exports, require, dump, console
-  test.assertObject(module, "have 'module', good");
-  test.assertObject(exports, "have 'exports', good");
-  test.assertFunction(require, "have 'require', good");
-  test.assertFunction(dump, "have 'dump', good");
-  test.assertObject(console, "have 'console', good");
+  assert.equal(typeof module, 'object', 'have "module" global');
+  assert.equal(typeof exports, 'object', 'have "exports" global');
+  assert.equal(typeof require, 'function', 'have "require" global');
+  assert.equal(typeof dump, 'function', 'have "dump" global');
+  assert.equal(typeof console, 'object', 'have "console" global');
 
   // in particular, these old globals should no longer be present
-  test.assert(!('packaging' in global), "no 'packaging', good");
-  test.assert(!('memory' in global), "no 'memory', good");
+  assert.ok(!('packaging' in global), 'no "packaging" global was found');
+  assert.ok(!('memory' in global), 'no "memory" global was found');
 
-  test.assertMatches(module.uri, /test-globals\.js$/,
-                     'should contain filename');
+  assert.ok(/test-globals\.js$/.test(module.uri), 'should contain filename');
 };
+
+require("test").run(exports);
--- a/addon-sdk/source/test/test-hidden-frame.js
+++ b/addon-sdk/source/test/test-hidden-frame.js
@@ -1,12 +1,11 @@
 /* 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 { Loader } = require("sdk/test/loader");
 const hiddenFrames = require("sdk/frame/hidden-frame");
 const { HiddenFrame } = hiddenFrames;
 
 exports["test Frame"] = function(assert, done) {
   let url = "data:text/html;charset=utf-8,<!DOCTYPE%20html>";
@@ -39,17 +38,16 @@ exports["test frame removed properly"] =
       assert.ok(frame.parentNode, "frame has a parent node");
       hiddenFrames.remove(hiddenFrame);
       assert.ok(!frame.parentNode, "frame no longer has a parent node");
       done();
     }
   }));
 };
 
-
 exports["test unload detaches panels"] = function(assert, done) {
   let loader = Loader(module);
   let { add, remove, HiddenFrame } = loader.require("sdk/frame/hidden-frame");
   let frames = []
 
   function ready() {
     frames.push(this.element);
     if (frames.length === 2) complete();
@@ -65,11 +63,9 @@ exports["test unload detaches panels"] =
     loader.unload();
     frames.forEach(function(frame) {
       assert.ok(!frame.parentNode, "frame isn't in the document'");
     });
     done();
   }
 };
 
-
-
 require("test").run(exports);
--- a/addon-sdk/source/test/test-match-pattern.js
+++ b/addon-sdk/source/test/test-match-pattern.js
@@ -1,10 +1,8 @@
-/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et: */
 /* 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 { MatchPattern } = require("sdk/util/match-pattern");
 
 exports.testMatchPatternTestTrue = function(test) {
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/test/test-method.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/. */
+
+"use strict";
+
+module.exports = require("method/test/common");
--- a/addon-sdk/source/test/test-notifications.js
+++ b/addon-sdk/source/test/test-notifications.js
@@ -1,11 +1,9 @@
-/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * vim:set ts=2 sw=2 sts=2 et filetype=javascript
- * This Source Code Form is subject to the terms of the Mozilla Public
+/* 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 { Loader } = require('sdk/test/loader');
 
 exports.testOnClick = function (test) {
   let [loader, mockAlertServ] = makeLoader(module);
   let notifs = loader.require("sdk/notifications");
--- a/addon-sdk/source/test/test-panel.js
+++ b/addon-sdk/source/test/test-panel.js
@@ -127,34 +127,36 @@ exports["test Show Hide Panel"] = functi
 };
 
 exports["test Document Reload"] = function(assert, done) {
   const { Panel } = require('sdk/panel');
 
   let url2 = "data:text/html;charset=utf-8,page2";
   let content =
     "<script>" +
-    "window.addEventListener('message', function() {"+
-    "  window.location = '" + url2 + "';" +
+    "window.addEventListener('message', function({ data }) {"+
+    "  if (data == 'move') window.location = '" + url2 + "';" +
     '}, false);' +
     "</script>";
   let messageCount = 0;
   let panel = Panel({
     // using URL here is intentional, see bug 859009
     contentURL: URL("data:text/html;charset=utf-8," + encodeURIComponent(content)),
     contentScript: "self.postMessage(window.location.href);" +
                    // initiate change to url2
                    "self.port.once('move', function() document.defaultView.postMessage('move', '*'));",
     onMessage: function (message) {
       messageCount++;
       assert.notEqual(message, "about:blank", "about:blank is not a message " + messageCount);
 
       if (messageCount == 1) {
         assert.ok(/data:text\/html/.test(message), "First document had a content script; " + message);
         panel.port.emit('move');
+        assert.pass('move message was sent');
+        return;
       }
       else if (messageCount == 2) {
         assert.equal(message, url2, "Second document too; " + message);
         panel.destroy();
         done();
       }
     }
   });
--- a/addon-sdk/source/test/test-places-bookmarks.js
+++ b/addon-sdk/source/test/test-places-bookmarks.js
@@ -312,22 +312,22 @@ exports.testAddingToExistingParent = fun
   firstBatch, secondBatch;
 
   saveP(bookmarks).then(data => {
     firstBatch = data;
     return saveP([
       { title: 'moz4', url: 'http://moz4.com', type: 'bookmark', group: group },
       { title: 'moz5', url: 'http://moz5.com', type: 'bookmark', group: group }
     ]);
-  }, console.error).then(data => {
+  }, assert.fail).then(data => {
     secondBatch = data;
     assert.equal(firstBatch[0].group.id, secondBatch[0].group.id,
       'successfully saved to the same parent');
     done();
-  }, console.error);
+  }, assert.fail);
 };
 
 exports.testUpdateParent = function (assert, done) {
   let group = { type: 'group', title: 'mozgroup' };
   saveP(group).then(item => {
     item[0].title = 'mozgroup-resave';
     return saveP(item[0]);
   }).then(item => {
@@ -621,17 +621,17 @@ exports.testSearchByGroupSimple = functi
   }).then(results => {
     let groups = results.filter(({type}) => type === 'group');
     assert.equal(groups.length, 1, 'returns one subfolder');
     assert.equal(results.length, 6,
       'returns all children bookmarks/folders');
     assert.ok(results.filter(({url}) => url === 'http://w3schools.com/'),
       'returns nested children');
     done();
-  }).then(null, console.error);
+  }).then(null, assert.fail);
 };
 
 exports.testSearchByGroupComplex = function (assert, done) {
   let mozgroup;
   createBookmarkTree().then(results => {
     mozgroup = results.filter(({title}) => title === 'mozgroup')[0];
     return searchP({ group: mozgroup, query: 'javascript' });
   }).then(results => {
@@ -639,17 +639,17 @@ exports.testSearchByGroupComplex = funct
     assert.equal(results[0].url, 'http://w3schools.com/', 'correct result');
     return searchP({ group: mozgroup, url: '*.mozilla.org' });
   }).then(results => {
     assert.equal(results.length, 2, 'expected results');
     assert.ok(
       !results.filter(({url}) => /developer.mozilla/.test(url)).length,
       'does not find results from other folders');
     done();
-  }, console.error);
+  }, assert.fail);
 };
 
 exports.testSearchEmitters = function (assert, done) {
   createBookmarkTree().then(() => {
     let count = 0;
     search({ tags: ['mozilla', 'firefox'] }).on('data', data => {
       assert.ok(/mozilla|firefox/.test(data.title), 'one of the correct items');
       assert.ok(data.tags.has('firefox'), 'has firefox tag');
@@ -768,24 +768,24 @@ exports.testSearchQuery = function (asse
  * and if implementation changes, this could increase or decrease
  */
 
 exports.testCaching = function (assert, done) {
   let count = 0;
   let stream = filter(request, ({event}) =>
     /sdk-places-bookmarks-get/.test(event));
   on(stream, 'data', handle);
- 
+
   let group = { type: 'group', title: 'mozgroup' };
   let bookmarks = [
     { title: 'moz1', url: 'http://moz1.com', type: 'bookmark', group: group },
     { title: 'moz2', url: 'http://moz2.com', type: 'bookmark', group: group },
     { title: 'moz3', url: 'http://moz3.com', type: 'bookmark', group: group }
   ];
-  
+
   /*
    * Use timeout in tests since the platform calls are synchronous
    * and the counting event shim may not have occurred yet
    */
 
   saveP(bookmarks).then(() => {
     assert.equal(count, 0, 'all new items and root group, no fetches should occur');
     count = 0;
@@ -811,17 +811,17 @@ exports.testCaching = function (assert, 
 };
 
 /*
  * Search Query Options
  */
 
 exports.testSearchCount = function (assert, done) {
   let max = 8;
-  createBookmarkTree()  
+  createBookmarkTree()
   .then(testCount(1))
   .then(testCount(2))
   .then(testCount(3))
   .then(testCount(5))
   .then(testCount(10))
   .then(() => {
     done();
   });
@@ -837,17 +837,17 @@ exports.testSearchCount = function (asse
   }
 };
 
 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,'')}))
   ).then(() => {
     return searchP({}, { sort: 'title' });
   }).then(results => {
     checkOrder(results, [4,3,0,2,1]);
     return searchP({}, { sort: 'title', descending: true });
@@ -863,17 +863,17 @@ exports.testSearchSort = function (asser
   }).then(() =>
     saveP(Bookmark({ url: 'http://github.com', title: 'github.com' }))
   ).then(() => addVisits('http://bandcamp.com/'))
   .then(() => searchP({ query: 'webfwd' }))
   .then(results => {
     results[0].title = 'new title for webfwd';
     return saveP(results[0]);
   })
-  .then(() => 
+  .then(() =>
     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');
@@ -899,17 +899,17 @@ exports.testSearchSort = function (asser
       'last modified should be last');
     return searchP({}, { sort: 'lastModified', descending: true });
   }).then(results => {
     assert.equal(results[0].url, 'http://mozilla.com/webfwd/',
       'last modified should be first');
   }).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, urls[nums[i]], 'successful order');
     }
   }
 };
 
--- a/addon-sdk/source/test/test-places-host.js
+++ b/addon-sdk/source/test/test-places-host.js
@@ -41,17 +41,17 @@ exports.testBookmarksCreate = function (
   }, {
     title: 'my folder',
     type: 'group',
     group: bmsrv.bookmarksMenuFolder
   }, {
     type: 'separator',
     group: bmsrv.unfiledBookmarksFolder
   }];
-  
+
   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));
 };
@@ -106,17 +106,17 @@ exports.testBookmarkRemove = function (a
     compareWithHost(assert, data); // ensure bookmark exists
     bmsrv.getItemTitle(id); // does not throw an error
     return send('sdk-places-bookmarks-remove', data);
   }).then(function () {
     assert.throws(function () {
       bmsrv.getItemTitle(id);
     }, 'item should no longer exist');
     done();
-  }, console.error);
+  }, assert.fail);
 };
 
 exports.testBookmarkGet = function (assert, done) {
   let bookmark;
   createBookmark({
     url: 'http://test-places-host.com/testBookmarkGet/'
   }).then(function (data) {
     bookmark = data;
@@ -222,25 +222,25 @@ exports.testHostQuery = function (assert
     }),
     createBookmark({
       url: 'http://mozilla.com/testHostQuery/',
       tags: ['mozilla']
     }),
     createBookmark({ url: 'http://thunderbird.com/testHostQuery/' })
   ]).then(data => {
     return send('sdk-places-query', {
-      queries: { tags: ['mozilla'] }, 
+      queries: { tags: ['mozilla'] },
       options: { sortingMode: 6, queryType: 1 } // sort by URI ascending, bookmarks only
     });
   }).then(results => {
     assert.equal(results.length, 2, 'should only return two');
     assert.equal(results[0].url,
       'http://mozilla.com/testHostQuery/', 'is sorted by URI asc');
     return send('sdk-places-query', {
-      queries: { tags: ['mozilla'] }, 
+      queries: { tags: ['mozilla'] },
       options: { sortingMode: 5, queryType: 1 } // sort by URI descending, bookmarks only
     });
   }).then(results => {
     assert.equal(results.length, 2, 'should only return two');
     assert.equal(results[0].url,
       'http://firefox.com/testHostQuery/', 'is sorted by URI desc');
     done();
   });
@@ -274,22 +274,22 @@ exports.testHostMultiQuery = function (a
     });
   }).then(results => {
     assert.equal(results.length, 0, 'query props should be AND\'d');
     done();
   });
 };
 
 exports.testGetAllBookmarks = function (assert, done) {
-  createBookmarkTree().then(() => { 
+  createBookmarkTree().then(() => {
     return send('sdk-places-bookmarks-get-all', {});
   }).then(res => {
     assert.equal(res.length, 8, 'all bookmarks returned');
     done();
-  }, console.error);
+  }, assert.fail);
 };
 
 exports.testGetAllChildren = function (assert, done) {
   createBookmarkTree().then(results => {
     return send('sdk-places-bookmarks-get-children', {
       id: results.filter(({title}) => title === 'mozgroup')[0].id
     });
   }).then(results => {
--- a/addon-sdk/source/test/test-plain-text-console.js
+++ b/addon-sdk/source/test/test-plain-text-console.js
@@ -11,106 +11,106 @@ const { loadSubScript } = Cc['@mozilla.o
 const ADDON_LOG_LEVEL_PREF = "extensions." + id + ".sdk.console.logLevel";
 const SDK_LOG_LEVEL_PREF = "extensions.sdk.console.logLevel";
 
 const HAS_ORIGINAL_ADDON_LOG_LEVEL = prefs.has(ADDON_LOG_LEVEL_PREF);
 const ORIGINAL_ADDON_LOG_LEVEL = prefs.get(ADDON_LOG_LEVEL_PREF);
 const HAS_ORIGINAL_SDK_LOG_LEVEL = prefs.has(SDK_LOG_LEVEL_PREF);
 const ORIGINAL_SDK_LOG_LEVEL = prefs.get(SDK_LOG_LEVEL_PREF);
 
-exports.testPlainTextConsole = function(test) {
-  var prints = [];
+exports.testPlainTextConsole = function(assert) {
+  let prints = [];
   function print(message) {
     prints.push(message);
   }
   function lastPrint() {
-    var last = prints.slice(-1)[0];
+    let last = prints.slice(-1)[0];
     prints = [];
     return last;
   }
 
   prefs.set(SDK_LOG_LEVEL_PREF, "all");
   prefs.reset(ADDON_LOG_LEVEL_PREF);
 
-  var Console = require("sdk/console/plain-text").PlainTextConsole;
-  var con = new Console(print);
+  let Console = require("sdk/console/plain-text").PlainTextConsole;
+  let con = new Console(print);
 
-  test.pass("PlainTextConsole instantiates");
+  assert.ok("PlainTextConsole instantiates");
 
   con.log('testing', 1, [2, 3, 4]);
-  test.assertEqual(lastPrint(), "console.log: " + name + ": testing 1 Array [2,3,4]\n",
+  assert.equal(lastPrint(), "console.log: " + name + ": testing 1 Array [2,3,4]\n",
                    "PlainTextConsole.log() must work.");
 
   con.info('testing', 1, [2, 3, 4]);
-  test.assertEqual(lastPrint(), "console.info: " + name + ": testing 1 Array [2,3,4]\n",
+  assert.equal(lastPrint(), "console.info: " + name + ": testing 1 Array [2,3,4]\n",
                    "PlainTextConsole.info() must work.");
 
   con.warn('testing', 1, [2, 3, 4]);
-  test.assertEqual(lastPrint(), "console.warn: " + name + ": testing 1 Array [2,3,4]\n",
+  assert.equal(lastPrint(), "console.warn: " + name + ": testing 1 Array [2,3,4]\n",
                    "PlainTextConsole.warn() must work.");
 
   con.error('testing', 1, [2, 3, 4]);
-  test.assertEqual(prints[0], "console.error: " + name + ": \n",
+  assert.equal(prints[0], "console.error: " + name + ": \n",
                    "PlainTextConsole.error() must work.");
-  test.assertEqual(prints[1], "  testing\n")
-  test.assertEqual(prints[2], "  1\n")
-  test.assertEqual(prints[3], "Array\n    - 0 = 2\n    - 1 = 3\n    - 2 = 4\n    - length = 3\n");
+  assert.equal(prints[1], "  testing\n")
+  assert.equal(prints[2], "  1\n")
+  assert.equal(prints[3], "Array\n    - 0 = 2\n    - 1 = 3\n    - 2 = 4\n    - length = 3\n");
   prints = [];
 
   con.debug('testing', 1, [2, 3, 4]);
-  test.assertEqual(prints[0], "console.debug: " + name + ": \n",
+  assert.equal(prints[0], "console.debug: " + name + ": \n",
                    "PlainTextConsole.debug() must work.");
-  test.assertEqual(prints[1], "  testing\n")
-  test.assertEqual(prints[2], "  1\n")
-  test.assertEqual(prints[3], "Array\n    - 0 = 2\n    - 1 = 3\n    - 2 = 4\n    - length = 3\n");
+  assert.equal(prints[1], "  testing\n")
+  assert.equal(prints[2], "  1\n")
+  assert.equal(prints[3], "Array\n    - 0 = 2\n    - 1 = 3\n    - 2 = 4\n    - length = 3\n");
   prints = [];
 
   con.log('testing', undefined);
-  test.assertEqual(lastPrint(), "console.log: " + name + ": testing undefined\n",
+  assert.equal(lastPrint(), "console.log: " + name + ": testing undefined\n",
                    "PlainTextConsole.log() must stringify undefined.");
 
   con.log('testing', null);
-  test.assertEqual(lastPrint(), "console.log: " + name + ": testing null\n",
+  assert.equal(lastPrint(), "console.log: " + name + ": testing null\n",
                    "PlainTextConsole.log() must stringify null.");
 
   // TODO: Fix console.jsm to detect custom toString.
   con.log("testing", { toString: function() "obj.toString()" });
-  test.assertEqual(lastPrint(), "console.log: " + name + ": testing {}\n",
+  assert.equal(lastPrint(), "console.log: " + name + ": testing {}\n",
                    "PlainTextConsole.log() doesn't printify custom toString.");
 
   con.log("testing", { toString: function() { throw "fail!"; } });
-  test.assertEqual(lastPrint(), "console.log: " + name + ": testing {}\n",
+  assert.equal(lastPrint(), "console.log: " + name + ": testing {}\n",
                    "PlainTextConsole.log() must stringify custom bad toString.");
 
-  
+
   con.exception(new Error("blah"));
 
-  
-  test.assertEqual(prints[0], "console.error: " + name + ": \n");
+
+  assert.equal(prints[0], "console.error: " + name + ": \n");
   let tbLines = prints[1].split("\n");
-  test.assertEqual(tbLines[0], "  Message: Error: blah");
-  test.assertEqual(tbLines[1], "  Stack:");
-  test.assert(prints[1].indexOf(module.uri + ":84") !== -1);
+  assert.equal(tbLines[0], "  Message: Error: blah");
+  assert.equal(tbLines[1], "  Stack:");
+  assert.ok(prints[1].indexOf(module.uri + ":84") !== -1);
   prints = []
 
   try {
     loadSubScript("invalid-url", {});
-    test.fail("successed in calling loadSubScript with invalid-url");
+    assert.fail("successed in calling loadSubScript with invalid-url");
   }
   catch(e) {
     con.exception(e);
   }
-  test.assertEqual(prints[0], "console.error: " + name + ": \n");
-  test.assertEqual(prints[1], "  Error creating URI (invalid URL scheme?)\n");
+  assert.equal(prints[0], "console.error: " + name + ": \n");
+  assert.equal(prints[1], "  Error creating URI (invalid URL scheme?)\n");
   prints = [];
 
   con.trace();
   let tbLines = prints[0].split("\n");
-  test.assertEqual(tbLines[0], "console.trace: " + name + ": ");
-  test.assert(tbLines[1].indexOf("_ain-text-console.js 105") == 0);
+  assert.equal(tbLines[0], "console.trace: " + name + ": ");
+  assert.ok(tbLines[1].indexOf("_ain-text-console.js 105") == 0);
   prints = [];
 
   // Whether or not console methods should print at the various log levels,
   // structured as a hash of levels, each of which contains a hash of methods,
   // each of whose value is whether or not it should print, i.e.:
   // { [level]: { [method]: [prints?], ... }, ... }.
   let levels = {
     all:   { debug: true,  log: true,  info: true,  warn: true,  error: true  },
@@ -135,40 +135,111 @@ exports.testPlainTextConsole = function(
     for (let method in methods) {
       // We have to reset the log level pref each time we run the test
       // because the test runner relies on the console to print test output,
       // and test results would not get printed to the console for some
       // values of the pref.
       prefs.set(SDK_LOG_LEVEL_PREF, level);
       con[method]("");
       prefs.set(SDK_LOG_LEVEL_PREF, "all");
-      test.assertEqual(prints.join(""), 
+      assert.equal(prints.join(""),
                        (methods[method] ? messages[method] : ""),
                        "at log level '" + level + "', " + method + "() " +
                        (methods[method] ? "prints" : "doesn't print"));
       prints = [];
     }
   }
 
   prefs.set(SDK_LOG_LEVEL_PREF, "off");
   prefs.set(ADDON_LOG_LEVEL_PREF, "all");
   con.debug("");
-  test.assertEqual(prints.join(""), messages["debug"],
+  assert.equal(prints.join(""), messages["debug"],
                    "addon log level 'all' overrides SDK log level 'off'");
   prints = [];
 
   prefs.set(SDK_LOG_LEVEL_PREF, "all");
   prefs.set(ADDON_LOG_LEVEL_PREF, "off");
   con.error("");
   prefs.reset(ADDON_LOG_LEVEL_PREF);
-  test.assertEqual(lastPrint(), null,
+  assert.equal(lastPrint(), null,
                    "addon log level 'off' overrides SDK log level 'all'");
 
+  restorePrefs();
+};
+
+exports.testPlainTextConsoleBoundMethods = function(assert) {
+  let prints = [];
+  function print(message) {
+    prints.push(message);
+  }
+  function lastPrint() {
+    let last = prints.slice(-1)[0];
+    prints = [];
+    return last;
+  }
+
+  prefs.set(SDK_LOG_LEVEL_PREF, "all");
+  prefs.reset(ADDON_LOG_LEVEL_PREF);
+
+  let Console = require("sdk/console/plain-text").PlainTextConsole;
+  let { log, info, warn, error, debug, exception, trace } = new Console(print);
+
+  assert.ok("PlainTextConsole instantiates");
+
+  log('testing', 1, [2, 3, 4]);
+  assert.equal(lastPrint(), "console.log: " + name + ": testing 1 Array [2,3,4]\n",
+                   "PlainTextConsole.log() must work.");
+
+  info('testing', 1, [2, 3, 4]);
+  assert.equal(lastPrint(), "console.info: " + name + ": testing 1 Array [2,3,4]\n",
+                   "PlainTextConsole.info() must work.");
+
+  warn('testing', 1, [2, 3, 4]);
+  assert.equal(lastPrint(), "console.warn: " + name + ": testing 1 Array [2,3,4]\n",
+                   "PlainTextConsole.warn() must work.");
+
+  error('testing', 1, [2, 3, 4]);
+  assert.equal(prints[0], "console.error: " + name + ": \n",
+                   "PlainTextConsole.error() must work.");
+  assert.equal(prints[1], "  testing\n")
+  assert.equal(prints[2], "  1\n")
+  assert.equal(prints[3], "Array\n    - 0 = 2\n    - 1 = 3\n    - 2 = 4\n    - length = 3\n");
+  prints = [];
+
+  debug('testing', 1, [2, 3, 4]);
+  assert.equal(prints[0], "console.debug: " + name + ": \n",
+                   "PlainTextConsole.debug() must work.");
+  assert.equal(prints[1], "  testing\n")
+  assert.equal(prints[2], "  1\n")
+  assert.equal(prints[3], "Array\n    - 0 = 2\n    - 1 = 3\n    - 2 = 4\n    - length = 3\n");
+  prints = [];
+
+  exception(new Error("blah"));
+
+  assert.equal(prints[0], "console.error: " + name + ": \n");
+  let tbLines = prints[1].split("\n");
+  assert.equal(tbLines[0], "  Message: Error: blah");
+  assert.equal(tbLines[1], "  Stack:");
+  assert.ok(prints[1].indexOf(module.uri + ":215") !== -1);
+  prints = []
+
+  trace();
+  let tbLines = prints[0].split("\n");
+  assert.equal(tbLines[0], "console.trace: " + name + ": ");
+  assert.ok(tbLines[1].indexOf("_ain-text-console.js 224") === 0);
+  prints = [];
+
+  restorePrefs();
+};
+
+function restorePrefs() {
   if (HAS_ORIGINAL_ADDON_LOG_LEVEL)
     prefs.set(ADDON_LOG_LEVEL_PREF, ORIGINAL_ADDON_LOG_LEVEL);
   else
     prefs.reset(ADDON_LOG_LEVEL_PREF);
 
   if (HAS_ORIGINAL_SDK_LOG_LEVEL)
     prefs.set(SDK_LOG_LEVEL_PREF, ORIGINAL_SDK_LOG_LEVEL);
   else
     prefs.reset(SDK_LOG_LEVEL_PREF);
-};
+}
+
+require("test").run(exports);
--- a/addon-sdk/source/test/test-promise.js
+++ b/addon-sdk/source/test/test-promise.js
@@ -1,12 +1,11 @@
-/* vim:set ts=2 sw=2 sts=2 expandtab */
-/*jshint asi: true undef: true es5: true node: true devel: true
-         forin: true */
-/*global define: true */
+/* 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 core = require('sdk/core/promise'),
     defer = core.defer, resolve = core.resolve, reject = core.reject, all = core.all,
     promised = core.promised;
 
 var { setTimeout } = require('sdk/timers');
--- a/addon-sdk/source/test/test-self.js
+++ b/addon-sdk/source/test/test-self.js
@@ -1,65 +1,65 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
 "use strict";
 
-const {Cc, Ci, Cu, Cm, components} = require('chrome');
-Cu.import("resource://gre/modules/AddonManager.jsm", this);
+const { Cc, Ci, Cu, Cm, components } = require("chrome");
 const xulApp = require("sdk/system/xul-app");
+const self = require("sdk/self");
 
-exports.testSelf = function(test) {
-  var self = require("sdk/self");
+const { AddonManager } = Cu.import("resource://gre/modules/AddonManager.jsm", {});
 
+exports.testSelf = function(assert) {
   var source = self.data.load("test-content-symbiont.js");
-  test.assert(source.match(/test-content-symbiont/), "self.data.load() works");
+  assert.ok(source.match(/test-content-symbiont/), "self.data.load() works");
 
   // Likewise, we can't assert anything about the full URL, because that
   // depends on self.id . We can only assert that it ends in the right
   // thing.
   var url = self.data.url("test-content-symbiont.js");
-  test.assertEqual(typeof(url), "string", "self.data.url('x') returns string");
-  test.assertEqual(/\/test-content-symbiont\.js$/.test(url), true);
+  assert.equal(typeof(url), "string", "self.data.url('x') returns string");
+  assert.equal(/\/test-content-symbiont\.js$/.test(url), true);
 
   // Make sure 'undefined' is not in url when no string is provided.
   url = self.data.url();
-  test.assertEqual(typeof(url), "string", "self.data.url() returns string");
-  test.assertEqual(/\/undefined$/.test(url), false);
+  assert.equal(typeof(url), "string", "self.data.url() returns string");
+  assert.equal(/\/undefined$/.test(url), false);
 
   // When tests are run on just the api-utils package, self.name is
   // api-utils. When they're run as 'cfx testall', self.name is testpkgs.
-  test.assert(self.name == "addon-sdk", "self.name is addon-sdk");
+  assert.equal(self.name, "addon-sdk", "self.name is addon-sdk");
 
   // loadReason may change here, as we change the way tests addons are installed
   // Bug 854937 fixed loadReason and is now install
   let testLoadReason = xulApp.versionInRange(xulApp.platformVersion,
                                              "23.0a1", "*") ? "install"
                                                             : "startup";
-  test.assertEqual(self.loadReason, testLoadReason,
-                   "self.loadReason is either startup or install on test runs");
+  assert.equal(self.loadReason, testLoadReason,
+               "self.loadReason is either startup or install on test runs");
 
-  test.assertEqual(self.isPrivateBrowsingSupported, false,
-                   'usePrivateBrowsing property is false by default');
+  assert.equal(self.isPrivateBrowsingSupported, false,
+               'usePrivateBrowsing property is false by default');
 };
 
-exports.testSelfID = function(test) {
-  test.waitUntilDone();
-
+exports.testSelfID = function(assert, done) {
   var self = require("sdk/self");
   // We can't assert anything about the ID inside the unit test right now,
   // because the ID we get depends upon how the test was invoked. The idea
   // is that it is supposed to come from the main top-level package's
   // package.json file, from the "id" key.
-  test.assertEqual(typeof(self.id), "string", "self.id is a string");
-  test.assert(self.id.length > 0);
+  assert.equal(typeof(self.id), "string", "self.id is a string");
+  assert.ok(self.id.length > 0);
 
   AddonManager.getAddonByID(self.id, function(addon) {
     if (!addon) {
-      test.fail("did not find addon with self.id");
+      assert.fail("did not find addon with self.id");
     }
     else {
-      test.pass("found addon with self.id");
+      assert.pass("found addon with self.id");
     }
-    test.done();
+
+    done();
   });
 }
+
+require("sdk/test").run(exports);
--- a/addon-sdk/source/test/test-simple-storage.js
+++ b/addon-sdk/source/test/test-simple-storage.js
@@ -1,11 +1,9 @@
-/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * vim:set ts=2 sw=2 sts=2 et filetype=javascript
- * This Source Code Form is subject to the terms of the Mozilla Public
+/* 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 file = require("sdk/io/file");
 const prefs = require("sdk/preferences/service");
 
 const QUOTA_PREF = "extensions.addon-sdk.simple-storage.quota";
 
--- a/addon-sdk/source/test/test-system-runtime.js
+++ b/addon-sdk/source/test/test-system-runtime.js
@@ -1,13 +1,11 @@
 /* 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/.
- */
-
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 var runtime = require("sdk/system/runtime");
 
 exports["test system runtime"] = function(assert) {
   assert.equal(typeof(runtime.inSafeMode), "boolean",
                "inSafeMode is boolean");
   assert.equal(typeof(runtime.OS), "string",
--- a/addon-sdk/source/test/test-text-streams.js
+++ b/addon-sdk/source/test/test-text-streams.js
@@ -1,11 +1,9 @@
-/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * vim:set ts=2 sw=2 sts=2 et filetype=javascript
- * This Source Code Form is subject to the terms of the Mozilla Public
+/* 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 file = require("sdk/io/file");
 const { pathFor } = require("sdk/system");
 const { Loader } = require("sdk/test/loader");
 
 const STREAM_CLOSED_ERROR = "The stream is closed and cannot be used.";
--- a/addon-sdk/source/test/test-traits-core.js
+++ b/addon-sdk/source/test/test-traits-core.js
@@ -1,75 +1,76 @@
 /* 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 ERR_CONFLICT = 'Remaining conflicting property: ',
       ERR_REQUIRED = 'Missing required property: ';
 
-function assertSametrait(test, trait1, trait2) {
+function assertSametrait(assert, trait1, trait2) {
   let names1 = Object.getOwnPropertyNames(trait1),
       names2 = Object.getOwnPropertyNames(trait2);
 
-  test.assertEqual(
+  assert.equal(
     names1.length,
     names2.length,
     'equal traits must have same amount of properties'
   );
 
   for (let i = 0; i < names1.length; i++) {
     let name = names1[i];
-    test.assertNotEqual(
+    assert.notEqual(
       -1,
       names2.indexOf(name),
       'equal traits must contain same named properties: ' + name
     );
-    assertSameDescriptor(test, name, trait1[name], trait2[name]);
+    assertSameDescriptor(assert, name, trait1[name], trait2[name]);
   }
 }
 
-function assertSameDescriptor(test, name, desc1, desc2) {
+function assertSameDescriptor(assert, name, desc1, desc2) {
   if (desc1.conflict || desc2.conflict) {
-    test.assertEqual(
+    assert.equal(
       desc1.conflict,
       desc2.conflict,
       'if one of same descriptors has `conflict` another must have it: '
         + name
     );
-  } else if (desc1.required || desc2.required) {
-    test.assertEqual(
+  }
+  else if (desc1.required || desc2.required) {
+    assert.equal(
       desc1.required,
       desc2.required,
       'if one of same descriptors is has `required` another must have it: '
         + name
     );
-  } else {
-    test.assertEqual(
+  }
+  else {
+    assert.equal(
       desc1.get,
       desc2.get,
       'get must be the same on both descriptors: ' + name
     );
-    test.assertEqual(
+    assert.equal(
       desc1.set,
       desc2.set,
       'set must be the same on both descriptors: ' + name
     );
-    test.assertEqual(
+    assert.equal(
       desc1.value,
       desc2.value,
       'value must be the same on both descriptors: ' + name
     );
-    test.assertEqual(
+    assert.equal(
       desc1.enumerable,
       desc2.enumerable,
       'enumerable must be the same on both descriptors: ' + name
     );
-    test.assertEqual(
+    assert.equal(
       desc1.required,
       desc2.required,
       'value must be the same on both descriptors: ' + name
     );
   }
 }
 
 function Data(value, enumerable, confligurable, writable) {
@@ -118,199 +119,202 @@ function Conflict(name) {
 }
 
 function testMethod() {};
 
 const { trait, compose, resolve, required, override, create } =
   require('sdk/deprecated/traits/core');
 
 
-exports['test:empty trait'] = function(test) {
+exports['test:empty trait'] = function(assert) {
   assertSametrait(
-    test,
+    assert,
     trait({}),
     {}
   );
 };
 
-exports['test:simple trait'] = function(test) {
+exports['test:simple trait'] = function(assert) {
   assertSametrait(
-    test,
+    assert,
     trait({
       a: 0,
       b: testMethod
     }),
     {
       a: Data(0, true, true, true),
       b: Method(testMethod, true, true, true)
     }
   );
 };
 
-exports['test:simple trait with required prop'] = function(test) {
+exports['test:simple trait with required prop'] = function(assert) {
   assertSametrait(
-    test,
+    assert,
     trait({
       a: required,
       b: 1
     }),
     {
       a: Required('a'),
       b: Data(1)
     }
   );
 };
 
-exports['test:ordering of trait properties is irrelevant'] = function(test) {
-  assertSametrait(test,
+exports['test:ordering of trait properties is irrelevant'] = function(assert) {
+  assertSametrait(
+    assert,
     trait({ a: 0, b: 1, c: required }),
     trait({ b: 1, c: required, a: 0 })
   );
 };
 
-exports['test:trait with accessor property'] = function(test) {
+exports['test:trait with accessor property'] = function(assert) {
   let record = { get a() {}, set a(v) {} };
   let get = Object.getOwnPropertyDescriptor(record,'a').get;
   let set = Object.getOwnPropertyDescriptor(record,'a').set;
-  assertSametrait(test,
+  assertSametrait(assert,
     trait(record),
     { a: Accessor(get, set ) }
   );
 };
 
-exports['test:simple composition'] = function(test) {
-  assertSametrait(test,
+exports['test:simple composition'] = function(assert) {
+  assertSametrait(
+    assert,
     compose(
       trait({ a: 0, b: 1 }),
       trait({ c: 2, d: testMethod })
     ),
     {
       a: Data(0),
       b: Data(1),
       c: Data(2),
       d: Method(testMethod)
     }
   );
 };
 
-exports['test:composition with conflict'] = function(test) {
-  assertSametrait(test,
+exports['test:composition with conflict'] = function(assert) {
+  assertSametrait(
+    assert,
     compose(
       trait({ a: 0, b: 1 }),
       trait({ a: 2, c: testMethod })
     ),
     {
       a: Conflict('a'),
       b: Data(1),
       c: Method(testMethod)
     }
   );
 };
 
 exports['test:composition of identical props does not cause conflict'] =
-function(test) {
-  assertSametrait(test,
+function(assert) {
+  assertSametrait(assert,
     compose(
       trait({ a: 0, b: 1 }),
       trait({ a: 0, c: testMethod })
     ),
     {
       a: Data(0),
       b: Data(1),
       c: Method(testMethod) }
   )
 };
 
 exports['test:composition with identical required props'] =
-function(test) {
-  assertSametrait(test,
+function(assert) {
+  assertSametrait(assert,
     compose(
       trait({ a: required, b: 1 }),
       trait({ a: required, c: testMethod })
     ),
     {
       a: Required(),
       b: Data(1),
       c: Method(testMethod)
     }
   );
 };
 
-exports['test:composition satisfying a required prop'] = function (test) {
-  assertSametrait(test,
+exports['test:composition satisfying a required prop'] = function (assert) {
+  assertSametrait(assert,
     compose(
       trait({ a: required, b: 1 }),
       trait({ a: testMethod })
     ),
     {
       a: Method(testMethod),
       b: Data(1)
     }
   );
 };
 
-exports['test:compose is neutral wrt conflicts'] = function (test) {
-  assertSametrait(test,
+exports['test:compose is neutral wrt conflicts'] = function (assert) {
+  assertSametrait(assert,
     compose(
       compose(
         trait({ a: 1 }),
         trait({ a: 2 })
       ),
       trait({ b: 0 })
     ),
     {
       a: Conflict('a'),
       b: Data(0)
     }
   );
 };
 
-exports['test:conflicting prop overrides required prop'] = function (test) {
-  assertSametrait(test,
+exports['test:conflicting prop overrides required prop'] = function (assert) {
+  assertSametrait(assert,
     compose(
       compose(
         trait({ a: 1 }),
         trait({ a: 2 })
       ),
       trait({ a: required })
     ),
     {
       a: Conflict('a')
     }
   );
 };
 
-exports['test:compose is commutative'] = function (test) {
-  assertSametrait(test,
+exports['test:compose is commutative'] = function (assert) {
+  assertSametrait(assert,
     compose(
       trait({ a: 0, b: 1 }),
       trait({ c: 2, d: testMethod })
     ),
     compose(
       trait({ c: 2, d: testMethod }),
       trait({ a: 0, b: 1 })
     )
   );
 };
 
 exports['test:compose is commutative, also for required/conflicting props'] =
-function (test) {
-  assertSametrait(test,
+function (assert) {
+  assertSametrait(assert,
     compose(
       trait({ a: 0, b: 1, c: 3, e: required }),
       trait({ c: 2, d: testMethod })
     ),
     compose(
       trait({ c: 2, d: testMethod }),
       trait({ a: 0, b: 1, c: 3, e: required })
     )
   );
 };
-exports['test:compose is associative'] = function (test) {
-  assertSametrait(test,
+exports['test:compose is associative'] = function (assert) {
+  assertSametrait(assert,
     compose(
       trait({ a: 0, b: 1, c: 3, d: required }),
       compose(
         trait({ c: 3, d: required }),
         trait({ c: 2, d: testMethod, e: 'foo' })
       )
     ),
     compose(
@@ -319,18 +323,18 @@ exports['test:compose is associative'] =
         trait({ c: 3, d: required })
       ),
       trait({ c: 2, d: testMethod, e: 'foo' })
     )
   );
 };
 
 exports['test:diamond import of same prop does not generate conflict'] =
-function (test) {
-  assertSametrait(test,
+function (assert) {
+  assertSametrait(assert,
     compose(
       compose(
         trait({ b: 2 }),
         trait({ a: 1 })
       ),
       compose(
         trait({ c: 3 }),
         trait({ a: 1 })
@@ -342,371 +346,371 @@ function (test) {
       b: Data(2),
       c: Data(3),
       d: Data(4)
     }
   );
 };
 
 exports['test:resolve with empty resolutions has no effect'] =
-function (test) {
-  assertSametrait(test, resolve({}, trait({
+function (assert) {
+  assertSametrait(assert, resolve({}, trait({
     a: 1,
     b: required,
     c: testMethod
   })), {
     a: Data(1),
     b: Required(),
     c: Method(testMethod)
   });
 };
 
-exports['test:resolve: renaming'] = function (test) {
-  assertSametrait(test,
+exports['test:resolve: renaming'] = function (assert) {
+  assertSametrait(assert,
     resolve(
       { a: 'A', c: 'C' },
       trait({ a: 1, b: required, c: testMethod })
     ),
     {
       A: Data(1),
       b: Required(),
       C: Method(testMethod),
       a: Required(),
       c: Required()
     }
   );
 };
 
 exports['test:resolve: renaming to conflicting name causes conflict, order 1']
-= function (test) {
-  assertSametrait(test,
+= function (assert) {
+  assertSametrait(assert,
     resolve(
       { a: 'b'},
       trait({ a: 1, b: 2 })
     ),
     {
       b: Conflict('b'),
       a: Required()
     }
   );
 };
 
 exports['test:resolve: renaming to conflicting name causes conflict, order 2']
-= function (test) {
-  assertSametrait(test,
+= function (assert) {
+  assertSametrait(assert,
     resolve(
       { a: 'b' },
       trait({ b: 2, a: 1 })
     ),
     {
       b: Conflict('b'),
       a: Required()
     }
   );
 };
 
-exports['test:resolve: simple exclusion'] = function (test) {
-  assertSametrait(test,
+exports['test:resolve: simple exclusion'] = function (assert) {
+  assertSametrait(assert,
     resolve(
       { a: undefined },
       trait({ a: 1, b: 2 })
     ),
     {
       a: Required(),
       b: Data(2)
     }
   );
 };
 
-exports['test:resolve: exclusion to "empty" trait'] = function (test) {
-  assertSametrait(test,
+exports['test:resolve: exclusion to "empty" trait'] = function (assert) {
+  assertSametrait(assert,
     resolve(
       { a: undefined, b: undefined },
       trait({ a: 1, b: 2 })
     ),
     {
       a: Required(),
       b: Required()
     }
   );
 };
 
 exports['test:resolve: exclusion and renaming of disjoint props'] =
-function (test) {
-  assertSametrait(test,
+function (assert) {
+  assertSametrait(assert,
     resolve(
       { a: undefined, b: 'c' },
       trait({ a: 1, b: 2 })
     ),
     {
       a: Required(),
       c: Data(2),
       b: Required()
     }
   );
 };
 
 exports['test:resolve: exclusion and renaming of overlapping props'] =
-function (test) {
-  assertSametrait(test,
+function (assert) {
+  assertSametrait(assert,
     resolve(
       { a: undefined, b: 'a' },
       trait({ a: 1, b: 2 })
     ),
     {
       a: Data(2),
       b: Required()
     }
   );
 };
 
 exports['test:resolve: renaming to a common alias causes conflict'] =
-function (test) {
-  assertSametrait(test,
+function (assert) {
+  assertSametrait(assert,
     resolve(
       { a: 'c', b: 'c' },
       trait({ a: 1, b: 2 })
     ),
     {
       c: Conflict('c'),
       a: Required(),
       b: Required()
     }
   );
 };
 
 exports['test:resolve: renaming overrides required target'] =
-function (test) {
-  assertSametrait(test,
+function (assert) {
+  assertSametrait(assert,
     resolve(
       { b: 'a' },
       trait({ a: required, b: 2 })
     ),
     {
       a: Data(2),
       b: Required()
     }
   );
 };
 
 exports['test:resolve: renaming required properties has no effect'] =
-function (test) {
-  assertSametrait(test,
+function (assert) {
+  assertSametrait(assert,
     resolve(
       { b: 'a' },
       trait({ a: 2, b: required })
     ),
     {
       a: Data(2),
       b: Required()
     }
   );
 };
 
 exports['test:resolve: renaming of non-existent props has no effect'] =
-function (test) {
-  assertSametrait(test,
+function (assert) {
+  assertSametrait(assert,
     resolve(
       { a: 'c', d: 'c' },
       trait({ a: 1, b: 2 })
     ),
     {
       c: Data(1),
       b: Data(2),
       a: Required()
     }
   );
 };
 
 exports['test:resolve: exclusion of non-existent props has no effect'] =
-function (test) {
-  assertSametrait(test,
+function (assert) {
+  assertSametrait(assert,
     resolve(
       { b: undefined },
       trait({ a: 1 })
     ),
     {
       a: Data(1)
     }
   );
 };
 
 exports['test:resolve is neutral w.r.t. required properties'] =
-function (test) {
-  assertSametrait(test,
+function (assert) {
+  assertSametrait(assert,
     resolve(
       { a: 'c', b: undefined },
       trait({ a: required, b: required, c: 'foo', d: 1 })
     ),
     {
       a: Required(),
       b: Required(),
       c: Data('foo'),
       d: Data(1)
     }
   );
 };
 
 exports['test:resolve supports swapping of property names, ordering 1'] =
-function (test) {
-  assertSametrait(test,
+function (assert) {
+  assertSametrait(assert,
     resolve(
       { a: 'b', b: 'a' },
       trait({ a: 1, b: 2 })
     ),
     {
       a: Data(2),
       b: Data(1)
     }
   );
 };
 
 exports['test:resolve supports swapping of property names, ordering 2'] =
-function (test) {
-  assertSametrait(test,
+function (assert) {
+  assertSametrait(assert,
     resolve(
       { b: 'a', a: 'b' },
       trait({ a: 1, b: 2 })
     ),
     {
       a: Data(2),
       b: Data(1)
     }
   );
 };
 
 exports['test:resolve supports swapping of property names, ordering 3'] =
-function (test) {
-  assertSametrait(test,
+function (assert) {
+  assertSametrait(assert,
     resolve(
       { b: 'a', a: 'b' },
       trait({ b: 2, a: 1 })
     ),
     {
       a: Data(2),
       b: Data(1)
     }
   );
 };
 
 exports['test:resolve supports swapping of property names, ordering 4'] =
-function (test) {
-  assertSametrait(test,
+function (assert) {
+  assertSametrait(assert,
     resolve(
       { a: 'b', b: 'a' },
       trait({ b: 2, a: 1 })
     ),
     {
       a: Data(2),
       b: Data(1)
     }
   );
 };
 
-exports['test:override of mutually exclusive traits'] = function (test) {
-  assertSametrait(test,
+exports['test:override of mutually exclusive traits'] = function (assert) {
+  assertSametrait(assert,
     override(
       trait({ a: 1, b: 2 }),
       trait({ c: 3, d: testMethod })
     ),
     {
       a: Data(1),
       b: Data(2),
       c: Data(3),
       d: Method(testMethod)
     }
   );
 };
 
 exports['test:override of mutually exclusive traits is compose'] =
-function (test) {
-  assertSametrait(test,
+function (assert) {
+  assertSametrait(assert,
     override(
       trait({ a: 1, b: 2 }),
       trait({ c: 3, d: testMethod })
     ),
     compose(
       trait({ d: testMethod, c: 3 }),
       trait({ b: 2, a: 1 })
     )
   );
 };
 
-exports['test:override of overlapping traits'] = function (test) {
-  assertSametrait(test,
+exports['test:override of overlapping traits'] = function (assert) {
+  assertSametrait(assert,
     override(
       trait({ a: 1, b: 2 }),
       trait({ a: 3, c: testMethod })
     ),
     {
       a: Data(1),
       b: Data(2),
       c: Method(testMethod)
     }
   );
 };
 
-exports['test:three-way override of overlapping traits'] = function (test) {
-  assertSametrait(test,
+exports['test:three-way override of overlapping traits'] = function (assert) {
+  assertSametrait(assert,
     override(
       trait({ a: 1, b: 2 }),
       trait({ b: 4, c: 3 }),
       trait({ a: 3, c: testMethod, d: 5 })
     ),
     {
       a: Data(1),
       b: Data(2),
       c: Data(3),
       d: Data(5)
     }
   );
 };
 
-exports['test:override replaces required properties'] = function (test) {
-  assertSametrait(test,
+exports['test:override replaces required properties'] = function (assert) {
+  assertSametrait(assert,
     override(
       trait({ a: required, b: 2 }),
       trait({ a: 1, c: testMethod })
     ),
     {
       a: Data(1),
       b: Data(2),
       c: Method(testMethod)
     }
   );
 };
 
-exports['test:override is not commutative'] = function (test) {
-  assertSametrait(test,
+exports['test:override is not commutative'] = function (assert) {
+  assertSametrait(assert,
     override(
       trait({ a: 1, b: 2 }),
       trait({ a: 3, c: 4 })
     ),
     {
       a: Data(1),
       b: Data(2),
       c: Data(4)
     }
   );
 
-  assertSametrait(test,
+  assertSametrait(assert,
     override(
       trait({ a: 3, c: 4 }),
       trait({ a: 1, b: 2 })
     ),
     {
       a: Data(3),
       b: Data(2),
       c: Data(4)
     }
   );
 };
 
-exports['test:override is associative'] = function (test) {
-  assertSametrait(test,
+exports['test:override is associative'] = function (assert) {
+  assertSametrait(assert,
     override(
       override(
         trait({ a: 1, b: 2 }),
         trait({ a: 3, c: 4, d: 5 })
       ),
       trait({ a: 6, c: 7, e: 8 })
     ),
     override(
@@ -714,125 +718,133 @@ exports['test:override is associative'] 
       override(
         trait({ a: 3, c: 4, d: 5 }),
         trait({ a: 6, c: 7, e: 8 })
       )
     )
   );
 };
 
-exports['test:create simple'] = function(test) {
+exports['test:create simple'] = function(assert) {
   let o1 = create(
     Object.prototype,
     trait({ a: 1, b: function() { return this.a; } })
   );
 
-  test.assertEqual(
+  assert.equal(
     Object.prototype,
     Object.getPrototypeOf(o1),
     'o1 prototype'
   );
-  test.assertEqual(1, o1.a, 'o1.a');
-  test.assertEqual(1, o1.b(), 'o1.b()');
-  test.assertEqual(
+  assert.equal(1, o1.a, 'o1.a');
+  assert.equal(1, o1.b(), 'o1.b()');
+  assert.equal(
     2,
     Object.getOwnPropertyNames(o1).length,
     'Object.keys(o1).length === 2'
   );
 };
 
-exports['test:create with Array.prototype'] = function(test) {
+exports['test:create with Array.prototype'] = function(assert) {
   let o2 = create(Array.prototype, trait({}));
-  test.assertEqual(
+  assert.equal(
     Array.prototype,
     Object.getPrototypeOf(o2),
     "o2 prototype"
   );
 };
 
 exports['test:exception for incomplete required properties'] =
-function(test) {
+function(assert) {
   try {
     create(Object.prototype, trait({ foo: required }));
-    test.fail('expected create to complain about missing required props');
-  } catch(e) {
-    test.assertEqual(
+    assert.fail('expected create to complain about missing required props');
+  }
+  catch(e) {
+    assert.equal(
       'Error: Missing required property: foo',
       e.toString(),
       'required prop error'
     );
   }
 };
 
-exports['test:exception for unresolved conflicts'] = function(test) {
+exports['test:exception for unresolved conflicts'] = function(assert) {
   try {
     create({}, compose(trait({ a: 0 }), trait({ a: 1 })));
-    test.fail('expected create to complain about unresolved conflicts');
-  } catch(e) {
-    test.assertEqual(
+    assert.fail('expected create to complain about unresolved conflicts');
+  }
+  catch(e) {
+    assert.equal(
       'Error: Remaining conflicting property: a',
       e.toString(),
       'conflicting prop error'
     );
   }
 };
 
 exports['test:verify that required properties are present but undefined'] =
-function(test) {
+function(assert) {
   try {
     let o4 = Object.create(Object.prototype, trait({ foo: required }));
-    test.assertEqual(true, 'foo' in o4, 'required property present');
+    assert.equal(true, 'foo' in o4, 'required property present');
     try {
       let foo = o4.foo;
-      test.fail('access to required property must throw');
-    } catch(e) {
-      test.assertEqual(
+      assert.fail('access to required property must throw');
+    }
+    catch(e) {
+      assert.equal(
         'Error: Missing required property: foo',
         e.toString(),
         'required prop error'
       )
     }
-  } catch(e) {
-    test.fail('did not expect create to complain about required props');
+  }
+  catch(e) {
+    assert.fail('did not expect create to complain about required props');
   }
 };
 
 exports['test:verify that conflicting properties are present'] =
-function(test) {
+function(assert) {
   try {
     let o5 = Object.create(
       Object.prototype,
       compose(trait({ a: 0 }), trait({ a: 1 }))
     );
-    test.assertEqual(true, 'a' in o5, 'conflicting property present');
+    assert.equal(true, 'a' in o5, 'conflicting property present');
     try {
       let a = o5.a; // accessors or data prop
-      test.fail('expected conflicting prop to cause exception');
-    } catch (e) {
-      test.assertEqual(
+      assert.fail('expected conflicting prop to cause exception');
+    }
+    catch (e) {
+      assert.equal(
         'Error: Remaining conflicting property: a',
         e.toString(),
         'conflicting prop access error'
       );
     }
-  } catch(e) {
-    test.fail('did not expect create to complain about conflicting props');
+  }
+  catch(e) {
+    assert.fail('did not expect create to complain about conflicting props');
   }
 };
 
-exports['test diamond with conflicts'] = function(test) {
+exports['test diamond with conflicts'] = function(assert) {
   function makeT1(x) trait({ m: function() { return x; } })
   function makeT2(x) compose(trait({ t2: 'foo' }), makeT1(x))
   function makeT3(x) compose(trait({ t3: 'bar' }), makeT1(x))
 
   let T4 = compose(makeT2(5), makeT3(5));
   try {
     let o = create(Object.prototype, T4);
-    test.fail('expected diamond prop to cause exception');
-  } catch(e) {
-    test.assertEqual(
+    assert.fail('expected diamond prop to cause exception');
+  }
+  catch(e) {
+    assert.equal(
       'Error: Remaining conflicting property: m',
       e.toString(),
       'diamond prop conflict'
     );
   }
 };
 
+require('sdk/test').run(exports);
--- a/addon-sdk/source/test/test-unload.js
+++ b/addon-sdk/source/test/test-unload.js
@@ -1,69 +1,67 @@
-/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et: */
 /* 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 unload = require("sdk/system/unload");
 var { Loader, LoaderWithHookedConsole } = require("sdk/test/loader");
 
-exports.testUnloading = function(test) {
+exports.testUnloading = function(assert) {
   let { loader, messages } = LoaderWithHookedConsole(module);
   var ul = loader.require("sdk/system/unload");
   var unloadCalled = 0;
   function unload() {
     unloadCalled++;
     throw new Error("error");
   }
   ul.when(unload);
 
   // This should be ignored, as we already registered it
   ul.when(unload);
 
   function unload2() { unloadCalled++; }
   ul.when(unload2);
   loader.unload();
-  test.assertEqual(unloadCalled, 2,
+  assert.equal(unloadCalled, 2,
                    "Unloader functions are called on unload.");
-  test.assertEqual(messages.length, 1,
+  assert.equal(messages.length, 1,
                    "One unload handler threw exception 1/2");
-  test.assertEqual(messages[0].type, "exception",
+  assert.equal(messages[0].type, "exception",
                    "One unload handler threw exception 2/2");
 };
 
-exports.testEnsure = function(test) {
-  test.assertRaises(function() { unload.ensure({}); },
-                    "object has no 'unload' property",
-                    "passing obj with no unload prop should fail");
-  test.assertRaises(function() { unload.ensure({}, "destroy"); },
-                    "object has no 'destroy' property",
-                    "passing obj with no custom unload prop should fail");
+exports.testEnsure = function(assert) {
+  assert.throws(function() { unload.ensure({}); },
+                /object has no 'unload' property/,
+                "passing obj with no unload prop should fail");
+  assert.throws(function() { unload.ensure({}, "destroy"); },
+                /object has no 'destroy' property/,
+                "passing obj with no custom unload prop should fail");
 
   var called = 0;
   var obj = {unload: function() { called++; }};
 
   unload.ensure(obj);
   obj.unload();
-  test.assertEqual(called, 1,
+  assert.equal(called, 1,
                    "unload() should be called");
   obj.unload();
-  test.assertEqual(called, 1,
+  assert.equal(called, 1,
                    "unload() should be called only once");
 };
 
 /**
  * Check that destructors are called only once with Traits.
  * - check that public API is calling the destructor and unregister it,
  * - check that composed traits with multiple ensure calls, leads to only
  * one destructor call.
  */
-exports.testEnsureWithTraits = function(test) {
-
+exports.testEnsureWithTraits = function(assert) {
   let { Trait } = require("sdk/deprecated/traits");
   let loader = Loader(module);
   let ul = loader.require("sdk/system/unload");
 
   let called = 0;
   let composedCalled = 0;
   let composedTrait = Trait.compose({
       constructor: function () {
@@ -74,52 +72,51 @@ exports.testEnsureWithTraits = function(
       },
       unload: function unload() {
         composedCalled++;
       }
     });
   let obj = Trait.compose(
     composedTrait.resolve({
       constructor: "_constructor",
-      unload : "_unload" 
+      unload : "_unload"
     }), {
       constructor: function constructor() {
         // Same thing applies here, we need to pass public interface
         ul.ensure(this._public);
         this._constructor();
       },
       unload: function unload() {
         called++;
         this._unload();
       }
     })();
 
   obj.unload();
-  test.assertEqual(called, 1,
+  assert.equal(called, 1,
                    "unload() should be called");
 
-  test.assertEqual(composedCalled, 1,
+  assert.equal(composedCalled, 1,
                    "composed object unload() should be called");
 
   obj.unload();
-  test.assertEqual(called, 1,
+  assert.equal(called, 1,
                    "unload() should be called only once");
-  test.assertEqual(composedCalled, 1,
+  assert.equal(composedCalled, 1,
                    "composed object unload() should be called only once");
 
   loader.unload();
-  test.assertEqual(called, 1,
+  assert.equal(called, 1,
                    "unload() should be called only once, after addon unload");
-  test.assertEqual(composedCalled, 1,
+  assert.equal(composedCalled, 1,
                    "composed object unload() should be called only once, " +
                    "after addon unload");
 };
 
-exports.testEnsureWithTraitsPrivate = function(test) {
-
+exports.testEnsureWithTraitsPrivate = function(assert) {
   let { Trait } = require("sdk/deprecated/traits");
   let loader = Loader(module);
   let ul = loader.require("sdk/system/unload");
 
   let called = 0;
   let privateObj = null;
   let obj = Trait.compose({
       constructor: function constructor() {
@@ -130,33 +127,35 @@ exports.testEnsureWithTraitsPrivate = fu
       },
       _unload: function unload() {
         called++;
         this._unload();
       }
     })();
 
   loader.unload();
-  test.assertEqual(called, 1,
+  assert.equal(called, 1,
                    "unload() should be called");
 
   privateObj._unload();
-  test.assertEqual(called, 1,
+  assert.equal(called, 1,
                    "_unload() should be called only once, after addon unload");
 };
 
-exports.testReason = function (test) {
+exports.testReason = function (assert) {
   var reason = "Reason doesn't actually have to be anything in particular.";
   var loader = Loader(module);
   var ul = loader.require("sdk/system/unload");
   ul.when(function (rsn) {
-    test.assertEqual(rsn, reason,
+    assert.equal(rsn, reason,
                      "when() reason should be reason given to loader");
   });
   var obj = {
     unload: function (rsn) {
-      test.assertEqual(rsn, reason,
+      assert.equal(rsn, reason,
                        "ensure() reason should be reason given to loader");
     }
   };
   ul.ensure(obj);
   loader.unload(reason);
 };
+
+require("sdk/test").run(exports);
--- a/addon-sdk/source/test/test-widget.js
+++ b/addon-sdk/source/test/test-widget.js
@@ -12,359 +12,359 @@ module.metadata = {
 const widgets = require("sdk/widget");
 const { Cc, Ci } = require("chrome");
 const { Loader } = require('sdk/test/loader');
 const url = require("sdk/url");
 const timer = require("sdk/timers");
 const self = require("sdk/self");
 const windowUtils = require("sdk/deprecated/window-utils");
 const { getMostRecentBrowserWindow } = require('sdk/window/utils');
+const { close } = require("sdk/window/helpers");
 
 let jetpackID = "testID";
 try {
   jetpackID = require("sdk/self").id;
 } catch(e) {}
 
 const australis = !!require("sdk/window/utils").getMostRecentBrowserWindow().CustomizableUI;
 
-exports.testConstructor = function(test) {
-  test.waitUntilDone();
-
+exports.testConstructor = function(assert, done) {
   let browserWindow = windowUtils.activeBrowserWindow;
   let doc = browserWindow.document;
   let AddonsMgrListener;
   if (australis) {
     AddonsMgrListener = {
       onInstalling: () => {},
       onInstalled: () => {},
       onUninstalling: () => {},
       onUninstalled: () => {}
     };
-  } else {
+  }
+  else {
     AddonsMgrListener = browserWindow.AddonsMgrListener;
   }
 
   function container() australis ? doc.getElementById("nav-bar") : doc.getElementById("addon-bar");
   function getWidgets() container() ? container().querySelectorAll('[id^="widget\:"]') : [];
   function widgetCount() getWidgets().length;
   let widgetStartCount = widgetCount();
   function widgetNode(index) getWidgets()[index];
 
   // Test basic construct/destroy
   AddonsMgrListener.onInstalling();
   let w = widgets.Widget({ id: "fooID", label: "foo", content: "bar" });
   AddonsMgrListener.onInstalled();
-  test.assertEqual(widgetCount(), widgetStartCount + 1, "panel has correct number of child elements after widget construction");
+  assert.equal(widgetCount(), widgetStartCount + 1, "panel has correct number of child elements after widget construction");
 
   // test widget height
-  test.assertEqual(widgetNode(0).firstChild.boxObject.height, 16, "widget has correct default height");
+  assert.equal(widgetNode(0).firstChild.boxObject.height, 16, "widget has correct default height");
 
   AddonsMgrListener.onUninstalling();
   w.destroy();
   AddonsMgrListener.onUninstalled();
   w.destroy();
-  test.pass("Multiple destroys do not cause an error");
-  test.assertEqual(widgetCount(), widgetStartCount, "panel has correct number of child elements after destroy");
+  assert.pass("Multiple destroys do not cause an error");
+  assert.equal(widgetCount(), widgetStartCount, "panel has correct number of child elements after destroy");
 
   // Test automatic widget destroy on unload
   let loader = Loader(module);
   let widgetsFromLoader = loader.require("sdk/widget");
   let widgetStartCount = widgetCount();
   let w = widgetsFromLoader.Widget({ id: "fooID", label: "foo", content: "bar" });
-  test.assertEqual(widgetCount(), widgetStartCount + 1, "widget has been correctly added");
+  assert.equal(widgetCount(), widgetStartCount + 1, "widget has been correctly added");
   loader.unload();
-  test.assertEqual(widgetCount(), widgetStartCount, "widget has been destroyed on module unload");
+  assert.equal(widgetCount(), widgetStartCount, "widget has been destroyed on module unload");
 
   // Test nothing
-  test.assertRaises(
+  assert.throws(
     function() widgets.Widget({}),
-    "The widget must have a non-empty label property.",
+    /^The widget must have a non-empty label property\.$/,
     "throws on no properties");
 
   // Test no label
-  test.assertRaises(
+  assert.throws(
     function() widgets.Widget({content: "foo"}),
-    "The widget must have a non-empty label property.",
+    /^The widget must have a non-empty label property\.$/,
     "throws on no label");
 
   // Test empty label
-  test.assertRaises(
+  assert.throws(
     function() widgets.Widget({label: "", content: "foo"}),
-    "The widget must have a non-empty label property.",
+    /^The widget must have a non-empty label property\.$/,
     "throws on empty label");
 
   // Test no content or image
-  test.assertRaises(
+  assert.throws(
     function() widgets.Widget({id: "fooID", label: "foo"}),
-    "No content or contentURL property found. Widgets must have one or the other.",
+    /^No content or contentURL property found\. Widgets must have one or the other\.$/,
     "throws on no content");
 
   // Test empty content, no image
-  test.assertRaises(
+  assert.throws(
     function() widgets.Widget({id:"fooID", label: "foo", content: ""}),
-    "No content or contentURL property found. Widgets must have one or the other.",
+    /^No content or contentURL property found\. Widgets must have one or the other\.$/,
     "throws on empty content");
 
   // Test empty image, no content
-  test.assertRaises(
+  assert.throws(
     function() widgets.Widget({id:"fooID", label: "foo", image: ""}),
-    "No content or contentURL property found. Widgets must have one or the other.",
+    /^No content or contentURL property found\. Widgets must have one or the other\.$/,
     "throws on empty content");
 
   // Test empty content, empty image
-  test.assertRaises(
+  assert.throws(
     function() widgets.Widget({id:"fooID", label: "foo", content: "", image: ""}),
-    "No content or contentURL property found. Widgets must have one or the other.",
+    /^No content or contentURL property found. Widgets must have one or the other\.$/,
     "throws on empty content");
 
   // Test duplicated ID
   let duplicateID = widgets.Widget({id: "foo", label: "foo", content: "bar"});
-  test.assertRaises(
+  assert.throws(
     function() widgets.Widget({id: "foo", label: "bar", content: "bar"}),
-    /This widget ID is already used:/,
+    /^This widget ID is already used: foo$/,
     "throws on duplicated id");
   duplicateID.destroy();
 
   // Test Bug 652527
-  test.assertRaises(
+  assert.throws(
     function() widgets.Widget({id: "", label: "bar", content: "bar"}),
-    /You have to specify a unique value for the id property of/,
+    /^You have to specify a unique value for the id property of your widget in order for the application to remember its position\./,
     "throws on falsey id");
 
   // Test duplicate label, different ID
   let w1 = widgets.Widget({id: "id1", label: "foo", content: "bar"});
   let w2 = widgets.Widget({id: "id2", label: "foo", content: "bar"});
   w1.destroy();
   w2.destroy();
 
   // Test position restore on create/destroy/create
   // Create 3 ordered widgets
   let w1 = widgets.Widget({id: "first", label:"first", content: "bar"});
   let w2 = widgets.Widget({id: "second", label:"second", content: "bar"});
   let w3 = widgets.Widget({id: "third", label:"third", content: "bar"});
   // Remove the middle widget
-  test.assertEqual(widgetNode(1).getAttribute("label"), "second", "second widget is the second widget inserted");
+  assert.equal(widgetNode(1).getAttribute("label"), "second", "second widget is the second widget inserted");
   w2.destroy();
-  test.assertEqual(widgetNode(1).getAttribute("label"), "third", "second widget is removed, so second widget is now the third one");
+  assert.equal(widgetNode(1).getAttribute("label"), "third", "second widget is removed, so second widget is now the third one");
   w2 = widgets.Widget({id: "second", label:"second", content: "bar"});
-  test.assertEqual(widgetNode(1).getAttribute("label"), "second", "second widget is created again, at the same location");
+  assert.equal(widgetNode(1).getAttribute("label"), "second", "second widget is created again, at the same location");
   // Cleanup this testcase
   AddonsMgrListener.onUninstalling();
   w1.destroy();
   w2.destroy();
   w3.destroy();
   AddonsMgrListener.onUninstalled();
 
   // Test concurrent widget module instances on addon-bar hiding
   if (!australis) {
     let loader = Loader(module);
     let anotherWidgetsInstance = loader.require("sdk/widget");
-    test.assert(container().collapsed, "UI is hidden when no widgets");
+    assert.ok(container().collapsed, "UI is hidden when no widgets");
     AddonsMgrListener.onInstalling();
     let w1 = widgets.Widget({id: "foo", label: "foo", content: "bar"});
     // Ideally we would let AddonsMgrListener display the addon bar
     // But, for now, addon bar is immediatly displayed by sdk code
     // https://bugzilla.mozilla.org/show_bug.cgi?id=627484
-    test.assert(!container().collapsed, "UI is already visible when we just added the widget");
+    assert.ok(!container().collapsed, "UI is already visible when we just added the widget");
     AddonsMgrListener.onInstalled();
-    test.assert(!container().collapsed, "UI become visible when we notify AddonsMgrListener about end of addon installation");
+    assert.ok(!container().collapsed, "UI become visible when we notify AddonsMgrListener about end of addon installation");
     let w2 = anotherWidgetsInstance.Widget({id: "bar", label: "bar", content: "foo"});
-    test.assert(!container().collapsed, "UI still visible when we add a second widget");
+    assert.ok(!container().collapsed, "UI still visible when we add a second widget");
     AddonsMgrListener.onUninstalling();
     w1.destroy();
     AddonsMgrListener.onUninstalled();
-    test.assert(!container().collapsed, "UI still visible when we remove one of two widgets");
+    assert.ok(!container().collapsed, "UI still visible when we remove one of two widgets");
     AddonsMgrListener.onUninstalling();
     w2.destroy();
-    test.assert(!container().collapsed, "UI is still visible when we have removed all widget but still not called onUninstalled");
+    assert.ok(!container().collapsed, "UI is still visible when we have removed all widget but still not called onUninstalled");
     AddonsMgrListener.onUninstalled();
-    test.assert(container().collapsed, "UI is hidden when we have removed all widget and called onUninstalled");
+    assert.ok(container().collapsed, "UI is hidden when we have removed all widget and called onUninstalled");
   }
   // Helper for testing a single widget.
   // Confirms proper addition and content setup.
   function testSingleWidget(widgetOptions) {
     // We have to display which test is being run, because here we do not
     // use the regular test framework but rather a custom one that iterates
     // the `tests` array.
     console.info("executing: " + widgetOptions.id);
 
     let startCount = widgetCount();
     let widget = widgets.Widget(widgetOptions);
     let node = widgetNode(startCount);
-    test.assert(node, "widget node at index");
-    test.assertEqual(node.tagName, "toolbaritem", "widget element is correct");
-    test.assertEqual(widget.width + "px", node.style.minWidth, "widget width is correct");
-    test.assertEqual(widgetCount(), startCount + 1, "container has correct number of child elements");
+    assert.ok(node, "widget node at index");
+    assert.equal(node.tagName, "toolbaritem", "widget element is correct");
+    assert.equal(widget.width + "px", node.style.minWidth, "widget width is correct");
+    assert.equal(widgetCount(), startCount + 1, "container has correct number of child elements");
     let content = node.firstElementChild;
-    test.assert(content, "found content");
-    test.assertMatches(content.tagName, /iframe|image/, "content is iframe or image");
+    assert.ok(content, "found content");
+    assert.ok(/iframe|image/.test(content.tagName), "content is iframe or image");
     return widget;
   }
 
   // Array of widgets to test
   // and a function to test them.
   let tests = [];
   function nextTest() {
-    test.assertEqual(widgetCount(), 0, "widget in last test property cleaned itself up");
+    assert.equal(widgetCount(), 0, "widget in last test property cleaned itself up");
     if (!tests.length)
-      test.done();
+      done();
     else
       timer.setTimeout(tests.shift(), 0);
   }
   function doneTest() nextTest();
 
   // text widget
   tests.push(function testTextWidget() testSingleWidget({
     id: "text",
     label: "text widget",
     content: "oh yeah",
     contentScript: "self.postMessage(document.body.innerHTML);",
     contentScriptWhen: "end",
     onMessage: function (message) {
-      test.assertEqual(this.content, message, "content matches");
+      assert.equal(this.content, message, "content matches");
       this.destroy();
       doneTest();
     }
   }));
 
   // html widget
   tests.push(function testHTMLWidget() testSingleWidget({
     id: "html",
     label: "html widget",
     content: "<div>oh yeah</div>",
     contentScript: "self.postMessage(document.body.innerHTML);",
     contentScriptWhen: "end",
     onMessage: function (message) {
-      test.assertEqual(this.content, message, "content matches");
+      assert.equal(this.content, message, "content matches");
       this.destroy();
       doneTest();
     }
   }));
 
   // image url widget
   tests.push(function testImageURLWidget() testSingleWidget({
     id: "image",
     label: "image url widget",
     contentURL: require("sdk/self").data.url("test.html"),
     contentScript: "self.postMessage({title: document.title, " +
                    "tag: document.body.firstElementChild.tagName, " +
                    "content: document.body.firstElementChild.innerHTML});",
     contentScriptWhen: "end",
     onMessage: function (message) {
-      test.assertEqual(message.title, "foo", "title matches");
-      test.assertEqual(message.tag, "P", "element matches");
-      test.assertEqual(message.content, "bar", "element content matches");
+      assert.equal(message.title, "foo", "title matches");
+      assert.equal(message.tag, "P", "element matches");
+      assert.equal(message.content, "bar", "element content matches");
       this.destroy();
       doneTest();
     }
   }));
 
   // web uri widget
   tests.push(function testWebURIWidget() testSingleWidget({
     id: "web",
     label: "web uri widget",
     contentURL: require("sdk/self").data.url("test.html"),
     contentScript: "self.postMessage({title: document.title, " +
                    "tag: document.body.firstElementChild.tagName, " +
                    "content: document.body.firstElementChild.innerHTML});",
     contentScriptWhen: "end",
     onMessage: function (message) {
-      test.assertEqual(message.title, "foo", "title matches");
-      test.assertEqual(message.tag, "P", "element matches");
-      test.assertEqual(message.content, "bar", "element content matches");
+      assert.equal(message.title, "foo", "title matches");
+      assert.equal(message.tag, "P", "element matches");
+      assert.equal(message.content, "bar", "element content matches");
       this.destroy();
       doneTest();
     }
   }));
 
   // event: onclick + content
   tests.push(function testOnclickEventContent() testSingleWidget({
     id: "click",
     label: "click test widget - content",
     content: "<div id='me'>foo</div>",
     contentScript: "var evt = new MouseEvent('click', {button: 0});" +
                    "document.getElementById('me').dispatchEvent(evt);",
     contentScriptWhen: "end",
     onClick: function() {
-      test.pass("onClick called");
+      assert.pass("onClick called");
       this.destroy();
       doneTest();
     }
   }));
 
   // event: onmouseover + content
   tests.push(function testOnmouseoverEventContent() testSingleWidget({
     id: "mouseover",
     label: "mouseover test widget - content",
     content: "<div id='me'>foo</div>",
     contentScript: "var evt = new MouseEvent('mouseover'); " +
                    "document.getElementById('me').dispatchEvent(evt);",
     contentScriptWhen: "end",
     onMouseover: function() {
-      test.pass("onMouseover called");
+      assert.pass("onMouseover called");
       this.destroy();
       doneTest();
     }
   }));
 
   // event: onmouseout + content
   tests.push(function testOnmouseoutEventContent() testSingleWidget({
     id: "mouseout",
     label: "mouseout test widget - content",
     content: "<div id='me'>foo</div>",
     contentScript: "var evt = new MouseEvent('mouseout');" +
                    "document.getElementById('me').dispatchEvent(evt);",
     contentScriptWhen: "end",
     onMouseout: function() {
-      test.pass("onMouseout called");
+      assert.pass("onMouseout called");
       this.destroy();
       doneTest();
     }
   }));
 
   // event: onclick + image
   tests.push(function testOnclickEventImage() testSingleWidget({
     id: "click",
     label: "click test widget - image",
     contentURL: require("sdk/self").data.url("moz_favicon.ico"),
     contentScript: "var evt = new MouseEvent('click'); " +
                    "document.body.firstElementChild.dispatchEvent(evt);",
     contentScriptWhen: "end",
     onClick: function() {
-      test.pass("onClick called");
+      assert.pass("onClick called");
       this.destroy();
       doneTest();
     }
   }));
 
   // event: onmouseover + image
   tests.push(function testOnmouseoverEventImage() testSingleWidget({
     id: "mouseover",
     label: "mouseover test widget - image",
     contentURL: require("sdk/self").data.url("moz_favicon.ico"),
     contentScript: "var evt = new MouseEvent('mouseover');" +
                    "document.body.firstElementChild.dispatchEvent(evt);",
     contentScriptWhen: "end",
     onMouseover: function() {
-      test.pass("onMouseover called");
+      assert.pass("onMouseover called");
       this.destroy();
       doneTest();
     }
   }));
 
   // event: onmouseout + image
   tests.push(function testOnmouseoutEventImage() testSingleWidget({
     id: "mouseout",
     label: "mouseout test widget - image",
     contentURL: require("sdk/self").data.url("moz_favicon.ico"),
     contentScript: "var evt = new MouseEvent('mouseout'); " +
                    "document.body.firstElementChild.dispatchEvent(evt);",
     contentScriptWhen: "end",
     onMouseout: function() {
-      test.pass("onMouseout called");
+      assert.pass("onMouseout called");
       this.destroy();
       doneTest();
     }
   }));
 
   // test multiple widgets
   tests.push(function testMultipleWidgets() {
     let w1 = widgets.Widget({id: "first", label: "first widget", content: "first content"});
@@ -385,17 +385,17 @@ exports.testConstructor = function(test)
     contentScript: "self.postMessage(1)",
     contentScriptWhen: "ready",
     onMessage: function(message) {
       if (!this.flag) {
         this.content = "<div id='me'>bar</div>";
         this.flag = 1;
       }
       else {
-        test.assertEqual(this.content, "<div id='me'>bar</div>");
+        assert.equal(this.content, "<div id='me'>bar</div>", 'content is as expected');
         this.destroy();
         doneTest();
       }
     }
   }));
 
   // test updating widget contentURL
   let url1 = "data:text/html;charset=utf-8,<body>foodle</body>";
@@ -404,110 +404,110 @@ exports.testConstructor = function(test)
   tests.push(function testUpdatingContentURL() testSingleWidget({
     id: "content",
     label: "content update test widget",
     contentURL: url1,
     contentScript: "self.postMessage(document.location.href);",
     contentScriptWhen: "end",
     onMessage: function(message) {
       if (!this.flag) {
-        test.assertEqual(this.contentURL.toString(), url1);
-        test.assertEqual(message, url1);
+        assert.equal(this.contentURL.toString(), url1);
+        assert.equal(message, url1);
         this.contentURL = url2;
         this.flag = 1;
       }
       else {
-        test.assertEqual(this.contentURL.toString(), url2);
-        test.assertEqual(message, url2);
+        assert.equal(this.contentURL.toString(), url2);
+        assert.equal(message, url2);
         this.destroy();
         doneTest();
       }
     }
   }));
 
   // test tooltip
   tests.push(function testTooltip() testSingleWidget({
     id: "text",
     label: "text widget",
     content: "oh yeah",
     tooltip: "foo",
     contentScript: "self.postMessage(1)",
     contentScriptWhen: "ready",
     onMessage: function(message) {
-      test.assertEqual(this.tooltip, "foo", "tooltip matches");
+      assert.equal(this.tooltip, "foo", "tooltip matches");
       this.destroy();
       doneTest();
     }
   }));
 
   // test tooltip fallback to label
   tests.push(function testTooltipFallback() testSingleWidget({
     id: "fallback",
     label: "fallback",
     content: "oh yeah",
     contentScript: "self.postMessage(1)",
     contentScriptWhen: "ready",
     onMessage: function(message) {
-      test.assertEqual(this.tooltip, this.label, "tooltip fallbacks to label");
+      assert.equal(this.tooltip, this.label, "tooltip fallbacks to label");
       this.destroy();
       doneTest();
     }
   }));
 
   // test updating widget tooltip
   let updated = false;
   tests.push(function testUpdatingTooltip() testSingleWidget({
     id: "tooltip",
     label: "tooltip update test widget",
     tooltip: "foo",
     content: "<div id='me'>foo</div>",
     contentScript: "self.postMessage(1)",
     contentScriptWhen: "ready",
     onMessage: function(message) {
       this.tooltip = "bar";
-      test.assertEqual(this.tooltip, "bar", "tooltip gets updated");
+      assert.equal(this.tooltip, "bar", "tooltip gets updated");
       this.destroy();
       doneTest();
     }
   }));
 
   // test allow attribute
   tests.push(function testDefaultAllow() testSingleWidget({
     id: "allow",
     label: "allow.script attribute",
     content: "<script>document.title = 'ok';</script>",
     contentScript: "self.postMessage(document.title)",
     onMessage: function(message) {
-      test.assertEqual(message, "ok", "scripts are evaluated by default");
+      assert.equal(message, "ok", "scripts are evaluated by default");
       this.destroy();
       doneTest();
     }
   }));
 
   tests.push(function testExplicitAllow() testSingleWidget({
     id: "allow",
     label: "allow.script attribute",
     allow: {script: true},
     content: "<script>document.title = 'ok';</script>",
     contentScript: "self.postMessage(document.title)",
     onMessage: function(message) {
-      test.assertEqual(message, "ok", "scripts are evaluated when we want to");
+      assert.equal(message, "ok", "scripts are evaluated when we want to");
       this.destroy();
       doneTest();
     }
   }));
 
   tests.push(function testExplicitDisallow() testSingleWidget({
     id: "allow",
     label: "allow.script attribute",
     content: "<script>document.title = 'ok';</script>",
     allow: {script: false},
     contentScript: "self.postMessage(document.title)",
     onMessage: function(message) {
-      test.assertNotEqual(message, "ok", "scripts aren't evaluated when " +
+      assert.notEqual(message, "ok", "scripts aren't evaluated when " +
                                          "explicitly blocked it");
       this.destroy();
       doneTest();
     }
   }));
 
   // test multiple windows
   tests.push(function testMultipleWindows() {
@@ -517,30 +517,28 @@ exports.testConstructor = function(test)
       let browserWindow = e.target.defaultView;
       let doc = browserWindow.document;
       function container() australis ? doc.getElementById("nav-bar") : doc.getElementById("addon-bar");
       function widgetCount2() container() ? container().querySelectorAll('[id^="widget\:"]').length : 0;
       let widgetStartCount2 = widgetCount2();
 
       let w1Opts = {id:"first", label: "first widget", content: "first content"};
       let w1 = testSingleWidget(w1Opts);
-      test.assertEqual(widgetCount2(), widgetStartCount2 + 1, "2nd window has correct number of child elements after first widget");
+      assert.equal(widgetCount2(), widgetStartCount2 + 1, "2nd window has correct number of child elements after first widget");
 
       let w2Opts = {id:"second", label: "second widget", content: "second content"};
       let w2 = testSingleWidget(w2Opts);
-      test.assertEqual(widgetCount2(), widgetStartCount2 + 2, "2nd window has correct number of child elements after second widget");
+      assert.equal(widgetCount2(), widgetStartCount2 + 2, "2nd window has correct number of child elements after second widget");
 
       w1.destroy();
-      test.assertEqual(widgetCount2(), widgetStartCount2 + 1, "2nd window has correct number of child elements after first destroy");
+      assert.equal(widgetCount2(), widgetStartCount2 + 1, "2nd window has correct number of child elements after first destroy");
       w2.destroy();
-      test.assertEqual(widgetCount2(), widgetStartCount2, "2nd window has correct number of child elements after second destroy");
+      assert.equal(widgetCount2(), widgetStartCount2, "2nd window has correct number of child elements after second destroy");
 
-      closeBrowserWindow(browserWindow, function() {
-        doneTest();
-      });
+      close(browserWindow).then(doneTest);
     }});
   });
 
   // test window closing
   tests.push(function testWindowClosing() {
     // 1/ Create a new widget
     let w1Opts = {
       id:"first",
@@ -549,117 +547,115 @@ exports.testConstructor = function(test)
       contentScript: "self.port.on('event', function () self.port.emit('event'))"
     };
     let widget = testSingleWidget(w1Opts);
     let windows = require("sdk/windows").browserWindows;
 
     // 2/ Retrieve a WidgetView for the initial browser window
     let acceptDetach = false;
     let mainView = widget.getView(windows.activeWindow);
-    test.assert(mainView, "Got first widget view");
+    assert.ok(mainView, "Got first widget view");
     mainView.on("detach", function () {
       // 8/ End of our test. Accept detach event only when it occurs after
       // widget.destroy()
       if (acceptDetach)
         doneTest();
       else
-        test.fail("View on initial window should not be destroyed");
+        assert.fail("View on initial window should not be destroyed");
     });
     mainView.port.on("event", function () {
       // 7/ Receive event sent during 6/ and cleanup our test
       acceptDetach = true;
       widget.destroy();
     });
 
     // 3/ First: open a new browser window
     windows.open({
       url: "about:blank",
       onOpen: function(window) {
         // 4/ Retrieve a WidgetView for this new window
         let view = widget.getView(window);
-        test.assert(view, "Got second widget view");
+        assert.ok(view, "Got second widget view");
         view.port.on("event", function () {
-          test.fail("We should not receive event on the detach view");
+          assert.fail("We should not receive event on the detach view");
         });
         view.on("detach", function () {
           // The related view is destroyed
           // 6/ Send a custom event
-          test.assertRaises(function () {
+          assert.throws(function () {
               view.port.emit("event");
             },
-            /The widget has been destroyed and can no longer be used./,
+            /^The widget has been destroyed and can no longer be used.$/,
             "emit on a destroyed view should throw");
           widget.port.emit("event");
         });
 
         // 5/ Destroy this window
         window.close();
       }
     });
   });
 
   if (!australis) {
     tests.push(function testAddonBarHide() {
       const tabBrowser = require("sdk/deprecated/tab-browser");
 
       // Hide the addon-bar
       browserWindow.setToolbarVisibility(container(), false);
-      test.assert(container().collapsed,
-                  "1st window starts with an hidden addon-bar");
+      assert.ok(container().collapsed,
+                "1st window starts with an hidden addon-bar");
 
       // Then open a browser window and verify that the addon-bar remains hidden
       tabBrowser.addTab("about:blank", { inNewWindow: true, onLoad: function(e) {
         let browserWindow2 = e.target.defaultView;
         let doc2 = browserWindow2.document;
         function container2() doc2.getElementById("addon-bar");
         function widgetCount2() container2() ? container2().childNodes.length : 0;
         let widgetStartCount2 = widgetCount2();
-        test.assert(container2().collapsed,
-                    "2nd window starts with an hidden addon-bar");
+        assert.ok(container2().collapsed,
+                  "2nd window starts with an hidden addon-bar");
 
         let w1Opts = {id:"first", label: "first widget", content: "first content"};
         let w1 = testSingleWidget(w1Opts);
-        test.assertEqual(widgetCount2(), widgetStartCount2 + 1,
-                         "2nd window has correct number of child elements after" +
-                         "widget creation");
+        assert.equal(widgetCount2(), widgetStartCount2 + 1,
+                     "2nd window has correct number of child elements after" +
+                     "widget creation");
         w1.destroy();
-        test.assertEqual(widgetCount2(), widgetStartCount2,
-                         "2nd window has correct number of child elements after" +
-                         "widget destroy");
+        assert.equal(widgetCount2(), widgetStartCount2,
+                     "2nd window has correct number of child elements after" +
+                     "widget destroy");
 
-        test.assert(container().collapsed, "1st window has an hidden addon-bar");
-        test.assert(container2().collapsed, "2nd window has an hidden addon-bar");
+        assert.ok(container().collapsed, "1st window has an hidden addon-bar");
+        assert.ok(container2().collapsed, "2nd window has an hidden addon-bar");
 
         // Reset addon-bar visibility before exiting this test
         browserWindow.setToolbarVisibility(container(), true);
 
-        closeBrowserWindow(browserWindow2, function() {
-          doneTest();
-        });
+        close(browserWindow2).then(doneTest);
       }});
     });
   }
 
   // test widget.width
   tests.push(function testWidgetWidth() testSingleWidget({
     id: "text",
     label: "test widget.width",
     content: "test width",
     width: 200,
     contentScript: "self.postMessage(1)",
     contentScriptWhen: "ready",
     onMessage: function(message) {
-      test.assertEqual(this.width, 200);
+      assert.equal(this.width, 200, 'width is 200');
 
       let node = widgetNode(0);
-      test.assertEqual(this.width, node.style.minWidth.replace("px", ""));
-      test.assertEqual(this.width, node.firstElementChild.style.width.replace("px", ""));
+      assert.equal(this.width, node.style.minWidth.replace("px", ""));
+      assert.equal(this.width, node.firstElementChild.style.width.replace("px", ""));
       this.width = 300;
-      test.assertEqual(this.width, node.style.minWidth.replace("px", ""));
-      test.assertEqual(this.width, node.firstElementChild.style.width.replace("px", ""));
+      assert.equal(this.width, node.style.minWidth.replace("px", ""));
+      assert.equal(this.width, node.firstElementChild.style.width.replace("px", ""));
 
       this.destroy();
       doneTest();
     }
   }));
 
   // test click handler not respond to right-click
   let clickCount = 0;
@@ -677,218 +673,205 @@ exports.testConstructor = function(test)
                    "evt = new MouseEvent('click', {button: 2});" +
                    "document.getElementById('me').dispatchEvent(evt); " +
                    // Mouseover
                    "evt = new MouseEvent('mouseover');" +
                    "document.getElementById('me').dispatchEvent(evt);",
     contentScriptWhen: "end",
     onClick: function() clickCount++,
     onMouseover: function() {
-      test.assertEqual(clickCount, 1, "only left click was sent to click handler");
+      assert.equal(clickCount, 1, "only left click was sent to click handler");
       this.destroy();
       doneTest();
     }
   }));
 
   // kick off test execution
   doneTest();
 };
 
-exports.testWidgetWithValidPanel = function(test) {
+exports.testWidgetWithValidPanel = function(assert, done) {
   const widgets = require("sdk/widget");
 
   let widget1 = widgets.Widget({
     id: "panel1",
     label: "panel widget 1",
     content: "<div id='me'>foo</div>",
     contentScript: "var evt = new MouseEvent('click', {button: 0});" +
                    "document.body.dispatchEvent(evt);",
     contentScriptWhen: "end",
     panel: require("sdk/panel").Panel({
       contentURL: "data:text/html;charset=utf-8,<body>Look ma, a panel!</body>",
       onShow: function() {
         let { document } = getMostRecentBrowserWindow();
         let widgetEle = document.getElementById("widget:" + jetpackID + "-" + widget1.id);
         let panelEle = document.getElementById('mainPopupSet').lastChild;
         // See bug https://bugzilla.mozilla.org/show_bug.cgi?id=859592
-        test.assertEqual(panelEle.getAttribute("type"), "arrow", 'the panel is a arrow type');
-        test.assertStrictEqual(panelEle.anchorNode, widgetEle, 'the panel is properly anchored to the widget');
+        assert.equal(panelEle.getAttribute("type"), "arrow", 'the panel is a arrow type');
+        assert.strictEqual(panelEle.anchorNode, widgetEle, 'the panel is properly anchored to the widget');
 
         widget1.destroy();
-        test.pass("panel displayed on click");
-        test.done();
+        assert.pass("panel displayed on click");
+        done();
       }
     })
   });
-  test.waitUntilDone();
 };
 
-exports.testWidgetWithInvalidPanel = function(test) {
+exports.testWidgetWithInvalidPanel = function(assert) {
   const widgets = require("sdk/widget");
-  test.assertRaises(
+  assert.throws(
     function() {
       widgets.Widget({
         id: "panel2",
         label: "panel widget 2",
         panel: {}
       });
     },
-    "The option \"panel\" must be one of the following types: null, undefined, object",
-    "widget.panel must be a Panel object"
-  );
+    /^The option \"panel\" must be one of the following types: null, undefined, object$/,
+    "widget.panel must be a Panel object");
 };
 
-exports.testPanelWidget3 = function testPanelWidget3(test) {
+exports.testPanelWidget3 = function testPanelWidget3(assert, done) {
   const widgets = require("sdk/widget");
   let onClickCalled = false;
   let widget3 = widgets.Widget({
     id: "panel3",
     label: "panel widget 3",
     content: "<div id='me'>foo</div>",
     contentScript: "var evt = new MouseEvent('click', {button: 0});" +
                    "document.body.firstElementChild.dispatchEvent(evt);",
     contentScriptWhen: "end",
     onClick: function() {
       onClickCalled = true;
       this.panel.show();
     },
     panel: require("sdk/panel").Panel({
       contentURL: "data:text/html;charset=utf-8,<body>Look ma, a panel!</body>",
       onShow: function() {
-        test.assert(
+        assert.ok(
           onClickCalled,
-          "onClick called on click for widget with both panel and onClick"
-        );
+          "onClick called on click for widget with both panel and onClick");
         widget3.destroy();
-        test.done();
+        done();
       }
     })
   });
-  test.waitUntilDone();
 };
 
-exports.testWidgetMessaging = function testWidgetMessaging(test) {
-  test.waitUntilDone();
+exports.testWidgetMessaging = function testWidgetMessaging(assert, done) {
   let origMessage = "foo";
   const widgets = require("sdk/widget");
   let widget = widgets.Widget({
     id: "foo",
     label: "foo",
     content: "<bar>baz</bar>",
     contentScriptWhen: "end",
     contentScript: "self.on('message', function(data) { self.postMessage(data); }); self.postMessage('ready');",
     onMessage: function(message) {
       if (message == "ready")
         widget.postMessage(origMessage);
       else {
-        test.assertEqual(origMessage, message);
+        assert.equal(origMessage, message);
         widget.destroy();
-        test.done();
+        done();
       }
     }
   });
 };
 
-exports.testWidgetViews = function testWidgetViews(test) {
-  test.waitUntilDone();
+exports.testWidgetViews = function testWidgetViews(assert, done) {
   const widgets = require("sdk/widget");
   let widget = widgets.Widget({
     id: "foo",
     label: "foo",
     content: "<bar>baz</bar>",
     contentScriptWhen: "ready",
     contentScript: "self.on('message', function(data) self.postMessage(data)); self.postMessage('ready')",
     onAttach: function(view) {
-      test.pass("WidgetView created");
+      assert.pass("WidgetView created");
       view.on("message", function () {
-        test.pass("Got message in WidgetView");
+        assert.pass("Got message in WidgetView");
         widget.destroy();
       });
       view.on("detach", function () {
-        test.pass("WidgetView destroyed");
-        test.done();
+        assert.pass("WidgetView destroyed");
+        done();
       });
     }
   });
-
 };
 
-exports.testWidgetViewsUIEvents = function testWidgetViewsUIEvents(test) {
-  test.waitUntilDone();
+exports.testWidgetViewsUIEvents = function testWidgetViewsUIEvents(assert, done) {
   const widgets = require("sdk/widget");
   let view = null;
   let widget = widgets.Widget({
     id: "foo",
     label: "foo",
     content: "<div id='me'>foo</div>",
     contentScript: "var evt = new MouseEvent('click', {button: 0});" +
                    "document.getElementById('me').dispatchEvent(evt);",
     contentScriptWhen: "ready",
     onAttach: function(attachView) {
       view = attachView;
-      test.pass("Got attach event");
+      assert.pass("Got attach event");
     },
     onClick: function (eventView) {
-      test.assertEqual(view, eventView,
+      assert.equal(view, eventView,
                          "event first argument is equal to the WidgetView");
       let view2 = widget.getView(require("sdk/windows").browserWindows.activeWindow);
-      test.assertEqual(view, view2,
+      assert.equal(view, view2,
                          "widget.getView return the same WidgetView");
       widget.destroy();
-      test.done();
+      done();
     }
   });
 };
 
-exports.testWidgetViewsCustomEvents = function testWidgetViewsCustomEvents(test) {
-  test.waitUntilDone();
+exports.testWidgetViewsCustomEvents = function testWidgetViewsCustomEvents(assert, done) {
   const widgets = require("sdk/widget");
   let widget = widgets.Widget({
     id: "foo",
     label: "foo",
     content: "<div id='me'>foo</div>",
     contentScript: "self.port.emit('event', 'ok');",
     contentScriptWhen: "ready",
     onAttach: function(view) {
       view.port.on("event", function (data) {
-        test.assertEqual(data, "ok",
+        assert.equal(data, "ok",
                          "event argument is valid on WidgetView");
       });
     },
   });
   widget.port.on("event", function (data) {
-    test.assertEqual(data, "ok",
-                     "event argument is valid on Widget");
+    assert.equal(data, "ok", "event argument is valid on Widget");
     widget.destroy();
-    test.done();
+    done();
   });
 };
 
-exports.testWidgetViewsTooltip = function testWidgetViewsTooltip(test) {
-  test.waitUntilDone();
+exports.testWidgetViewsTooltip = function testWidgetViewsTooltip(assert, done) {
   const widgets = require("sdk/widget");
 
   let widget = new widgets.Widget({
     id: "foo",
     label: "foo",
     content: "foo"
   });
   let view = widget.getView(require("sdk/windows").browserWindows.activeWindow);
   widget.tooltip = null;
-  test.assertEqual(view.tooltip, "foo",
-                   "view tooltip defaults to base widget label");
-  test.assertEqual(widget.tooltip, "foo",
-                   "tooltip defaults to base widget label");
+  assert.equal(view.tooltip, "foo",
+               "view tooltip defaults to base widget label");
+  assert.equal(widget.tooltip, "foo",
+               "tooltip defaults to base widget label");
   widget.destroy();
-  test.done();
+  done();
 };
 
-exports.testWidgetMove = function testWidgetMove(test) {
-  test.waitUntilDone();
-
+exports.testWidgetMove = function testWidgetMove(assert, done) {
   let windowUtils = require("sdk/deprecated/window-utils");
   let widgets = require("sdk/widget");
 
   let browserWindow = windowUtils.activeBrowserWindow;
   let doc = browserWindow.document;
 
   let label = "unique-widget-label";
   let origMessage = "message after node move";
@@ -898,137 +881,128 @@ exports.testWidgetMove = function testWi
     id: "foo",
     label: label,
     content: "<bar>baz</bar>",
     contentScriptWhen: "ready",
     contentScript: "self.on('message', function(data) { self.postMessage(data); }); self.postMessage('ready');",
     onMessage: function(message) {
       if (message == "ready") {
         if (!gotFirstReady) {
-          test.pass("Got first ready event");
+          assert.pass("Got first ready event");
           let widgetNode = doc.querySelector('toolbaritem[label="' + label + '"]');
           let parent = widgetNode.parentNode;
           parent.insertBefore(widgetNode, parent.firstChild);
           gotFirstReady = true;
-        } else {
-          test.pass("Got second ready event");
+        }
+        else {
+          assert.pass("Got second ready event");
           widget.postMessage(origMessage);
         }
       }
       else {
-        test.assertEqual(origMessage, message, "Got message after node move");
+        assert.equal(origMessage, message, "Got message after node move");
         widget.destroy();
-        test.done();
+        done();
       }
     }
   });
 };
 
 /*
 The bug is exhibited when a widget with HTML content has it's content
 changed to new HTML content with a pound in it. Because the src of HTML
 content is converted to a data URI, the underlying iframe doesn't
 consider the content change a navigation change, so doesn't load
 the new content.
 */
-exports.testWidgetWithPound = function testWidgetWithPound(test) {
-  test.waitUntilDone();
-
+exports.testWidgetWithPound = function testWidgetWithPound(assert, done) {
   function getWidgetContent(widget) {
     let windowUtils = require("sdk/deprecated/window-utils");
     let browserWindow = windowUtils.activeBrowserWindow;
     let doc = browserWindow.document;
     let widgetNode = doc.querySelector('toolbaritem[label="' + widget.label + '"]');
-    test.assert(widgetNode, 'found widget node in the front-end');
+    assert.ok(widgetNode, 'found widget node in the front-end');
     return widgetNode.firstChild.contentDocument.body.innerHTML;
   }
 
   let widgets = require("sdk/widget");
   let count = 0;
   let widget = widgets.Widget({
     id: "1",
     label: "foo",
     content: "foo",
     contentScript: "window.addEventListener('load', self.postMessage, false);",
     onMessage: function() {
       count++;
       if (count == 1) {
         widget.content = "foo#";
       }
       else {
-        test.assertEqual(getWidgetContent(widget), "foo#", "content updated to pound?");
+        assert.equal(getWidgetContent(widget), "foo#", "content updated to pound?");
         widget.destroy();
-        test.done();
+        done();
       }
     }
   });
 };
 
-exports.testContentScriptOptionsOption = function(test) {
-  test.waitUntilDone();
-
+exports.testContentScriptOptionsOption = function(assert, done) {
   let widget = require("sdk/widget").Widget({
       id: "fooz",
       label: "fooz",
       content: "fooz",
       contentScript: "self.postMessage( [typeof self.options.d, self.options] );",
       contentScriptWhen: "end",
       contentScriptOptions: {a: true, b: [1,2,3], c: "string", d: function(){ return 'test'}},
       onMessage: function(msg) {
-        test.assertEqual( msg[0], 'undefined', 'functions are stripped from contentScriptOptions' );
-        test.assertEqual( typeof msg[1], 'object', 'object as contentScriptOptions' );
-        test.assertEqual( msg[1].a, true, 'boolean in contentScriptOptions' );
-        test.assertEqual( msg[1].b.join(), '1,2,3', 'array and numbers in contentScriptOptions' );
-        test.assertEqual( msg[1].c, 'string', 'string in contentScriptOptions' );
+        assert.equal( msg[0], 'undefined', 'functions are stripped from contentScriptOptions' );
+        assert.equal( typeof msg[1], 'object', 'object as contentScriptOptions' );
+        assert.equal( msg[1].a, true, 'boolean in contentScriptOptions' );
+        assert.equal( msg[1].b.join(), '1,2,3', 'array and numbers in contentScriptOptions' );
+        assert.equal( msg[1].c, 'string', 'string in contentScriptOptions' );
         widget.destroy();
-        test.done();
+        done();
       }
     });
 };
 
-exports.testOnAttachWithoutContentScript = function(test) {
-  test.waitUntilDone();
-
+exports.testOnAttachWithoutContentScript = function(assert, done) {
   let widget = require("sdk/widget").Widget({
       id: "onAttachNoCS",
       label: "onAttachNoCS",
       content: "onAttachNoCS",
       onAttach: function (view) {
-        test.pass("received attach event");
+        assert.pass("received attach event");
         widget.destroy();
-        test.done();
+        done();
       }
     });
 };
 
-exports.testPostMessageOnAttach = function(test) {
-  test.waitUntilDone();
-
+exports.testPostMessageOnAttach = function(assert, done) {
   let widget = require("sdk/widget").Widget({
       id: "onAttach",
       label: "onAttach",
       content: "onAttach",
       // 1) Send a message immediatly after `attach` event
       onAttach: function (view) {
         view.postMessage("ok");
       },
       // 2) Listen to it and forward it back to the widget
       contentScript: "self.on('message', self.postMessage);",
       // 3) Listen to this forwarded message
       onMessage: function (msg) {
-        test.assertEqual( msg, "ok", "postMessage works on `attach` event");
+        assert.equal( msg, "ok", "postMessage works on `attach` event");
         widget.destroy();
-        test.done();
+        done();
       }
     });
 };
 
-exports.testPostMessageOnLocationChange = function(test) {
-  test.waitUntilDone();
-
+exports.testPostMessageOnLocationChange = function(assert, done) {
   let attachEventCount = 0;
   let messagesCount = 0;
   let widget = require("sdk/widget").Widget({
       id: "onLocationChange",
       label: "onLocationChange",
       content: "onLocationChange",
       contentScript: "new " + function ContentScriptScope() {
         // Emit an event when content script is applied in order to know when
@@ -1036,57 +1010,53 @@ exports.testPostMessageOnLocationChange 
         self.postMessage("ready");
         // And forward any incoming message back to the widget to see if
         // messaging is working on 2nd document
         self.on("message", self.postMessage);
       },
       onMessage: function (msg) {
         messagesCount++;
         if (messagesCount == 1) {
-          test.assertEqual(msg, "ready", "First document is loaded");
+          assert.equal(msg, "ready", "First document is loaded");
           widget.content = "location changed";
         }
         else if (messagesCount == 2) {
-          test.assertEqual(msg, "ready", "Second document is loaded");
+          assert.equal(msg, "ready", "Second document is loaded");
           widget.postMessage("ok");
         }
         else if (messagesCount == 3) {
-          test.assertEqual(msg, "ok",
-                           "We receive the message sent to the 2nd document");
+          assert.equal(msg, "ok",
+                       "We receive the message sent to the 2nd document");
           widget.destroy();
-          test.done();
+          done();
         }
       }
     });
 };
 
-exports.testSVGWidget = function(test) {
-  test.waitUntilDone();
-
+exports.testSVGWidget = function(assert, done) {
   // use of capital SVG here is intended, that was failing..
   let SVG_URL = self.data.url("mofo_logo.SVG");
 
   let widget = require("sdk/widget").Widget({
     id: "mozilla-svg-logo",
     label: "moz foundation logo",
     contentURL: SVG_URL,
     contentScript: "self.postMessage({count: window.document.images.length, src: window.document.images[0].src});",
     onMessage: function(data) {
       widget.destroy();
-      test.assertEqual(data.count, 1, 'only one image');
-      test.assertEqual(data.src, SVG_URL, 'only one image');
-      test.done();
+      assert.equal(data.count, 1, 'only one image');
+      assert.equal(data.src, SVG_URL, 'only one image');
+      done();
     }
   });
 };
 
 if (!australis) {
-  exports.testNavigationBarWidgets = function testNavigationBarWidgets(test) {
-    test.waitUntilDone();
-
+  exports.testNavigationBarWidgets = function testNavigationBarWidgets(assert, done) {
     let w1 = widgets.Widget({id: "1st", label: "1st widget", content: "1"});
     let w2 = widgets.Widget({id: "2nd", label: "2nd widget", content: "2"});
     let w3 = widgets.Widget({id: "3rd", label: "3rd widget", content: "3"});
 
     // First wait for all 3 widgets to be added to the current browser window
     let firstAttachCount = 0;
     function onAttachFirstWindow(widget) {
       if (++firstAttachCount<3)
@@ -1141,43 +1111,30 @@ if (!australis) {
       function onAttach(widget) {
         if (++attachCount < 3)
           return;
         let doc = browserWindow2.document;
         let addonBar = doc.getElementById("addon-bar");
         let searchBox = doc.getElementById("search-container");
 
         // Ensure that 1st is in addon bar
-        test.assertEqual(getWidgetNode(addonBar, 0).getAttribute("label"), w1.label);
+        assert.equal(getWidgetNode(addonBar, 0).getAttribute("label"), w1.label);
         // And that 2nd and 3rd keep their original positions in navigation bar,
         // i.e. right after search box
-        test.assertEqual(searchBox.nextSibling.getAttribute("label"), w2.label);
-        test.assertEqual(searchBox.nextSibling.nextSibling.getAttribute("label"), w3.label);
+        assert.equal(searchBox.nextSibling.getAttribute("label"), w2.label);
+        assert.equal(searchBox.nextSibling.nextSibling.getAttribute("label"), w3.label);
 
         w1.destroy();
         w2.destroy();
         w3.destroy();
 
-        closeBrowserWindow(browserWindow2, function() {
-          test.done();
-        });
+        close(browserWindow2).then(done);
       }
       w1.on("attach", onAttach);
       w2.on("attach", onAttach);
       w3.on("attach", onAttach);
 
       browserWindow2 = openBrowserWindow(browserWindow);
     }
   };
 }
 
-/******************* helpers *********************/
-
-// Helper for calling code at window close
-function closeBrowserWindow(window, callback) {
-  timer.setTimeout(function() {
-    window.addEventListener("unload", function onUnload() {
-      window.removeEventListener("unload", onUnload, false);
-      callback();
-    }, false);
-    window.close();
-  }, 0);
-}
+require("sdk/test").run(exports);
--- a/addon-sdk/source/test/test-window-events.js
+++ b/addon-sdk/source/test/test-window-events.js
@@ -16,18 +16,29 @@ const { open, getMostRecentBrowserWindow
 exports["test browser events"] = function(assert, done) {
   let loader = Loader(module);
   let { events } = loader.require("sdk/window/events");
   let { on, off } = loader.require("sdk/event/core");
   let actual = [];
 
   on(events, "data", function handler(e) {
     actual.push(e);
-    if (e.type === "load") window.close();
-    if (e.type === "close") {
+
+    if (e.type === "open") {
+      assert.pass("window open has occured");
+    }
+    else if (e.type === "DOMContentLoaded") {
+      assert.pass("window DOMContentLoaded has occured");
+    }
+    else if (e.type === "load") {
+      assert.pass("window load has occured");
+      window.close();
+    }
+    else if (e.type === "close") {
+      // confirm the ordering of events
       let [ open, ready, load, close ] = actual;
       assert.equal(open.type, "open")
       assert.equal(open.target, window, "window is open")
 
       assert.equal(ready.type, "DOMContentLoaded")
       assert.equal(ready.target, window, "window ready")
 
       assert.equal(load.type, "load")
--- a/addon-sdk/source/test/test-window-loader.js
+++ b/addon-sdk/source/test/test-window-loader.js
@@ -1,12 +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";
+'use strict';
 
 // Opening new windows in Fennec causes issues
 module.metadata = {
   engines: {
     'Firefox': '*'
   }
 };
 
@@ -26,101 +26,88 @@ const Loader = Trait.compose(
     },
     window: null,
     _onLoad: null,
     _onUnload: null,
     _tabOptions: []
   }
 );
 
-exports['test compositions with missing required properties'] = function(test) {
-  test.assertRaises(
+exports['test compositions with missing required properties'] = function(assert) {
+  assert.throws(
     function() WindowLoader.compose({})(),
-    'Missing required property: _onLoad',
+    /Missing required property: _onLoad/,
     'should throw missing required property exception'
   );
-  test.assertRaises(
+  assert.throws(
     function() WindowLoader.compose({ _onLoad: null, _tabOptions: null })(),
-    'Missing required property: _onUnload',
+    /Missing required property: _onUnload/,
     'should throw missing required property `_onUnload`'
   );
-  test.assertRaises(
+  assert.throws(
     function() WindowLoader.compose({ _onUnload: null, _tabOptions: null })(),
-    'Missing required property: _onLoad',
+    /Missing required property: _onLoad/,
     'should throw missing required property `_onLoad`'
   );
-  test.assertRaises(
+  assert.throws(
     function() WindowLoader.compose({ _onUnload: null, _onLoad: null })(),
-    'Missing required property: _tabOptions',
+    /Missing required property: _tabOptions/,
     'should throw missing required property `_tabOptions`'
   );
 };
 
-exports['test `load` events'] = function(test) {
-  test.waitUntilDone();
+exports['test `load` events'] = function(assert, done) {
   let onLoadCalled = false;
   Loader({
     onLoad: function(window) {
       onLoadCalled = true;
-      test.assertEqual(
-        window, this._window, 'windows should match'
-      );
-      test.assertEqual(
+      assert.equal(window, this._window, 'windows should match');
+      assert.equal(
         window.document.readyState, 'complete', 'window must be fully loaded'
       );
       window.close();
     },
     onUnload: function(window) {
-      test.assertEqual(
-        window, this._window, 'windows should match'
-      );
-      test.assertEqual(
+      assert.equal(window, this._window, 'windows should match');
+      assert.equal(
         window.document.readyState, 'complete', 'window must be fully loaded'
       );
-      test.assert(onLoadCalled, 'load callback is supposed to be called');
-      test.done();
+      assert.ok(onLoadCalled, 'load callback is supposed to be called');
+      done();
     }
   });
 };
 
-exports['test removeing listeners'] = function(test) {
-  test.waitUntilDone();
+exports['test removeing listeners'] = function(assert, done) {
   Loader({
     onLoad: function(window) {
-      test.assertEqual(
-        window, this._window, 'windows should match'
-      );
+      assert.equal(window, this._window, 'windows should match');
       window.close();
     },
-    onUnload: function(window) {
-      test.done();
-    }
+    onUnload: done
   });
 };
 
-exports['test create loader from opened window'] = function(test) {
-  test.waitUntilDone();
+exports['test create loader from opened window'] = function(assert, done) {
   let onUnloadCalled = false;
   Loader({
     onLoad: function(window) {
-      test.assertEqual(
-        window, this._window, 'windows should match'
-      );
-      test.assertEqual(
-        window.document.readyState, 'complete', 'window must be fully loaded'
-      );
+      assert.equal(window, this._window, 'windows should match');
+      assert.equal(window.document.readyState, 'complete', 'window must be fully loaded');
       Loader({
         window: window,
         onLoad: function(win) {
-          test.assertEqual(win, window, 'windows should match');
+          assert.equal(win, window, 'windows should match');
           window.close();
         },
         onUnload: function(window) {
-          test.assert(onUnloadCalled, 'first handler should be called already');
-          test.done();
+          assert.ok(onUnloadCalled, 'first handler should be called already');
+          done();
         }
       });
     },
     onUnload: function(window) {
       onUnloadCalled = true;
     }
   });
 };
+
+require('sdk/test').run(exports);
--- a/addon-sdk/source/test/test-window-observer.js
+++ b/addon-sdk/source/test/test-window-observer.js
@@ -53,9 +53,9 @@ exports["test unload window observer"] =
   function onClose(window) {
     // Ignore non-browser windows & already opened `activeWindow` (unload will
     // emit close on it even though it is not actually closed).
     if (isBrowser(window))
       closed++;
   }
 };
 
-require("test").run(exports);
+require("sdk/test").run(exports);
--- a/addon-sdk/source/test/windows/test-firefox-windows.js
+++ b/addon-sdk/source/test/windows/test-firefox-windows.js
@@ -11,400 +11,403 @@ const { open, close, focus } = require('
 const { browserWindows } = require("sdk/windows");
 const tabs = require("sdk/tabs");
 const winUtils = require("sdk/deprecated/window-utils");
 const { WindowTracker } = winUtils;
 const { isPrivate } = require('sdk/private-browsing');
 const { isWindowPBSupported } = require('sdk/private-browsing/utils');
 
 // TEST: open & close window
-exports.testOpenAndCloseWindow = function(test) {
-  test.waitUntilDone();
-
-  test.assertEqual(browserWindows.length, 1, "Only one window open");
+exports.testOpenAndCloseWindow = function(assert, done) {
+  assert.equal(browserWindows.length, 1, "Only one window open");
+  let title = 'testOpenAndCloseWindow';
 
   browserWindows.open({
-    url: "data:text/html;charset=utf-8,<title>windows API test</title>",
+    url: "data:text/html;charset=utf-8,<title>" + title + "</title>",
     onOpen: function(window) {
-      test.assertEqual(this, browserWindows,
-                       "The 'this' object is the windows object.");
-      test.assertEqual(window.tabs.length, 1, "Only one tab open");
-      test.assertEqual(browserWindows.length, 2, "Two windows open");
-      window.tabs.activeTab.on('ready', function onReady(tab) {
-        tab.removeListener('ready', onReady);
-        test.assert(window.title.indexOf("windows API test") != -1,
-                    "URL correctly loaded");
+      assert.equal(this, browserWindows, "The 'this' object is the windows object.");
+      assert.equal(window.tabs.length, 1, "Only one tab open");
+      assert.equal(browserWindows.length, 2, "Two windows open");
+
+      window.tabs.activeTab.once('ready', function onReady(tab) {
+        assert.pass(RegExp(title).test(window.title), "URL correctly loaded");
         window.close();
       });
     },
     onClose: function(window) {
-      test.assertEqual(window.tabs.length, 0, "Tabs were cleared");
-      test.assertEqual(browserWindows.length, 1, "Only one window open");
-      test.done();
+      assert.equal(window.tabs.length, 0, "Tabs were cleared");
+      assert.equal(browserWindows.length, 1, "Only one window open");
+      done();
     }
   });
 };
 
-exports.testAutomaticDestroy = function(test) {
-  test.waitUntilDone();
-
+exports.testAutomaticDestroy = function(assert, done) {
   let windows = browserWindows;
 
   // Create a second windows instance that we will unload
   let called = false;
   let loader = Loader(module);
   let windows2 = loader.require("sdk/windows").browserWindows;
+
   windows2.on("open", function() {
     called = true;
   });
 
   loader.unload();
 
   // Fire a windows event and check that this unloaded instance is inactive
   windows.open({
     url: "data:text/html;charset=utf-8,foo",
     onOpen: function(window) {
       setTimeout(function () {
-        test.assert(!called,
-          "Unloaded windows instance is destroyed and inactive");
-        window.close(function () {
-          test.done();
-        });
+        assert.ok(!called, "Unloaded windows instance is destroyed and inactive");
+
+        window.close(done);
       });
     }
   });
 };
 
-exports.testWindowTabsObject = function(test) {
-  test.waitUntilDone();
-
-  let count = 0;
-  let window;
+exports.testWindowTabsObject = function(assert, done) {
+  let window, count = 0;
   function runTest() {
     if (++count != 2)
       return;
 
-    test.assertEqual(window.tabs.length, 1, "Only 1 tab open");
-    test.assertEqual(window.tabs.activeTab.title, "tab 1", "Correct active tab");
+    assert.equal(window.tabs.length, 1, "Only 1 tab open");
+    assert.equal(window.tabs.activeTab.title, "tab 1", "Correct active tab");
 
     window.tabs.open({
       url: "data:text/html;charset=utf-8,<title>tab 2</title>",
       inBackground: true,
       onReady: function onReady(newTab) {
-        test.assertEqual(window.tabs.length, 2, "New tab open");
-        test.assertEqual(newTab.title, "tab 2", "Correct new tab title");
-        test.assertEqual(window.tabs.activeTab.title, "tab 1", "Correct active tab");
+        assert.equal(window.tabs.length, 2, "New tab open");
+        assert.equal(newTab.title, "tab 2", "Correct new tab title");
+        assert.equal(window.tabs.activeTab.title, "tab 1", "Correct active tab");
 
         let i = 1;
-        for each (let tab in window.tabs)
-          test.assertEqual(tab.title, "tab " + i++, "Correct title");
+        for (let tab of window.tabs)
+          assert.equal(tab.title, "tab " + i++, "Correct title");
 
         window.close();
       }
     });
   }
+
+  tabs.once("ready", runTest);
+
   browserWindows.open({
     url: "data:text/html;charset=utf-8,<title>tab 1</title>",
     onActivate: function onActivate(win) {
       window = win;
       runTest();
     },
     onClose: function onClose(window) {
-      test.assertEqual(window.tabs.length, 0, "No more tabs on closed window");
-      test.done();
+      assert.equal(window.tabs.length, 0, "No more tabs on closed window");
+      done();
     }
   });
-  tabs.once("ready", runTest);
 };
 
-exports.testOnOpenOnCloseListeners = function(test) {
-  test.waitUntilDone();
+exports.testOnOpenOnCloseListeners = function(assert, done) {
   let windows = browserWindows;
 
-  test.assertEqual(browserWindows.length, 1, "Only one window open");
+  assert.equal(browserWindows.length, 1, "Only one window open");
 
   let received = {
     listener1: false,
     listener2: false,
     listener3: false,
     listener4: false
   }
 
-   function listener1() {
-    test.assertEqual(this, windows, "The 'this' object is the windows object.");
+  function listener1() {
+    assert.equal(this, windows, "The 'this' object is the windows object.");
+
     if (received.listener1)
-      test.fail("Event received twice");
+      assert.fail("Event received twice");
     received.listener1 = true;
   }
 
   function listener2() {
     if (received.listener2)
-      test.fail("Event received twice");
+      assert.fail("Event received twice");
     received.listener2 = true;
   }
 
   function listener3() {
-    test.assertEqual(this, windows, "The 'this' object is the windows object.");
+    assert.equal(this, windows, "The 'this' object is the windows object.");
     if (received.listener3)
-      test.fail("Event received twice");
+      assert.fail("Event received twice");
     received.listener3 = true;
   }
 
   function listener4() {
     if (received.listener4)
-      test.fail("Event received twice");
+      assert.fail("Event received twice");
     received.listener4 = true;
   }
 
   windows.on('open', listener1);
   windows.on('open', listener2);
   windows.on('close', listener3);
   windows.on('close', listener4);
 
-  function verify() {
-    test.assert(received.listener1, "onOpen handler called");
-    test.assert(received.listener2, "onOpen handler called");
-    test.assert(received.listener3, "onClose handler called");
-    test.assert(received.listener4, "onClose handler called");
-
-    windows.removeListener('open', listener1);
-    windows.removeListener('open', listener2);
-    windows.removeListener('close', listener3);
-    windows.removeListener('close', listener4);
-    test.done();
-  }
-
-
   windows.open({
     url: "data:text/html;charset=utf-8,foo",
     onOpen: function(window) {
-      window.close(verify);
+      window.close(function() {
+        assert.ok(received.listener1, "onOpen handler called");
+        assert.ok(received.listener2, "onOpen handler called");
+        assert.ok(received.listener3, "onClose handler called");
+        assert.ok(received.listener4, "onClose handler called");
+
+        windows.removeListener('open', listener1);
+        windows.removeListener('open', listener2);
+        windows.removeListener('close', listener3);
+        windows.removeListener('close', listener4);
+
+        done();
+      });
     }
   });
 };
 
-/*
-Disabled due to all of the Win8 PGO bustage in bug 873007.
-exports.testActiveWindow = function(test) {
+exports.testActiveWindow = function(assert, done) {
   let windows = browserWindows;
 
   // API window objects
   let window2, window3;
 
   // Raw window objects
   let rawWindow2, rawWindow3;
 
-  test.waitUntilDone();
-
   let testSteps = [
     function() {
-      test.assertEqual(windows.length, 3, "Correct number of browser windows");
+      assert.equal(windows.length, 3, "Correct number of browser windows");
+
       let count = 0;
       for (let window in windows)
         count++;
-      test.assertEqual(count, 3, "Correct number of windows returned by iterator");
+      assert.equal(count, 3, "Correct number of windows returned by iterator");
 
-      test.assertEqual(windows.activeWindow.title, window3.title, "Correct active window - 3");
+      assert.equal(windows.activeWindow.title, window3.title, "Correct active window - 3");
 
       continueAfterFocus(rawWindow2);
       rawWindow2.focus();
     },
     function() {
-      nextStep();
-    },
-    function() {
-      test.assertEqual(windows.activeWindow.title, window2.title, "Correct active window - 2");
+      assert.equal(windows.activeWindow.title, window2.title, "Correct active window - 2");
 
       continueAfterFocus(rawWindow2);
       window2.activate();
     },
     function() {
-      test.assertEqual(windows.activeWindow.title, window2.title, "Correct active window - 2");
+      assert.equal(windows.activeWindow.title, window2.title, "Correct active window - 2");
+
       continueAfterFocus(rawWindow3);
       window3.activate();
     },
     function() {
-      test.assertEqual(windows.activeWindow.title, window3.title, "Correct active window - 3");
+      assert.equal(windows.activeWindow.title, window3.title, "Correct active window - 3");
       finishTest();
     }
   ];
 
   let newWindow = null;
   let tracker = new WindowTracker({
     onTrack: function(window) {
       newWindow = window;
     }
   });
 
   windows.open({
     url: "data:text/html;charset=utf-8,<title>window 2</title>",
     onOpen: function(window) {
+      assert.pass('window 2 open');
+
       window.tabs.activeTab.on('ready', function() {
+        assert.pass('window 2 tab activated');
+
         window2 = window;
-        test.assert(newWindow, "A new window was opened");
+        assert.ok(newWindow, "A new window was opened");
         rawWindow2 = newWindow;
         newWindow = null;
-        test.assertEqual(rawWindow2.content.document.title, "window 2", "Got correct raw window 2");
-        test.assertEqual(rawWindow2.document.title, window2.title, "Saw correct title on window 2");
+
+        assert.equal(rawWindow2.content.document.title, "window 2", "Got correct raw window 2");
+        assert.equal(rawWindow2.document.title, window2.title, "Saw correct title on window 2");
 
         windows.open({
           url: "data:text/html;charset=utf-8,<title>window 3</title>",
           onOpen: function(window) {
+            assert.pass('window 3 open');
+
             window.tabs.activeTab.on('ready', function onReady() {
+              assert.pass('window 3 tab activated');
+
               window3 = window;
-              test.assert(newWindow, "A new window was opened");
+              assert.ok(newWindow, "A new window was opened");
               rawWindow3 = newWindow;
               tracker.unload();
-              test.assertEqual(rawWindow3.content.document.title, "window 3", "Got correct raw window 3");
-              test.assertEqual(rawWindow3.document.title, window3.title, "Saw correct title on window 3");
+
+              assert.equal(rawWindow3.content.document.title, "window 3", "Got correct raw window 3");
+              assert.equal(rawWindow3.document.title, window3.title, "Saw correct title on window 3");
+
               continueAfterFocus(rawWindow3);
               rawWindow3.focus();
             });
           }
         });
       });
     }
   });
 
   function nextStep() {
-    if (testSteps.length)
-      testSteps.shift()();
+    if (testSteps.length) {
+      setTimeout(testSteps.shift())
+    }
   }
 
   let continueAfterFocus = function(w) onFocus(w).then(nextStep);
 
   function finishTest() {
-    window3.close(function() {
-      window2.close(function() {
-        test.done();
+    // close unactive window first to avoid unnecessary focus changing
+    window2.close(function() {
+      window3.close(function() {
+        assert.equal(rawWindow2.closed, true, 'window 2 is closed');
+        assert.equal(rawWindow3.closed, true, 'window 3 is closed');
+
+        done();
       });
     });
   }
 };
-*/
 
-exports.testTrackWindows = function(test) {
-  test.waitUntilDone();
-
+exports.testTrackWindows = function(assert, done) {
   let windows = [];
   let actions = [];
 
   let expects = [
     "activate 0", "global activate 0", "deactivate 0", "global deactivate 0",
     "activate 1", "global activate 1", "deactivate 1", "global deactivate 1",
     "activate 2", "global activate 2"
   ];
 
+  function windowsActivation(window) {
+    let index = windows.indexOf(window);
+    // only concerned with windows opened for this test
+    if (index < 0)
+      return;
+
+    assert.equal(actions.join(), expects.slice(0, index*4 + 1).join(), expects[index*4 + 1]);
+    actions.push("global activate " + index)
+  }
+
+  function windowsDeactivation(window) {
+    let index = windows.indexOf(window);
+    // only concerned with windows opened for this test
+    if (index < 0)
+      return;
+
+    assert.equal(actions.join(), expects.slice(0, index*4 + 3).join(), expects[index*4 + 3]);
+    actions.push("global deactivate " + index)
+  }
+
+  // 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);
 
-        test.assertEqual(actions.join(), expects.slice(0, index*4).join(), expects[index*4]);
+        assert.equal(actions.join(),
+                     expects.slice(0, index*4).join(),
+                     "expecting " + expects[index*4]);
         actions.push("activate " + index);
 
         if (windows.length < 3) {
           openWindow()
         }
         else {
           (function closeWindows(windows) {
-            if (!windows.length)
-              return test.done();
+            if (!windows.length) {
+              browserWindows.removeListener("activate", windowsActivation);
+              browserWindows.removeListener("deactivate", windowsDeactivation);
+              return done();
+            }
 
             return windows.pop().close(function() {
-              test.pass('window was closed');
+              assert.pass('window was closed');
               closeWindows(windows);
             });
           })(windows)
         }
       },
-
       onDeactivate: function(window) {
         let index = windows.indexOf(window);
 
-        test.assertEqual(actions.join(), expects.slice(0, index*4 + 2).join(), expects[index*4 + 2]);
+        assert.equal(actions.join(),
+                     expects.slice(0, index*4 + 2).join(),
+                     "expecting " + expects[index*4 + 2]);
         actions.push("deactivate " + index)
       }
     }));
   }
-
-  browserWindows.on("activate", function (window) {
-    let index = windows.indexOf(window);
-    // only concerned with windows opened for this test
-    if (index < 0)
-      return;
-
-    test.assertEqual(actions.join(), expects.slice(0, index*4 + 1).join(), expects[index*4 + 1]);
-    actions.push("global activate " + index)
-  })
-
-  browserWindows.on("deactivate", function (window) {
-    let index = windows.indexOf(window);
-    // only concerned with windows opened for this test
-    if (index < 0)
-      return;
-
-    test.assertEqual(actions.join(), expects.slice(0, index*4 + 3).join(), expects[index*4 + 3]);
-    actions.push("global deactivate " + index)
-  })
-
   openWindow();
 }
 
 // test that it is not possible to open a private window by default
-exports.testWindowOpenPrivateDefault = function(test) {
-  test.waitUntilDone();
-
+exports.testWindowOpenPrivateDefault = function(assert, done) {
   browserWindows.open({
     url: 'about:mozilla',
     isPrivate: true,
     onOpen: function(window) {
       let tab = window.tabs[0];
+
       tab.once('ready', function() {
-        test.assertEqual(tab.url, 'about:mozilla', 'opened correct tab');
-        test.assertEqual(isPrivate(tab), false, 'tab is not private');
+        assert.equal(tab.url, 'about:mozilla', 'opened correct tab');
+        assert.equal(isPrivate(tab), false, 'tab is not private');
 
-        window.close(test.done.bind(test));
+        window.close(done);
       });
     }
   });
 }
 
 // test that it is not possible to find a private window in
 // windows module's iterator
-exports.testWindowIteratorPrivateDefault = function(test) {
-  test.waitUntilDone();
-
-  test.assertEqual(browserWindows.length, 1, 'only one window open');
+exports.testWindowIteratorPrivateDefault = function(assert, done) {
+  assert.equal(browserWindows.length, 1, 'only one window open');
 
   open('chrome://browser/content/browser.xul', {
     features: {
       private: true,
       chrome: true
     }
-  }).then(function(window) focus(window).then(function() {
+  }).then(focus).then(function(window) {
     // test that there is a private window opened
-    test.assertEqual(isPrivate(window), isWindowPBSupported, 'there is a private window open');
-    test.assertStrictEqual(window, winUtils.activeWindow);
-    test.assertStrictEqual(window, getMostRecentWindow());
+    assert.equal(isPrivate(window), isWindowPBSupported, 'there is a private window open');
+    assert.strictEqual(window, winUtils.activeWindow);
+    assert.strictEqual(window, getMostRecentWindow());
 
-    test.assert(!isPrivate(browserWindows.activeWindow));
+    assert.ok(!isPrivate(browserWindows.activeWindow));
+
+    assert.equal(browserWindows.length, 1, 'only one window in browserWindows');
+    assert.equal(windows().length, 1, 'only one window in windows()');
 
-    if (isWindowPBSupported) {
-      test.assertEqual(browserWindows.length, 1, 'only one window in browserWindows');
-      test.assertEqual(windows().length, 1, 'only one window in windows()');
-    }
-    else {
-      test.assertEqual(browserWindows.length, 2, 'two windows open');
-      test.assertEqual(windows().length, 2, 'two windows in windows()');
-    }
-    test.assertEqual(windows(null, { includePrivate: true }).length, 2);
+    assert.equal(windows(null, { includePrivate: true }).length, 2);
+
+    // test that all windows in iterator are not private
+    for (let window of browserWindows)
+      assert.ok(!isPrivate(window), 'no window in browserWindows is private');
 
-    for each(let window in browserWindows) {
-      // test that all windows in iterator are not private
-      test.assert(!isPrivate(window), 'no window in browserWindows is private');
-    }
+    close(window).then(done);
+  });
+}
 
-    close(window).then(test.done.bind(test));
-  }));
-}
+require('sdk/test').run(exports);
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -992,16 +992,17 @@ let RemoteDebugger = {
         if ("nsIProfiler" in Ci) {
           DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/profiler.js");
         }
         DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/styleeditor.js");
         DebuggerServer.enableWebappsContentActor = true;
       }
       DebuggerServer.addActors('chrome://browser/content/dbg-browser-actors.js');
       DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/webapps.js");
+      DebuggerServer.registerModule("devtools/server/actors/device");
     }
 
     let port = Services.prefs.getIntPref('devtools.debugger.remote-port') || 6000;
     try {
       DebuggerServer.openListener(port);
     } catch (e) {
       dump('Unable to start debugger server: ' + e + '\n');
     }
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,4 +1,4 @@
 {
-    "revision": "dbaee540a7d23d271e62e7da80d7db226be8860b", 
+    "revision": "085648d44f2a1ad2c73b0eb4225c6448104e70f5", 
     "repo_path": "/integration/gaia-central"
 }
new file mode 100644
--- /dev/null
+++ b/browser/devtools/app-manager/Makefile.in
@@ -0,0 +1,16 @@
+#
+# 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/.
+
+DEPTH		= @DEPTH@
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+include $(topsrcdir)/config/rules.mk
+
+libs::
+	$(NSINSTALL) $(srcdir)/*.js $(FINAL_TARGET)/modules/devtools/app-manager
new file mode 100644
--- /dev/null
+++ b/browser/devtools/app-manager/connection-store.js
@@ -0,0 +1,48 @@
+/* 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 ObservableObject = require("devtools/shared/observable-object");
+const {Connection} = require("devtools/client/connection-manager");
+
+const _knownConnectionStores = new WeakMap();
+
+let ConnectionStore;
+
+module.exports = ConnectionStore = function(connection) {
+  // If we already know about this connection,
+  // let's re-use the existing store.
+  if (_knownConnectionStores.has(connection)) {
+    return _knownConnectionStores.get(connection);
+  }
+  _knownConnectionStores.set(connection, this);
+
+  ObservableObject.call(this, {status:null,host:null,port:null});
+
+  this._destroy = this._destroy.bind(this);
+  this._feedStore = this._feedStore.bind(this);
+
+  this._connection = connection;
+  this._connection.once(Connection.Events.DESTROYED, this._destroy);
+  this._connection.on(Connection.Events.STATUS_CHANGED, this._feedStore);
+  this._connection.on(Connection.Events.PORT_CHANGED, this._feedStore);
+  this._connection.on(Connection.Events.HOST_CHANGED, this._feedStore);
+  this._feedStore();
+  return this;
+}
+
+ConnectionStore.prototype = {
+  _destroy: function() {
+    this._connection.off(Connection.Events.STATUS_CHANGED, this._feedStore);
+    this._connection.off(Connection.Events.PORT_CHANGED, this._feedStore);
+    this._connection.off(Connection.Events.HOST_CHANGED, this._feedStore);
+    _knownConnectionStores.delete(this._connection);
+    this._connection = null;
+  },
+
+  _feedStore: function() {
+    this.object.status = this._connection.status;
+    this.object.host = this._connection.host;
+    this.object.port = this._connection.port;
+  }
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/app-manager/content/template.js
@@ -0,0 +1,320 @@
+/* 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/. */
+
+/**
+ * Template mechanism based on Object Emitters.
+ *
+ * The data used to expand the templates comes from
+ * a ObjectEmitter object. The templates are automatically
+ * updated as the ObjectEmitter is updated (via the "set"
+ * event). See documentation in observable-object.js.
+ *
+ * Templates are used this way:
+ *
+ * (See examples in browser/devtools/app-manager/content/*.xhtml)
+ *
+ * <div template="{JSON Object}">
+ *
+ * {
+ *  type: "attribute"
+ *  name: name of the attribute
+ *  path: location of the attribute value in the ObjectEmitter
+ * }
+ *
+ * {
+ *  type: "textContent"
+ *  path: location of the textContent value in the ObjectEmitter
+ * }
+ *
+ * {
+ *  type: "localizedContent"
+ *  paths: array of locations of the value of the arguments of the property
+ *  property: l10n property
+ * }
+ *
+ * <div template-loop="{JSON Object}">
+ *
+ * {
+ *  arrayPath: path of the array in the ObjectEmitter to loop from
+ *  childSelector: selector of the element to duplicate in the loop
+ * }
+ *
+ */
+
+const NOT_FOUND_STRING = "n/a";
+
+/**
+ * let t = new Template(root, store, l10nResolver);
+ * t.start();
+ *
+ * @param DOMNode root.
+ *        Node from where templates are expanded.
+ * @param ObjectEmitter store.
+ *        ObjectEmitter object.
+ * @param function (property, args). l10nResolver
+ *        A function that returns localized content.
+ */
+function Template(root, store, l10nResolver) {
+  this._store = store;
+  this._l10n = l10nResolver;
+
+  // Listeners are stored in Maps.
+  // path => Set(node1, node2, ..., nodeN)
+  // For example: "foo.bar.4.name" => Set(div1,div2)
+
+  this._nodeListeners = new Map();
+  this._loopListeners = new Map();
+  this._root = root;
+  this._doc = this._root.ownerDocument;
+
+  this._store.on("set", (event,path,value) => this._storeChanged(path,value));
+}
+
+Template.prototype = {
+  start: function() {
+    this._processTree(this._root);
+  },
+
+  _resolvePath: function(path, defaultValue=null) {
+
+    // From the store, get the value of an object located
+    // at @path.
+    //
+    // For example, if the store is designed as:
+    //
+    // {
+    //   foo: {
+    //     bar: [
+    //       {},
+    //       {},
+    //       {a: 2}
+    //   }
+    // }
+    //
+    // _resolvePath("foo.bar.2.a") will return "2".
+    //
+    // Array indexes are not surrounded by brackets.
+
+    let chunks = path.split(".");
+    let obj = this._store.object;
+    for (let word of chunks) {
+      if ((typeof obj) == "object" &&
+          (word in obj)) {
+        obj = obj[word];
+      } else {
+        return defaultValue;
+      }
+    }
+    return obj;
+  },
+
+  _storeChanged: function(path, value) {
+
+    // The store has changed (a "set" event has been emitted).
+    // We need to invalidate and rebuild the affected elements.
+
+    let strpath = path.join(".");
+    this._invalidate(strpath);
+
+    for (let [registeredPath, set] of this._nodeListeners) {
+      if (strpath != registeredPath &&
+          registeredPath.indexOf(strpath) > -1) {
+        this._invalidate(registeredPath);
+      }
+    }
+  },
+
+  _invalidate: function(path) {
+    // Loops:
+    let set = this._loopListeners.get(path);
+    if (set) {
+      for (let elt of set) {
+        this._processLoop(elt);
+      }
+    }
+
+    // Nodes:
+    set = this._nodeListeners.get(path);
+    if (set) {
+      for (let elt of set) {
+        this._processNode(elt);
+      }
+    }
+  },
+
+  _registerNode: function(path, element) {
+
+    // We map a node to a path.
+    // If the value behind this path is updated,
+    // we get notified from the ObjectEmitter,
+    // and then we know which objects to update.
+
+    if (!this._nodeListeners.has(path)) {
+      this._nodeListeners.set(path, new Set());
+    }
+    let set = this._nodeListeners.get(path);
+    set.add(element);
+  },
+
+  _unregisterNodes: function(nodes) {
+    for (let [registeredPath, set] of this._nodeListeners) {
+      for (let e of nodes) {
+        set.delete(e);
+      }
+      if (set.size == 0) {
+        this._nodeListeners.delete(registeredPath);
+      }
+    }
+  },
+
+  _registerLoop: function(path, element) {
+    if (!this._loopListeners.has(path)) {
+      this._loopListeners.set(path, new Set());
+    }
+    let set = this._loopListeners.get(path);
+    set.add(element);
+  },
+
+  _processNode: function(element, rootPath="") {
+    // The actual magic.
+    // The element has a template attribute.
+    // The value is supposed to be a JSON string.
+    // rootPath is the prefex to the path used by
+    // these elements (if children of template-loop);
+
+    let e = element;
+    let str = e.getAttribute("template");
+
+    if (rootPath) {
+      // We will prefix paths with this rootPath.
+      // It needs to end with a dot.
+      rootPath = rootPath + ".";
+    }
+
+    try {
+      let json = JSON.parse(str);
+      // Sanity check
+      if (!("type" in json)) {
+        throw new Error("missing property");
+      }
+      if (json.rootPath) {
+        // If node has been generated through a loop, we stored
+        // previously its rootPath.
+        rootPath = json.rootPath;
+      }
+
+      // paths is an array that will store all the paths we needed
+      // to expand the node. We will then, via _registerNode, link
+      // this element to these paths.
+
+      let paths = [];
+
+      switch (json.type) {
+        case "attribute": {
+          if (!("name" in json) ||
+              !("path" in json)) {
+            throw new Error("missing property");
+          }
+          e.setAttribute(json.name, this._resolvePath(rootPath + json.path, NOT_FOUND_STRING));
+          paths.push(rootPath + json.path);
+          break;
+        }
+        case "textContent": {
+          if (!("path" in json)) {
+            throw new Error("missing property");
+          }
+          e.textContent = this._resolvePath(rootPath + json.path, NOT_FOUND_STRING);
+          paths.push(rootPath + json.path);
+          break;
+        }
+        case "localizedContent": {
+          if (!("property" in json) ||
+              !("paths" in json)) {
+            throw new Error("missing property");
+          }
+          let params = json.paths.map((p) => {
+            paths.push(rootPath + p);
+            let str = this._resolvePath(rootPath + p, NOT_FOUND_STRING);
+            return str;
+          });
+          e.textContent = this._l10n(json.property, params);
+          break;
+        }
+      }
+      if (rootPath) {
+        // We save the rootPath if any.
+        json.rootPath = rootPath;
+        e.setAttribute("template", JSON.stringify(json));
+      }
+      if (paths.length > 0) {
+        for (let path of paths) {
+          this._registerNode(path, e);
+        }
+      }
+    } catch(exception) {
+      console.error("Invalid template: " + e.outerHTML + " (" + exception + ")");
+    }
+  },
+
+  _processLoop: function(element, rootPath="") {
+    // The element has a template-loop attribute.
+    // The related path must be an array. We go
+    // through the array, and build one child per
+    // item. The template for this child is pointed
+    // by the childSelector property.
+    try {
+      let e = element;
+      let template, count;
+      let str = e.getAttribute("template-loop");
+      let json = JSON.parse(str);
+      if (!("arrayPath" in json)     ||
+          !("childSelector" in json)) {
+        throw new Error("missing property");
+      }
+      if (rootPath) {
+        json.arrayPath = rootPath + "." + json.arrayPath;
+      }
+      let templateParent = this._doc.querySelector(json.childSelector);
+      if (!templateParent) {
+        throw new Error("can't find child");
+      }
+      template = this._doc.createElement("div");
+      template.innerHTML = templateParent.innerHTML;
+      template = template.firstElementChild;
+      let array = this._resolvePath(json.arrayPath, []);
+      if (!Array.isArray(array)) {
+        console.error("referenced array is not an array");
+      }
+      count = array.length;
+
+      let fragment = this._doc.createDocumentFragment();
+      for (let i = 0; i < count; i++) {
+        let node = template.cloneNode(true);
+        this._processTree(node, json.arrayPath + "." + i);
+        fragment.appendChild(node);
+      }
+      this._registerLoop(json.arrayPath, e);
+      this._registerLoop(json.arrayPath + ".length", e);
+      this._unregisterNodes(e.querySelectorAll("[template]"));
+      e.innerHTML = "";
+      e.appendChild(fragment);
+    } catch(exception) {
+      console.error("Invalid template: " + e.outerHTML + " (" + exception + ")");
+    }
+  },
+
+  _processTree: function(parent, rootPath="") {
+    let loops = parent.querySelectorAll(":not(template) [template-loop]");
+    let nodes = parent.querySelectorAll(":not(template) [template]");
+    for (let e of loops) {
+      this._processLoop(e, rootPath);
+    }
+    for (let e of nodes) {
+      this._processNode(e, rootPath);
+    }
+    if (parent.hasAttribute("template")) {
+      this._processNode(parent, rootPath);
+    }
+  },
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/app-manager/device-store.js
@@ -0,0 +1,94 @@
+/* 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 ObservableObject = require("devtools/shared/observable-object");
+const {getDeviceFront} = require("devtools/server/actors/device");
+const {Connection} = require("devtools/client/connection-manager");
+
+const {Cu} = require("chrome");
+
+const _knownDeviceStores = new WeakMap();
+
+let DeviceStore;
+
+module.exports = DeviceStore = function(connection) {
+  // If we already know about this connection,
+  // let's re-use the existing store.
+  if (_knownDeviceStores.has(connection)) {
+    return _knownDeviceStores.get(connection);
+  }
+
+  _knownDeviceStores.set(connection, this);
+
+  ObservableObject.call(this, {});
+
+  this._resetStore();
+
+  this._destroy = this._destroy.bind(this);
+  this._onStatusChanged = this._onStatusChanged.bind(this);
+
+  this._connection = connection;
+  this._connection.once(Connection.Events.DESTROYED, this._destroy);
+  this._connection.on(Connection.Events.STATUS_CHANGED, this._onStatusChanged);
+  this._onStatusChanged();
+  return this;
+}
+
+DeviceStore.prototype = {
+  _destroy: function() {
+    this._connection.off(Connection.Events.STATUS_CHANGED, this._onStatusChanged);
+    _knownDeviceStores.delete(this._connection);
+    this._connection = null;
+  },
+
+  _resetStore: function() {
+    this.object.description = {};
+    this.object.permissions = [];
+  },
+
+  _onStatusChanged: function() {
+    if (this._connection.status == Connection.Status.CONNECTED) {
+      this._listTabs();
+    } else {
+      this._resetStore();
+    }
+  },
+
+  _listTabs: function() {
+    this._connection.client.listTabs((resp) => {
+      this._deviceFront = getDeviceFront(this._connection.client, resp);
+      this._feedStore();
+    });
+  },
+
+  _feedStore: function() {
+    this._getDeviceDescription();
+    this._getDevicePermissionsTable();
+  },
+
+  _getDeviceDescription: function() {
+    return this._deviceFront.getDescription()
+    .then(json => {
+      json.dpi = Math.ceil(json.dpi);
+      this.object.description = json;
+    });
+  },
+
+  _getDevicePermissionsTable: function() {
+    return this._deviceFront.getRawPermissionsTable()
+    .then(json => {
+      let permissionsTable = json.rawPermissionsTable;
+      let permissionsArray = [];
+      for (let name in permissionsTable) {
+        permissionsArray.push({
+          name: name,
+          app: permissionsTable[name].app,
+          privileged: permissionsTable[name].privileged,
+          certified: permissionsTable[name].certified,
+        });
+      }
+      this.object.permissions = permissionsArray;
+    });
+  },
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/app-manager/moz.build
@@ -0,0 +1,7 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+TEST_DIRS += ['test']
new file mode 100644
--- /dev/null
+++ b/browser/devtools/app-manager/test/Makefile.in
@@ -0,0 +1,19 @@
+# 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/.
+
+DEPTH     = @DEPTH@
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+relativesrcdir  = @relativesrcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MOCHITEST_CHROME_FILES = \
+		test_template.html \
+		test_connection_store.html \
+		test_device_store.html \
+		$(NULL)
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/browser/devtools/app-manager/test/moz.build
@@ -0,0 +1,6 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
new file mode 100644
--- /dev/null
+++ b/browser/devtools/app-manager/test/test_connection_store.html
@@ -0,0 +1,106 @@
+<!DOCTYPE html>
+
+<!--
+Bug 901519 - [app manager] data store for connections
+-->
+
+<html>
+
+  <head>
+    <meta charset="utf8">
+    <title></title>
+
+    <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+    <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+  </head>
+
+  <body>
+
+    <div id="root">
+      <span id="status" template='{"type":"textContent","path":"status"}'></span>
+      <span id="host" template='{"type":"textContent","path":"host"}'></span>
+      <span id="port" template='{"type":"textContent","path":"port"}'></span>
+    </div>
+
+    <script type="application/javascript;version=1.8" src="chrome://browser/content/devtools/app-manager/template.js"></script>
+    <script type="application/javascript;version=1.8">
+      const Cu = Components.utils;
+      Cu.import("resource://gre/modules/devtools/dbg-server.jsm");
+      DebuggerServer.init(function () { return true; });
+      DebuggerServer.addBrowserActors();
+
+      window.onload = function() {
+        SimpleTest.waitForExplicitFinish();
+
+        Cu.import("resource://gre/modules/Services.jsm");
+        Cu.import("resource:///modules/devtools/gDevTools.jsm");
+
+        const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+        const {require} = devtools;
+
+        const {ConnectionManager} = require("devtools/client/connection-manager");
+        const ConnectionStore = require("devtools/app-manager/connection-store");
+
+        let connection = ConnectionManager.createConnection();
+        let store = new ConnectionStore(connection);
+
+        let root = document.querySelector("#root");
+        let status = root.querySelector("#status");
+        let host = root.querySelector("#host");
+        let port = root.querySelector("#port");
+        let template = new Template(root, store, () => {});
+        template.start();
+
+        connection.host = "foobar";
+        connection.port = 42;
+
+        is(host.textContent, "foobar", "host updated");
+        is(port.textContent, 42, "port updated");
+
+        let been_through_connecting = false;
+        let been_through_connected = false;
+        let been_through_disconnected = false;
+
+        is(status.textContent, "disconnected", "status updated (diconnected)");
+
+        connection.once("connecting", (e) => {
+          SimpleTest.executeSoon(() => {
+            been_through_connecting = true;
+            is(status.textContent, "connecting", "status updated (connecting)");
+          })
+        });
+
+        connection.once("connected", (e) => {
+          SimpleTest.executeSoon(() => {
+            been_through_connected = true;
+            is(status.textContent, "connected", "status updated (connected)");
+            connection.disconnect();
+          })
+        });
+
+        connection.once("disconnected", (e) => {
+          SimpleTest.executeSoon(() => {
+            been_through_disconnected = true;
+            is(status.textContent, "disconnected", "status updated (disconnected)");
+            connection.destroy();
+            finishup();
+          })
+        });
+
+        function finishup() {
+          ok(been_through_connecting &&
+            been_through_connected &&
+            been_through_disconnected, "All updates happened");
+          DebuggerServer.destroy();
+          SimpleTest.finish();
+        }
+
+        connection.host = null; // force pipe
+        connection.port = null;
+
+        connection.connect();
+      }
+
+    </script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/devtools/app-manager/test/test_device_store.html
@@ -0,0 +1,84 @@
+<!DOCTYPE html>
+
+<!--
+Bug 901520 - [app manager] data store for device
+-->
+
+<html>
+
+  <head>
+    <meta charset="utf8">
+    <title></title>
+
+    <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+    <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+  </head>
+
+  <body>
+
+    <script type="application/javascript;version=1.8" src="chrome://browser/content/devtools/app-manager/template.js"></script>
+    <script type="application/javascript;version=1.8">
+      const Cu = Components.utils;
+      Cu.import("resource://gre/modules/devtools/dbg-server.jsm");
+      DebuggerServer.init(function () { return true; });
+      DebuggerServer.addBrowserActors();
+
+      function compare(o1, o2, msg) {
+        is(JSON.stringify(o1), JSON.stringify(o2), msg);
+      }
+
+      window.onload = function() {
+        SimpleTest.waitForExplicitFinish();
+
+        Cu.import("resource://gre/modules/Services.jsm");
+        Cu.import("resource:///modules/devtools/gDevTools.jsm");
+
+
+        const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+        const {require} = devtools;
+
+        const {ConnectionManager} = require("devtools/client/connection-manager");
+        const DeviceStore = require("devtools/app-manager/device-store");
+
+        let {getDeviceFront} = devtools.require("devtools/server/actors/device");
+
+        let connection = ConnectionManager.createConnection();
+        let store = new DeviceStore(connection);
+
+        connection.once("connected", function() {
+          store.on("set", function check(event, path, value) {
+            if (path.join(".") != "description") return;
+            store.off("set", check);
+            info("Connected");
+            connection.client.listTabs((resp) => {
+              info("List tabs response");
+              let deviceFront = getDeviceFront(connection.client, resp);
+              deviceFront.getDescription().then(json => {
+                info("getDescription response: " + JSON.stringify(json));
+                json.dpi = Math.ceil(json.dpi);
+                for (let key in json) {
+                  compare(json[key], store.object.description[key], "description." + key + " is valid");
+                  compare(json[key], value[key], "description." + key + " is valid");
+                }
+                connection.disconnect();
+              }).then(null, (error) => ok(false, "Error:" + error));
+            });
+          });
+        });
+
+        connection.once("disconnected", function() {
+          compare(store.object, {description:{},permissions:[]}, "empty store after disconnect")
+          connection.destroy();
+          DebuggerServer.destroy();
+          SimpleTest.finish();
+        });
+
+        compare(store.object, {description:{},permissions:[]}, "empty store before disconnect")
+
+        connection.connect();
+
+      }
+
+    </script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/devtools/app-manager/test/test_template.html
@@ -0,0 +1,239 @@
+<!DOCTYPE html>
+
+<html>
+
+  <head>
+    <meta charset="utf8">
+    <title></title>
+
+    <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+    <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+  </head>
+
+  <div id="root">
+    <span template='{"type":"textContent","path":"title"}'></span>
+    <span template='{"type":"attribute","name":"title","path":"title"}'></span>
+    <span template='{"type":"localizedContent","paths":["foo2.foo_l10n","foo2.bar_l10n"],"property":"foo2"}'></span>
+    <div template-loop='{"arrayPath":"foo1.bar1","childSelector":"#template"}'></div>
+  </div>
+
+  <div id="ref0">
+    <span template='{"type":"textContent","path":"title"}'>ttt</span>
+    <span title="ttt" template='{"type":"attribute","name":"title","path":"title"}'></span>
+    <span template='{"type":"localizedContent","paths":["foo2.foo_l10n","foo2.bar_l10n"],"property":"foo2"}'>foo2:foo_l10n/bar_l10n</span>
+    <div template-loop='{"arrayPath":"foo1.bar1","childSelector":"#template"}'>
+      <div>
+        <span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.0."}'>xx0</span>
+        <span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.0."}'>a</span>
+        <span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.0."}'>b</span>
+      </div>
+      <div>
+        <span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.1."}'>xx1</span>
+        <span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.1."}'>a</span>
+        <span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.1."}'>b</span>
+      </div>
+    </div>
+  </div>
+
+
+  <div id="ref1">
+    <span template='{"type":"textContent","path":"title"}'>xxx</span>
+    <span title="xxx" template='{"type":"attribute","name":"title","path":"title"}'></span>
+    <span template='{"type":"localizedContent","paths":["foo2.foo_l10n","foo2.bar_l10n"],"property":"foo2"}'>foo2:foo2_l10n/bar_l10n</span>
+    <div template-loop='{"arrayPath":"foo1.bar1","childSelector":"#template"}'>
+      <div>
+        <span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.0."}'>xx0</span>
+        <span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.0."}'>a</span>
+        <span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.0."}'>b</span>
+      </div>
+      <div>
+        <span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.1."}'>xx1</span>
+        <span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.1."}'>a</span>
+        <span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.1."}'>b</span>
+      </div>
+    </div>
+  </div>
+
+  <div id="ref2">
+    <span template='{"type":"textContent","path":"title"}'>xxx</span>
+    <span title="xxx" template='{"type":"attribute","name":"title","path":"title"}'></span>
+    <span template='{"type":"localizedContent","paths":["foo2.foo_l10n","foo2.bar_l10n"],"property":"foo2"}'>foo2:yyy/zzz</span>
+    <div template-loop='{"arrayPath":"foo1.bar1","childSelector":"#template"}'>
+      <div>
+        <span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.0."}'>xx0</span>
+        <span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.0."}'>a</span>
+        <span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.0."}'>b</span>
+      </div>
+      <div>
+        <span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.1."}'>xx1</span>
+        <span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.1."}'>a</span>
+        <span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.1."}'>b</span>
+      </div>
+    </div>
+  </div>
+
+  <div id="ref3">
+    <span template='{"type":"textContent","path":"title"}'>xxx</span>
+    <span title="xxx" template='{"type":"attribute","name":"title","path":"title"}'></span>
+    <span template='{"type":"localizedContent","paths":["foo2.foo_l10n","foo2.bar_l10n"],"property":"foo2"}'>foo2:yyy/zzz</span>
+    <div template-loop='{"arrayPath":"foo1.bar1","childSelector":"#template"}'>
+      <div>
+        <span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.0."}'>xx0</span>
+        <span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.0."}'>a</span>
+        <span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.0."}'>b</span>
+      </div>
+      <div>
+        <span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.1."}'>xx1</span>
+        <span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.1."}'>a</span>
+        <span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.1."}'>b</span>
+      </div>
+      <div>
+        <span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.2."}'>xx2</span>
+        <span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.2."}'>a</span>
+        <span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.2."}'>b</span>
+      </div>
+      <div>
+        <span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.3."}'>xx3</span>
+        <span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.3."}'>a</span>
+        <span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.3."}'>b</span>
+      </div>
+      <div>
+        <span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.4."}'>xx4</span>
+        <span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.4."}'>a</span>
+        <span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.4."}'>b</span>
+      </div>
+    </div>
+  </div>
+
+  <div id="ref4">
+    <span template='{"type":"textContent","path":"title"}'>xxx</span>
+    <span title="xxx" template='{"type":"attribute","name":"title","path":"title"}'></span>
+    <span template='{"type":"localizedContent","paths":["foo2.foo_l10n","foo2.bar_l10n"],"property":"foo2"}'>foo2:yyy/zzz</span>
+    <div template-loop='{"arrayPath":"foo1.bar1","childSelector":"#template"}'>
+      <div>
+        <span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.0."}'>xx0</span>
+        <span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.0."}'>a</span>
+        <span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.0."}'>b</span>
+      </div>
+      <div>
+        <span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.1."}'>xx1</span>
+        <span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.1."}'>a</span>
+        <span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.1."}'>b</span>
+      </div>
+      <div>
+        <span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.2."}'>xx2</span>
+        <span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.2."}'>a</span>
+        <span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.2."}'>b</span>
+      </div>
+      <div>
+        <span template='{"type":"textContent","path":"idx","rootPath":"foo1.bar1.3."}'>xx3</span>
+        <span template='{"type":"textContent","path":"a","rootPath":"foo1.bar1.3."}'>a</span>
+        <span template='{"type":"textContent","path":"b","rootPath":"foo1.bar1.3."}'>b</span>
+      </div>
+    </div>
+  </div>
+
+
+
+  <template id="template">
+    <div>
+      <span template='{"type":"textContent","path":"idx"}'></span>
+      <span template='{"type":"textContent","path":"a"}'></span>
+      <span template='{"type":"textContent","path":"b"}'></span>
+    </div>
+  </template>
+
+  <script type="application/javascript;version=1.8" src="chrome://browser/content/devtools/app-manager/template.js"></script>
+  <script type="application/javascript;version=1.8">
+    SimpleTest.waitForExplicitFinish();
+
+    const Cu = Components.utils;
+    Cu.import("resource:///modules/devtools/gDevTools.jsm");
+    const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+    const {require} = devtools;
+    const ObservableObject = require("devtools/shared/observable-object");
+
+    let data = {
+      title: "ttt",
+      foo1: {
+        bar1: [
+          {idx: "xx0", a: "a", b: "b"},
+          {idx: "xx1", a: "a", b: "b"},
+        ],
+      },
+      foo2: {
+        foo_l10n: "foo_l10n",
+        bar_l10n: "bar_l10n"
+      },
+    };
+
+    let store = new ObservableObject(data);
+
+    let changes = [
+      {
+        exec: function() {},
+        reference: document.querySelector("#ref0")
+      },
+      {
+        exec: function() {
+          store.object.title = "xxx";
+          store.object.foo2.foo_l10n = "foo2_l10n";
+        },
+        reference: document.querySelector("#ref1")
+      },
+      {
+        exec: function() {
+          store.object.foo2 = {
+            foo_l10n: "yyy",
+            bar_l10n: "zzz",
+          }
+        },
+        reference: document.querySelector("#ref2")
+      },
+      {
+        exec: function() {
+          let items = [];
+          for (let i = 2; i < 5; i++) {
+            items.push({idx: "xx" + i, a: "a", b: "b"});
+          }
+
+          store.object.foo1.bar1 = store.object.foo1.bar1.concat(items);
+        },
+        reference: document.querySelector("#ref3")
+      },
+      {
+        exec: function() {
+          store.object.foo1.bar1.pop();
+        },
+        reference: document.querySelector("#ref4")
+      },
+    ];
+
+    function compare(node1, node2) {
+      let text1 = node1.innerHTML;
+      let text2 = node2.innerHTML;
+      text1 = text1.replace(/\n/g,"");
+      text2 = text2.replace(/\n/g,"");
+      text1 = text1.replace(/\s+/g,"");
+      text2 = text2.replace(/\s+/g,"");
+      return text1 == text2;
+    }
+
+
+    let root = document.querySelector("#root");
+
+    let t = new Template(root, store, (prop, args) => {
+      return prop + ":" + args.join("/");
+    });
+
+    t.start();
+
+    for (let i = 0; i < changes.length; i++) {
+      let change = changes[i];
+      change.exec();
+      ok(compare(change.reference, root), "Content " + i + " looks good.");
+    }
+  SimpleTest.finish();
+
+  </script>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/devtools/app-manager/webapps-store.js
@@ -0,0 +1,207 @@
+/* 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 ObservableObject = require("devtools/shared/observable-object");
+const promise = require("sdk/core/promise");
+const {Connection} = require("devtools/client/connection-manager");
+
+const {Cu} = require("chrome");
+const dbgClient = Cu.import("resource://gre/modules/devtools/dbg-client.jsm");
+dbgClient.UnsolicitedNotifications.appOpen = "appOpen";
+dbgClient.UnsolicitedNotifications.appClose = "appClose"
+
+const _knownWebappsStores = new WeakMap();
+
+let WebappsStore;
+
+module.exports = WebappsStore = function(connection) {
+  // If we already know about this connection,
+  // let's re-use the existing store.
+  if (_knownWebappsStores.has(connection)) {
+    return _knownWebappsStores.get(connection);
+  }
+
+  _knownWebappsStores.set(connection, this);
+
+  ObservableObject.call(this, {});
+
+  this._resetStore();
+
+  this._destroy = this._destroy.bind(this);
+  this._onStatusChanged = this._onStatusChanged.bind(this);
+
+  this._connection = connection;
+  this._connection.once(Connection.Events.DESTROYED, this._destroy);
+  this._connection.on(Connection.Events.STATUS_CHANGED, this._onStatusChanged);
+  this._onStatusChanged();
+  return this;
+}
+
+WebappsStore.prototype = {
+  _destroy: function() {
+    this._connection.off(Connection.Events.STATUS_CHANGED, this._onStatusChanged);
+    _knownWebappsStores.delete(this._connection);
+    this._connection = null;
+  },
+
+  _resetStore: function() {
+    this.object.all = []; // list of app objects
+    this.object.running = []; // list of manifests
+  },
+
+  _getAppFromManifest: function(manifest) {
+    for (let app of this.object.all) {
+      if (app.manifestURL == manifest) {
+        return app;
+      }
+    }
+    return null;
+  },
+
+  _onStatusChanged: function() {
+    if (this._connection.status == Connection.Status.CONNECTED) {
+      this._listTabs();
+    } else {
+      this._resetStore();
+    }
+  },
+
+  _listTabs: function() {
+    this._connection.client.listTabs((resp) => {
+      this._webAppsActor = resp.webappsActor;
+      this._feedStore();
+    });
+  },
+
+  _feedStore: function(deviceFront, webAppsActor) {
+    this._listenToApps();
+    this._getAllApps()
+    .then(this._getRunningApps.bind(this))
+    .then(this._getAppsIcons.bind(this))
+  },
+
+  _listenToApps: function() {
+    let deferred = promise.defer();
+    let client = this._connection.client;
+
+    let request = {
+      to: this._webAppsActor,
+      type: "watchApps"
+    };
+
+    client.request(request, (res) => {
+      if (res.error) {
+        return deferred.reject(res.error);
+      }
+
+      client.addListener("appOpen", (type, { manifestURL }) => {
+        this._onAppOpen(manifestURL);
+      });
+
+      client.addListener("appClose", (type, { manifestURL }) => {
+        this._onAppClose(manifestURL);
+      });
+
+      return deferred.resolve();
+    })
+    return deferred.promise;
+  },
+
+  _getAllApps: function() {
+    let deferred = promise.defer();
+    let request = {
+      to: this._webAppsActor,
+      type: "getAll"
+    };
+
+    this._connection.client.request(request, (res) => {
+      if (res.error) {
+        return deferred.reject(res.error);
+      }
+      let apps = res.apps;
+      for (let a of apps) {
+        a.running = false;
+      }
+      this.object.all = apps;
+      return deferred.resolve();
+    });
+    return deferred.promise;
+  },
+
+  _getRunningApps: function() {
+    let deferred = promise.defer();
+    let request = {
+      to: this._webAppsActor,
+      type: "listRunningApps"
+    };
+
+    this._connection.client.request(request, (res) => {
+      if (res.error) {
+        return deferred.reject(res.error);
+      }
+
+      let manifests = res.apps;
+      this.object.running = manifests;
+
+      for (let m of manifests) {
+        let a = this._getAppFromManifest(m);
+        if (a) {
+          a.running = true;
+        } else {
+          return deferred.reject("Unexpected manifest: " + m);
+        }
+      }
+
+      return deferred.resolve();
+    });
+    return deferred.promise;
+  },
+
+  _getAppsIcons: function() {
+    let deferred = promise.defer();
+    let allApps = this.object.all;
+
+    let request = {
+      to: this._webAppsActor,
+      type: "getIconAsDataURL"
+    };
+
+    let client = this._connection.client;
+
+    let idx = 0;
+    (function getIcon() {
+      if (idx == allApps.length) {
+        return deferred.resolve();
+      }
+      let a = allApps[idx++];
+      request.manifestURL = a.manifestURL;
+      return client.request(request, (res) => {
+        if (res.url) {
+          a.iconURL = res.url;
+        }
+        getIcon();
+      });
+    })();
+
+    return deferred.promise;
+  },
+
+  _onAppOpen: function(manifest) {
+    let a = this._getAppFromManifest(manifest);
+    a.running = true;
+    let running = this.object.running;
+    if (running.indexOf(manifest) < 0) {
+      this.object.running.push(manifest);
+    }
+  },
+
+  _onAppClose: function(manifest) {
+    let a = this._getAppFromManifest(manifest);
+    a.running = false;
+    let running = this.object.running;
+    this.object.running = running.filter((m) => {
+      return m != manifest;
+    });
+  },
+}
--- a/browser/devtools/jar.mn
+++ b/browser/devtools/jar.mn
@@ -60,8 +60,9 @@ browser.jar:
     content/browser/devtools/framework/toolbox-options.js              (framework/toolbox-options.js)
 *   content/browser/devtools/framework/toolbox.xul                     (framework/toolbox.xul)
     content/browser/devtools/framework/toolbox.css                     (framework/toolbox.css)
     content/browser/devtools/inspector/inspector.xul                   (inspector/inspector.xul)
     content/browser/devtools/inspector/inspector.css                   (inspector/inspector.css)
     content/browser/devtools/connect.xhtml                             (framework/connect/connect.xhtml)
     content/browser/devtools/connect.css                               (framework/connect/connect.css)
     content/browser/devtools/connect.js                                (framework/connect/connect.js)
+    content/browser/devtools/app-manager/template.js                   (app-manager/content/template.js)
--- a/browser/devtools/moz.build
+++ b/browser/devtools/moz.build
@@ -17,10 +17,10 @@ DIRS += [
     'debugger',
     'netmonitor',
     'layoutview',
     'shared',
     'responsivedesign',
     'framework',
     'profiler',
     'fontinspector',
-
+    'app-manager',
 ]
--- a/browser/devtools/shared/observable-object.js
+++ b/browser/devtools/shared/observable-object.js
@@ -1,9 +1,9 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
+/* 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/. */
 
 /**
  * ObservableObject
  *
  * An observable object is a JSON-like object that throws
  * events when its direct properties or properties of any
@@ -23,31 +23,34 @@
  *   let emitter = new ObservableObject({ x: { y: [10] } });
  *   emitter.on("set", console.log);
  *   emitter.on("get", console.log);
  *   let obj = emitter.object;
  *   obj.x.y[0] = 50;
  *
  */
 
+"use strict";
+
 const EventEmitter = require("devtools/shared/event-emitter");
 
 function ObservableObject(object = {}) {
+  EventEmitter.decorate(this);
   let handler = new Handler(this);
   this.object = new Proxy(object, handler);
   handler._wrappers.set(this.object, object);
   handler._paths.set(object, []);
 }
 
-exports.ObservableObject = ObservableObject;
+module.exports = ObservableObject;
 
-ObservableObject.prototype = new EventEmitter();
-
-function isObject(value) {
-  return Object(value) === value;
+function isObject(x) {
+  if (typeof x === "object")
+    return x !== null;
+  return typeof x === "function";
 }
 
 function Handler(emitter) {
   this._emitter = emitter;
   this._wrappers = new WeakMap();
   this._paths = new WeakMap();
 }
 
@@ -66,17 +69,17 @@ Handler.prototype = {
       value = wrapper;
     }
     return [value, path];
   },
   unwrap: function(target, key, value) {
     if (!isObject(value) || !this._wrappers.has(value)) {
       return [value, this._paths.get(target).concat(key)];
     }
-    return [this._wrappers.get(value), this._paths.get(value)];
+    return [this._wrappers.get(value), this._paths.get(target).concat(key)];
   },
   get: function(target, key) {
     let value = target[key];
     let [wrapped, path] = this.wrap(target, key, value);
     this._emitter.emit("get", path, value);
     return wrapped;
   },
   set: function(target, key, value) {
--- a/browser/devtools/shared/test/browser_observableobject.js
+++ b/browser/devtools/shared/test/browser_observableobject.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 function test() {
   let tmp = {};
   Cu.import("resource://gre/modules/devtools/Loader.jsm", tmp);
-  let {ObservableObject} = tmp.devtools.require("devtools/shared/observable-object");
+  let ObservableObject = tmp.devtools.require("devtools/shared/observable-object");
 
   let rawObject = {};
   let oe = new ObservableObject(rawObject);
 
   function str(o) {
     return JSON.stringify(o);
   }
 
@@ -35,30 +35,32 @@ function test() {
     {type: "set", path: "bar", value: {}},
     {type: "set", path: "foo", value: [{a:42}]},
     {type: "get", path: "foo", value: [{a:42}]},
     {type: "get", path: "foo.0", value: {a:42}},
     {type: "get", path: "foo.0.a", value: 42},
     {type: "get", path: "foo", value: [{a:42}]},
     {type: "get", path: "foo.0", value: {a:42}},
     {type: "set", path: "foo.0.a", value: 2},
+    {type: "get", path: "foo", value: [{a:2}]},
+    {type: "get", path: "bar", value: {}},
+    {type: "set", path: "foo.1", value: {}},
   ];
 
   function callback(event, path, value) {
     oe.off("get", callback);
+    ok(event, "event defined");
+    ok(path, "path defined");
     let e = expected[index];
     is(event, e.type, "[" + index + "] Right event received");
     is(path.join("."), e.path, "[" + index + "] Path valid");
     is(str(value), str(e.value), "[" + index + "] Value valid");
     index++;
     areObjectsSynced();
     oe.on("get", callback);
-    if (index == expected.length) {
-      finish();
-    }
   }
 
   oe.on("set", callback);
   oe.on("get", callback);
 
   oe.object.foo = 4;
   oe.object.foo;
   Object.getOwnPropertyDescriptor(oe.object, "foo")
@@ -67,9 +69,14 @@ function test() {
   oe.object.bar = {};
   oe.object.bar;
   oe.object.bar.a = [1,2,3,4];
   Object.defineProperty(oe.object.bar, "mop", {value:1});
   oe.object.bar = {};
   oe.object.foo = [{a:42}];
   oe.object.foo[0].a;
   oe.object.foo[0].a = 2;
+  oe.object.foo[1] = oe.object.bar;
+
+  is(index, expected.length, "Event count is right");
+
+  finish();
 }
--- a/browser/devtools/styleeditor/StyleEditorDebuggee.jsm
+++ b/browser/devtools/styleeditor/StyleEditorDebuggee.jsm
@@ -90,22 +90,27 @@ StyleEditorDebuggee.prototype = {
     return null;
   },
 
   /**
    * Clear stylesheets and state.
    */
   clear: function() {
     this.baseURI = null;
+    this.clearStyleSheets();
+  },
 
+  /**
+   * Clear stylesheets.
+   */
+  clearStyleSheets: function() {
     for (let stylesheet of this.styleSheets) {
       stylesheet.destroy();
     }
     this.styleSheets = [];
-
     this.emit("stylesheets-cleared");
   },
 
   /**
    * Called when target is created or has navigated.
    * Clear previous sheets and request new document's
    */
   _onNewDocument: function() {
@@ -132,16 +137,19 @@ StyleEditorDebuggee.prototype = {
    * all the stylesheets available on load.
    *
    * @param  {string} type
    *         Event type
    * @param  {object} request
    *         Object with 'styleSheets' array of actor forms
    */
   _onDocumentLoad: function(type, request) {
+    if (this.styleSheets.length > 0) {
+      this.clearStyleSheets();
+    }
     let sheets = [];
     for (let form of request.styleSheets) {
       let sheet = this._addStyleSheet(form);
       sheets.push(sheet);
     }
     this.emit("document-load", sheets);
   },
 
--- a/browser/devtools/styleeditor/test/Makefile.in
+++ b/browser/devtools/styleeditor/test/Makefile.in
@@ -21,16 +21,17 @@ MOCHITEST_BROWSER_FILES := \
                  browser_styleeditor_loading.js \
                  browser_styleeditor_new.js \
                  browser_styleeditor_pretty.js \
                  browser_styleeditor_private_perwindowpb.js \
                  browser_styleeditor_sv_keynav.js \
                  browser_styleeditor_sv_resize.js \
                  browser_styleeditor_bug_740541_iframes.js \
                  browser_styleeditor_bug_851132_middle_click.js \
+                 browser_styleeditor_bug_870339.js \
                  browser_styleeditor_nostyle.js \
                  browser_styleeditor_reload.js \
                  head.js \
                  four.html \
                  head.js \
                  import.css \
                  import.html \
                  import2.css \
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_bug_870339.js
@@ -0,0 +1,46 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function test()
+{
+  const SIMPLE = TEST_BASE_HTTP + "simple.css";
+  const DOCUMENT_WITH_ONE_STYLESHEET = "data:text/html;charset=UTF-8," +
+          encodeURIComponent(
+            ["<!DOCTYPE html>",
+             "<html>",
+             " <head>",
+             "  <title>Bug 870339</title>",
+             '  <link rel="stylesheet" type="text/css" href="'+SIMPLE+'">',
+             " </head>",
+             " <body>",
+             " </body>",
+             "</html>"
+            ].join("\n"));
+
+  waitForExplicitFinish();
+  addTabAndOpenStyleEditor(function (aPanel) {
+    let debuggee = aPanel._debuggee;
+
+    // Spam the _onNewDocument callback multiple times before the
+    // StyleEditorActor has a chance to respond to the first one.
+    const SPAM_COUNT = 2;
+    for (let i=0; i<SPAM_COUNT; ++i) {
+      debuggee._onNewDocument();
+    }
+
+    // Wait for the StyleEditorActor to respond to each "newDocument"
+    // message.
+    let loadCount = 0;
+    debuggee.on("document-load", function () {
+      ++loadCount;
+      if (loadCount == SPAM_COUNT) {
+        // No matter how large SPAM_COUNT is, the number of style
+        // sheets should never be more than the number of style sheets
+        // in the document.
+        is(debuggee.styleSheets.length, 1, "correct style sheet count");
+        finish();
+      }
+    });
+  });
+  content.location = DOCUMENT_WITH_ONE_STYLESHEET;
+}
--- a/browser/metro/components/HelperAppDialog.js
+++ b/browser/metro/components/HelperAppDialog.js
@@ -36,18 +36,20 @@ HelperAppLauncherDialog.prototype = {
       this._showDownloadInfobar(aLauncher);
     }
   },
 
   _getDownloadSize: function dv__getDownloadSize (aSize) {
     let displaySize = DownloadUtils.convertByteUnits(aSize);
     if (displaySize[0] > 0) // [0] is size, [1] is units
       return displaySize.join("");
-    else
-      return Strings.browser.GetStringFromName("downloadsUnknownSize");
+    else {
+      let browserBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
+      return browserBundle.GetStringFromName("downloadsUnknownSize");
+    }
   },
 
   _getChromeWindow: function (aWindow) {
       let chromeWin = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                             .getInterface(Ci.nsIWebNavigation)
                             .QueryInterface(Ci.nsIDocShellTreeItem)
                             .rootTreeItem
                             .QueryInterface(Ci.nsIInterfaceRequestor)
--- a/browser/metro/theme/defines.inc
+++ b/browser/metro/theme/defines.inc
@@ -14,17 +14,20 @@
 %define selected_color #FF8000
 
 %define urlbar_border_color #BFC6CC
 %define urlbar_edit_height 36px
 
 %define toolbar_vertical_spacing 5px
 %define toolbar_horizontal_spacing 20px
 %define toolbar_snapped_horizontal_spacing 10px
-%define toolbar_height 68px
+
+% XXX Per UX mockups, this should be 68px, but we need to make this
+% one pixel higher to reduce button clipping until bug 905453 is resolved
+%define toolbar_height 69px
 %define labelled_toolbar_height 90px
 %define tabs_height 178px
 %define findbar_height 54px
 
 %define progress_height 5px
 
 %define metro_orange #FF8000
 
--- a/build/mobile/robocop/Actions.java.in
+++ b/build/mobile/robocop/Actions.java.in
@@ -78,12 +78,19 @@ public interface Actions {
      * @param key The special key to send
      */
     void sendSpecialKey(SpecialKey key);
     void sendKeyCode(int keyCode);
 
     void drag(int startingX, int endingX, int startingY, int endingY);
 
     /**
+     * This is the implementation of clickLongOnScreen from Robotium 4.0 since this sometimes fails for Robotium 3.6
+     * TODO : Remove this when Robotium is updated
+     */
+
+    void clickLongOnScreen(float x, float y);
+
+    /**
      * Run a sql query on the specified database
      */
     public Cursor querySql(String dbPath, String sql);
 }
--- a/build/mobile/robocop/FennecNativeActions.java.in
+++ b/build/mobile/robocop/FennecNativeActions.java.in
@@ -17,17 +17,19 @@ import java.util.ArrayList;
 import android.app.Activity;
 import android.app.Instrumentation;
 import android.content.Context;
 import android.database.Cursor;
 import android.os.SystemClock;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.KeyEvent;
+import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewConfiguration;
 
 import com.jayway.android.robotium.solo.Solo;
 
 import static @ANDROID_PACKAGE_NAME@.FennecNativeDriver.LogLevel;
 
 public class FennecNativeActions implements Actions {
     private Solo mSolo;
     private Instrumentation mInstr;
@@ -453,16 +455,51 @@ public class FennecNativeActions impleme
     public void sendKeys(String input) {
         mInstr.sendStringSync(input);
     }
 
     public void drag(int startingX, int endingX, int startingY, int endingY) {
         mSolo.drag(startingX, endingX, startingY, endingY, 10);
     }
 
+     /**
+     * This is the implementation of clickLongOnScreen from Robotium 4.0 since this sometimes fails for Robotium 3.6
+     * TODO : Remove this when Robotium is updated
+     */
+
+    public void clickLongOnScreen(float x, float y) {
+    	boolean successfull = false;
+        int retry = 0;
+        long downTime = SystemClock.uptimeMillis();
+        long eventTime = SystemClock.uptimeMillis();
+        MotionEvent event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_DOWN, x, y, 0);
+
+        while(!successfull && retry < 10) {
+            try{
+                mInstr.sendPointerSync(event);
+                successfull = true;
+            }catch(SecurityException e){
+                FennecNativeDriver.log(LogLevel.ERROR, e);
+                retry++;
+            }
+        }
+
+        mAsserter.ok(successfull, "Trying to click on long on screen at (" + x + "," + y + ")", "Was able to click long on screen");
+
+        eventTime = SystemClock.uptimeMillis();
+        event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE, x + 1.0f, y + 1.0f, 0);
+        mInstr.sendPointerSync(event);
+        mSolo.sleep(((int)(ViewConfiguration.getLongPressTimeout() * 2.5f)));
+
+        eventTime = SystemClock.uptimeMillis();
+        event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, x, y, 0);
+        mInstr.sendPointerSync(event);
+        mSolo.sleep(500);
+    }
+
     public Cursor querySql(String dbPath, String sql) {
         try {
             return (Cursor)mQuerySql.invoke(mRobocopApi, dbPath, sql);
         } catch(InvocationTargetException ex) {
             Log.e(LOGTAG, "Error invoking method", ex);
         } catch(IllegalAccessException ex) {
             Log.e(LOGTAG, "Error using field", ex);
         }
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -772,8 +772,11 @@ pref("media.useAudioChannelService", fal
 // Turn on the CSP 1.0 parser for Content Security Policy headers
 pref("security.csp.speccompliant", true);
 
 // Enable hardware-accelerated Skia canvas
 pref("gfx.canvas.azure.backends", "skia");
 pref("gfx.canvas.azure.accelerated", true);
 
 pref("general.useragent.override.youtube.com", "Android; Tablet;#Android; Mobile;");
+
+// When true, phone number linkification is enabled.
+pref("browser.ui.linkify.phone", false);
--- a/mobile/android/base/ActivityHandlerHelper.java
+++ b/mobile/android/base/ActivityHandlerHelper.java
@@ -31,17 +31,16 @@ import java.util.concurrent.TimeUnit;
 
 public class ActivityHandlerHelper implements GeckoEventListener {
     private static final String LOGTAG = "GeckoActivityHandlerHelper";
 
     private final ConcurrentLinkedQueue<String> mFilePickerResult;
 
     private final ActivityResultHandlerMap mActivityResultHandlerMap;
     private final FilePickerResultHandlerSync mFilePickerResultHandlerSync;
-    private final AwesomebarResultHandler mAwesomebarResultHandler;
     private final CameraImageResultHandler mCameraImageResultHandler;
     private final CameraVideoResultHandler mCameraVideoResultHandler;
 
     public interface FileResultHandler {
         public void gotFile(String filename);
     }
 
     @SuppressWarnings("serial")
@@ -53,17 +52,16 @@ public class ActivityHandlerHelper imple
                     GeckoAppShell.sendEventToGecko(GeckoEvent.createNoOpEvent());
                     return true;
                 }
                 return false;
             }
         };
         mActivityResultHandlerMap = new ActivityResultHandlerMap();
         mFilePickerResultHandlerSync = new FilePickerResultHandlerSync(mFilePickerResult);
-        mAwesomebarResultHandler = new AwesomebarResultHandler();
         mCameraImageResultHandler = new CameraImageResultHandler(mFilePickerResult);
         mCameraVideoResultHandler = new CameraVideoResultHandler(mFilePickerResult);
         GeckoAppShell.getEventDispatcher().registerEventListener("FilePicker:Show", this);
     }
 
     @Override
     public void handleMessage(String event, final JSONObject message) {
         if (event.equals("FilePicker:Show")) {
@@ -86,20 +84,16 @@ public class ActivityHandlerHelper imple
                     }
                     GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent(
                         "FilePicker:Result", message.toString()));
                 }
             });
         }
     }
 
-    public int makeRequestCodeForAwesomebar() {
-        return mActivityResultHandlerMap.put(mAwesomebarResultHandler);
-    }
-
     public int makeRequestCode(ActivityResultHandler aHandler) {
         return mActivityResultHandlerMap.put(aHandler);
     }
 
     public void startIntentForActivity (Activity activity, Intent intent, ActivityResultHandler activityResultHandler) {
         activity.startActivityForResult(intent, mActivityResultHandlerMap.put(activityResultHandler));
     }
 
--- a/mobile/android/base/AndroidManifest.xml.in
+++ b/mobile/android/base/AndroidManifest.xml.in
@@ -225,21 +225,16 @@
         <activity android:name="org.mozilla.gecko.VideoPlayer"
                   android:configChanges="keyboard|keyboardHidden|mcc|mnc|orientation"
                   android:theme="@android:style/Theme.NoTitleBar">
             <intent-filter>
                 <action android:name="org.mozilla.gecko.PLAY_VIDEO" />
             </intent-filter>
         </activity>
 
-        <activity android:name="org.mozilla.gecko.AwesomeBar"
-                  android:theme="@style/Gecko.AwesomeBar"
-                  android:configChanges="orientation|screenSize"
-                  android:windowSoftInputMode="stateUnspecified|adjustResize"/>
-
         <activity android:name="org.mozilla.gecko.GeckoPreferences"
                   android:theme="@style/Gecko.Preferences"
                   android:label="@string/settings_title"
                   android:configChanges="orientation|screenSize"
                   android:excludeFromRecents="true"/>
 
         <provider android:name="org.mozilla.gecko.db.BrowserProvider"
                   android:authorities="@ANDROID_PACKAGE_NAME@.db.browser"
--- a/mobile/android/base/AnimatedHeightLayout.java
+++ b/mobile/android/base/AnimatedHeightLayout.java
@@ -12,18 +12,22 @@ import android.view.animation.Animation;
 import android.view.animation.DecelerateInterpolator;
 import android.widget.RelativeLayout;
 
 public class AnimatedHeightLayout extends RelativeLayout {
     private static final String LOGTAG = "GeckoAnimatedHeightLayout";
     private static final int ANIMATION_DURATION = 100;
     private boolean mAnimating = false;
 
+    public AnimatedHeightLayout(Context context) {
+        super(context, null);
+    }
+
     public AnimatedHeightLayout(Context context, AttributeSet attrs) {
-        super(context, attrs);
+        super(context, attrs, 0);
     }
 
     public AnimatedHeightLayout(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
     }
 
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/AutocompleteHandler.java
@@ -0,0 +1,10 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * 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/. */
+
+package org.mozilla.gecko;
+
+public interface AutocompleteHandler {
+    void onAutocomplete(String res);
+}
deleted file mode 100644
--- a/mobile/android/base/AwesomeBar.java
+++ /dev/null
@@ -1,730 +0,0 @@
-/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
- * 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/. */
-
-package org.mozilla.gecko;
-
-import org.mozilla.gecko.db.BrowserContract.Combined;
-import org.mozilla.gecko.db.BrowserDB;
-import org.mozilla.gecko.gfx.BitmapUtils;
-import org.mozilla.gecko.health.BrowserHealthRecorder;
-import org.mozilla.gecko.util.GamepadUtils;
-import org.mozilla.gecko.util.StringUtils;
-import org.mozilla.gecko.util.ThreadUtils;
-import org.mozilla.gecko.util.UiAsyncTask;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Configuration;
-import android.graphics.Bitmap;
-import android.net.Uri;
-import android.os.Bundle;
-import android.text.Editable;
-import android.text.InputType;
-import android.text.Spanned;
-import android.text.TextUtils;
-import android.text.TextWatcher;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.ContextMenu;
-import android.view.ContextMenu.ContextMenuInfo;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.MenuItem;
-import android.view.MenuInflater;
-import android.view.View;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.ImageButton;
-import android.widget.TabWidget;
-import android.widget.Toast;
-
-import org.json.JSONObject;
-
-import java.net.URLEncoder;
-
-interface AutocompleteHandler {
-    void onAutocomplete(String res);
-}
-
-public class AwesomeBar extends GeckoActivity
-                        implements AutocompleteHandler,
-                                   TextWatcher {
-    private static final String LOGTAG = "GeckoAwesomeBar";
-
-    public static final String URL_KEY = "url";
-    public static final String TAB_KEY = "tab";
-    public static final String CURRENT_URL_KEY = "currenturl";
-    public static final String TARGET_KEY = "target";
-    public static final String SEARCH_KEY = "search";
-    public static final String TITLE_KEY = "title";
-    public static final String USER_ENTERED_KEY = "user_entered";
-    public static final String READING_LIST_KEY = "reading_list";
-    public static enum Target { NEW_TAB, CURRENT_TAB, PICK_SITE };
-
-    private String mTarget;
-    private AwesomeBarTabs mAwesomeTabs;
-    private CustomEditText mText;
-    private ImageButton mGoButton;
-    private ContextMenuSubject mContextMenuSubject;
-    private boolean mDelayRestartInput;
-    // The previous autocomplete result returned to us
-    private String mAutoCompleteResult = "";
-    // The user typed part of the autocomplete result
-    private String mAutoCompletePrefix = null;
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        LayoutInflater.from(this).setFactory(this);
-
-        super.onCreate(savedInstanceState);
-
-        Log.d(LOGTAG, "creating awesomebar");
-
-        setContentView(R.layout.awesomebar);
-
-        mGoButton = (ImageButton) findViewById(R.id.awesomebar_button);
-        mText = (CustomEditText) findViewById(R.id.awesomebar_text);
-
-        TabWidget tabWidget = (TabWidget) findViewById(android.R.id.tabs);
-        tabWidget.setDividerDrawable(null);
-
-        mAwesomeTabs = (AwesomeBarTabs) findViewById(R.id.awesomebar_tabs);
-        mAwesomeTabs.setOnUrlOpenListener(new AwesomeBarTabs.OnUrlOpenListener() {
-            @Override
-            public void onUrlOpen(String url, String title) {
-                openUrlAndFinish(url, title, false);
-            }
-
-            @Override
-            public void onSearch(SearchEngine engine, String text) {
-                Intent resultIntent = new Intent();
-                resultIntent.putExtra(URL_KEY, text);
-                resultIntent.putExtra(TARGET_KEY, mTarget);
-                resultIntent.putExtra(SEARCH_KEY, engine.name);
-                recordSearch(engine.identifier, "barsuggest");
-                finishWithResult(resultIntent);
-            }
-
-            @Override
-            public void onEditSuggestion(final String text) {
-                ThreadUtils.postToUiThread(new Runnable() {
-                    @Override
-                    public void run() {
-                        mText.setText(text);
-                        mText.setSelection(mText.getText().length());
-                        mText.requestFocus();
-                    }
-                });
-            }
-
-            @Override
-            public void onSwitchToTab(final int tabId) {
-                Intent resultIntent = new Intent();
-                resultIntent.putExtra(TAB_KEY, Integer.toString(tabId));
-                finishWithResult(resultIntent);
-            }
-        });
-
-        mGoButton.setOnClickListener(new Button.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                openUserEnteredAndFinish(mText.getText().toString());
-            }
-        });
-
-        Intent intent = getIntent();
-        String currentUrl = intent.getStringExtra(CURRENT_URL_KEY);
-        if (currentUrl != null) {
-            mText.setText(currentUrl);
-            mText.selectAll();
-        }
-
-        mTarget = intent.getStringExtra(TARGET_KEY);
-        if (mTarget.equals(Target.CURRENT_TAB.name())) {
-            Tab tab = Tabs.getInstance().getSelectedTab();
-            if (tab != null && tab.isPrivate()) {
-                BrowserToolbarBackground mAddressBarBg = (BrowserToolbarBackground) findViewById(R.id.address_bar_bg);
-                mAddressBarBg.setPrivateMode(true);
-
-                ShapedButton mTabs = (ShapedButton) findViewById(R.id.dummy_tab);
-                if (mTabs != null)
-                    mTabs.setPrivateMode(true);
-
-                mText.setPrivateMode(true);
-            }
-        }
-        mAwesomeTabs.setTarget(mTarget);
-
-        mText.setOnKeyPreImeListener(new CustomEditText.OnKeyPreImeListener() {
-            @Override
-            public boolean onKeyPreIme(View v, int keyCode, KeyEvent event) {
-                // We only want to process one event per tap
-                if (event.getAction() != KeyEvent.ACTION_DOWN)
-                    return false;
-
-                if (keyCode == KeyEvent.KEYCODE_ENTER) {
-                    // If the AwesomeBar has a composition string, don't submit the text yet.
-                    // ENTER is needed to commit the composition string.
-                    Editable content = mText.getText();
-                    if (!hasCompositionString(content)) {
-                        openUserEnteredAndFinish(content.toString());
-                        return true;
-                    }
-                }
-
-                // If input method is in fullscreen mode, we want to dismiss
-                // it instead of closing awesomebar straight away.
-                InputMethodManager imm =
-                        (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
-                if (keyCode == KeyEvent.KEYCODE_BACK && !imm.isFullscreenMode()) {
-                    return handleBackKey();
-                }
-
-                return false;
-            }
-        });
-
-        mText.addTextChangedListener(this);
-
-        mText.setOnKeyListener(new View.OnKeyListener() {
-            @Override
-            public boolean onKey(View v, int keyCode, KeyEvent event) {
-                if (keyCode == KeyEvent.KEYCODE_ENTER) {
-                    if (event.getAction() != KeyEvent.ACTION_DOWN)
-                        return true;
-
-                    openUserEnteredAndFinish(mText.getText().toString());
-                    return true;
-                } else if (GamepadUtils.isBackKey(event)) {
-                    return handleBackKey();
-                } else {
-                    return false;
-                }
-            }
-        });
-
-        mText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
-            @Override
-            public void onFocusChange(View v, boolean hasFocus) {
-                if (v == null || hasFocus) {
-                    return;
-                }
-
-                InputMethodManager imm = (InputMethodManager)
-                    getSystemService(Context.INPUT_METHOD_SERVICE);
-                try {
-                    imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
-                } catch (NullPointerException e) {
-                    Log.e(LOGTAG, "InputMethodManagerService, why are you throwing"
-                                  + " a NullPointerException? See bug 782096", e);
-                }
-            }
-        });
-
-        boolean showReadingList = intent.getBooleanExtra(READING_LIST_KEY, false);
-        if (showReadingList) {
-            BookmarksTab bookmarksTab = mAwesomeTabs.getBookmarksTab();
-            bookmarksTab.setShowReadingList(true);
-            mAwesomeTabs.setCurrentItemByTag(bookmarksTab.getTag());
-        }
-    }
-
-    private boolean handleBackKey() {
-        // Let mAwesomeTabs try to handle the back press, since we may be in a
-        // bookmarks sub-folder.
-        if (mAwesomeTabs.onBackPressed())
-            return true;
-
-        // If mAwesomeTabs.onBackPressed() returned false, we didn't move up
-        // a folder level, so just exit the activity.
-        cancelAndFinish();
-        return true;
-    }
-
-    @Override
-    public void onConfigurationChanged(Configuration newConfiguration) {
-        super.onConfigurationChanged(newConfiguration);
-    }
-
-    @Override
-    public boolean onSearchRequested() {
-        cancelAndFinish();
-        return true;
-    }
-
-    private void updateGoButton(String text) {
-        if (text.length() == 0) {
-            mGoButton.setVisibility(View.GONE);
-            return;
-        }
-
-        mGoButton.setVisibility(View.VISIBLE);
-
-        int imageResource = R.drawable.ic_awesomebar_go;
-        String contentDescription = getString(R.string.go);
-        int imeAction = EditorInfo.IME_ACTION_GO;
-
-        int actionBits = mText.getImeOptions() & EditorInfo.IME_MASK_ACTION;
-        if (StringUtils.isSearchQuery(text, actionBits == EditorInfo.IME_ACTION_SEARCH)) {
-            imageResource = R.drawable.ic_awesomebar_search;
-            contentDescription = getString(R.string.search);
-            imeAction = EditorInfo.IME_ACTION_SEARCH;
-        }
-
-        InputMethodManager imm = InputMethods.getInputMethodManager(mText.getContext());
-        if (imm == null) {
-            return;
-        }
-        boolean restartInput = false;
-        if (actionBits != imeAction) {
-            int optionBits = mText.getImeOptions() & ~EditorInfo.IME_MASK_ACTION;
-            mText.setImeOptions(optionBits | imeAction);
-
-            mDelayRestartInput = (imeAction == EditorInfo.IME_ACTION_GO) &&
-                                 (InputMethods.shouldDelayAwesomebarUpdate(mText.getContext()));
-            if (!mDelayRestartInput) {
-                restartInput = true;
-            }
-        } else if (mDelayRestartInput) {
-            // Only call delayed restartInput when actionBits == imeAction
-            // so if there are two restarts in a row, the first restarts will
-            // be discarded and the second restart will be properly delayed
-            mDelayRestartInput = false;
-            restartInput = true;
-        }
-        if (restartInput) {
-            updateKeyboardInputType();
-            imm.restartInput(mText);
-            mGoButton.setImageResource(imageResource);
-            mGoButton.setContentDescription(contentDescription);
-        }
-    }
-
-    private void updateKeyboardInputType() {
-        // If the user enters a space, then we know they are entering search terms, not a URL.
-        // We can then switch to text mode so,
-        // 1) the IME auto-inserts spaces between words
-        // 2) the IME doesn't reset input keyboard to Latin keyboard.
-        String text = mText.getText().toString();
-        int currentInputType = mText.getInputType();
-        int newInputType = StringUtils.isSearchQuery(text, false)
-                           ? (currentInputType & ~InputType.TYPE_TEXT_VARIATION_URI) // Text mode
-                           : (currentInputType | InputType.TYPE_TEXT_VARIATION_URI); // URL mode
-        if (newInputType != currentInputType) {
-            mText.setRawInputType(newInputType);
-        }
-    }
-
-    private void cancelAndFinish() {
-        setResult(Activity.RESULT_CANCELED);
-        finish();
-        overridePendingTransition(R.anim.awesomebar_hold_still, R.anim.awesomebar_fade_out);
-    }
-
-    private void finishWithResult(Intent intent) {
-        setResult(Activity.RESULT_OK, intent);
-        finish();
-        overridePendingTransition(R.anim.awesomebar_hold_still, R.anim.awesomebar_fade_out);
-    }
-
-    private void openUrlAndFinish(String url) {
-        openUrlAndFinish(url, null, false);
-    }
-
-    private void openUrlAndFinish(String url, String title, boolean userEntered) {
-        Intent resultIntent = new Intent();
-        resultIntent.putExtra(URL_KEY, url);
-        if (title != null && !TextUtils.isEmpty(title))
-            resultIntent.putExtra(TITLE_KEY, title);
-        if (userEntered)
-            resultIntent.putExtra(USER_ENTERED_KEY, userEntered);
-        resultIntent.putExtra(TARGET_KEY, mTarget);
-        finishWithResult(resultIntent);
-    }
-
-    /**
-     * Record in Health Report that a search has occurred.
-     *
-     * @param identifier
-     *        a search identifier, such as "partnername". Can be null.
-     * @param where
-     *        where the search was initialized; one of the values in
-     *        {@link BrowserHealthRecorder#SEARCH_LOCATIONS}.
-     */
-    private static void recordSearch(String identifier, String where) {
-        Log.i(LOGTAG, "Recording search: " + identifier + ", " + where);
-        try {
-            JSONObject message = new JSONObject();
-            message.put("type", BrowserHealthRecorder.EVENT_SEARCH);
-            message.put("location", where);
-            message.put("identifier", identifier);
-            GeckoAppShell.getEventDispatcher().dispatchEvent(message);
-        } catch (Exception e) {
-            Log.w(LOGTAG, "Error recording search.", e);
-        }
-    }
-
-    private void openUserEnteredAndFinish(final String url) {
-        final int index = url.indexOf(' ');
-
-        // Check for a keyword if the URL looks like a search query
-        if (!StringUtils.isSearchQuery(url, true)) {
-            openUrlAndFinish(url, "", true);
-            return;
-        }
-        ThreadUtils.postToBackgroundThread(new Runnable() {
-            @Override
-            public void run() {
-                final String keyword;
-                final String keywordSearch;
-
-                if (index == -1) {
-                    keyword = url;
-                    keywordSearch = "";
-                } else {
-                    keyword = url.substring(0, index);
-                    keywordSearch = url.substring(index + 1);
-                }
-
-                final String keywordUrl = BrowserDB.getUrlForKeyword(getContentResolver(), keyword);
-                final String searchUrl = (keywordUrl != null)
-                                       ? keywordUrl.replace("%s", URLEncoder.encode(keywordSearch))
-                                       : url;
-                if (keywordUrl != null) {
-                    recordSearch(null, "barkeyword");
-                }
-                openUrlAndFinish(searchUrl, "", true);
-            }
-        });
-    }
-
-    @Override
-    public boolean onKeyDown(int keyCode, KeyEvent event) {
-        // Galaxy Note sends key events for the stylus that are outside of the
-        // valid keyCode range (see bug 758427)
-        if (keyCode > KeyEvent.getMaxKeyCode())
-            return true;
-
-        // This method is called only if the key event was not handled
-        // by any of the views, which usually means the edit box lost focus
-        if (keyCode == KeyEvent.KEYCODE_BACK ||
-            keyCode == KeyEvent.KEYCODE_MENU ||
-            keyCode == KeyEvent.KEYCODE_DPAD_UP ||
-            keyCode == KeyEvent.KEYCODE_DPAD_DOWN ||
-            keyCode == KeyEvent.KEYCODE_DPAD_LEFT ||
-            keyCode == KeyEvent.KEYCODE_DPAD_RIGHT ||
-            keyCode == KeyEvent.KEYCODE_DPAD_CENTER ||
-            keyCode == KeyEvent.KEYCODE_DEL ||
-            keyCode == KeyEvent.KEYCODE_VOLUME_UP ||
-            keyCode == KeyEvent.KEYCODE_VOLUME_DOWN ||
-            GamepadUtils.isActionKey(event)) {
-            return super.onKeyDown(keyCode, event);
-        } else if (keyCode == KeyEvent.KEYCODE_SEARCH) {
-             mText.setText("");
-             mText.requestFocus();
-             return true;
-        } else {
-            int prevSelStart = mText.getSelectionStart();
-            int prevSelEnd = mText.getSelectionEnd();
-
-            // Manually dispatch the key event to the AwesomeBar. If selection changed as
-            // a result of the key event, then give focus back to mText
-            mText.dispatchKeyEvent(event);
-
-            int curSelStart = mText.getSelectionStart();
-            int curSelEnd = mText.getSelectionEnd();
-            if (prevSelStart != curSelStart || prevSelEnd != curSelEnd) {
-                mText.requestFocusFromTouch();
-                // Restore the selection, which gets lost due to the focus switch
-                mText.setSelection(curSelStart, curSelEnd);
-            }
-            return true;
-        }
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        if (mText != null && mText.getText() != null) {
-            updateGoButton(mText.getText().toString());
-            if (mDelayRestartInput) {
-                // call updateGoButton again to force a restartInput call
-                updateGoButton(mText.getText().toString());
-            }
-        }
-
-        // Invlidate the cached value that keeps track of whether or
-        // not desktop bookmarks exist
-        BrowserDB.invalidateCachedState();
-    }
-
-    @Override
-    public void onDestroy() {
-        super.onDestroy();
-        mAwesomeTabs.destroy();
-    }
-
-    @Override
-    public void onBackPressed() {
-        // Let mAwesomeTabs try to handle the back press, since we may be in a
-        // bookmarks sub-folder.
-        if (mAwesomeTabs.onBackPressed())
-            return;
-
-        // Otherwise, just exit the awesome screen
-        cancelAndFinish();
-    }
-
-    static public class ContextMenuSubject {
-        public int id;
-        public String url;
-        public byte[] favicon;
-        public String title;
-        public String keyword;
-        public int display;
-
-        public ContextMenuSubject(int id, String url, byte[] favicon, String title, String keyword) {
-            this(id, url, favicon, title, keyword, Combined.DISPLAY_NORMAL);
-        }
-
-        public ContextMenuSubject(int id, String url, byte[] favicon, String title, String keyword, int display) {
-            this.id = id;
-            this.url = url;
-            this.favicon = favicon;
-            this.title = title;
-            this.keyword = keyword;
-            this.display = display;
-        }
-    };
-
-    @Override
-    public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
-        super.onCreateContextMenu(menu, view, menuInfo);
-        AwesomeBarTab tab = mAwesomeTabs.getAwesomeBarTabForView(view);
-        mContextMenuSubject = tab.getSubject(menu, view, menuInfo);
-    }
-
-    @Override
-    public boolean onContextItemSelected(MenuItem item) {
-        if (mContextMenuSubject == null)
-            return false;
-
-        final int id = mContextMenuSubject.id;
-        final String url = mContextMenuSubject.url;
-        final byte[] b = mContextMenuSubject.favicon;
-        final String title = mContextMenuSubject.title;
-        final String keyword = mContextMenuSubject.keyword;
-        final int display = mContextMenuSubject.display;
-
-        final int itemId = item.getItemId();
-        if (itemId == R.id.open_private_tab || itemId == R.id.open_new_tab) {
-            if (url == null) {
-                Log.e(LOGTAG, "Can't open in new tab because URL is null");
-            }
-
-            String newTabUrl = url;
-            if (display == Combined.DISPLAY_READER)
-                newTabUrl = ReaderModeUtils.getAboutReaderForUrl(url, true);
-
-            int flags = Tabs.LOADURL_NEW_TAB;
-            if (item.getItemId() == R.id.open_private_tab)
-                flags |= Tabs.LOADURL_PRIVATE;
-
-            Tabs.getInstance().loadUrl(newTabUrl, flags);
-            Toast.makeText(this, R.string.new_tab_opened, Toast.LENGTH_SHORT).show();
-
-            return true;
-        }
-
-        if (itemId == R.id.open_in_reader) {
-            if (url == null) {
-                Log.e(LOGTAG, "Can't open in reader mode because URL is null");
-            } else {
-                openUrlAndFinish(ReaderModeUtils.getAboutReaderForUrl(url, true));
-            }
-            return true;
-        }
-
-        if (itemId == R.id.edit_bookmark) {
-            new EditBookmarkDialog(this).show(id, title, url, keyword);
-            return true;
-        }
-
-        if (itemId == R.id.remove_bookmark) {
-            (new UiAsyncTask<Void, Void, Integer>(ThreadUtils.getBackgroundHandler()) {
-                private boolean mInReadingList;
-
-                @Override
-                public void onPreExecute() {
-                    mInReadingList = mAwesomeTabs.isInReadingList();
-                }
-
-                @Override
-                public Integer doInBackground(Void... params) {
-                    BrowserDB.removeBookmark(getContentResolver(), id);
-                    Integer count = mInReadingList ?
-                        BrowserDB.getReadingListCount(getContentResolver()) : 0;
-
-                    return count;
-                }
-
-                @Override
-                public void onPostExecute(Integer aCount) {
-                    int messageId = R.string.bookmark_removed;
-                    if (mInReadingList) {
-                        messageId = R.string.reading_list_removed;
-
-                        GeckoEvent e = GeckoEvent.createBroadcastEvent("Reader:Remove", url);
-                        GeckoAppShell.sendEventToGecko(e);
-
-                        // Delete from Awesomebar context menu can alter reading list bookmark count
-                        e = GeckoEvent.createBroadcastEvent("Reader:ListCountUpdated", Integer.toString(aCount));
-                        GeckoAppShell.sendEventToGecko(e);
-                    }
-
-                    Toast.makeText(AwesomeBar.this, messageId, Toast.LENGTH_SHORT).show();
-                }
-            }).execute();
-
-            return true;
-        }
-
-        if (itemId == R.id.remove_history) {
-            (new UiAsyncTask<Void, Void, Void>(ThreadUtils.getBackgroundHandler()) {
-                @Override
-                public Void doInBackground(Void... params) {
-                    BrowserDB.removeHistoryEntry(getContentResolver(), id);
-                    return null;
-                }
-
-                @Override
-                public void onPostExecute(Void result) {
-                    Toast.makeText(AwesomeBar.this, R.string.history_removed, Toast.LENGTH_SHORT).show();
-                }
-            }).execute();
-
-            return true;
-        }
-
-        if (itemId == R.id.add_to_launcher) {
-            if (url == null) {
-                Log.e(LOGTAG, "Can't add to home screen because URL is null");
-            } else {
-                Bitmap bitmap = null;
-                if (b != null) {
-                    bitmap = BitmapUtils.decodeByteArray(b);
-                }
-
-                String shortcutTitle = TextUtils.isEmpty(title) ? url.replaceAll("^([a-z]+://)?(www\\.)?", "") : title;
-                GeckoAppShell.createShortcut(shortcutTitle, url, bitmap, "");
-            }
-
-            return true;
-        }
-
-        if (itemId == R.id.share) {
-            if (url == null) {
-                Log.e(LOGTAG, "Can't share because URL is null");
-            } else {
-                GeckoAppShell.openUriExternal(url, "text/plain", "", "",
-                                              Intent.ACTION_SEND, title);
-            }
-
-            return true;
-        }
-
-        return super.onContextItemSelected(item);
-    }
-
-    public static String getReaderForUrl(String url) {
-        // FIXME: still need to define the final way to open items from
-        // reading list. For now, we're using an about:reader page.
-        return "about:reader?url=" + Uri.encode(url) + "&readingList=1";
-    }
-
-    private static boolean hasCompositionString(Editable content) {
-        Object[] spans = content.getSpans(0, content.length(), Object.class);
-        if (spans != null) {
-            for (Object span : spans) {
-                if ((content.getSpanFlags(span) & Spanned.SPAN_COMPOSING) != 0) {
-                    // Found composition string.
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    // return early if we're backspacing through the string, or have no autocomplete results
-    public void onAutocomplete(final String result) {
-        final String text = mText.getText().toString();
-
-        if (result == null) {
-            mAutoCompleteResult = "";
-            return;
-        }
-
-        if (!result.startsWith(text) || text.equals(result)) {
-            return;
-        }
-
-        mAutoCompleteResult = result;
-        mText.getText().append(result.substring(text.length()));
-        mText.setSelection(text.length(), result.length());
-    }
-
-    @Override
-    public void afterTextChanged(final Editable s) {
-        final String text = s.toString();
-        boolean useHandler = false;
-        boolean reuseAutocomplete = false;
-        if (!hasCompositionString(s) && !StringUtils.isSearchQuery(text, false)) {
-            useHandler = true;
-
-            // If you're hitting backspace (the string is getting smaller
-            // or is unchanged), don't autocomplete.
-            if (mAutoCompletePrefix != null && (mAutoCompletePrefix.length() >= text.length())) {
-                useHandler = false;
-            } else if (mAutoCompleteResult != null && mAutoCompleteResult.startsWith(text)) {
-                // If this text already matches our autocomplete text, autocomplete likely
-                // won't change. Just reuse the old autocomplete value.
-                useHandler = false;
-                reuseAutocomplete = true;
-            }
-        }
-
-        // If this is the autocomplete text being set, don't run the filter.
-        if (TextUtils.isEmpty(mAutoCompleteResult) || !mAutoCompleteResult.equals(text)) {
-            mAwesomeTabs.filter(text, useHandler ? this : null);
-            mAutoCompletePrefix = text;
-
-            if (reuseAutocomplete) {
-                onAutocomplete(mAutoCompleteResult);
-            }
-        }
-
-        // If the AwesomeBar has a composition string, don't call updateGoButton().
-        // That method resets IME and composition state will be broken.
-        if (!hasCompositionString(s) ||
-            InputMethods.isGestureKeyboard(mText.getContext())) {
-            updateGoButton(text);
-        }
-    }
-
-    @Override
-    public void beforeTextChanged(CharSequence s, int start, int count,
-                                  int after) {
-        // do nothing
-    }
-
-    @Override
-    public void onTextChanged(CharSequence s, int start, int before,
-                              int count) {
-        // do nothing
-    }
-}
deleted file mode 100644
--- a/mobile/android/base/AwesomeBarTabs.java
+++ /dev/null
@@ -1,381 +0,0 @@
-/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
- * 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/. */
-
-package org.mozilla.gecko;
-
-import android.content.Context;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.StateListDrawable;
-import android.support.v4.view.PagerAdapter;
-import android.support.v4.view.ViewPager;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.TabHost;
-import android.widget.TabWidget;
-
-public class AwesomeBarTabs extends TabHost
-                            implements LightweightTheme.OnChangeListener { 
-    private static final String LOGTAG = "GeckoAwesomeBarTabs";
-
-    private Context mContext;
-    private GeckoActivity mActivity;
-
-    private boolean mInflated;
-    private LayoutInflater mInflater;
-    private OnUrlOpenListener mUrlOpenListener;
-    private View.OnTouchListener mListTouchListener;
-    private boolean mSearching = false;
-    private String mTarget;
-    private ViewPager mViewPager;
-    private AwesomePagerAdapter mPagerAdapter;
-
-    private AwesomeBarTab mTabs[];
-
-    public interface OnUrlOpenListener {
-        public void onUrlOpen(String url, String title);
-        public void onSearch(SearchEngine engine, String text);
-        public void onEditSuggestion(String suggestion);
-        public void onSwitchToTab(final int tabId);
-    }
-
-    private class AwesomePagerAdapter extends PagerAdapter {
-        public AwesomePagerAdapter() {
-            super();
-        }
-
-        @Override
-        public Object instantiateItem(ViewGroup group, int index) {
-            AwesomeBarTab tab = mTabs[index];
-            group.addView(tab.getView());
-            return tab;
-        }
-
-        @Override
-        public void destroyItem(ViewGroup group, int index, Object obj) {
-            AwesomeBarTab tab = (AwesomeBarTab)obj;
-            group.removeView(tab.getView());
-        }
-
-        @Override
-        public int getCount() {
-            if (mSearching)
-                return 1;
-            return mTabs.length;
-        }
-
-        @Override
-        public boolean isViewFromObject(View view, Object object) {
-            return getAwesomeBarTabForView(view) == object;
-        }
-    }
-
-    private AwesomeBarTab getCurrentAwesomeBarTab() {
-        int index = mViewPager.getCurrentItem();
-        return mTabs[index];
-    }
-
-    public AwesomeBarTab getAwesomeBarTabForView(View view) {
-        String tag = (String)view.getTag();
-        return getAwesomeBarTabForTag(tag);
-    }
-
-    public AwesomeBarTab getAwesomeBarTabForTag(String tag) {
-        for (AwesomeBarTab tab : mTabs) {
-            if (tag.equals(tab.getTag())) {
-                return tab;
-            }
-        }
-        return null;
-    }
-
-    public boolean onBackPressed() {
-        AwesomeBarTab tab = getCurrentAwesomeBarTab();
-        if (tab == null)
-             return false;
-        return tab.onBackPressed();
-    }
-
-    public AwesomeBarTabs(Context context, AttributeSet attrs) {
-        super(context, attrs);
-
-        Log.d(LOGTAG, "Creating AwesomeBarTabs");
-
-        mContext = context;
-        mActivity = (GeckoActivity) context;
-
-        mInflated = false;
-        mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-
-        // HACK: Without this, the onFinishInflate is called twice
-        // This issue is due to a bug when Android inflates a layout with a
-        // parent. Fixed in Honeycomb
-        if (mInflated)
-            return;
-
-        mInflated = true;
-
-        // This should be called before adding any tabs
-        // to the TabHost.
-        setup();
-
-        mListTouchListener = new View.OnTouchListener() {
-            @Override
-            public boolean onTouch(View view, MotionEvent event) {
-                if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
-                    // take focus away from awesome bar to hide the keyboard
-                    requestFocus();
-                }
-                return false;
-            }
-        };
-
-        mTabs = new AwesomeBarTab[] {
-            new AllPagesTab(mContext),
-            new BookmarksTab(mContext),
-            new HistoryTab(mContext)
-        };
-
-        final TabWidget tabWidget = (TabWidget) findViewById(android.R.id.tabs);
-        // hide the strip since we aren't using the TabHost...
-        tabWidget.setStripEnabled(false);
-
-        mViewPager = (ViewPager) findViewById(R.id.tabviewpager);
-        mPagerAdapter = new AwesomePagerAdapter();
-        mViewPager.setAdapter(mPagerAdapter);
-
-        mViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
-            @Override
-            public void onPageScrollStateChanged(int state) { }
-            @Override
-            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { }
-            @Override
-            public void onPageSelected(int position) {
-                tabWidget.setCurrentTab(position);
-                styleSelectedTab();
-                // take focus away from awesome bar to hide the keyboard
-                requestFocus();
-             }
-         });
-
-        for (int i = 0; i < mTabs.length; i++) {
-            mTabs[i].setListTouchListener(mListTouchListener);
-            addAwesomeTab(mTabs[i].getTag(),
-                          mTabs[i].getTitleStringId(),
-                          i);
-        }
-
-        // Initialize "All Pages" list with no filter
-        filter("", null);
-    }
-
-    @Override
-    public void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        mActivity.getLightweightTheme().addListener(this);
-    }
-
-    @Override
-    public void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        mActivity.getLightweightTheme().removeListener(this);
-    }
-
-    @Override
-    public void onLightweightThemeChanged() {
-        styleSelectedTab();
-    }
-
-    @Override
-    public void onLightweightThemeReset() {
-        styleSelectedTab();
-    }
-
-    public void setCurrentItemByTag(String tag) {
-        mViewPager.setCurrentItem(getTabIdByTag(tag));
-    }
-
-    public int getTabIdByTag(String tag) {
-        for (int i = 0; i < mTabs.length; i++) {
-            if (tag.equals(mTabs[i].getTag())) {
-                return i;
-            }
-        }
-        return -1;
-    }
-
-    private void styleSelectedTab() {
-        int selIndex = mViewPager.getCurrentItem();
-        TabWidget tabWidget = getTabWidget();
-        boolean isPrivate = false;
-
-        if (mTarget != null && mTarget.equals(AwesomeBar.Target.CURRENT_TAB.name())) {
-            Tab tab = Tabs.getInstance().getSelectedTab();
-            if (tab != null)
-                isPrivate = tab.isPrivate();
-        }
-
-        for (int i = 0; i < tabWidget.getTabCount(); i++) {
-            GeckoTextView view = (GeckoTextView) tabWidget.getChildTabViewAt(i);
-            if (isPrivate) {
-                view.resetTheme();
-                view.setPrivateMode((i == selIndex) ? false : true);
-            } else {
-                if (i == selIndex)
-                    view.resetTheme();
-                else if (mActivity.getLightweightTheme().isEnabled())
-                    view.setTheme(mActivity.getLightweightTheme().isLightTheme());
-                else
-                    view.resetTheme();
-            }
-
-            if (i < (selIndex - 1))
-                view.getBackground().setLevel(3);
-            else if (i == (selIndex - 1))
-                view.getBackground().setLevel(1);
-            else if (i == (selIndex + 1))
-                view.getBackground().setLevel(2);
-            else if (i > (selIndex + 1))
-                view.getBackground().setLevel(4);
-        }
-
-        if (selIndex == 0)
-            findViewById(R.id.tab_widget_left).getBackground().setLevel(1);