Bug 882407 - Uplift addon-sdk to firefox. r=me
authorWes Kocher <wkocher@mozilla.com>
Wed, 12 Jun 2013 13:29:26 -0700
changeset 146350 d85200e586a445295573d9c57cdd729acce0703a
parent 146349 cc75035974184bb4deda8926191ad04df188b7e5
child 146351 b50aafc75977a474d72e14a0bf0ec85f8a33fd9e
push id2697
push userbbajaj@mozilla.com
push dateMon, 05 Aug 2013 18:49:53 +0000
treeherdermozilla-beta@dfec938c7b63 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersme
bugs882407
milestone24.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 882407 - Uplift addon-sdk to firefox. r=me
addon-sdk/source/doc/module-source/sdk/event/target.md
addon-sdk/source/doc/module-source/sdk/lang/functional.md
addon-sdk/source/doc/module-source/sdk/panel.md
addon-sdk/source/examples/annotator/lib/main.js
addon-sdk/source/examples/library-detector/lib/main.js
addon-sdk/source/examples/reading-data/lib/main.js
addon-sdk/source/examples/reading-data/tests/test-main.js
addon-sdk/source/examples/reddit-panel/lib/main.js
addon-sdk/source/examples/reddit-panel/tests/test-main.js
addon-sdk/source/lib/sdk/deprecated/window-utils.js
addon-sdk/source/lib/sdk/event/target.js
addon-sdk/source/lib/sdk/lang/functional.js
addon-sdk/source/lib/sdk/private-browsing.js
addon-sdk/source/lib/sdk/private-browsing/utils.js
addon-sdk/source/lib/sdk/tabs/tabs-firefox.js
addon-sdk/source/lib/sdk/test/harness.js
addon-sdk/source/lib/sdk/window/browser.js
addon-sdk/source/lib/sdk/windows/dom.js
addon-sdk/source/lib/sdk/windows/fennec.js
addon-sdk/source/python-lib/cuddlefish/manifest.py
addon-sdk/source/python-lib/cuddlefish/tests/addons/simplest-test/main.js
addon-sdk/source/python-lib/cuddlefish/tests/linker-files/one/lib/main.js
addon-sdk/source/python-lib/cuddlefish/tests/linker-files/seven/lib/main.js
addon-sdk/source/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/lib/main.js
addon-sdk/source/python-lib/cuddlefish/tests/test_linker.py
addon-sdk/source/test/addons/content-permissions/main.js
addon-sdk/source/test/addons/layout-change/package.json
addon-sdk/source/test/addons/private-browsing-supported/test-panel.js
addon-sdk/source/test/addons/require/package.json
addon-sdk/source/test/private-browsing/windows.js
addon-sdk/source/test/test-content-events.js
addon-sdk/source/test/test-disposable.js
addon-sdk/source/test/test-event-target.js
addon-sdk/source/test/test-functional.js
addon-sdk/source/test/test-namespace.js
addon-sdk/source/test/test-selection.js
addon-sdk/source/test/test-system-events.js
addon-sdk/source/test/windows/test-fennec-windows.js
addon-sdk/source/test/windows/test-firefox-windows.js
--- a/addon-sdk/source/doc/module-source/sdk/event/target.md
+++ b/addon-sdk/source/doc/module-source/sdk/event/target.md
@@ -99,16 +99,24 @@ Exceptions in the listeners can be handl
       console.log('caught an error: ' + error.message);
     });
     emit(target, 'boom');
     // info: caught an error: Boom!
 
 If there is no listener registered for `error` event or if it also throws
 exception then such exceptions are logged into a console.
 
+## Chaining
+
+Emitters can also have their methods chained:
+
+    target.on('message', handleMessage)
+      .on('data', parseData)
+      .on('error', handleError);
+
 <api name="EventTarget">
 @class
 `EventTarget` is an exemplar for creating an objects that can be used to
 add / remove event listeners on them. Events on these objects may be emitted
 via `emit` function exported by [`event/core`](modules/sdk/event/core.html)
 module.
 
 <api name="initialize">
@@ -126,30 +134,41 @@ specified `type` are emitted.
     worker.on('message', function (data) {
       console.log('data received: ' + data)
     });
 
 @param type {String}
    The type of event.
 @param listener {Function}
    The listener function that processes the event.
+@returns {EventTarget}
+   Returns the EventTarget instance
 </api>
 
 <api name="once">
 @method
 Registers an event `listener` that is called only once:
 the next time an event of the specified `type` is emitted.
 @param type {String}
    The type of event.
 @param listener {Function}
    The listener function that processes the event.
+@returns {EventTarget}
+   Returns the EventTarget instance
 </api>
 
 <api name="removeListener">
 @method
 Removes an event `listener` for the given event `type`.
 @param type {String}
    The type of event.
 @param listener {Function}
    The listener function that processes the event.
