Bug 842762: Uplift Add-on SDK changeset 4e44dd094e8636216e8c9ca5664866a5cf7d0cdd
☠☠ backed out by 1c3fed103251 ☠ ☠
authorDave Townsend <dtownsend@oxymoronical.com>
Tue, 19 Feb 2013 14:14:54 -0800
changeset 132767 a104642698a0a40b2f7e0bba316c28d63b00939c
parent 132766 27652a4eddb470d29d1850e02be5d73727385803
child 132768 60c254ffc78fc1b3bd6bf7e5b6d5b02335695812
push id2452
push userlsblakk@mozilla.com
push dateMon, 13 May 2013 16:59:38 +0000
treeherdermozilla-beta@d4b152d29d8d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs842762
milestone22.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 842762: Uplift Add-on SDK changeset 4e44dd094e8636216e8c9ca5664866a5cf7d0cdd https://github.com/mozilla/addon-sdk/compare/a16bbd5772880b578a939eeb65102bca6560d494...4e44dd094e8636216e8c9ca5664866a5cf7d0cdd
addon-sdk/source/README
addon-sdk/source/app-extension/bootstrap.js
addon-sdk/source/app-extension/install.rdf
addon-sdk/source/data/test-trusted-document.html
addon-sdk/source/doc/dev-guide-source/credits.md
addon-sdk/source/doc/dev-guide-source/guides/content-scripts/using-port.md
addon-sdk/source/doc/dev-guide-source/guides/stability.md
addon-sdk/source/doc/dev-guide-source/tutorials/mobile.md
addon-sdk/source/doc/module-source/sdk/context-menu.md
addon-sdk/source/doc/module-source/sdk/core/promise.md
addon-sdk/source/doc/module-source/sdk/preferences/service.md
addon-sdk/source/doc/module-source/sdk/private-browsing.md
addon-sdk/source/doc/static-files/base.html
addon-sdk/source/doc/static-files/css/sdk-docs.css
addon-sdk/source/lib/sdk/addon/runner.js
addon-sdk/source/lib/sdk/core/promise.js
addon-sdk/source/lib/sdk/deprecated/unit-test-finder.js
addon-sdk/source/lib/sdk/indexed-db.js
addon-sdk/source/lib/sdk/l10n/html.js
addon-sdk/source/lib/sdk/loader/cuddlefish.js
addon-sdk/source/lib/sdk/net/xhr.js
addon-sdk/source/lib/sdk/private-browsing.js
addon-sdk/source/lib/sdk/private-browsing/utils.js
addon-sdk/source/lib/sdk/private-browsing/window/utils.js
addon-sdk/source/lib/sdk/request.js
addon-sdk/source/lib/sdk/selection.js
addon-sdk/source/lib/sdk/system/xul-app.js
addon-sdk/source/lib/sdk/tabs/tab-fennec.js
addon-sdk/source/lib/sdk/tabs/tab-firefox.js
addon-sdk/source/lib/sdk/tabs/utils.js
addon-sdk/source/lib/sdk/test.js
addon-sdk/source/lib/sdk/test/harness.js
addon-sdk/source/lib/sdk/test/runner.js
addon-sdk/source/lib/sdk/window/browser.js
addon-sdk/source/lib/sdk/windows/firefox.js
addon-sdk/source/lib/test.js
addon-sdk/source/lib/toolkit/loader.js
addon-sdk/source/python-lib/cuddlefish/__init__.py
addon-sdk/source/python-lib/cuddlefish/docs/generate.py
addon-sdk/source/python-lib/cuddlefish/manifest.py
addon-sdk/source/python-lib/cuddlefish/rdf.py
addon-sdk/source/python-lib/cuddlefish/tests/test_linker.py
addon-sdk/source/python-lib/cuddlefish/tests/test_xpi.py
addon-sdk/source/python-lib/markdown/__init__.py
addon-sdk/source/python-lib/mozrunner/__init__.py
addon-sdk/source/test/addons/layout-change/main.js
addon-sdk/source/test/addons/layout-change/package.json
addon-sdk/source/test/addons/packed/main.js
addon-sdk/source/test/addons/packed/package.json
addon-sdk/source/test/addons/unpacked/main.js
addon-sdk/source/test/addons/unpacked/package.json
addon-sdk/source/test/commonjs-test-adapter/asserts.js
addon-sdk/source/test/modules/tiger.js
addon-sdk/source/test/private-browsing/global.js
addon-sdk/source/test/private-browsing/helper.js
addon-sdk/source/test/private-browsing/tabs.js
addon-sdk/source/test/private-browsing/windows.js
addon-sdk/source/test/tabs/test-firefox-tabs.js
addon-sdk/source/test/test-addon-page.js
addon-sdk/source/test/test-context-menu.js
addon-sdk/source/test/test-httpd.js
addon-sdk/source/test/test-indexed-db.js
addon-sdk/source/test/test-layout-change.js
addon-sdk/source/test/test-loader.js
addon-sdk/source/test/test-net-url.js
addon-sdk/source/test/test-page-mod.js
addon-sdk/source/test/test-panel.js
addon-sdk/source/test/test-private-browsing.js
addon-sdk/source/test/test-request.js
addon-sdk/source/test/test-selection.js
addon-sdk/source/test/test-simple-prefs.js
addon-sdk/source/test/test-tab.js
addon-sdk/source/test/test-url.js
addon-sdk/source/test/test-widget.js
addon-sdk/source/test/windows/test-firefox-windows.js
--- a/addon-sdk/source/README
+++ b/addon-sdk/source/README
@@ -33,13 +33,13 @@ If you get an error when running cfx or 
 started, see the "Troubleshooting" guide at:
 https://addons.mozilla.org/en-US/developers/docs/sdk/latest/dev-guide/tutorials/troubleshooting.html
 
 Bugs
 -------
 
 * file a bug: https://bugzilla.mozilla.org/enter_bug.cgi?product=Add-on%20SDK
 
+
 Style Guidelines
 --------------------
 
-* https://github.com/mozilla/addon-sdk/wiki/Coding-style-guide
-
+* https://github.com/mozilla/addon-sdk/wiki/Coding-style-guide
--- a/addon-sdk/source/app-extension/bootstrap.js
+++ b/addon-sdk/source/app-extension/bootstrap.js
@@ -50,39 +50,16 @@ function readURI(uri) {
     data += str.value;
   } while (read != 0);
 
   cstream.close();
 
   return data;
 }
 
