Bug 855831: Uplift Add-on SDK changeset bb071f7ac300d9a3d8227148153c892efc94f0b7.
authorDave Townsend <dtownsend@oxymoronical.com>
Thu, 28 Mar 2013 14:42:30 -0700
changeset 137042 f5bfd7b0b6bc558c5d6ae298a30bef979199151b
parent 137041 29c3bf1d142ea1ae49f614262b4db2b53f0cc7e4
child 137043 6d3525785f9fc1074a7c08eeaeda80c9b9c82a8b
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)
bugs855831
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 855831: Uplift Add-on SDK changeset bb071f7ac300d9a3d8227148153c892efc94f0b7. https://github.com/mozilla/addon-sdk/compare/4e4e29d...bb071f7ac3
addon-sdk/source/app-extension/bootstrap.js
addon-sdk/source/app-extension/install.rdf
addon-sdk/source/doc/dev-guide-source/credits.md
addon-sdk/source/doc/dev-guide-source/guides/content-scripts/using-postmessage.md
addon-sdk/source/doc/dev-guide-source/guides/events.md
addon-sdk/source/doc/dev-guide-source/package-spec.md
addon-sdk/source/doc/dev-guide-source/tutorials/annotator/overview.md
addon-sdk/source/doc/dev-guide-source/tutorials/annotator/storing.md
addon-sdk/source/doc/dev-guide-source/tutorials/event-targets.md
addon-sdk/source/doc/module-source/sdk/context-menu.md
addon-sdk/source/doc/module-source/sdk/page-mod.md
addon-sdk/source/doc/module-source/sdk/panel.md
addon-sdk/source/doc/module-source/sdk/private-browsing.md
addon-sdk/source/doc/module-source/sdk/selection.md
addon-sdk/source/doc/module-source/sdk/self.md
addon-sdk/source/doc/module-source/sdk/simple-storage.md
addon-sdk/source/doc/module-source/sdk/system/events.md
addon-sdk/source/doc/module-source/sdk/tabs.md
addon-sdk/source/doc/module-source/sdk/tabs/utils.md
addon-sdk/source/doc/module-source/sdk/widget.md
addon-sdk/source/doc/module-source/sdk/window/utils.md
addon-sdk/source/doc/module-source/sdk/windows.md
addon-sdk/source/examples/annotator/lib/main.js
addon-sdk/source/lib/sdk/content/content-worker.js
addon-sdk/source/lib/sdk/content/worker.js
addon-sdk/source/lib/sdk/deprecated/window-utils.js
addon-sdk/source/lib/sdk/event/core.js
addon-sdk/source/lib/sdk/frame/hidden-frame.js
addon-sdk/source/lib/sdk/panel.js
addon-sdk/source/lib/sdk/panel/window.js
addon-sdk/source/lib/sdk/private-browsing/utils.js
addon-sdk/source/lib/sdk/tabs/tab-fennec.js
addon-sdk/source/lib/sdk/tabs/utils.js
addon-sdk/source/lib/sdk/test/loader.js
addon-sdk/source/lib/sdk/util/array.js
addon-sdk/source/lib/sdk/view/core.js
addon-sdk/source/lib/sdk/widget.js
addon-sdk/source/lib/sdk/window/browser.js
addon-sdk/source/lib/sdk/windows/fennec.js
addon-sdk/source/lib/sdk/windows/tabs-fennec.js
addon-sdk/source/lib/sdk/windows/tabs-firefox.js
addon-sdk/source/python-lib/cuddlefish/prefs.py
addon-sdk/source/python-lib/cuddlefish/rdf.py
addon-sdk/source/python-lib/cuddlefish/tests/test_init.py
addon-sdk/source/test/addons/layout-change/main.js
addon-sdk/source/test/addons/private-browsing-supported/main.js
addon-sdk/source/test/addons/private-browsing-supported/test-global-private-browsing.js
addon-sdk/source/test/addons/private-browsing-supported/test-private-browsing.js
addon-sdk/source/test/addons/require/main.js
addon-sdk/source/test/addons/require/packages/tabs/main.js
addon-sdk/source/test/addons/require/packages/tabs/package.json
addon-sdk/source/test/addons/require/packages/tabs/page-mod.js
addon-sdk/source/test/addons/require/tabs.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/test-content-worker.js
addon-sdk/source/test/test-event-core.js
addon-sdk/source/test/test-hidden-frame.js
addon-sdk/source/test/test-page-mod.js
addon-sdk/source/test/test-page-worker.js
addon-sdk/source/test/test-panel.js
addon-sdk/source/test/test-tab-utils.js
addon-sdk/source/test/test-tabs-common.js
addon-sdk/source/test/test-window-utils-global-private-browsing.js
addon-sdk/source/test/test-window-utils-private-browsing.js
--- a/addon-sdk/source/app-extension/bootstrap.js
+++ b/addon-sdk/source/app-extension/bootstrap.js
@@ -15,17 +15,18 @@ const { classes: Cc, Constructor: CC, in
 const ioService = Cc['@mozilla.org/network/io-service;1'].
                   getService(Ci.nsIIOService);
 const resourceHandler = ioService.getProtocolHandler('resource').
                         QueryInterface(Ci.nsIResProtocolHandler);
 const systemPrincipal = CC('@mozilla.org/systemprincipal;1', 'nsIPrincipal')();
 const scriptLoader = Cc['@mozilla.org/moz/jssubscript-loader;1'].
                      getService(Ci.mozIJSSubScriptLoader);
 const prefService = Cc['@mozilla.org/preferences-service;1'].
-                    getService(Ci.nsIPrefService);
+                    getService(Ci.nsIPrefService).
+                    QueryInterface(Ci.nsIPrefBranch);
 const appInfo = Cc["@mozilla.org/xre/app-info;1"].
                 getService(Ci.nsIXULAppInfo);
 const vc = Cc["@mozilla.org/xpcom/version-comparator;1"].
            getService(Ci.nsIVersionComparator);
 
 
 const REASON = [ 'unknown', 'startup', 'shutdown', 'enable', 'disable',
                  'install', 'uninstall', 'upgrade', 'downgrade' ];
@@ -122,22 +123,31 @@ function startup(data, reasonCode) {
       return result;
     }, paths);
 
     // 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/';
 
+    let useBundledSDK = options['force-use-bundled-sdk'];
+    if (!useBundledSDK) {
+      try {
+        useBundledSDK = prefService.getBoolPref("extensions.addon-sdk.useBundledSDK");
+      }
+      catch (e) {
+        // Pref doesn't exist, allow using Firefox shipped SDK
+      }
+    }
+
     // Starting with Firefox 21.0a1, we start using modules shipped into firefox
     // Still allow using modules from the xpi if the manifest tell us to do so.
     // And only try to look for sdk modules in xpi if the xpi actually ship them
     if (options['is-sdk-bundled'] &&
-        (vc.compare(appInfo.version, '21.0a1') < 0 ||
-         options['force-use-bundled-sdk'])) {
+        (vc.compare(appInfo.version, '21.0a1') < 0 || useBundledSDK)) {
       // Maps sdk module folders to their resource folder
       paths[''] = prefixURI + 'addon-sdk/lib/';
       // 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';
     }
 
--- a/addon-sdk/source/app-extension/install.rdf
+++ b/addon-sdk/source/app-extension/install.rdf
@@ -12,18 +12,18 @@
     <em:type>2</em:type>
     <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>21.0a1</em:maxVersion>
+        <em:minVersion>19.0</em:minVersion>
+        <em:maxVersion>22.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/doc/dev-guide-source/credits.md
+++ b/addon-sdk/source/doc/dev-guide-source/credits.md
@@ -130,17 +130,17 @@ We'd like to thank our many Jetpack proj
 * Paul O’Shannessy
 * Les Orchard
 
 <!--end-->
 
 ### P ###
 
 * Robert Pankowecki