+@returns {EventTarget}
+   Returns the EventTarget instance
+</api>
+
+<api name="off">
+@method
+An alias for [removeListener](modules/sdk/event/target.html#removeListener(type, listener)).
 </api>
 
 </api>
--- a/addon-sdk/source/doc/module-source/sdk/lang/functional.md
+++ b/addon-sdk/source/doc/module-source/sdk/lang/functional.md
@@ -281,12 +281,37 @@ of having to set a boolean flag and chec
 
 @param fn {function}
   The function that will be executed only once inside the once wrapper.
 
 @returns {function}
   The wrapped `fn` that can only be executed once.
 </api>
 
+<api name="chain">
+@function
+Creates a version of the input function that will return `this`.
+
+    let { chain } = require("sdk/lang/functional");
+
+    function Person (age) { this.age = age; }
+    Person.prototype.happyBirthday = chain(function () this.age++);
+
+    let person = new Person(30);
+
+    person
+      .happyBirthday()
+      .happyBirthday()
+      .happyBirthday()
+
+    console.log(person.age); // 33
+
+@param fn {function}
+  The function that will be wrapped by the chain function.
+
+@returns {function}
+  The wrapped function that executes `fn` and returns `this`.
+</api>
+
 <api name="cache">
 @function
 An alias for [once](modules/sdk/lang/functional.html#once(fn)).
 </api>
--- a/addon-sdk/source/doc/module-source/sdk/panel.md
+++ b/addon-sdk/source/doc/module-source/sdk/panel.md
@@ -65,16 +65,23 @@ method exported by the
 [`self`](modules/sdk/self.html) module, like this:
 
     var panel = require("sdk/panel").Panel({
       contentURL: require("sdk/self").data.url("myFile.html")
     });
 
     panel.show();
 
+## Panel Positioning ##
+
+By default the panel appears in the center of the currently active browser window.
+You can position the panel by passing a `position` to the panel's
+[constructor](modules/sdk/panel.html#Panel(options)) or to
+its [`show()`](modules/sdk/panel.html#show(options)) method.
+
 ## Updating Panel Content ##
 
 You can update the panel's content simply by setting the panel's `contentURL`
 property.
 
 Here's an add-on that adds two widgets to the add-on bar, one which
 shows Google's mobile site and one which shows Bing's mobile site. The widgets
 share a panel object, and switch between the two sites by updating the panel's
@@ -420,59 +427,63 @@ Creates a panel.
   @prop [width] {number}
     The width of the panel in pixels. Optional.
   @prop [height] {number}
     The height of the panel in pixels. Optional.
   @prop [position] {object}
     The position of the panel.
     Ignored if the panel is opened by a widget.
 
-    You can set as value an object that has one or more of the following
-    properites: `top`, `right`, `bottom` and `left`. Their values are expressed
+    This is an object that has one or more of the following
+    properties: `top`, `right`, `bottom` and `left`. Their values are expressed
     in pixels. Any other properties will be ignored.
 
-    The default alignment is centered, so for example panel can be displayed in
-    the center of the bottom corner by leaving off vertical axis:
+    The default alignment along each axis is centered: so to display a panel centred
+    along the vertical or horizontal axis, just omit that axis:
 
-        // Show the panel to the centered horizontally and aligned to the bottom
-        // of the content area
+        // Show the panel centered horizontally and
+        // aligned to the bottom of the content area
         require("sdk/panel").Panel({
           position: {
            bottom: 0
           }
         }).show();
 
-        // Show the panel to the centered vertically and aligned to the left o
-        // the content area
+        // Show the panel centered vertically and
+        // aligned to the left of the content area
         require("sdk/panel").Panel({
           position: {
             left: 0
           }
         }).show();
 
         // Centered panel, default behavior
         require("sdk/panel").Panel({}).show();
 
-    In the same way of their CSS counterpart, setting both `top` and `bottom`,
-    or `left` and `right`, will results in calculated the `height` and `width`:
+    As with the CSS `top`, `bottom`, `left`, and `right` properties, setting
+    both `top` and `bottom` or both `left` and `right` will implicitly set the
+    panel's `height` or `width` relative to the content window:
 
-        // Show the panel centered horizontally, that is distant 40px
-        // from the top and 100px from the bottom.
+        // Show the panel centered horizontally, with:
+        // - the top edge 40px from the top of the content window
+        // - the bottom edge 100px from the bottom of the content window
         require("sdk/panel").Panel({
           position: {
             top: 40,
             bottom: 100
           }
         }).show();
 
-    Set implicitly `height` in this example, will makes the panel ignore the
-    `bottom` property, as the CSS homonym properties does:
+    If you set both `top` and `bottom`, but also set the panel's height
+    explicitly using the `height` property, then the panel will ignore
+    `bottom`, just as CSS does for its properties with the same name:
 
-        // Show the panel centered horizontally, that is distant 40px from the top
-        // and has 400px as height
+        // Show the panel centered horizontally, with:
+        // - the top edge 40px from the top of the content window
+        // - a height of 400px
         require("sdk/panel").Panel({
           position: {
             top: 40,
             bottom: 100,
           },
           height: 400
         }).show();
 
@@ -480,17 +491,18 @@ Creates a panel.
 
         require("panel").Panel({
           position {
             top: 40
           },
           height: 400
         }).show();
 
-    The same principle is applied for `width`, `left` and `right`.
+    The same principle is applied in the horizontal axis with
+    `width`, `left` and `right`.
 
   @prop [focus=true] {boolean}
     Set to `false` to prevent taking the focus away when the panel is shown.
     Only turn this off if necessary, to prevent accessibility issue.
     Optional, default to `true`.
   @prop [contentURL] {string,URL}
     The URL of the content to load in the panel.
   @prop [allow] {object}
@@ -639,18 +651,20 @@ The message to send.  Must be stringifia
 </api>
 
 <api name="show">
 @method
 Displays the panel.
 
 If the `options` argument is given, it will be shallow merged with the options
 provided in the constructor: the `options` passed in the `show` method takes
-the precedence.
-It's useful for temporary changes, without touching the default values.
+precedence.
+
+Passing options here is useful for making temporary changes without touching
+the default values.
 
 @param options {object}
   Showing options for the panel, with the following keys:
   @prop [width] {number}
     The width of the panel in pixels. Optional.
   @prop [height] {number}
     The height of the panel in pixels. Optional.
   @prop [position] {object}
--- a/addon-sdk/source/examples/annotator/lib/main.js
+++ b/addon-sdk/source/examples/annotator/lib/main.js
@@ -1,18 +1,18 @@
 /* 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/. */
 
-var widgets = require('widget');
-var pageMod = require('page-mod');
-var data = require('self').data;
-var panels = require('panel');
-var simpleStorage = require('simple-storage');
-var notifications = require("notifications");
+var widgets = require('sdk/widget');
+var pageMod = require('sdk/page-mod');
+var data = require('sdk/self').data;
+var panels = require('sdk/panel');
+var simpleStorage = require('sdk/simple-storage');
+var notifications = require("sdk/notifications");
 
 /*
 Global variables
 * Boolean to indicate whether the add-on is switched on or not
 * Array for all workers associated with the 'selector' page mod
 * Array for all workers associated with the 'matcher' page mod
 */
 var annotatorIsOn = false;
@@ -183,17 +183,17 @@ in the browser.
     contentURL: data.url('list/annotation-list.html'),
     contentScriptFile: [data.url('jquery-1.4.2.min.js'),
                         data.url('list/annotation-list.js')],
     contentScriptWhen: 'ready',
     onShow: function() {
       this.postMessage(simpleStorage.storage.annotations);
     },
     onMessage: function(message) {
-      require('tabs').open(message);
+      require('sdk/tabs').open(message);
     }
   });
 
 /*
 We listen for the OverQuota event from simple-storage.
 If it fires we just notify the user and delete the most
 recent annotations until we are back in quota.
 */
--- a/addon-sdk/source/examples/library-detector/lib/main.js
+++ b/addon-sdk/source/examples/library-detector/lib/main.js
@@ -1,17 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-const tabs = require('tabs');
-const widgets = require('widget');
-const data = require('self').data;
-const pageMod = require('page-mod');
-const panel = require('panel');
+const tabs = require('sdk/tabs');
+const widgets = require('sdk/widget');
+const data = require('sdk/self').data;
+const pageMod = require('sdk/page-mod');
+const panel = require('sdk/panel');
 
 const ICON_WIDTH = 16;
 
 function updateWidgetView(tab) {
   let widgetView = widget.getView(tab.window);
   if (!tab.libraries) {
     tab.libraries = [];
   }
--- a/addon-sdk/source/examples/reading-data/lib/main.js
+++ b/addon-sdk/source/examples/reading-data/lib/main.js
@@ -1,15 +1,15 @@
 /* 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/. */
 
-var self = require("self");
-var panels = require("addon-kit/panel");
-var widgets = require("addon-kit/widget");
+var self = require("sdk/self");
+var panels = require("sdk/panel");
+var widgets = require("sdk/widget");
 
 function replaceMom(html) {
   return html.replace("World", "Mom");
 }
 exports.replaceMom = replaceMom;
 
 exports.main = function(options, callbacks) {
   console.log("My ID is " + self.id);
--- a/addon-sdk/source/examples/reading-data/tests/test-main.js
+++ b/addon-sdk/source/examples/reading-data/tests/test-main.js
@@ -1,14 +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/. */
 
 var m = require("main");
-var self = require("self");
+var self = require("sdk/self");
 
 exports.testReplace = function(test) {
   var input = "Hello World";
   var output = m.replaceMom(input);
   test.assertEqual(output, "Hello Mom");
   var callbacks = { quit: function() {} };
 
   // Make sure it doesn't crash...
--- a/addon-sdk/source/examples/reddit-panel/lib/main.js
+++ b/addon-sdk/source/examples/reddit-panel/lib/main.js
@@ -1,27 +1,27 @@
 /* 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/. */
 
-var data = require("self").data;
+var data = require("sdk/self").data;
 
-var reddit_panel = require("panel").Panel({
+var reddit_panel = require("sdk/panel").Panel({
   width: 240,
   height: 320,
   contentURL: "http://www.reddit.com/.mobile?keep_extension=True",
   contentScriptFile: [data.url("jquery-1.4.4.min.js"),
                       data.url("panel.js")]
 });
 
 reddit_panel.port.on("click", function(url) {
-  require("tabs").open(url);
+  require("sdk/tabs").open(url);
 });
 
-require("widget").Widget({
+require("sdk/widget").Widget({
   id: "open-reddit-btn",
   label: "Reddit",
   contentURL: "http://www.reddit.com/static/favicon.ico",
   panel: reddit_panel
 });
 
 exports.main = function(options, callbacks) {
   // If you run cfx with --static-args='{"quitWhenDone":true}' this program
--- a/addon-sdk/source/examples/reddit-panel/tests/test-main.js
+++ b/addon-sdk/source/examples/reddit-panel/tests/test-main.js
@@ -1,14 +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/. */
 
 var m = require("main");
-var self = require("self");
+var self = require("sdk/self");
 
 exports.testMain = function(test) {
   var callbacks = { quit: function() {
     test.pass();
     test.done();
   } };
 
   test.waitUntilDone();
--- a/addon-sdk/source/lib/sdk/deprecated/window-utils.js
+++ b/addon-sdk/source/lib/sdk/deprecated/window-utils.js
@@ -200,27 +200,27 @@ Object.defineProperties(exports, {
 });
 
 
 /**
  * Returns the ID of the window's current inner window.
  */
 exports.getInnerId = deprecateFunction(getInnerId,
   'require("window-utils").getInnerId is deprecated, ' +
-  'please use require("window/utils").getInnerId instead'
+  'please use require("sdk/window/utils").getInnerId instead'
 );
 
 exports.getOuterId = deprecateFunction(getOuterId,
   'require("window-utils").getOuterId is deprecated, ' +
-  'please use require("window/utils").getOuterId instead'
+  'please use require("sdk/window/utils").getOuterId instead'
 );
 
 exports.isBrowser = deprecateFunction(isBrowser,
   'require("window-utils").isBrowser is deprecated, ' +
-  'please use require("window/utils").isBrowser instead'
+  'please use require("sdk/window/utils").isBrowser instead'
 );
 
 exports.hiddenWindow = appShellService.hiddenDOMWindow;
 
 when(
   function() {
     gDocsToClose.slice().forEach(
       function(doc) { doc.defaultView.close(); });
--- a/addon-sdk/source/lib/sdk/event/target.js
+++ b/addon-sdk/source/lib/sdk/event/target.js
@@ -6,17 +6,17 @@
 
 'use strict';
 
 module.metadata = {
   "stability": "stable"
 };
 
 const { on, once, off, setListeners } = require('./core');
-const { method } = require('../lang/functional');
+const { method, chain } = require('../lang/functional');
 const { Class } = require('../core/heritage');
 
 /**
  * `EventTarget` is an exemplar for creating an objects that can be used to
  * add / remove event listeners on them. Events on these objects may be emitted
  * via `emit` function exported by 'event/core' module.
  */
 const EventTarget = Class({
@@ -40,37 +40,39 @@ const EventTarget = Class({
    *    The type of event.
    * @param {Function} listener
    *    The listener function that processes the event.
    * @example
    *      worker.on('message', function (data) {
    *        console.log('data received: ' + data)
    *      })
    */
-  on: method(on),
+  on: chain(method(on)),
   /**
    * Registers an event `listener` that is called once the next time an event
    * of the specified `type` is emitted.
    * @param {String} type
    *    The type of the event.
    * @param {Function} listener
    *    The listener function that processes the event.
    */
-  once: method(once),
+  once: chain(method(once)),
   /**
    * Removes an event `listener` for the given event `type`.
    * @param {String} type
    *    The type of event.
    * @param {Function} listener
    *    The listener function that processes the event.
    */
   removeListener: function removeListener(type, listener) {
     // Note: We can't just wrap `off` in `method` as we do it for other methods
     // cause skipping a second or third argument will behave very differently
     // than intended. This way we make sure all arguments are passed and only
     // one listener is removed at most.
     off(this, type, listener);
+    return this;
   },
   off: function(type, listener) {
-    off(this, type, listener)
+    off(this, type, listener);
+    return this;
   }
 });
 exports.EventTarget = EventTarget;
--- a/addon-sdk/source/lib/sdk/lang/functional.js
+++ b/addon-sdk/source/lib/sdk/lang/functional.js
@@ -37,16 +37,27 @@ exports.method = method;
 function defer(f) {
   return function deferred()
     setTimeout(invoke, 0, f, arguments, this);
 }
 exports.defer = defer;
 // Exporting `remit` alias as `defer` may conflict with promises.
 exports.remit = defer;
 
+/*
+ * Takes a funtion and returns a wrapped function that returns `this`
+ */
+function chain(f) {
+  return function chainable(...args) {
+    f.apply(this, args);
+    return this;
+  };
+}
+exports.chain = chain;
+
 /**
  * Invokes `callee` by passing `params` as an arguments and `self` as `this`
  * pseudo-variable. Returns value that is returned by a callee.
  * @param {Function} callee
  *    Function to invoke.
  * @param {Array} params
  *    Arguments to invoke function with.
  * @param {Object} self
--- a/addon-sdk/source/lib/sdk/private-browsing.js
+++ b/addon-sdk/source/lib/sdk/private-browsing.js
@@ -68,14 +68,15 @@ exports.isPrivate = function(thing) {
   // if we get here, and global private browsing
   // is available, and it is true, then return
   // true otherwise false is returned here
   return getMode();
 };
 
 function deprecateEvents(func) deprecateEvent(
   func,
-   'The require("private-browsing") module\'s "start" and "stop" events are deprecated.',
+   'The require("sdk/private-browsing") module\'s "start" and "stop" events ' +
+   'are deprecated.',
   ['start', 'stop']
 );
 
 // Make sure listeners are cleaned up.
 unload(function() off(exports));
--- a/addon-sdk/source/lib/sdk/private-browsing/utils.js
+++ b/addon-sdk/source/lib/sdk/private-browsing/utils.js
@@ -75,18 +75,19 @@ if (isGlobalPBSupported) {
 let setMode = defer(function setMode(value) {
   value = !!value;  // Cast to boolean.
 
   // default
   return pbService && (pbService.privateBrowsingEnabled = value);
 });
 exports.setMode = deprecateFunction(
   setMode,
-  'require("private-browsing").activate and require("private-browsing").deactivate ' +
-  'is deprecated.'
+  'require("sdk/private-browsing").activate and ' +
+  'require("sdk/private-browsing").deactivate ' +
+  'are deprecated.'
 );
 
 let getMode = function getMode(chromeWin) {
   if (isWindowPrivate(chromeWin))
     return true;
 
   // default
   return pbService ? pbService.privateBrowsingEnabled : false;
--- a/addon-sdk/source/lib/sdk/tabs/tabs-firefox.js
+++ b/addon-sdk/source/lib/sdk/tabs/tabs-firefox.js
@@ -7,46 +7,48 @@
 const { browserWindows: windows } = require('../windows');
 const { tabs } = require('../windows/tabs-firefox');
 const { isPrivate } = require('../private-browsing');
 const { isWindowPBSupported } = require('../private-browsing/utils')
 const { isPrivateBrowsingSupported } = require('sdk/self');
 
 const supportPrivateTabs = isPrivateBrowsingSupported && isWindowPBSupported;
 
+function newTabWindow(options) {
+  // `tabs` option is under review and may be removed.
+  return windows.open({
+    tabs: [ options ],
+    isPrivate: options.isPrivate
+  });
+}
+
 Object.defineProperties(tabs, {
   open: { value: function open(options) {
     if (options.inNewWindow) {
-        // `tabs` option is under review and may be removed.
-        windows.open({
-          tabs: [ options ],
-          isPrivate: options.isPrivate
-        });
+        newTabWindow(options);
         return undefined;
     }
     // Open in active window if new window was not required.
 
     let activeWindow = windows.activeWindow;
     let privateState = !!options.isPrivate;
+
     // if the active window is in the state that we need then use it
-    if (!supportPrivateTabs || privateState === isPrivate(activeWindow)) {
+    if (activeWindow && (!supportPrivateTabs || privateState === isPrivate(activeWindow))) {
       activeWindow.tabs.open(options);
     }
     else {
       // find a window in the state that we need
       let window = getWindow(privateState);
       if (window) {
         window.tabs.open(options);
       }
       // open a window in the state that we need
       else {
-        windows.open({
-          tabs: [ options ],
-          isPrivate: options.isPrivate
-        });
+        newTabWindow(options);
       }
     }
 
     return undefined;
   }}
 });
 
 function getWindow(privateState) {
--- a/addon-sdk/source/lib/sdk/test/harness.js
+++ b/addon-sdk/source/lib/sdk/test/harness.js
@@ -252,19 +252,19 @@ function cleanup() {
     console.error("unload.send() threw an exception.");
     console.exception(e);
   };
 
   setTimeout(require('@test/options').checkMemory ? checkMemory : showResults, 1);
 
   // dump the coverobject
   if (Object.keys(coverObject).length){
-    const self = require('self');
+    const self = require('sdk/self');
     const {pathFor} = require("sdk/system");
-    let file = require('file');
+    let file = require('sdk/io/file');
     const {env} = require('sdk/system/environment');
     console.log("CWD:", env.PWD);
     let out = file.join(env.PWD,'coverstats-'+self.id+'.json');
     console.log('coverstats:', out);
     let outfh = file.open(out,'w');
     outfh.write(JSON.stringify(coverObject,null,2));
     outfh.flush();
     outfh.close();
--- a/addon-sdk/source/lib/sdk/window/browser.js
+++ b/addon-sdk/source/lib/sdk/window/browser.js
@@ -9,17 +9,17 @@ const { on, off, once } = require('../ev
 const { method } = require('../lang/functional');
 const { getWindowTitle } = require('./utils');
 const unload = require('../system/unload');
 const { isWindowPrivate } = require('../window/utils');
 const { EventTarget } = require('../event/target');
 const { getOwnerWindow: getPBOwnerWindow } = require('../private-browsing/window/utils');
 const { deprecateUsage } = require('../util/deprecate');
 
-const ERR_FENNEC_MSG = 'This method is not yet supported by Fennec, consider using require("tabs") instead';
+const ERR_FENNEC_MSG = 'This method is not yet supported by Fennec, consider using require("sdk/tabs") instead';
 
 const BrowserWindow = Class({
   initialize: function initialize(options) {
     EventTarget.prototype.initialize.call(this, options);
     windowNS(this).window = options.window;
   },
   activate: function activate() {
     // TODO
@@ -36,17 +36,17 @@ const BrowserWindow = Class({
   get tabs() require('../tabs'),
   get activeTab() require('../tabs').activeTab,
   on: method(on),
   removeListener: method(off),
   once: method(once),
   get isPrivateBrowsing() {
     deprecateUsage('`browserWindow.isPrivateBrowsing` is deprecated, please ' +
                  'consider using ' +
-                 '`require("private-browsing").isPrivate(browserWindow)` ' +
+                 '`require("sdk/private-browsing").isPrivate(browserWindow)` ' +
                  'instead.');
     return isWindowPrivate(windowNS(this).window);
   }
 });
 exports.BrowserWindow = BrowserWindow;
 
 getPBOwnerWindow.define(BrowserWindow, function(window) {
   return windowNS(window).window;
--- a/addon-sdk/source/lib/sdk/windows/dom.js
+++ b/addon-sdk/source/lib/sdk/windows/dom.js
@@ -24,14 +24,14 @@ const WindowDom = Trait.compose({
   activate: function activate() {
     let window = this._window;
     if (window) window.focus();
     return this._public;
   },
   get isPrivateBrowsing() {
     deprecateUsage('`browserWindow.isPrivateBrowsing` is deprecated, please ' +
                    'consider using ' +
-                   '`require("private-browsing").isPrivate(browserWindow)` ' +
+                   '`require("sdk/private-browsing").isPrivate(browserWindow)` ' +
                    'instead.');
     return isWindowPrivate(this._window);
   }
 });
 exports.WindowDom = WindowDom;
--- a/addon-sdk/source/lib/sdk/windows/fennec.js
+++ b/addon-sdk/source/lib/sdk/windows/fennec.js
@@ -8,17 +8,17 @@ const { BrowserWindow } = require('../wi
 const { WindowTracker } = require('../deprecated/window-utils');
 const { isBrowser, getMostRecentBrowserWindow } = require('../window/utils');
 const { windowNS } = require('../window/namespace');
 const { on, off, once, emit } = require('../event/core');
 const { method } = require('../lang/functional');
 const { EventTarget } = require('../event/target');
 const { List, addListItem } = require('../util/list');
 
-const ERR_FENNEC_MSG = 'This method is not yet supported by Fennec, consider using require("tabs") instead';
+const ERR_FENNEC_MSG = 'This method is not yet supported by Fennec, consider using require("sdk/tabs") instead';
 
 // NOTE: On Fennec there is only one window.
 
 let BrowserWindows = Class({
   implements: [ List ],
   extends: EventTarget,
   initialize: function() {
     List.prototype.initialize.apply(this);
--- a/addon-sdk/source/python-lib/cuddlefish/manifest.py
+++ b/addon-sdk/source/python-lib/cuddlefish/manifest.py
@@ -402,17 +402,17 @@ class ManifestBuilder:
                 # shared instance? This is a deep question. For now say yes.
 
                 # find_req_for() returns an entry to put in our
                 # 'requirements' dict, and will recursively process
                 # everything transitively required from here. It will also
                 # populate the self.modules[] cache. Note that we must
                 # tolerate cycles in the reference graph.
                 looked_in = [] # populated by subroutines
-                them_me = self.find_req_for(mi, reqname, looked_in)
+                them_me = self.find_req_for(mi, reqname, looked_in, locations)
                 if them_me is None:
                     if mi.section == "tests":
                         # tolerate missing modules in tests, because
                         # test-securable-module.js, and the modules/red.js
                         # that it imports, both do that intentionally
                         continue
                     lineno = locations.get(reqname) # None means define()
                     if lineno is None:
@@ -423,17 +423,17 @@ class ManifestBuilder:
                                               mi.js, lineno, looked_in)
                     raise err
                 else:
                     me.add_requirement(reqname, them_me)
 
         return me
         #print "LEAVING", pkg.name, mi.name
 
-    def find_req_for(self, from_module, reqname, looked_in):
+    def find_req_for(self, from_module, reqname, looked_in, locations):
         # handle a single require(reqname) statement from from_module .
         # Return a uri that exists in self.manifest
         # Populate looked_in with places we looked.
         def BAD(msg):
             return BadModuleIdentifier(msg + " in require(%s) from %s" %
                                        (reqname, from_module))
 
         if not reqname:
@@ -508,18 +508,31 @@ class ManifestBuilder:
         if normalized.endswith(".js"):
             normalized = normalized[:-len(".js")]
         if normalized.startswith("addon-kit/"):
             normalized = normalized[len("addon-kit/"):]
         if normalized.startswith("api-utils/"):
             normalized = normalized[len("api-utils/"):]
         if normalized in NEW_LAYOUT_MAPPING:
             # get the new absolute path for this module
+            original_reqname = reqname
             reqname = NEW_LAYOUT_MAPPING[normalized]
             from_pkg = from_module.package.name
+
+            # If the addon didn't explicitely told us to ignore deprecated
+            # require path, warn the developer:
+            # (target_cfg is the package.json file)
+            if not "ignore-deprecated-path" in self.target_cfg:
+                lineno = locations.get(original_reqname)
+                print >>self.stderr, "Warning: Use of deprecated require path:"
+                print >>self.stderr, "  In %s:%d:" % (from_module.js, lineno)
+                print >>self.stderr, "    require('%s')." % original_reqname
+                print >>self.stderr, "  New path should be:"
+                print >>self.stderr, "    require('%s')" % reqname
+
             return self._search_packages_for_module(from_pkg,
                                                     lookfor_sections, reqname,
                                                     looked_in)
         else:
             # We weren't able to find this module, really.
             return None
 
     def _handle_module(self, mi):
--- a/addon-sdk/source/python-lib/cuddlefish/tests/addons/simplest-test/main.js
+++ b/addon-sdk/source/python-lib/cuddlefish/tests/addons/simplest-test/main.js
@@ -1,17 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const { Cc, Ci } = require("chrome");
 
 exports.main = function(options, callbacks) {
   // Close Firefox window. Firefox should quit.
-  require("api-utils/window-utils").activeBrowserWindow.close();
+  require("sdk/deprecated/window-utils").activeBrowserWindow.close();
 
   // But not on Mac where it stay alive! We have to request application quit.
-  if (require("api-utils/runtime").OS == "Darwin") {
+  if (require("sdk/system/runtime").OS == "Darwin") {
     let appStartup = Cc['@mozilla.org/toolkit/app-startup;1'].
                      getService(Ci.nsIAppStartup);
     appStartup.quit(appStartup.eAttemptQuit);
   }
 }
--- a/addon-sdk/source/python-lib/cuddlefish/tests/linker-files/one/lib/main.js
+++ b/addon-sdk/source/python-lib/cuddlefish/tests/linker-files/one/lib/main.js
@@ -1,9 +1,9 @@
 /* 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/. */
 
-var panel = require("panel");
+var panel = require("sdk/panel");
 var two = require("two.js");
 var a = require("./two");
 var b = require("sdk/tabs.js");
 var c = require("./subdir/three");
--- a/addon-sdk/source/python-lib/cuddlefish/tests/linker-files/seven/lib/main.js
+++ b/addon-sdk/source/python-lib/cuddlefish/tests/linker-files/seven/lib/main.js
@@ -1,6 +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/. */
 
-var self = require("self"); // trigger inclusion of data
+var self = require("sdk/self"); // trigger inclusion of data
 exports.main = function () { console.log("main"); };
--- a/addon-sdk/source/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/lib/main.js
+++ b/addon-sdk/source/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/lib/main.js
@@ -1,8 +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/. */
 
 exports.main = 42;
 require("./subdir/subfile");
-require("self"); // trigger inclusion of our data/ directory
+require("sdk/self"); // trigger inclusion of our data/ directory
 
--- a/addon-sdk/source/python-lib/cuddlefish/tests/test_linker.py
+++ b/addon-sdk/source/python-lib/cuddlefish/tests/test_linker.py
@@ -43,17 +43,17 @@ class Basic(unittest.TestCase):
         # target_cfg.dependencies is not provided, so we'll search through
         # all known packages (everything in 'deps').
         m = manifest.build_manifest(target_cfg, pkg_cfg, deps, scan_tests=False)
         m = m.get_harness_options_manifest(False)
 
         def assertReqIs(modname, reqname, path):
             reqs = m["one/%s" % modname]["requirements"]
             self.failUnlessEqual(reqs[reqname], path)
-        assertReqIs("main", "panel", "sdk/panel")
+        assertReqIs("main", "sdk/panel", "sdk/panel")
         assertReqIs("main", "two.js", "one/two")
         assertReqIs("main", "./two", "one/two")
         assertReqIs("main", "sdk/tabs.js", "sdk/tabs")
         assertReqIs("main", "./subdir/three", "one/subdir/three")
         assertReqIs("two", "main", "one/main")
         assertReqIs("subdir/three", "../main", "one/main")
 
         target_cfg.dependencies = []
--- a/addon-sdk/source/test/addons/content-permissions/main.js
+++ b/addon-sdk/source/test/addons/content-permissions/main.js
@@ -1,16 +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/. */
 "use strict";
 
-const xulApp = require("xul-app");
-const { PageMod } = require("page-mod");
-const tabs = require("tabs");
+const xulApp = require("sdk/system/xul-app");
+const { PageMod } = require("sdk/page-mod");
+const tabs = require("sdk/tabs");
 
 exports.testCrossDomainIframe = function(assert, done) {
   let serverPort = 8099;
   let server = require("sdk/test/httpd").startServerAsync(serverPort);
   server.registerPathHandler("/iframe", function handle(request, response) {
     response.write("<html><body>foo</body></html>");
   });
 
--- a/addon-sdk/source/test/addons/layout-change/package.json
+++ b/addon-sdk/source/test/addons/layout-change/package.json
@@ -1,3 +1,4 @@
 {
-  "id": "test-layout-change"
+  "id": "test-layout-change",
+  "ignore-deprecated-path": true
 }
\ No newline at end of file
--- a/addon-sdk/source/test/addons/private-browsing-supported/test-panel.js
+++ b/addon-sdk/source/test/addons/private-browsing-supported/test-panel.js
@@ -2,17 +2,17 @@
 
 const { open, focus, close } = require('sdk/window/helpers');
 const { isPrivate } = require('sdk/private-browsing');
 const { defer } = require('sdk/core/promise');
 
 const BROWSER = 'chrome://browser/content/browser.xul';
 
 exports.testRequirePanel = function(assert) {
-  require('panel');
+  require('sdk/panel');
   assert.ok('the panel module should not throw an error');
 };
 
 exports.testShowPanelInPrivateWindow = function(assert, done) {
   let panel = require('sdk/panel').Panel({
     contentURL: "data:text/html;charset=utf-8,"
   });
 
--- a/addon-sdk/source/test/addons/require/package.json
+++ b/addon-sdk/source/test/addons/require/package.json
@@ -1,4 +1,5 @@
 {
   "id": "test-require",
-  "packages": "packages"
+  "packages": "packages",
+  "ignore-deprecated-path": true
 }
\ No newline at end of file
--- a/addon-sdk/source/test/private-browsing/windows.js
+++ b/addon-sdk/source/test/private-browsing/windows.js
@@ -4,16 +4,17 @@
 'use strict';
 
 const { pb, pbUtils } = require('./helper');
 const { onFocus, openDialog, open } = require('sdk/window/utils');
 const { open: openPromise, close, focus, promise } = require('sdk/window/helpers');
 const { isPrivate } = require('sdk/private-browsing');
 const { browserWindows: windows } = require('sdk/windows');
 const { defer } = require('sdk/core/promise');
+const tabs = require('sdk/tabs');
 
 // test openDialog() from window/utils with private option
 // test isActive state in pwpb case
 // test isPrivate on ChromeWindow
 exports.testPerWindowPrivateBrowsingGetter = function(assert, done) {
   let win = openDialog({
     private: true
   });
@@ -85,17 +86,52 @@ exports.testIsPrivateOnWindowOpenFromPri
           assert.equal(isPrivate(w), false, 'new test window is not private');
           w.close(function() resolve(window));
         }
       });
 
       return promise;
     }).then(close).
        then(done, assert.fail);
-}
+};
+
+exports.testOpenTabWithPrivateWindow = function(assert, done) {
+  function start() {
+    openPromise(null, {
+      features: {
+        private: true,
+        toolbar: true
+      }
+    }).then(focus).then(function(window) {
+      let { promise, resolve } = defer();
+      assert.equal(isPrivate(window), true, 'the focused window is private');
+
+      tabs.open({
+        url: 'about:blank',
+        onOpen: function(tab) {
+          assert.equal(isPrivate(tab), false, 'the opened tab is not private');
+          // not closing this tab on purpose.. for now...
+          // we keep this tab open because we closed all windows
+          // and must keep a non-private window open at end of this test for next ones.
+          resolve(window);
+        }
+      });
+
+      return promise;
+    }).then(close).then(done, assert.fail);
+  }
+
+  (function closeWindows() {
+    if (windows.length > 0) {
+      return windows.activeWindow.close(closeWindows);
+    }
+    assert.pass('all pre test windows have been closed');
+    return start();
+  })()
+};
 
 exports.testIsPrivateOnWindowOff = function(assert, done) {
   windows.open({
     onOpen: function(window) {
       assert.equal(isPrivate(window), false, 'isPrivate for a window is false when it should be');
       assert.equal(isPrivate(window.tabs[0]), false, 'isPrivate for a tab is false when it should be');
       window.close(done);
     }
--- a/addon-sdk/source/test/test-content-events.js
+++ b/addon-sdk/source/test/test-content-events.js
@@ -7,17 +7,17 @@
 const { Loader } = require("sdk/test/loader");
 const { getMostRecentBrowserWindow, getInnerId } = require("sdk/window/utils");
 const { openTab, closeTab, getBrowserForTab } = require("sdk/tabs/utils");
 const { defer } = require("sdk/core/promise");
 const { curry, identity, partial } = require("sdk/lang/functional");
 
 let when = curry(function(options, tab) {
   let type = options.type || options;
-  let capture = options.captuer || false;
+  let capture = options.capture || false;
   let target = getBrowserForTab(tab);
   let { promise, resolve } = defer();
 
   target.addEventListener(type, function handler(event) {
     if (!event.target.defaultView.frameElement) {
       target.removeEventListener(type, handler, capture);
       resolve(tab);
     }
@@ -37,18 +37,22 @@ let close = function(tab) {
 }
 
 exports["test multiple tabs"] = function(assert, done) {
   let loader = Loader(module);
   let { events } = loader.require("sdk/content/events");
   let { on, off } = loader.require("sdk/event/core");
   let actual = [];
   on(events, "data", function({type, target, timeStamp}) {
-    // ignore about:blank pages.
-    if (target.URL !== "about:blank") actual.push(type + " -> " + target.URL)
+    // ignore about:blank pages and *-document-global-created
+    // events that are not very consistent.
+    if (target.URL !== "about:blank" &&
+        type !== "chrome-document-global-created" &&
+        type !== "content-document-global-created")
+      actual.push(type + " -> " + target.URL)
   });
 
   let window =  getMostRecentBrowserWindow();
   let firstTab = open("data:text/html,first-tab", window);
 
   when("pageshow", firstTab).
     then(close).
     then(use(window)).
@@ -75,30 +79,33 @@ exports["test multiple tabs"] = function
 };
 
 exports["test nested frames"] = function(assert, done) {
   let loader = Loader(module);
   let { events } = loader.require("sdk/content/events");
   let { on, off } = loader.require("sdk/event/core");
   let actual = [];
   on(events, "data", function({type, target, timeStamp}) {
-    // ignore about:blank pages.
-    if (target.URL !== "about:blank") actual.push(type + " -> " + target.URL)
+    // ignore about:blank pages and *-global-created
+    // events that are not very consistent.
+    if (target.URL !== "about:blank" &&
+       type !== "chrome-document-global-created" &&
+       type !== "content-document-global-created")
+      actual.push(type + " -> " + target.URL)
   });
 
   let window =  getMostRecentBrowserWindow();
   let uri = encodeURI("data:text/html,<iframe src='data:text/html,iframe'>");
   let tab = open(uri, window);
 
   when("pageshow", tab).
     then(close).
     then(function() {
       assert.deepEqual(actual, [
         "document-element-inserted -> " + uri,
-        "content-document-global-created -> data:text/html,iframe",
         "DOMContentLoaded -> " + uri,
         "document-element-inserted -> data:text/html,iframe",
         "DOMContentLoaded -> data:text/html,iframe",
         "load -> data:text/html,iframe",
         "pageshow -> data:text/html,iframe",
         "load -> " + uri,
         "pageshow -> " + uri
       ], "events where dispatched")
--- a/addon-sdk/source/test/test-disposable.js
+++ b/addon-sdk/source/test/test-disposable.js
@@ -110,23 +110,21 @@ exports["test disposables are GC-able"] 
   })
 
   let foo1 = Foo(arg1, arg2)
   let foo2 = Foo(arg1, arg2)
 
   let foo1 = null
   let foo2 = null
 
-  Cu.forceGC();
-  setTimeout(function() {
-    Cu.forceGC();
+  Cu.schedulePreciseGC(function() {
     loader.unload();
     assert.equal(disposals, 0, "GC removed dispose listeners");
     done();
-  }, 300);
+  });
 }
 
 
 exports["test loader unloads do not affect other loaders"] = function(assert) {
   let loader1 = Loader(module);
   let loader2 = Loader(module);
   let { Disposable: Disposable1 } = loader1.require("sdk/core/disposable");
   let { Disposable: Disposable2 } = loader2.require("sdk/core/disposable");
--- a/addon-sdk/source/test/test-event-target.js
+++ b/addon-sdk/source/test/test-event-target.js
@@ -158,10 +158,48 @@ exports['test unhandled errors'] = funct
             'unhandled exception is logged');
 
   target.on('error', function() { throw drax; });
   emit(target, 'message');
   assert.ok(~String(exceptions[1]).indexOf('Draax!'),
             'error in error handler is logged');
 };
 
+exports['test target is chainable'] = function (assert, done) {
+  let loader = Loader(module);
+  let exceptions = [];
+  let { EventTarget } = loader.require('sdk/event/target');
+  let { emit } = loader.require('sdk/event/core');
+  Object.defineProperties(loader.sandbox('sdk/event/core'), {
+    console: { value: {
+      exception: function(e) {
+        exceptions.push(e);
+      }
+    }}
+  });
+
+  let emitter = EventTarget();
+  let boom = Error('Boom');
+  let onceCalled = 0;
+  
+  emitter.once('oneTime', function () {
+    assert.equal(++onceCalled, 1, 'once event called only once');
+  }).on('data', function (message) {
+    assert.equal(message, 'message', 'handles event');
+    emit(emitter, 'oneTime');
+    emit(emitter, 'data2', 'message2');
+  }).on('phony', function () {
+    assert.fail('removeListener does not remove chained event');
+  }).removeListener('phony')
+  .on('data2', function (message) {
+    assert.equal(message, 'message2', 'handle chained event');
+    emit(emitter, 'oneTime');
+    throw boom;
+  }).on('error', function (error) {
+    assert.equal(error, boom, 'error handled in chained event');
+    done();
+  });
+
+  emit(emitter, 'data', 'message');
+};
+
 require('test').run(exports);
 
--- a/addon-sdk/source/test/test-functional.js
+++ b/addon-sdk/source/test/test-functional.js
@@ -1,16 +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/. */
 
 
 const { setTimeout } = require('sdk/timers');
 const utils = require('sdk/lang/functional');
-const { invoke, defer, partial, compose, memoize, once, delay, wrap, curry } = utils;
+const { invoke, defer, partial, compose, memoize, once, delay, wrap, curry, chain } = utils;
 const { LoaderWithHookedConsole } = require('sdk/test/loader');
 
 exports['test forwardApply'] = function(assert) {
   function sum(b, c) this.a + b + c
   assert.equal(invoke(sum, [2, 3], { a: 1 }), 6,
                'passed arguments and pseoude-variable are used');
 
   assert.equal(invoke(sum.bind({ a: 2 }), [2, 3], { a: 1 }), 7,
@@ -175,9 +175,24 @@ exports['test once'] = function(assert) 
   let target = { state: 0, update: once(function() this.state ++ ) };
 
   target.update();
   target.update();
 
   assert.equal(target.state, 1, 'this was passed in and called only once');
 };
 
+exports['test chain'] = function (assert) {
+  let Player = function () { this.volume = 5; };
+  Player.prototype = {
+    setBand: chain(function (band) this.band = band),
+    incVolume: chain(function () this.volume++)
+  };
+  let player = new Player();
+  player
+    .setBand('Animals As Leaders')
+    .incVolume().incVolume().incVolume().incVolume().incVolume().incVolume();
+
+  assert.equal(player.band, 'Animals As Leaders', 'passes arguments into chained');
+  assert.equal(player.volume, 11, 'accepts no arguments in chain');
+};
+
 require('test').run(exports);
--- a/addon-sdk/source/test/test-namespace.js
+++ b/addon-sdk/source/test/test-namespace.js
@@ -9,21 +9,20 @@ const { Cc, Ci, Cu } = require("chrome")
 const { setTimeout } = require("sdk/timers")
 
 exports["test post GC references"] = function (assert, done) {
   var target = {}, local = ns()
   local(target).there = true
 
   assert.equal(local(target).there, true, "namespaced preserved");
 
-  setTimeout(function() {
-    Cu.forceGC();
+  Cu.schedulePreciseGC(function() {
     assert.equal(local(target).there, true, "namespace is preserved post GC");
     done();
-  }, 300);
+  });
 };
 
 exports["test namsepace basics"] = function(assert) {
   var privates = ns();
   var object = { foo: function foo() { return "hello foo"; } };
 
   assert.notEqual(privates(object), object,
                   "namespaced object is not the same");
--- a/addon-sdk/source/test/test-selection.js
+++ b/addon-sdk/source/test/test-selection.js
@@ -154,29 +154,27 @@ function getFrameWindow(window) {
  * is not garbaged.
  */
 function hideAndShowFrame(window) {
   let { promise, resolve } = defer();
   let iframe = window.document.querySelector("iframe");
 
   iframe.style.display = "none";
 
-  Cu.forceGC();
-
-  setTimeout(function() {
+  Cu.schedulePreciseGC(function() {
     events.on("document-shown", function shown(event) {
       if (iframe.contentWindow !== event.subject.defaultView)
         return;
 
       events.off("document-shown", shown);
       setTimeout(resolve, 0, window);
     }, true);
 
     iframe.style.display = "";
-  }, 0)
+  });
 
   return promise;
 }
 
 /**
  * Select the first div in the page, adding the range to the selection.
  */
 function selectFirstDiv(window) {
--- a/addon-sdk/source/test/test-system-events.js
+++ b/addon-sdk/source/test/test-system-events.js
@@ -1,17 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const events = require("sdk/system/events");
 const self = require("sdk/self");
 const { Cc, Ci, Cu } = require("chrome");
 const { setTimeout } = require("sdk/timers");
-const { LoaderWithHookedConsole2 } = require("sdk/test/loader");
+const { Loader, LoaderWithHookedConsole2 } = require("sdk/test/loader");
 const nsIObserverService = Cc["@mozilla.org/observer-service;1"].
                            getService(Ci.nsIObserverService);
 
 
 exports["test basic"] = function(assert) {
   let type = Date.now().toString(32);
 
   let timesCalled = 0;
@@ -59,37 +59,41 @@ exports["test error reporting"] = functi
   events.off(errorType, brokenHandler);
 
   loader.unload();
 };
 
 exports["test listeners are GC-ed"] = function(assert, done) {
   let receivedFromWeak = [];
   let receivedFromStrong = [];
-  let type = Date.now().toString(32);
+  let loader = Loader(module);
+  let events = loader.require('sdk/system/events');
+
+  let type = 'test-listeners-are-garbage-collected';
   function handler(event) { receivedFromStrong.push(event); }
   function weakHandler(event) { receivedFromWeak.push(event); }
 
   events.on(type, handler, true);
   events.on(type, weakHandler);
 
   events.emit(type, { data: 1 });
   assert.equal(receivedFromStrong.length, 1, "strong listener invoked");
   assert.equal(receivedFromWeak.length, 1, "weak listener invoked");
 
   handler = weakHandler = null;
 
-  Cu.forceGC();
-  setTimeout(function() {
-    Cu.forceGC();
+  Cu.schedulePreciseGC(function() {
     events.emit(type, { data: 2 });
+
     assert.equal(receivedFromWeak.length, 1, "weak listener was GC-ed");
     assert.equal(receivedFromStrong.length, 2, "strong listener was invoked");
+
+    loader.unload();
     done();
-  }, 300);
+  });
 };
 
 exports["test handle nsIObserverService notifications"] = function(assert) {
   let ios = Cc['@mozilla.org/network/io-service;1']
             .getService(Ci.nsIIOService);
 
   let uri = ios.newURI("http://www.foo.com", null, null);
 
--- a/addon-sdk/source/test/windows/test-fennec-windows.js
+++ b/addon-sdk/source/test/windows/test-fennec-windows.js
@@ -5,17 +5,17 @@
 
 const { Cc, Ci } = require('chrome');
 const { setTimeout } = require('sdk/timers');
 const { Loader } = require('sdk/test/loader');
 const WM = Cc['@mozilla.org/appshell/window-mediator;1'].
            getService(Ci.nsIWindowMediator);
 const { browserWindows } = require('sdk/windows');
 
-const ERR_MSG = 'This method is not yet supported by Fennec, consider using require("tabs") instead';
+const ERR_MSG = 'This method is not yet supported by Fennec, consider using require("sdk/tabs") instead';
 
 // TEST: browserWindows.length for Fennec
 exports.testBrowserWindowsLength = function(test) {
   test.assertEqual(browserWindows.length, 1, "Only one window open");
 };
 
 // TEST: open & close window
 exports.testOpenWindow = function(test) {
--- a/addon-sdk/source/test/windows/test-firefox-windows.js
+++ b/addon-sdk/source/test/windows/test-firefox-windows.js
@@ -176,16 +176,18 @@ exports.testOnOpenOnCloseListeners = fun
   windows.open({
     url: "data:text/html;charset=utf-8,foo",
     onOpen: function(window) {
       window.close(verify);
     }
   });
 };
 
+/*
+Disabled due to all of the Win8 PGO bustage in bug 873007.
 exports.testActiveWindow = function(test) {
   const xulApp = require("sdk/system/xul-app");
   if (xulApp.versionInRange(xulApp.platformVersion, "1.9.2", "1.9.2.*")) {
     test.pass("This test is disabled on 3.6. For more information, see bug 598525");
     return;
   }
 
   let windows = browserWindows;
@@ -278,16 +280,17 @@ exports.testActiveWindow = function(test
   function finishTest() {
     window3.close(function() {
       window2.close(function() {
         test.done();
       });
     });
   }
 };
+*/
 
 exports.testTrackWindows = function(test) {
   test.waitUntilDone();
 
   let windows = [];
   let actions = [];
 
   let expects = [