-// Utility function that converts cfx-py generated paths to a
-// module ids.
-function path2id(path) {
-  // Strips out `/lib` and `.js` from package/lib/path.js
-  return path.replace(/([^\/]*)\/lib/, '$1').replace(/.js$/, '');
-}
-// Utility function that takes old manifest format and creates a manifest
-// in a new format: https://github.com/mozilla/addon-sdk/wiki/JEP-Linker
-function manifestV2(manifest) {
-  return Object.keys(manifest).reduce(function(result, path) {
-    let entry = manifest[path];
-    let id = path2id(path);
-    let requirements = entry.requirements || {};
-    result[id] = {
-      requirements: Object.keys(requirements).reduce(function(result, path) {
-        result[path] = path2id(requirements[path].path);
-        return result;
-      }, {})
-    };
-    return result
-  }, {});
-}
-
 // We don't do anything on install & uninstall yet, but in a future
 // we should allow add-ons to cleanup after uninstall.
 function install(data, reason) {}
 function uninstall(data, reason) {}
 
 function startup(data, reasonCode) {
   try {
     let reason = REASON[reasonCode];
@@ -110,38 +87,50 @@ function startup(data, reasonCode) {
       replace(/\./g, '-dot-').
       replace(uuidRe, '$1');
 
     let prefixURI = 'resource://' + domain + '/';
     let resourcesURI = ioService.newURI(rootURI + '/resources/', null, null);
     resourceHandler.setSubstitution(domain, resourcesURI);
 
     // Create path to URLs mapping supported by loader.
-    let paths = Object.keys(options.metadata).reduce(function(result, name) {
-      result[name + '/'] = prefixURI + name + '/lib/'
-      result[name + '/tests/'] = prefixURI + name + '/tests/'
-      return result
-    }, {
+    let paths = {
       // Relative modules resolve to add-on package lib
       './': prefixURI + name + '/lib/',
-      'toolkit/': 'resource://gre/modules/toolkit/',
-      '': 'resources:///modules/'
-    });
+      './tests/': prefixURI + name + '/tests/',
+      '': 'resource://gre/modules/commonjs/'
+    };
+
+    // Maps addon lib and tests ressource folders
+    paths[name + '/'] = prefixURI + name + '/lib/';
+    paths[name + '/tests/'] = prefixURI + name + '/tests/';
+    // We need to map tests folder when we run sdk tests whose package name
+    // is stripped
+    if (name == 'addon-sdk')
+      paths['tests/'] = prefixURI + name + '/tests/';
+
+    // Maps sdk module folders to their resource folder
+    paths['sdk/'] = prefixURI + 'addon-sdk/lib/sdk/';
+    paths['toolkit/'] = prefixURI + 'addon-sdk/lib/toolkit/';
+    // test.js is usually found in root commonjs or SDK_ROOT/lib/ folder,
+    // so that it isn't shipped in the xpi. Keep a copy of it in sdk/ folder
+    // until we no longer support SDK modules in XPI:
+    paths['test'] = prefixURI + 'addon-sdk/lib/sdk/test.js';
 
     // Make version 2 of the manifest
-    let manifest = manifestV2(options.manifest);
+    let manifest = options.manifest;
 
     // Import `cuddlefish.js` module using a Sandbox and bootstrap loader.
     let cuddlefishURI = prefixURI + options.loader;
     cuddlefishSandbox = loadSandbox(cuddlefishURI);
     let cuddlefish = cuddlefishSandbox.exports;
 
     // Normalize `options.mainPath` so that it looks like one that will come
     // in a new version of linker.
-    let main = path2id(options.mainPath);
+    let main = options.mainPath;
 
     unload = cuddlefish.unload;
     loader = cuddlefish.Loader({
       paths: paths,
       // modules manifest.
       manifest: manifest,
 
       // Add-on ID used by different APIs as a unique identifier.
@@ -175,17 +164,17 @@ function startup(data, reasonCode) {
           profileMemory: options.profileMemory,
           stopOnError: options.stopOnError,
           verbose: options.verbose,
           parseable: options.parseable,
         }
       }
     });
 
-    let module = cuddlefish.Module('addon-sdk/sdk/loader/cuddlefish', cuddlefishURI);
+    let module = cuddlefish.Module('sdk/loader/cuddlefish', cuddlefishURI);
     let require = cuddlefish.Require(loader, module);
 
     require('sdk/addon/runner').startup(reason, {
       loader: loader,
       main: main,
       prefsURI: rootURI + 'defaults/preferences/prefs.js'
     });
   } catch (error) {
--- a/addon-sdk/source/app-extension/install.rdf
+++ b/addon-sdk/source/app-extension/install.rdf
@@ -13,17 +13,17 @@
     <em:bootstrap>true</em:bootstrap>
     <em:unpack>false</em:unpack>
 
     <!-- Firefox -->
     <em:targetApplication>
       <Description>
         <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
         <em:minVersion>18.0</em:minVersion>
-        <em:maxVersion>20.*</em:maxVersion>
+        <em:maxVersion>21.0a1</em:maxVersion>
       </Description>
     </em:targetApplication>
 
     <!-- Front End MetaData -->
     <em:name>Test App</em:name>
     <em:description>Harness for tests.</em:description>
     <em:creator>Mozilla Corporation</em:creator>
     <em:homepageURL></em:homepageURL>
--- a/addon-sdk/source/data/test-trusted-document.html
+++ b/addon-sdk/source/data/test-trusted-document.html
@@ -5,14 +5,16 @@
 <html>
 <head>
     <meta charset="UTF-8">
     <title>Worker test</title>
 </head>
 <body>
   <p id="paragraph">Lorem ipsum dolor sit amet.</p>
   <script>
-    addon.port.on('addon-to-document', function (arg) {
-      addon.port.emit('document-to-addon', arg);
-    });
+    if ("addon" in window) {
+      addon.port.on('addon-to-document', function (arg) {
+        addon.port.emit('document-to-addon', arg);
+      });
+    }
   </script>
 </body>
 </html>
--- a/addon-sdk/source/doc/dev-guide-source/credits.md
+++ b/addon-sdk/source/doc/dev-guide-source/credits.md
@@ -6,63 +6,149 @@
 
 We'd like to thank our many Jetpack project contributors!  They include:
 
 * Adamantium
 * Ehsan Akhgari
 * arky
 * [Heather Arthur](https://github.com/harthur)
 * Dietrich Ayala
+
+<!--end-->
+
 * [Romain B](https://github.com/Niamor)
+* [Louis-Rémi Babé](https://github.com/louisremi)
 * Will Bamberg
+* Thomas Bassetto
+* Tomaz Bevec
 * Zbigniew Braniecki
 * Daniel Buchner
 * James Burke
+
+<!--end-->
+
 * [Shane Caraveo](https://github.com/mixedpuppy)
 * [Matěj Cepl](https://github.com/mcepl)
+* Marc Chevrier
 * Hernán Rodriguez Colmeiro
 * [David Creswick](https://github.com/dcrewi)
+
+<!--end-->
+
 * dexter
+* Christopher Dorn
+* Connor Dunn
+* dynamis
+
+<!--end-->
+
 * [Matteo Ferretti (ZER0)](https://github.com/ZER0)
 * fuzzykiller
+
+<!--end-->
+
 * [Marcio Galli](https://github.com/taboca)
 * [Ben Gillbanks](http://www.iconfinder.com/browse/iconset/circular_icons/)
 * Felipe Gomes
 * Irakli Gozalishvili
 * Luca Greco
+* Jeff Griffiths
+* [David Guo](https://github.com/dglol)
+
+<!--end-->
+
 * Mark Hammond
+* Mark A. Hershberger
 * Lloyd Hilaiel
 * Bobby Holley
+
+<!--end-->
+
+* Shun Ikejima
+
+<!--end-->
+
 * Eric H. Jung
+
+<!--end-->
+
 * Hrishikesh Kale
 * Wes Kocher
+* Lajos Koszti
+
+<!--end-->
+
 * Edward Lee
+* Gregg Lind
+
+<!--end-->
+
+* [Nils Maier](https://github.com/nmaier)
+* Gervase Markham
+* Dave Mason
 * Myk Melez
 * Zandr Milewski
 * Noelle Murata
+
+<!--end-->
+
+* Siavash Askari Nasr
 * Joe R. Nassimian ([placidrage](https://github.com/placidrage))
+* Dương H. Nguyễn
 * Nick Nguyen
+
+<!--end-->
+
 * [ongaeshi](https://github.com/ongaeshi)
 * Paul O’Shannessy
-* l.m.orchard
+* Les Orchard
+
+<!--end-->
+
+* Robert Pankowecki
 * Alexandre Poirot
 * Nickolay Ponomarev
+
+<!--end-->
+
 * Aza Raskin
+
+<!--end-->
+
 * Till Schneidereit
 * Justin Scott
 * Ayan Shah
 * [skratchdot](https://github.com/skratchdot)
+* Henrik Skupin
+* slash
+* Markus Stange
+* Dan Stevens
 * [Mihai Sucan](https://github.com/mihaisucan)
+
+<!--end-->
+
+* taku0
 * Clint Talbert
-* Thomas
+* Tim Taubert
+* Shane Tomlinson
 * Dave Townsend
+* [Matthias Tylkowski](https://github.com/tylkomat)
+
+<!--end-->
+
 * Peter Van der Beken
+* Sander van Veen
 * Atul Varma
 * [Erik Vold](https://github.com/erikvold)
 * Vladimir Vukicevic
+
+<!--end-->
+
 * Brian Warner
 * [Henri Wiechers](https://github.com/hwiechers)
 * Drew Willcoxon
+* Blake Winton
+* Michal Wojciechowski
+
+<!--end-->
+
 * Piotr Zalewa
-* [David Guo](https://github.com/dglol)
-* [Nils Maier](https://github.com/nmaier)
-* [Louis-Rémi Babé](https://github.com/louisremi)
-* [Matthias Tylkowski](https://github.com/tylkomat)
+* Brett Zamir
--- a/addon-sdk/source/doc/dev-guide-source/guides/content-scripts/using-port.md
+++ b/addon-sdk/source/doc/dev-guide-source/guides/content-scripts/using-port.md
@@ -1,110 +1,109 @@
 <!-- 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/. -->
 
 
 # Communicating using "port" #
 
 To enable add-on scripts and content scripts to communicate with each other,
-each end of the conversation has access to a `port` object which defines two
-functions:
+each end of the conversation has access to a `port` object.
 
-**`emit()`** is used to emit an event. It may be called with any number of
-parameters, but is most likely to be called with a name for the event and
-an optional payload. The payload can be any value that is
-<a href = "dev-guide/guides/content-scripts/using-port.html#json_serializable">serializable to JSON</a>
+* to send messages from one side to the other, use `port.emit()`
+* to receive messages sent from the other side, use `port.on()`
 
-    port.emit("myEvent", myEventPayload);
-
-**`on()`** takes two parameters: the name of the event and a function to handle it:
+<img class="image-center" src="static-files/media/content-scripting-events.png"
+alt="Content script events">
 
-    port.on("myEvent", function handleMyEvent(myEventPayload) {
-      // Handle the event
-    });
+Messages are asynchronous: that is, the sender does not wait for a reply from
+the recipient but just emits the message and continues processing.
 
-Here's  simple add-on that sends a message to a content script using `port`:
+Here's a simple add-on that sends a message to a content script using `port`:
 
     var tabs = require("sdk/tabs");
 
     var alertContentScript = "self.port.on('alert', function(message) {" +
                              "  window.alert(message);" +
                              "})";
 
     tabs.on("ready", function(tab) {
       worker = tab.attach({
         contentScript: alertContentScript
       });
       worker.port.emit("alert", "Message from the add-on");
     });
 
     tabs.open("http://www.mozilla.org");
 
-We could depict the interface between add-on code and content script code like
-this:
+In total, the `port` object defines four functions:
 
-<img class="image-center" src="static-files/media/content-scripting-events.png"
-alt="Content script events">
+* [`emit()`](dev-guide/guides/content-scripts/using-port.html#port.emit()):
+emit a message.
+* [`on()`](dev-guide/guides/content-scripts/using-port.html#port.on()):
+listen to a message.
+* [`removeListener()`](dev-guide/guides/content-scripts/using-port.html#port.removeListener()):
+stop listening to a message.
+* [`once()`](dev-guide/guides/content-scripts/using-port.html#port.once()):
+listen to only the first occurrence of a message.
 
-Events are asynchronous: that is, the sender does not wait for a reply from
-the recipient but just emits the event and continues processing.
+## Accessing `port` ##
 
-## Accessing `port` in the Content Script ##
+### Accessing `port` in the Content Script ###
 
 <span class="aside">Note that the global `self` object is completely
 different from the [`self` module](modules/sdk/self.html), which
 provides an API for an add-on to access its data files and ID.</span>
 
 In the content script the `port` object is available as a property of the
-global `self` object. Thus, to emit an event from a content script:
+global `self` object. Thus, to emit a message from a content script:
 
-    self.port.emit("myContentScriptEvent", myContentScriptEventPayload);
+    self.port.emit("myContentScriptMessage", myContentScriptMessagePayload);
 
-To receive an event from the add-on code:
+To receive a message from the add-on code:
 
-    self.port.on("myAddonEvent", function(myAddonEventPayload) {
-      // Handle the event
+    self.port.on("myAddonMessage", function(myAddonMessagePayload) {
+      // Handle the message
     });
 
-Compare this to the technique used to receive _built-in_ events in the
-content script. For example, to receive the `context` event in a content script
+Compare this to the technique used to receive _built-in_ messages in the
+content script. For example, to receive the `context` message in a content script
 associated with a [context menu](modules/sdk/context-menu.html)
 object, you would call the `on` function attached to the global `self` object:
 
     self.on("context", function() {
-      // Handle the event
+      // Handle the message
     });
 
 So the `port` property is essentially used here as a namespace for
-user-defined events.
+user-defined messages.
 
-## Accessing `port` in the Add-on Script ##
+### Accessing `port` in the Add-on Script ###
 
 In the add-on code, the channel of communication between the add-on and a
 particular content script context is encapsulated by the `worker` object. Thus
 the `port` object for communicating with a content script is a property of the
 corresponding `worker` object.
 
 However, the worker is not exposed to add-on code in quite the same way
 in all modules. The `panel` and `page-worker` objects integrate the
-worker API directly. So to receive events from a content script associated
+worker API directly. So to receive messages from a content script associated
 with a panel you use `panel.port.on()`:
 
     var panel = require("sdk/panel").Panel({
       contentScript: "self.port.emit('showing', 'panel is showing');"
     });
 
     panel.port.on("showing", function(text) {
       console.log(text);
     });
 
     panel.show();
 
-Conversely, to emit user-defined events from your add-on you can just call
+Conversely, to emit user-defined messages from your add-on you can just call
 `panel.port.emit()`:
 
     var panel = require("sdk/panel").Panel({
       contentScript: "self.port.on('alert', function(text) {" +
                      "  console.log(text);" +
                      "});"
     });
 
@@ -116,18 +115,18 @@ so each distinct page object only needs 
 to its content scripts. But some modules, such as `page-mod`, might need to
 handle multiple pages, each with its own context in which the content scripts
 are executing, so it needs a separate channel (worker) for each page.
 
 So `page-mod` does not integrate the worker API directly: instead, each time a
 content script is attached to a page, the worker associated with the page is
 supplied to the page-mod in its `onAttach` function. By supplying a target for
 this function in the page-mod's constructor you can register to receive
-events from the content script, and take a reference to the worker so as to
-emit events to it.
+messages from the content script, and take a reference to the worker so as to
+emit messages to the content script.
 
     var pageModScript = "window.addEventListener('click', function(event) {" +
                         "  self.port.emit('click', event.target.toString());" +
                         "  event.stopPropagation();" +
                         "  event.preventDefault();" +
                         "}, false);" +
                         "self.port.on('warning', function(message) {" +
                         "window.alert(message);" +
@@ -138,28 +137,136 @@ emit events to it.
       contentScript: pageModScript,
       onAttach: function(worker) {
         worker.port.on('click', function(html) {
           worker.port.emit('warning', 'Do not click this again');
         });
       }
     });
 
-In the add-on above there are two user-defined events:
+In the add-on above there are two user-defined messages:
 
 * `click` is sent from the page-mod to the add-on, when the user clicks an
 element in the page
 * `warning` sends a silly string back to the page-mod
 
+## port.emit() ##
+
+The `port.emit()` function sends a message from the "main.js", or another
+add-on module, to a content script, or vice versa.
+
+It may be called with any number of parameters, but is most likely to be
+called with a name for the message and an optional payload.
+The payload can be any value that is
+<a href = "dev-guide/guides/content-scripts/using-port.html#json_serializable">serializable to JSON</a>.
+
+From the content script to the main add-on code:
+
+    var myMessagePayload = "some data";
+    self.port.emit("myMessage", myMessagePayload);
+
+From the main add-on code (in this case a panel instance)
+to the content script:
+
+    var myMessagePayload = "some data";
+    panel.port.emit("myMessage", myMessagePayload);
+
+## port.on() ##
+
+The `port.on()` function registers a function as a listener for a specific
+named message sent from the other side using `port.emit()`.
+
+It takes two parameters: the name of the message and a function to handle it.
+
+In a content script, to listen for "myMessage" sent from the main
+add-on code:
+
+    self.port.on("myMessage", function handleMyMessage(myMessagePayload) {
+      // Handle the message
+    });
+
+In the main add-on code (in this case a panel instance), to listen for
+"myMessage" sent from a a content script:
+
+    panel.port.on("myMessage", function handleMyMessage(myMessagePayload) {
+      // Handle the message
+    });
+
+## port.removeListener() ##
+
+You can uses `port.on()` to listen for messages. To stop listening for a
+particular message, use `port.removeListener()`. This takes the
+same two parameters as `port.on()`: the name of the message, and the name
+of the listener function.
+
+For example, here's an add-on that creates a page-worker and a widget.
+The page-worker loads
+[http://en.wikipedia.org/wiki/Chalk](http://en.wikipedia.org/wiki/Chalk)
+alongside a content script "listener.js". The widget sends the content script
+a message called "get-first-para" when it is clicked:
+
+    pageWorker = require("sdk/page-worker").Page({
+      contentScriptFile: require("sdk/self").data.url("listener.js"),
+      contentURL: "http://en.wikipedia.org/wiki/Chalk"
+    });
+
+    require("sdk/widget").Widget({
+      id: "mozilla-icon",
+      label: "My Mozilla Widget",
+      contentURL: "http://www.mozilla.org/favicon.ico",
+      onClick: function() {
+        console.log("sending 'get-first-para'");
+        pageWorker.port.emit("get-first-para");
+      }
+    });
+
+The content script "listener.js" listens for "get-first-para". When it
+receives this message, the script logs the first paragraph of the document
+and then calls `removeListener()` to stop listening.
+
+    function getFirstParagraph() {
+      var paras = document.getElementsByTagName('p');
+      console.log(paras[0].textContent);
+      self.port.removeListener("get-first-para", getFirstParagraph);
+    }
+
+    self.port.on("get-first-para", getFirstParagraph);
+
+The result is that the paragraph is only logged the first time the widget
+is clicked.
+
+Due to [bug 816272](https://bugzilla.mozilla.org/show_bug.cgi?id=816272)
+the [`page-mod`](modules/sdk/page-mod.html)'s `removeListener()` function
+does not prevent the listener from receiving messages that are already queued.
+This means that if "main.js" sends the message twice in successive lines, and
+the listener stops listening as soon as it receives the first message, then
+the listener will still receive the second message.
+
+## port.once() ##
+
+Often you'll want to receive a message just once, then stop listening. The
+`port` object offers a shortcut to do this: the `once()` method.
+
+This example rewrites the "listener.js" content script in the
+[`port.removeListener()` example](dev-guide/guides/content-scripts/using-port.html#port.removeListener())
+so that it uses `once()`:
+
+    function getFirstParagraph() {
+      var paras = document.getElementsByTagName('p');
+      console.log(paras[0].textContent);
+    }
+
+    self.port.once("get-first-para", getFirstParagraph);
+
 ## <a name="json_serializable">JSON-Serializable Values</a> ##
 
-The payload for an event can be any JSON-serializable value. When events are
-sent their payloads are automatically serialized, and when events are received
-their payloads are automatically deserialized, so you don't need to worry
-about serialization.
+The payload for an message can be any JSON-serializable value. When messages
+are sent their payloads are automatically serialized, and when messages are
+received their payloads are automatically deserialized, so you don't need to
+worry about serialization.
 
 However, you _do_ have to ensure that the payload can be serialized to JSON.
 This means that it needs to be a string, number, boolean, null, array of
 JSON-serializable values, or an object whose property values are themselves
 JSON-serializable. This means you can't send functions, and if the object
 contains methods they won't be encoded.
 
 For example, to include an array of strings in the payload:
--- a/addon-sdk/source/doc/dev-guide-source/guides/stability.md
+++ b/addon-sdk/source/doc/dev-guide-source/guides/stability.md
@@ -19,27 +19,35 @@ that defines how stable each module is
 that defines when and how stable SDK APIs can be changed or removed from
 future versions of the SDK while giving developers enough time to update
 their code.
 
 ## Stability Index ##
 
 The stability index is adopted from
 [node.js](http://nodejs.org/api/documentation.html#documentation_stability_index).
-The SDK uses only three of the six values defined by node.js:
+The SDK uses only four of the six values defined by node.js:
 
 <table>
   <tr>
     <td>Experimental</td>
     <td>The module is not yet stabilized.
 You can try it out and provide feedback, but we may change or remove
 it in future versions without having to pass through a formal
 deprecation process.</td>
   </tr>
   <tr>
+    <td>Unstable</td>
+    <td>The API is in the process of settling, but has not yet had sufficient
+real-world testing to be considered stable.
+Backwards-compatibility will be maintained if reasonable.
+If we do have to make backwards-incompatible changes, we will not guarantee
+to go through the formal deprecation process.</td>
+  </tr>
+  <tr>
     <td>Stable</td>
     <td>The module is a fully-supported part of
 the SDK. We will avoid breaking backwards compatibility unless absolutely
 necessary. If we do have to make backwards-incompatible changes, we will
 go through the formal deprecation process.</td>
   </tr>
   <tr>
     <td>Deprecated</td>
--- a/addon-sdk/source/doc/dev-guide-source/tutorials/mobile.md
+++ b/addon-sdk/source/doc/dev-guide-source/tutorials/mobile.md
@@ -235,116 +235,101 @@ Afterwards you can delete it using `adb`
 adb shell
 cd /mnt/sdcard
 rm my-addon.xpi
 </pre>
 
 <a name="modules-compatibility"></a>
 ## Module Compatibility
 
-Modules not yet supported in Firefox Mobile are <span class="unsupported-on-mobile">highlighted</span> in the tables below.
+Modules not yet supported in Firefox Mobile are
+<span class="unsupported-on-mobile">**marked**</span> in the tables below.
 
 ### High-Level APIs ###
 
-<ul class="module-list">
-  <li class="unsupported-on-mobile"><a href="modules/sdk/addon-page.html">addon-page</a></li>
-  <li><a href="modules/sdk/base64.html">base64</a></li>
-  <li class="unsupported-on-mobile"><a href="modules/sdk/clipboard.html">clipboard</a></li>
-  <li class="unsupported-on-mobile"><a href="modules/sdk/context-menu.html">context-menu</a></li>
-  <li><a href="modules/sdk/hotkeys.html">hotkeys</a></li>
-  <!-- test-l10n-locale, test-l10n-plural-rules -->
-  <li><a href="modules/sdk/l10n.html">l10n</a></li>
-  <li><a href="modules/sdk/notifications.html">notifications</a></li>
-  <!-- test-page-mod fails, but we know the module works -->
-  <li><a href="modules/sdk/page-mod.html">page-mod</a></li>
-  <li class="unsupported-on-mobile"><a href="modules/sdk/panel.html">panel</a></li>
-  <!-- test-passwords, test-passwords-utils (with exceptions / warning from js console) -->
-  <li><a href="modules/sdk/passwords.html">passwords</a></li>
-  <li class="unsupported-on-mobile"><a href="modules/sdk/private-browsing.html">private-browsing</a></li>
-  <li><a href="modules/sdk/querystring.html">querystring</a></li>
-  <li><a href="modules/sdk/request.html">request</a></li>
-  <li class="unsupported-on-mobile"><a href="modules/sdk/selection.html">selection</a></li>
-  <li><a href="modules/sdk/self.html">self</a></li>
-  <li><a href="modules/sdk/simple-prefs.html">simple-prefs</a></li>
-  <li><a href="modules/sdk/simple-storage.html">simple-storage</a></li>
-  <!-- test-tabs, test-tabs-common -->
-  <li><a href="modules/sdk/tabs.html">tabs</a></li>
-  <!-- test-timer -->
-  <li><a href="modules/sdk/timers.html">timers</a></li>
-  <li><a href="modules/sdk/url.html">url</a></li>
-  <li><a href="modules/sdk/widget.html">widget</a></li>
-  <!-- test-windows-common, test-windows -->
-  <li><a href="modules/sdk/windows.html">windows</a></li>
-</ul>
+ - [**addon-page**](modules/sdk/addon-page.html)
+ - [base64](modules/sdk/clipboard.html)
+ - [**clipboard**](modules/sdk/clipboard.html)
+ - [**context-menu**](modules/sdk/context-menu.html)
+ - [hotkeys](modules/sdk/hotkeys.html)
+ - [indexed-db](modules/sdk/indexed-db.html)
+ - [l10n](modules/sdk/l10n.html)
+ - [notifications](modules/sdk/notifications.html)
+ - [page-mod](modules/sdk/notifications.html)
+ - [page-worker](modules/sdk/page-worker.html)
+ - [**panel**](modules/sdk/panel.html)
+ - [passwords](modules/sdk/passwords.html)
+ - [**private-browsing**](modules/sdk/private-browsing.html)
+ - [querystring](modules/sdk/querystring.html)
+ - [request](modules/sdk/request.html)
+ - [**selection**](modules/sdk/selection.html)
+ - [self](modules/sdk/self.html)
+ - [simple-prefs](modules/sdk/simple-prefs.html)
+ - [simple-storage](modules/sdk/simple-storage.html)
+ - [system](modules/sdk/system.html)
+ - [tabs](modules/sdk/tabs.html)
+ - [timers](modules/sdk/timers.html)
+ - [url](modules/sdk/url.html)
+ - [**widget**](modules/sdk/widget.html)
+ - [windows](modules/sdk/windows.html)
 
 ### Low-Level APIs ###
 
-<ul class="module-list">
-  <li><a href="modules/toolkit/loader.html">/loader</a></li>
-  <li><a href="dev-guide/tutorials/chrome.html">chrome</a></li>
-  <li><a href="modules/sdk/console/plain-text.html">console/plain-text</a></li>
-  <li><a href="modules/sdk/console/traceback.html">console/traceback</a></li>
-  <li class="unsupported-on-mobile"><a href="modules/sdk/content/content.html">content/content</a></li>
-  <li><a href="modules/sdk/content/loader.html">content/loader</a></li>
-  <li class="unsupported-on-mobile"><a href="modules/sdk/content/symbiont.html">content/symbiont</a></li>
-  <li class="unsupported-on-mobile"><a href="modules/sdk/content/worker.html">content/worker</a></li>
-  <li>core/disposable</li>
-  <li><a href="modules/sdk/core/heritage.html">core/heritage</a></li>
-  <li><a href="modules/sdk/core/namespace.html">core/namespace</a></li>
-  <li><a href="modules/sdk/core/promise.html">core/promise</a></li>
-  <li><a href="modules/sdk/deprecated/api-utils.html">deprecated/api-utils</a></li>
-  <li><a href="modules/sdk/deprecated/app-strings.html">deprecated/app-strings</a></li>
-  <li><a href="modules/sdk/deprecated/cortex.html">deprecated/cortex</a></li>
-  <li><a href="modules/sdk/deprecated/errors.html">deprecated/errors</a></li>
-  <li><a href="modules/sdk/deprecated/events.html">deprecated/events</a></li>
-  <li><a href="modules/sdk/deprecated/light-traits.html">deprecated/light-traits</a></li>
-  <li>deprecated/list</li>
-  <li><a href="modules/sdk/deprecated/observer-service.html">deprecated/observer-service</a></li>
-  <li class="unsupported-on-mobile"><a href="modules/sdk/deprecated/tab-browser.html">deprecated/tab-browser</a></li>
-  <!-- test-traits-core, test-traits -->
-  <li><a href="modules/sdk/deprecated/traits.html">deprecated/traits</a></li>
-  <li class="unsupported-on-mobile"><a href="modules/sdk/deprecated/window-utils.html">deprecated/window-utils</a></li>
-  <!-- test-dom -->
-  <li>dom/events</li>
-  <li><a href="modules/sdk/event/core.html">event/core</a></li>
-  <li><a href="modules/sdk/event/target.html">event/target</a></li>
-  <li><a href="modules/sdk/frame/hidden-frame.html">frame/hidden-frame</a></li>
-  <li><a href="modules/sdk/frame/utils.html">frame/utils</a></li>
-  <li><a href="modules/sdk/io/byte-streams.html">io/byte-streams</a></li>
-  <li><a href="modules/sdk/io/file.html">io/file</a></li>
-  <li><a href="modules/sdk/io/text-streams.html">io/text-streams</a></li>
-  <li>keyboard/observer</li>
-  <li>keyboard/utils</li>
-  <li>lang/functional</li>
-  <li>lang/type</li>
-  <li><a href="modules/sdk/loader/cuddlefish.html">loader/cuddlefish</a></li>
-  <li><a href="modules/sdk/loader/sandbox.html">loader/sandbox</a></li>
-  <li><a href="modules/sdk/net/url.html">net/url</a></li>
-  <li><a href="modules/sdk/net/xhr.html">net/xhr</a></li>
-  <li><a href="modules/sdk/page-mod/match-pattern.html">page-mod/match-pattern</a></li>
-  <li><a href="modules/sdk/platform/xpcom.html">platform/xpcom</a></li>
-  <!-- test-preferences-service, test-preferences-target -->
-  <li><a href="modules/sdk/preferences/service.html">preferences/service</a></li>
-  <li><a href="modules/sdk/system/environment.html">system/environment</a></li>
-  <!-- No test for `system/events`, assuming it works because other compatible modules are using it -->
-  <li><a href="modules/sdk/system/events.html">system/events</a></li>
-  <li>system/globals</li>
-  <!-- No test for `system/events`, assuming it works because other compatible modules are using it -->
-  <li><a href="modules/sdk/system/runtime.html">system/runtime</a></li>
-  <li><a href="modules/sdk/system/unload.html">system/unload</a></li>
-  <li><a href="modules/sdk/system/xul-app.html">system/xul-app</a></li>
-  <!-- No test for `assert`, assuming it works because the test are using it -->
-  <li><a href="modules/sdk/test/assert.html">test/assert</a></li>
-  <!-- No test for `harness`, assuming it works because the test are using it -->
-  <li><a href="modules/sdk/test/harness.html">test/harness</a></li>
-  <li><a href="modules/sdk/test/httpd.html">test/httpd</a></li>
-  <!-- No test for `runner`, assuming it works because the test are using it -->
-  <li><a href="modules/sdk/test/runner.html">test/runner</a></li>
-  <li>test/tmp-file</li>
-  <li>util/array</li>
-  <li><a href="modules/sdk/util/collection.html">util/collection</a></li>
-  <li><a href="modules/sdk/util/deprecate.html">util/deprecate</a></li>
-  <li><a href="modules/sdk/util/list.html">util/list</a></li>
-  <li>util/registry</li>
-  <li><a href="modules/sdk/util/uuid.html">util/uuid</a></li>
-  <!-- test-window-utils2 -->
-  <li><a href="modules/sdk/window/utils.html">window/utils</a></li>
-</ul>
+ - [/loader](modules/toolkit/loader.html)
+ - [chrome](dev-guide/tutorials/chrome.html)
+ - [console/plain-text](modules/sdk/console/plain-text.html)
+ - [console/traceback](modules/sdk/console/traceback.html)
+ - [**content/content**](modules/sdk/content/content.html)
+ - [content/loader](modules/sdk/content/loader.html)
+ - [**content/symbiont**](modules/sdk/content/symbiont.html)
+ - [**content/worker**](modules/sdk/content/worker.html)
+ - core/disposable
+ - [core/heritage](modules/sdk/core/heritage.html)
+ - [core/namespace](modules/sdk/core/namespace.html)
+ - [core/promise](modules/sdk/core/promise.html)
+ - [deprecated/api-utils](modules/sdk/deprecated/api-utils.html)
+ - [deprecated/app-strings](modules/sdk/deprecated/app-strings.html)
+ - [deprecated/cortex](modules/sdk/deprecated/cortex.html)
+ - [deprecated/errors](modules/sdk/deprecated/errors.html)
+ - [deprecated/events](modules/sdk/deprecated/events.html)
+ - [deprecated/light-traits](modules/sdk/deprecated/light-traits.html)
+ - deprecated/list
+ - [deprecated/observer-service](modules/sdk/deprecated/observer-service.html)
+ - [**deprecated/tab-browser**](modules/sdk/deprecated/tab-browser.html)
+ - [deprecated/traits](modules/sdk/deprecated/traits.html)
+ - [**deprecated/window-utils**](modules/sdk/deprecated/window-utils.html)
+ - dom/events
+ - [event/core](modules/sdk/event/core.html)
+ - [event/target](modules/sdk/event/target.html)
+ - [frame/hidden-frame](modules/sdk/frame/hidden-frame.html)
+ - [frame/utils](modules/sdk/frame/utils.html)
+ - [io/byte-streams](modules/sdk/io/byte-streams.html)
+ - [io/file](modules/sdk/io/file.html)
+ - [io/text-streams](modules/sdk/io/text-streams.html)
+ - keyboard/observer
+ - keyboard/utils
+ - lang/functional
+ - lang/type
+ - [loader/cuddlefish](modules/sdk/loader/cuddlefish.html)
+ - [loader/sandbox](modules/sdk/loader/sandbox.html)
+ - [net/url](modules/sdk/net/url.html)
+ - [net/xhr](modules/sdk/net/xhr.html)
+ - [page-mod/match-pattern](modules/sdk/page-mod/match-pattern.html)
+ - [platform/xpcom](modules/sdk/platform/xpcom.html)
+ - [preferences/service](modules/sdk/preferences/service.html)
+ - [system/environment](modules/sdk/system/environment.html)
+ - [system/events](modules/sdk/system/events.html)
+ - system/globals
+ - [system/runtime](modules/sdk/system/runtime.html)
+ - [system/unload](modules/sdk/system/unload.html)
+ - [system/xul-app](modules/sdk/system/xul-app.html)
+ - [test/assert](modules/sdk/test/assert.html)
+ - [test/harness](modules/sdk/test/harness.html)
+ - [test/httpd](modules/sdk/test/httpd.html)
+ - [test/runner](modules/sdk/test/runner.html)
+ - test/tmp-file
+ - util/array
+ - [util/collection](modules/sdk/util/collection.html)
+ - [util/deprecate](modules/sdk/util/deprecate.html)
+ - [util/list](modules/sdk/util/list.html)
+ - util/registry
+ - [util/uuid](modules/sdk/util/uuid.html)
+ - [window/utils](modules/sdk/window/utils.html)
--- a/addon-sdk/source/doc/module-source/sdk/context-menu.md
+++ b/addon-sdk/source/doc/module-source/sdk/context-menu.md
@@ -7,84 +7,84 @@
 
 The `context-menu` module lets you add items to Firefox's page context menu.
 
 
 Introduction
 ------------
 
 The `context-menu` API provides a simple, declarative way to add items to the
-page's context menu.  You can add items that perform an action when clicked,
+page's context menu. You can add items that perform an action when clicked,
 submenus, and menu separators.
 
 Instead of manually adding items when particular contexts occur and then
 removing them when those contexts go away, you *bind* items to contexts, and the
-adding and removing is automatically handled for you.  Items are bound to
-contexts in much the same way that event listeners are bound to events.  When
+adding and removing is automatically handled for you. Items are bound to
+contexts in much the same way that event listeners are bound to events. When
 the user invokes the context menu, all of the items bound to the current context
-are automatically added to the menu.  If no items are bound, none are added.
+are automatically added to the menu. If no items are bound, none are added.
 Likewise, any items that were previously in the menu but are not bound to the
-current context are automatically removed from the menu.  You never need to
+current context are automatically removed from the menu. You never need to
 manually remove your items from the menu unless you want them to never appear
 again.
 
 For example, if your add-on needs to add a context menu item whenever the
 user visits a certain page, don't create the item when that page loads, and
-don't remove it when the page unloads.  Rather, create your item only once and
+don't remove it when the page unloads. Rather, create your item only once and
 supply a context that matches the target URL.
 
 Context menu items are displayed in the order created or in the case of sub
 menus the order added to the sub menu. Menu items for each add-on will be
 grouped together automatically. If the total number of menu items in the main
 context menu from all add-ons exceeds a certain number (normally 10 but
 configurable with the `extensions.addon-sdk.context-menu.overflowThreshold`
 preference) all of the menu items will instead appear in an overflow menu to
 avoid making the context menu too large.
 
 Specifying Contexts
 -------------------
 
 As its name implies, the context menu should be reserved for the occurrence of
-specific contexts.  Contexts can be related to page content or the page itself,
+specific contexts. Contexts can be related to page content or the page itself,
 but they should never be external to the page.
 
 For example, a good use of the menu would be to show an "Edit Image" item when
-the user right-clicks an image in the page.  A bad use would be to show a
+the user right-clicks an image in the page. A bad use would be to show a
 submenu that listed all the user's tabs, since tabs aren't related to the page
 or the node the user clicked to open the menu.
 
 ### The Page Context
 
-First of all, you may not need to specify a context at all.  When a top-level
-item does not specify a context, the page context applies.  An item that is in a
+First of all, you may not need to specify a context at all. When a top-level
+item does not specify a context, the page context applies. An item that is in a
 submenu is visible unless you specify a context.
 
 The *page context* occurs when the user invokes the context menu on a
-non-interactive portion of the page.  Try right-clicking a blank spot in this
-page, or on text.  Make sure that no text is selected.  The menu that appears
-should contain the items "Back", "Forward", "Reload", "Stop", and so on.  This
+non-interactive portion of the page. Try right-clicking a blank spot in this
+page, or on text. Make sure that no text is selected. The menu that appears
+should contain the items "Back", "Forward", "Reload", "Stop", and so on. This
 is the page context.
 
-The page context is appropriate when your item acts on the page as a whole.  It
+The page context is appropriate when your item acts on the page as a whole. It
 does not occur when the user invokes the context menu on a link, image, or other
 non-text node, or while a selection exists.
 
 ### Declarative Contexts
 
 You can specify some simple, declarative contexts when you create a menu item by
 setting the `context` property of the options object passed to its constructor,
 like this:
 
     var cm = require("sdk/context-menu");
     cm.Item({
       label: "My Menu Item",
       context: cm.URLContext("*.mozilla.org")
     });
 
-These contexts may be specified by calling the following constructors.  Each is
+These contexts may be specified by calling the following constructors. Each is
 exported by the `context-menu` module.
 
 <table>
   <tr>
     <th>Constructor</th>
     <th>Description</th>
   </tr>
   <tr>
@@ -116,125 +116,203 @@ exported by the `context-menu` module.
     </td>
   </tr>
   <tr>
     <td><code>
       URLContext(matchPattern)
     </code></td>
     <td>
       This context occurs when the menu is invoked on pages with particular
-      URLs.  <code>matchPattern</code> is a match pattern string or an array of
-      match pattern strings.  When <code>matchPattern</code> is an array, the
+      URLs. <code>matchPattern</code> is a match pattern string or an array of
+      match pattern strings. When <code>matchPattern</code> is an array, the
       context occurs when the menu is invoked on a page whose URL matches any of
-      the patterns.  These are the same match pattern strings that you use with
+      the patterns. These are the same match pattern strings that you use with
       the <a href="modules/sdk/page-mod.html"><code>page-mod</code></a>
       <code>include</code> property.
       <a href="modules/sdk/page-mod/match-pattern.html">Read more about patterns</a>.
     </td>
   </tr>
   <tr>
     <td>
       array
     </td>
     <td>
-      An array of any of the other types.  This context occurs when all contexts
+      An array of any of the other types. This context occurs when all contexts
       in the array occur.
     </td>
   </tr>
 </table>
 
 Menu items also have a `context` property that can be used to add and remove
-declarative contexts after construction.  For example:
+declarative contexts after construction. For example:
 
     var context = require("sdk/context-menu").SelectorContext("img");
     myMenuItem.context.add(context);
     myMenuItem.context.remove(context);
 
 When a menu item is bound to more than one context, it appears in the menu when
 all of those contexts occur.
 
 ### In Content Scripts
 
-The declarative contexts are handy but not very powerful.  For instance, you
+The declarative contexts are handy but not very powerful. For instance, you
 might want your menu item to appear for any page that has at least one image,
 but declarative contexts won't help you there.
 
-When you need more control control over the context in which your menu items are
-shown, you can use content scripts.  Like other APIs in the SDK, the
+When you need more control over the context in which your menu items are
+shown, you can use content scripts. Like other APIs in the SDK, the
 `context-menu` API uses
 [content scripts](dev-guide/guides/content-scripts/index.html) to let your
-add-on interact with pages in the browser.  Each menu item you create in the
+add-on interact with pages in the browser. Each menu item you create in the
 top-level context menu can have a content script.
 
 A special event named `"context"` is emitted in your content scripts whenever
-the context menu is about to be shown.  If you register a listener function for
+the context menu is about to be shown. If you register a listener function for
 this event and it returns true, the menu item associated with the listener's
 content script is shown in the menu.
 
 For example, this item appears whenever the context menu is invoked on a page
 that contains at least one image:
 
     require("sdk/context-menu").Item({
       label: "This Page Has Images",
       contentScript: 'self.on("context", function (node) {' +
                      '  return !!document.querySelector("img");' +
                      '});'
     });
 
-Note that the listener function has a parameter called `node`.  This is the node
-in the page that the user context-clicked to invoke the menu.  You can use it to
+Note that the listener function has a parameter called `node`. This is the node
+in the page that the user context-clicked to invoke the menu. You can use it to
 determine whether your item should be shown.
 
 You can both specify declarative contexts and listen for contexts in a content
-script.  In that case, the declarative contexts are evaluated first.  If they
-are not current, then your context listener is never called.
+script. In that case, the declarative contexts are evaluated first, and your
+item is shown only when all declarative contexts are current and your
+context listener returns true.
 
-This example takes advantage of that fact.  The listener can be assured that
-`node` will always be an image:
+If any declarative contexts are not current, then your context listener
+is never called. This example takes advantage of that fact. The listener
+can be assured that `node` will always be an image:
 
     var cm = require("sdk/context-menu");
     cm.Item({
       label: "A Mozilla Image",
       context: cm.SelectorContext("img"),
       contentScript: 'self.on("context", function (node) {' +
                      '  return /mozilla/.test(node.src);' +
                      '});'
     });
 
-Your item is shown only when all declarative contexts are current and your
-context listener returns true.
+However, if you do combine `SelectorContext` and the `"context"` event,
+be aware that the `node` argument passed to the `"context"` event will
+not always match the type specified in `SelectorContext`.
+
+`SelectorContext` will match if the menu is invoked on the node specified
+*or any descendant of that node*, but the `"context"` event handler is
+passed *the actual node* on which the menu was invoked. The example above
+works because `<IMG>` elements can't contain other elements, but in the
+example below, `node.nodeName` is not guaranteed to be "P" - for example,
+it won't be "P" if the user context-clicked a link inside a paragraph:
+
+    var cm = require("sdk/context-menu");
+    cm.Item({
+      label: "A Paragraph",
+      context: cm.SelectorContext("p"),
+      contentScript: 'self.on("context", function (node) {' +
+                     '  console.log(node.nodeName);' +
+                     '  return true;' +
+                     '});'
+    }); 
 
 The content script is executed for every page that a context menu is shown for.
 It will be executed the first time it is needed (i.e. when the context menu is
 first shown and all of the declarative contexts for your item are current) and
 then remains active until you destroy your context menu item or the page is
 unloaded.
 
 Handling Menu Item Clicks
 -------------------------
 
 In addition to using content scripts to listen for the `"context"` event as
-described above, you can use content scripts to handle item clicks.  When the
+described above, you can use content scripts to handle item clicks. When the
 user clicks your menu item, an event named `"click"` is emitted in the item's
 content script.
 
 Therefore, to handle an item click, listen for the `"click"` event in that
 item's content script like so:
 
     require("sdk/context-menu").Item({
       label: "My Item",
       contentScript: 'self.on("click", function (node, data) {' +
                      '  console.log("Item clicked!");' +
                      '});'
     });
 
-Note that the listener function has parameters called `node` and `data`.  `node`
-is the node that the user context-clicked to invoke the menu.  You can use it
-when performing some action.  `data` is the `data` property of the menu item
-that was clicked.  Note that when you have a hierarchy of menu items the click
+Note that the listener function has parameters called `node` and `data`.
+
+### The "node" Argument ###
+
+`node` is the node that the user context-clicked to invoke the menu.
+
+* If you did not use `SelectorContext` to decide whether to show the menu item,
+then this is the actual node clicked.
+* If you did use `SelectorContext`, then this is the node that matched your
+selector.
+
+For example, suppose your add-on looks like this:
+
+    var script = "self.on('click', function (node, data) {" +
+                 "  console.log('clicked: ' + node.nodeName);" +
+                 "});";
+
+    var cm = require("sdk/context-menu");
+
+    cm.Item({
+      label: "body context",
+      context: cm.SelectorContext("body"),
+      contentScript: script
+    });
+
+This add-on creates a context-menu item that uses `SelectorContext` to display
+the item whenever the context menu is activated on any descendant of the
+`<BODY>` element. When clicked, the item just logs the
+[`nodeName`](https://developer.mozilla.org/en-US/docs/DOM/Node.nodeName)
+property for the node passed to the click handler.
+
+If you run this add-on you'll see that it always logs "BODY", even if you
+click on a paragraph element inside the page:
+
+<pre>
+info: contextmenu-example: clicked: BODY
+</pre>
+
+By contrast, this add-on uses the `PageContext`:
+
+    var script = "self.on('click', function (node, data) {" +
+                 "  console.log('clicked: ' + node.nodeName);" +
+                 "});";
+
+    var cm = require("sdk/context-menu");
+
+    cm.Item({
+      label: "body context",
+      context: cm.PageContext(),
+      contentScript: script
+    });
+
+It will log the name of the actual node clicked:
+
+<pre>
+info: contextmenu-example: clicked: P
+</pre>
+
+### The "data" Argument ###
+
+`data` is the `data` property of the menu item
+that was clicked. Note that when you have a hierarchy of menu items the click
 event will be sent to the content script of the item clicked and all ancestors
 so be sure to verify that the `data` value passed matches the item you expect.
 You can use this to simplify click handling by providing just a single click
 listener on a `Menu` that reacts to clicks for any child items.:
 
     var cm = require("sdk/context-menu");
     cm.Menu({
       label: "My Menu",
@@ -243,21 +321,23 @@ listener on a `Menu` that reacts to clic
                      '});',
       items: [
         cm.Item({ label: "Item 1", data: "item1" }),
         cm.Item({ label: "Item 2", data: "item2" }),
         cm.Item({ label: "Item 3", data: "item3" })
       ]
     });
 
+### Communicating With the Add-on ###
+
 Often you will need to collect some kind of information in the click listener
-and perform an action unrelated to content.  To communicate to the menu item
+and perform an action unrelated to content. To communicate to the menu item
 associated with the content script, the content script can call the
 `postMessage` function attached to the global `self` object, passing it some
-JSON-able data.  The menu item's `"message"` event listener will be called with
+JSON-able data. The menu item's `"message"` event listener will be called with
 that data.
 
     var cm = require("sdk/context-menu");
     cm.Item({
       label: "Edit Image",
       context: cm.SelectorContext("img"),
       contentScript: 'self.on("click", function (node, data) {' +
                      '  self.postMessage(node.src);' +
@@ -269,37 +349,37 @@ that data.
 
 
 Updating a Menu Item's Label
 ----------------------------
 
 Each menu item must be created with a label, but you can change its label later
 using a couple of methods.
 
-The simplest method is to set the menu item's `label` property.  This example
+The simplest method is to set the menu item's `label` property. This example
 updates the item's label based on the number of times it's been clicked:
 
     var numClicks = 0;
     var myItem = require("sdk/context-menu").Item({
       label: "Click Me: " + numClicks,
       contentScript: 'self.on("click", self.postMessage);',
       onMessage: function () {
         numClicks++;
         this.label = "Click Me: " + numClicks;
         // Setting myItem.label is equivalent.
       }
     });
 
-Sometimes you might want to update the label based on the context.  For
+Sometimes you might want to update the label based on the context. For
 instance, if your item performs a search with the user's selected text, it would
-be nice to display the text in the item to provide feedback to the user.  In
-these cases you can use the second method.  Recall that your content scripts can
+be nice to display the text in the item to provide feedback to the user. In
+these cases you can use the second method. Recall that your content scripts can
 listen for the `"context"` event and if your listeners return true, the items
-associated with the content scripts are shown in the menu.  In addition to
-returning true, your `"context"` listeners can also return strings.  When a
+associated with the content scripts are shown in the menu. In addition to
+returning true, your `"context"` listeners can also return strings. When a
 `"context"` listener returns a string, it becomes the item's new label.
 
 This item implements the aforementioned search example:
 
     var cm = require("sdk/context-menu");
     cm.Item({
       label: "Search Google",
       context: cm.SelectionContext(),
@@ -307,28 +387,28 @@ This item implements the aforementioned 
                      '  var text = window.getSelection().toString();' +
                      '  if (text.length > 20)' +
                      '    text = text.substr(0, 20) + "...";' +
                      '  return "Search Google for " + text;' +
                      '});'
     });
 
 The `"context"` listener gets the window's current selection, truncating it if
-it's too long, and includes it in the returned string.  When the item is shown,
+it's too long, and includes it in the returned string. When the item is shown,
 its label will be "Search Google for `text`", where `text` is the truncated
 selection.
 
 
 More Examples
 -------------
 
 For conciseness, these examples create their content scripts as strings and use
-the `contentScript` property.  In your own add-on, you will probably want to
+the `contentScript` property. In your own add-on, you will probably want to
 create your content scripts in separate files and pass their URLs using the
-`contentScriptFile` property.  See
+`contentScriptFile` property. See
 [Working with Content Scripts](dev-guide/guides/content-scripts/index.html)
 for more information.
 
 <div class="warning">
 <p>Unless your content script is extremely simple and consists only of a
 static string, don't use <code>contentScript</code>: if you do, you may
 have problems getting your add-on approved on AMO.</p>
 <p>Instead, keep the script in a separate file and load it using
@@ -431,76 +511,76 @@ Google or Wikipedia with the text contai
 @class
 A labeled menu item that can perform an action when clicked.
 <api name="Item">
 @constructor
   Creates a labeled menu item that can perform an action when clicked.
 @param options {object}
   An object with the following keys:
   @prop label {string}
-    The item's label.  It must either be a string or an object that implements
+    The item's label. It must either be a string or an object that implements
     `toString()`.
   @prop [image] {string}
-    The item's icon, a string URL.  The URL can be remote, a reference to an
+    The item's icon, a string URL. The URL can be remote, a reference to an
     image in the add-on's `data` directory, or a data URI.
   @prop [data] {string}
-    An optional arbitrary value to associate with the item.  It must be either a
-    string or an object that implements `toString()`.  It will be passed to
+    An optional arbitrary value to associate with the item. It must be either a
+    string or an object that implements `toString()`. It will be passed to
     click listeners.
   @prop [context] {value}
     If the item is contained in the top-level context menu, this declaratively
     specifies the context under which the item will appear; see Specifying
     Contexts above.
   @prop [contentScript] {string,array}
     If the item is contained in the top-level context menu, this is the content
     script or an array of content scripts that the item can use to interact with
     the page.
   @prop [contentScriptFile] {string,array}
     If the item is contained in the top-level context menu, this is the local
     file URL of the content script or an array of such URLs that the item can
     use to interact with the page.
   @prop [onMessage] {function}
     If the item is contained in the top-level context menu, this function will
-    be called when the content script calls `self.postMessage`.  It will be
+    be called when the content script calls `self.postMessage`. It will be
     passed the data that was passed to `postMessage`.
 </api>
 
 <api name="label">
 @property {string}
-  The menu item's label.  You can set this after creating the item to update its
+  The menu item's label. You can set this after creating the item to update its
   label later.
 </api>
 
 <api name="image">
 @property {string}
-  The item's icon, a string URL.  The URL can be remote, a reference to an image
-  in the add-on's `data` directory, or a data URI.  You can set this after
-  creating the item to update its image later.  To remove the item's image, set
+  The item's icon, a string URL. The URL can be remote, a reference to an image
+  in the add-on's `data` directory, or a data URI. You can set this after
+  creating the item to update its image later. To remove the item's image, set
   it to `null`.
 </api>
 
 <api name="data">
 @property {string}
-  An optional arbitrary value to associate with the item.  It must be either a
-  string or an object that implements `toString()`.  It will be passed to
-  click listeners.  You can set this after creating the item to update its data
+  An optional arbitrary value to associate with the item. It must be either a
+  string or an object that implements `toString()`. It will be passed to
+  click listeners. You can set this after creating the item to update its data
   later.
 </api>
 
 <api name="context">
 @property {list}
   A list of declarative contexts for which the menu item will appear in the
-  context menu.  Contexts can be added by calling `context.add()` and removed by
+  context menu. Contexts can be added by calling `context.add()` and removed by
   called `context.remove()`.
 </api>
 
 <api name="parentMenu">
 @property {Menu}
   The item's parent `Menu`, or `null` if the item is contained in the top-level
-  context menu.  This property is read-only.  To add the item to a new menu,
+  context menu. This property is read-only. To add the item to a new menu,
   call that menu's `addItem()` method.
 </api>
 
 <api name="contentScript">
 @property {string,array}
   The content script or the array of content scripts associated with the menu
   item during creation.
 </api>
@@ -509,17 +589,17 @@ A labeled menu item that can perform an 
 @property {string,array}
   The URL of a content script or the array of such URLs associated with the menu
   item during creation.
 </api>
 
 <api name="destroy">
 @method
   Permanently removes the item from its parent menu and frees its resources.
-  The item must not be used afterward.  If you need to remove the item from its
+  The item must not be used afterward. If you need to remove the item from its
   parent menu but use it afterward, call `removeItem()` on the parent menu
   instead.
 </api>
 
 <api name="message">
 @event
 If you listen to this event you can receive message events from content
 scripts associated with this menu item. When a content script posts a
@@ -539,75 +619,75 @@ from the content script. The message can
 A labeled menu item that expands into a submenu.
 
 <api name="Menu">
 @constructor
   Creates a labeled menu item that expands into a submenu.
 @param options {object}
   An object with the following keys:
   @prop label {string}
-    The item's label.  It must either be a string or an object that implements
+    The item's label. It must either be a string or an object that implements
     `toString()`.
   @prop items {array}
-    An array of menu items that the menu will contain.  Each must be an `Item`,
+    An array of menu items that the menu will contain. Each must be an `Item`,
     `Menu`, or `Separator`.
   @prop [image] {string}
-    The menu's icon, a string URL.  The URL can be remote, a reference to an
+    The menu's icon, a string URL. The URL can be remote, a reference to an
     image in the add-on's `data` directory, or a data URI.
   @prop [context] {value}
     If the menu is contained in the top-level context menu, this declaratively
     specifies the context under which the menu will appear; see Specifying
     Contexts above.
   @prop [contentScript] {string,array}
     If the menu is contained in the top-level context menu, this is the content
     script or an array of content scripts that the menu can use to interact with
     the page.
   @prop [contentScriptFile] {string,array}
     If the menu is contained in the top-level context menu, this is the local
     file URL of the content script or an array of such URLs that the menu can
     use to interact with the page.
   @prop [onMessage] {function}
     If the menu is contained in the top-level context menu, this function will
-    be called when the content script calls `self.postMessage`.  It will be
+    be called when the content script calls `self.postMessage`. It will be
     passed the data that was passed to `postMessage`.
 </api>
 
 <api name="label">
 @property {string}
-  The menu's label.  You can set this after creating the menu to update its
+  The menu's label. You can set this after creating the menu to update its
   label later.
 </api>
 
 <api name="items">
 @property {array}
-  An array containing the items in the menu.  The array is read-only, meaning
-  that modifications to it will not affect the menu.  However, setting this
+  An array containing the items in the menu. The array is read-only, meaning
+  that modifications to it will not affect the menu. However, setting this
   property to a new array will replace all the items currently in the menu with
   the items in the new array.
 </api>
 
 <api name="image">
 @property {string}
-  The menu's icon, a string URL.  The URL can be remote, a reference to an image
-  in the add-on's `data` directory, or a data URI.  You can set this after
-  creating the menu to update its image later.  To remove the menu's image, set
+  The menu's icon, a string URL. The URL can be remote, a reference to an image
+  in the add-on's `data` directory, or a data URI. You can set this after
+  creating the menu to update its image later. To remove the menu's image, set
   it to `null`.
 </api>
 
 <api name="context">
 @property {list}
   A list of declarative contexts for which the menu will appear in the context
-  menu.  Contexts can be added by calling `context.add()` and removed by called
+  menu. Contexts can be added by calling `context.add()` and removed by called
   `context.remove()`.
 </api>
 
 <api name="parentMenu">
 @property {Menu}
   The menu's parent `Menu`, or `null` if the menu is contained in the top-level
-  context menu.  This property is read-only.  To add the menu to a new menu,
+  context menu. This property is read-only. To add the menu to a new menu,
   call that menu's `addItem()` method.
 </api>
 
 <api name="contentScript">
 @property {string,array}
   The content script or the array of content scripts associated with the menu
   during creation.
 </api>
@@ -615,36 +695,36 @@ A labeled menu item that expands into a 
 <api name="contentScriptFile">
 @property {string,array}
   The URL of a content script or the array of such URLs associated with the menu
   during creation.
 </api>
 
 <api name="addItem">
 @method
-  Appends a menu item to the end of the menu.  If the item is already contained
+  Appends a menu item to the end of the menu. If the item is already contained
   in another menu or in the top-level context menu, it's automatically removed
-  first.  If the item is already contained in this menu it will just be moved
+  first. If the item is already contained in this menu it will just be moved
   to the end of the menu.
 @param item {Item,Menu,Separator}
   The `Item`, `Menu`, or `Separator` to add to the menu.
 </api>
 
 <api name="removeItem">
 @method
-  Removes the given menu item from the menu.  If the menu does not contain the
+  Removes the given menu item from the menu. If the menu does not contain the
   item, this method does nothing.
 @param item {Item,Menu,Separator}
   The menu item to remove from the menu.
 </api>
 
 <api name="destroy">
 @method
   Permanently removes the menu from its parent menu and frees its resources.
-  The menu must not be used afterward.  If you need to remove the menu from its
+  The menu must not be used afterward. If you need to remove the menu from its
   parent menu but use it afterward, call `removeItem()` on the parent menu
   instead.
 </api>
 
 <api name="message">
 @event
 If you listen to this event you can receive message events from content
 scripts associated with this menu item. When a content script posts a
@@ -656,71 +736,71 @@ Listeners are passed a single argument w
 from the content script. The message can be any
 <a href = "dev-guide/guides/content-scripts/using-port.html#json_serializable">JSON-serializable value</a>.
 </api>
 
 </api>
 
 <api name="Separator">
 @class
-A menu separator.  Separators can be contained only in `Menu`s, not in the
+A menu separator. Separators can be contained only in `Menu`s, not in the
 top-level context menu.
 
 <api name="Separator">
 @constructor
   Creates a menu separator.
 </api>
 
 <api name="parentMenu">
 @property {Menu}
-  The separator's parent `Menu`.  This property is read-only.  To add the
+  The separator's parent `Menu`. This property is read-only. To add the
   separator to a new menu, call that menu's `addItem()` method.
 </api>
 
 <api name="destroy">
 @method
   Permanently removes the separator from its parent menu and frees its
-  resources.  The separator must not be used afterward.  If you need to remove
+  resources. The separator must not be used afterward. If you need to remove
   the separator from its parent menu but use it afterward, call `removeItem()`
   on the parent menu instead.
 </api>
 
 </api>
 
 <api name="PageContext">
 @class
 <api name="PageContext">
 @constructor
-  Creates a page context.  See Specifying Contexts above.
+  Creates a page context. See Specifying Contexts above.
 </api>
 </api>
 
 <api name="SelectionContext">
 @class
 <api name="SelectionContext">
 @constructor
-  Creates a context that occurs when a page contains a selection.  See
+  Creates a context that occurs when a page contains a selection. See
   Specifying Contexts above.
 </api>
 </api>
 
 <api name="SelectorContext">
 @class
 <api name="SelectorContext">
 @constructor
-  Creates a context that matches a given CSS selector.  See Specifying Contexts
+  Creates a context that matches a given CSS selector. See Specifying Contexts
   above.
 @param selector {string}
   A CSS selector.
 </api>
 </api>
 
 <api name="URLContext">
 @class
 <api name="URLContext">
 @constructor
-  Creates a context that matches pages with particular URLs.  See Specifying
+  Creates a context that matches pages with particular URLs. See Specifying
   Contexts above.
 @param matchPattern {string,array}
   A [match pattern](modules/sdk/page-mod/match-pattern.html) string, regexp or an
   array of match pattern strings or regexps.
 </api>
 </api>
--- a/addon-sdk/source/doc/module-source/sdk/core/promise.md
+++ b/addon-sdk/source/doc/module-source/sdk/core/promise.md
@@ -196,21 +196,21 @@ In terms of promises, this means chainin
 # Consuming promises
 
 In general, whole purpose of promises is to avoid a callback spaghetti in the
 code. As a matter of fact it would be great if we could convert any synchronous
 functions to asynchronous by making it aware of promises. Module exports
 `promised` function to do exactly that:
 
     const { promised } = require('sdk/core/promise');
-    function sum(x, y) { return x + y }
-    var sumAsync = promised(sum);
+    function sum(x, y) { return x + y };
+    var asyncSum = promised(sum);
 
     var c = sum(a, b);
-    var cAsync = asyncSum(aAsync(), bAsinc());
+    var cAsync = asyncSum(aAsync(), bAsync());
 
 `promised` takes normal function and composes new promise aware version of it
 that may take both normal values and promises as arguments and returns promise
 that will resolve to value that would have being returned by an original
 function if it was called with fulfillment values of given arguments.
 
 This technique is so powerful that it can replace most of the promise utility
 functions provided by other promise libraries. For example grouping promises
--- a/addon-sdk/source/doc/module-source/sdk/preferences/service.md
+++ b/addon-sdk/source/doc/module-source/sdk/preferences/service.md
@@ -2,17 +2,17 @@
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 
 <!-- contributed by Myk Melez [myk@mozilla.org]  -->
 <!-- contributed by Daniel Aquino [mr.danielaquino@gmail.com]  -->
 <!-- contributed by Atul Varma [atul@mozilla.com]  -->
 <!-- edited by Noelle Murata [fiveinchpixie@gmail.com]  -->
 
-The `preferences-service` module provides access to the
+The `preferences/service` module provides access to the
 application-wide preferences service singleton.
 
 
 <api name="set">
 @function
 Sets the application preference `name` to `value`.
 @param name {string} Preference name.
 @param value {string,number,boolean} Preference value.
--- a/addon-sdk/source/doc/module-source/sdk/private-browsing.md
+++ b/addon-sdk/source/doc/module-source/sdk/private-browsing.md
@@ -22,24 +22,33 @@ and <a href="modules/sdk/private-browsin
 events are all
 now deprecated due to per-window private browsing. They will continue to work
 until version 1.13 of the SDK. From version 1.13 onwards they will still exist
 but will have no effect when called.
 </div>
 
 <api name="isActive">
 @property {boolean}
-  This read-only boolean is true if global private browsing mode is turned on.
+  This read-only boolean is `true` if global private browsing mode is turned on.
 
   <div class="warning">
   This property is deprecated. It will continue to work until version 1.13 of the SDK.
-  From version 1.13 onwards it will always return false.
+  From version 1.13 onwards it will always return `false`.
   </div>
 </api>
 
+<api name="isPrivate">
+@function
+  Returns `true` if the argument is a private window or tab.
+@param [thing] {any}
+  The thing to check if it is private, only handles windows and tabs at the moment.
+  Everything else returns `false` automatically.
+  In global private browsing mode, this method returns the same value as `isActive`.
+</api>
+
 <api name="activate">
 @function
   Turns on global private browsing mode.
 
   <div class="warning">
   This function is deprecated. It will continue to work until version 1.13 of the SDK.
   From version 1.13 onwards it will still exist but will have no effect when called.
   </div>
--- a/addon-sdk/source/doc/static-files/base.html
+++ b/addon-sdk/source/doc/static-files/base.html
@@ -40,17 +40,16 @@
       <form action="dev-guide/search.html" id="cse-search-box">
         <div>
           <input type="hidden" name="cx" value="017013284162333743052:rvlazd1zehe" />
           <input type="hidden" name="cof" value="FORID:10" />
           <input type="hidden" name="ie" value="UTF-8" />
           <input type="text" name="q" size="31" id="search-box" />
         </div>
       </form>
-      <script type="text/javascript" src="https://www.google.com/cse/brand?form=cse-search-box&lang=en"></script>
     <!-- Google CSE Search Box Ends -->
     </div>
   </div>
 </div>
 
 <div id="site-header">
   <div class="funnel">
     <h1>
@@ -158,12 +157,14 @@
       Except where otherwise <a href="http://mozilla.com/about/legal.html#site">noted</a>, content on this site is licensed under the <br> <a href="http://creativecommons.org/licenses/by-sa/3.0/"> Creative Commons Attribution Share-Alike License v3.0 </a> or any later version.
       </p>
     </div>
   </div>
 </div>
 
 <script type="text/javascript" src="static-files/js/jquery.js"></script>
 <script type="text/javascript" src="static-files/js/main.js"></script>
+<!-- load the google JS last, in case we're offline ( bug 836955 ) -->
+<script type="text/javascript" src="https://www.google.com/cse/brand?form=cse-search-box&lang=en"></script>
 
 </body>
 
 </html>
--- a/addon-sdk/source/doc/static-files/css/sdk-docs.css
+++ b/addon-sdk/source/doc/static-files/css/sdk-docs.css
@@ -436,27 +436,27 @@ ul.tree ul li {
   background: url("../media/icons/node.png") no-repeat;
   font-weight: normal;
 }
 
 ul.tree ul li:last-child {
   background: #fff url("../media/icons/lastnode.png") no-repeat;
 }
 
-ul.module-list,
-ul#module-index {
+ul#module-index,
+h2[id="Module Compatibility"] ~ ul {
   border-color: #a0d0fb;
   border: solid 1px #a0d0fb;
   padding: 1em;
   list-style-type: none;
   -moz-column-count: 3;
   -moz-column-gap: 1em;
   -webkit-column-count: 3;
   -webkit-column-gap: 1em;
   column-count: 3;
   column-gap: 1em;
 }
 
-span.unsupported-on-mobile,
-ul.module-list li.unsupported-on-mobile,
-ul.module-list li.unsupported-on-mobile > a {
-  color: maroon;
+span.unsupported-on-mobile strong,
+h2[id="Module Compatibility"] ~ ul strong {
+  font-weight: normal;
+  text-decoration: line-through;
 }
--- a/addon-sdk/source/lib/sdk/addon/runner.js
+++ b/addon-sdk/source/lib/sdk/addon/runner.js
@@ -81,18 +81,17 @@ function startup(reason, options) {
   require('../l10n/loader').
     load(rootURI).
     then(null, function failure(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
-      if (data)
-        definePseudo(options.loader, '@l10n/data', data);
+      definePseudo(options.loader, '@l10n/data', data ? data : null);
       run(options);
     });
 }
 
 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.
--- a/addon-sdk/source/lib/sdk/core/promise.js
+++ b/addon-sdk/source/lib/sdk/core/promise.js
@@ -4,20 +4,21 @@
 /*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')) { // JSM
     this[factory.name] = {};
+    var Cu = this.Components["utils"];
+    this.console = Cu.import("resource://gre/modules/devtools/Console.jsm", {})
+                  .console;
     factory(function require(uri) {
-      var imports = {};
-      this['Components'].utils.import(uri, imports);
-      return imports;
+      return Cu.import(uri, {});
     }, this[factory.name], { uri: __URI__, id: id });
     this.EXPORTED_SYMBOLS = [factory.name];
   } else {  // Browser or alike
     var globals = this
     factory(function require(id) {
       return globals[id];
     }, (globals[id] = {}), { uri: document.location.href + '#' + id, id: id });
   }
@@ -48,17 +49,22 @@ function rejection(reason) {
 function attempt(f) {
   /**
   Returns wrapper function that delegates to `f`. If `f` throws then captures
   error and returns promise that rejects with a thrown error. Otherwise returns
   return value. (Internal utility)
   **/
   return function effort(options) {
     try { return f(options) }
-    catch(error) { return rejection(error) }
+    catch(error) {
+      if (exports._reportErrors && typeof(console) == 'object') {
+        console.error(error)
+      }
+      return rejection(error)
+    }
   }
 }
 
 function isPromise(value) {
   /**
   Returns true if given `value` is promise. Value is assumed to be promise if
   it implements `then` method.
   **/
--- a/addon-sdk/source/lib/sdk/deprecated/unit-test-finder.js
+++ b/addon-sdk/source/lib/sdk/deprecated/unit-test-finder.js
@@ -6,17 +6,18 @@
 
 module.metadata = {
   "stability": "deprecated"
 };
 
 const file = require("../io/file");
 const memory = require('./memory');
 const suites = require('@test/options').allTestModules;
-
+const { Loader } = require("sdk/test/loader");
+const cuddlefish = require("sdk/loader/cuddlefish");
 
 const NOT_TESTS = ['setup', 'teardown'];
 
 var TestFinder = exports.TestFinder = function TestFinder(options) {
   memory.track(this);
   this.filter = options.filter;
   this.testInProcess = options.testInProcess === false ? false : true;
   this.testOutOfProcess = options.testOutOfProcess === true ? true : false;
@@ -47,17 +48,21 @@ TestFinder.prototype = {
                ((testname && filterNameRegex) ? filterNameRegex.test(testname)
                                               : true);
       };
     } else
       filter = function() {return true};
 
     suites.forEach(
       function(suite) {
-        var module = require(suite);
+        // Load each test file as a main module in its own loader instance
+        // `suite` is defined by cuddlefish/manifest.py:ManifestBuilder.build
+        var loader = Loader(module);
+        var module = cuddlefish.main(loader, suite);
+
         if (self.testInProcess)
           for each (let name in Object.keys(module).sort()) {
             if(NOT_TESTS.indexOf(name) === -1 && filter(suite, name)) {
               tests.push({
                            setup: module.setup,
                            teardown: module.teardown,
                            testFunction: module[name],
                            name: suite + "." + name
--- a/addon-sdk/source/lib/sdk/indexed-db.js
+++ b/addon-sdk/source/lib/sdk/indexed-db.js
@@ -5,17 +5,17 @@
 "use strict";
 
 module.metadata = {
   "stability": "experimental"
 };
 
 const { Cc, Ci } = require("chrome");
 const { extend } = require("./core/heritage");
-const { id } = require("self");
+const { id } = require("./self");
 
 // placeholder, copied from bootstrap.js
 let sanitizeId = function(id){
   let uuidRe =
     /^\{([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\}$/;
 
   let domain = id.
     toLowerCase().
--- a/addon-sdk/source/lib/sdk/l10n/html.js
+++ b/addon-sdk/source/lib/sdk/l10n/html.js
@@ -3,21 +3,25 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 module.metadata = {
   "stability": "unstable"
 };
 
-const { Ci } = require("chrome");
+const { Ci, Cu } = require("chrome");
 const events = require("../system/events");
 const core = require("./core");
 
 const assetsURI = require('../self').data.url();
+const { Services } = Cu.import("resource://gre/modules/Services.jsm");
+
+const hideContentStyle = "data:text/css,:root {visibility: hidden !important;}";
+const hideSheetUri = Services.io.newURI(hideContentStyle, null, null);
 
 // Taken from Gaia:
 // https://github.com/andreasgal/gaia/blob/04fde2640a7f40314643016a5a6c98bf3755f5fd/webapi.js#L1470
 function translateElement(element) {
   element = element || document;
 
   // check all translatable children (= w/ a `data-l10n-id' attribute)
   var children = element.querySelectorAll('*[data-l10n-id]');
@@ -36,18 +40,25 @@ exports.translateElement = translateElem
 
 function onDocumentReady2Translate(event) {
   let document = event.target;
   document.removeEventListener("DOMContentLoaded", onDocumentReady2Translate,
                                false);
 
   translateElement(document);
 
-  // Finally display document when we finished replacing all text content
-  document.documentElement.style.visibility = "visible";
+  try {
+    // Finally display document when we finished replacing all text content
+    let winUtils = document.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
+                                       .getInterface(Ci.nsIDOMWindowUtils);
+    winUtils.removeSheet(hideSheetUri, winUtils.USER_SHEET);
+  }
+  catch(e) {
+    console.exception(e);
+  }
 }
 
 function onContentWindow(event) {
   let document = event.subject;
 
   // Accept only HTML documents
   if (!(document instanceof Ci.nsIDOMHTMLDocument))
     return;
@@ -56,22 +67,26 @@ function onContentWindow(event) {
   // have a null `location` attribute at this time
   if (!document.location)
     return;
 
   // Accept only document from this addon
   if (document.location.href.indexOf(assetsURI) !== 0)
     return;
 
-  // First hide content of the document in order to have content blinking
-  // between untranslated and translated states
-  // TODO: use result of bug 737003 discussion in order to avoid any conflict
-  // with document CSS
-  document.documentElement.style.visibility = "hidden";
-
+  try {
+    // First hide content of the document in order to have content blinking
+    // between untranslated and translated states
+    let winUtils = document.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
+                                       .getInterface(Ci.nsIDOMWindowUtils);
+    winUtils.loadSheet(hideSheetUri, winUtils.USER_SHEET);
+  }
+  catch(e) {
+    console.exception(e);
+  }
   // Wait for DOM tree to be built before applying localization
   document.addEventListener("DOMContentLoaded", onDocumentReady2Translate,
                             false);
 }
 
 // Listen to creation of content documents in order to translate them as soon
 // as possible in their loading process
 const ON_CONTENT = "document-element-inserted";
@@ -87,9 +102,9 @@ exports.enable = enable;
 function disable() {
   if (enabled) {
     events.off(ON_CONTENT, onContentWindow);
     enabled = false;
   }
 }
 exports.disable = disable;
 
-require("api-utils/unload").when(disable);
+require("sdk/system/unload").when(disable);
--- a/addon-sdk/source/lib/sdk/loader/cuddlefish.js
+++ b/addon-sdk/source/lib/sdk/loader/cuddlefish.js
@@ -83,40 +83,39 @@ function incompatibility(module) {
 function CuddlefishLoader(options) {
   let { manifest } = options;
 
   options = override(options, {
     // Put `api-utils/loader` and `api-utils/cuddlefish` loaded as JSM to module
     // cache to avoid subsequent loads via `require`.
     modules: override({
       'toolkit/loader': loaderModule,
-      'addon-sdk/sdk/loader/cuddlefish': exports,
-      'addon-sdk/sdk/system/xul-app': xulappModule
+      'sdk/loader/cuddlefish': exports,
+      'sdk/system/xul-app': xulappModule
     }, options.modules),
     resolve: function resolve(id, requirer) {
       let entry = requirer && requirer in manifest && manifest[requirer];
       let uri = null;
 
       // If manifest entry for this requirement is present we follow manifest.
       // Note: Standard library modules like 'panel' will be present in
       // manifest unless they were moved to platform.
       if (entry) {
         let requirement = entry.requirements[id];
         // If requirer entry is in manifest and it's requirement is not, than
         // it has no authority to load since linker was not able to find it.
         if (!requirement)
-          throw Error('Module: ' + requirer.id + ' located at ' + requirer.uri
-                      + ' has no authority to load: ' + id, requirer.uri);
+          throw Error('Module: ' + requirer + ' has no authority to load: '
+                      + id, requirer);
 
         uri = requirement;
-      }
-      // If requirer is off manifest than it's a system module and we allow it
-      // to go off manifest.
-      else {
-        uri = id;
+      } else {
+        // If requirer is off manifest than it's a system module and we allow it
+        // to go off manifest by resolving a relative path.
+        uri = loaderModule.resolve(id, requirer);
       }
       return uri;
     },
     load: function(loader, module) {
       let result;
       let error;
 
       // In order to get the module's metadata, we need to load the module.
--- a/addon-sdk/source/lib/sdk/net/xhr.js
+++ b/addon-sdk/source/lib/sdk/net/xhr.js
@@ -109,16 +109,20 @@ XMLHttpRequest.prototype = {
     throw new Error("not implemented");
   },
   removeEventListener: function removeEventListener() {
     throw new Error("not implemented");
   },
   set upload(newValue) {
     throw new Error("not implemented");
   },
+  forceAllowThirdPartyCookie: function forceAllowThirdPartyCookie() {
+    if (this._req.channel instanceof Ci.nsIHttpChannelInternal)
+      this._req.channel.forceAllowThirdPartyCookie = true;
+  },
   get onreadystatechange() {
     return this._orsc;
   },
   set onreadystatechange(cb) {
     this._orsc = cb;
     if (cb) {
       var self = this;
       this._req.onreadystatechange = function() {
--- a/addon-sdk/source/lib/sdk/private-browsing.js
+++ b/addon-sdk/source/lib/sdk/private-browsing.js
@@ -2,20 +2,21 @@
  * 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"
 };
 
-const { setMode, getMode, on: onStateChange } = require('./private-browsing/utils');
+const { setMode, getMode, on: onStateChange, isWindowPrivate } = require('./private-browsing/utils');
 const { emit, on, once, off } = require('./event/core');
 const { when: unload } = require('./system/unload');
 const { deprecateUsage, deprecateFunction, deprecateEvent } = require('./util/deprecate');
+const { getOwnerWindow } = require('./private-browsing/window/utils');
 
 onStateChange('start', function onStart() {
   emit(exports, 'start');
 });
 
 onStateChange('stop', function onStop() {
   emit(exports, 'stop');
 });
@@ -31,16 +32,29 @@ exports.on = deprecateEvents(on.bind(nul
 exports.once = deprecateEvents(once.bind(null, exports));
 exports.removeListener = deprecateEvents(function removeListener(type, listener) {
   // Note: We can't just bind `off` as we do it for other methods cause skipping
   // a listener argument will remove all listeners for the given event type
   // causing misbehavior. This way we make sure all arguments are passed.
   off(exports, type, listener);
 });
 
+exports.isPrivate = function(thing) {
+  if (!!thing) {
+    if (isWindowPrivate(thing)) {
+      return true;
+    }
+
+    let window = getOwnerWindow(thing);
+    if (window)
+      return isWindowPrivate(window);
+  }
+  return getMode();
+};
+
 function deprecateEvents(func) deprecateEvent(
   func,
    'The require("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
@@ -10,23 +10,24 @@ module.metadata = {
 const { Cc, Ci, Cu } = require('chrome');
 const { defer } = require('../lang/functional');
 const { emit, on, once, off } = require('../event/core');
 const { when: unload } = require('../system/unload');
 const { getWindowLoadingContext, windows } = require('../window/utils');
 const { WindowTracker } = require("../deprecated/window-utils");
 const events = require('../system/events');
 const { deprecateFunction } = require('../util/deprecate');
+const { isOneOf, is, satisfiesVersion, version } = require('../system/xul-app');
 
 let deferredEmit = defer(emit);
 let pbService;
 let PrivateBrowsingUtils;
 
 // Private browsing is only supported in Fx
-if (require("../system/xul-app").is("Firefox")) {
+if (isOneOf(['Firefox', 'Fennec'])) {
   // get the nsIPrivateBrowsingService if it exists
   try {
     pbService = Cc["@mozilla.org/privatebrowsing;1"].
                 getService(Ci.nsIPrivateBrowsingService);
 
     // a dummy service exists for the moment (Fx20 atleast), but will be removed eventually
     // ie: the service will exist, but it won't do anything and the global private browing
     //     feature is not really active.  See Bug 818800 and Bug 826037
@@ -36,30 +37,46 @@ if (require("../system/xul-app").is("Fir
 
   try {
     PrivateBrowsingUtils = Cu.import('resource://gre/modules/PrivateBrowsingUtils.jsm', {}).PrivateBrowsingUtils;
   }
   catch(e) { /* if this file DNE then an error will be thrown */ }
 }
 
 function isWindowPrivate(win) {
+  if (!PrivateBrowsingUtils || !win)
+    return false;
+
   // if the pbService is undefined, the PrivateBrowsingUtils.jsm is available,
   // and the app is Firefox, then assume per-window private browsing is
   // enabled.
-  return win instanceof Ci.nsIDOMWindow &&
-         isWindowPBSupported &&
-         PrivateBrowsingUtils.isWindowPrivate(win);
+  if (win instanceof Ci.nsIDOMWindow) {
+    return PrivateBrowsingUtils.isWindowPrivate(win);
+  }
+
+  // Sometimes the input is not a nsIDOMWindow.. but it is still a winodw.
+  try {
+    return !!win.docShell.QueryInterface(Ci.nsILoadContext).usePrivateBrowsing;
+  }
+  catch (e) {}
+
+  return false;
 }
 exports.isWindowPrivate = isWindowPrivate;
 
 // checks that global private browsing is implemented
-let isGlobalPBSupported = exports.isGlobalPBSupported =  !!pbService;
+let isGlobalPBSupported = exports.isGlobalPBSupported = !!pbService && is('Firefox');
 
 // checks that per-window private browsing is implemented
-let isWindowPBSupported = exports.isWindowPBSupported = !isGlobalPBSupported && !!PrivateBrowsingUtils;
+let isWindowPBSupported = exports.isWindowPBSupported =
+                          !pbService && !!PrivateBrowsingUtils && is('Firefox');
+
+// checks that per-tab private browsing is implemented
+let isTabPBSupported = exports.isTabPBSupported =
+                       !pbService && !!PrivateBrowsingUtils && is('Fennec') && satisfiesVersion(version, '>=20.0*');
 
 function onChange() {
   // Emit event with in next turn of event loop.
   deferredEmit(exports, pbService.privateBrowsingEnabled ? 'start' : 'stop');
 }
 
 // Currently, only Firefox implements the private browsing service.
 if (isGlobalPBSupported) {
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/lib/sdk/private-browsing/window/utils.js
@@ -0,0 +1,32 @@
+/* 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'
+};
+
+const privateNS = require('../../core/namespace').ns();
+
+function getOwnerWindow(thing) {
+  try {
+    // check for and return associated window
+    let fn = (privateNS(thing.prototype) || privateNS(thing) || {}).getOwnerWindow;
+    if (fn)
+      return fn.apply(fn, [thing].concat(arguments));
+  }
+  // stuff like numbers and strings throw errors with namespaces
+  catch(e) {}
+  // default
+  return undefined;
+}
+getOwnerWindow.define = function(Type, fn) {
+  privateNS(Type.prototype).getOwnerWindow = fn;
+}
+
+getOwnerWindow.implement = function(instance, fn) {
+  privateNS(instance).getOwnerWindow = fn;
+}
+
+exports.getOwnerWindow = getOwnerWindow;
--- a/addon-sdk/source/lib/sdk/request.js
+++ b/addon-sdk/source/lib/sdk/request.js
@@ -66,16 +66,18 @@ function runRequest(mode, target) {
   let data = stringify(content);
   // If the URL already has ? in it, then we want to just use &
   if (mode == "GET" && data)
     url = url + (/\?/.test(url) ? "&" : "?") + data;
 
   // open the request
   xhr.open(mode, url);
 
+  xhr.forceAllowThirdPartyCookie();
+
   // request header must be set after open, but before send
   xhr.setRequestHeader("Content-Type", contentType);
 
   // set other headers
   Object.keys(headers).forEach(function(name) {
     xhr.setRequestHeader(name, headers[name]);
   });
 
--- a/addon-sdk/source/lib/sdk/selection.js
+++ b/addon-sdk/source/lib/sdk/selection.js
@@ -15,17 +15,17 @@ const { Ci, Cc } = require("chrome"),
     { setTimeout } = require("./timers"),
     { emit, off } = require("./event/core"),
     { Class, obscure } = require("./core/heritage"),
     { EventTarget } = require("./event/target"),
     { ns } = require("./core/namespace"),
     { when: unload } = require("./system/unload"),
     { getTabs, getTabContentWindow, getTabForContentWindow,
       getAllTabContentWindows } = require('./tabs/utils'),
-    { getInnerId, getMostRecentBrowserWindow,
+    { getMostRecentBrowserWindow,
       windows, getFocusedWindow, getFocusedElement } = require("./window/utils"),
     events = require("./system/events");
 
 // The selection types
 const HTML = 0x01,
       TEXT = 0x02,
       DOM  = 0x03; // internal use only
 
@@ -316,27 +316,16 @@ function addSelectionListener(window) {
   // a selection is in a text field, therefore we need to add a listener to
   // window.onselect, that is fired only for text fields.
   // For consistency, we add it only when the nsISelectionListener is added.
   //
   // https://developer.mozilla.org/en/DOM/window.onselect
   window.addEventListener("select", selectionListener.onSelect, true);
 
   selections(window).selection = selection;
-
-  let innerId = getInnerId(window);
-
-  events.on("inner-window-destroyed", function destroyed (event) {
-    let destroyedId = event.subject.QueryInterface(Ci.nsISupportsPRUint64).data;
-
-    if (destroyedId === innerId) {
-      removeSelectionListener(window);
-      events.off("inner-window-destroyed", destroyed);
-    }
-  });
 };
 
 /**
  * Removes the Selection Listener to the content's window given
  */
 function removeSelectionListener(window) {
   // Don't remove the selection's listener to a window that wasn't handled.
   if (!("selection" in selections(window)))
@@ -377,17 +366,17 @@ getAllTabContentWindows().forEach(addSel
 // a new selection object is created when it becomes visible again.
 // That makes the previous selection's listeners added previously totally
 // useless – the listeners are not notified anymore.
 // To fix that we're listening for `document-shown` event in order to add
 // the listeners to the new selection object created.
 //
 // See bug 665386 for further details.
 
-events.on("document-shown", function (event) {
+function onShown(event) {
   let window = event.subject.defaultView;
 
   // We are not interested in documents without valid defaultView.
   // For example XML documents don't have windows and we don't yet support them.
   if (!window)
     return;
 
   // We want to handle only the windows where we added selection's listeners
@@ -403,27 +392,31 @@ events.on("document-shown", function (ev
     // because is detached. An attempt to remove the listener, will raise an
     // error (see http://mxr.mozilla.org/mozilla-central/source/layout/generic/nsSelection.cpp#5343 )
     //
     // We ensure that the current selection is an instance of
     // `nsISelectionPrivate` before working on it, in case is `null`.
     if (currentSelection instanceof Ci.nsISelectionPrivate &&
       currentSelection !== selection) {
 
+      window.addEventListener("select", selectionListener.onSelect, true);
       currentSelection.addSelectionListener(selectionListener);
       selections(window).selection = currentSelection;
     }
   }
-});
+}
+
+events.on("document-shown", onShown, true);
 
 // Removes Selection listeners when the add-on is unloaded
 unload(function(){
   getAllTabContentWindows().forEach(removeSelectionListener);
 
   events.off("document-element-inserted", onContent);
+  events.off("document-shown", onShown);
 
   off(exports);
 });
 
 const selection = Class({
   extends: EventTarget,
   implements: [ Selection, selectionIterator ]
 })();
--- a/addon-sdk/source/lib/sdk/system/xul-app.js
+++ b/addon-sdk/source/lib/sdk/system/xul-app.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";
 
 module.metadata = {
   "stability": "experimental"
 };
 
 var { Cc, Ci } = require("chrome");
 
@@ -98,17 +97,16 @@ function normalizeRange(range) {
  *
  * @param {String} comparison
  *  The comparison operator
  *
  * @param {String} compareVersion
  *  A version to compare
  */
 function compareVersion(version, comparison, compareVersion) {
-
   let hasWildcard = compareVersion.indexOf("*") !== -1;
 
   comparison = comparison || "=";
 
   if (hasWildcard) {
     switch (comparison) {
       case "=":
         let zeroVersion = compareVersion.replace(reSubInfinity, ".0");
@@ -176,10 +174,9 @@ function satisfiesVersion(version, versi
 
     let [, lowMod, lowVer, highMod, highVer] = matches;
 
     return compareVersion(version, lowMod, lowVer) && (highVer !== undefined
       ? compareVersion(version, highMod, highVer)
       : true);
   });
 }
-
 exports.satisfiesVersion = satisfiesVersion;
--- a/addon-sdk/source/lib/sdk/tabs/tab-fennec.js
+++ b/addon-sdk/source/lib/sdk/tabs/tab-fennec.js
@@ -2,19 +2,20 @@
  * 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 } = require('chrome');
 const { Class } = require('../core/heritage');
 const { tabNS } = require('./namespace');
 const { EventTarget } = require('../event/target');
-const { activateTab, getTabTitle, setTabTitle, closeTab, getTabURL,
+const { activateTab, getTabTitle, setTabTitle, closeTab, getTabURL, getContentWindowForTab,
         setTabURL, getOwnerWindow, getTabContentType, getTabId } = require('./utils');
 const { emit } = require('../event/core');
+const { getOwnerWindow: getPBOwnerWindow } = require('../private-browsing/window/utils');
 const { when: unload } = require('../system/unload');
 
 const { EVENTS } = require('./events');
 const ERR_FENNEC_MSG = 'This method is not yet supported by Fennec';
 
 const Tab = Class({
   extends: EventTarget,
   initialize: function initialize(options) {
@@ -138,8 +139,12 @@ const Tab = Class({
   /**
    * Reload the tab
    */
   reload: function reload() {
     tabNS(this).tab.browser.reload();
   }
 });
 exports.Tab = Tab;
+
+getPBOwnerWindow.define(Tab, function(tab) {
+  return getContentWindowForTab(tabNS(tab).tab);
+});
--- a/addon-sdk/source/lib/sdk/tabs/tab-firefox.js
+++ b/addon-sdk/source/lib/sdk/tabs/tab-firefox.js
@@ -6,16 +6,18 @@
 const { Trait } = require("../deprecated/traits");
 const { EventEmitter } = require("../deprecated/events");
 const { defer } = require("../lang/functional");
 const { EVENTS } = require("./events");
 const { getThumbnailURIForWindow } = require("../content/thumbnail");
 const { getFaviconURIForLocation } = require("../io/data");
 const { activateTab, getOwnerWindow, getBrowserForTab, getTabTitle, setTabTitle,
         getTabURL, setTabURL, getTabContentType, getTabId } = require('./utils');
+const { getOwnerWindow: getPBOwnerWindow } = require('../private-browsing/window/utils');
+const viewNS = require('sdk/core/namespace').ns();
 
 // Array of the inner instances of all the wrapped tabs.
 const TABS = [];
 
 /**
  * Trait used to create tab wrappers.
  */
 const TabTrait = Trait.compose(EventEmitter, {
@@ -45,16 +47,19 @@ const TabTrait = Trait.compose(EventEmit
     }
 
     this.on(EVENTS.close.name, this.destroy.bind(this));
     this._browser.addEventListener(EVENTS.ready.dom, this._onReady, true);
 
     if (options.isPinned)
       this.pin();
 
+    viewNS(this._public).tab = this._tab;
+    getPBOwnerWindow.implement(this._public, getChromeTab);
+
     // Since we will have to identify tabs by a DOM elements facade function
     // is used as constructor that collects all the instances and makes sure
     // that they more then one wrapper is not created per tab.
     return this;
   },
   destroy: function destroy() {
     this._removeAllListeners();
     if (this._tab) {
@@ -207,16 +212,20 @@ const TabTrait = Trait.compose(EventEmit
    */
   reload: function reload() {
     if (!this._tab)
       return;
     this._window.gBrowser.reloadTab(this._tab);
   }
 });
 
+function getChromeTab(tab) {
+  return getOwnerWindow(viewNS(tab).tab);
+}
+
 function Tab(options) {
   let chromeTab = options.tab;
   for each (let tab in TABS) {
     if (chromeTab == tab._tab)
       return tab._public;
   }
   let tab = TabTrait(options);
   TABS.push(tab);
--- a/addon-sdk/source/lib/sdk/tabs/utils.js
+++ b/addon-sdk/source/lib/sdk/tabs/utils.js
@@ -75,17 +75,18 @@ function getOwnerWindow(tab) {
   // try fennec case
   return getWindowHoldingTab(tab);
 }
 exports.getOwnerWindow = getOwnerWindow;
 
 // fennec
 function getWindowHoldingTab(rawTab) {
   for each (let window in windows()) {
-    // this function may be called when not using fennec
+    // this function may be called when not using fennec,
+    // but BrowserApp is only defined on Fennec
     if (!window.BrowserApp)
       continue;
 
     for each (let tab in window.BrowserApp.tabs) {
       if (tab === rawTab)
         return window;
     }
   }
@@ -95,17 +96,18 @@ function getWindowHoldingTab(rawTab) {
 
 function openTab(window, url, options) {
   options = options || {};
 
   // fennec?
   if (window.BrowserApp) {
     return window.BrowserApp.addTab(url, {
       selected: options.inBackground ? false : true,
-      pinned: options.isPinned || false
+      pinned: options.isPinned || false,
+      isPrivate: options.private || false
     });
   }
   return window.gBrowser.addTab(url);
 };
 exports.openTab = openTab;
 
 function isTabOpen(tab) {
   // try normal case then fennec case
@@ -145,25 +147,30 @@ exports.getTabBrowserForTab = getTabBrow
 function getBrowserForTab(tab) {
   if (tab.browser) // fennec
     return tab.browser;
 
   return tab.linkedBrowser;
 }
 exports.getBrowserForTab = getBrowserForTab;
 
+
+function getContentWindowForTab(tab) {
+  return getBrowserForTab(tab).contentWindow;
+}
+exports.getContentWindowForTab = getContentWindowForTab;
+
 function getTabId(tab) {
   if (tab.browser) // fennec
     return tab.id
 
   return String.split(tab.linkedPanel, 'panel').pop();
 }
 exports.getTabId = getTabId;
 
-
 function getTabTitle(tab) {
   return getBrowserForTab(tab).contentDocument.title || tab.label || "";
 }
 exports.getTabTitle = getTabTitle;
 
 function setTabTitle(tab, title) {
   title = String(title);
   if (tab.browser)
--- a/addon-sdk/source/lib/sdk/test.js
+++ b/addon-sdk/source/lib/sdk/test.js
@@ -4,18 +4,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 module.metadata = {
   "stability": "unstable"
 };
 
-const BaseAssert = require("./test/assert").Assert;
-const { isFunction, isObject } = require("./lang/type");
+const BaseAssert = require("sdk/test/assert").Assert;
+const { isFunction, isObject } = require("sdk/lang/type");
 
 function extend(target) {
   let descriptor = {}
   Array.slice(arguments, 1).forEach(function(source) {
     Object.getOwnPropertyNames(source).forEach(function onEach(name) {
       descriptor[name] = Object.getOwnPropertyDescriptor(source, name);
     });
   });
--- a/addon-sdk/source/lib/sdk/test/harness.js
+++ b/addon-sdk/source/lib/sdk/test/harness.js
@@ -15,17 +15,17 @@ const { setTimeout } = require('../timer
 const memory = require('../deprecated/memory');
 const { PlainTextConsole } = require("../console/plain-text");
 const { when: unload } = require("../system/unload");
 const { format, fromException }  = require("../console/traceback");
 const system = require("../system");
 
 // Trick manifest builder to make it think we need these modules ?
 const unit = require("../deprecated/unit-test");
-const test = require("../test");
+const test = require("../../test");
 const url = require("../url");
 
 var cService = Cc['@mozilla.org/consoleservice;1'].getService()
                .QueryInterface(Ci.nsIConsoleService);
 
 // The console used to log messages
 var testConsole;
 
--- a/addon-sdk/source/lib/sdk/test/runner.js
+++ b/addon-sdk/source/lib/sdk/test/runner.js
@@ -100,17 +100,17 @@ exports.runTestsFromModule = function ru
   // Make a copy of exports as it may already be frozen by module loader
   let exports = {};
   Object.keys(module.exports).forEach(function(key) {
     exports[key] = module.exports[key];
   });
 
   runTests(function findAndRunTests(loader, nextIteration) {
     // Consider that all these tests are CommonJS ones
-    loader.require('../test').run(exports);
+    loader.require('../../test').run(exports);
 
     // Reproduce what is done in unit-test-finder.findTests()
     let tests = [];
     for each (let name in Object.keys(exports).sort()) {
       tests.push({
         setup: exports.setup,
         teardown: exports.teardown,
         testFunction: exports[name],
--- a/addon-sdk/source/lib/sdk/window/browser.js
+++ b/addon-sdk/source/lib/sdk/window/browser.js
@@ -6,16 +6,17 @@
 const { Class } = require('../core/heritage');
 const { windowNS } = require('./namespace');
 const { on, off, once } = require('../event/core');
 const { method } = require('../lang/functional');
 const { getWindowTitle } = require('./utils');
 const unload = require('../system/unload');
 const { getMode } = require('../private-browsing/utils');
 const { EventTarget } = require('../event/target');
+const { getOwnerWindow: getPBOwnerWindow } = require('../private-browsing/window/utils');
 
 const ERR_FENNEC_MSG = 'This method is not yet supported by Fennec, consider using require("tabs") instead';
 
 const BrowserWindow = Class({
   initialize: function initialize(options) {
     EventTarget.prototype.initialize.call(this, options);
     windowNS(this).window = options.window;
   },
@@ -34,8 +35,12 @@ const BrowserWindow = Class({
   get tabs() require('../tabs'),
   get activeTab() require('../tabs').activeTab,
   on: method(on),
   removeListener: method(off),
   once: method(once),
   get isPrivateBrowsing() getMode(windowNS(this).window),
 });
 exports.BrowserWindow = BrowserWindow;
+
+getPBOwnerWindow.define(BrowserWindow, function(window) {
+  return windowNS(window).window;
+});
--- a/addon-sdk/source/lib/sdk/windows/firefox.js
+++ b/addon-sdk/source/lib/sdk/windows/firefox.js
@@ -12,17 +12,19 @@ const { Cc, Ci, Cr } = require('chrome')
       { WindowLoader } = require('./loader'),
       { isBrowser, getWindowDocShell } = require('../window/utils'),
       { Options } = require('../tabs/common'),
       apiUtils = require('../deprecated/api-utils'),
       unload = require('../system/unload'),
       windowUtils = require('../deprecated/window-utils'),
       { WindowTrackerTrait } = windowUtils,
       { ns } = require('../core/namespace'),
-      { observer: windowObserver } = require('./observer');
+      { observer: windowObserver } = require('./observer'),
+      { getOwnerWindow } = require('../private-browsing/window/utils'),
+      viewNS = require('sdk/core/namespace').ns();
 
 /**
  * Window trait composes safe wrappers for browser window that are E10S
  * compatible.
  */
 const BrowserWindowTrait = Trait.compose(
   EventEmitter,
   WindowDom.resolve({ close: '_close' }),
@@ -64,16 +66,20 @@ const BrowserWindowTrait = Trait.compose
       }
       else if ('url' in options) {
         this._tabOptions = [ Options(options.url) ];
       }
 
       this._private = !!options.private;
 
       this._load();
+
+      viewNS(this._public).window = this._window;
+      getOwnerWindow.implement(this._public, getChromeWindow);
+
       return this;
     },
     destroy: function () this._onUnload(),
     _tabOptions: [],
     _onLoad: function() {
       try {
         this._initWindowTabTracker();
       }
@@ -234,9 +240,13 @@ const browserWindows = Trait.resolve({ t
       // Bug 724404: do not leak this module and linked windows:
       // We have to do it on untrack and not only when `_onUnload` is called
       // when windows are closed, otherwise, we will leak on addon disabling.
       window.destroy();
     }
   }).resolve({ toString: null })
 )();
 
+function getChromeWindow(window) {
+  return viewNS(window).window;
+}
+
 exports.browserWindows = browserWindows;
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/lib/test.js
@@ -0,0 +1,112 @@
+/* vim:ts=2:sts=2:sw=2:
+ * 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"
+};
+
+const BaseAssert = require("sdk/test/assert").Assert;
+const { isFunction, isObject } = require("sdk/lang/type");
+
+function extend(target) {
+  let descriptor = {}
+  Array.slice(arguments, 1).forEach(function(source) {
+    Object.getOwnPropertyNames(source).forEach(function onEach(name) {
+      descriptor[name] = Object.getOwnPropertyDescriptor(source, name);
+    });
+  });
+  return Object.create(target, descriptor);
+}
+
+/**
+ * Function takes test `suite` object in CommonJS format and defines all of the
+ * tests from that suite and nested suites in a jetpack format on a given
+ * `target` object. Optionally third argument `prefix` can be passed to prefix
+ * all the test names.
+ */
+function defineTestSuite(target, suite, prefix) {
+  prefix = prefix || "";
+  // If suite defines `Assert` that's what `assert` object have to be created
+  // from and passed to a test function (This allows custom assertion functions)
+  // See for details: http://wiki.commonjs.org/wiki/Unit_Testing/1.1
+  let Assert = suite.Assert || BaseAssert;
+  // Going through each item in the test suite and wrapping it into a
+  // Jetpack test format.
+  Object.keys(suite).forEach(function(key) {
+     // If name starts with test then it's a test function or suite.
+    if (key.indexOf("test") === 0) {
+      let test = suite[key];
+
+      // For each test function so we create a wrapper test function in a
+      // jetpack format and copy that to a `target` exports.
+      if (isFunction(test)) {
+
+        // Since names of the test may match across suites we use full object
+        // path as a name to avoid overriding same function.
+        target[prefix + key] = function(options) {
+
+          // Creating `assert` functions for this test.
+          let assert = Assert(options);
+
+          // If CommonJS test function expects more than one argument
+          // it means that test is async and second argument is a callback
+          // to notify that test is finished.
+          if (1 < test.length) {
+
+            // Letting test runner know that test is executed async and
+            // creating a callback function that CommonJS tests will call
+            // once it's done.
+            options.waitUntilDone();
+            test(assert, function() {
+              options.done();
+            });
+          }
+
+          // Otherwise CommonJS test is synchronous so we call it only with
+          // one argument.
+          else {
+            test(assert);
+          }
+        }
+      }
+
+      // If it's an object then it's a test suite containing test function
+      // and / or nested test suites. In that case we just extend prefix used
+      // and call this function to copy and wrap tests from nested suite.
+      else if (isObject(test)) {
+        // We need to clone `tests` instead of modifying it, since it's very
+        // likely that it is frozen (usually test suites imported modules).
+        test = extend(Object.prototype, test, {
+          Assert: test.Assert || Assert
+        });
+        defineTestSuite(target, test, prefix + key + ".");
+      }
+    }
+  });
+}
+
+/**
+ * This function is a CommonJS test runner function, but since Jetpack test
+ * runner and test format is different from CommonJS this function shims given
+ * `exports` with all its tests into a Jetpack test format so that the built-in
+ * test runner will be able to run CommonJS test without manual changes.
+ */
+exports.run = function run(exports) {
+
+  // We can't leave old properties on exports since those are test in a CommonJS
+  // format that why we move everything to a new `suite` object.
+  let suite = {};
+  Object.keys(exports).forEach(function(key) {
+    suite[key] = exports[key];
+    delete exports[key];
+  });
+
+  // Now we wrap all the CommonJS tests to a Jetpack format and define
+  // those to a given `exports` object since that where jetpack test runner
+  // will look for them.
+  defineTestSuite(exports, suite);
+};
--- a/addon-sdk/source/lib/toolkit/loader.js
+++ b/addon-sdk/source/lib/toolkit/loader.js
@@ -201,17 +201,16 @@ exports.Sandbox = Sandbox;
 const evaluate = iced(function evaluate(sandbox, uri, options) {
   let { source, line, version, encoding } = override({
     encoding: 'UTF-8',
     line: 1,
     version: '1.8',
     source: null
   }, options);
 
-
   return source ? Cu.evalInSandbox(source, sandbox, version, uri, line)
                 : loadSubScript(uri, sandbox, encoding);
 });
 exports.evaluate = evaluate;
 
 // Populates `exports` of the given CommonJS `module` object, in the context
 // of the given `loader` by evaluating code associated with it.
 const load = iced(function load(loader, module) {
@@ -276,16 +275,17 @@ exports.load = load;
 // Utility function to check if id is relative.
 function isRelative(id) { return id[0] === '.'; }
 // Utility function to normalize module `uri`s so they have `.js` extension.
 function normalize(uri) { return uri.substr(-3) === '.js' ? uri : uri + '.js'; }
 // Utility function to join paths. In common case `base` is a
 // `requirer.uri` but in some cases it may be `baseURI`. In order to
 // avoid complexity we require `baseURI` with a trailing `/`.
 const resolve = iced(function resolve(id, base) {
+  if (!isRelative(id)) return id;
   let paths = id.split('/');
   let result = base.split('/');
   result.pop();
   while (paths.length) {
     let path = paths.shift();
     if (path === '..')
       result.pop();
     else if (path !== '.')
@@ -315,17 +315,16 @@ const Require = iced(function Require(lo
   function require(id) {
     if (!id) // Throw if `id` is not passed.
       throw Error('you must provide a module name when calling require() from '
                   + requirer.id, requirer.uri);
 
     // Resolve `id` to its requirer if it's relative.
     let requirement = requirer ? resolve(id, requirer.id) : id;
 
-
     // Resolves `uri` of module using loaders resolve function.
     let uri = resolveURI(requirement, mapping);
 
     if (!uri) // Throw if `uri` can not be resolved.
       throw Error('Module: Can not resolve "' + id + '" module required by ' +
                   requirer.id + ' located at ' + requirer.uri, requirer.uri);
 
     let module = null;
--- a/addon-sdk/source/python-lib/cuddlefish/__init__.py
+++ b/addon-sdk/source/python-lib/cuddlefish/__init__.py
@@ -764,17 +764,17 @@ def run(arguments=sys.argv[1:], target_c
 
     harness_options.update(build)
 
     extra_environment = {}
     if command == "test":
         # This should be contained in the test runner package.
         # maybe just do: target_cfg.main = 'test-harness/run-tests'
         harness_options['main'] = 'sdk/test/runner'
-        harness_options['mainPath'] = manifest.get_manifest_entry("addon-sdk", "lib", "sdk/test/runner").get_path()
+        harness_options['mainPath'] = 'sdk/test/runner'
     else:
         harness_options['main'] = target_cfg.get('main')
         harness_options['mainPath'] = manifest.top_path
     extra_environment["CFX_COMMAND"] = command
 
     for option in inherited_options:
         harness_options[option] = getattr(options, option)
 
--- a/addon-sdk/source/python-lib/cuddlefish/docs/generate.py
+++ b/addon-sdk/source/python-lib/cuddlefish/docs/generate.py
@@ -191,9 +191,8 @@ def write_file(env_root, doc_html, dest_
 def replace_file(env_root, dest_path, file_contents, must_rewrite_links):
     if os.path.exists(dest_path):
         os.remove(dest_path)
     # before we copy the final version, we'll rewrite the links
     # I'll do this last, just because we know definitely what the dest_path is at this point
     if must_rewrite_links and dest_path.endswith(".html"):
         file_contents = rewrite_links(env_root, get_sdk_docs_path(env_root), file_contents, dest_path)
     open(dest_path, "w").write(file_contents)
-
--- a/addon-sdk/source/python-lib/cuddlefish/manifest.py
+++ b/addon-sdk/source/python-lib/cuddlefish/manifest.py
@@ -53,40 +53,51 @@ class UnreachablePrefixError(Exception):
 class ManifestEntry:
     def __init__(self):
         self.docs_filename = None
         self.docs_hash = None
         self.requirements = {}
         self.datamap = None
 
     def get_path(self):
-        path = "%s/%s/%s" % \
-               (self.packageName, self.sectionName, self.moduleName)
-        if not path.endswith(".js"):
-          path += ".js"
-        return path
+        name = self.moduleName
+
+        if name.endswith(".js"):
+            name = name[:-3]
+        items = []
+        # Only add package name for addons, so that system module paths match
+        # the path from the commonjs root directory and also match the loader
+        # mappings.
+        if self.packageName != "addon-sdk":
+            items.append(self.packageName)
+        # And for the same reason, do not append `lib/`.
+        if self.sectionName == "tests":
+            items.append(self.sectionName)
+        items.append(name)
+
+        return "/".join(items)
 
     def get_entry_for_manifest(self):
         entry = { "packageName": self.packageName,
                   "sectionName": self.sectionName,
                   "moduleName": self.moduleName,
                   "jsSHA256": self.js_hash,
                   "docsSHA256": self.docs_hash,
                   "requirements": {},
                   }
         for req in self.requirements:
             if isinstance(self.requirements[req], ManifestEntry):
                 them = self.requirements[req] # this is another ManifestEntry
-                them_path = them.get_path()
-                entry["requirements"][req] = {"path": them_path}
+                entry["requirements"][req] = them.get_path()
             else:
                 # something magic. The manifest entry indicates that they're
                 # allowed to require() it
                 entry["requirements"][req] = self.requirements[req]
-            assert isinstance(entry["requirements"][req], dict)
+            assert isinstance(entry["requirements"][req], unicode) or \
+                   isinstance(entry["requirements"][req], str)
         return entry
 
     def add_js(self, js_filename):
         self.js_filename = js_filename
         self.js_hash = hash_file(js_filename)
     def add_docs(self, docs_filename):
         self.docs_filename = docs_filename
         self.docs_hash = hash_file(docs_filename)
@@ -221,17 +232,18 @@ class ManifestBuilder:
             test_finder = self.get_manifest_entry("addon-sdk", "lib",
                                                   "sdk/deprecated/unit-test-finder")
             for (testname,tme) in test_modules:
                 test_finder.add_requirement(testname, tme)
                 # finally, tell the runtime about it, so they won't have to
                 # search for all tests. self.test_modules will be passed
                 # through the harness-options.json file in the
                 # .allTestModules property.
-                self.test_modules.append(testname)
+                # Pass the absolute module path.
+                self.test_modules.append(tme.get_path())
 
         # include files used by the loader
         for em in self.extra_modules:
             (pkgname, section, modname, js) = em
             mi = ModuleInfo(self.pkg_cfg.packages[pkgname], section, modname,
                             js, None)
             self.process_module(mi)
 
@@ -373,17 +385,17 @@ class ManifestBuilder:
 
         # We update our requirements on the way out of the depth-first
         # traversal of the module graph
 
         for reqname in sorted(requires.keys()):
             # If requirement is chrome or a pseudo-module (starts with @) make
             # path a requirement name.
             if reqname == "chrome" or reqname.startswith("@"):
-                me.add_requirement(reqname, {"path": reqname})
+                me.add_requirement(reqname, reqname)
             else:
                 # when two modules require() the same name, do they get a
                 # 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
--- a/addon-sdk/source/python-lib/cuddlefish/rdf.py
+++ b/addon-sdk/source/python-lib/cuddlefish/rdf.py
@@ -164,17 +164,17 @@ def gen_manifest(template_root_dir, targ
         elem.appendChild(dom.createTextNode("{aa3c5121-dab2-40e2-81ca-7ea25febc110}"))
         ta_desc.appendChild(elem)
 
         elem = dom.createElement("em:minVersion")
         elem.appendChild(dom.createTextNode("18.0"))
         ta_desc.appendChild(elem)
 
         elem = dom.createElement("em:maxVersion")
-        elem.appendChild(dom.createTextNode("20.*"))
+        elem.appendChild(dom.createTextNode("21.0a1"))
         ta_desc.appendChild(elem)
 
     if target_cfg.get("homepage"):
         manifest.set("em:homepageURL", target_cfg.get("homepage"))
     else:
         manifest.remove("em:homepageURL")
 
     return manifest
--- a/addon-sdk/source/python-lib/cuddlefish/tests/test_linker.py
+++ b/addon-sdk/source/python-lib/cuddlefish/tests/test_linker.py
@@ -41,25 +41,25 @@ class Basic(unittest.TestCase):
                                               [target_cfg.name, "addon-sdk"])
         self.failUnlessEqual(deps, ["addon-sdk", "one"])
         # 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()
 
         def assertReqIs(modname, reqname, path):
-            reqs = m["one/lib/%s.js" % modname]["requirements"]
-            self.failUnlessEqual(reqs[reqname]["path"], path)
-        assertReqIs("main", "panel", "addon-sdk/lib/sdk/panel.js")
-        assertReqIs("main", "two.js", "one/lib/two.js")
-        assertReqIs("main", "./two", "one/lib/two.js")
-        assertReqIs("main", "sdk/tabs.js", "addon-sdk/lib/sdk/tabs.js")
-        assertReqIs("main", "./subdir/three", "one/lib/subdir/three.js")
-        assertReqIs("two", "main", "one/lib/main.js")
-        assertReqIs("subdir/three", "../main", "one/lib/main.js")
+            reqs = m["one/%s" % modname]["requirements"]
+            self.failUnlessEqual(reqs[reqname], path)
+        assertReqIs("main", "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 = []
         # now, because .dependencies *is* provided, we won't search 'deps',
         # so we'll get a link error
         self.assertRaises(manifest.ModuleNotFoundError,
                           manifest.build_manifest,
                           target_cfg, pkg_cfg, deps, scan_tests=False)
 
@@ -69,34 +69,34 @@ class Basic(unittest.TestCase):
         pkg_cfg = packaging.build_config(ROOT, target_cfg,
                                          packagepath=package_path)
         deps = packaging.get_deps_for_targets(pkg_cfg,
                                               [target_cfg.name, "addon-sdk"])
         self.failUnlessEqual(deps, ["addon-sdk", "three"])
         m = manifest.build_manifest(target_cfg, pkg_cfg, deps, scan_tests=False)
         m = m.get_harness_options_manifest()
         def assertReqIs(modname, reqname, path):
-            reqs = m["three/lib/%s.js" % modname]["requirements"]
-            self.failUnlessEqual(reqs[reqname]["path"], path)
-        assertReqIs("main", "three-a", "three-a/lib/main.js")
-        assertReqIs("main", "three-b", "three-b/lib/main.js")
-        assertReqIs("main", "three-c", "three-c/lib/main.js")
+            reqs = m["three/%s" % modname]["requirements"]
+            self.failUnlessEqual(reqs[reqname], path)
+        assertReqIs("main", "three-a", "three-a/main")
+        assertReqIs("main", "three-b", "three-b/main")
+        assertReqIs("main", "three-c", "three-c/main")
 
     def test_relative_main_in_top(self):
         target_cfg = self.get_pkg("five")
         package_path = []
         pkg_cfg = packaging.build_config(ROOT, target_cfg,
                                          packagepath=package_path)
         deps = packaging.get_deps_for_targets(pkg_cfg,
                                               [target_cfg.name, "addon-sdk"])
         self.failUnlessEqual(deps, ["addon-sdk", "five"])
         # all we care about is that this next call doesn't raise an exception
         m = manifest.build_manifest(target_cfg, pkg_cfg, deps, scan_tests=False)
         m = m.get_harness_options_manifest()
-        reqs = m["five/lib/main.js"]["requirements"]
+        reqs = m["five/main"]["requirements"]
         self.failUnlessEqual(reqs, {});
 
     def test_unreachable_relative_main_in_top(self):
         target_cfg = self.get_pkg("six")
         package_path = []
         pkg_cfg = packaging.build_config(ROOT, target_cfg,
                                          packagepath=package_path)
         deps = packaging.get_deps_for_targets(pkg_cfg,
--- a/addon-sdk/source/python-lib/cuddlefish/tests/test_xpi.py
+++ b/addon-sdk/source/python-lib/cuddlefish/tests/test_xpi.py
@@ -323,17 +323,17 @@ class SmallXPI(unittest.TestCase):
         package_path = [self.get_linker_files_dir("three-deps")]
         pkg_cfg = packaging.build_config(self.root, target_cfg,
                                          packagepath=package_path)
 
         deps = packaging.get_deps_for_targets(pkg_cfg,
                                               [target_cfg.name, "addon-sdk"])
         m = manifest.build_manifest(target_cfg, pkg_cfg, deps, scan_tests=True)
         self.failUnlessEqual(sorted(m.get_all_test_modules()),
-                             sorted(["test-one", "test-two"]))
+                             sorted(["three/tests/test-one", "three/tests/test-two"]))
         # the current __init__.py code omits limit_to=used_files for 'cfx
         # test', so all test files are included in the XPI. But the test
         # runner will only execute the tests that m.get_all_test_modules()
         # tells us about (which are put into the .allTestModules property of
         # harness-options.json).
         used_deps = m.get_used_packages()
 
         build = packaging.generate_build_for_target(pkg_cfg, target_cfg.name,
@@ -366,17 +366,17 @@ class SmallXPI(unittest.TestCase):
         pkg_cfg = packaging.build_config(self.root, target_cfg,
                                          packagepath=package_path)
         deps = packaging.get_deps_for_targets(pkg_cfg,
                                               [target_cfg.name, "addon-sdk"])
         FILTER = ".*one.*"
         m = manifest.build_manifest(target_cfg, pkg_cfg, deps, scan_tests=True,
                                     test_filter_re=FILTER)
         self.failUnlessEqual(sorted(m.get_all_test_modules()),
-                             sorted(["test-one"]))
+                             sorted(["three/tests/test-one"]))
         # the current __init__.py code omits limit_to=used_files for 'cfx
         # test', so all test files are included in the XPI. But the test
         # runner will only execute the tests that m.get_all_test_modules()
         # tells us about (which are put into the .allTestModules property of
         # harness-options.json).
         used_deps = m.get_used_packages()
 
         build = packaging.generate_build_for_target(pkg_cfg, target_cfg.name,
--- a/addon-sdk/source/python-lib/markdown/__init__.py
+++ b/addon-sdk/source/python-lib/markdown/__init__.py
@@ -177,17 +177,17 @@ import html4
 
 
 class Markdown:
     """Convert Markdown to HTML."""
 
     def __init__(self,
                  extensions=[],
                  extension_configs={},
-                 safe_mode = False, 
+                 safe_mode = False,
                  output_format=DEFAULT_OUTPUT_FORMAT):
         """
         Creates a new Markdown instance.
 
         Keyword arguments:
 
         * extensions: A list of extensions.
            If they are of type string, the module mdx_name.py will be loaded.
@@ -195,22 +195,22 @@ class Markdown:
            as-is.
         * extension-configs: Configuration setting for extensions.
         * safe_mode: Disallow raw html. One of "remove", "replace" or "escape".
         * output_format: Format of output. Supported formats are:
             * "xhtml1": Outputs XHTML 1.x. Default.
             * "xhtml": Outputs latest supported version of XHTML (currently XHTML 1.1).
             * "html4": Outputs HTML 4
             * "html": Outputs latest supported version of HTML (currently HTML 4).
-            Note that it is suggested that the more specific formats ("xhtml1" 
+            Note that it is suggested that the more specific formats ("xhtml1"
             and "html4") be used as "xhtml" or "html" may change in the future
-            if it makes sense at that time. 
+            if it makes sense at that time.
 
         """
-        
+
         self.safeMode = safe_mode
         self.registeredExtensions = []
         self.docType = ""
         self.stripTopLevelTags = True
 
         # Preprocessors
         self.preprocessors = odict.OrderedDict()
         self.preprocessors["html_block"] = \
@@ -295,19 +295,19 @@ class Markdown:
         self.postprocessors["raw_html"] = \
                 postprocessors.RawHtmlPostprocessor(self)
         self.postprocessors["amp_substitute"] = \
                 postprocessors.AndSubstitutePostprocessor()
         # footnote postprocessor will be inserted with ">amp_substitute"
 
         # Map format keys to serializers
         self.output_formats = {
-            'html'  : html4.to_html_string, 
+            'html'  : html4.to_html_string,
             'html4' : html4.to_html_string,
-            'xhtml' : etree.tostring, 
+            'xhtml' : etree.tostring,
             'xhtml1': etree.tostring,
         }
 
         self.references = {}
         self.htmlStash = preprocessors.HtmlStash()
         self.registerExtensions(extensions = extensions,
                                 configs = extension_configs)
         self.set_output_format(output_format)
@@ -327,17 +327,17 @@ class Markdown:
         for ext in extensions:
             if isinstance(ext, basestring):
                 ext = load_extension(ext, configs.get(ext, []))
             try:
                 ext.extendMarkdown(self, globals())
             except AttributeError:
                 message(ERROR, "Incorrect type! Extension '%s' is "
                                "neither a string or an Extension." %(repr(ext)))
-            
+
 
     def registerExtension(self, extension):
         """ This gets called by the extension """
         self.registeredExtensions.append(extension)
 
     def reset(self):
         """
         Resets all state variables so that we can start with a new text.
@@ -569,35 +569,35 @@ def markdown(text,
     * text: Markdown formatted text as Unicode or ASCII string.
     * extensions: A list of extensions or extension names (may contain config args).
     * safe_mode: Disallow raw html.  One of "remove", "replace" or "escape".
     * output_format: Format of output. Supported formats are:
         * "xhtml1": Outputs XHTML 1.x. Default.
         * "xhtml": Outputs latest supported version of XHTML (currently XHTML 1.1).
         * "html4": Outputs HTML 4
         * "html": Outputs latest supported version of HTML (currently HTML 4).
-        Note that it is suggested that the more specific formats ("xhtml1" 
+        Note that it is suggested that the more specific formats ("xhtml1"
         and "html4") be used as "xhtml" or "html" may change in the future
-        if it makes sense at that time. 
+        if it makes sense at that time.
 
     Returns: An HTML document as a string.
 
     """
     md = Markdown(extensions=load_extensions(extensions),
-                  safe_mode=safe_mode, 
+                  safe_mode=safe_mode,
                   output_format=output_format)
     return md.convert(text)
 
 
 def markdownFromFile(input = None,
                      output = None,
                      extensions = [],
                      encoding = None,
                      safe_mode = False,
                      output_format = DEFAULT_OUTPUT_FORMAT):
     """Read markdown code from a file and write it to a file or a stream."""
-    md = Markdown(extensions=load_extensions(extensions), 
+    md = Markdown(extensions=load_extensions(extensions),
                   safe_mode=safe_mode,
                   output_format=output_format)
     md.convertFile(input, output, encoding)
 
 
 
--- a/addon-sdk/source/python-lib/mozrunner/__init__.py
+++ b/addon-sdk/source/python-lib/mozrunner/__init__.py
@@ -448,18 +448,20 @@ class Runner(object):
                 if sys.platform == 'cygwin':
                     program_files = os.environ['PROGRAMFILES']
                 else:
                     program_files = os.environ['ProgramFiles']
 
                 if binary is None:
                     for bin in [(program_files, 'Mozilla Firefox', 'firefox.exe'),
                                 (os.environ.get("ProgramFiles(x86)"),'Mozilla Firefox', 'firefox.exe'),
-                                (program_files,'Nightly', 'firefox.exe'),
-                                (os.environ.get("ProgramFiles(x86)"),'Nightly', 'firefox.exe')
+                                (program_files, 'Nightly', 'firefox.exe'),
+                                (os.environ.get("ProgramFiles(x86)"),'Nightly', 'firefox.exe'),
+                                (program_files, 'Aurora', 'firefox.exe'),
+                                (os.environ.get("ProgramFiles(x86)"),'Aurora', 'firefox.exe')
                                 ]:
                         path = os.path.join(*bin)
                         if os.path.isfile(path):
                             binary = path
                             break
         elif sys.platform == 'darwin':
             for bundle_name in self.bundle_names:
                 # Look for the application bundle in the user's home directory
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/test/addons/layout-change/main.js
@@ -0,0 +1,187 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+// This test makes sure that require statements used by all AMO hosted
+// add-ons will be able to use old require statements.
+// Tests are based on following usage data:
+// https://docs.google.com/spreadsheet/ccc?key=0ApEBy-GRnGxzdHlRMHJ5RXN1aWJ4RGhINkxSd0FCQXc#gid=0
+
+exports["test compatibility"] = function(assert) {
+  assert.equal(require("self"),
+               require("sdk/self"), "sdk/self -> self");
+
+  assert.equal(require("tabs"),
+               require("sdk/tabs"), "sdk/tabs -> tabs");
+
+  assert.equal(require("widget"),
+               require("sdk/widget"), "sdk/widget -> widget");
+
+  assert.equal(require("page-mod"),
+               require("sdk/page-mod"), "sdk/page-mod -> page-mod");
+
+  assert.equal(require("panel"),
+               require("sdk/panel"), "sdk/panel -> panel");
+
+  assert.equal(require("request"),
+               require("sdk/request"), "sdk/request -> request");
+
+  assert.equal(require("chrome"),
+               require("chrome"), "chrome -> chrome");
+
+  assert.equal(require("simple-storage"),
+               require("sdk/simple-storage"), "sdk/simple-storage -> simple-storage");
+
+  assert.equal(require("context-menu"),
+               require("sdk/context-menu"), "sdk/context-menu -> context-menu");
+
+  assert.equal(require("notifications"),
+               require("sdk/notifications"), "sdk/notifications -> notifications");
+
+  assert.equal(require("preferences-service"),
+               require("sdk/preferences/service"), "sdk/preferences/service -> preferences-service");
+
+  assert.equal(require("window-utils"),
+               require("sdk/deprecated/window-utils"), "sdk/deprecated/window-utils -> window-utils");
+
+  assert.equal(require("url"),
+               require("sdk/url"), "sdk/url -> url");
+
+  assert.equal(require("selection"),
+               require("sdk/selection"), "sdk/selection -> selection");
+
+  assert.equal(require("timers"),
+               require("sdk/timers"), "sdk/timers -> timers");
+
+  assert.equal(require("simple-prefs"),
+               require("sdk/simple-prefs"), "sdk/simple-prefs -> simple-prefs");
+
+  assert.equal(require("traceback"),
+               require("sdk/console/traceback"), "sdk/console/traceback -> traceback");
+
+  assert.equal(require("unload"),
+               require("sdk/system/unload"), "sdk/system/unload -> unload");
+
+  assert.equal(require("hotkeys"),
+               require("sdk/hotkeys"), "sdk/hotkeys -> hotkeys");
+
+  assert.equal(require("clipboard"),
+               require("sdk/clipboard"), "sdk/clipboard -> clipboard");
+
+  assert.equal(require("windows"),
+               require("sdk/windows"), "sdk/windows -> windows");
+
+  assert.equal(require("page-worker"),
+               require("sdk/page-worker"), "sdk/page-worker -> page-worker");
+
+  assert.equal(require("timer"),
+               require("sdk/timers"), "sdk/timers -> timer");
+
+  assert.equal(require("xhr"),
+               require("sdk/net/xhr"), "sdk/io/xhr -> xhr");
+
+  assert.equal(require("observer-service"),
+               require("sdk/deprecated/observer-service"), "sdk/deprecated/observer-service -> observer-service");
+
+  assert.equal(require("private-browsing"),
+               require("sdk/private-browsing"), "sdk/private-browsing -> private-browsing");
+
+  assert.equal(require("passwords"),
+               require("sdk/passwords"), "sdk/passwords -> passwords");
+
+  assert.equal(require("events"),
+               require("sdk/deprecated/events"), "sdk/deprecated/events -> events");
+
+  assert.equal(require("match-pattern"),
+               require("sdk/page-mod/match-pattern"), "sdk/page-mod/match-pattern -> match-pattern");
+
+  assert.equal(require("tab-browser"),
+               require("sdk/deprecated/tab-browser"), "sdk/deprecated/tab-browser -> tab-browser");
+
+  assert.equal(require("file"),
+               require("sdk/io/file"), "sdk/io/file -> file");
+
+  assert.equal(require("xul-app"),
+               require("sdk/system/xul-app"), "sdk/system/xul-app -> xul-app");
+
+  assert.equal(require("api-utils"),
+               require("sdk/deprecated/api-utils"), "sdk/deprecated/api-utils -> api-utils");
+
+  assert.equal(require("runtime"),
+               require("sdk/system/runtime"), "sdk/system/runtime -> runtime");
+
+  assert.equal(require("base64"),
+               require("sdk/base64"), "sdk/base64 -> base64");
+
+  assert.equal(require("xpcom"),
+               require("sdk/platform/xpcom"), "sdk/platform/xpcom -> xpcom");
+
+  assert.equal(require("traits"),
+               require("sdk/deprecated/traits"), "sdk/deprecated/traits -> traits");
+
+  assert.equal(require("keyboard/utils"),
+               require("sdk/keyboard/utils"), "sdk/keyboard/utils -> keyboard/utils");
+
+  assert.equal(require("system"),
+               require("sdk/system"), "sdk/system -> system");
+
+  assert.equal(require("querystring"),
+               require("sdk/querystring"), "sdk/querystring -> querystring");
+
+  assert.equal(require("addon-page"),
+               require("sdk/addon-page"), "sdk/addon-page -> addon-page");
+
+  assert.equal(require("tabs/utils"),
+               require("sdk/tabs/utils"), "sdk/tabs/utils -> tabs/utils");
+
+  assert.equal(require("app-strings"),
+               require("sdk/deprecated/app-strings"), "sdk/deprecated/app-strings -> app-strings");
+
+  assert.equal(require("dom/events"),
+               require("sdk/dom/events"), "sdk/dom/events -> dom/events");
+
+  assert.equal(require("tabs/tab.js"),
+               require("sdk/tabs/tab"), "sdk/tabs/tab -> tabs/tab.js");
+
+  assert.equal(require("memory"),
+               require("sdk/deprecated/memory"), "sdk/deprecated/memory -> memory");
+
+  assert.equal(require("light-traits"),
+               require("sdk/deprecated/light-traits"), "sdk/deprecated/light-traits -> light-traits");
+
+  assert.equal(require("environment"),
+               require("sdk/system/environment"), "sdk/system/environment -> environment");
+
+  assert.equal(require("utils/data"),
+               require("sdk/io/data"), "sdk/io/data -> utils/data");
+
+  assert.equal(require("test/assert"),
+               require("sdk/test/assert"), "sdk/test/assert -> test/assert");
+
+  assert.equal(require("hidden-frame"),
+               require("sdk/frame/hidden-frame"), "sdk/frame/hidden-frame -> hidden-frame");
+
+  assert.equal(require("collection"),
+               require("sdk/util/collection"), "sdk/util/collection -> collection");
+
+  assert.equal(require("array"),
+               require("sdk/util/array"), "sdk/util/array -> array");
+
+  assert.equal(require("api-utils/cortex"),
+               require("sdk/deprecated/cortex"),
+               "api-utils/cortex -> sdk/deprecated/cortex");
+};
+
+if (require("sdk/system/xul-app").is("Fennec")) {
+  module.exports = {
+    "test Unsupported Test": function UnsupportedTest (assert) {
+        assert.pass(
+          "Skipping this test until Fennec support is implemented." +
+          "See bug 809352");
+    }
+  }
+}
+
+require("sdk/test/runner").runTestsFromModule(module);
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/test/addons/layout-change/package.json
@@ -0,0 +1,3 @@
+{
+  "id": "test-layout-change"
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/test/addons/packed/main.js
@@ -0,0 +1,20 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+const { packed } = require("sdk/self");
+const url = require("sdk/url");
+
+exports["test self.packed"] = function (assert) {
+  assert.ok(packed, "require('sdk/self').packed is correct");
+}
+
+exports["test url.toFilename"] = function (assert) {
+  assert.throws(
+      function() { url.toFilename(module.uri); },
+      /cannot map to filename: /,
+      "url.toFilename() can fail for packed XPIs");
+}
+
+require("sdk/test/runner").runTestsFromModule(module);
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/test/addons/packed/package.json
@@ -0,0 +1,4 @@
+{
+  "id": "test-url",
+  "unpack": false
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/test/addons/unpacked/main.js
@@ -0,0 +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/. */
+"use strict";
+
+const { packed } = require("sdk/self");
+const url = require("sdk/url");
+
+exports["test self.packed"] = function (assert) {
+  assert.ok(!packed, "require('sdk/self').packed is correct");
+}
+
+exports["test url.toFilename"] = function (assert) {
+  assert.ok(/.*main\.js$/.test(url.toFilename(module.uri)),
+            "url.toFilename() on resource: URIs should work");
+}
+
+require("sdk/test/runner").runTestsFromModule(module);
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/test/addons/unpacked/package.json
@@ -0,0 +1,4 @@
+{
+  "id": "test-url",
+  "unpack": true
+}
\ No newline at end of file
--- a/addon-sdk/source/test/commonjs-test-adapter/asserts.js
+++ b/addon-sdk/source/test/commonjs-test-adapter/asserts.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/. */
 
 "use strict";
 
-const AssertBase = require("test/assert").Assert;
+const AssertBase = require("sdk/test/assert").Assert;
 
 /**
  * Generates custom assertion constructors that may be bundled with a test
  * suite.
  * @params {String}
  *    names of assertion function to be added to the generated Assert.
  */
 function Assert() {
--- a/addon-sdk/source/test/modules/tiger.js
+++ b/addon-sdk/source/test/modules/tiger.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/. */
 
 define(function (require, exports) {
   exports.name = 'tiger';
-  exports.type = require('modules/types/cat').type;
+  exports.type = require('./types/cat').type;
 });
--- a/addon-sdk/source/test/private-browsing/global.js
+++ b/addon-sdk/source/test/private-browsing/global.js
@@ -42,55 +42,65 @@ exports["test activate private mode via 
 
 // tests that isActive has the same value as the private browsing service
 // expects
 exports.testGetIsActive = function (test) {
   test.waitUntilDone();
 
   test.assertEqual(pb.isActive, false,
                    "private-browsing.isActive is correct without modifying PB service");
+  test.assertEqual(pb.isPrivate(), false,
+                   "private-browsing.sPrivate() is correct without modifying PB service");
 
   pb.once("start", function() {
     test.assert(pb.isActive,
                   "private-browsing.isActive is correct after modifying PB service");
+    test.assert(pb.isPrivate(),
+                  "private-browsing.sPrivate() is correct after modifying PB service");
     // Switch back to normal mode.
     pb.deactivate();
   });
   pb.activate();
 
   pb.once("stop", function() {
     test.assert(!pb.isActive,
                 "private-browsing.isActive is correct after modifying PB service");
+    test.assert(!pb.isPrivate(),
+                "private-browsing.sPrivate() is correct after modifying PB service");
     test.done();
   });
 };
 
 exports.testStart = function(test) {
   test.waitUntilDone();
 
   pb.on("start", function onStart() {
     test.assertEqual(this, pb, "`this` should be private-browsing module");
     test.assert(pbUtils.getMode(),
                 'private mode is active when "start" event is emitted');
     test.assert(pb.isActive,
                 '`isActive` is `true` when "start" event is emitted');
+    test.assert(pb.isPrivate(),
+                '`isPrivate` is `true` when "start" event is emitted');
     pb.removeListener("start", onStart);
     deactivate(function() test.done());
   });
   pb.activate();
 };
 
 exports.testStop = function(test) {
   test.waitUntilDone();
   pb.once("stop", function onStop() {
     test.assertEqual(this, pb, "`this` should be private-browsing module");
     test.assertEqual(pbUtils.getMode(), false,
                      "private mode is disabled when stop event is emitted");
     test.assertEqual(pb.isActive, false,
                      "`isActive` is `false` when stop event is emitted");
+    test.assertEqual(pb.isPrivate(), false,
+                     "`isPrivate()` is `false` when stop event is emitted");
     test.done();
   });
   pb.activate();
   pb.once("start", function() {
     pb.deactivate();
   });
 };
 
@@ -101,55 +111,62 @@ exports.testBothListeners = function(tes
 
   function onStop() {
     test.assertEqual(stop, false,
                      "stop callback must be called only once");
     test.assertEqual(pbUtils.getMode(), false,
                      "private mode is disabled when stop event is emitted");
     test.assertEqual(pb.isActive, false,
                      "`isActive` is `false` when stop event is emitted");
+    test.assertEqual(pb.isPrivate(), false,
+                     "`isPrivate()` is `false` when stop event is emitted");
 
     pb.on("start", finish);
     pb.removeListener("start", onStart);
     pb.removeListener("start", onStart2);
     pb.activate();
     stop = true;
   }
 
   function onStart() {
     test.assertEqual(false, start,
                      "stop callback must be called only once");
     test.assert(pbUtils.getMode(),
                 "private mode is active when start event is emitted");
     test.assert(pb.isActive,
                 "`isActive` is `true` when start event is emitted");
+    test.assert(pb.isPrivate(),
+                "`isPrivate()` is `true` when start event is emitted");
 
     pb.on("stop", onStop);
     pb.deactivate();
     start = true;
   }
 
   function onStart2() {
     test.assert(start, "start listener must be called already");
     test.assertEqual(false, stop, "stop callback must not be called yet");
   }
 
   function finish() {
     test.assert(pbUtils.getMode(), true,
                 "private mode is active when start event is emitted");
     test.assert(pb.isActive,
                 "`isActive` is `true` when start event is emitted");
+    test.assert(pb.isPrivate(),
+                "`isPrivate()` is `true` when start event is emitted");
 
     pb.removeListener("start", finish);
     pb.removeListener("stop", onStop);
 
     pb.deactivate();
     pb.once("stop", function () {
       test.assertEqual(pbUtils.getMode(), false);
       test.assertEqual(pb.isActive, false);
+      test.assertEqual(pb.isPrivate(), false);
 
       test.done();
     });
   }
 
   pb.on("start", onStart);
   pb.on("start", onStart2);
   pb.activate();
--- a/addon-sdk/source/test/private-browsing/helper.js
+++ b/addon-sdk/source/test/private-browsing/helper.js
@@ -2,21 +2,25 @@
  * 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');
 const unload = require("sdk/system/unload");
 const { Loader } = require('sdk/test/loader');
 const { windows: windowsIterator } = require("sdk/window/utils");
-const windows = require("windows").browserWindows;
+const windows = require("sdk/windows").browserWindows;
 
 let { loader } = LoaderWithHookedConsole();
 const pb = loader.require('sdk/private-browsing');
 const pbUtils = loader.require('sdk/private-browsing/utils');
+const { getOwnerWindow } = require('sdk/private-browsing/window/utils');
+
+require('sdk/tabs/utils');
+require('sdk/windows');
 
 function LoaderWithHookedConsole() {
   let errors = [];
   let loader = Loader(module, {
     console: Object.create(console, {
       error: { value: function(e) {
         if (!/DEPRECATED:/.test(e)) {
           console.error(e);
@@ -35,11 +39,13 @@ function deactivate(callback) {
   if (pbUtils.isGlobalPBSupported) {
     if (callback)
       pb.once('stop', callback);
     pb.deactivate();
   }
 }
 exports.deactivate = deactivate;
 
+exports.loader = loader;
 exports.pb = pb;
 exports.pbUtils = pbUtils;
+exports.getOwnerWindow = getOwnerWindow;
 exports.LoaderWithHookedConsole = LoaderWithHookedConsole;
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/test/private-browsing/tabs.js
@@ -0,0 +1,24 @@
+'use strict';
+
+const { Ci } = require('chrome');
+const { pb, pbUtils, loader: pbLoader, getOwnerWindow } = require('./helper');
+
+exports.testIsPrivateOnTab = function(test) {
+  const { openTab, closeTab } = pbLoader.require('sdk/tabs/utils');
+
+  let window = pbLoader.require('sdk/windows').browserWindows.activeWindow;
+  let chromeWindow = pbLoader.require('sdk/private-browsing/window/utils').getOwnerWindow(window);
+  test.assert(chromeWindow instanceof Ci.nsIDOMWindow, 'associated window is found');
+  test.assert(!pb.isPrivate(chromeWindow), 'the top level window is not private');
+
+  let rawTab = openTab(chromeWindow, 'data:text/html,<h1>Hi!</h1>', {
+  	private: true
+  });
+
+  // test that the tab is private
+  test.assert(rawTab.browser.docShell.QueryInterface(Ci.nsILoadContext).usePrivateBrowsing);
+  test.assert(pb.isPrivate(rawTab.browser.contentWindow));
+  test.assert(pb.isPrivate(rawTab.browser));
+
+  closeTab(rawTab);
+}
--- a/addon-sdk/source/test/private-browsing/windows.js
+++ b/addon-sdk/source/test/private-browsing/windows.js
@@ -1,30 +1,54 @@
 /* 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 { pb, pbUtils } = require('./helper');
-const { openDialog } = require('window/utils');
+const { openDialog } = require('sdk/window/utils');
+const { isPrivate } = require('sdk/private-browsing');
+const { browserWindows: windows } = require('sdk/windows');
 
-exports["test Per Window Private Browsing getter"] = function(assert, done) {
+exports.testPerWindowPrivateBrowsingGetter = function(assert, done) {
   let win = openDialog({
     private: true
   });
 
   win.addEventListener('DOMContentLoaded', function onload() {
     win.removeEventListener('DOMContentLoaded', onload, false);
 
     assert.equal(pbUtils.getMode(win),
                  true, 'Newly opened window is in PB mode');
     assert.equal(pb.isActive, false, 'PB mode is not active');
 
     win.addEventListener("unload", function onunload() {
       win.removeEventListener('unload', onload, false);
       assert.equal(pb.isActive, false, 'PB mode is not active');
       done();
     }, false);
+
     win.close();
   }, false);
 }
 
+exports.testIsPrivateOnWindowOn = function(assert, done) {
+  windows.open({
+    private: true,
+    onOpen: function(window) {
+      assert.equal(isPrivate(window), true, 'isPrivate for a window is true when it should be');
+      assert.equal(isPrivate(window.tabs[0]), true, 'isPrivate for a tab is false when it should be');
+      window.close(done);
+    }
+  });
+}
+
+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);
+    }
+  })
+}
+
 require("test").run(exports);
--- a/addon-sdk/source/test/tabs/test-firefox-tabs.js
+++ b/addon-sdk/source/test/tabs/test-firefox-tabs.js
@@ -913,17 +913,17 @@ exports['test ready event on new window 
   });
 
   let window = openBrowserWindow(function(){}, uri);
 };
 
 exports['test unique tab ids'] = function(test) {
   test.waitUntilDone();
 
-  var windows = require('windows').browserWindows,
+  var windows = require('sdk/windows').browserWindows,
     tabIds = {}, win1, win2;
 
   let steps = [
     function (index) {
       win1 = windows.open({
           url: "data:text/html;charset=utf-8,foo",
           onOpen: function(window) {
             tabIds['tab1'] = window.tabs.activeTab.id;
--- a/addon-sdk/source/test/test-addon-page.js
+++ b/addon-sdk/source/test/test-addon-page.js
@@ -16,17 +16,17 @@ let uri = require('sdk/self').data.url('
 
 function isChromeVisible(window) {
   let x = window.document.documentElement.getAttribute('disablechrome')
   return x !== 'true';
 }
 
 exports['test that add-on page has no chrome'] = function(assert, done) {
   let loader = Loader(module);
-  loader.require('addon-kit/addon-page');
+  loader.require('sdk/addon-page');
 
   let window = windows.activeBrowserWindow;
   let tab = openTab(window, uri);
 
   assert.ok(isChromeVisible(window), 'chrome is visible for non addon page');
 
   // need to do this in another turn to make sure event listener
   // that sets property has time to do that.
@@ -39,17 +39,17 @@ exports['test that add-on page has no ch
     assert.ok(isChromeVisible(window), 'chrome is visible again');
     loader.unload();
     done();
   });
 };
 
 exports['test that add-on page with hash has no chrome'] = function(assert, done) {
   let loader = Loader(module);
-  loader.require('addon-kit/addon-page');
+  loader.require('sdk/addon-page');
 
   let window = windows.activeBrowserWindow;
   let tab = openTab(window, uri + "#foo");
 
   assert.ok(isChromeVisible(window), 'chrome is visible for non addon page');
 
   // need to do this in another turn to make sure event listener
   // that sets property has time to do that.
@@ -62,17 +62,17 @@ exports['test that add-on page with hash
     assert.ok(isChromeVisible(window), 'chrome is visible again');
     loader.unload();
     done();
   });
 };
 
 exports['test that add-on page with querystring has no chrome'] = function(assert, done) {
   let loader = Loader(module);
-  loader.require('addon-kit/addon-page');
+  loader.require('sdk/addon-page');
 
   let window = windows.activeBrowserWindow;
   let tab = openTab(window, uri + '?foo=bar');
 
   assert.ok(isChromeVisible(window), 'chrome is visible for non addon page');
 
   // need to do this in another turn to make sure event listener
   // that sets property has time to do that.
@@ -85,17 +85,17 @@ exports['test that add-on page with quer
     assert.ok(isChromeVisible(window), 'chrome is visible again');
     loader.unload();
     done();
   });
 };
 
 exports['test that add-on page with hash and querystring has no chrome'] = function(assert, done) {
   let loader = Loader(module);
-  loader.require('addon-kit/addon-page');
+  loader.require('sdk/addon-page');
 
   let window = windows.activeBrowserWindow;
   let tab = openTab(window, uri + '#foo?foo=bar');
 
   assert.ok(isChromeVisible(window), 'chrome is visible for non addon page');
 
   // need to do this in another turn to make sure event listener
   // that sets property has time to do that.
@@ -108,17 +108,17 @@ exports['test that add-on page with hash
     assert.ok(isChromeVisible(window), 'chrome is visible again');
     loader.unload();
     done();
   });
 };
 
 exports['test that malformed uri is not an addon-page'] = function(assert, done) {
   let loader = Loader(module);
-  loader.require('addon-kit/addon-page');
+  loader.require('sdk/addon-page');
 
   let window = windows.activeBrowserWindow;
   let tab = openTab(window, uri + 'anguage');
 
   // need to do this in another turn to make sure event listener
   // that sets property has time to do that.
   setTimeout(function() {
     activateTab(tab);
@@ -141,9 +141,9 @@ exports['test that add-on pages are clos
       loader.unload();
       assert.ok(!isTabOpen(tab), 'add-on page tabs are closed on unload');
 
       done();
     }
   });
 };
 
-require('sdk/test').run(exports);
+require('test').run(exports);
--- a/addon-sdk/source/test/test-context-menu.js
+++ b/addon-sdk/source/test/test-context-menu.js
@@ -1355,17 +1355,17 @@ exports.testMultipleModulesOrder = funct
 // Checks that the order of menu items is correct when adding/removing across
 // multiple modules when overflowing. All items from a single module should
 // remain in a group
 exports.testMultipleModulesOrderOverflow = function (test) {
   test = new TestHelper(test);
   let loader0 = test.newLoader();
   let loader1 = test.newLoader();
 
-  let prefs = loader0.loader.require("preferences-service");
+  let prefs = loader0.loader.require("sdk/preferences/service");
   prefs.set(OVERFLOW_THRESH_PREF, 0);
 
   // Use each module to add an item, then unload each module in turn.
   let item0 = new loader0.cm.Item({ label: "item 0" });
   let item1 = new loader1.cm.Item({ label: "item 1" });
 
   test.showMenu(null, function (popup) {
 
@@ -1396,17 +1396,17 @@ exports.testMultipleModulesOrderOverflow
 
 // Checks that if a module's items are all hidden then the overflow menu doesn't
 // get hidden
 exports.testMultipleModulesOverflowHidden = function (test) {
   test = new TestHelper(test);
   let loader0 = test.newLoader();
   let loader1 = test.newLoader();
 
-  let prefs = loader0.loader.require("preferences-service");
+  let prefs = loader0.loader.require("sdk/preferences/service");
   prefs.set(OVERFLOW_THRESH_PREF, 0);
 
   // Use each module to add an item, then unload each module in turn.
   let item0 = new loader0.cm.Item({ label: "item 0" });
   let item1 = new loader1.cm.Item({
     label: "item 1",
     context: loader1.cm.SelectorContext("a")
   });
@@ -1421,17 +1421,17 @@ exports.testMultipleModulesOverflowHidde
 
 // Checks that if a module's items are all hidden then the overflow menu doesn't
 // get hidden (reverse order to above)
 exports.testMultipleModulesOverflowHidden2 = function (test) {
   test = new TestHelper(test);
   let loader0 = test.newLoader();
   let loader1 = test.newLoader();
 
-  let prefs = loader0.loader.require("preferences-service");
+  let prefs = loader0.loader.require("sdk/preferences/service");
   prefs.set(OVERFLOW_THRESH_PREF, 0);
 
   // Use each module to add an item, then unload each module in turn.
   let item0 = new loader0.cm.Item({
     label: "item 0",
     context: loader0.cm.SelectorContext("a")
   });
   let item1 = new loader1.cm.Item({ label: "item 1" });
@@ -1445,17 +1445,17 @@ exports.testMultipleModulesOverflowHidde
 
 
 // Checks that we don't overflow if there are more items than the overflow
 // threshold but not all of them are visible
 exports.testOverflowIgnoresHidden = function (test) {
   test = new TestHelper(test);
   let loader = test.newLoader();
 
-  let prefs = loader.loader.require("preferences-service");
+  let prefs = loader.loader.require("sdk/preferences/service");
   prefs.set(OVERFLOW_THRESH_PREF, 2);
 
   let allItems = [
     new loader.cm.Item({
       label: "item 0"
     }),
     new loader.cm.Item({
       label: "item 1"
@@ -1476,17 +1476,17 @@ exports.testOverflowIgnoresHidden = func
 
 // Checks that we don't overflow if there are more items than the overflow
 // threshold but not all of them are visible
 exports.testOverflowIgnoresHiddenMultipleModules1 = function (test) {
   test = new TestHelper(test);
   let loader0 = test.newLoader();
   let loader1 = test.newLoader();
 
-  let prefs = loader0.loader.require("preferences-service");
+  let prefs = loader0.loader.require("sdk/preferences/service");
   prefs.set(OVERFLOW_THRESH_PREF, 2);
 
   let allItems = [
     new loader0.cm.Item({
       label: "item 0"
     }),
     new loader0.cm.Item({
       label: "item 1"
@@ -1511,17 +1511,17 @@ exports.testOverflowIgnoresHiddenMultipl
 
 // Checks that we don't overflow if there are more items than the overflow
 // threshold but not all of them are visible
 exports.testOverflowIgnoresHiddenMultipleModules2 = function (test) {
   test = new TestHelper(test);
   let loader0 = test.newLoader();
   let loader1 = test.newLoader();
 
-  let prefs = loader0.loader.require("preferences-service");
+  let prefs = loader0.loader.require("sdk/preferences/service");
   prefs.set(OVERFLOW_THRESH_PREF, 2);
 
   let allItems = [
     new loader0.cm.Item({
       label: "item 0"
     }),
     new loader0.cm.Item({
       label: "item 1",
@@ -1546,17 +1546,17 @@ exports.testOverflowIgnoresHiddenMultipl
 
 // Checks that we don't overflow if there are more items than the overflow
 // threshold but not all of them are visible
 exports.testOverflowIgnoresHiddenMultipleModules3 = function (test) {
   test = new TestHelper(test);
   let loader0 = test.newLoader();
   let loader1 = test.newLoader();
 
-  let prefs = loader0.loader.require("preferences-service");
+  let prefs = loader0.loader.require("sdk/preferences/service");
   prefs.set(OVERFLOW_THRESH_PREF, 2);
 
   let allItems = [
     new loader0.cm.Item({
       label: "item 0",
       context: loader0.cm.SelectorContext("a")
     }),
     new loader0.cm.Item({
@@ -1580,17 +1580,17 @@ exports.testOverflowIgnoresHiddenMultipl
 
 
 // Tests that we transition between overflowing to non-overflowing to no items
 // and back again
 exports.testOverflowTransition = function (test) {
   test = new TestHelper(test);
   let loader = test.newLoader();
 
-  let prefs = loader.loader.require("preferences-service");
+  let prefs = loader.loader.require("sdk/preferences/service");
   prefs.set(OVERFLOW_THRESH_PREF, 2);
 
   let pItems = [
     new loader.cm.Item({
       label: "item 0",
       context: loader.cm.SelectorContext("p")
     }),
     new loader.cm.Item({
@@ -1713,17 +1713,16 @@ exports.testMenuCommand = function (test
     label: "submenu",
     context: loader.cm.SelectorContext("a"),
     items: [item]
   });
 
   let topMenu = new loader.cm.Menu({
     label: "top menu",
     contentScript: 'self.on("click", function (node, data) {' +
-                   '  let Ci = Components["interfaces"];' +
                    '  self.postMessage({' +
                    '    tagName: node.tagName,' +
                    '    data: data' +
                    '  });' +
                    '});',
     onMessage: function (data) {
       test.assertEqual(this, topMenu, "`this` inside top menu should be menu");
       test.assertEqual(data.tagName, "A", "Clicked node should be anchor");
@@ -1793,17 +1792,16 @@ exports.testItemCommandMultipleModules =
 exports.testItemClick = function (test) {
   test = new TestHelper(test);
   let loader = test.newLoader();
 
   let item = new loader.cm.Item({
     label: "item",
     data: "item data",
     contentScript: 'self.on("click", function (node, data) {' +
-                   '  let Ci = Components["interfaces"];' +
                    '  self.postMessage({' +
                    '    tagName: node.tagName,' +
                    '    data: data' +
                    '  });' +
                    '});',
     onMessage: function (data) {
       test.assertEqual(this, item, "`this` inside onMessage should be item");
       test.assertEqual(data.tagName, "HTML", "node should be an HTML element");
@@ -1841,17 +1839,16 @@ exports.testMenuClick = function (test) 
     label: "submenu",
     context: loader.cm.SelectorContext("a"),
     items: [item]
   });
 
   let topMenu = new loader.cm.Menu({
     label: "top menu",
     contentScript: 'self.on("click", function (node, data) {' +
-                   '  let Ci = Components["interfaces"];' +
                    '  self.postMessage({' +
                    '    tagName: node.tagName,' +
                    '    data: data' +
                    '  });' +
                    '});',
     onMessage: function (data) {
       test.assertEqual(this, topMenu, "`this` inside top menu should be menu");
       test.assertEqual(data.tagName, "A", "Clicked node should be anchor");
--- a/addon-sdk/source/test/test-httpd.js
+++ b/addon-sdk/source/test/test-httpd.js
@@ -1,28 +1,34 @@
 /* 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 port = 8099;
 const file = require("sdk/io/file");
 const { pathFor } = require("sdk/system");
+const { Loader } = require("sdk/test/loader");
+const options = require("@test/options");
+
+const loader = Loader(module);
+const httpd = loader.require("sdk/test/httpd");
+if (options.parseable || options.verbose)
+  loader.sandbox("sdk/test/httpd").DEBUG = true;
 
 exports.testBasicHTTPServer = function(test) {
   // Use the profile directory for the temporary file as that will be deleted
   // when tests are complete
   let basePath = pathFor("ProfD");
   let filePath = file.join(basePath, 'test-httpd.txt');
   let content = "This is the HTTPD test file.\n";
   let fileStream = file.open(filePath, 'w');
   fileStream.write(content);
   fileStream.close();
 
-  let { startServerAsync } = require("sdk/test/httpd");
-  let srv = startServerAsync(port, basePath);
+  let srv = httpd.startServerAsync(port, basePath);
 
   test.waitUntilDone();
 
   // Request this very file.
   let Request = require('sdk/request').Request;
   Request({
     url: "http://localhost:" + port + "/test-httpd.txt",
     onComplete: function (response) {
@@ -36,18 +42,17 @@ exports.testBasicHTTPServer = function(t
       test.done();
     });
   }
 };
 
 exports.testDynamicServer = function (test) {
   let content = "This is the HTTPD test file.\n";
 
-  let { startServerAsync } = require("sdk/test/httpd");
-  let srv = startServerAsync(port);
+  let srv = httpd.startServerAsync(port);
 
   // See documentation here:
   //http://doxygen.db48x.net/mozilla/html/interfacensIHttpServer.html#a81fc7e7e29d82aac5ce7d56d0bedfb3a
   //http://doxygen.db48x.net/mozilla/html/interfacensIHttpRequestHandler.html
   srv.registerPathHandler("/test-httpd.txt", function handle(request, response) {
     // Add text content type, only to avoid error in `Request` API
     response.setHeader("Content-Type", "text/plain", false);
     response.write(content);
--- a/addon-sdk/source/test/test-indexed-db.js
+++ b/addon-sdk/source/test/test-indexed-db.js
@@ -1,22 +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 xulApp = require("api-utils/xul-app");
+let xulApp = require("sdk/system/xul-app");
 if (xulApp.versionInRange(xulApp.platformVersion, "16.0a1", "*")) {
 new function tests() {
 
 const { indexedDB, IDBKeyRange, DOMException, IDBCursor, IDBTransaction,
         IDBOpenDBRequest, IDBVersionChangeEvent, IDBDatabase, IDBFactory,
         IDBIndex, IDBObjectStore, IDBRequest
-      } = require("indexed-db");
+      } = require("sdk/indexed-db");
 
 exports["test indexedDB is frozen"] = function(assert){
   let original = indexedDB.open;
   let f = function(){};
   assert.throws(function(){indexedDB.open = f});
   assert.equal(indexedDB.open,original);
   assert.notEqual(indexedDB.open,f);
 
deleted file mode 100644
--- a/addon-sdk/source/test/test-layout-change.js
+++ /dev/null
@@ -1,187 +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";
-
-// This test makes sure that require statements used by all AMO hosted
-// add-ons will be able to use old require statements.
-// Tests are based on following usage data:
-// https://docs.google.com/spreadsheet/ccc?key=0ApEBy-GRnGxzdHlRMHJ5RXN1aWJ4RGhINkxSd0FCQXc#gid=0
-
-exports["test compatibility"] = function(assert) {
-  assert.equal(require("self"),
-               require("sdk/self"), "sdk/self -> self");
-
-  assert.equal(require("tabs"),
-               require("sdk/tabs"), "sdk/tabs -> tabs");
-
-  assert.equal(require("widget"),
-               require("sdk/widget"), "sdk/widget -> widget");
-
-  assert.equal(require("page-mod"),
-               require("sdk/page-mod"), "sdk/page-mod -> page-mod");
-
-  assert.equal(require("panel"),
-               require("sdk/panel"), "sdk/panel -> panel");
-
-  assert.equal(require("request"),
-               require("sdk/request"), "sdk/request -> request");
-
-  assert.equal(require("chrome"),
-               require("chrome"), "chrome -> chrome");
-
-  assert.equal(require("simple-storage"),
-               require("sdk/simple-storage"), "sdk/simple-storage -> simple-storage");
-
-  assert.equal(require("context-menu"),
-               require("sdk/context-menu"), "sdk/context-menu -> context-menu");
-
-  assert.equal(require("notifications"),
-               require("sdk/notifications"), "sdk/notifications -> notifications");
-
-  assert.equal(require("preferences-service"),
-               require("sdk/preferences/service"), "sdk/preferences/service -> preferences-service");
-
-  assert.equal(require("window-utils"),
-               require("sdk/deprecated/window-utils"), "sdk/deprecated/window-utils -> window-utils");
-
-  assert.equal(require("url"),
-               require("sdk/url"), "sdk/url -> url");
-
-  assert.equal(require("selection"),
-               require("sdk/selection"), "sdk/selection -> selection");
-
-  assert.equal(require("timers"),
-               require("sdk/timers"), "sdk/timers -> timers");
-
-  assert.equal(require("simple-prefs"),
-               require("sdk/simple-prefs"), "sdk/simple-prefs -> simple-prefs");
-
-  assert.equal(require("traceback"),
-               require("sdk/console/traceback"), "sdk/console/traceback -> traceback");
-
-  assert.equal(require("unload"),
-               require("sdk/system/unload"), "sdk/system/unload -> unload");
-
-  assert.equal(require("hotkeys"),
-               require("sdk/hotkeys"), "sdk/hotkeys -> hotkeys");
-
-  assert.equal(require("clipboard"),
-               require("sdk/clipboard"), "sdk/clipboard -> clipboard");
-
-  assert.equal(require("windows"),
-               require("sdk/windows"), "sdk/windows -> windows");
-
-  assert.equal(require("page-worker"),
-               require("sdk/page-worker"), "sdk/page-worker -> page-worker");
-
-  assert.equal(require("timer"),
-               require("sdk/timers"), "sdk/timers -> timer");
-
-  assert.equal(require("xhr"),
-               require("sdk/net/xhr"), "sdk/io/xhr -> xhr");
-
-  assert.equal(require("observer-service"),
-               require("sdk/deprecated/observer-service"), "sdk/deprecated/observer-service -> observer-service");
-
-  assert.equal(require("private-browsing"),
-               require("sdk/private-browsing"), "sdk/private-browsing -> private-browsing");
-
-  assert.equal(require("passwords"),
-               require("sdk/passwords"), "sdk/passwords -> passwords");
-
-  assert.equal(require("events"),
-               require("sdk/deprecated/events"), "sdk/deprecated/events -> events");
-
-  assert.equal(require("match-pattern"),
-               require("sdk/page-mod/match-pattern"), "sdk/page-mod/match-pattern -> match-pattern");
-
-  assert.equal(require("tab-browser"),
-               require("sdk/deprecated/tab-browser"), "sdk/deprecated/tab-browser -> tab-browser");
-
-  assert.equal(require("file"),
-               require("sdk/io/file"), "sdk/io/file -> file");
-
-  assert.equal(require("xul-app"),
-               require("sdk/system/xul-app"), "sdk/system/xul-app -> xul-app");
-
-  assert.equal(require("api-utils"),
-               require("sdk/deprecated/api-utils"), "sdk/deprecated/api-utils -> api-utils");
-
-  assert.equal(require("runtime"),
-               require("sdk/system/runtime"), "sdk/system/runtime -> runtime");
-
-  assert.equal(require("base64"),
-               require("sdk/base64"), "sdk/base64 -> base64");
-
-  assert.equal(require("xpcom"),
-               require("sdk/platform/xpcom"), "sdk/platform/xpcom -> xpcom");
-
-  assert.equal(require("traits"),
-               require("sdk/deprecated/traits"), "sdk/deprecated/traits -> traits");
-
-  assert.equal(require("keyboard/utils"),
-               require("sdk/keyboard/utils"), "sdk/keyboard/utils -> keyboard/utils");
-
-  assert.equal(require("system"),
-               require("sdk/system"), "sdk/system -> system");
-
-  assert.equal(require("querystring"),
-               require("sdk/querystring"), "sdk/querystring -> querystring");
-
-  assert.equal(require("addon-page"),
-               require("sdk/addon-page"), "sdk/addon-page -> addon-page");
-
-  assert.equal(require("tabs/utils"),
-               require("sdk/tabs/utils"), "sdk/tabs/utils -> tabs/utils");
-
-  assert.equal(require("app-strings"),
-               require("sdk/deprecated/app-strings"), "sdk/deprecated/app-strings -> app-strings");
-
-  assert.equal(require("dom/events"),
-               require("sdk/dom/events"), "sdk/dom/events -> dom/events");
-
-  assert.equal(require("tabs/tab.js"),
-               require("sdk/tabs/tab"), "sdk/tabs/tab -> tabs/tab.js");
-
-  assert.equal(require("memory"),
-               require("sdk/deprecated/memory"), "sdk/deprecated/memory -> memory");
-
-  assert.equal(require("light-traits"),
-               require("sdk/deprecated/light-traits"), "sdk/deprecated/light-traits -> light-traits");
-
-  assert.equal(require("environment"),
-               require("sdk/system/environment"), "sdk/system/environment -> environment");
-
-  assert.equal(require("utils/data"),
-               require("sdk/io/data"), "sdk/io/data -> utils/data");
-
-  assert.equal(require("test/assert"),
-               require("sdk/test/assert"), "sdk/test/assert -> test/assert");
-
-  assert.equal(require("hidden-frame"),
-               require("sdk/frame/hidden-frame"), "sdk/frame/hidden-frame -> hidden-frame");
-
-  assert.equal(require("collection"),
-               require("sdk/util/collection"), "sdk/util/collection -> collection");
-
-  assert.equal(require("array"),
-               require("sdk/util/array"), "sdk/util/array -> array");
-
-  assert.equal(require("api-utils/cortex"),
-               require("sdk/deprecated/cortex"),
-               "api-utils/cortex -> sdk/deprecated/cortex");
-};
-
-if (require("sdk/system/xul-app").is("Fennec")) {
-  module.exports = {
-    "test Unsupported Test": function UnsupportedTest (assert) {
-        assert.pass(
-          "Skipping this test until Fennec support is implemented." +
-          "See bug 809352");
-    }
-  }
-}
-
-require("test").run(exports);
--- a/addon-sdk/source/test/test-loader.js
+++ b/addon-sdk/source/test/test-loader.js
@@ -1,35 +1,33 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 'use strict';
 
 let { Loader, main, unload, parseStack } = require('toolkit/loader');
 
+let root = module.uri.substr(0, module.uri.lastIndexOf('/'))
+
 exports['test dependency cycles'] = function(assert) {
-  let uri = module.uri.substr(0, module.uri.lastIndexOf('/')) +
-            '/fixtures/loader/cycles/'
-
+  let uri = root + '/fixtures/loader/cycles/';
   let loader = Loader({ paths: { '': uri } });
 
   let program = main(loader, 'main');
 
   assert.equal(program.a.b, program.b, 'module `a` gets correct `b`');
   assert.equal(program.b.a, program.a, 'module `b` gets correct `a`');
   assert.equal(program.c.main, program, 'module `c` gets correct `main`');
 
   unload(loader);
-};
+}
 
 exports['test syntax errors'] = function(assert) {
-  let uri = module.uri.substr(0, module.uri.lastIndexOf('/')) +
-            '/fixtures/loader/syntax-error/';
-
+  let uri = root + '/fixtures/loader/syntax-error/';
   let loader = Loader({ paths: { '': uri } });
 
   try {
     let program = main(loader, 'main');
   } catch (error) {
     assert.equal(error.name, "SyntaxError", "throws syntax error");
     assert.equal(error.fileName.split("/").pop(), "error.js",
               "Error contains filename");
@@ -38,22 +36,20 @@ exports['test syntax errors'] = function
     assert.equal(stack.pop().fileName, uri + "main.js",
                  "loader stack is omitted");
     assert.equal(stack.pop().fileName, module.uri,
                  "previous in the stack is test module");
 
   } finally {
     unload(loader);
   }
-};
+}
 
 exports['test missing module'] = function(assert) {
-  let uri = module.uri.substr(0, module.uri.lastIndexOf('/')) +
-            '/fixtures/loader/missing/'
-
+  let uri = root + '/fixtures/loader/missing/'
   let loader = Loader({ paths: { '': uri } });
 
   try {
     let program = main(loader, 'main')
   } catch (error) {
     assert.equal(error.message, "Module `not-found` is not found at " +
                 uri + "not-found.js", "throws if error not found");
 
@@ -70,18 +66,17 @@ exports['test missing module'] = functio
     assert.equal(stack.pop().fileName, module.uri,
                  "previous in the stack is test module");
   } finally {
     unload(loader);
   }
 }
 
 exports['test exceptions in modules'] = function(assert) {
-  let uri = module.uri.substr(0, module.uri.lastIndexOf('/')) +
-            '/fixtures/loader/exceptions/'
+  let uri = root + '/fixtures/loader/exceptions/'
 
   let loader = Loader({ paths: { '': uri } });
 
   try {
     let program = main(loader, 'main')
   } catch (error) {
     assert.equal(error.message, "Boom!", "thrown errors propagate");
 
@@ -107,19 +102,17 @@ exports['test exceptions in modules'] = 
     assert.equal(stack.pop().fileName, module.uri,
                  "this test module is next in the stack");
   } finally {
     unload(loader);
   }
 }
 
 exports['test early errors in module'] = function(assert) {
-  let uri = module.uri.substr(0, module.uri.lastIndexOf('/')) +
-            '/fixtures/loader/errors/'
-
+  let uri = root + '/fixtures/loader/errors/';
   let loader = Loader({ paths: { '': uri } });
 
   try {
     let program = main(loader, 'main')
   } catch (error) {
     assert.equal(String(error),
                  "Error: opening input stream (invalid filename?)",
                  "thrown errors propagate");
--- a/addon-sdk/source/test/test-net-url.js
+++ b/addon-sdk/source/test/test-net-url.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 { readURI, readURISync } = require("sdk/net/url");
-const { data } = require("self");
+const { data } = require("sdk/self");
 
 const utf8text = "Hello, ゼロ!";
 const latin1text = "Hello, ゼロ!";
 
 const dataURIutf8 = "data:text/plain;charset=utf-8," + encodeURIComponent(utf8text);
 const dataURIlatin1 = "data:text/plain;charset=ISO-8859-1," + escape(latin1text);
 const chromeURI = "chrome://global-platform/locale/accessible.properties";
 
--- a/addon-sdk/source/test/test-page-mod.js
+++ b/addon-sdk/source/test/test-page-mod.js
@@ -7,17 +7,17 @@ var pageMod = require("sdk/page-mod");
 var testPageMod = require("./pagemod-test-helpers").testPageMod;
 const { Loader } = require('sdk/test/loader');
 const tabs = require("sdk/tabs");
 const timer = require("sdk/timers");
 const { Cc, Ci } = require("chrome");
 const { open, getFrames, getMostRecentBrowserWindow } = require('sdk/window/utils');
 const windowUtils = require('sdk/deprecated/window-utils');
 const { getTabContentWindow, getActiveTab, openTab, closeTab } = require('sdk/tabs/utils');
-const { data } = require('self');
+const { data } = require('sdk/self');
 
 /* XXX This can be used to delay closing the test Firefox instance for interactive
  * testing or visual inspection. This test is registered first so that it runs
  * the last. */
 exports.delay = function(test) {
   if (false) {
     test.waitUntilDone(60000);
     timer.setTimeout(function() {test.done();}, 4000);
@@ -352,16 +352,37 @@ exports.testRelatedTab = function(test) 
   tabs.open({
     url: "about:",
     onOpen: function onOpen(t) {
       tab = t;
     }
   });
 };
 
+exports.testRelatedTabNoRequireTab = function(test) {
+  test.waitUntilDone();
+
+  let loader = Loader(module);
+  let tab;
+  let url = "data:text/html;charset=utf-8," + encodeURI("Test related worker tab 2");
+  let { PageMod } = loader.require("sdk/page-mod");
+  let pageMod = new PageMod({
+    include: url,
+    onAttach: function(worker) {
+      test.assertEqual(worker.tab.url, url, "Worker.tab.url is valid");
+      worker.tab.close();
+      pageMod.destroy();
+      loader.unload();
+      test.done();
+    }
+  });
+
+  tabs.open(url);
+};
+
 exports.testRelatedTabNoOtherReqs = function(test) {
   test.waitUntilDone();
 
   let loader = Loader(module);
   let { PageMod } = loader.require("sdk/page-mod");
   let pageMod = new PageMod({
     include: "about:*",
     onAttach: function(worker) {
@@ -628,17 +649,17 @@ exports['test111 attachTo [frame]'] = fu
       test.pass("worker on first frame");
     else if (href == subFrameURL)
       test.pass("worker on second frame");
     else
       test.fail("worker on unexpected document: " + href);
     this.destroy();
     if (++messageCount == 2) {
       mod.destroy();
-      require('tabs').activeTab.close(function() {
+      require('sdk/tabs').activeTab.close(function() {
         test.done();
       });
     }
   }
   let mod = PageMod({
     include: 'data:text/html*',
     contentScriptWhen: 'start',
     contentScript: 'self.postMessage(document.location.href);',
@@ -787,17 +808,17 @@ exports.testPageModCssDestroy = function
     }
   );
 };
 
 exports.testPageModCssAutomaticDestroy = function(test) {
   test.waitUntilDone();
   let loader = Loader(module);
 
-  let pageMod = loader.require("page-mod").PageMod({
+  let pageMod = loader.require("sdk/page-mod").PageMod({
     include: "data:*",
     contentStyle: "div { width: 100px!important; }"
   });
 
   tabs.open({
     url: "data:text/html;charset=utf-8,<div style='width:200px'>css test</div>",
 
     onReady: function onReady(tab) {
@@ -827,17 +848,17 @@ exports.testPageModCssAutomaticDestroy =
   });
 };
 
 
 exports.testPageModTimeout = function(test) {
   test.waitUntilDone();
   let tab = null
   let loader = Loader(module);
-  let { PageMod } = loader.require("page-mod");
+  let { PageMod } = loader.require("sdk/page-mod");
 
   let mod = PageMod({
     include: "data:*",
     contentScript: Isolate(function() {
       var id = setTimeout(function() {
         self.port.emit("fired", id)
       }, 10)
       self.port.emit("scheduled", id);
@@ -862,17 +883,17 @@ exports.testPageModTimeout = function(te
   })
 }
 
 
 exports.testPageModcancelTimeout = function(test) {
   test.waitUntilDone();
   let tab = null
   let loader = Loader(module);
-  let { PageMod } = loader.require("page-mod");
+  let { PageMod } = loader.require("sdk/page-mod");
 
   let mod = PageMod({
     include: "data:*",
     contentScript: Isolate(function() {
       var id1 = setTimeout(function() {
         self.port.emit("failed")
       }, 10)
       var id2 = setTimeout(function() {
@@ -1024,9 +1045,8 @@ if (require("sdk/system/xul-app").is("Fe
   module.exports = {
     "test Unsupported Test": function UnsupportedTest (test) {
         test.pass(
           "Skipping this test until Fennec support is implemented." +
           "See bug 784224");
     }
   }
 }
-
--- a/addon-sdk/source/test/test-panel.js
+++ b/addon-sdk/source/test/test-panel.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/. */
 
 let { Cc, Ci } = require("chrome");
 const { Loader } = require('sdk/test/loader');
 const timer = require("sdk/timers");
-const self = require('self');
+const self = require('sdk/self');
 
 exports["test Panel"] = function(assert, done) {
   const { Panel } = require('sdk/panel');
 
   let panel = Panel({
     contentURL: "about:buildconfig",
     contentScript: "self.postMessage(1); self.on('message', function() self.postMessage(2));",
     onMessage: function (message) {
--- a/addon-sdk/source/test/test-private-browsing.js
+++ b/addon-sdk/source/test/test-private-browsing.js
@@ -1,31 +1,87 @@
 /* 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 { pb, pbUtils } = require('./private-browsing/helper');
+const { Ci } = require('chrome');
+const { pb, pbUtils, getOwnerWindow } = require('./private-browsing/helper');
 const { merge } = require('sdk/util/object');
 const windows = require('sdk/windows').browserWindows;
 const winUtils = require('sdk/window/utils');
+const { is } = require('sdk/system/xul-app');
+const { isPrivate } = require('sdk/private-browsing');
 
 // is global pb is enabled?
 if (pbUtils.isGlobalPBSupported) {
   merge(module.exports, require('./private-browsing/global'));
+
+  exports.testGlobalOnlyOnFirefox = function(test) {
+    test.assert(is("Firefox"), "isGlobalPBSupported is only true on Firefox");
+  }
 }
 else if (pbUtils.isWindowPBSupported) {
   merge(module.exports, require('./private-browsing/windows'));
+
+  exports.testPWOnlyOnFirefox = function(test) {
+    test.assert(is("Firefox"), "isWindowPBSupported is only true on Firefox");
+  }
 }
+// only on Fennec
+else if (pbUtils.isTabPBSupported) {
+  merge(module.exports, require('./private-browsing/tabs'));
+
+  exports.testPTOnlyOnFennec = function(test) {
+    test.assert(is("Fennec"), "isTabPBSupported is only true on Fennec");
+  }
+}
+
+exports.testIsPrivateDefaults = function(test) {
+  test.assertEqual(pb.isPrivate(), false, 'undefined is not private');
+  test.assertEqual(pb.isPrivate('test'), false, 'strings are not private');
+  test.assertEqual(pb.isPrivate({}), false, 'random objects are not private');
+  test.assertEqual(pb.isPrivate(4), false, 'numbers are not private');
+  test.assertEqual(pb.isPrivate(/abc/), false, 'regex are not private');
+  test.assertEqual(pb.isPrivate(function() {}), false, 'functions are not private');
+};
 
 exports.testWindowDefaults = function(test) {
   test.assertEqual(windows.activeWindow.isPrivateBrowsing, false, 'window is not private browsing by default');
   let chromeWin = winUtils.getMostRecentBrowserWindow();
   test.assertEqual(pbUtils.getMode(chromeWin), false);
   test.assertEqual(pbUtils.isWindowPrivate(chromeWin), false);
 }
 
 // tests for the case where private browsing doesn't exist
 exports.testIsActiveDefault = function(test) {
   test.assertEqual(pb.isActive, false,
                    'pb.isActive returns false when private browsing isn\'t supported');
 };
 
+exports.testGetOwnerWindow = function(test) {
+  test.waitUntilDone();
+
+  let window = windows.activeWindow;
+  let chromeWindow = getOwnerWindow(window);
+  test.assert(chromeWindow instanceof Ci.nsIDOMWindow, 'associated window is found');
+
+  window.tabs.open({
+    url: 'about:blank',
+    private: true, // should be ignored in this case
+    onOpen: function(tab) {
+      // test that getOwnerWindow works as expected
+      if (is('Fennec')) {
+        test.assertNotStrictEqual(chromeWindow, getOwnerWindow(tab)); 
+        test.assert(getOwnerWindow(tab) instanceof Ci.nsIDOMWindow); 
+      }
+      else {
+        test.assertStrictEqual(chromeWindow, getOwnerWindow(tab), 'associated window is the same for window and window\'s tab');
+      }
+
+      // test that the tab is not private
+      // private flag should be ignored by default
+      test.assert(!isPrivate(tab));
+
+      tab.close(function() test.done());
+    }
+  });
+}
--- a/addon-sdk/source/test/test-request.js
+++ b/addon-sdk/source/test/test-request.js
@@ -10,16 +10,19 @@ const { Loader } = require("sdk/test/loa
 const options = require("@test/options");
 
 const loader = Loader(module);
 const httpd = loader.require("sdk/test/httpd");
 if (options.parseable || options.verbose)
   loader.sandbox("sdk/test/httpd").DEBUG = true;
 const { startServerAsync } = httpd;
 
+const { Cc, Ci, Cu } = require("chrome");
+const { Services } = Cu.import("resource://gre/modules/Services.jsm");
+
 // Use the profile directory for the temporary files as that will be deleted
 // when tests are complete
 const basePath = pathFor("ProfD")
 const port = 8099;
 
 
 exports.testOptionsValidator = function(test) {
   // First, a simple test to make sure we didn't break normal functionality.
@@ -135,16 +138,65 @@ exports.testComplexHeader = function (te
       for (k in headers) {
         test.assertEqual(response.headers[k], headers[k]);
       }
       srv.stop(function() test.done());
     }
   }).get();
 }
 
+// Force Allow Third Party cookies
+exports.test3rdPartyCookies = function (test) {
+  let srv = startServerAsync(port, basePath);
+
+  let basename = "test-request-3rd-party-cookies.sjs";
+
+  // Function to handle the requests in the server
+  let content = function handleRequest(request, response) {
+    var cookiePresent = request.hasHeader("Cookie");
+    // If no cookie, set it
+    if(!cookiePresent) {
+      response.setHeader("Set-Cookie", "cookie=monster;", "true");
+      response.setHeader("x-jetpack-3rd-party", "false", "true");
+    } else {
+      // We got the cookie, say so
+      response.setHeader("x-jetpack-3rd-party", "true", "true");
+    }
+
+    response.write("<html><body>This tests 3rd party cookies.</body></html>");
+  }.toString()
+
+  prepareFile(basename, content);
+
+  // Disable the 3rd party cookies
+  Services.prefs.setIntPref("network.cookie.cookieBehavior", 1);
+
+  test.waitUntilDone();
+  Request({
+    url: "http://localhost:" + port + "/test-request-3rd-party-cookies.sjs",
+    onComplete: function (response) {
+      // Check that the server created the cookie
+      test.assertEqual(response.headers['Set-Cookie'], 'cookie=monster;');
+
+      // Check it wasn't there before
+      test.assertEqual(response.headers['x-jetpack-3rd-party'], 'false');
+
+      // Make a second request, and check that the server this time
+      // got the cookie
+      Request({
+        url: "http://localhost:" + port + "/test-request-3rd-party-cookies.sjs",
+        onComplete: function (response) {
+          test.assertEqual(response.headers['x-jetpack-3rd-party'], 'true');
+          srv.stop(function() test.done());
+        }
+      }).get();
+    }
+  }).get();
+}
+
 exports.testSimpleJSON = function (test) {
   let srv = startServerAsync(port, basePath);
   let json = { foo: "bar" };
   let basename = "test-request.json";
   prepareFile(basename, JSON.stringify(json));
 
   test.waitUntilDone();
   Request({
--- a/addon-sdk/source/test/test-selection.js
+++ b/addon-sdk/source/test/test-selection.js
@@ -9,22 +9,26 @@ const HTML = "<html>\
     <div>foo</div>\
     <div>and</div>\
     <textarea>noodles</textarea>\
   </body>\
 </html>";
 
 const URL = "data:text/html;charset=utf-8," + encodeURIComponent(HTML);
 
+const FRAME_HTML = "<iframe src='" + URL + "'><iframe>";
+const FRAME_URL = "data:text/html;charset=utf-8," + encodeURIComponent(FRAME_HTML);
+
 const { defer } = require("sdk/core/promise");
 const tabs = require("sdk/tabs");
 const { getActiveTab, getTabContentWindow, closeTab } = require("sdk/tabs/utils")
 const { getMostRecentBrowserWindow } = require("sdk/window/utils");
 const { Loader } = require("sdk/test/loader");
 const { setTimeout } = require("sdk/timers");
+const { Cu } = require("chrome");
 
 // General purpose utility functions
 
 /**
  * Opens the url given and return a promise, that will be resolved with the
  * content window when the document is ready.
  *
  * I believe this approach could be useful in most of our unit test, that
@@ -78,16 +82,65 @@ function reload(window) {
   window.location.reload(true);
 
   return promise;
 }
 
 // Selection's unit test utility function
 
 /**
+ * Returns the frame's window once the document is loaded
+ */
+function getFrameWindow(window) {
+  let { promise, resolve } = defer();
+
+  let frame = window.frames[0];
+  let { document } = frame;
+
+  frame.focus();
+
+  if (document.readyState === "complete")
+    return frame;
+
+  document.addEventListener("readystatechange", function readystate() {
+    if (this.readyState === "complete") {
+      this.removeEventListener("readystatechange", readystate);
+      frame.focus();
+      resolve(frame);
+    }
+  });
+
+  return promise;
+}
+
+/**
+ * Hide the frame in order to destroy the selection object, and show it again
+ * after ~500 msec, to give time to attach the code on `document-shown`
+ * notification.
+ * In the process, call `Cu.forgeGC` to ensure that the `document-shown` code
+ * is not garbaged.
+ */
+function hideAndShowFrame(window) {
+  let { promise, resolve } = defer();
+  let iframe = window.document.querySelector("iframe");
+
+  iframe.style.display = "none";
+
+  Cu.forceGC();
+
+  setTimeout(function(){
+    iframe.style.display = "";
+
+    setTimeout(resolve, 500, window);
+  }, 0)
+
+  return promise;
+}
+
+/**
  * Select the first div in the page, adding the range to the selection.
  */
 function selectFirstDiv(window) {
   let div = window.document.querySelector("div");
   let selection = window.getSelection();
   let range = window.document.createRange();
 
   if (selection.rangeCount > 0)
@@ -169,17 +222,17 @@ function dispatchSelectionEvent(window) 
  */
 function dispatchOnSelectEvent(window) {
   let { document } = window;
   let textarea = document.querySelector("textarea");
   let event = document.createEvent("UIEvents");
 
   event.initUIEvent("select", true, true, window, 1);
 
-  textarea.dispatchEvent(event)
+  textarea.dispatchEvent(event);
 }
 
 /**
  * Creates empty ranges and add them to selections
  */
 function createEmptySelections(window) {
   selectAllDivs(window);
 
@@ -725,16 +778,52 @@ exports["test Textarea OnSelect Listener
   open(URL).
     then(reload).
     then(selectTextarea).
     then(dispatchOnSelectEvent).
     then(close).
     then(loader.unload);
 };
 
+exports["test Selection Listener on frame"] = function(assert, done) {
+  let loader = Loader(module);
+  let selection = loader.require("sdk/selection");
+
+  selection.once("select", function() {
+    assert.equal(selection.text, "fo");
+    done();
+  });
+
+  open(FRAME_URL).
+    then(hideAndShowFrame).
+    then(getFrameWindow).
+    then(selectContentFirstDiv).
+    then(dispatchSelectionEvent).
+    then(close).
+    then(loader.unload)
+};
+
+exports["test Textarea onSelect Listener on frame"] = function(assert, done) {
+  let loader = Loader(module);
+  let selection = loader.require("sdk/selection");
+
+  selection.once("select", function() {
+    assert.equal(selection.text, "noodles");
+    done();
+  });
+
+  open(FRAME_URL).
+    then(hideAndShowFrame).
+    then(getFrameWindow).
+    then(selectTextarea).
+    then(dispatchOnSelectEvent).
+    then(close).
+    then(loader.unload)
+};
+
 // TODO: test Selection Listener on long-held connection (Bug 661884)
 //
 //  I didn't find a way to do so with httpd, using `processAsync` I'm able to
 //  Keep the connection but not to flush the buffer to the client in two steps,
 //  that is what I need for this test (e.g. flush "Hello" to the client, makes
 //  selection when the connection is still hold, and check that the listener
 //  is executed before the server send "World" and close the connection).
 //
--- a/addon-sdk/source/test/test-simple-prefs.js
+++ b/addon-sdk/source/test/test-simple-prefs.js
@@ -194,28 +194,28 @@ exports.testPrefUnloadListener = functio
 };
 
 
 // Bug 710117: Test that simple-pref listeners are removed on unload
 exports.testPrefUnloadWildcardListener = function(test) {
   test.waitUntilDone();
   let testpref = "test-wildcard-unload-listener";
   let loader = Loader(module);
-  let sp = loader.require("simple-prefs");
+  let sp = loader.require("sdk/simple-prefs");
   let counter = 0;
 
   let listener = function() {
     test.assertEqual(++counter, 1, "This listener should only be called once");
 
     loader.unload();
 
     // this may not execute after unload, but definitely shouldn't fire listener
     sp.prefs[testpref] = false;
     // this should execute, but also definitely shouldn't fire listener
-    require("simple-prefs").prefs[testpref] = false;
+    require("sdk/simple-prefs").prefs[testpref] = false;
 
     test.done();
   };
 
   sp.on("", listener);
   // emit change
   sp.prefs[testpref] = true;
 };
--- a/addon-sdk/source/test/test-tab.js
+++ b/addon-sdk/source/test/test-tab.js
@@ -110,17 +110,18 @@ function step3(assert, done) {
 }
 
 exports["test behavior on close"] = function(assert, done) {
 
   tabs.open({
     url: "about:mozilla",
     onReady: function(tab) {
       assert.equal(tab.url, "about:mozilla", "Tab has the expected url");
-      assert.equal(tab.index, 1, "Tab has the expected index");
+      // if another test ends before closing a tab then index != 1 here
+      assert.ok(tab.index >= 1, "Tab has the expected index, a value greater than 0");
       tab.close(function () {
         assert.equal(tab.url, undefined,
                      "After being closed, tab attributes are undefined (url)");
         assert.equal(tab.index, undefined,
                      "After being closed, tab attributes are undefined (index)");
         // Ensure that we can call destroy multiple times without throwing
         tab.destroy();
         tab.destroy();
--- a/addon-sdk/source/test/test-url.js
+++ b/addon-sdk/source/test/test-url.js
@@ -1,14 +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/. */
 
 var url = require("sdk/url");
-var { packed } = require("sdk/self");
 
 exports.testResolve = function(test) {
   test.assertEqual(url.URL("bar", "http://www.foo.com/").toString(),
                    "http://www.foo.com/bar");
 
   test.assertEqual(url.URL("bar", "http://www.foo.com"),
                    "http://www.foo.com/bar");
 
@@ -81,26 +80,16 @@ exports.testParseFTPWithUserPass = funct
 
 exports.testToFilename = function(test) {
   test.assertRaises(
     function() { url.toFilename("resource://nonexistent"); },
     "resource does not exist: resource://nonexistent/",
     "url.toFilename() on nonexistent resources should throw"
   );
 
-  if (!packed)
-    test.assertMatches(url.toFilename(module.uri),
-                       /.*test-url\.js$/,
-                       "url.toFilename() on resource: URIs should work");
-  else
-    test.assertRaises(
-      function() { url.toFilename(module.uri); },
-      "cannot map to filename: "+module.uri,
-      "url.toFilename() can fail for packed XPIs");
-
   test.assertRaises(
     function() { url.toFilename("http://foo.com/"); },
     "cannot map to filename: http://foo.com/",
     "url.toFilename() on http: URIs should raise error"
   );
 
   try {
     test.assertMatches(
--- a/addon-sdk/source/test/test-widget.js
+++ b/addon-sdk/source/test/test-widget.js
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { Cc, Ci } = require("chrome");
 const { Loader } = require('sdk/test/loader');
 const url = require("sdk/url");
 const timer = require("sdk/timers");
-const self = require("self");
+const self = require("sdk/self");
 const windowUtils = require("sdk/deprecated/window-utils");
 
 exports.testConstructor = function(test) {
   test.waitUntilDone();
 
   let browserWindow = windowUtils.activeBrowserWindow;
   let doc = browserWindow.document;
   let AddonsMgrListener = browserWindow.AddonsMgrListener;
@@ -1024,17 +1024,17 @@ exports.testPostMessageOnLocationChange 
 };
 
 exports.testSVGWidget = function(test) {
   test.waitUntilDone();
 
   // use of capital SVG here is intended, that was failing..
   let SVG_URL = self.data.url("mofo_logo.SVG");
 
-  let widget = require("widget").Widget({
+  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');
--- a/addon-sdk/source/test/windows/test-firefox-windows.js
+++ b/addon-sdk/source/test/windows/test-firefox-windows.js
@@ -5,17 +5,18 @@
 
 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 tabs = require("tabs");
+const tabs = require("sdk/tabs");
+const { WindowTracker } = require("sdk/deprecated/window-utils");
 
 // TEST: open & close window
 exports.testOpenAndCloseWindow = function(test) {
   test.waitUntilDone();
 
   test.assertEqual(browserWindows.length, 1, "Only one window open");
 
   browserWindows.open({
@@ -198,65 +199,74 @@ exports.testActiveWindow = function(test
   let testSteps = [
     function() {
       test.assertEqual(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");
 
+      test.assertEqual(windows.activeWindow.title, window3.title, "Correct active window - 3");
+
+      continueAfterFocus(rawWindow2);
       rawWindow2.focus();
-      continueAfterFocus(rawWindow2);
     },
     function() {
       nextStep();
     },
     function() {
-      /**
-       * Bug 614079: This test fails intermittently on some specific linux
-       *             environnements, without being able to reproduce it in same
-       *             distribution with same window manager.
-       *             Disable it until being able to reproduce it easily.
+      test.assertEqual(windows.activeWindow.title, window2.title, "Correct active window - 2");
 
-      // On linux, focus is not consistent, so we can't be sure
-      // what window will be on top.
-      // Here when we focus "non-browser" window,
-      // Any Browser window may be selected as "active".
-      test.assert(windows.activeWindow == window2 || windows.activeWindow == window3,
-        "Non-browser windows aren't handled by this module");
-      */
+      continueAfterFocus(rawWindow2);
       window2.activate();
-      continueAfterFocus(rawWindow2);
     },
     function() {
       test.assertEqual(windows.activeWindow.title, window2.title, "Correct active window - 2");
+      continueAfterFocus(rawWindow3);
       window3.activate();
-      continueAfterFocus(rawWindow3);
     },
     function() {
       test.assertEqual(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) {
-      window2 = window;
-      rawWindow2 = wm.getMostRecentWindow("navigator:browser");
+      window.tabs.activeTab.on('ready', function() {
+        window2 = window;
+        test.assert(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");
 
-      windows.open({
-        url: "data:text/html;charset=utf-8,<title>window 3</title>",
-        onOpen: function(window) {
-          window.tabs.activeTab.on('ready', function onReady() {
-            window3 = window;
-            rawWindow3 = wm.getMostRecentWindow("navigator:browser");
-            nextStep()
-          });
-        }
+        windows.open({
+          url: "data:text/html;charset=utf-8,<title>window 3</title>",
+          onOpen: function(window) {
+            window.tabs.activeTab.on('ready', function onReady() {
+              window3 = window;
+              test.assert(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");
+              continueAfterFocus(rawWindow3);
+              rawWindow3.focus();
+            });
+          }
+        });
       });
     }
   });
 
   function nextStep() {
     if (testSteps.length > 0)
       testSteps.shift()();
   }
@@ -273,21 +283,21 @@ exports.testActiveWindow = function(test
     var focusedChildWindow = {};
     if (fm.activeWindow) {
       fm.getFocusedElementForWindow(fm.activeWindow, true, focusedChildWindow);
       focusedChildWindow = focusedChildWindow.value;
     }
 
     var focused = (focusedChildWindow == childTargetWindow);
     if (focused) {
-      nextStep();
+      setTimeout(nextStep, 0);
     } else {
       childTargetWindow.addEventListener("focus", function focusListener() {
         childTargetWindow.removeEventListener("focus", focusListener, true);
-        nextStep();
+        setTimeout(nextStep, 0);
       }, true);
     }
 
   }
 
   function finishTest() {
     window3.close(function() {
       window2.close(function() {