-* [Jamie Phelps](https://github.com/ongaeshi)
+* [Jamie Phelps](https://github.com/jxpx777)
 * [Alexandre Poirot](https://github.com/ochameau)
 * Nickolay Ponomarev
 
 <!--end-->
 
 ### R ###
 
 * Aza Raskin
--- a/addon-sdk/source/doc/dev-guide-source/guides/content-scripts/using-postmessage.md
+++ b/addon-sdk/source/doc/dev-guide-source/guides/content-scripts/using-postmessage.md
@@ -54,60 +54,16 @@ To simplify this most content modules pr
 argument to the constructor:
 
     panel = require("sdk/panel").Panel({
       onMessage: function(contentScriptMessage) {
         // Handle message from the content script
       }
     });
 
-## Timing Issues Using postMessage ##
-
-Content scripts are loaded according to the value of the
-[`contentScriptWhen`](dev-guide/guides/content-scripts/loading.html)
-option: until that point is reached, any attempt to send a message to
-the script using `postMessage()` will trigger an exception, probably
-this:
-
-<span class="aside">
-This is a generic message which is emitted whenever we try to
-send a message to a content script, but can't find the worker
-which is supposed to receive it.
-</span>
-
-<pre>
-Error: Couldn't find the worker to receive this message. The script may not be initialized yet, or may already have been unloaded.
-</pre>
-
-So code like this, where we create a panel and then
-synchronously send it a message using `postMessage()`, will not work:
-
-    var data = require("sdk/self").data;
-
-    var panel = require("sdk/panel").Panel({
-      contentURL: "http://www.bbc.co.uk/mobile/index.html",
-      contentScriptFile: data.url("panel.js")
-    });
-
-    panel.postMessage("hi from main.js");
-
-[`port.emit()`](dev-guide/guides/content-scripts/using-port.html)
-queues messages until the content script is ready to receive them,
-so the equivalent code using `port.emit()` will work:
-
-    var data = require("sdk/self").data;
-
-    var panel = require("sdk/panel").Panel({
-      contentURL: "http://www.bbc.co.uk/mobile/index.html",
-      contentScriptFile: data.url("panel.js")
-    });
-
-    panel.port.emit("hi from main.js");
-
-
 ## Message Events Versus User-Defined Events ##
 
 You can use message events as an alternative to user-defined events:
 
     var pageModScript = "window.addEventListener('mouseover', function(event) {" +
                         "  self.postMessage(event.target.toString());" +
                         "}, false);";
 
--- a/addon-sdk/source/doc/dev-guide-source/guides/events.md
+++ b/addon-sdk/source/doc/dev-guide-source/guides/events.md
@@ -46,41 +46,36 @@ It takes two parameters:
 Many event emitters may emit more than one type of event: for example, a browser
 window might emit both `open` and `close` events. The list of valid event types
 is specific to an event emitter and is included with its documentation.
 
 * **`listener`**: the listener itself. This is a function which will be called
 whenever the event occurs. The arguments that will be passed to the listener
 are specific to an event type and are documented with the event emitter.
 
-For example, the following add-on registers two listeners with the
-[`private-browsing`](modules/sdk/private-browsing.html) module to
-listen for the `start` and `stop` events, and logs a string to the console
-reporting the change:
-
-    var pb = require("sdk/private-browsing");
+For example, the following add-on registers a listener with the
+[`tabs`](modules/sdk/tabs.html) module to
+listen for the `ready` event, and logs a string to the console
+reporting the event:
 
-    pb.on("start", function() {
-      console.log("Private browsing is on");
-    });
+    var tabs = require("sdk/tabs");
 
-    pb.on("stop", function() {
-      console.log("Private browsing is off");
+    tabs.on("ready", function () {
+      console.log("tab loaded");
     });
 
 It is not possible to enumerate the set of listeners for a given event.
 
 The value of `this` in the listener function is the object that emitted
 the event.
 
 ### Adding Listeners in Constructors ###
 
-Event emitters may be modules, as is the case for the
-`private-browsing` events, or they may be objects returned by
-constructors.
+Event emitters may be modules, as is the case for the `ready` event
+above, or they may be objects returned by constructors.
 
 In the latter case the `options` object passed to the constructor typically
 defines properties whose names are the names of supported event types prefixed
 with "on": for example, "onOpen", "onReady" and so on. Then in the constructor
 you can assign a listener function to this property as an alternative to
 calling the object's `on()` method.
 
 For example: the [`widget`](modules/sdk/widget.html) object emits
@@ -121,33 +116,41 @@ widget's `on()` method:
 ## Removing Event Listeners ##
 
 Event listeners can be removed by calling `removeListener(type, listener)`,
 supplying the type of event and the listener to remove.
 
 The listener must have been previously been added using one of the methods
 described above.
 
-In the following add-on, we add two listeners to private-browsing's `start`
-event, enter and exit private browsing, then remove the first listener and
-enter private browsing again.
+In the following add-on, we add two listeners to the
+[`tabs` module's `ready` event](modules/sdk/tabs.html#ready).
+One of the handler functions removes the listener again.
 
-    var pb = require("sdk/private-browsing");
+Then we open two tabs.
+
+    var tabs = require("sdk/tabs");
 
     function listener1() {
       console.log("Listener 1");
-      pb.removeListener("start", listener1);
+      tabs.removeListener("ready", listener1);
     }
 
     function listener2() {
       console.log("Listener 2");
     }
 
-    pb.on("start", listener1);
-    pb.on("start", listener2);
+    tabs.on("ready", listener1);
+    tabs.on("ready", listener2);
+
+    tabs.open("https://www.mozilla.org");
+    tabs.open("https://www.mozilla.org");
+
+We should see output like this:
 
-    pb.activate();
-    pb.deactivate();
-    pb.activate();
+<pre>
+info: tabevents: Listener 1
+info: tabevents: Listener 2
+info: tabevents: Listener 2
+</pre>
 
-Removing listeners is optional since they will be removed in any case
-when the application or add-on is unloaded.
+Listeners will be removed automatically when the add-on is unloaded.
 
--- a/addon-sdk/source/doc/dev-guide-source/package-spec.md
+++ b/addon-sdk/source/doc/dev-guide-source/package-spec.md
@@ -1,125 +1,222 @@
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 
-# Package Specification #
+# package.json #
+
+The "package.json" file contains metadata for your add-on.
+
+Some of its entries, such as [`icon`](dev-guide/package-spec.html#icon),
+[`name`](dev-guide/package-spec.html#name), and
+[`description`](dev-guide/package-spec.html#description), have
+direct analogues in the
+[install manifest](https://developer.mozilla.org/en-US/docs/Install_Manifests)
+format, and entries from package.json are written into the install
+manifest when the add-on is built using [`cfx xpi`](dev-guide/cfx-tool.html#cfx xpi).
 
-A *package* is a directory that, at minimum, contains a JSON file
-called `package.json`. This file is also referred to as the
-*package manifest*.
+Others, such as
+[`lib`](dev-guide/package-spec.html#lib),
+[`permissions`](dev-guide/package-spec.html#permissions),
+and [`preferences`](dev-guide/package-spec.html#preferences),
+represent instructions to the cfx tool itself to generate and include
+particular code and data structures in your add-on.
 
-## The Package Manifest ##
+The `package.json` file is initially generated in your add-on's root
+directory the first time you run
+[`cfx init`](dev-guide/cfx-tool.html#cfx init). It looks like this
+(assuming the add-on's directory is "my-addon"):
+
+<pre>
+{
+  "name": "my-addon",
+  "fullName": "my-addon",
+  "description": "a basic add-on",
+  "author": "",
+  "license": "MPL 2.0",
+  "version": "0.1"
+}
+</pre>
 
 `package.json` may contain the following keys:
 
-* `name` - the name of the package. The package system will only load
-  one package with a given name. This name cannot contain spaces or periods.
-  The name defaults to the name of the parent directory. If the package is
-  ever built as an XPI and the `fullName` key is not present, this is
-  used as the add-on's `em:name` element in its `install.rdf`.
+<table>
+
+<colgroup>
+  <col width="20%"></col>
+  <col width="80%"></col>
+</colgroup>
 
-* `fullName` - the full name of the package. It can contain spaces. If
-  the package is ever built as an XPI, this is used as the add-on's
-  `em:name` element in its `install.rdf`.
+<tr>
+  <td id="author"><code>author</code></td>
+  <td><p>The original author of the package. Defaults to an empty string.
+  It may include a optional URL in parentheses and an email
+  address in angle brackets.</p>
+  <p>This value will be used as the add-on's
+  <a href="https://developer.mozilla.org/en-US/docs/Install_Manifests#creator"><code>em:creator</code></a>
+  element in its "install.rdf".</p></td>
+</tr>
 
-* `description` - a String describing the package. If the package is
-  ever built as an XPI, this is used as the add-on's
-  `em:description` element in its `install.rdf`.
+<tr>
+  <td id="contributors"><code>contributors</code></td>
+  <td><p>An array of additional <a href="dev-guide/package-spec.html#author"><code>author</code></a>
+  strings.</p>
+  <p>These values will be used as the add-on's
+  <a href="https://developer.mozilla.org/en-US/docs/Install_Manifests#contributor"><code>em:contributor</code></a>
+  elements in its "install.rdf".</p></td>
+</tr>
 
-* `author` - the original author of the package. The author may be a
-  String including an optional URL in parentheses and optional email
-  address in angle brackets. If the package is ever built as an XPI,
-  this is used as the add-on's `em:creator` element in its
-  `install.rdf`.
+<tr>
+  <td id="dependencies"><code>dependencies</code></td>
+  <td><p>String or array of strings representing package
+  names that this add-on requires in order to function properly.</p></td>
+</tr>
 
-* `translators` - an Array of Strings consisted of translators of the package.
-
-* `contributors` - may be an Array of additional author Strings.
-
-* `homepage` - the URL of the package's website.
+<tr>
+  <td id="description"><code>description</code></td>
+  <td><p>The add-on's description. This defaults to the text
+  <code>"a basic add-on"</code>.</p>
+  <p>This value will be used as the add-on's
+  <a href="https://developer.mozilla.org/en-US/docs/Install_Manifests#description"><code>em:description</code></a>
+  element in its "install.rdf".</p></td>
+</tr>
 
-* `icon` - the relative path from the root of the package to a
-  PNG file containing the icon for the package. By default, this
-  is `icon.png`. If the package is built as an XPI, this is used
-  as the add-on's icon to display in the Add-on Manager's add-ons list.
-  This key maps on to the
-  [`iconURL` entry in the Install Manifest](https://developer.mozilla.org/en/install_manifests#iconURL),
-  so the icon may be up to 48x48 pixels in size.
+<tr>
+  <td id="fullName"><code>fullName</code></td>
+  <td><p>The full name of the package. It can contain spaces.<p></p>
+  If this key is present its value will be used as the add-on's
+  <a href="https://developer.mozilla.org/en-US/docs/Install_Manifests#name"><code>em:name</code></a>
+  element in its "install.rdf".</p></td>
+</tr>
+
+<tr>
+  <td id="harnessClassID"><code>harnessClassID</code></td>
+  <td><p>String in the <a href="https://developer.mozilla.org/en-US/docs/Generating_GUIDs">GUID format</a>.</p>
+  <p>This is used as a
+  <a href="https://developer.mozilla.org/en-US/docs/Creating_XPCOM_Components/An_Overview_of_XPCOM#CID"><code>classID</code></a>
+  of the "harness service" XPCOM component. Defaults to a random GUID generated by <code>cfx</code>.</p></td>
+</tr>
 
-* `icon64` - the relative path from the root of the package to a
-  PNG file containing the icon64 for the package. By default, this
-  is `icon64.png`. If the package is built as an XPI, this is used
-  as the add-on's icon to display in the Addon Manager's add-on details view.
-  This key maps on to the
-  [`icon64URL` entry in the Install Manifest](https://developer.mozilla.org/en/install_manifests#icon64URL),
-  so the icon should be 64x64 pixels in size.
+<tr>
+  <td id="homepage"><code>homepage</code></td>
+  <td><p>The URL of the add-on's website.</p>
+  <p>This value will be used as the add-on's
+  <a href="https://developer.mozilla.org/en-US/docs/Install_Manifests#homepageURL"><code>em:homepageURL</code></a>
+  element in its "install.rdf".</p></td>
+</tr>
 
-* `preferences` - *experimental*
-  An array of JSON objects that use the following keys `name`, `type`, `value`,
-  `title`, and `description`.  These JSON objects will be used to automatically
-  create a preferences interface for the addon in the Add-ons Manager.
-  For more information see the documentation of [simple-prefs](modules/sdk/simple-prefs.html).
+<tr>
+  <td id="icon"><code>icon</code></td>
+  <td><p>The relative path from the root of the add-on to a
+  PNG file containing the icon for the add-on. Defaults to
+  <code>"icon.png"</code>.</p>
+  <p>This value will be used as the add-on's
+  <a href="https://developer.mozilla.org/en-US/docs/Install_Manifests#iconURL"><code>em:iconURL</code></a>
+  element in its "install.rdf".</p>
+  <p>The icon may be up to 48x48 pixels in size.</p></td>
+</tr>
 
-* `license` - the name of the license as a String, with an optional
-  URL in parentheses.
-
-* `id` - a globally unique identifier for the package. When the package is
-   built as an XPI, this is used as the add-on's `em:id` element in its
-  `install.rdf`. See the
-  [Program ID page](dev-guide/guides/program-id.html).
+<tr>
+  <td id="icon64"><code>icon64</code></td>
+  <td><p>The relative path from the root of the add-on to a
+  PNG file containing the large icon for the add-on. Defaults to
+  <code>"icon64.png"</code>.</p>
+  <p>This value will be used as the add-on's
+  <a href="https://developer.mozilla.org/en-US/docs/Install_Manifests#icon64URL"><code>em:icon64URL</code></a>
+  element in its "install.rdf".</p>
+  <p>The icon may be up to 64x64 pixels in size.</p></td>
+</tr>
 
-* `version` - a String representing the version of the package. If the
-  package is ever built as an XPI, this is used as the add-on's
-  `em:version` element in its `install.rdf`.
+<tr>
+  <td id="id"><code>id</code></td>
+  <td><p>A globally unique identifier for the add-on.</p>
+  <p>This value will be used as the add-on's
+  <a href="https://developer.mozilla.org/en-US/docs/Install_Manifests#id"><code>em:id</code></a>
+  element in its "install.rdf".</p>
+  <p>See the <a href="dev-guide/guides/program-id.html">Program ID documentation</a>.</p></td>
+</tr>
 
-* `dependencies` - a String or Array of Strings representing package
-  names that this package requires in order to function properly.
+<tr>
+  <td id="lib"><code>lib</code></td>
+  <td><p>String representing the top-level module directory provided in
+  this add-on. Defaults to <code>"lib"</code>.</p></td>
+</tr>
 
-* `lib` - a String representing the top-level module directory provided in
-  this package. Defaults to `"lib"`.
-
-* `tests` - a String representing the top-level module directory containing
-  test suites for this package. Defaults to `"tests"`.
+<tr>
+  <td id="license"><code>license</code></td>
+  <td><p>The name of the license under which the add-on is distributed, with an optional
+  URL in parentheses. Defaults to <code>"MPL 2.0"</code>.</p></td>
+</tr>
 
-* `packages` - a String or Array of Strings representing paths to
-  directories containing additional packages, defaults to
-  `"packages"`.
-
-* `main` - a String representing the name of a program module that is
+<tr>
+  <td id="main"><code>main</code></td>
+  <td><p>String representing the name of a program module that is
   located in one of the top-level module directories specified by
-  `lib`. Defaults to `"main"`.
+  <a href="dev-guide/package-spec.html#lib"><code>lib</code></a>.
+  Defaults to <code>"main"</code>.</p></td>
+</tr>
 
-* `harnessClassID` - a String in the GUID format:
-  `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`, where `x` represents a single
-  hexadecimal digit. It is used as a `classID` (CID) of the "harness service"
-  XPCOM component. Defaults to a random GUID generated by `cfx`.
+<tr>
+  <td id="name"><code>name</code></td>
+  <td><p>The add-on's name. This name cannot contain spaces or periods, and
+  defaults to the name of the parent directory.</p><p>When the add-on is
+  built as an XPI, if the <a href="dev-guide/package-spec.html#fullName"><code>fullName</code></a>
+  key is not present, <code>name</code> is used as the add-on's
+  <a href="https://developer.mozilla.org/en-US/docs/Install_Manifests#name"><code>em:name</code></a>
+  element in its "install.rdf".</p></td>
+</tr>
+
+<tr>
+  <td id="packages"><code>packages</code></td>
+  <td><p>String or array of strings representing paths to
+  directories containing additional packages. Defaults to
+  <code>"packages"</code>.</p></td>
+</tr>
 
-* `permissions` - a set of permissions that the add-on needs.
-    * `private-browsing` - A Boolean indicating whether or not the
-      package supports private browsing.  If this value is not `true`
-      then the package will not see private windows.
+<tr>
+  <td id="permissions"><code>permissions</code></td>
+  <td><p>A set of permissions that the add-on needs.</p>
+    <p><strong><code>private-browsing</code></strong>: a boolean
+  indicating whether or not the
+  add-on supports private browsing. If this value is not <code>true</code>
+  or is omitted, then the add-on will not see any private windows or
+objects, such as tabs, that are associated with private windows. See the
+documentation for the
+<a href="modules/sdk/private-browsing.html"><code>private-browsing</code> module</a>.</p>
+  </td>
+</tr>
 
-## Documentation ##
-
-A package may optionally contain a
-[Markdown](http://daringfireball.net/projects/markdown/)-formatted file
-called `README.md` in its root directory. Package-browsing tools may display
-this file to developers.
+<tr>
+  <td id="preferences"><code>preferences</code></td>
+  <td><p>An array of JSON objects that use the following keys:
+  <code>name</code>,<code>type</code>, <code>value</code>,
+  <code>title</code>, and <code>description</code>.  These JSON objects will be used to
+  create a preferences interface for the add-on in the Add-ons Manager.</p>
+  <p>See the documentation for the
+  <a href="modules/sdk/simple-prefs.html"><code>simple-prefs</code> module</a>.</p></td>
+</tr>
 
-Additionally, Markdown files can be placed in an optional `docs`
-directory. When package-browsing tools are asked to show the
-documentation for a module, they will look in this directory for a
-`.md` file with the module's name. Thus, for instance, if a user
-browses to a module at `lib/foo/bar.js`, the package-browsing tool
-will look for a file at `docs/foo/bar.js` to represent the module's
-API documentation.
+<tr>
+  <td id="tests"><code>tests</code></td>
+  <td><p>String representing the top-level module directory containing
+  test suites for this package. Defaults to <code>"tests"</code>.</p></td>
+</tr>
 
-## Data Resources ##
+<tr>
+  <td id="translators"><code>translators</code></td>
+  <td><p>An array of strings listing translators of this add-on.</p>
+  <p>These values will be used as the add-on's
+  <a href="https://developer.mozilla.org/en-US/docs/Install_Manifests#translator"><code>em:translator</code></a>
+  elements in its "install.rdf".</p></td>
+</tr>
 
-Packages may optionally contain a directory called `data` into which
-arbitrary files may be placed, such as images or text files. The
-URL for these resources may be reached using the
-[self](modules/sdk/self.html) module.
+<tr>
+  <td id="version"><code>version</code></td>
+  <td><p>String representing the version of the add-on. Defaults to
+  <code>"0.1"</code>.</p>
+  <p>This value will be used as the add-on's
+  <a href="https://developer.mozilla.org/en-US/docs/Install_Manifests#version"><code>em:version</code></a>
+  element in its "install.rdf".</p></td>
+</tr>
 
-  [Markdown]: http://daringfireball.net/projects/markdown/
-  [non-bootstrapped XUL extension]: #guide/xul-extensions
+</table>
+
--- a/addon-sdk/source/doc/dev-guide-source/tutorials/annotator/overview.md
+++ b/addon-sdk/source/doc/dev-guide-source/tutorials/annotator/overview.md
@@ -45,18 +45,22 @@ elements they are associated with. It hi
 are found. When the user moves the mouse over an annotated element
 the matcher tells the main add-on code, which displays the annotation panel.
 
 ## Working with Data ##
 
 We'll use the `simple-storage` module to store annotations.
 
 Because we are recording potentially sensitive information, we want to prevent
-the user creating annotations when in private browsing mode, so we'll use the
-`private-browsing` module for that.
+the user creating annotations when in private browsing mode. The simplest way
+to do this is to omit the
+[`"private-browsing"` key](dev-guide/package-spec.html#permissions) from the
+add-on's "package.json" file. If we do this, then the add-on won't see any
+private windows, and the annotator's widget will not appear in any private
+windows.
 
 ## Getting Started ##
 
 
 Let's get started by creating a directory called "annotator". Navigate to it
 and type `cfx init`.
 
 Next, we will implement the
--- a/addon-sdk/source/doc/dev-guide-source/tutorials/annotator/storing.md
+++ b/addon-sdk/source/doc/dev-guide-source/tutorials/annotator/storing.md
@@ -272,98 +272,25 @@ Because we use a notification to alert t
 
 (It should be obvious that this is an incredibly unhelpful way to deal with the
 problem. A real add-on should give the user a chance to choose which data to
 keep, and prevent the user from adding any more data until the add-on is back
 under quota.)
 
 ## Respecting Private Browsing ##
 
-Since annotations record the user's browsing history we should prevent the user
-from creating annotations while the browser is in
-[Private Browsing](http://support.mozilla.com/en-US/kb/Private%20Browsing) mode.
-
-First let's import the `private-browsing` module into `main.js`:
-
-    var privateBrowsing = require('sdk/private-browsing');
-
-We already have a variable `annotatorIsOn` that we use to indicate whether the
-user can enter annotations. But we don't want to use that here, because we want
-to remember the underlying state so that when they exit Private Browsing the
-annotator is back in whichever state it was in before.
-
-So we'll implement a function defining that to enter annotations, the annotator
-must be active *and* Private Browsing must be off:
-
-    function canEnterAnnotations() {
-      return (annotatorIsOn && !privateBrowsing.isActive);
-    }
-
-Next, everywhere we previously used `annotatorIsOn` directly, we'll call this
-function instead:
-
-    function activateSelectors() {
-      selectors.forEach(
-        function (selector) {
-          selector.postMessage(canEnterAnnotations());
-      });
-    }
-<br>
-
-    function toggleActivation() {
-      annotatorIsOn = !annotatorIsOn;
-      activateSelectors();
-      return canEnterAnnotations();
-    }
-<br>
+Since annotations record the user's browsing history we should avoid recording
+annotations in private windows.
 
-    var selector = pageMod.PageMod({
-      include: ['*'],
-      contentScriptWhen: 'ready',
-      contentScriptFile: [data.url('jquery-1.4.2.min.js'),
-                          data.url('selector.js')],
-      onAttach: function(worker) {
-        worker.postMessage(canEnterAnnotations());
-        selectors.push(worker);
-        worker.port.on('show', function(data) {
-          annotationEditor.annotationAnchor = data;
-          annotationEditor.show();
-        });
-        worker.on('detach', function () {
-          detachWorker(this, selectors);
-        });
-      }
-    });
-
-We want to stop the user changing the underlying activation state when in
-Private Browsing mode, so we'll edit `toggleActivation` again:
+There's a very simple way to do this: do nothing. By omitting the
+[`"private-browsing"` key](dev-guide/package-spec.html#permissions) from the
+annotator's "package.json" file, the annotator opts out of private browsing
+altogether.
 
-    function toggleActivation() {
-      if (privateBrowsing.isActive) {
-        return false;
-      }
-      annotatorIsOn = !annotatorIsOn;
-      activateSelectors();
-      return canEnterAnnotations();
-    }
-
-Finally, inside the `main` function, we'll add the following code to handle
-changes in Private Browsing state by changing the icon and notifying the
-selectors:
+This means that its widget will not appear on any private windows and its
+selector and matcher content scripts won't run, so the user won't be able to
+enter any annotations in private windows.
 
-    privateBrowsing.on('start', function() {
-      widget.contentURL = data.url('widget/pencil-off.png');
-      activateSelectors();
-    });
-
-    privateBrowsing.on('stop', function() {
-      if (canEnterAnnotations()) {
-        widget.contentURL = data.url('widget/pencil-on.png');
-        activateSelectors();
-      }
-    });
-
-Try it: execute `cfx run`, and experiment with switching the annotator on and
-off while in and out of Private Browsing mode.
+Try it: execute cfx run and open a new private window: you should no longer
+see the annotator's widget.
 
 Now we can create and store annotations, the last piece is to
-[display them when the user loads the
-page](dev-guide/tutorials/annotator/displaying.html).
+[display them when the user loads the page](dev-guide/tutorials/annotator/displaying.html).
\ No newline at end of file
--- a/addon-sdk/source/doc/dev-guide-source/tutorials/event-targets.md
+++ b/addon-sdk/source/doc/dev-guide-source/tutorials/event-targets.md
@@ -5,17 +5,17 @@
 # Creating Event Targets #
 
 <span class="aside">This tutorial describes the use of low-level APIs.
 These APIs are still in active development, and we expect to make
 incompatible changes to them in future releases.</span>
 
 The [guide to event-driven programming with the SDK](dev-guide/guides/events.html)
 describes how to consume events: that is, how to listen to events generated
-by event targets. For example, you can listen to [`private-browsing`'s `start` event](modules/sdk/private-browsing.html#start) or the
+by event targets. For example, you can listen to the [`tabs` module's `ready` event](modules/sdk/tabs.html#ready) or the
 [`Panel` object's `show` event](modules/sdk/panel.html#show).
 
 With the SDK, it's also simple to implement your own event targets.
 This is especially useful if you want to
 [build your own modules](dev-guide/tutorials/reusable-modules.html),
 either to organize your add-on better or to enable other developers to
 reuse your code. If you use the SDK's event framework for your
 event targets, users of your module can listen for events using the SDK's
--- a/addon-sdk/source/doc/module-source/sdk/context-menu.md
+++ b/addon-sdk/source/doc/module-source/sdk/context-menu.md
@@ -1,21 +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/. -->
 
 <!-- contributed by Drew Willcoxon [adw@mozilla.com]  -->
 <!-- edited by Noelle Murata [fiveinchpixie@gmail.com]  -->
 
-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,
 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
@@ -391,16 +385,25 @@ This item implements the aforementioned 
                      '});'
     });
 
 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,
 its label will be "Search Google for `text`", where `text` is the truncated
 selection.
 
+## Private Windows ##
+
+If your add-on has not opted into private browsing, then any menus or
+menu items that you add will not appear in context menus belonging to
+private browser windows.
+
+To learn more about private windows, how to opt into private browsing, and how
+to support private browsing, refer to the
+[documentation for the `private-browsing` module](modules/sdk/private-browsing.html).
 
 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
 create your content scripts in separate files and pass their URLs using the
 `contentScriptFile` property. See
--- a/addon-sdk/source/doc/module-source/sdk/page-mod.md
+++ b/addon-sdk/source/doc/module-source/sdk/page-mod.md
@@ -254,16 +254,26 @@ The following add-on creates a widget wh
             'var divs = document.getElementsByTagName("div");' +
             'for (var i = 0; i < divs.length; ++i) {' +
               'divs[i].setAttribute("style", "border: solid red 1px;");' +
             '}'
         });
       }
     });
 
+## Private Windows ##
+
+If your add-on has not opted into private browsing, then your page-mods will
+not attach content scripts to documents loaded into private windows, even if
+their URLs match the pattern you have specified.
+
+To learn more about private windows, how to opt into private browsing, and how
+to support private browsing, refer to the
+[documentation for the `private-browsing` module](modules/sdk/private-browsing.html).
+
 <api name="PageMod">
 @class
 A page-mod object. Once created a page-mod will execute the supplied content
 scripts, and load any supplied stylesheets, in the context of any documents
 matching the pattern specified by the `include` property.
 
 <api name="PageMod">
 @constructor
--- a/addon-sdk/source/doc/module-source/sdk/panel.md
+++ b/addon-sdk/source/doc/module-source/sdk/panel.md
@@ -26,16 +26,25 @@ them.
 A panel's content is loaded as soon as it is created, before the panel is shown,
 and the content remains loaded when a panel is hidden, so it is possible
 to keep a panel around in the background, updating its content as appropriate
 in preparation for the next time it is shown.
 
 Your add-on can receive notifications when a panel is shown or hidden by
 listening to its `show` and `hide` events.
 
+Opening a panel will close an already opened panel.
+
+<div class="warning">
+If your add-on has
+<a href="modules/sdk/private-browsing.html#Opting into private browsing">opted into private browsing</a>,
+then you can't use panels in your add-on. This is due to a platform bug which we expect to
+be fixed in Firefox 21.
+</div>
+
 ## Panel Content ##
 
 The panel's content is specified as HTML, which is loaded from the URL
 supplied in the `contentURL` option to the panel's constructor.
 
 You can load remote HTML into the panel:
 
     var panel = require("sdk/panel").Panel({
@@ -371,16 +380,28 @@ The panel's default style is different f
 alt="OS X panel default style">
 
 This helps to ensure that the panel's style is consistent with the dialogs
 displayed by Firefox and other applications, but means you need to take care
 when applying your own styles. For example, if you set the panel's
 `background-color` property to `white` and do not set the `color` property,
 then the panel's text will be invisible on OS X although it looks fine on Ubuntu.
 
+## Private Browsing ##
+
+If your add-on has
+[opted into private browsing](modules/sdk/private-browsing.html#Opting into private browsing),
+then **you can't use panels in your add-on**. This is due to a platform bug which we expect to
+be fixed in Firefox 21.
+
+If your add-on has not opted into private browsing, and it calls `panel.show()`
+when the currently active window is a
+[private window](modules/sdk/private-browsing.html#Per-window private browsing),
+then the panel will not be shown.
+
 <api name="Panel">
 @class
 The Panel object represents a floating modal dialog that can by an add-on to
 present user interface content.
 
 Once a panel object has been created it can be shown and hidden using its
 `show()` and `hide()` methods. Once a panel is no longer needed it can be
 deactivated using `destroy()`.
@@ -395,16 +416,20 @@ add-on.
 @constructor
 Creates a panel.
 @param options {object}
   Options for the panel, with the following keys:
   @prop [width] {number}
     The width of the panel in pixels. Optional.
   @prop [height] {number}
     The height of the panel in pixels. Optional.
+  @prop [focus] {boolean}
+    Set to `false` to prevent taking the focus away when the panel is shown.
+    Only turn this off if necessary, to prevent accessibility issue.
+    Optional, default to `true`.
   @prop [contentURL] {string}
     The URL of the content to load in the panel.
   @prop [allow] {object}
     An optional object describing permissions for the content.  It should
     contain a single key named `script` whose value is a boolean that indicates
     whether or not to execute script in the content.  `script` defaults to true.
   @prop [contentScriptFile] {string,array}
     A local file URL or an array of local file URLs of content scripts to load.
@@ -467,16 +492,22 @@ Tells if the panel is currently shown or
 The height of the panel in pixels.
 </api>
 
 <api name="width">
 @property {number}
 The width of the panel in pixels.
 </api>
 
+<api name="focus">
+@property {boolean}
+Whether of not focus will be taken away when the panel is shown.
+This property is read-only.
+</api>
+
 <api name="contentURL">
 @property {string}
 The URL of content loaded into the panel.  This can point to
 local content loaded from your add-on's "data" directory or remote content.
 Setting it updates the panel's content immediately.
 </api>
 
 <api name="allow">
--- a/addon-sdk/source/doc/module-source/sdk/private-browsing.md
+++ b/addon-sdk/source/doc/module-source/sdk/private-browsing.md
@@ -1,95 +1,175 @@
 <!-- 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/. -->
 
 <!-- contributed by Paul O'Shannessy [paul@oshannessy.com]  -->
 <!-- edited by Noelle Murata [fiveinchpixie@gmail.com]  -->
 <!-- contributed by Irakli Gozalishvili [gozala@mozilla.com] -->
 
-The `private-browsing` module allows you to access Firefox's private browsing
-mode, detecting if it is active and when its state changes.
+## Per-window private browsing ##
+
+Firefox 20 introduces per-window private browsing. This means that private
+browsing status is a property of an individual browser window.
+
+The user enters
+private browsing by opening a new private browser window. When they do this,
+any existing non-private windows are kept open, so the user will typically
+have both private and non-private windows open at the same time.
+
+## Opting into private browsing ##
+
+Add-ons built using the SDK must opt into private browsing by setting the
+following key in their [`package.json`](dev-guide/package-spec.html) file:
+
+<pre>"permissions": {"private-browsing": true}</pre>
+
+If an add-on has not opted in, then the high-level SDK modules will not
+expose private windows, or objects (such as tabs) that are associated
+with private windows:
+
+* the [`windows`](modules/sdk/windows.html) module will not list any
+private browser windows, generate any events for private browser windows,
+or let the add-on open any private browser windows
+
+* the [`tabs`](modules/sdk/tabs.html) module will not list any tabs that
+belong to private browser windows, and the add-on won't receive any events
+for such tabs
+
+* any [`widgets`](modules/sdk/widget.html) will not be displayed in
+private browser windows
+
+* any menus or menu items created using the
+[`context-menu`](modules/sdk/context-menu.html) will not be shown in
+context menus that belong to private browser windows
 
-This module is available in all applications. However, only Firefox will ever
-transition into or out of private browsing mode. For all other applications,
-`pb.isActive` will always be `false`, and none of the events will be emitted.
+* the [`page-mod`](modules/sdk/page-mod.html) module will not attach
+content scripts to documents belonging to private browser windows
+
+* any [`panel`](modules/sdk/panel.html) objects will not be shown if the
+active window is a private browser window
+
+* the [`selection`](modules/sdk/selection.html) module will not include
+any selections made in private browser windows
+
+Add-ons that have opted in:
+
+* will see private windows, so they will need to
+use the `private-browsing` module to check whether objects are private,
+so as to avoid storing data derived from such objects.
+
+* will not be able to use panels in their code. This is due to a platform
+restriction which will be fixed in Firefox 21.
+
+Additionally, add-ons that use low-level modules such as
+[`window/utils`](modules/sdk/window/utils.html) may see private browser
+windows with certain functions, even if they have not explicitly opted
+into private browsing.
+
+## Respecting private browsing ##
+
+The `private-browsing` module exports a single function
+[`isPrivate()`](modules/sdk/private-browsing.html#isPrivate(object))
+that takes an object, which may be a
+[`BrowserWindow`](modules/sdk/windows.html#BrowserWindow),
+[`tab`](modules/sdk/tabs.html#Tab), or
+[`worker`](modules/sdk/content/worker.html#Worker),
+as an argument. It returns `true` only if the object is:
+
+* a private window, or
+* a tab belonging to a private window, or
+* a worker that's associated with a document hosted in a private window
 
-<div class="warning">
-The <a href="modules/sdk/private-browsing.html#activate()"><code>activate</code></a>
-and <a href="modules/sdk/private-browsing.html#deactivate()"><code>deactivate</code></a>
-functions, <a href="modules/sdk/private-browsing.html#isActive"><code>isActive</code></a>
-property, <a href="modules/sdk/private-browsing.html#start"><code>start</code></a>,
-and <a href="modules/sdk/private-browsing.html#stop"><code>stop</code></a>
-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>
+Add-ons can use this API to decide whether or not to store user data.
+For example, here's an add-on that stores the titles of tabs the user loads,
+and uses `isPrivate()` to exclude the titles of tabs that were loaded into
+private windows:
+
+    var simpleStorage = require("simple-storage");
+ 
+    if (!simpleStorage.storage.titles)
+      simpleStorage.storage.titles = [];
+ 
+    require("tabs").on("ready", function(tab) {
+      if (!require("private-browsing").isPrivate(tab)) {
+        console.log("storing...");
+        simpleStorage.storage.titles.push(tab.title);
+      }
+      else {
+        console.log("not storing, private data");
+      }
+    });
+
+Here's an add-on that uses a [page-mod](modules/sdk/page-mod.html) to log
+the content of pages loaded by the user, unless the page is private. In
+the handler for the page-mod's [`attach`](modules/sdk/page-mod.html#attach)
+event, it passes the worker into `isPrivate()`:
+
+    var pageMod = require("sdk/page-mod");
+    var privateBrowsing = require("sdk/private-browsing");
+
+    var loggingScript = "self.port.on('log-content', function() {" +
+                        "  console.log(document.body.innerHTML);" +
+                        "});";
 
-<api name="isActive">
-@property {boolean}
-  This read-only boolean is `true` if global private browsing mode is turned on.
+    function logPublicPageContent(worker) {
+      if (privateBrowsing.isPrivate(worker)) {
+        console.log("private window, doing nothing");
+      }
+      else {
+        worker.port.emit("log-content");
+      }
+    }
+
+    pageMod.PageMod({
+      include: "*",
+      contentScript: loggingScript,
+      onAttach: logPublicPageContent
+    });
+
+## Tracking private-browsing exit ##
+
+Sometimes it can be useful to cache some data from private windows while they
+are open, as long as you don't store it after the private browsing windows
+have been closed. For example, the "Downloads" window might want to display
+all downloads while there are still some private windows open, then clean out
+all the private data when all private windows have closed.
 
-  <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`.
-  </div>
-</api>
+To do this with the SDK, you can listen to the system event named
+"last-pb-context-exited":
+
+    var events = require("sdk/system/events");
+
+    function listener(event) {
+      console.log("last private window closed");
+    }
+
+    events.on("last-pb-context-exited", listener);
+
+## Working with Firefox 19 ##
+
+In Firefox 19, private browsing is a global property for the entire browser.
+When the user enters private browsing, the existing browsing session is
+suspended and a new blank window opens. This window is private, as are any
+other windows opened until the user chooses to exit private browsing, at which
+point all private windows are closed and the user is returned to the original
+non-private session.
+
+When running on Firefox 19, `isPrivate()` will return true if and only if
+the user has global private browsing enabled.
 
 <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>
-</api>
-
-<api name="deactivate">
-@function
-  Turns off 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>
-</api>
+Function to check whether the given object is private. It takes an
+object as an argument, and returns `true` only if the object is:
 
-<api name="start">
-@event
-Emitted immediately after global private browsing begins.
-
-    var pb = require("sdk/private-browsing");
-    pb.on("start", function() {
-      // Do something when the browser starts private browsing mode.
-    });
-
-  <div class="warning">
-  This event is deprecated. It will continue to work until version 1.13 of the SDK.
-  From version 1.13 onwards this event will not be emitted.
-  </div>
-</api>
+* a private [`BrowserWindow`](modules/sdk/windows.html#BrowserWindow) or
+* a [`tab`](modules/sdk/tabs.html#Tab) belonging to a private window, or
+* a [`worker`](modules/sdk/content/worker.html#Worker) that's associated
+with a document hosted in a private window
 
-<api name="stop">
-@event
-Emitted immediately after global private browsing ends.
-
-    var pb = require("sdk/private-browsing");
-    pb.on("stop", function() {
-      // Do something when the browser stops private browsing mode.
-    });
-
-  <div class="warning">
-  This event is deprecated. It will continue to work until version 1.13 of the SDK.
-  From version 1.13 onwards this event will not be emitted.
-  </div>
+@param [object] {any}
+  The object to check. This may be a
+[`BrowserWindow`](modules/sdk/windows.html#BrowserWindow),
+[`tab`](modules/sdk/tabs.html#Tab), or
+[`worker`](modules/sdk/content/worker.html#Worker).
 </api>
--- a/addon-sdk/source/doc/module-source/sdk/selection.md
+++ b/addon-sdk/source/doc/module-source/sdk/selection.md
@@ -25,16 +25,24 @@ To be notified when the user makes a sel
 
 Iterating Over Discontiguous Selections
 ---------------------------------------
 
 Discontiguous selections can be accessed by iterating over the `selection`
 module itself. Each iteration yields a `Selection` object from which `text`,
 `html`, and `isContiguous` properties can be accessed.
 
+## Private Windows ##
+
+If your add-on has not opted into private browsing, then you won't see any
+selections made in tabs that are hosted by private browser windows.
+
+To learn more about private windows, how to opt into private browsing, and how
+to support private browsing, refer to the
+[documentation for the `private-browsing` module](modules/sdk/private-browsing.html).
 
 Examples
 --------
 
 Log the current contiguous selection as text:
 
     var selection = require("sdk/selection");
     if (selection.text)
--- a/addon-sdk/source/doc/module-source/sdk/self.md
+++ b/addon-sdk/source/doc/module-source/sdk/self.md
@@ -48,18 +48,19 @@ startup
 upgrade
 downgrade
 </pre>
 
 </api>
 
 <api name="isPrivateBrowsingSupported">
 @property {boolean}
-This property indicates add-on's support for private browsing. It comes from the
-`private-browsing` property set in the `package.json` file in the main package.
+This property indicates whether or not the add-on supports private browsing.
+It comes from the [`private-browsing` key](dev-guide/package-spec.html#permissions)
+in the add-on's `package.json` file.
 </api>
 
 <api name="data">
 @property {object}
 The `data` object is used to access data that was bundled with the add-on.
 This data lives in the main package's `data/` directory, immediately below
 the `package.json` file. All files in this directory will be copied into the
 XPI and made available through the `data` object.
--- a/addon-sdk/source/doc/module-source/sdk/simple-storage.md
+++ b/addon-sdk/source/doc/module-source/sdk/simple-storage.md
@@ -167,43 +167,23 @@ data you remove is up to you.  For examp
       while (ss.quotaUsage > 1)
         ss.storage.myList.pop();
     });
 
 
 Private Browsing
 ----------------
 If your storage is related to your users' Web history, personal information, or
-other sensitive data, your add-on should respect [private browsing mode][SUMO].
-While private browsing mode is active, you should not store any sensitive data.
-
-Because any kind of data can be placed into simple storage, support for private
-browsing is not built into the module.  Instead, use the
-[`private-browsing`](modules/sdk/private-browsing.html) module to
-check private browsing status and respond accordingly.
-
-For example, the URLs your users visit should not be stored during private
-browsing.  If your add-on records the URL of the selected tab, here's how you
-might handle that:
+other sensitive data, your add-on should respect
+[private browsing](http://support.mozilla.com/en-US/kb/Private+Browsing).
 
-    ss.storage.history = [];
-    var privateBrowsing = require("sdk/private-browsing");
-    if (!privateBrowsing.isActive) {
-      var url = getSelectedTabURL();
-      ss.storage.history.push(url);
-    }
-
-For more information on supporting private browsing, see its [Mozilla Developer
-Network documentation][MDN].  While that page does not apply specifically to
-SDK-based add-ons (and its code samples don't apply at all), you should follow
-its guidance on best practices and policies.
-
-[SUMO]: http://support.mozilla.com/en-US/kb/Private+Browsing
-[MDN]: https://developer.mozilla.org/En/Supporting_private_browsing_mode
-
+To read about how to opt into private browsing mode and how to use the
+SDK to avoid storing user data associated with private windows, refer to the
+documentation for the
+[`private-browsing` module](modules/sdk/private-browsing.html).
 
 <api name="storage">
 @property {object}
   A persistent object private to your add-on.  Properties with array, boolean,
   number, object, null, and string values will be persisted.
 </api>
 
 <api name="quotaUsage">
--- a/addon-sdk/source/doc/module-source/sdk/system/events.md
+++ b/addon-sdk/source/doc/module-source/sdk/system/events.md
@@ -8,20 +8,22 @@ observer service, also known as
 You can find a list of events dispatched by firefox codebase
 [here](https://developer.mozilla.org/en-US/docs/Observer_Notifications).
 
 ## Example
 
     var events = require("sdk/system/events");
     var { Ci } = require("chrome");
 
-    events.on("http-on-modify-request", function (event) {
+    function listener(event) {
       var channel = event.subject.QueryInterface(Ci.nsIHttpChannel);
       channel.setRequestHeader("User-Agent", "MyBrowser/1.0", false);
-    });
+    }
+
+    events.on("http-on-modify-request", listener);
 
 <api name="emit">
 @function
   Send an event to observer service
 
 @param type {string}
   The event type.
 @param [event] {object}
--- a/addon-sdk/source/doc/module-source/sdk/tabs.md
+++ b/addon-sdk/source/doc/module-source/sdk/tabs.md
@@ -63,16 +63,28 @@ content:
       tab.attach({
         contentScript: 'self.postMessage(document.body.innerHTML);',
         onMessage: function (message) {
           console.log(message);
         }
       });
     });
 
+## Private Windows ##
+
+If your add-on has not opted into private browsing, then you won't see any
+tabs that are hosted by private browser windows.
+
+Tabs hosted by private browser windows won't be seen if you enumerate the
+`tabs` module itself, and you won't receive any events for them.
+
+To learn more about private windows, how to opt into private browsing, and how
+to support private browsing, refer to the
+[documentation for the `private-browsing` module](modules/sdk/private-browsing.html).
+
 <api name="activeTab">
 @property {Tab}
 
 The currently active tab in the active window. This property is read-only. To
 activate a `Tab` object, call its `activate` method.
 
 **Example**
 
@@ -135,18 +147,18 @@ This is a required property.
 If present and true, a new browser window will be opened and the URL will be
 opened in the first tab in that window. This is an optional property.
 
 @prop [inBackground] {boolean}
 If present and true, the new tab will be opened to the right of the active tab
 and will not be active. This is an optional property.
 
 @prop isPrivate {boolean}
-Boolean which will determine if a private tab should be opened.
-Private browsing mode must be supported in order to do this.
+Boolean which will determine whether the new tab should be private or not.
+If your add-on does not support private browsing this will have no effect.
 See the [private-browsing](modules/sdk/private-browsing.html) documentation for more information.
 
 @prop [isPinned] {boolean}
 If present and true, then the new tab will be pinned as an
 [app tab](http://support.mozilla.com/en-US/kb/what-are-app-tabs).
 
 @prop [onOpen] {function}
 A callback function that will be registered for 'open' event.
--- a/addon-sdk/source/doc/module-source/sdk/tabs/utils.md
+++ b/addon-sdk/source/doc/module-source/sdk/tabs/utils.md
@@ -34,18 +34,27 @@ Get the `tabbrowser`'s
 property.
 @param window {window}
 A browser window.
 @returns {tabContainer}
 </api>
 
 <api name="getTabs">
 @function
-Returns the tabs for the specified `window`, or the tabs
-across all the browser's windows if `window` is omitted.
+Returns the tabs for the specified `window`.
+
+If you omit `window`, this function will return tabs
+across all the browser's windows. However, if your add-on
+has not opted into private browsing, then the function will
+exclude all tabs that are hosted by private browser windows.
+
+To learn more about private windows, how to opt into private browsing, and how
+to support private browsing, refer to the
+[documentation for the `private-browsing` module](modules/sdk/private-browsing.html).
+
 @param window {nsIWindow}
 Optional.
 @returns {Array}
 An array of [`tab`](https://developer.mozilla.org/en-US/docs/XUL/tab)
 elements.
 </api>
 
 <api name="getActiveTab">
--- a/addon-sdk/source/doc/module-source/sdk/widget.md
+++ b/addon-sdk/source/doc/module-source/sdk/widget.md
@@ -341,16 +341,25 @@ listener, the panel will not be anchored
       onClick: function() {
         // Will not be anchored
         this.panel.show();
       }
     });
 
 See [bug 638142](https://bugzilla.mozilla.org/show_bug.cgi?id=638142).
 
+## Private Windows ##
+
+If your add-on has not opted into private browsing, then your widget will
+not appear in any private browser windows.
+
+To learn more about private windows, how to opt into private browsing, and how
+to support private browsing, refer to the
+[documentation for the `private-browsing` module](modules/sdk/private-browsing.html).
+
 ## Examples ##
 
 For conciseness, these examples create their content scripts as strings and use
 the `contentScript` property.  In your own add-ons, you will probably want to
 create your content scripts in separate files and pass their URLs using the
 `contentScriptFile` property.  See
 [Working with Content Scripts](dev-guide/guides/content-scripts/index.html) for more
 information.
@@ -680,16 +689,19 @@ attribute.
 
 @argument {WidgetView}
 The related `WidgetView` object.
 </api>
 
 <api name="click">
 @event
 This event is emitted when the widget is clicked.
+
+@argument {WidgetView}
+Listeners are passed a single argument which is the `WidgetView` that triggered the click event.
 </api>
 
 <api name="message">
 @event
 If you listen to this event you can receive message events from content
 scripts associated with this widget. When a content script posts a
 message using `self.postMessage()`, the message is delivered to the add-on
 code in the widget's `message` event.
--- a/addon-sdk/source/doc/module-source/sdk/window/utils.md
+++ b/addon-sdk/source/doc/module-source/sdk/window/utils.md
@@ -1,15 +1,30 @@
 <!-- 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/. -->
 
 The `window/utils` module provides helper functions for working with
 application windows.
 
+## Private Windows ##
+
+With this module your add-on will see private browser windows
+even if it has not explicitly opted into private browsing, so you need
+to take care not to store any user data derived from private browser windows.
+
+The exception is the [`windows()`](modules/sdk/window/utils.html#windows())
+function which returns an array of currently opened windows. Private windows
+will not be included in this list if your add-on has not opted into private
+browsing.
+
+To learn more about private windows, how to opt into private browsing, and how
+to support private browsing, refer to the
+[documentation for the `private-browsing` module](modules/sdk/private-browsing.html).
+
 <api name="getMostRecentBrowserWindow">
   @function
   Get the topmost browser window, as an
   [`nsIDOMWindow`](https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/nsIDOMWindow) instance.
   @returns {nsIDOMWindow}
 </api>
 
 <api name="getInnerId">
@@ -164,16 +179,21 @@ element.
     documentation. Optional, defaults to: `{'chrome,all,dialog=no'}`.
   @returns {nsIDOMWindow}
 </api>
 
 <api name="windows">
   @function
   Returns an array of all currently opened windows.
   Note that these windows may still be loading.
+
+  If your add-on has not
+  [opted into private browsing](modules/sdk/private-browsing.html),
+  any private browser windows will not be included in the array.
+
   @returns {Array}
   Array of `nsIDOMWindow` instances.
 </api>
 
 <api name="isDocumentLoaded">
   @function
   Check if the given window's document is completely loaded.
   This means that its "load" event has been fired and all content
--- a/addon-sdk/source/doc/module-source/sdk/windows.md
+++ b/addon-sdk/source/doc/module-source/sdk/windows.md
@@ -1,40 +1,46 @@
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 
 <!-- contributed by Felipe Gomes [felipc@gmail.com]  -->
 
+The `windows` module provides basic functions for working with browser
+windows. With this module, you can:
 
-The `windows` module provides easy access to browser windows, their
-tabs, and open/close related functions and events.
+* [enumerate the currently opened browser windows](modules/sdk/windows.html#browserWindows)
+* [open new browser windows](modules/sdk/windows.html#open(options))
+* [listen for common window events such as open and close](modules/sdk/windows.html#Events)
+
+## Private Windows ##
 
-This module currently only supports browser windows and does not provide
-access to non-browser windows such as the Bookmarks Library, preferences
-or other non-browser windows created via add-ons.
+If your add-on has not opted into private browsing, then you won't see any
+private browser windows. Private browser windows won't appear in the
+[`browserWindows`](modules/sdk/windows.html#browserWindows) property, you
+won't receive any window events, and you won't be able to open private 
+windows.
+
+To learn more about private windows, how to opt into private browsing, and how
+to support private browsing, refer to the
+[documentation for the `private-browsing` module](modules/sdk/private-browsing.html).
 
 <api name="browserWindows">
 @property {List}
-An object that contains various properties and methods to access
-functionality from browser windows, such as opening new windows, accessing
-their tabs or switching the current active window.
-
-`browserWindows` provides access to all the currently open browser windows:
+`browserWindows` provides access to all the currently open browser windows as
+[BrowserWindow](modules/sdk/windows.html#BrowserWindow) objects.
 
     var windows = require("sdk/windows");
     for each (var window in windows.browserWindows) {
       console.log(window.title);
     }
 
     console.log(windows.browserWindows.length);
 
-Object emits all the events listed under "Events" section.
-
-####Examples####
+This object emits all the events listed under the "Events" section:
 
     var windows = require("sdk/windows").browserWindows;
 
     // add a listener to the 'open' event
     windows.on('open', function(window) {
       myOpenWindows.push(window);
     });
 
@@ -114,18 +120,18 @@ as well as a callback for being notified
 If the only option being used is `url`, then a bare string URL can be passed to
 `open` instead of specifying it as a property of the `options` object.
 
 @prop url {string}
 String URL to be opened in the new window.
 This is a required property.
 
 @prop isPrivate {boolean}
-Boolean which will determine if a private window should be opened.
-Private browsing mode must be supported in order to do this.
+Boolean which will determine whether the new window should be private or not.
+If your add-on does not support private browsing this will have no effect.
 See the [private-browsing](modules/sdk/private-browsing.html) documentation for more information.
 
 @prop [onOpen] {function}
 A callback function that is called when the window has opened. This does not
 mean that the URL content has loaded, only that the window itself is fully
 functional and its properties can be accessed. This is an optional property.
 
 @prop [onClose] {function}
@@ -180,18 +186,17 @@ tabs in this window, not all tabs in all
 </api>
 
 <api name="isPrivateBrowsing">
 @property {boolean}
 Returns `true` if the window is in private browsing mode, and `false` otherwise.
 
 <div class="warning">
   This property is deprecated.
-  From version 1.14, please consider using following code instead:<br/>
-  <code>require("private-browsing").isPrivate(browserWindow)</code>
+  From version 1.14, use the <a href="modules/sdk/private-browsing.html#isPrivate()">private-browsing module's <code>isPrivate()</code></a> function instead.
 </div>
 
 </api>
 
 <api name="activate">
 @method
 Makes window active, which will focus that window and bring it to the
 foreground.
--- a/addon-sdk/source/examples/annotator/lib/main.js
+++ b/addon-sdk/source/examples/annotator/lib/main.js
@@ -3,17 +3,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 var widgets = require('widget');
 var pageMod = require('page-mod');
 var data = require('self').data;
 var panels = require('panel');
 var simpleStorage = require('simple-storage');
 var notifications = require("notifications");
-var privateBrowsing = require('private-browsing');
 
 /*
 Global variables
 * Boolean to indicate whether the add-on is switched on or not
 * Array for all workers associated with the 'selector' page mod
 * Array for all workers associated with the 'matcher' page mod
 */
 var annotatorIsOn = false;
@@ -28,23 +27,16 @@ Update the matchers: call this whenever 
 */
 function updateMatchers() {
   matchers.forEach(function (matcher) {
     matcher.postMessage(simpleStorage.storage.annotations);
   });
 }
 
 /*
-You can add annotations iff the add-on is on AND private browsing is off
-*/
-function canEnterAnnotations() {
-  return (annotatorIsOn && !privateBrowsing.isActive);
-}
-
-/*
 Constructor for an Annotation object
 */
 function Annotation(annotationText, anchor) {
   this.annotationText = annotationText;
   this.url = anchor[0];
   this.ancestorId = anchor[1];
   this.anchorText = anchor[2];
 }
@@ -61,31 +53,27 @@ function handleNewAnnotation(annotationT
 }
 
 /*
 Function to tell the selector page mod that the add-on has become (in)active
 */
 function activateSelectors() {
   selectors.forEach(
     function (selector) {
-      selector.postMessage(canEnterAnnotations());
+      selector.postMessage(annotatorIsOn);
   });
 }
 
 /*
 Toggle activation: update the on/off state and notify the selectors.
-Toggling activation is disabled when private browsing is on.
 */
 function toggleActivation() {
-  if (privateBrowsing.isActive) {
-    return false;
-  }
   annotatorIsOn = !annotatorIsOn;
   activateSelectors();
-  return canEnterAnnotations();
+  return annotatorIsOn;
 }
 
 function detachWorker(worker, workerArray) {
   var index = workerArray.indexOf(worker);
   if(index != -1) {
     workerArray.splice(index, 1);
   }
 }
@@ -133,17 +121,17 @@ When we receive this message we assign t
 display it.
 */
   var selector = pageMod.PageMod({
     include: ['*'],
     contentScriptWhen: 'ready',
     contentScriptFile: [data.url('jquery-1.4.2.min.js'),
                         data.url('selector.js')],
     onAttach: function(worker) {
-      worker.postMessage(canEnterAnnotations());
+      worker.postMessage(annotatorIsOn);
       selectors.push(worker);
       worker.port.on('show', function(data) {
         annotationEditor.annotationAnchor = data;
         annotationEditor.show();
       });
       worker.on('detach', function () {
         detachWorker(this, selectors);
       });
@@ -213,32 +201,16 @@ recent annotations until we are back in 
     notifications.notify({
       title: 'Storage space exceeded',
       text: 'Removing recent annotations'});
     while (simpleStorage.quotaUsage > 1)
       simpleStorage.storage.annotations.pop();
   });
 
 /*
-We listen for private browsing start/stop events to change the widget icon
-and to notify the selectors of the change in state.
-*/
-  privateBrowsing.on('start', function() {
-    widget.contentURL = data.url('widget/pencil-off.png');
-    activateSelectors();
-  });
-
-  privateBrowsing.on('stop', function() {
-    if (canEnterAnnotations()) {
-      widget.contentURL = data.url('widget/pencil-on.png');
-      activateSelectors();
-    }
-  });
-
-/*
 The matcher page-mod locates anchors on web pages and prepares for the
 annotation to be displayed.
 
 It is attached to all pages, and when it is attached we pass it the complete
 list of annotations. It looks for anchors in its page. If it finds one it
 highlights the anchor and binds mouseenter/mouseout events to 'show' and 'hide'
 messages to the add-on.
 
--- a/addon-sdk/source/lib/sdk/content/content-worker.js
+++ b/addon-sdk/source/lib/sdk/content/content-worker.js
@@ -219,17 +219,17 @@ const ContentWorker = Object.freeze({
     // (i.e. when it is removed from bfcache)
     pipe.on("detach", function clearTimeouts() {
       disableAllTimers();
       _timers = {};
       frozenTimers = [];
     });
   },
 
-  injectMessageAPI: function injectMessageAPI(exports, pipe) {
+  injectMessageAPI: function injectMessageAPI(exports, pipe, console) {
 
     let { eventEmitter: port, emit : portEmit } =
       ContentWorker.createEventEmitter(pipe.emit.bind(null, "event"));
     pipe.on("event", portEmit);
 
     let self = {
       port: port,
       postMessage: pipe.emit.bind(null, "message"),
@@ -288,17 +288,17 @@ const ContentWorker = Object.freeze({
   },
 
   inject: function (exports, chromeAPI, emitToChrome, options) {
     let { pipe, onChromeEvent, hasListenerFor } =
       ContentWorker.createPipe(emitToChrome);
 
     ContentWorker.injectConsole(exports, pipe);
     ContentWorker.injectTimers(exports, chromeAPI, pipe, exports.console);
-    ContentWorker.injectMessageAPI(exports, pipe);
+    ContentWorker.injectMessageAPI(exports, pipe, exports.console);
     if ( options !== undefined ) {
       ContentWorker.injectOptions(exports, options);
     }
 
     Object.freeze( exports.self );
 
     return {
       emitToContent: onChromeEvent,
--- a/addon-sdk/source/lib/sdk/content/worker.js
+++ b/addon-sdk/source/lib/sdk/content/worker.js
@@ -373,51 +373,57 @@ const WorkerSandbox = EventEmitter.compo
  * Message-passing facility for communication between code running
  * in the content and add-on process.
  * @see https://jetpack.mozillalabs.com/sdk/latest/docs/#module/api-utils/content/worker
  */
 const Worker = EventEmitter.compose({
   on: Trait.required,
   _removeAllListeners: Trait.required,
 
+  // List of messages fired before worker is initialized
+  get _earlyEvents() {
+    delete this._earlyEvents;
+    this._earlyEvents = [];
+    return this._earlyEvents;
+  },
+
   /**
    * Sends a message to the worker's global scope. Method takes single
    * argument, which represents data to be sent to the worker. The data may
    * be any primitive type value or `JSON`. Call of this method asynchronously
    * emits `message` event with data value in the global scope of this
    * symbiont.
    *
    * `message` event listeners can be set either by calling
    * `self.on` with a first argument string `"message"` or by
    * implementing `onMessage` function in the global scope of this worker.
    * @param {Number|String|JSON} data
    */
-  postMessage: function postMessage(data) {
-    if (!this._contentWorker)
-      throw new Error(ERR_DESTROYED);
-    if (this._frozen)
-      throw new Error(ERR_FROZEN);
-
-    this._contentWorker.emit("message", data);
+  postMessage: function (data) {
+    let args = ['message'].concat(Array.slice(arguments));
+    if (!this._inited) {
+      this._earlyEvents.push(args);
+      return;
+    }
+    processMessage.apply(this, args);
   },
 
   /**
    * EventEmitter, that behaves (calls listeners) asynchronously.
    * A way to send customized messages to / from the worker.
    * Events from in the worker can be observed / emitted via
    * worker.on / worker.emit.
    */
   get port() {
     // We generate dynamically this attribute as it needs to be accessible
     // before Worker.constructor gets called. (For ex: Panel)
 
     // create an event emitter that receive and send events from/to the worker
-    let self = this;
     this._port = EventEmitterTrait.create({
-      emit: function () self._emitEventToContent(Array.slice(arguments))
+      emit: this._emitEventToContent.bind(this)
     });
 
     // expose wrapped port, that exposes only public properties:
     // We need to destroy this getter in order to be able to set the
     // final value. We need to update only public port attribute as we never
     // try to access port attribute from private API.
     delete this._public.port;
     this._public.port = Cortex(this._port);
@@ -433,50 +439,32 @@ const Worker = EventEmitter.compose({
    * Allow access to _emit, in order to send event to port.
    */
   _port: null,
 
   /**
    * Emit a custom event to the content script,
    * i.e. emit this event on `self.port`
    */
-  _emitEventToContent: function _emitEventToContent(args) {
-    // We need to save events that are emitted before the worker is
-    // initialized
+  _emitEventToContent: function () {
+    let args = ['event'].concat(Array.slice(arguments));
     if (!this._inited) {
       this._earlyEvents.push(args);
       return;
     }
-
-    if (this._frozen)
-      throw new Error(ERR_FROZEN);
-
-    // We throw exception when the worker has been destroyed
-    if (!this._contentWorker) {
-      throw new Error(ERR_DESTROYED);
-    }
-
-    // Forward the event to the WorkerSandbox object
-    this._contentWorker.emit.apply(null, ["event"].concat(args));
+    processMessage.apply(this, args);
   },
 
   // Is worker connected to the content worker sandbox ?
   _inited: false,
 
   // Is worker being frozen? i.e related document is frozen in bfcache.
   // Content script should not be reachable if frozen.
   _frozen: true,
 
-  // List of custom events fired before worker is initialized
-  get _earlyEvents() {
-    delete this._earlyEvents;
-    this._earlyEvents = [];
-    return this._earlyEvents;
-  },
-
   constructor: function Worker(options) {
     options = options || {};
 
     if ('window' in options)
       this._window = options.window;
     if ('contentScriptFile' in options)
       this.contentScriptFile = options.contentScriptFile;
     if ('contentScriptOptions' in options)
@@ -520,19 +508,21 @@ const Worker = EventEmitter.compose({
 
     // will set this._contentWorker pointing to the private API:
     this._contentWorker = WorkerSandbox(this);
 
     // Mainly enable worker.port.emit to send event to the content worker
     this._inited = true;
     this._frozen = false;
 
-    // Flush all events that have been fired before the worker is initialized.
-    this._earlyEvents.forEach((function (args) this._emitEventToContent(args)).
-                              bind(this));
+    // Process all events and messages that were fired before the
+    // worker was initialized.
+    this._earlyEvents.forEach((function (args) {
+      processMessage.apply(this, args);
+    }).bind(this));
   },
 
   _documentUnload: function _documentUnload(subject, topic, data) {
     let innerWinID = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
     if (innerWinID != this._windowID) return false;
     this._workerCleanup();
     return true;
   },
@@ -585,17 +575,17 @@ const Worker = EventEmitter.compose({
       this._window.removeEventListener("pagehide", this._pageHide, true);
     }
     this._window = null;
     // This method may be called multiple times,
     // avoid dispatching `detach` event more than once
     if (this._windowID) {
       this._windowID = null;
       observers.remove("inner-window-destroyed", this._documentUnload);
-      this._earlyEvents.slice(0, this._earlyEvents.length);
+      this._earlyEvents.length = 0;
       this._emit("detach");
     }
   },
 
   /**
    * Receive an event from the content script that need to be sent to
    * worker.port. Provide a way for composed object to catch all events.
    */
@@ -617,9 +607,25 @@ const Worker = EventEmitter.compose({
   _window: null,
 
   /**
    * Flag to enable `addon` object injection in document. (bug 612726)
    * @type {Boolean}
    */
   _injectInDocument: false
 });
+
+/**
+ * Fired from postMessage and _emitEventToContent, or from the _earlyMessage
+ * queue when fired before the content is loaded. Sends arguments to
+ * contentWorker if able
+ */
+
+function processMessage () {
+  if (!this._contentWorker)
+    throw new Error(ERR_DESTROYED);
+  if (this._frozen)
+    throw new Error(ERR_FROZEN);
+
+  this._contentWorker.emit.apply(null, Array.slice(arguments));
+}
+
 exports.Worker = Worker;
--- a/addon-sdk/source/lib/sdk/deprecated/window-utils.js
+++ b/addon-sdk/source/lib/sdk/deprecated/window-utils.js
@@ -10,26 +10,26 @@ module.metadata = {
 const { Cc, Ci } = require('chrome');
 const { EventEmitter } = require('../deprecated/events');
 const { Trait } = require('../deprecated/traits');
 const { when } = require('../system/unload');
 const { getInnerId, getOuterId, windows, isDocumentLoaded, isBrowser,
         getMostRecentBrowserWindow, getMostRecentWindow } = require('../window/utils');
 const errors = require('../deprecated/errors');
 const { deprecateFunction } = require('../util/deprecate');
-const { ignoreWindow } = require('sdk/private-browsing/utils');
+const { ignoreWindow, isGlobalPBSupported } = require('sdk/private-browsing/utils');
 const { isPrivateBrowsingSupported } = require('../self');
 
 const windowWatcher = Cc['@mozilla.org/embedcomp/window-watcher;1'].
                        getService(Ci.nsIWindowWatcher);
 const appShellService = Cc['@mozilla.org/appshell/appShellService;1'].
                         getService(Ci.nsIAppShellService);
 
 // Bug 834961: ignore private windows when they are not supported
-function getWindows() windows(null, { includePrivate: isPrivateBrowsingSupported });
+function getWindows() windows(null, { includePrivate: isPrivateBrowsingSupported || isGlobalPBSupported });
 
 /**
  * An iterator for XUL windows currently in the application.
  *
  * @return A generator that yields XUL windows exposing the
  *         nsIDOMWindow interface.
  */
 function windowIterator() {
--- a/addon-sdk/source/lib/sdk/event/core.js
+++ b/addon-sdk/source/lib/sdk/event/core.js
@@ -89,30 +89,33 @@ function emit(target, type, message /*, 
  * This is very experimental feature that you should not use unless absolutely
  * need it. Also it may be removed at any point without any further notice.
  *
  * Creates lazy iterator of return values of listeners. You can think of it
  * as lazy array of return values of listeners for the `emit` with the given
  * arguments.
  */
 emit.lazy = function lazy(target, type, message /*, ...*/) {
-  let args = Array.slice(arguments, 2)
-  let listeners = observers(target, type).slice()
-  while (listeners.length) {
-    try {
-      yield listeners.shift().apply(target, args);
-    }
+  let args = Array.slice(arguments, 2);
+  let listeners = observers(target, type).slice();
+  let index = 0;
+  let count = listeners.length;
+
+  // If error event and there are no handlers then print error message
+  // into a console.
+  if (count === 0 && type === 'error') console.exception(message);
+  while (index < count) {
+    try { yield listeners[index].apply(target, args); }
     catch (error) {
       // If exception is not thrown by a error listener and error listener is
       // registered emit `error` event. Otherwise dump exception to the console.
-      if (type !== 'error' && observers(target, 'error').length)
-        emit(target, 'error', error);
-      else
-        console.exception(error);
+      if (type !== 'error') emit(target, 'error', error);
+      else console.exception(error);
     }
+    index = index + 1;
   }
 }
 exports.emit = emit;
 
 /**
  * Removes an event `listener` for the given event `type` on the given event
  * `target`. If no `listener` is passed removes all listeners of the given
  * `type`. If `type` is not passed removes all the listeners of the given
--- a/addon-sdk/source/lib/sdk/frame/hidden-frame.js
+++ b/addon-sdk/source/lib/sdk/frame/hidden-frame.js
@@ -16,20 +16,21 @@ const { Class } = require("../core/herit
 const { List, addListItem, removeListItem } = require("../util/list");
 const { EventTarget } = require("../event/target");
 const { emit } = require("../event/core");
 const { create: makeFrame } = require("./utils");
 const { defer } = require("../core/promise");
 const { when: unload } = require("../system/unload");
 const { validateOptions, getTypeOf } = require("../deprecated/api-utils");
 const { window } = require("../addon/window");
+const { fromIterator } = require("../util/array");
 
 // This cache is used to access friend properties between functions
 // without exposing them on the public API.
-let cache = [];
+let cache = new Set();
 let elements = new WeakMap();
 
 function contentLoaded(target) {
   var deferred = defer();
   target.addEventListener("DOMContentLoaded", function DOMContentLoaded(event) {
     // "DOMContentLoaded" events from nested frames propagate up to target,
     // ignore events unless it's DOMContentLoaded for the given target.
     if (event.target === target || event.target === target.contentDocument) {
@@ -70,30 +71,23 @@ var HiddenFrame = Class({
     return elements.get(this);
   },
   toString: function toString() {
     return "[object Frame]"
   }
 });
 exports.HiddenFrame = HiddenFrame
 
-function isFrameCached(frame) {
-  // Function returns `true` if frame was already cached.
-  return cache.some(function(value) {
-    return value === frame
-  })
-}
-
 function addHidenFrame(frame) {
   if (!(frame instanceof HiddenFrame))
     throw Error("The object to be added must be a HiddenFrame.");
 
   // This instance was already added.
-  if (isFrameCached(frame)) return frame;
-  else cache.push(frame);
+  if (cache.has(frame)) return frame;
+  else cache.add(frame);
 
   let element = makeFrame(window.document, {
     nodeName: "iframe",
     type: "content",
     allowJavascript: true,
     allowPlugins: true,
     allowAuth: true,
   });
@@ -106,19 +100,19 @@ function addHidenFrame(frame) {
   return frame;
 }
 exports.add = addHidenFrame
 
 function removeHiddenFrame(frame) {
   if (!(frame instanceof HiddenFrame))
     throw Error("The object to be removed must be a HiddenFrame.");
 
-  if (!isFrameCached(frame)) return;
+  if (!cache.has(frame)) return;
 
   // Remove from cache before calling in order to avoid loop
-  cache.splice(cache.indexOf(frame), 1);
+  cache.delete(frame);
   emit(frame, "unload")
   let element = frame.element
   if (element) element.parentNode.removeChild(element)
 }
 exports.remove = removeHiddenFrame;
 
-unload(function() cache.splice(0).forEach(removeHiddenFrame));
+unload(function() fromIterator(cache).forEach(removeHiddenFrame));
--- a/addon-sdk/source/lib/sdk/panel.js
+++ b/addon-sdk/source/lib/sdk/panel.js
@@ -12,26 +12,30 @@ module.metadata = {
   }
 };
 
 const { Ci } = require("chrome");
 const { validateOptions: valid } = require('./deprecated/api-utils');
 const { Symbiont } = require('./content/content');
 const { EventEmitter } = require('./deprecated/events');
 const { setTimeout } = require('./timers');
+const { on, off, emit } = require('./system/events');
 const runtime = require('./system/runtime');
 const { getDocShell } = require("./frame/utils");
 const { getWindow } = require('./panel/window');
 const { isPrivateBrowsingSupported } = require('./self');
 const { isWindowPBSupported } = require('./private-browsing/utils');
+const { getNodeView } = require('./view/core');
 
 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
       ON_SHOW = 'popupshown',
       ON_HIDE = 'popuphidden',
-      validNumber = { is: ['number', 'undefined', 'null'] };
+      validNumber = { is: ['number', 'undefined', 'null'] },
+      validBoolean = { is: ['boolean', 'undefined', 'null'] },
+      ADDON_ID = require('./self').id;
 
 if (isPrivateBrowsingSupported && isWindowPBSupported) {
   throw Error('The panel module cannot be used with per-window private browsing at the moment, see Bug 816257');
 }
 
 /**
  * Emits show and hide events.
  */
@@ -63,39 +67,49 @@ const Panel = Symbiont.resolve({
       .swapFrameLoaders(this._viewFrame);
     this.__frameLoadersSwapped = value;
   },
   __frameLoadersSwapped: false,
 
   constructor: function Panel(options) {
     this._onShow = this._onShow.bind(this);
     this._onHide = this._onHide.bind(this);
+    this._onAnyPanelShow = this._onAnyPanelShow.bind(this);
+    on('sdk-panel-show', this._onAnyPanelShow);
+
     this.on('inited', this._onSymbiontInit.bind(this));
     this.on('propertyChange', this._onChange.bind(this));
 
     options = options || {};
     if ('onShow' in options)
       this.on('show', options.onShow);
     if ('onHide' in options)
       this.on('hide', options.onHide);
     if ('width' in options)
       this.width = options.width;
     if ('height' in options)
       this.height = options.height;
     if ('contentURL' in options)
       this.contentURL = options.contentURL;
+    if ('focus' in options) {
+      var value = options.focus;
+      var validatedValue = valid({ $: value }, { $: validBoolean }).$;
+      this._focus =
+        (typeof validatedValue == 'boolean') ? validatedValue : this._focus;
+    }
 
     this._init(options);
   },
   _destructor: function _destructor() {
     this.hide();
     this._removeAllListeners('show');
     this._removeAllListeners('hide');
     this._removeAllListeners('propertyChange');
     this._removeAllListeners('inited');
+    off('sdk-panel-show', this._onAnyPanelShow);
     // defer cleanup to be performed after panel gets hidden
     this._xulPanel = null;
     this._symbiontDestructor(this);
     this._removeAllListeners();
   },
   destroy: function destroy() {
     this._destructor();
   },
@@ -104,33 +118,37 @@ const Panel = Symbiont.resolve({
   set width(value)
     this._width = valid({ $: value }, { $: validNumber }).$ || this._width,
   _width: 320,
   /* Public API: Panel.height */
   get height() this._height,
   set height(value)
     this._height =  valid({ $: value }, { $: validNumber }).$ || this._height,
   _height: 240,
+  /* Public API: Panel.focus */
+  get focus() this._focus,
+  _focus: true,
 
   /* Public API: Panel.isShowing */
   get isShowing() !!this._xulPanel && this._xulPanel.state == "open",
 
   /* Public API: Panel.show */
   show: function show(anchor) {
-    anchor = anchor || null;
+    anchor = anchor ? getNodeView(anchor) : null;
     let anchorWindow = getWindow(anchor);
 
     // If there is no open window, or the anchor is in a private window
     // then we will not be able to display the panel
     if (!anchorWindow) {
       return;
     }
 
     let document = anchorWindow.document;
     let xulPanel = this._xulPanel;
+    let panel = this;
     if (!xulPanel) {
       xulPanel = this._xulPanel = document.createElementNS(XUL_NS, 'panel');
       xulPanel.setAttribute("type", "arrow");
 
       // One anonymous node has a big padding that doesn't work well with
       // Jetpack, as we would like to display an iframe that completely fills
       // the panel.
       // -> Use a XBL wrapper with inner stylesheet to remove this padding.
@@ -160,17 +178,17 @@ const Panel = Symbiont.resolve({
 
       // Load an empty document in order to have an immediatly loaded iframe,
       // so swapFrameLoaders is going to work without having to wait for load.
       frame.setAttribute("src","data:;charset=utf-8,");
 
       xulPanel.appendChild(frame);
       document.getElementById("mainPopupSet").appendChild(xulPanel);
     }
-    let { width, height } = this, x, y, position;
+    let { width, height, focus } = this, x, y, position;
 
     if (!anchor) {
       // Open the popup in the middle of the window.
       x = document.documentElement.clientWidth / 2 - width / 2;
       y = document.documentElement.clientHeight / 2 - height / 2;
       position = null;
     }
     else {
@@ -205,23 +223,35 @@ const Panel = Symbiont.resolve({
       xulPanel.setAttribute("flip","both");
     }
 
     // Resize the iframe instead of using panel.sizeTo
     // because sizeTo doesn't work with arrow panels
     xulPanel.firstChild.style.width = width + "px";
     xulPanel.firstChild.style.height = height + "px";
 
+    // Only display xulPanel if Panel.hide() was not called
+    // after Panel.show(), but before xulPanel.openPopup
+    // was loaded
+    emit('sdk-panel-show', { data: ADDON_ID, subject: xulPanel });
+
+    // Prevent the panel from getting focus when showing up
+    // if focus is set to false
+    xulPanel.setAttribute("noautofocus",!focus);
+
     // Wait for the XBL binding to be constructed
     function waitForBinding() {
       if (!xulPanel.openPopup) {
         setTimeout(waitForBinding, 50);
         return;
       }
-      xulPanel.openPopup(anchor, position, x, y);
+
+      if (xulPanel.state !== 'hiding') {
+        xulPanel.openPopup(anchor, position, x, y);
+      }
     }
     waitForBinding();
 
     return this._public;
   },
   /* Public API: Panel.hide */
   hide: function hide() {
     // The popuphiding handler takes care of swapping back the frame loaders
@@ -284,16 +314,18 @@ const Panel = Symbiont.resolve({
   },
 
   /**
    * Retrieve computed text color style in order to apply to the iframe
    * document. As MacOS background is dark gray, we need to use skin's
    * text color.
    */
   _applyStyleToDocument: function _applyStyleToDocument() {
+    if (this._defaultStyleApplied)
+      return;
     try {
       let win = this._xulPanel.ownerDocument.defaultView;
       let node = win.document.getAnonymousElementByAttribute(
         this._xulPanel, "class", "panel-arrowcontent");
       if (!node) {
         // Before bug 764755, anonymous content was different:
         // TODO: Remove this when targeting FF16+
         node = win.document.getAnonymousElementByAttribute(
@@ -304,16 +336,17 @@ const Panel = Symbiont.resolve({
       let style = doc.createElement("style");
       style.textContent = "body { color: " + textColor + "; }";
       let container = doc.head ? doc.head : doc.documentElement;
 
       if (container.firstChild)
         container.insertBefore(style, container.firstChild);
       else
         container.appendChild(style);
+      this._defaultStyleApplied = true;
     }
     catch(e) {
       console.error("Unable to apply panel style");
       console.exception(e);
     }
   },
 
   /**
@@ -328,16 +361,26 @@ const Panel = Symbiont.resolve({
         this._frameLoadersSwapped = true;
         this._applyStyleToDocument();
         this._emit('show');
       }
     } catch(e) {
       this._emit('error', e);
     }
   },
+
+  /**
+   * When any panel is displayed, system-wide, close `this`
+   * panel unless it's the most recently displayed panel
+   */
+  _onAnyPanelShow: function _onAnyPanelShow(e) {
+    if (e.subject !== this._xulPanel)
+      this.hide();
+  },
+
   /**
    * Notification that panel was fully initialized.
    */
   _onInit: function _onInit() {
     this._inited = true;
 
     // Avoid panel document from resizing the browser window
     // New platform capability added through bug 635673
--- a/addon-sdk/source/lib/sdk/panel/window.js
+++ b/addon-sdk/source/lib/sdk/panel/window.js
@@ -1,21 +1,22 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 'use strict';
 
 const { getMostRecentBrowserWindow, windows: getWindows } = require('../window/utils');
 const { ignoreWindow } = require('../private-browsing/utils');
 const { isPrivateBrowsingSupported } = require('../self');
+const { isGlobalPBSupported } = require('../private-browsing/utils');
 
 function getWindow(anchor) {
   let window;
   let windows = getWindows("navigator:browser", {
-    includePrivate: isPrivateBrowsingSupported
+    includePrivate: isPrivateBrowsingSupported || isGlobalPBSupported
   });
 
   if (anchor) {
     let anchorWindow = anchor.ownerDocument.defaultView.top;
     let anchorDocument = anchorWindow.document;
 
     // loop thru supported windows
     for each(let enumWindow in windows) {
--- a/addon-sdk/source/lib/sdk/private-browsing/utils.js
+++ b/addon-sdk/source/lib/sdk/private-browsing/utils.js
@@ -49,17 +49,17 @@ let isGlobalPBSupported = exports.isGlob
 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 ignoreWindow(window) {
-  return !isPrivateBrowsingSupported && isWindowPrivate(window);
+  return !isPrivateBrowsingSupported && isWindowPrivate(window) && !isGlobalPBSupported;
 }
 exports.ignoreWindow = ignoreWindow;
 
 function onChange() {
   // Emit event with in next turn of event loop.
   deferredEmit(exports, pbService.privateBrowsingEnabled ? 'start' : 'stop');
 }
 
--- a/addon-sdk/source/lib/sdk/tabs/tab-fennec.js
+++ b/addon-sdk/source/lib/sdk/tabs/tab-fennec.js
@@ -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/. */
 'use strict';
 
 const { Cc, Ci } = require('chrome');
 const { Class } = require('../core/heritage');
 const { tabNS, rawTabNS } = require('./namespace');
 const { EventTarget } = require('../event/target');
-const { activateTab, getTabTitle, setTabTitle, closeTab, getTabURL, getContentWindowForTab,
+const { activateTab, getTabTitle, setTabTitle, closeTab, getTabURL, getTabContentWindow,
         getTabForBrowser,
         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';
@@ -124,17 +124,17 @@ const Tab = Class({
   /**
    * Create a worker for this tab, first argument is options given to Worker.
    * @type {Worker}
    */
   attach: function attach(options) {
     // BUG 792946 https://bugzilla.mozilla.org/show_bug.cgi?id=792946
     // TODO: fix this circular dependency
     let { Worker } = require('./worker');
-    return Worker(options, tabNS(this).tab.browser.contentWindow);
+    return Worker(options, getTabContentWindow(tabNS(this).tab));
   },
 
   /**
    * Make this tab active.
    */
   activate: function activate() {
     activateTab(tabNS(this).tab, tabNS(this).window);
   },
@@ -189,10 +189,10 @@ function onTabClose(event) {
   if (tabNS(this).tab !== rawTab)
     return;
 
   emit(this, EVENTS.close.name, this);
   cleanupTab(this);
 };
 
 getPBOwnerWindow.define(Tab, function(tab) {
-  return getContentWindowForTab(tabNS(tab).tab);
+  return getTabContentWindow(tabNS(tab).tab);
 });
--- a/addon-sdk/source/lib/sdk/tabs/utils.js
+++ b/addon-sdk/source/lib/sdk/tabs/utils.js
@@ -11,19 +11,20 @@ module.metadata = {
 
 // NOTE: This file should only deal with xul/native tabs
 
 
 const { Ci } = require('chrome');
 const { defer } = require("../lang/functional");
 const { windows, isBrowser } = require('../window/utils');
 const { isPrivateBrowsingSupported } = require('../self');
+const { isGlobalPBSupported } = require('../private-browsing/utils');
 
 // Bug 834961: ignore private windows when they are not supported
-function getWindows() windows(null, { includePrivate: isPrivateBrowsingSupported });
+function getWindows() windows(null, { includePrivate: isPrivateBrowsingSupported || isGlobalPBSupported });
 
 function activateTab(tab, window) {
   let gBrowser = getTabBrowserForTab(tab);
 
   // normal case
   if (gBrowser) {
     gBrowser.selectedTab = tab;
   }
@@ -161,22 +162,16 @@ 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;
 
--- a/addon-sdk/source/lib/sdk/test/loader.js
+++ b/addon-sdk/source/lib/sdk/test/loader.js
@@ -46,17 +46,21 @@ exports.LoaderWithHookedConsole = functi
   return {
     loader: CustomLoader(module, {
       console: {
         log: hook.bind("log"),
         info: hook.bind("info"),
         warn: hook.bind("warn"),
         error: hook.bind("error"),
         debug: hook.bind("debug"),
-        exception: hook.bind("exception")
+        exception: hook.bind("exception"),
+        __exposedProps__: {
+          log: "rw", info: "rw", warn: "rw", error: "rw", debug: "rw",
+          exception: "rw"
+        }
       }
     }),
     messages: messages
   };
 }
 
 // Same than LoaderWithHookedConsole with lower level, instead we get what is
 // actually printed to the command line console
--- a/addon-sdk/source/lib/sdk/util/array.js
+++ b/addon-sdk/source/lib/sdk/util/array.js
@@ -85,13 +85,19 @@ exports.flatten = function flatten(array
    for (var i = 0, l = array.length; i < l; i++) {
     flat = flat.concat(Array.isArray(array[i]) ? flatten(array[i]) : array[i]);
    }
    return flat;
 };
 
 function fromIterator(iterator) {
   let array = [];
-  for each (let item in iterator)
-    array.push(item);
+  if (iterator.__iterator__) {
+    for each (let item in iterator)
+      array.push(item);
+  }
+  else {
+    for (let item of iterator)
+      array.push(item);
+  }
   return array;
 }
 exports.fromIterator = fromIterator;
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/lib/sdk/view/core.js
@@ -0,0 +1,46 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+module.metadata = {
+  "stability": "unstable"
+};
+
+var { Ci } = require("chrome");
+
+/**
+Temporarily emulate method so we don't have to uplift whole method
+implementation.
+
+var method = require("method/core");
+
+// Returns DOM node associated with a view for
+// the given `value`. If `value` has no view associated
+// it returns `null`. You can implement this method for
+// this type to define what the result should be for it.
+let getNodeView = method("getNodeView");
+getNodeView.define(function(value) {
+  if (value instanceof Ci.nsIDOMNode)
+    return value;
+  return null;
+});
+**/
+
+let implementations = new WeakMap();
+
+function getNodeView(value) {
+  if (value instanceof Ci.nsIDOMNode)
+    return value;
+  if (implementations.has(value))
+    return implementations.get(value)(value);
+
+  return null;
+}
+getNodeView.implement = function(value, implementation) {
+  implementations.set(value, implementation)
+}
+
+exports.getNodeView = getNodeView;
--- a/addon-sdk/source/lib/sdk/widget.js
+++ b/addon-sdk/source/lib/sdk/widget.js
@@ -43,16 +43,17 @@ const LightTrait = require('./deprecated
 const { Loader, Symbiont } = require("./content/content");
 const { Cortex } = require('./deprecated/cortex');
 const windowsAPI = require("./windows");
 const { WindowTracker } = require("./deprecated/window-utils");
 const { isBrowser } = require("./window/utils");
 const { setTimeout } = require("./timers");
 const unload = require("./system/unload");
 const { uuid } = require("./util/uuid");
+const { getNodeView } = require("./view/core");
 
 // Data types definition
 const valid = {
   number: { is: ["null", "undefined", "number"] },
   string: { is: ["null", "undefined", "string"] },
   id: {
     is: ["string"],
     ok: function (v) v.length > 0,
@@ -357,17 +358,16 @@ const Widget = function Widget(options) 
   // Return a Cortex of widget in order to hide private attributes like _onEvent
   let _public = Cortex(w);
   unload.ensure(_public, "destroy");
   return _public;
 }
 exports.Widget = Widget;
 
 
-
 /**
  * WidgetView is an instance of a widget for a specific window.
  *
  * This is an external API that can be retrieved by calling Widget.getView or
  * by watching `attach` event on Widget.
  */
 const WidgetViewTrait = LightTrait.compose(EventEmitterTrait, LightTrait({
 
@@ -427,17 +427,20 @@ const WidgetViewTrait = LightTrait.compo
     if ("click" == type || type.indexOf("mouse") == 0)
       this._baseWidget._onEvent(type, this._public);
     else
       this._baseWidget._onEvent(type, eventData);
 
     // Special case for click events: if the widget doesn't have a click
     // handler, but it does have a panel, display the panel.
     if ("click" == type && !this._listeners("click").length && this.panel)
-      this.panel.show(domNode);
+      // This kind of ugly workaround, instead we should implement
+      // `getNodeView` for the `Widget` class itself, but that's kind of
+      // hard without cleaning things up.
+      this.panel.show(getNodeView.implement({}, function() domNode));
   },
 
   _isInWindow: function WidgetView__isInWindow(window) {
     return windowsAPI.BrowserWindow({
       window: this._chrome.window
     }) == window;
   },
 
@@ -467,24 +470,24 @@ const WidgetViewTrait = LightTrait.compo
     this._chrome.destroy();
     delete this._chrome;
     this._baseWidget._onViewDestroyed(this);
     this._emit("detach");
   }
 
 }));
 
+
 const WidgetView = function WidgetView(baseWidget) {
   let w = WidgetViewTrait.create(WidgetView.prototype);
   w._initWidgetView(baseWidget);
   return w;
 }
 
 
-
 /**
  * Keeps track of all browser windows.
  * Exposes methods for adding/removing widgets
  * across all open windows (and future ones).
  * Create a new instance of BrowserWindow per window.
  */
 let browserManager = {
   items: [],
--- a/addon-sdk/source/lib/sdk/window/browser.js
+++ b/addon-sdk/source/lib/sdk/window/browser.js
@@ -4,17 +4,17 @@
 'use strict';
 
 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 { isWindowPrivate } = require('../private-browsing/utils');
+const { isWindowPrivate } = require('../window/utils');
 const { EventTarget } = require('../event/target');
 const { getOwnerWindow: getPBOwnerWindow } = require('../private-browsing/window/utils');
 const { deprecateUsage } = require('../util/deprecate');
 
 const ERR_FENNEC_MSG = 'This method is not yet supported by Fennec, consider using require("tabs") instead';
 
 const BrowserWindow = Class({
   initialize: function initialize(options) {
--- a/addon-sdk/source/lib/sdk/windows/fennec.js
+++ b/addon-sdk/source/lib/sdk/windows/fennec.js
@@ -1,18 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 'use strict';
 
 const { Class } = require('../core/heritage');
 const { BrowserWindow } = require('../window/browser');
-const windowUtils = require('../deprecated/window-utils');
-const { WindowTracker } = windowUtils;
-const { isBrowser } = require('../window/utils');
+const { WindowTracker } = require('../deprecated/window-utils');
+const { isBrowser, getMostRecentBrowserWindow } = require('../window/utils');
 const { windowNS } = require('../window/namespace');
 const { on, off, once, emit } = require('../event/core');
 const { method } = require('../lang/functional');
 const { EventTarget } = require('../event/target');
 const { List, addListItem } = require('../util/list');
 
 const ERR_FENNEC_MSG = 'This method is not yet supported by Fennec, consider using require("tabs") instead';
 
@@ -20,17 +19,17 @@ const ERR_FENNEC_MSG = 'This method is n
 
 let BrowserWindows = Class({
   implements: [ List ],
   extends: EventTarget,
   initialize: function() {
     List.prototype.initialize.apply(this);
   },
   get activeWindow() {
-    let window = windowUtils.activeBrowserWindow;
+    let window = getMostRecentBrowserWindow();
     return window ? getBrowserWindow({window: window}) : null;
   },
   open: function open(options) {
     throw new Error(ERR_FENNEC_MSG);
     return null;
   }
 });
 const browserWindows = exports.browserWindows = BrowserWindows();
--- a/addon-sdk/source/lib/sdk/windows/tabs-fennec.js
+++ b/addon-sdk/source/lib/sdk/windows/tabs-fennec.js
@@ -3,28 +3,29 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 'use strict';
 
 const { Class } = require('../core/heritage');
 const { Tab } = require('../tabs/tab');
 const { browserWindows } = require('./fennec');
 const { windowNS } = require('../window/namespace');
 const { tabsNS, tabNS } = require('../tabs/namespace');
-const { openTab, getTabs, getSelectedTab, getTabForBrowser: getRawTabForBrowser } = require('../tabs/utils');
+const { openTab, getTabs, getSelectedTab, getTabForBrowser: getRawTabForBrowser,
+        getTabContentWindow } = require('../tabs/utils');
 const { Options } = require('../tabs/common');
 const { getTabForBrowser, getTabForRawTab } = require('../tabs/helpers');
 const { on, once, off, emit } = require('../event/core');
 const { method } = require('../lang/functional');
 const { EVENTS } = require('../tabs/events');
 const { EventTarget } = require('../event/target');
 const { when: unload } = require('../system/unload');
 const { windowIterator } = require('../deprecated/window-utils');
 const { List, addListItem, removeListItem } = require('../util/list');
-const { isPrivateBrowsingSupported } = require('sdk/self');
-const { isTabPBSupported } = require('sdk/private-browsing/utils');
+const { isPrivateBrowsingSupported } = require('../self');
+const { isTabPBSupported, ignoreWindow } = require('../private-browsing/utils');
 
 const mainWindow = windowNS(browserWindows.activeWindow).window;
 
 const ERR_FENNEC_MSG = 'This method is not yet supported by Fennec';
 
 const supportPrivateTabs = isPrivateBrowsingSupported && isTabPBSupported;
 
 const Tabs = Class({
@@ -108,16 +109,20 @@ function removeTab(tab) {
   removeListItem(gTabs, tab);
   return tab;
 }
 
 // TabOpen
 function onTabOpen(event) {
   let browser = event.target;
 
+  // Eventually ignore private tabs
+  if (ignoreWindow(browser.contentWindow))
+    return;
+
   let tab = getTabForBrowser(browser);
   if (tab === null) {
     let rawTab = getRawTabForBrowser(browser);
 
     // create a Tab instance for this new tab
     tab = addTab(Tab(rawTab));
   }
 
@@ -127,18 +132,24 @@ function onTabOpen(event) {
   tab.once('close', onTabClose);
 
   emit(tab, 'open', tab);
   emit(gTabs, 'open', tab);
 };
 
 // TabSelect
 function onTabSelect(event) {
+  let browser = event.target;
+
+  // Eventually ignore private tabs
+  if (ignoreWindow(browser.contentWindow))
+    return;
+
   // Set value whenever new tab becomes active.
-  let tab = getTabForBrowser(event.target);
+  let tab = getTabForBrowser(browser);
   emit(tab, 'activate', tab);
   emit(gTabs, 'activate', tab);
 
   for each (let t in gTabs) {
     if (t === tab) continue;
     emit(t, 'deactivate', t);
     emit(gTabs, 'deactivate', t);
   }
--- a/addon-sdk/source/lib/sdk/windows/tabs-firefox.js
+++ b/addon-sdk/source/lib/sdk/windows/tabs-firefox.js
@@ -11,17 +11,17 @@ const { Trait } = require("../deprecated
 const { List } = require("../deprecated/list");
 const { Tab } = require("../tabs/tab");
 const { EventEmitter } = require("../deprecated/events");
 const { EVENTS } = require("../tabs/events");
 const { getOwnerWindow, getActiveTab, getTabs,
         openTab } = require("../tabs/utils");
 const { Options } = require("../tabs/common");
 const { observer: tabsObserver } = require("../tabs/observer");
-const { ignoreWindow, isWindowPrivate } = require("../private-browsing/utils");
+const { ignoreWindow } = require("../private-browsing/utils");
 
 const TAB_BROWSER = "tabbrowser";
 
 /**
  * This is a trait that is used in composition of window wrapper. Trait tracks
  * tab related events of the wrapped window in order to keep track of open
  * tabs and maintain their wrappers. Every new tab gets wrapped and jetpack
  * type event is emitted.
--- a/addon-sdk/source/python-lib/cuddlefish/prefs.py
+++ b/addon-sdk/source/python-lib/cuddlefish/prefs.py
@@ -6,16 +6,19 @@ DEFAULT_COMMON_PREFS = {
     # allow debug output via dump to be printed to the system console
     # (setting it here just in case, even though PlainTextConsole also
     # sets this preference)
     'browser.dom.window.dump.enabled': True,
     # warn about possibly incorrect code
     'javascript.options.strict': True,
     'javascript.options.showInConsole': True,
 
+    # Allow remote connections to the debugger
+    'devtools.debugger.remote-enabled' : True,
+
     'extensions.sdk.console.logLevel': 'info',
 
     'extensions.checkCompatibility.nightly' : False,
 
     # Disable extension updates and notifications.
     'extensions.update.enabled' : False,
     'extensions.update.notifyUser' : False,
 
@@ -46,16 +49,17 @@ DEFAULT_FENNEC_PREFS = {
   'browser.firstrun.show.uidiscovery': False
 }
 
 # When launching a temporary new Firefox profile, use these preferences.
 DEFAULT_FIREFOX_PREFS = {
     'browser.startup.homepage' : 'about:blank',
     'startup.homepage_welcome_url' : 'about:blank',
     'devtools.errorconsole.enabled' : True,
+    'devtools.chrome.enabled' : True,
 
     # Disable the feedback extension
     'extensions.testpilot.runStudies' : False,
 
     # From:
     # http://hg.mozilla.org/mozilla-central/file/1dd81c324ac7/build/automation.py.in#l388
     # Make url-classifier updates so rare that they won't affect tests.
     'urlclassifier.updateinterval' : 172800,
--- a/addon-sdk/source/python-lib/cuddlefish/rdf.py
+++ b/addon-sdk/source/python-lib/cuddlefish/rdf.py
@@ -160,21 +160,21 @@ def gen_manifest(template_root_dir, targ
         ta_desc = dom.createElement("Description")
         target_app.appendChild(ta_desc)
 
         elem = dom.createElement("em:id")
         elem.appendChild(dom.createTextNode("{aa3c5121-dab2-40e2-81ca-7ea25febc110}"))
         ta_desc.appendChild(elem)
 
         elem = dom.createElement("em:minVersion")
-        elem.appendChild(dom.createTextNode("18.0"))
+        elem.appendChild(dom.createTextNode("19.0"))
         ta_desc.appendChild(elem)
 
         elem = dom.createElement("em:maxVersion")
-        elem.appendChild(dom.createTextNode("21.0a1"))
+        elem.appendChild(dom.createTextNode("22.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_init.py
+++ b/addon-sdk/source/python-lib/cuddlefish/tests/test_init.py
@@ -136,26 +136,25 @@ class TestInit(unittest.TestCase):
         main_js = os.path.join(basedir,"lib","main.js")
         package_json = os.path.join(basedir,"package.json")
         self.assertTrue(os.path.exists(main_js))
         self.assertTrue(os.path.exists(package_json))
 
 
 class TestCfxQuits(unittest.TestCase):
 
-    def run_cfx(self, addon_name, command):
+    def run_cfx(self, addon_path, command):
         old_cwd = os.getcwd()
-        addon_path = os.path.join(tests_path,
-                                  "addons", addon_name)
         os.chdir(addon_path)
         import sys
         old_stdout = sys.stdout
         old_stderr = sys.stderr
         sys.stdout = out = StringIO()
         sys.stderr = err = StringIO()
+        rc = 0
         try:
             import cuddlefish
             args = list(command)
             # Pass arguments given to cfx so that cfx can find firefox path
             # if --binary option is given:
             args.extend(sys.argv[1:])
             cuddlefish.run(arguments=args)
         except SystemExit, e:
@@ -177,22 +176,50 @@ class TestCfxQuits(unittest.TestCase):
     # implements our own
     def assertIn(self, member, container):
         """Just like self.assertTrue(a in b), but with a nicer default message."""
         if member not in container:
             standardMsg = '"%s" not found in "%s"' % (member,
                                                   container)
             self.fail(standardMsg)
 
-    def test_run(self):
-        rc, out, err = self.run_cfx("simplest-test", ["run"])
+    def test_cfx_run(self):
+        addon_path = os.path.join(tests_path,
+                                  "addons", "simplest-test")
+        rc, out, err = self.run_cfx(addon_path, ["run"])
         self.assertEqual(rc, 0)
         self.assertIn("Program terminated successfully.", err)
 
-    def test_test(self):
-        rc, out, err = self.run_cfx("simplest-test", ["test"])
+    def test_cfx_test(self):
+        addon_path = os.path.join(tests_path,
+                                  "addons", "simplest-test")
+        rc, out, err = self.run_cfx(addon_path, ["test"])
         self.assertEqual(rc, 0)
         self.assertIn("1 of 1 tests passed.", err)
         self.assertIn("Program terminated successfully.", err)
 
+    def test_cfx_init(self):
+        # Create an empty test directory
+        addon_path = os.path.abspath(os.path.join(".test_tmp", "test-cfx-init"))
+        if os.path.isdir(addon_path):
+            shutil.rmtree(addon_path)
+        os.makedirs(addon_path)
+
+        # Fake a call to cfx init
+        old_cwd = os.getcwd()
+        os.chdir(addon_path)
+        out, err = StringIO(), StringIO()
+        rc = initializer(None, ["init"], out, err)
+        os.chdir(old_cwd)
+        out, err = out.getvalue(), err.getvalue()
+        self.assertEqual(rc["result"], 0)
+        self.assertTrue("Have fun!" in out)
+        self.assertEqual(err,"")
+
+        # run cfx test
+        rc, out, err = self.run_cfx(addon_path, ["test"])
+        self.assertEqual(rc, 0)
+        self.assertIn("2 of 2 tests passed.", err)
+        self.assertIn("Program terminated successfully.", err)
+
 
 if __name__ == "__main__":
     unittest.main()
--- a/addon-sdk/source/test/addons/layout-change/main.js
+++ b/addon-sdk/source/test/addons/layout-change/main.js
@@ -1,61 +1,70 @@
 /* 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 app = require("sdk/system/xul-app");
 
 // 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");
+  if (app.is("Firefox")) {
+    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");
+  if (app.is("Firefox")) {
+    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");
+  if (app.is("Firefox")) {
+    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");
+  if (app.is("Firefox")) {
+    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"),
@@ -92,18 +101,20 @@ exports["test compatibility"] = function
                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");
+  if (app.is("Firefox")) {
+    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"),
@@ -149,18 +160,22 @@ exports["test compatibility"] = function
                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");
+  if (app.is("Firefox")) {
+    // This module fails on fennec because of favicon xpcom component
+    // being not implemented on it.
+    assert.equal(require("utils/data"),
+                 require("sdk/io/data"), "sdk/io/data -> utils/data");
+  }
 
   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"),
@@ -169,19 +184,9 @@ exports["test compatibility"] = function
   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);
--- a/addon-sdk/source/test/addons/private-browsing-supported/main.js
+++ b/addon-sdk/source/test/addons/private-browsing-supported/main.js
@@ -1,175 +1,24 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 'use strict';
 
-const { Ci } = require('chrome');
-const { isPrivateBrowsingSupported } = require('sdk/self');
-const tabs = require('sdk/tabs');
-const { browserWindows: windows } = require('sdk/windows');
-const { isPrivate } = require('sdk/private-browsing');
-const { getOwnerWindow } = require('sdk/private-browsing/window/utils');
-const { is } = require('sdk/system/xul-app');
-const { isWindowPBSupported, isTabPBSupported } = require('sdk/private-browsing/utils');
 const { merge } = require('sdk/util/object');
-
-const TAB_URL = 'data:text/html;charset=utf-8,TEST-TAB';
-
-exports.testIsPrivateBrowsingTrue = function(assert) {
-  assert.ok(isPrivateBrowsingSupported,
-            'isPrivateBrowsingSupported property is true');
-};
-
-// test tab.open with isPrivate: true
-// test isPrivate on a tab
-// test getOwnerWindow on windows and tabs
-exports.testGetOwnerWindow = function(assert, done) {
-  let window = windows.activeWindow;
-  let chromeWindow = getOwnerWindow(window);
-  assert.ok(chromeWindow instanceof Ci.nsIDOMWindow, 'associated window is found');
-
-  tabs.open({
-    url: 'about:blank',
-    isPrivate: true,
-    onOpen: function(tab) {
-      // test that getOwnerWindow works as expected
-      if (is('Fennec')) {
-        assert.notStrictEqual(chromeWindow, getOwnerWindow(tab));
-        assert.ok(getOwnerWindow(tab) instanceof Ci.nsIDOMWindow);
-      }
-      else {
-        if (isWindowPBSupported) {
-          assert.notStrictEqual(chromeWindow,
-                                getOwnerWindow(tab),
-                                'associated window is not the same for window and window\'s tab');
-        }
-        else {
-          assert.strictEqual(chromeWindow,
-                            getOwnerWindow(tab),
-                            'associated window is the same for window and window\'s tab');
-        }
-      }
-
-      let pbSupported = isTabPBSupported || isWindowPBSupported;
-
-      // test that the tab is private if it should be
-      assert.equal(isPrivate(tab), pbSupported);
-      assert.equal(isPrivate(getOwnerWindow(tab)), pbSupported);
-
-      tab.close(function() done());
-    }
-  });
-};
-
-// test that it is possible to open a private tab
-exports.testTabOpenPrivate = function(assert, done) {
-  tabs.open({
-    url: TAB_URL,
-    isPrivate: true,
-    onReady: function(tab) {
-      assert.equal(tab.url, TAB_URL, 'opened correct tab');
-      assert.equal(isPrivate(tab), (isWindowPBSupported || isTabPBSupported));
-
-      tab.close(function() {
-        done();
-      });
-    }
-  });
-}
-
-
-// test that it is possible to open a non private tab
-exports.testTabOpenPrivateDefault = function(assert, done) {
-  tabs.open({
-    url: TAB_URL,
-    onReady: function(tab) {
-      assert.equal(tab.url, TAB_URL, 'opened correct tab');
-      assert.equal(isPrivate(tab), false);
-
-      tab.close(function() {
-        done();
-      });
-    }
-  });
-}
-
-// test that it is possible to open a non private tab in explicit case
-exports.testTabOpenPrivateOffExplicit = function(assert, done) {
-  tabs.open({
-    url: TAB_URL,
-    isPrivate: false,
-    onReady: function(tab) {
-      assert.equal(tab.url, TAB_URL, 'opened correct tab');
-      assert.equal(isPrivate(tab), false);
-
-      tab.close(function() {
-        done();
-      });
-    }
-  });
-}
-
-// test windows.open with isPrivate: true
-// test isPrivate on a window
-if (!is('Fennec')) {
-  // test that it is possible to open a private window
-  exports.testWindowOpenPrivate = function(assert, done) {
-    windows.open({
-      url: TAB_URL,
-      isPrivate: true,
-      onOpen: function(window) {
-        let tab = window.tabs[0];
-        tab.once('ready', function() {
-          assert.equal(tab.url, TAB_URL, 'opened correct tab');
-          assert.equal(isPrivate(tab), isWindowPBSupported, 'tab is private');
-
-          window.close(function() {
-            done();
-          });
-        });
-      }
-    });
-  };
-
-  exports.testIsPrivateOnWindowOn = function(assert, done) {
-    windows.open({
-      isPrivate: true,
-      onOpen: function(window) {
-        assert.equal(isPrivate(window), isWindowPBSupported, 'isPrivate for a window is true when it should be');
-        assert.equal(isPrivate(window.tabs[0]), isWindowPBSupported, 'isPrivate for a tab is false when it should be');
-        window.close(done);
-      }
-    });
-  };
-
-  exports.testIsPrivateOnWindowOffImplicit = 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);
-      }
-    })
-  }
-
-  exports.testIsPrivateOnWindowOffExplicit = function(assert, done) {
-    windows.open({
-      isPrivate: false,
-      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);
-      }
-    })
-  }
-}
+const app = require("sdk/system/xul-app");
+const { isGlobalPBSupported } = require('sdk/private-browsing/utils');
 
 merge(module.exports,
-  require('./test-windows'),
   require('./test-tabs'),
   require('./test-page-mod'),
   require('./test-selection'),
-  require('./test-panel')
+  require('./test-panel'),
+  require('./test-private-browsing'),
+  isGlobalPBSupported ? require('./test-global-private-browsing') : {}
 );
 
+// Doesn't make sense to test window-utils and windows on fennec,
+// as there is only one window which is never private
+if (!app.is("Fennec"))
+  merge(module.exports, require('./test-windows'));
+
 require('sdk/test/runner').runTestsFromModule(module);
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/test/addons/private-browsing-supported/test-global-private-browsing.js
@@ -0,0 +1,150 @@
+/* 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 windowUtils = require('sdk/deprecated/window-utils');
+const { isWindowPBSupported, isGlobalPBSupported } = require('sdk/private-browsing/utils');
+const { getFrames, getWindowTitle, onFocus, isWindowPrivate, windows, isBrowser } = require('sdk/window/utils');
+const { open, close, focus } = require('sdk/window/helpers');
+const { isPrivate } = require('sdk/private-browsing');
+const { Panel } = require('sdk/panel');
+const { Widget } = require('sdk/widget');
+const { fromIterator: toArray } = require('sdk/util/array');
+
+let { Loader } = require('sdk/test/loader');
+let loader = Loader(module, {
+  console: Object.create(console, {
+    error: {
+      value: function(e) !/DEPRECATED:/.test(e) ? console.error(e) : undefined
+    }
+  })
+});
+const pb = loader.require('sdk/private-browsing');
+
+function makeEmptyBrowserWindow(options) {
+  options = options || {};
+  return open('chrome://browser/content/browser.xul', {
+    features: {
+      chrome: true,
+      private: !!options.private,
+      toolbar: true
+    }
+  });
+}
+
+exports.testShowPanelAndWidgetOnPrivateWindow = function(assert, done) {
+  var myPrivateWindow;
+  var finished = false;
+  var privateWindow;
+  var privateWindowClosed = false;
+
+  pb.once('start', function() {
+    assert.pass('private browsing mode started');
+
+    // make a new private window
+    makeEmptyBrowserWindow().then(function(window) {
+      myPrivateWindow = window;
+
+      let wt = windowUtils.WindowTracker({
+        onTrack: function(window) {
+          if (!isBrowser(window) || window !== myPrivateWindow) return;
+
+          assert.ok(isWindowPrivate(window), 'window is private onTrack!');
+          let panel = Panel({
+            onShow: function() {
+              assert.ok(this.isShowing, 'the panel is showing on the private window');
+
+              let count = 0;
+              let widget = Widget({
+                id: "testShowPanelAndWidgetOnPrivateWindow-id",
+                label: "My Hello Widget",
+                content: "Hello!",
+                onAttach: function(mod) {
+                  count++;
+                  if (count == 2) {
+                    panel.destroy();
+                    widget.destroy();
+                    close(window);
+                  }
+                }
+              });
+            }
+          }).show(window.gBrowser);
+        },
+        onUntrack: function(window) {
+          if (window === myPrivateWindow) {
+            wt.unload();
+
+            pb.once('stop', function() {
+              assert.pass('private browsing mode end');
+              done();
+            });
+
+            pb.deactivate();
+          }
+        }
+      });
+
+      assert.equal(isWindowPrivate(window), true, 'the opened window is private');
+      assert.equal(isPrivate(window), true, 'the opened window is private');
+      assert.ok(getFrames(window).length > 1, 'there are frames for private window');
+      assert.equal(getWindowTitle(window), window.document.title,
+                   'getWindowTitle works');
+    });
+  });
+  pb.activate();
+};
+
+exports.testWindowTrackerDoesNotIgnorePrivateWindows = function(assert, done) {
+  var myPrivateWindow;
+  var count = 0;
+
+  let wt = windowUtils.WindowTracker({
+    onTrack: function(window) {
+      if (!isBrowser(window) || !isWindowPrivate(window)) return;
+      assert.ok(isWindowPrivate(window), 'window is private onTrack!');
+      if (++count == 1)
+        close(window);
+    },
+    onUntrack: function(window) {
+      if (count == 1 && isWindowPrivate(window)) {
+        wt.unload();
+
+        pb.once('stop', function() {
+          assert.pass('private browsing mode end');
+          done();
+        });
+        pb.deactivate();
+      }
+    }
+  });
+
+  pb.once('start', function() {
+    assert.pass('private browsing mode started');
+    makeEmptyBrowserWindow();
+  });
+  pb.activate();
+}
+
+exports.testWindowIteratorDoesNotIgnorePrivateWindows = function(assert, done) {
+  pb.once('start', function() {
+    // make a new private window
+    makeEmptyBrowserWindow().then(function(window) {
+      assert.ok(isWindowPrivate(window), "window is private");
+      assert.equal(isPrivate(window), true, 'the opened window is private');
+      assert.ok(toArray(windowUtils.windowIterator()).indexOf(window) > -1,
+                "window is in windowIterator()");
+      assert.ok(windows(null, { includePrivate: true }).indexOf(window) > -1,
+                "window is in windows()");
+
+      close(window).then(function() {
+        pb.once('stop', function() {
+          done();
+        });
+        pb.deactivate();
+      });
+    });
+  });
+  pb.activate();
+};
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/test/addons/private-browsing-supported/test-private-browsing.js
@@ -0,0 +1,164 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+'use strict';
+
+const { Ci } = require('chrome');
+const { isPrivateBrowsingSupported } = require('sdk/self');
+const tabs = require('sdk/tabs');
+const { browserWindows: windows } = require('sdk/windows');
+const { isPrivate } = require('sdk/private-browsing');
+const { getOwnerWindow } = require('sdk/private-browsing/window/utils');
+const { is } = require('sdk/system/xul-app');
+const { isWindowPBSupported, isTabPBSupported } = require('sdk/private-browsing/utils');
+
+const TAB_URL = 'data:text/html;charset=utf-8,TEST-TAB';
+
+exports.testIsPrivateBrowsingTrue = function(assert) {
+  assert.ok(isPrivateBrowsingSupported,
+            'isPrivateBrowsingSupported property is true');
+};
+
+// test tab.open with isPrivate: true
+// test isPrivate on a tab
+// test getOwnerWindow on windows and tabs
+exports.testGetOwnerWindow = function(assert, done) {
+  let window = windows.activeWindow;
+  let chromeWindow = getOwnerWindow(window);
+  assert.ok(chromeWindow instanceof Ci.nsIDOMWindow, 'associated window is found');
+
+  tabs.open({
+    url: 'about:blank',
+    isPrivate: true,
+    onOpen: function(tab) {
+      // test that getOwnerWindow works as expected
+      if (is('Fennec')) {
+        assert.notStrictEqual(chromeWindow, getOwnerWindow(tab));
+        assert.ok(getOwnerWindow(tab) instanceof Ci.nsIDOMWindow);
+      }
+      else {
+        if (isWindowPBSupported) {
+          assert.notStrictEqual(chromeWindow,
+                                getOwnerWindow(tab),
+                                'associated window is not the same for window and window\'s tab');
+        }
+        else {
+          assert.strictEqual(chromeWindow,
+                            getOwnerWindow(tab),
+                            'associated window is the same for window and window\'s tab');
+        }
+      }
+
+      let pbSupported = isTabPBSupported || isWindowPBSupported;
+
+      // test that the tab is private if it should be
+      assert.equal(isPrivate(tab), pbSupported);
+      assert.equal(isPrivate(getOwnerWindow(tab)), pbSupported);
+
+      tab.close(function() done());
+    }
+  });
+};
+
+// test that it is possible to open a private tab
+exports.testTabOpenPrivate = function(assert, done) {
+  tabs.open({
+    url: TAB_URL,
+    isPrivate: true,
+    onReady: function(tab) {
+      assert.equal(tab.url, TAB_URL, 'opened correct tab');
+      assert.equal(isPrivate(tab), (isWindowPBSupported || isTabPBSupported));
+
+      tab.close(function() {
+        done();
+      });
+    }
+  });
+}
+
+
+// test that it is possible to open a non private tab
+exports.testTabOpenPrivateDefault = function(assert, done) {
+  tabs.open({
+    url: TAB_URL,
+    onReady: function(tab) {
+      assert.equal(tab.url, TAB_URL, 'opened correct tab');
+      assert.equal(isPrivate(tab), false);
+
+      tab.close(function() {
+        done();
+      });
+    }
+  });
+}
+
+// test that it is possible to open a non private tab in explicit case
+exports.testTabOpenPrivateOffExplicit = function(assert, done) {
+  tabs.open({
+    url: TAB_URL,
+    isPrivate: false,
+    onReady: function(tab) {
+      assert.equal(tab.url, TAB_URL, 'opened correct tab');
+      assert.equal(isPrivate(tab), false);
+
+      tab.close(function() {
+        done();
+      });
+    }
+  });
+}
+
+// test windows.open with isPrivate: true
+// test isPrivate on a window
+if (!is('Fennec')) {
+  // test that it is possible to open a private window
+  exports.testWindowOpenPrivate = function(assert, done) {
+    windows.open({
+      url: TAB_URL,
+      isPrivate: true,
+      onOpen: function(window) {
+        let tab = window.tabs[0];
+        tab.once('ready', function() {
+          assert.equal(tab.url, TAB_URL, 'opened correct tab');
+          assert.equal(isPrivate(tab), isWindowPBSupported, 'tab is private');
+
+          window.close(function() {
+            done();
+          });
+        });
+      }
+    });
+  };
+
+  exports.testIsPrivateOnWindowOn = function(assert, done) {
+    windows.open({
+      isPrivate: true,
+      onOpen: function(window) {
+        assert.equal(isPrivate(window), isWindowPBSupported, 'isPrivate for a window is true when it should be');
+        assert.equal(isPrivate(window.tabs[0]), isWindowPBSupported, 'isPrivate for a tab is false when it should be');
+        window.close(done);
+      }
+    });
+  };
+
+  exports.testIsPrivateOnWindowOffImplicit = 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);
+      }
+    })
+  }
+
+  exports.testIsPrivateOnWindowOffExplicit = function(assert, done) {
+    windows.open({
+      isPrivate: false,
+      onOpen: function(window) {
+        assert.equal(isPrivate(window), false, 'isPrivate for a window is false when it should be');
+        assert.equal(isPrivate(window.tabs[0]), false, 'isPrivate for a tab is false when it should be');
+        window.close(done);
+      }
+    })
+  }
+}
--- a/addon-sdk/source/test/addons/require/main.js
+++ b/addon-sdk/source/test/addons/require/main.js
@@ -7,52 +7,52 @@ exports["test local vs sdk module"] = fu
   assert.notEqual(require("memory"),
                   require("sdk/deprecated/memory"),
                   "Local module takes the priority over sdk modules");
   assert.ok(require("memory").local,
             "this module is really the local one");
 }
 
 exports["test 3rd party vs sdk module"] = function (assert) {
-  // We are testing with a 3rd party package called `panel` with 3 modules
+  // We are testing with a 3rd party package called `tabs` with 3 modules
   // main, page-mod and third-party
 
   // the only way to require 3rd party package modules are to use absolute paths
-  // require("panel/main"), require("panel/page-mod"),
-  // require("panel/third-party") and also require("panel") which will refer
-  // to panel's main package module.
+  // require("tabs/main"), require("tabs/page-mod"),
+  // require("tabs/third-party") and also require("tabs") which will refer
+  // to tabs's main package module.
 
   // So require(page-mod) shouldn't map the 3rd party
   assert.equal(require("page-mod"),
                require("sdk/page-mod"),
                "Third party modules don't overload sdk modules");
   assert.ok(require("page-mod").PageMod,
             "page-mod module is really the sdk one");
 
-  assert.equal(require("panel/page-mod").id, "page-mod",
-               "panel/page-mod is the 3rd party");
+  assert.equal(require("tabs/page-mod").id, "page-mod",
+               "tabs/page-mod is the 3rd party");
 
-  // But require(panel) will map to 3rd party main module
+  // But require(tabs) will map to 3rd party main module
   // *and* overload the sdk module
   // and also any local module with the same name
-  assert.equal(require("panel").id, "panel-main",
+  assert.equal(require("tabs").id, "tabs-main",
                "Third party main module overload sdk modules");
-  assert.equal(require("panel"),
-               require("panel/main"),
-               "require(panel) maps to require(panel/main)");
+  assert.equal(require("tabs"),
+               require("tabs/main"),
+               "require(tabs) maps to require(tabs/main)");
   // So that you have to use relative path to ensure getting the local module
-  assert.equal(require("./panel").id,
-               "local-panel",
-               "require(./panel) maps to the local module");
+  assert.equal(require("./tabs").id,
+               "local-tabs",
+               "require(./tabs) maps to the local module");
 
   // It should still be possible to require sdk module with absolute path
-  assert.ok(require("sdk/panel").Panel,
+  assert.ok(require("sdk/tabs").open,
             "We can bypass this overloading with absolute path to sdk modules");
-  assert.equal(require("sdk/panel"),
-               require("addon-kit/panel"),
+  assert.equal(require("sdk/tabs"),
+               require("addon-kit/tabs"),
                "Old and new layout both work");
 }
 
 // /!\ Always use distinct module for each test.
 //     Otherwise, the linker can correctly parse and allow the first usage of it
 //     but still silently fail on the second. 
 
 exports.testRelativeRequire = function (assert) {
@@ -65,13 +65,13 @@ exports.testRelativeSubFolderRequire = f
 
 exports.testMultipleRequirePerLine = function (assert) {
   var a=require('./multiple/a'),b=require('./multiple/b');
   assert.equal(a.id, 'a');
   assert.equal(b.id, 'b');
 }
 
 exports.testSDKRequire = function (assert) {
-  assert.deepEqual(Object.keys(require('sdk/widget')), ['Widget']);
-  assert.equal(require('widget'), require('sdk/widget'));
+  assert.deepEqual(Object.keys(require('sdk/page-worker')), ['Page']);
+  assert.equal(require('page-worker'), require('sdk/page-worker'));
 }
 
 require("sdk/test/runner").runTestsFromModule(module);
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/test/addons/require/packages/tabs/main.js
@@ -0,0 +1,5 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+exports.id = "tabs-main";
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/test/addons/require/packages/tabs/package.json
@@ -0,0 +1,3 @@
+{
+  "id": "test-panel"
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/test/addons/require/packages/tabs/page-mod.js
@@ -0,0 +1,5 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+exports.id = "page-mod";
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/test/addons/require/tabs.js
@@ -0,0 +1,5 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+exports.id = "local-tabs";
--- a/addon-sdk/source/test/private-browsing/global.js
+++ b/addon-sdk/source/test/private-browsing/global.js
@@ -1,16 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 'use strict';
 
 const timer = require("sdk/timers");
 const { LoaderWithHookedConsole, deactivate, pb, pbUtils } = require("./helper");
 const tabs = require("sdk/tabs");
+const { getMostRecentBrowserWindow, isWindowPrivate } = require('sdk/window/utils');
 
 exports["test activate private mode via handler"] = function(test) {
   test.waitUntilDone();
 
   function onReady(tab) {
     if (tab.url == "about:robots")
       tab.close(function() pb.activate());
   }
@@ -227,8 +228,23 @@ exports.testUnloadWhileActive = function
   pb.once("stop", function() {
     test.assert(!called, "stop was not called on unload");
     test.assert(errors.length, 2, "should have been 2 deprecation errors");
     test.done();
   });
 
   pb.activate();
 };
+
+exports.testIgnoreWindow = function(test) {
+  test.waitUntilDone();
+  let window = getMostRecentBrowserWindow();
+
+  pb.once('start', function() {
+    test.assert(isWindowPrivate(window), 'window is private');
+    test.assert(!pbUtils.ignoreWindow(window), 'window is not ignored');
+    pb.once('stop', function() {
+      test.done();
+    });
+    pb.deactivate();
+  });
+  pb.activate();
+};
--- a/addon-sdk/source/test/private-browsing/helper.js
+++ b/addon-sdk/source/test/private-browsing/helper.js
@@ -4,16 +4,21 @@
 'use strict';
 
 const { Loader } = require('sdk/test/loader');
 
 const { loader } = LoaderWithHookedConsole(module);
 
 const pb = loader.require('sdk/private-browsing');
 const pbUtils = loader.require('sdk/private-browsing/utils');
+const xulApp = require("sdk/system/xul-app");
+const { openDialog, getMostRecentBrowserWindow } = require('sdk/window/utils');
+const { openTab, getTabContentWindow, getActiveTab, setTabURL, closeTab } = require('sdk/tabs/utils');
+const promise = require("sdk/core/promise");
+const windowHelpers = require('sdk/window/helpers');
 
 function LoaderWithHookedConsole(module) {
   let globals = {};
   let errors = [];
 
   globals.console = Object.create(console, {
     error: {
       value: function(e) {
@@ -40,8 +45,45 @@ function deactivate(callback) {
     pb.deactivate();
   }
 }
 exports.deactivate = deactivate;
 
 exports.pb = pb;
 exports.pbUtils = pbUtils;
 exports.LoaderWithHookedConsole = LoaderWithHookedConsole;
+
+exports.openWebpage = function openWebpage(url, enablePrivate) {
+  if (xulApp.is("Fennec")) {
+    let chromeWindow = getMostRecentBrowserWindow();
+    let rawTab = openTab(chromeWindow, url, {
+      isPrivate: enablePrivate
+    });
+    return {
+      ready: promise.resolve(getTabContentWindow(rawTab)),
+      close: function () {
+        closeTab(rawTab);
+        // Returns a resolved promise as there is no need to wait
+        return promise.resolve();
+      }
+    };
+  }
+  else {
+    let win = openDialog({
+      private: enablePrivate
+    });
+    let deferred = promise.defer();
+    win.addEventListener("load", function onLoad() {
+      win.removeEventListener("load", onLoad, false);
+
+      let rawTab = getActiveTab(win);
+      setTabURL(rawTab, url);
+      deferred.resolve(getTabContentWindow(rawTab));
+    });
+    return {
+      ready: deferred.promise,
+      close: function () {
+        return windowHelpers.close(win);
+      }
+    };
+  }
+  return null;
+}
--- a/addon-sdk/source/test/private-browsing/tabs.js
+++ b/addon-sdk/source/test/private-browsing/tabs.js
@@ -1,24 +1,27 @@
 'use strict';
 
 const { Ci } = require('chrome');
 const { openTab, closeTab } = require('sdk/tabs/utils');
-const browserWindows = require('sdk/windows');
+const { browserWindows } = require('sdk/windows');
 const { getOwnerWindow } = require('sdk/private-browsing/window/utils');
+const { isPrivate } = require('sdk/private-browsing');
 
 exports.testIsPrivateOnTab = function(test) {
   let window = browserWindows.activeWindow;
+
   let chromeWindow = getOwnerWindow(window);
+
   test.assert(chromeWindow instanceof Ci.nsIDOMWindow, 'associated window is found');
-  test.assert(!pb.isPrivate(chromeWindow), 'the top level window is not private');
+  test.assert(!isPrivate(chromeWindow), 'the top level window is not private');
 
   let rawTab = openTab(chromeWindow, 'data:text/html,<h1>Hi!</h1>', {
     isPrivate: 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));
+  test.assert(isPrivate(rawTab.browser.contentWindow));
+  test.assert(isPrivate(rawTab.browser));
 
   closeTab(rawTab);
 }
--- a/addon-sdk/source/test/test-content-worker.js
+++ b/addon-sdk/source/test/test-content-worker.js
@@ -170,32 +170,37 @@ exports["test:emit hack message"] = Work
     });
     worker.port.emit("message", "event data");
   }
 );
 
 exports["test:n-arguments emit"] = WorkerTest(
   DEFAULT_CONTENT_URL,
   function(assert, browser, done) {
+    let repeat = 0;
     let worker =  Worker({
         window: browser.contentWindow,
         contentScript: "new " + function WorkerScope() {
           // Validate self.on and self.emit
           self.port.on("addon-to-content", function (a1, a2, a3) {
             self.port.emit("content-to-addon", a1, a2, a3);
           });
         }
       });
 
     // Validate worker.port
     worker.port.on("content-to-addon", function (arg1, arg2, arg3) {
-      assert.equal(arg1, "first argument");
-      assert.equal(arg2, "second");
-      assert.equal(arg3, "third");
-      done();
+      if (!repeat++) {
+        this.emit("addon-to-content", "first argument", "second", "third");
+      } else {
+        assert.equal(arg1, "first argument");
+        assert.equal(arg2, "second");
+        assert.equal(arg3, "third");
+        done();
+      }
     });
     worker.port.emit("addon-to-content", "first argument", "second", "third");
   }
 );
 
 exports["test:post-json-values-only"] = WorkerTest(
   DEFAULT_CONTENT_URL,
   function(assert, browser, done) {
--- a/addon-sdk/source/test/test-event-core.js
+++ b/addon-sdk/source/test/test-event-core.js
@@ -152,20 +152,20 @@ exports['test error handling'] = functio
 
   on(target, 'message', function() { throw error; })
   on(target, 'error', function(boom) {
     assert.equal(boom, error, 'thrown exception causes error event');
   });
   emit(target, 'message');
 };
 
-exports['test unhandled errors'] = function(assert) {
+exports['test unhandled exceptions'] = function(assert) {
   let exceptions = [];
   let { loader, messages } = LoaderWithHookedConsole(module);
-  
+
   let { emit, on } = loader.require('sdk/event/core');
   let target = {};
   let boom = Error('Boom!');
   let drax = Error('Draax!!');
 
   on(target, 'message', function() { throw boom; });
 
   emit(target, 'message');
@@ -177,16 +177,32 @@ exports['test unhandled errors'] = funct
   on(target, 'error', function() { throw drax; });
   emit(target, 'message');
   assert.equal(messages.length, 2, 'Got the second exception');
   assert.equal(messages[1].type, 'exception', 'The console message is exception');
   assert.ok(~String(messages[1].msg).indexOf('Draax!'),
             'error in error handler is logged');
 };
 
+exports['test unhandled errors'] = function(assert) {
+  let exceptions = [];
+  let { loader, messages } = LoaderWithHookedConsole(module);
+
+  let { emit, on } = loader.require('sdk/event/core');
+  let target = {};
+  let boom = Error('Boom!');
+
+  emit(target, 'error', boom);
+  assert.equal(messages.length, 1, 'Error was logged');
+  assert.equal(messages[0].type, 'exception', 'The console message is exception');
+  assert.ok(~String(messages[0].msg).indexOf('Boom!'),
+            'unhandled exception is logged');
+};
+
+
 exports['test count'] = function(assert) {
   let target = {};
 
   assert.equal(count(target, 'foo'), 0, 'no listeners for "foo" events');
   on(target, 'foo', function() {});
   assert.equal(count(target, 'foo'), 1, 'listener registered');
   on(target, 'foo', function() {}, 2, 'another listener registered');
   off(target)
--- a/addon-sdk/source/test/test-hidden-frame.js
+++ b/addon-sdk/source/test/test-hidden-frame.js
@@ -1,14 +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 { Loader } = require("sdk/test/loader");
 const hiddenFrames = require("sdk/frame/hidden-frame");
 const { HiddenFrame } = hiddenFrames;
 
 exports["test Frame"] = function(assert, done) {
   let url = "data:text/html;charset=utf-8,<!DOCTYPE%20html>";
 
   let hiddenFrame = hiddenFrames.add(HiddenFrame({
     onReady: function () {
@@ -24,9 +25,51 @@ exports["test Frame"] = function(assert,
       }
 
       this.element.addEventListener("DOMContentLoaded", onDOMReady, false);
       this.element.setAttribute("src", url);
     }
   }));
 };
 
+exports["test frame removed properly"] = function(assert, done) {
+  let url = "data:text/html;charset=utf-8,<!DOCTYPE%20html>";
+
+  let hiddenFrame = hiddenFrames.add(HiddenFrame({
+    onReady: function () {
+      let frame = this.element;
+      assert.ok(frame.parentNode, "frame has a parent node");
+      hiddenFrames.remove(hiddenFrame);
+      assert.ok(!frame.parentNode, "frame no longer has a parent node");
+      done();
+    }
+  }));
+};
+
+
+exports["test unload detaches panels"] = function(assert, done) {
+  let loader = Loader(module);
+  let { add, remove, HiddenFrame } = loader.require("sdk/frame/hidden-frame");
+  let frames = []
+
+  function ready() {
+    frames.push(this.element);
+    if (frames.length === 2) complete();
+  }
+
+  add(HiddenFrame({ onReady: ready }));
+  add(HiddenFrame({ onReady: ready }));
+
+  function complete() {
+    frames.forEach(function(frame) {
+      assert.ok(frame.parentNode, "frame is in the document");
+    })
+    loader.unload();
+    frames.forEach(function(frame) {
+      assert.ok(!frame.parentNode, "frame isn't in the document'");
+    });
+    done();
+  }
+};
+
+
+
 require("test").run(exports);
--- a/addon-sdk/source/test/test-page-mod.js
+++ b/addon-sdk/source/test/test-page-mod.js
@@ -4,25 +4,26 @@
 "use strict";
 
 const { PageMod } = require("sdk/page-mod");
 const 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, openDialog, getFrames, getMostRecentBrowserWindow } = require('sdk/window/utils');
+const { open, getFrames, getMostRecentBrowserWindow } = require('sdk/window/utils');
 const windowUtils = require('sdk/deprecated/window-utils');
-const windowHelpers = require('sdk/window/helpers');
 const { getTabContentWindow, getActiveTab, setTabURL, openTab, closeTab } = require('sdk/tabs/utils');
 const xulApp = require("sdk/system/xul-app");
-const { data } = require('sdk/self');
+const { data, isPrivateBrowsingSupported } = require('sdk/self');
 const { isPrivate } = require('sdk/private-browsing');
-const { isTabPBSupported, isWindowPBSupported } = require('sdk/private-browsing/utils');
+const { openWebpage } = require('./private-browsing/helper');
+const { isTabPBSupported, isWindowPBSupported, isGlobalPBSupported } = require('sdk/private-browsing/utils');
 const promise = require("sdk/core/promise");
+const { pb } = require('./private-browsing/helper');
 
 /* 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);
@@ -1050,46 +1051,16 @@ exports.testEvents = function(test) {
         win.receivedEvent,
         "Content script sent an event and document received it"
       );
       done();
     }
   );
 };
 
-function openWebpage(url, enablePrivate) {
-  if (xulApp.is("Fennec")) {
-    let chromeWindow = getMostRecentBrowserWindow();
-    let rawTab = openTab(chromeWindow, url, {
-      isPrivate: enablePrivate
-    });
-    return {
-      close: function () {
-        closeTab(rawTab)
-        // Returns a resolved promise as there is no need to wait
-        return promise.resolve();
-      }
-    };
-  }
-  else {
-    let win = openDialog({
-      private: enablePrivate
-    });
-    win.addEventListener("load", function onLoad() {
-      win.removeEventListener("load", onLoad, false);
-      setTabURL(getActiveTab(win), url);
-    });
-    return {
-      close: function () {
-        return windowHelpers.close(win);
-      }
-    };
-  }
-}
-
 exports["test page-mod on private tab"] = function (test) {
   test.waitUntilDone();
   let privateUri = "data:text/html;charset=utf-8," +
                    "<iframe src=\"data:text/html;charset=utf-8,frame\" />";
   let nonPrivateUri = "data:text/html;charset=utf-8,non-private";
 
   let pageMod = new PageMod({
     include: "data:*",
@@ -1108,8 +1079,51 @@ exports["test page-mod on private tab"] 
       pageMod.destroy();
       page1.close().then(page2.close).then(test.done.bind(test));
     }
   });
 
   let page1 = openWebpage(privateUri, true);
   let page2 = openWebpage(nonPrivateUri, false);
 }
+
+exports["test page-mod on private tab in global pb"] = function (test) {
+  test.waitUntilDone();
+  if (!isGlobalPBSupported) {
+    test.pass();
+    return test.done();
+  }
+
+  let privateUri = "data:text/html;charset=utf-8," +
+                   "<iframe%20src=\"data:text/html;charset=utf-8,frame\"/>";
+
+  let pageMod = new PageMod({
+    include: privateUri,
+    onAttach: function(worker) {
+      test.assertEqual(worker.tab.url,
+                       privateUri,
+                       "page-mod should attach");
+      test.assertEqual(isPrivateBrowsingSupported,
+                       false,
+                       "private browsing is not supported");
+      test.assert(isPrivate(worker),
+                  "The worker is really non-private");
+      test.assert(isPrivate(worker.tab),
+                  "The document is really non-private");
+      pageMod.destroy();
+
+      worker.tab.close(function() {
+        pb.once('stop', function() {
+          test.pass('global pb stop');
+          test.done();
+        });
+        pb.deactivate();
+      });
+    }
+  });
+
+  let page1;
+  pb.once('start', function() {
+    test.pass('global pb start');
+    tabs.open({ url: privateUri });
+  });
+  pb.activate();
+}
--- a/addon-sdk/source/test/test-page-worker.js
+++ b/addon-sdk/source/test/test-page-worker.js
@@ -320,16 +320,30 @@ exports.testContentScriptOptionsOption =
       assert.equal(msg[1].a, true, 'boolean in contentScriptOptions');
       assert.equal(msg[1].b.join(), '1,2,3', 'array and numbers in contentScriptOptions');
       assert.equal(msg[1].c, 'string', 'string in contentScriptOptions');
       done();
     }
   });
 };
 
+exports.testMessageQueue = function (assert, done) {
+  let page = new Page({
+    contentScript: 'self.on("message", function (m) {' +
+      'self.postMessage(m);' +
+      '});',
+    contentURL: 'data:text/html;charset=utf-8,',
+  });
+  page.postMessage('ping');
+  page.on('message', function (m) {
+    assert.equal(m, 'ping', 'postMessage should queue messages');
+    done();
+  });
+};
+
 function isDestroyed(page) {
   try {
     page.postMessage("foo");
   }
   catch (err if err.message == ERR_DESTROYED) {
     return true;
   }
   return false;
--- a/addon-sdk/source/test/test-panel.js
+++ b/addon-sdk/source/test/test-panel.js
@@ -5,19 +5,21 @@
 
 const { Cc, Ci } = require("chrome");
 const { Loader } = require('sdk/test/loader');
 const { LoaderWithHookedConsole } = require("sdk/test/loader");
 const timer = require("sdk/timers");
 const self = require('sdk/self');
 const { open, close, focus } = require('sdk/window/helpers');
 const { isPrivate } = require('sdk/private-browsing');
-const { isWindowPBSupported } = require('sdk/private-browsing/utils');
+const { isWindowPBSupported, isGlobalPBSupported } = require('sdk/private-browsing/utils');
 const { defer } = require('sdk/core/promise');
 const { getMostRecentBrowserWindow } = require('sdk/window/utils');
+const { getWindow } = require('sdk/panel/window');
+const { pb } = require('./private-browsing/helper');
 
 const SVG_URL = self.data.url('mofo_logo.SVG');
 
 function makeEmptyPrivateBrowserWindow(options) {
   options = options || {};
   return open('chrome://browser/content/browser.xul', {
     features: {
       chrome: true,
@@ -282,66 +284,158 @@ exports["test Several Show Hides"] = fun
   });
   panel.show();
 };
 
 exports["test Anchor And Arrow"] = function(assert, done) {
   const { Panel } = require('sdk/panel');
 
   let count = 0;
-  function newPanel(tab, anchor) {
+  let queue = [];
+  let tab;
+
+  function newPanel(anchor) {
     let panel = Panel({
       contentURL: "data:text/html;charset=utf-8,<html><body style='padding: 0; margin: 0; " +
                   "background: gray; text-align: center;'>Anchor: " +
                   anchor.id + "</body></html>",
       width: 200,
       height: 100,
       onShow: function () {
-        count++;
         panel.destroy();
-        if (count==5) {
-          assert.pass("All anchored panel test displayed");
-          tab.close(function () {
-            done();
-          });
-        }
+        next();
       }
     });
+    queue.push({ panel: panel, anchor: anchor });
+  }
+
+  function next () {
+    if (!queue.length) {
+      assert.pass("All anchored panel test displayed");
+      tab.close(function () {
+        done();
+      });
+      return;
+    }
+    let { panel, anchor } = queue.shift();
     panel.show(anchor);
   }
 
   let tabs= require("sdk/tabs");
   let url = 'data:text/html;charset=utf-8,' +
     '<html><head><title>foo</title></head><body>' +
     '<style>div {background: gray; position: absolute; width: 300px; ' +
            'border: 2px solid black;}</style>' +
     '<div id="tl" style="top: 0px; left: 0px;">Top Left</div>' +
     '<div id="tr" style="top: 0px; right: 0px;">Top Right</div>' +
     '<div id="bl" style="bottom: 0px; left: 0px;">Bottom Left</div>' +
     '<div id="br" style="bottom: 0px; right: 0px;">Bottom right</div>' +
     '</body></html>';
 
   tabs.open({
     url: url,
-    onReady: function(tab) {
+    onReady: function(_tab) {
+      tab = _tab;
       let browserWindow = Cc["@mozilla.org/appshell/window-mediator;1"].
                       getService(Ci.nsIWindowMediator).
                       getMostRecentWindow("navigator:browser");
       let window = browserWindow.content;
-      newPanel(tab, window.document.getElementById('tl'));
-      newPanel(tab, window.document.getElementById('tr'));
-      newPanel(tab, window.document.getElementById('bl'));
-      newPanel(tab, window.document.getElementById('br'));
+      newPanel(window.document.getElementById('tl'));
+      newPanel(window.document.getElementById('tr'));
+      newPanel(window.document.getElementById('bl'));
+      newPanel(window.document.getElementById('br'));
       let anchor = browserWindow.document.getElementById("identity-box");
-      newPanel(tab, anchor);
+      newPanel(anchor);
+
+      next();
+    }
+  });
+};
+
+exports["test Panel Focus True"] = function(assert, done) {
+  const { Panel } = require('sdk/panel');
+
+  const FM = Cc["@mozilla.org/focus-manager;1"].
+                getService(Ci.nsIFocusManager);
+
+  let browserWindow = Cc["@mozilla.org/appshell/window-mediator;1"].
+                      getService(Ci.nsIWindowMediator).
+                      getMostRecentWindow("navigator:browser");
+
+  // Make sure there is a focused element
+  browserWindow.document.documentElement.focus();
+
+  // Get the current focused element
+  let focusedElement = FM.focusedElement;
+
+  let panel = Panel({
+    contentURL: "about:buildconfig",
+    focus: true,
+    onShow: function () {
+      assert.ok(focusedElement !== FM.focusedElement,
+        "The panel takes the focus away.");
+      done();
     }
   });
+  panel.show();
+};
 
+exports["test Panel Focus False"] = function(assert, done) {
+  const { Panel } = require('sdk/panel');
+
+  const FM = Cc["@mozilla.org/focus-manager;1"].
+                getService(Ci.nsIFocusManager);
+
+  let browserWindow = Cc["@mozilla.org/appshell/window-mediator;1"].
+                      getService(Ci.nsIWindowMediator).
+                      getMostRecentWindow("navigator:browser");
+
+  // Make sure there is a focused element
+  browserWindow.document.documentElement.focus();
+
+  // Get the current focused element
+  let focusedElement = FM.focusedElement;
 
+  let panel = Panel({
+    contentURL: "about:buildconfig",
+    focus: false,
+    onShow: function () {
+      assert.ok(focusedElement === FM.focusedElement,
+        "The panel does not take the focus away.");
+      done();
+    }
+  });
+  panel.show();
+};
 
+exports["test Panel Focus Not Set"] = function(assert, done) {
+  const { Panel } = require('sdk/panel');
+
+  const FM = Cc["@mozilla.org/focus-manager;1"].
+                getService(Ci.nsIFocusManager);
+
+  let browserWindow = Cc["@mozilla.org/appshell/window-mediator;1"].
+                      getService(Ci.nsIWindowMediator).
+                      getMostRecentWindow("navigator:browser");
+
+  // Make sure there is a focused element
+  browserWindow.document.documentElement.focus();
+
+  // Get the current focused element
+  let focusedElement = FM.focusedElement;
+
+  let panel = Panel({
+    contentURL: "about:buildconfig",
+    onShow: function () {
+      assert.ok(focusedElement !== FM.focusedElement,
+        "The panel takes the focus away.");
+      done();
+    }
+  });
+  panel.show();
 };
 
 exports["test Panel Text Color"] = function(assert, done) {
   const { Panel } = require('sdk/panel');
 
   let html = "<html><head><style>body {color: yellow}</style></head>" +
              "<body><p>Foo</p></body></html>";
   let panel = Panel({
@@ -513,17 +607,17 @@ exports["test console.log in Panel"] = f
   let { loader } = LoaderWithHookedConsole(module, onMessage);
   let { Panel } = loader.require('sdk/panel');
 
   let panel = Panel({
     contentURL: 'data:text/html;charset=utf-8,' + encodeURIComponent(html)
   });
 
   panel.show();
-  
+
   function onMessage(type, message) {
     assert.equal(type, 'log', 'console.log() works');
     assert.equal(message, text, 'console.log() works');
     panel.destroy();
     done();
   }
 };
 
@@ -653,16 +747,116 @@ function testShowPanel(assert, panel) {
 
     panel.hide();
   })
   panel.show();
 
   return promise;
 }
 
+exports['test Style Applied Only Once'] = function (assert, done) {
+  let loader = Loader(module);
+  let panel = loader.require("sdk/panel").Panel({
+    contentURL: "data:text/html;charset=utf-8,",
+    contentScript:
+      'self.port.on("check",function() { self.port.emit("count", document.getElementsByTagName("style").length); });' +
+      'self.port.on("ping", function (count) { self.port.emit("pong", count); });'
+  });
+  
+  panel.port.on('count', function (styleCount) {
+    assert.equal(styleCount, 1, 'should only have one style');
+    done();
+  });
+
+  panel.port.on('pong', function (counter) {
+    panel[--counter % 2 ? 'hide' : 'show']();
+    panel.port.emit(!counter ? 'check' : 'ping', counter);
+  });
+
+  panel.on('show', init);
+  panel.show();
+
+  function init () {
+    panel.removeListener('show', init);
+    panel.port.emit('ping', 10);
+  }
+};
+
+exports['test Only One Panel Open Concurrently'] = function (assert, done) {
+  const loader = Loader(module);
+  const { Panel } = loader.require('sdk/panel')
+
+  let panelA = Panel({
+    contentURL: 'about:buildconfig'
+  });
+
+  let panelB = Panel({
+    contentURL: 'about:buildconfig',
+    onShow: function () {
+      // When loading two panels simulataneously, only the second
+      // should be shown, never showing the first
+      assert.equal(panelA.isShowing, false, 'First panel is hidden');
+      assert.equal(panelB.isShowing, true, 'Second panel is showing');
+      panelC.show();
+    }
+  });
+
+  let panelC = Panel({
+    contentURL: 'about:buildconfig',
+    onShow: function () {
+      assert.equal(panelA.isShowing, false, 'First panel is hidden');
+      assert.equal(panelB.isShowing, false, 'Second panel is hidden');
+      assert.equal(panelC.isShowing, true, 'Third panel is showing');
+      done();
+    }
+  });
+
+  panelA.show();
+  panelB.show();
+};
+
+if (isWindowPBSupported) {
+  exports.testGetWindow = function(assert, done) {
+    let activeWindow = getMostRecentBrowserWindow();
+    open(null, { features: {
+      toolbar: true,
+      chrome: true,
+      private: true
+    } }).then(function(window) {
+      assert.ok(isPrivate(window), 'window is private');
+      assert.equal(getWindow(window.gBrowser), null, 'private window elements returns null');
+      assert.equal(getWindow(activeWindow.gBrowser), activeWindow, 'non-private window elements returns window');
+      close(window).then(done);
+    })
+  }
+}
+else if (isGlobalPBSupported) {
+  exports.testGetWindow = function(assert, done) {
+    let activeWindow = getMostRecentBrowserWindow();
+
+    assert.equal(getWindow(activeWindow.gBrowser), activeWindow, 'non-private window elements returns window');
+    pb.once('start', function() {
+      assert.ok(isPrivate(activeWindow), 'window is private');
+      assert.equal(getWindow(activeWindow.gBrowser), activeWindow, 'private window elements returns window');
+      open(null, { features: {
+        toolbar: true,
+        chrome: true
+      } }).then(function(window) {
+        assert.ok(isPrivate(window), 'window is private');
+        assert.equal(getWindow(window.gBrowser), window, 'private window elements returns window');
+        assert.equal(getWindow(activeWindow.gBrowser), activeWindow, 'active window elements returns window');
+        
+        pb.once('stop', done);
+        pb.deactivate();
+      })
+    });
+    pb.activate();
+  }
+}
+
 try {
   require("sdk/panel");
 }
 catch (e) {
   if (!/^Unsupported Application/.test(e.message))
     throw e;
 
   module.exports = {
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/test/test-tab-utils.js
@@ -0,0 +1,70 @@
+'use strict';
+
+const { getTabs } = require('sdk/tabs/utils');
+const { isGlobalPBSupported, isWindowPBSupported, isTabPBSupported } = require('sdk/private-browsing/utils');
+const { browserWindows } = require('sdk/windows');
+const tabs = require('sdk/tabs');
+const { pb } = require('./private-browsing/helper');
+const { isPrivate } = require('sdk/private-browsing');
+const { openTab } = require('sdk/tabs/utils');
+const { open, close } = require('sdk/window/helpers');
+const { windows } = require('sdk/window/utils');
+const { getMostRecentBrowserWindow } = require('sdk/window/utils');
+const { fromIterator } = require('sdk/util/array');
+
+if (isGlobalPBSupported) {
+  exports.testGetTabs = function(assert, done) {
+    pb.once('start', function() {
+      tabs.open({
+      	url: 'about:blank',
+      	inNewWindow: true,
+      	onOpen: function(tab) {
+          assert.equal(getTabs().length, 2, 'there are two tabs');
+          assert.equal(browserWindows.length, 2, 'there are two windows');
+          pb.once('stop', function() {
+          	done();
+          });
+          pb.deactivate();
+      	}
+      });
+    });
+    pb.activate();
+  };
+}
+else if (isWindowPBSupported) {
+  exports.testGetTabs = function(assert, done) {
+    open(null, {
+      features: {
+      	private: true,
+      	toolbar: true,
+      	chrome: true
+      }
+    }).then(function(window) {
+      assert.ok(isPrivate(window), 'new tab is private');
+      assert.equal(getTabs().length, 1, 'there is one tab found');
+      assert.equal(browserWindows.length, 1, 'there is one window found');
+      fromIterator(browserWindows).forEach(function(window) {
+        assert.ok(!isPrivate(window), 'all found windows are not private');
+      });
+      assert.equal(windows(null, {includePrivate: true}).length, 2, 'there are really two windows');
+      close(window).then(done);
+    });
+  };
+}
+else if (isTabPBSupported) {
+  exports.testGetTabs = function(assert, done) {
+    tabs.once('open', function(tab) {
+      assert.ok(isPrivate(tab), 'new tab is private');
+      assert.equal(getTabs().length, 2, 'there are two tabs found');
+      assert.equal(browserWindows.length, 1, 'there is one window');
+      tab.close(function() {
+        done();
+      });
+	});
+    openTab(getMostRecentBrowserWindow(), 'about:blank', {
+      isPrivate: true
+    });
+  };
+}
+
+require('test').run(exports);
--- a/addon-sdk/source/test/test-tabs-common.js
+++ b/addon-sdk/source/test/test-tabs-common.js
@@ -3,19 +3,20 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 'use strict';
 
 const { Loader, LoaderWithHookedConsole } = require("sdk/test/loader");
 const { browserWindows } = require('sdk/windows');
 const tabs = require('sdk/tabs');
 const { isPrivate } = require('sdk/private-browsing');
 const { openDialog } = require('sdk/window/utils');
-const pbUtils = require('sdk/private-browsing/utils');
 const { isWindowPrivate } = require('sdk/window/utils');
 const { setTimeout } = require('sdk/timers');
+const { openWebpage } = require('./private-browsing/helper');
+const { isTabPBSupported, isWindowPBSupported } = require('sdk/private-browsing/utils');
 
 const URL = 'data:text/html;charset=utf-8,<html><head><title>#title#</title></head></html>';
 
 // TEST: tab count
 exports.testTabCounts = function(test) {
   test.waitUntilDone();
 
   tabs.open({
@@ -318,44 +319,37 @@ exports.testTabOpenPrivate = function(te
         test.done();
       });
     }
   });
 }
 
 // We need permission flag in order to see private window's tabs
 exports.testPrivateAreNotListed = function (test) {
-  test.waitUntilDone();
   let originalTabCount = tabs.length;
 
-  let win = openDialog({
-    private: true
-  });
+  let page = openWebpage("about:blank", true);
+  if (!page) {
+    test.pass("Private browsing isn't supported in this release");
+    return;
+  }
 
-  win.addEventListener("load", function onload() {
-    win.removeEventListener("load", onload);
-
-    // PWPB case
-    if (pbUtils.isWindowPBSupported) {
-      test.assert(isWindowPrivate(win), "window is private");
+  test.waitUntilDone();
+  page.ready.then(function (win) {
+    if (isTabPBSupported || isWindowPBSupported) {
+      test.assert(isWindowPrivate(win), "the window is private");
       test.assertEqual(tabs.length, originalTabCount,
-                       'New private window\'s tab isn\'t visible in tabs list');
+                       'but the tab is *not* visible in tabs list');
     }
     else {
-    // Global case, openDialog didn't opened a private window/tab
-      test.assert(!isWindowPrivate(win), "window is private");
+      test.assert(!isWindowPrivate(win), "the window isn't private");
       test.assertEqual(tabs.length, originalTabCount + 1,
-                       'New non-private window\'s tab is visible in tabs list');
+                       'so that the tab is visible is tabs list');
     }
-
-    win.addEventListener("unload", function onunload() {
-      win.removeEventListener('unload', onunload);
-      test.done();
-    });
-    win.close();
+    page.close().then(test.done.bind(test));
   });
 }
 
 // If we close the tab while being in `onOpen` listener,
 // we end up synchronously consuming TabOpen, closing the tab and still
 // synchronously consuming the related TabClose event before the second
 // loader have a change to process the first TabOpen event!
 exports.testImmediateClosing = function (test) {
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/test/test-window-utils-global-private-browsing.js
@@ -0,0 +1,152 @@
+/* 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 windowUtils = require('sdk/deprecated/window-utils');
+const { isWindowPBSupported, isGlobalPBSupported } = require('sdk/private-browsing/utils');
+const { getFrames, getWindowTitle, onFocus, isWindowPrivate, windows, isBrowser } = require('sdk/window/utils');
+const { open, close, focus } = require('sdk/window/helpers');
+const { isPrivate } = require('sdk/private-browsing');
+const { pb } = require('./private-browsing/helper');
+const { fromIterator: toArray } = require('sdk/util/array');
+
+function makeEmptyBrowserWindow(options) {
+  options = options || {};
+  return open('chrome://browser/content/browser.xul', {
+    features: {
+      chrome: true,
+      private: !!options.private,
+      toolbar: true
+    }
+  });
+}
+
+exports.testShowPanelAndWidgetOnPrivateWindow = function(assert, done) {
+  var myPrivateWindow;
+  var finished = false;
+  var privateWindow;
+  var privateWindowClosed = false;
+  var { Panel } = require('sdk/panel');
+  var { Widget } = require('sdk/widget');
+
+  pb.once('start', function() {
+    assert.pass('private browsing mode started');
+
+    // make a new private window
+    makeEmptyBrowserWindow().then(function(window) {
+      myPrivateWindow = window;
+
+      let wt = windowUtils.WindowTracker({
+        onTrack: function(window) {
+          if (!isBrowser(window) || window !== myPrivateWindow) return;
+
+          assert.ok(isWindowPrivate(window), 'window is private onTrack!');
+          let panel = Panel({
+            onShow: function() {
+              assert.ok(this.isShowing, 'the panel is showing on the private window');
+
+              let count = 0;
+              let widget = Widget({
+                id: "testShowPanelAndWidgetOnPrivateWindow-id",
+                label: "My Hello Widget",
+                content: "Hello!",
+                onAttach: function(mod) {
+                  count++;
+                  if (count == 2) {
+                    panel.destroy();
+                    widget.destroy();
+                    close(window);
+                  }
+                }
+              });
+            }
+          }).show(window.gBrowser);
+        },
+        onUntrack: function(window) {
+          if (window === myPrivateWindow) {
+            wt.unload();
+
+            pb.once('stop', function() {
+              assert.pass('private browsing mode end');
+              done();
+            });
+
+            pb.deactivate();
+          }
+        }
+      });
+
+      assert.equal(isWindowPrivate(window), true, 'the opened window is private');
+      assert.equal(isPrivate(window), true, 'the opened window is private');
+      assert.ok(getFrames(window).length > 1, 'there are frames for private window');
+      assert.equal(getWindowTitle(window), window.document.title,
+                   'getWindowTitle works');
+    });
+  });
+  pb.activate();
+};
+
+exports.testWindowTrackerDoesNotIgnorePrivateWindows = function(assert, done) {
+  var myPrivateWindow;
+  var count = 0;
+
+  let wt = windowUtils.WindowTracker({
+    onTrack: function(window) {
+      if (!isBrowser(window) || !isWindowPrivate(window)) return;
+      assert.ok(isWindowPrivate(window), 'window is private onTrack!');
+      if (++count == 1)
+        close(window);
+    },
+    onUntrack: function(window) {
+      if (count == 1 && isWindowPrivate(window)) {
+        wt.unload();
+
+        pb.once('stop', function() {
+          assert.pass('private browsing mode end');
+          done();
+        });
+        pb.deactivate();
+      }
+    }
+  });
+
+  pb.once('start', function() {
+    assert.pass('private browsing mode started');
+    makeEmptyBrowserWindow();
+  });
+  pb.activate();
+}
+
+exports.testWindowIteratorDoesNotIgnorePrivateWindows = function(assert, done) {
+  pb.once('start', function() {
+    // make a new private window
+    makeEmptyBrowserWindow().then(function(window) {
+      assert.ok(isWindowPrivate(window), "window is private");
+      assert.equal(isPrivate(window), true, 'the opened window is private');
+      assert.ok(toArray(windowUtils.windowIterator()).indexOf(window) > -1,
+                "window is in windowIterator()");
+      assert.ok(windows(null, { includePrivate: true }).indexOf(window) > -1,
+                "window is in windows()");
+
+      close(window).then(function() {
+        pb.once('stop', function() {
+          done();
+        });
+        pb.deactivate();
+      });
+    });
+  });
+  pb.activate();
+};
+
+if (!isGlobalPBSupported) {
+  module.exports = {
+    "test Unsupported Test": function UnsupportedTest (assert) {
+        assert.pass(
+          "Skipping global private browsing tests");
+    }
+  }
+}
+
+require("test").run(exports);
--- a/addon-sdk/source/test/test-window-utils-private-browsing.js
+++ b/addon-sdk/source/test/test-window-utils-private-browsing.js
@@ -25,24 +25,20 @@ function makeEmptyBrowserWindow(options)
 exports.testWindowTrackerIgnoresPrivateWindows = function(assert, done) {
   var myNonPrivateWindow, myPrivateWindow;
   var finished = false;
   var privateWindow;
   var privateWindowClosed = false;
 
   let wt = windowUtils.WindowTracker({
     onTrack: function(window) {
-      if (isWindowPrivate(window)) {
-        assert.fail('private window was tracked!');
-      }
+      assert.ok(!isWindowPrivate(window), 'private window was not tracked!');
     },
     onUntrack: function(window) {
-      if (isWindowPrivate(window)) {
-        assert.fail('private window was tracked!');
-      }
+      assert.ok(!isWindowPrivate(window), 'private window was not tracked!');
       // PWPB case
       if (window === myPrivateWindow && isWindowPBSupported) {
         privateWindowClosed = true;
       }
       if (window === myNonPrivateWindow) {
         assert.ok(!privateWindowClosed);
         wt.unload();
         done();