Bug 852322: Uplift Add-on SDK changeset 4e4e29d60115b985a72c261142e794d23ca9b221
authorDave Townsend <dtownsend@oxymoronical.com>
Tue, 19 Mar 2013 11:44:49 -0700
changeset 125481 a929b767761abbab79a7c9d0a3658e6a9a137a5f
parent 125480 51069da75d3d54a98c6f6aed8da496b3bc3d048a
child 125482 1f9af1a5c45b2c5af7c4169457cbc5d32cb16540
push id24459
push useremorley@mozilla.com
push dateWed, 20 Mar 2013 11:46:36 +0000
treeherdermozilla-central@1d6fe70c79c5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs852322
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 852322: Uplift Add-on SDK changeset 4e4e29d60115b985a72c261142e794d23ca9b221 https://github.com/mozilla/addon-sdk/compare/645b0ca...4e4e29d
addon-sdk/source/doc/module-source/sdk/core/promise.md
addon-sdk/source/lib/method/.travis.yml
addon-sdk/source/lib/method/History.md
addon-sdk/source/lib/method/License.md
addon-sdk/source/lib/method/Readme.md
addon-sdk/source/lib/method/core.js
addon-sdk/source/lib/method/package.json
addon-sdk/source/lib/method/test/browser.js
addon-sdk/source/lib/method/test/common.js
addon-sdk/source/lib/sdk/addon/runner.js
addon-sdk/source/lib/sdk/addon/window.js
addon-sdk/source/lib/sdk/content/symbiont.js
addon-sdk/source/lib/sdk/core/heritage.js
addon-sdk/source/lib/sdk/core/promise.js
addon-sdk/source/lib/sdk/deprecated/unit-test.js
addon-sdk/source/lib/sdk/frame/hidden-frame.js
addon-sdk/source/lib/sdk/frame/utils.js
addon-sdk/source/lib/sdk/indexed-db.js
addon-sdk/source/lib/sdk/page-mod.js
addon-sdk/source/lib/sdk/panel.js
addon-sdk/source/lib/sdk/panel/window.js
addon-sdk/source/lib/sdk/private-browsing.js
addon-sdk/source/lib/sdk/selection.js
addon-sdk/source/lib/sdk/tabs/tab-firefox.js
addon-sdk/source/lib/sdk/test/assert.js
addon-sdk/source/lib/sdk/test/loader.js
addon-sdk/source/lib/sdk/window/utils.js
addon-sdk/source/lib/sdk/windows/tabs-firefox.js
addon-sdk/source/python-lib/cuddlefish/__init__.py
addon-sdk/source/test/addons/private-browsing-supported/main.js
addon-sdk/source/test/addons/private-browsing-supported/tabs.js
addon-sdk/source/test/addons/private-browsing-supported/test-page-mod.js
addon-sdk/source/test/addons/private-browsing-supported/test-panel.js
addon-sdk/source/test/addons/private-browsing-supported/test-selection.js
addon-sdk/source/test/addons/private-browsing-supported/test-tabs.js
addon-sdk/source/test/addons/private-browsing-supported/test-windows.js
addon-sdk/source/test/addons/private-browsing-supported/windows.js
addon-sdk/source/test/private-browsing/tabs.js
addon-sdk/source/test/test-cuddlefish.js
addon-sdk/source/test/test-heritage.js
addon-sdk/source/test/test-page-mod.js
addon-sdk/source/test/test-panel.js
addon-sdk/source/test/test-promise.js
addon-sdk/source/test/test-selection.js
addon-sdk/source/test/test-tabs-common.js
--- a/addon-sdk/source/doc/module-source/sdk/core/promise.md
+++ b/addon-sdk/source/doc/module-source/sdk/core/promise.md
@@ -216,16 +216,43 @@ This technique is so powerful that it ca
 functions provided by other promise libraries. For example grouping promises
 to observe single resolution of all of them is as simple as this:
 
     var group = promised(Array);
     var abc = group(aAsync, bAsync, cAsync).then(function(items) {
       return items[0] + items[1] + items[2];
     });
 
+## all
+
+The `all` function is provided to consume an array of promises and return 
+a promise that will be accepted upon the acceptance of all the promises
+in the initial array. This can be used to perform an action that requires
+values from several promises, like getting user information and server
+status, for example:
+
+    const { all } = require('sdk/core/promise');
+    all([getUser, getServerStatus]).then(function (result) {
+      return result[0] + result[1]
+    });
+
+If one of the promises in the array is rejected, the rejection handler 
+handles the first failed promise and remaining promises remain unfulfilled.
+
+    const { all } = require('sdk/core/promise');
+    all([aAsync, failAsync, bAsync]).then(function (result) {
+      // success function will not be called
+      return result;
+    }, function (reason) {
+      // rejection handler called because `failAsync` promise
+      // was rejected with its reason propagated
+      return reason;
+    });
+
+
 # Making promises
 
 Everything above assumes you get a promise from somewhere else. This
 is the common case, but every once in a while, you will need to create a
 promise from scratch. Add-on SDK's `promise` module provides API for doing
 that.
 
 ## defer
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/lib/method/.travis.yml
@@ -0,0 +1,5 @@
+language: node_js
+node_js:
+  - 0.4
+  - 0.5
+  - 0.6
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/lib/method/History.md
@@ -0,0 +1,55 @@
+# Changes
+
+## 1.0.2 / 2012-12-26
+
+  - Delegate to polymorphic methods from `.define` and `.implement` so, they
+    can be overidden.
+
+## 1.0.1 / 2012-11-11
+
+  - Fix issues with different `Error` types as they all inherit from
+    `Error`.
+
+## 1.0.0 / 2012-11-09
+
+  - Add browser test integration.
+  - Fix cross-browser incompatibilities & test failures.
+  - Add support for host objects.
+  - Add optional `hint` argument for method to ease debugging.
+  - Remove default implementation at definition time.
+
+## 0.1.1 / 2012-10-15
+
+ - Fix regression causing custom type implementation to be stored on objects.
+
+## 0.1.0 / 2012-10-15
+
+ - Remove dependency on name module.
+ - Implement fallback for engines that do not support ES5.
+ - Add support for built-in type extensions without extending their prototypes.
+ - Make API for default definitions more intuitive.
+   Skipping type argument now defines default:
+
+      isFoo.define(function(value) {
+        return false
+      })
+
+ - Make exposed `define` and `implement` polymorphic.
+ - Removed dev dependency on swank-js.
+ - Primitive types `string, number, boolean` no longer inherit method
+   implementations from `Object`.
+
+## 0.0.3 / 2012-07-17
+
+  - Remove module boilerplate
+
+## 0.0.2 / 2012-06-26
+
+  - Name changes to make it less conflicting with other library conventions.
+  - Expose function version of `define` & `implement` methods.
+  - Expose `Null` and `Undefined` object holding implementations for an
+    associated types.
+
+## 0.0.1 / 2012-06-25
+
+  - Initial release
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/lib/method/License.md
@@ -0,0 +1,18 @@
+Copyright 2012 Irakli Gozalishvili. All rights reserved.
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/lib/method/Readme.md
@@ -0,0 +1,117 @@
+# method
+
+[![Build Status](https://secure.travis-ci.org/Gozala/method.png)](http://travis-ci.org/Gozala/method)
+
+Library provides an API for defining polymorphic methods that dispatch on the
+first argument type. This provides a powerful way for decouple abstraction
+interface definition from an actual implementation per type, without risks
+of interference with other libraries.
+
+### Motivation
+
+  - Provide a high-performance, dynamic polymorphism construct as an
+    alternative to existing object methods that does not provides any
+    mechanics for guarding against name conflicts.
+  - Allow independent extension of types, and implementations of methods
+    on types, by different parties.
+
+## Install
+
+    npm install method
+
+## Use
+
+```js
+var method = require("method")
+
+// Define `isWatchable` method that can be implemented for any type.
+var isWatchable = method("isWatchable")
+
+// If you call it on any object it will
+// throw as nothing implements that method yet.
+//isWatchable({}) // => Exception: method is not implemented
+
+// If you define private method on `Object.prototype`
+// all objects will inherit it.
+Object.prototype[isWatchable] = function() {
+  return false;
+}
+
+isWatchable({}) // => false
+
+
+// Although `isWatchable` property above will be enumerable and there for
+// may damage some assumbtions made by other libraries. There for it"s
+// recomended to use built-in helpers methods that will define extension
+// without breaking assumbtions made by other libraries:
+
+isWatchable.define(Object, function() { return false })
+
+
+// There are primitive types in JS that won"t inherit methods from Object:
+isWatchable(null) // => Exception: method is not implemented
+
+// One could either implement methods for such types:
+isWatchable.define(null, function() { return false })
+isWatchable.define(undefined, function() { return false })
+
+// Or simply define default implementation:
+isWatchable.define(function() { return false })
+
+// Alternatively default implementation may be provided at creation:
+isWatchable = method(function() { return false })
+
+// Method dispatches on an first argument type. That allows us to create
+// new types with an alternative implementations:
+function Watchable() {}
+isWatchable.define(Watchable, function() { return true })
+
+// This will make all `Watchable` instances watchable!
+isWatchable(new Watchable()) // => true
+
+// Arbitrary objects can also be extended to implement given method. For example
+// any object can simply made watchable:
+function watchable(object) {
+  return isWatchable.implement(objct, function() { return true })
+}
+
+isWatchable(watchable({})) // => true
+
+// Full protocols can be defined with such methods:
+var observers = "observers@" + module.filename
+var watchers = method("watchers")
+var watch = method("watch")
+var unwatch = method("unwatch")
+
+watchers.define(Watchable, function(target) {
+  return target[observers] || (target[observers] = [])
+})
+
+watch.define(Watchable, function(target, watcher) {
+  var observers = watchers(target)
+  if (observers.indexOf(watcher) < 0) observers.push(watcher)
+  return target
+})
+unwatch.define(Watchable, function(target, watcher) {
+  var observers = watchers(target)
+  var index = observers.indexOf(watcher)
+  if (observers.indexOf(watcher) >= 0) observers.unshift(watcher)
+  return target
+})
+
+// Define type Port that inherits form Watchable
+
+function Port() {}
+Port.prototype = Object.create(Watchable.prototype)
+
+var emit = method("emit")
+emit.define(Port, function(port, message) {
+  watchers(port).slice().forEach(function(watcher) {
+    watcher(message)
+  })
+})
+
+var p = new Port()
+watch(p, console.log)
+emit(p, "hello world") // => info: "hello world"
+```
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/lib/method/core.js
@@ -0,0 +1,225 @@
+"use strict";
+
+var defineProperty = Object.defineProperty || function(object, name, property) {
+  object[name] = property.value
+  return object
+}
+
+// Shortcut for `Object.prototype.toString` for faster access.
+var typefy = Object.prototype.toString
+
+// Map to for jumping from typeof(value) to associated type prefix used
+// as a hash in the map of builtin implementations.
+var types = { "function": "Object", "object": "Object" }
+
+// Array is used to save method implementations for the host objects in order
+// to avoid extending them with non-primitive values that could cause leaks.
+var host = []
+// Hash map is used to save method implementations for builtin types in order
+// to avoid extending their prototypes. This also allows to share method
+// implementations for types across diff contexts / frames / compartments.
+var builtin = {}
+
+function Primitive() {}
+function ObjectType() {}
+ObjectType.prototype = new Primitive()
+function ErrorType() {}
+ErrorType.prototype = new ObjectType()
+
+var Default = builtin.Default = Primitive.prototype
+var Null = builtin.Null = new Primitive()
+var Void = builtin.Void = new Primitive()
+builtin.String = new Primitive()
+builtin.Number = new Primitive()
+builtin.Boolean = new Primitive()
+
+builtin.Object = ObjectType.prototype
+builtin.Error = ErrorType.prototype
+
+builtin.EvalError = new ErrorType()
+builtin.InternalError = new ErrorType()
+builtin.RangeError = new ErrorType()
+builtin.ReferenceError = new ErrorType()
+builtin.StopIteration = new ErrorType()
+builtin.SyntaxError = new ErrorType()
+builtin.TypeError = new ErrorType()
+builtin.URIError = new ErrorType()
+
+
+function Method(hint) {
+  /**
+  Private Method is a callable private name that dispatches on the first
+  arguments same named Method:
+
+      method(object, ...rest) => object[method](...rest)
+
+  Optionally hint string may be provided that will be used in generated names
+  to ease debugging.
+
+  ## Example
+
+      var foo = Method()
+
+      // Implementation for any types
+      foo.define(function(value, arg1, arg2) {
+        // ...
+      })
+
+      // Implementation for a specific type
+      foo.define(BarType, function(bar, arg1, arg2) {
+        // ...
+      })
+  **/
+
+  // Create an internal unique name if `hint` is provided it is used to
+  // prefix name to ease debugging.
+  var name = (hint || "") + "#" + Math.random().toString(32).substr(2)
+
+  function dispatch(value) {
+    // Method dispatches on type of the first argument.
+    // If first argument is `null` or `void` associated implementation is
+    // looked up in the `builtin` hash where implementations for built-ins
+    // are stored.
+    var type = null
+    var method = value === null ? Null[name] :
+                 value === void(0) ? Void[name] :
+                 // Otherwise attempt to use method with a generated private
+                 // `name` that is supposedly in the prototype chain of the
+                 // `target`.
+                 value[name] ||
+                 // Otherwise assume it's one of the built-in type instances,
+                 // in which case implementation is stored in a `builtin` hash.
+                 // Attempt to find a implementation for the given built-in
+                 // via constructor name and method name.
+                 ((type = builtin[(value.constructor || "").name]) &&
+                  type[name]) ||
+                 // Otherwise assume it's a host object. For host objects
+                 // actual method implementations are stored in the `host`
+                 // array and only index for the implementation is stored
+                 // in the host object's prototype chain. This avoids memory
+                 // leaks that otherwise could happen when saving JS objects
+                 // on host object.
+                 host[value["!" + name]] ||
+                 // Otherwise attempt to lookup implementation for builtins by
+                 // a type of the value. This basically makes sure that all
+                 // non primitive values will delegate to an `Object`.
+                 ((type = builtin[types[typeof(value)]]) && type[name])
+
+
+    // If method implementation for the type is still not found then
+    // just fallback for default implementation.
+    method = method || Default[name]
+
+
+    // If implementation is still not found (which also means there is no
+    // default) just throw an error with a descriptive message.
+    if (!method) throw TypeError("Type does not implements method: " + name)
+
+    // If implementation was found then just delegate.
+    return method.apply(method, arguments)
+  }
+
+  // Make `toString` of the dispatch return a private name, this enables
+  // method definition without sugar:
+  //
+  //    var method = Method()
+  //    object[method] = function() { /***/ }
+  dispatch.toString = function toString() { return name }
+
+  // Copy utility methods for convenient API.
+  dispatch.implement = implementMethod
+  dispatch.define = defineMethod
+
+  return dispatch
+}
+
+// Create method shortcuts form functions.
+var defineMethod = function defineMethod(Type, lambda) {
+  return define(this, Type, lambda)
+}
+var implementMethod = function implementMethod(object, lambda) {
+  return implement(this, object, lambda)
+}
+
+// Define `implement` and `define` polymorphic methods to allow definitions
+// and implementations through them.
+var implement = Method("implement")
+var define = Method("define")
+
+
+function _implement(method, object, lambda) {
+  /**
+  Implements `Method` for the given `object` with a provided `implementation`.
+  Calling `Method` with `object` as a first argument will dispatch on provided
+  implementation.
+  **/
+  return defineProperty(object, method.toString(), {
+    enumerable: false,
+    configurable: false,
+    writable: false,
+    value: lambda
+  })
+}
+
+function _define(method, Type, lambda) {
+  /**
+  Defines `Method` for the given `Type` with a provided `implementation`.
+  Calling `Method` with a first argument of this `Type` will dispatch on
+  provided `implementation`. If `Type` is a `Method` default implementation
+  is defined. If `Type` is a `null` or `undefined` `Method` is implemented
+  for that value type.
+  **/
+
+  // Attempt to guess a type via `Object.prototype.toString.call` hack.
+  var type = Type && typefy.call(Type.prototype)
+
+  // If only two arguments are passed then `Type` is actually an implementation
+  // for a default type.
+  if (!lambda) Default[method] = Type
+  // If `Type` is `null` or `void` store implementation accordingly.
+  else if (Type === null) Null[method] = lambda
+  else if (Type === void(0)) Void[method] = lambda
+  // If `type` hack indicates built-in type and type has a name us it to
+  // store a implementation into associated hash. If hash for this type does
+  // not exists yet create one.
+  else if (type !== "[object Object]" && Type.name) {
+    var Bulitin = builtin[Type.name] || (builtin[Type.name] = new ObjectType())
+    Bulitin[method] = lambda
+  }
+  // If `type` hack indicates an object, that may be either object or any
+  // JS defined "Class". If name of the constructor is `Object`, assume it's
+  // built-in `Object` and store implementation accordingly.
+  else if (Type.name === "Object")
+    builtin.Object[method] = lambda
+  // Host objects are pain!!! Every browser does some crazy stuff for them
+  // So far all browser seem to not implement `call` method for host object
+  // constructors. If that is a case here, assume it's a host object and
+  // store implementation in a `host` array and store `index` in the array
+  // in a `Type.prototype` itself. This avoids memory leaks that could be
+  // caused by storing JS objects on a host objects.
+  else if (Type.call === void(0)) {
+    var index = host.indexOf(lambda)
+    if (index < 0) index = host.push(lambda) - 1
+    // Prefix private name with `!` so it can be dispatched from the method
+    // without type checks.
+    implement("!" + method, Type.prototype, index)
+  }
+  // If Got that far `Type` is user defined JS `Class`. Define private name
+  // as hidden property on it's prototype.
+  else
+    implement(method, Type.prototype, lambda)
+}
+
+// And provided implementations for a polymorphic equivalents.
+_define(define, _define)
+_define(implement, _implement)
+
+// Define exports on `Method` as it's only thing being exported.
+Method.implement = implement
+Method.define = define
+Method.Method = Method
+Method.method = Method
+Method.builtin = builtin
+Method.host = host
+
+module.exports = Method
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/lib/method/package.json
@@ -0,0 +1,41 @@
+{
+  "name": "method",
+  "id": "method",
+  "version": "1.0.2",
+  "description": "Functional polymorphic method dispatch",
+  "keywords": [
+    "method",
+    "dispatch",
+    "protocol",
+    "polymorphism",
+    "type dispatch"
+  ],
+  "author": "Irakli Gozalishvili <rfobic@gmail.com> (http://jeditoolkit.com)",
+  "homepage": "https://github.com/Gozala/method",
+  "main": "./core.js",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/Gozala/method.git",
+    "web": "https://github.com/Gozala/method"
+  },
+  "bugs": {
+    "url": "http://github.com/Gozala/method/issues/"
+  },
+  "devDependencies": {
+    "test": "~0.x.0",
+    "repl-utils": "~2.0.1",
+    "phantomify": "~0.1.0"
+  },
+  "scripts": {
+    "test": "npm run test-node && npm run test-browser",
+    "test-browser": "node ./node_modules/phantomify/bin/cmd.js ./test/browser.js",
+    "test-node": "node ./test/common.js",
+    "repl": "node node_modules/repl-utils"
+  },
+  "licenses": [
+    {
+      "type": "MIT",
+      "url": "https://github.com/Gozala/method/License.md"
+    }
+  ]
+}
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/lib/method/test/browser.js
@@ -0,0 +1,20 @@
+"use strict";
+
+exports["test common"] = require("./common")
+
+var Method = require("../core")
+
+exports["test host objects"] = function(assert) {
+  var isElement = Method("is-element")
+  isElement.define(function() { return false })
+
+  isElement.define(Element, function() { return true })
+
+  assert.notDeepEqual(typeof(Element.prototype[isElement]), "number",
+                     "Host object's prototype is extended with a number value")
+
+  assert.ok(!isElement({}), "object is not an Element")
+  assert.ok(document.createElement("div"), "Element is an element")
+}
+
+require("test").run(exports)
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/lib/method/test/common.js
@@ -0,0 +1,272 @@
+"use strict";
+
+var Method = require("../core")
+
+function type(value) {
+  return Object.prototype.toString.call(value).
+    split(" ").
+    pop().
+    split("]").
+    shift().
+    toLowerCase()
+}
+
+var values = [
+  null,                   // 0
+  undefined,              // 1
+  Infinity,               // 2
+  NaN,                    // 3
+  5,                      // 4
+  {},                     // 5
+  Object.create({}),      // 6
+  Object.create(null),    // 7
+  [],                     // 8
+  /foo/,                  // 9
+  new Date(),             // 10
+  Function,               // 11
+  function() {},          // 12
+  true,                   // 13
+  false,                  // 14
+  "string"                // 15
+]
+
+function True() { return true }
+function False() { return false }
+
+var trues = values.map(True)
+var falses = values.map(False)
+
+exports["test throws if not implemented"] = function(assert) {
+  var method = Method("nope")
+
+  assert.throws(function() {
+    method({})
+  }, /not implement/i, "method throws if not implemented")
+
+  assert.throws(function() {
+    method(null)
+  }, /not implement/i, "method throws on null")
+}
+
+exports["test all types inherit from default"] = function(assert) {
+  var isImplemented = Method("isImplemented")
+  isImplemented.define(function() { return true })
+
+  values.forEach(function(value) {
+    assert.ok(isImplemented(value),
+              type(value) + " inherits deafult implementation")
+  })
+}
+
+exports["test default can be implemented later"] = function(assert) {
+  var isImplemented = Method("isImplemented")
+  isImplemented.define(function() {
+    return true
+  })
+
+  values.forEach(function(value) {
+    assert.ok(isImplemented(value),
+              type(value) + " inherits deafult implementation")
+  })
+}
+
+exports["test dispatch not-implemented"] = function(assert) {
+  var isDefault = Method("isDefault")
+  values.forEach(function(value) {
+    assert.throws(function() {
+      isDefault(value)
+    }, /not implement/, type(value) + " throws if not implemented")
+  })
+}
+
+exports["test dispatch default"] = function(assert) {
+  var isDefault = Method("isDefault")
+
+  // Implement default
+  isDefault.define(True)
+  assert.deepEqual(values.map(isDefault), trues,
+                   "all implementation inherit from default")
+
+}
+
+exports["test dispatch null"] = function(assert) {
+  var isNull = Method("isNull")
+
+  // Implement default
+  isNull.define(False)
+  isNull.define(null, True)
+  assert.deepEqual(values.map(isNull),
+                   [ true ].
+                   concat(falses.slice(1)),
+                   "only null gets methods defined for null")
+}
+
+exports["test dispatch undefined"] = function(assert) {
+  var isUndefined = Method("isUndefined")
+
+  // Implement default
+  isUndefined.define(False)
+  isUndefined.define(undefined, True)
+  assert.deepEqual(values.map(isUndefined),
+                   [ false, true ].
+                   concat(falses.slice(2)),
+                   "only undefined gets methods defined for undefined")
+}
+
+exports["test dispatch object"] = function(assert) {
+  var isObject = Method("isObject")
+
+  // Implement default
+  isObject.define(False)
+  isObject.define(Object, True)
+  assert.deepEqual(values.map(isObject),
+                   [ false, false, false, false, false ].
+                   concat(trues.slice(5, 13)).
+                   concat([false, false, false]),
+                   "all values except primitives inherit Object methods")
+
+}
+
+exports["test dispatch number"] = function(assert) {
+  var isNumber = Method("isNumber")
+  isNumber.define(False)
+  isNumber.define(Number, True)
+
+  assert.deepEqual(values.map(isNumber),
+                  falses.slice(0, 2).
+                  concat(true, true, true).
+                  concat(falses.slice(5)),
+                  "all numbers inherit from Number method")
+}
+
+exports["test dispatch string"] = function(assert) {
+  var isString = Method("isString")
+  isString.define(False)
+  isString.define(String, True)
+
+  assert.deepEqual(values.map(isString),
+                  falses.slice(0, 15).
+                  concat(true),
+                  "all strings inherit from String method")
+}
+
+exports["test dispatch function"] = function(assert) {
+  var isFunction = Method("isFunction")
+  isFunction.define(False)
+  isFunction.define(Function, True)
+
+  assert.deepEqual(values.map(isFunction),
+                  falses.slice(0, 11).
+                  concat(true, true).
+                  concat(falses.slice(13)),
+                  "all functions inherit from Function method")
+}
+
+exports["test dispatch date"] = function(assert) {
+  var isDate = Method("isDate")
+  isDate.define(False)
+  isDate.define(Date, True)
+
+  assert.deepEqual(values.map(isDate),
+                  falses.slice(0, 10).
+                  concat(true).
+                  concat(falses.slice(11)),
+                  "all dates inherit from Date method")
+}
+
+exports["test dispatch RegExp"] = function(assert) {
+  var isRegExp = Method("isRegExp")
+  isRegExp.define(False)
+  isRegExp.define(RegExp, True)
+
+  assert.deepEqual(values.map(isRegExp),
+                  falses.slice(0, 9).
+                  concat(true).
+                  concat(falses.slice(10)),
+                  "all regexps inherit from RegExp method")
+}
+
+exports["test redefine for descendant"] = function(assert) {
+  var isFoo = Method("isFoo")
+  var ancestor = {}
+  isFoo.implement(ancestor, function() { return true })
+  var descendant = Object.create(ancestor)
+  isFoo.implement(descendant, function() { return false })
+
+  assert.ok(isFoo(ancestor), "defined on ancestor")
+  assert.ok(!isFoo(descendant), "overrided for descendant")
+}
+
+exports["test on custom types"] = function(assert) {
+  function Bar() {}
+  var isBar = Method("isBar")
+
+  isBar.define(function() { return false })
+  isBar.define(Bar, function() { return true })
+
+  assert.ok(!isBar({}), "object is get's default implementation")
+  assert.ok(isBar(new Bar()), "Foo type objects get own implementation")
+
+  var isObject = Method("isObject")
+  isObject.define(function() { return false })
+  isObject.define(Object, function() { return true })
+
+  assert.ok(isObject(new Bar()), "foo inherits implementation from object")
+
+
+  isObject.define(Bar, function() { return false })
+
+  assert.ok(!isObject(new Bar()),
+            "implementation inherited form object can be overrided")
+}
+
+
+exports["test error types"] = function(assert) {
+  var isError = Method("isError")
+  isError.define(function() { return false })
+  isError.define(Error, function() { return true })
+
+  assert.ok(isError(Error("boom")), "error is error")
+  assert.ok(isError(TypeError("boom")), "type error is an error")
+  assert.ok(isError(EvalError("boom")), "eval error is an error")
+  assert.ok(isError(RangeError("boom")), "range error is an error")
+  assert.ok(isError(ReferenceError("boom")), "reference error is an error")
+  assert.ok(isError(SyntaxError("boom")), "syntax error is an error")
+  assert.ok(isError(URIError("boom")), "URI error is an error")
+}
+
+exports["test override define polymorphic method"] = function(assert) {
+  var define = Method.define
+  var implement = Method.implement
+
+  var fn = Method("fn")
+  var methods = {}
+  implement(define, fn, function(method, label, implementation) {
+    methods[label] = implementation
+  })
+
+  function foo() {}
+
+  define(fn, "foo-case", foo)
+
+  assert.equal(methods["foo-case"], foo, "define set property")
+}
+
+exports["test override define via method API"] = function(assert) {
+  var define = Method.define
+  var implement = Method.implement
+
+  var fn = Method("fn")
+  var methods = {}
+  define.implement(fn, function(method, label, implementation) {
+    methods[label] = implementation
+  })
+
+  function foo() {}
+
+  define(fn, "foo-case", foo)
+
+  assert.equal(methods["foo-case"], foo, "define set property")
+}
+
+require("test").run(exports)
--- a/addon-sdk/source/lib/sdk/addon/runner.js
+++ b/addon-sdk/source/lib/sdk/addon/runner.js
@@ -71,29 +71,34 @@ function wait(reason, options) {
 
 function startup(reason, options) {
   if (reason === 'startup')
     return wait(reason, options);
 
   // Inject globals ASAP in order to have console API working ASAP
   Object.defineProperties(options.loader.globals, descriptor(globals));
 
+  // NOTE: Module is intentionally required only now because it relies
+  // on existence of hidden window, which does not exists until startup.
+  let { ready } = require('../addon/window');
   // Load localization manifest and .properties files.
   // Run the addon even in case of error (best effort approach)
   require('../l10n/loader').
     load(rootURI).
     then(null, function failure(error) {
       console.info("Error while loading localization: " + error.message);
     }).
     then(function onLocalizationReady(data) {
       // Exports data to a pseudo module so that api-utils/l10n/core
       // can get access to it
       definePseudo(options.loader, '@l10n/data', data ? data : null);
+      return ready;
+    }).then(function() {
       run(options);
-    });
+    }).then(null, console.exception);
 }
 
 function run(options) {
   try {
     // Try initializing HTML localization before running main module. Just print
     // an exception in case of error, instead of preventing addon to be run.
     try {
       // Do not enable HTML localization while running test as it is hard to
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/lib/sdk/addon/window.js
@@ -0,0 +1,57 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+"use strict";
+
+module.metadata = {
+  "stability": "experimental"
+};
+
+const { Ci, Cc } = require("chrome");
+const { make: makeWindow, getHiddenWindow } = require("../window/utils");
+const { create: makeFrame, getDocShell } = require("../frame/utils");
+const { defer } = require("../core/promise");
+const { when: unload } = require("../system/unload");
+
+let addonPrincipal = Cc["@mozilla.org/systemprincipal;1"].
+                     createInstance(Ci.nsIPrincipal);
+
+// Once Bug 565388 is fixed and shipped we'll be able to make invisible,
+// permanent docShells. Meanwhile we create hidden top level window and
+// use it's docShell.
+let frame = makeFrame(getHiddenWindow().document, {
+  nodeName: "iframe",
+  namespaceURI: "http://www.w3.org/1999/xhtml",
+  allowJavascript: true,
+  allowPlugins: true
+})
+let docShell = getDocShell(frame);
+let eventTarget = docShell.chromeEventHandler;
+
+// We need to grant docShell system principals in order to load XUL document
+// from data URI into it.
+docShell.createAboutBlankContentViewer(addonPrincipal);
+
+// Get a reference to the DOM window of the given docShell and load
+// such document into that would allow us to create XUL iframes, that
+// are necessary for hidden frames etc..
+let window = docShell.contentViewer.DOMDocument.defaultView;
+window.location = "data:application/vnd.mozilla.xul+xml;charset=utf-8,<window/>";
+
+// Create a promise that is delivered once add-on window is interactive,
+// used by add-on runner to defer add-on loading until window is ready.
+let { promise, resolve } = defer();
+eventTarget.addEventListener("DOMContentLoaded", function handler(event) {
+  eventTarget.removeEventListener("DOMContentLoaded", handler, false);
+  resolve();
+}, false);
+
+
+
+exports.ready = promise;
+exports.window = window;
+
+// Still close window on unload to claim memory back early.
+unload(function() { window.close() });
--- a/addon-sdk/source/lib/sdk/content/symbiont.js
+++ b/addon-sdk/source/lib/sdk/content/symbiont.js
@@ -10,16 +10,17 @@ module.metadata = {
 };
 
 const { Worker } = require('./worker');
 const { Loader } = require('./loader');
 const hiddenFrames = require('../frame/hidden-frame');
 const observers = require('../deprecated/observer-service');
 const unload = require('../system/unload');
 const { getDocShell } = require("../frame/utils");
+const { ignoreWindow } = require('../private-browsing/utils');
 
 const assetsURI = require('../self').data.url();
 
 /**
  * This trait is layered on top of `Worker` and in contrast to symbiont
  * Worker constructor requires `content` option that represents content
  * that will be loaded in the provided frame, if frame is not provided
  * Worker will create hidden one.
@@ -102,16 +103,17 @@ const Symbiont = Worker.resolve({
    * Listener to the `'frameReady"` event (emitted when `iframe` is ready).
    * Removes listener, sets right permissions to the frame and loads content.
    */
   _initFrame: function _initFrame(frame) {
     if (this._loadListener)
       this._unregisterListener();
     
     this._frame = frame;
+
     getDocShell(frame).allowJavascript = this.allow.script;
     frame.setAttribute("src", this._contentURL);
 
     // Inject `addon` object in document if we load a document from
     // one of our addon folder and if no content script are defined. bug 612726
     let isDataResource =
       typeof this._contentURL == "string" &&
       this._contentURL.indexOf(assetsURI) == 0;
@@ -137,18 +139,22 @@ const Symbiont = Worker.resolve({
     }
     
     let self = this;
     
     if ('start' == this.contentScriptWhen) {
       this._loadEvent = 'start';
       observers.add('document-element-inserted', 
         this._loadListener = function onStart(doc) {
-          
           let window = doc.defaultView;
+
+          if (ignoreWindow(window)) {
+            return;
+          }
+
           if (window && window == frame.contentWindow) {
             self._unregisterListener();
             self._onInit();
           }
           
         });
       return;
     }
--- a/addon-sdk/source/lib/sdk/core/heritage.js
+++ b/addon-sdk/source/lib/sdk/core/heritage.js
@@ -93,17 +93,17 @@ var mix = function(source) {
 };
 exports.mix = mix;
 
 /**
  * Returns a frozen object with that inherits from the given `prototype` and
  * implements all own properties of the given `properties` object.
  */
 function extend(prototype, properties) {
-  return freeze(create(prototype, getOwnPropertyDescriptors(properties)));
+  return create(prototype, getOwnPropertyDescriptors(properties));
 }
 exports.extend = extend;
 
 /**
  * Returns a constructor function with a proper `prototype` setup. Returned
  * constructor's `prototype` inherits from a given `options.extends` or
  * `Class.prototype` if omitted and implements all the properties of the
  * given `option`. If `options.implemens` array is passed, it's elements
--- a/addon-sdk/source/lib/sdk/core/promise.js
+++ b/addon-sdk/source/lib/sdk/core/promise.js
@@ -50,34 +50,16 @@ function fulfilled(value) {
  * with a given `reason`. Note the result is not a complete promise
  * implementation, as its method `then` does not returns anything.
  */
 function rejected(reason) {
   return { then: function then(fulfill, reject) { reject(reason); } };
 }
 
 /**
- * Internal utility: Decorates given `f` function, so that on exception promise
- * rejected with thrown error is returned.
- */
-function attempt(f) {
-  return function effort(input) {
-    try {
-      return f(input);
-    }
-    catch(error) {
-      if (exports._reportErrors && typeof(console) === 'object') {
-        console.error(error)
-      }
-      return rejected(error)
-    }
-  };
-}
-
-/**
  * Internal utility: Returns `true` if given `value` is a promise. Value is
  * assumed to be a promise if it implements method `then`.
  */
 function isPromise(value) {
   return value && typeof(value.then) === 'function';
 }
 
 /**
@@ -125,42 +107,57 @@ function defer(prototype) {
 
   prototype = (prototype || prototype === null) ? prototype : Object.prototype
 
   // Create an object implementing promise API.
   var promise = Object.create(prototype, {
     then: { value: function then(onFulfill, onError) {
       var deferred = defer(prototype);
 
-      // Decorate `onFulfill` / `onError` handlers with `attempt`, that
-      // way if wrapped handler throws exception decorator will catch and
-      // return promise rejected with it, which will cause rejection of
-      // `deferred.promise`. If handler is missing, substitute it with an
-      // utility function that takes one argument and returns promise
-      // fulfilled / rejected with it. This takes care of propagation
-      // through the rest of the promise chain.
-      onFulfill = onFulfill ? attempt(onFulfill) : fulfilled;
-      onError = onError ? attempt(onError) : rejected;
+      function resolve(value) {
+        // If `onFulfill` handler is provided resolve `deferred.promise` with
+        // result of invoking it with a resolution value. If handler is not
+        // provided propagate value through.
+        try {
+          deferred.resolve(onFulfill ? onFulfill(value) : value);
+        }
+        // `onFulfill` may throw exception in which case resulting promise
+        // is rejected with thrown exception.
+        catch(error) {
+          if (exports._reportErrors && typeof(console) === 'object')
+            console.error(error);
+          // Note: Following is equivalent of `deferred.reject(error)`,
+          // we use this shortcut to reduce a stack.
+          deferred.resolve(rejected(error));
+        }
+      }
 
-      // Create a pair of observers that invoke given handlers & propagate
-      // results to `deferred.promise`.
-      function resolveDeferred(value) { deferred.resolve(onFulfill(value)); }
-      function rejectDeferred(reason) { deferred.resolve(onError(reason)); }
+      function reject(reason) {
+        try {
+          if (onError) deferred.resolve(onError(reason));
+          else deferred.resolve(rejected(reason));
+        }
+        catch(error) {
+          if (exports._reportErrors && typeof(console) === 'object')
+            console.error(error)
+          deferred.resolve(rejected(error));
+        }
+      }
 
       // If enclosed promise (`this.promise`) observers queue is still alive
       // enqueue a new observer pair into it. Note that this does not
       // necessary means that promise is pending, it may already be resolved,
       // but we still have to queue observers to guarantee an order of
       // propagation.
       if (observers) {
-        observers.push({ resolve: resolveDeferred, reject: rejectDeferred });
+        observers.push({ resolve: resolve, reject: reject });
       }
       // Otherwise just forward observer pair right to a `result` promise.
       else {
-        result.then(resolveDeferred, rejectDeferred);
+        result.then(resolve, reject);
       }
 
       return deferred.promise;
     }}
   })
 
   var deferred = {
     promise: promise,
@@ -284,9 +281,12 @@ var promised = (function() {
         reduce(promisedConcat, resolve([], prototype)).
         // finally map that to promise of `f.apply(this, args...)`
         then(execute)
     }
   }
 })()
 exports.promised = promised;
 
+var all = promised(Array);
+exports.all = all;
+
 });
--- a/addon-sdk/source/lib/sdk/deprecated/unit-test.js
+++ b/addon-sdk/source/lib/sdk/deprecated/unit-test.js
@@ -104,16 +104,18 @@ TestRunner.prototype = {
   expectFail: function(callback) {
     this.expectFailure = true;
     callback();
     this.expectFailure = false;
   },
 
   exception: function exception(e) {
     this._logTestFailed("exception");
+    if (cfxArgs.parseable)
+      this.console.print("TEST-UNEXPECTED-FAIL | " + this.test.name + " | " + e + "\n");
     this.console.exception(e);
     this.failed++;
     this.test.failed++;
   },
 
   assertMatches: function assertMatches(string, regexp, message) {
     if (regexp.test(string)) {
       if (!message)
--- a/addon-sdk/source/lib/sdk/frame/hidden-frame.js
+++ b/addon-sdk/source/lib/sdk/frame/hidden-frame.js
@@ -12,33 +12,20 @@ module.metadata = {
 
 const { Cc, Ci } = require("chrome");
 const errors = require("../deprecated/errors");
 const { Class } = require("../core/heritage");
 const { List, addListItem, removeListItem } = require("../util/list");
 const { EventTarget } = require("../event/target");
 const { emit } = require("../event/core");
 const { create: makeFrame } = require("./utils");
-const { defer, resolve } = require("../core/promise");
+const { defer } = require("../core/promise");
 const { when: unload } = require("../system/unload");
 const { validateOptions, getTypeOf } = require("../deprecated/api-utils");
-
-
-let appShellService = Cc["@mozilla.org/appshell/appShellService;1"].
-                        getService(Ci.nsIAppShellService);
-
-let hiddenWindow = appShellService.hiddenDOMWindow;
-
-if (!hiddenWindow) {
-  throw new Error([
-    "The hidden-frame module needs an app that supports a hidden window. ",
-    "We would like it to support other applications, however. Please see ",
-    "https://bugzilla.mozilla.org/show_bug.cgi?id=546740 for more information."
-  ].join(""));
-}
+const { window } = require("../addon/window");
 
 // This cache is used to access friend properties between functions
 // without exposing them on the public API.
 let cache = [];
 let elements = new WeakMap();
 
 function contentLoaded(target) {
   var deferred = defer();
@@ -48,43 +35,16 @@ function contentLoaded(target) {
     if (event.target === target || event.target === target.contentDocument) {
       target.removeEventListener("DOMContentLoaded", DOMContentLoaded, false);
       deferred.resolve(target);
     }
   }, false);
   return deferred.promise;
 }
 
-function makeHostFrame() {
-  // Check if we can use the hidden window itself to host our iframes.
-  // If it's not a suitable host, the hostFrame will be lazily created
-  // by the first HiddenFrame instance.
-  if (hiddenWindow.location.protocol == "chrome:" &&
-      (hiddenWindow.document.contentType == "application/vnd.mozilla.xul+xml" ||
-      hiddenWindow.document.contentType == "application/xhtml+xml")) {
-
-    // Resolve to an object with a shape similar to iframe.
-    return resolve({ contentDocument: hiddenWindow.document });
-  }
-  else {
-    return contentLoaded(makeFrame(hiddenWindow.document, {
-      // Ugly ugly hack. This is the most lightweight "chrome:" file I could
-      // find on the tree.
-      // This hack should be removed by proper platform support on bug 565388
-      uri: "chrome://global/content/mozilla.xhtml",
-      namespaceURI: hiddenWindow.document.documentElement.namespaceURI,
-      nodeName: "iframe",
-      allowJavascript: true,
-      allowPlugins: true,
-      allowAuth: true
-    }));
-  }
-}
-var hostFrame = makeHostFrame();
-
 function FrameOptions(options) {
   options = options || {}
   return validateOptions(options, FrameOptions.validator);
 }
 FrameOptions.validator = {
   onReady: {
     is: ["undefined", "function", "array"],
     ok: function(v) {
@@ -125,27 +85,26 @@ function isFrameCached(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);
 
-  hostFrame.then(function({ contentDocument }) {
-    let element = makeFrame(contentDocument, {
-      nodeName: "iframe",
-      type: "content",
-      allowJavascript: true,
-      allowPlugins: true,
-      allowAuth: true,
-    });
-    elements.set(frame, element);
-    return contentLoaded(element);
-  }).then(function onFrameReady(element) {
+  let element = makeFrame(window.document, {
+    nodeName: "iframe",
+    type: "content",
+    allowJavascript: true,
+    allowPlugins: true,
+    allowAuth: true,
+  });
+  elements.set(frame, element);
+
+  contentLoaded(element).then(function onFrameReady(element) {
     emit(frame, "ready");
   }, console.exception);
 
   return frame;
 }
 exports.add = addHidenFrame
 
 function removeHiddenFrame(frame) {
@@ -157,15 +116,9 @@ function removeHiddenFrame(frame) {
   // Remove from cache before calling in order to avoid loop
   cache.splice(cache.indexOf(frame), 1);
   emit(frame, "unload")
   let element = frame.element
   if (element) element.parentNode.removeChild(element)
 }
 exports.remove = removeHiddenFrame;
 
-unload(function () {
-  cache.splice(0).forEach(removeHiddenFrame);
-
-  hostFrame.then(function(host) {
-    if (hast.parentNode) frame.parentNode.removeChild(frame);
-  });
-});
+unload(function() cache.splice(0).forEach(removeHiddenFrame));
--- a/addon-sdk/source/lib/sdk/frame/utils.js
+++ b/addon-sdk/source/lib/sdk/frame/utils.js
@@ -38,36 +38,45 @@ exports.getDocShell = getDocShell;
  * @params {Boolean} options.allowJavascript
  *    Whether to allow Javascript execution. Defaults to `false`.
  * @params {Boolean} options.allowPlugins
  *    Whether to allow plugin execution. Defaults to `false`.
  */
 function create(document, options) {
   options = options || {};
   let remote = options.remote || false;
-  let nodeName = options.nodeName || 'browser';
   let namespaceURI = options.namespaceURI || XUL;
+  let isXUL = namespaceURI === XUL;
+  let nodeName = isXUL && options.browser ? 'browser' : 'iframe';
 
   let frame = document.createElementNS(namespaceURI, nodeName);
   // Type="content" is mandatory to enable stuff here:
   // http://mxr.mozilla.org/mozilla-central/source/content/base/src/nsFrameLoader.cpp#1776
   frame.setAttribute('type', options.type || 'content');
   frame.setAttribute('src', options.uri || 'about:blank');
 
+  document.documentElement.appendChild(frame);
+
   // Load in separate process if `options.remote` is `true`.
   // http://mxr.mozilla.org/mozilla-central/source/content/base/src/nsFrameLoader.cpp#1347
   if (remote) {
-    // We remove XBL binding to avoid execution of code that is not going to
-    // work because browser has no docShell attribute in remote mode
-    // (for example)
-    frame.setAttribute('style', '-moz-binding: none;');
-    frame.setAttribute('remote', 'true');
+    if (isXUL) {
+      // We remove XBL binding to avoid execution of code that is not going to
+      // work because browser has no docShell attribute in remote mode
+      // (for example)
+      frame.setAttribute('style', '-moz-binding: none;');
+      frame.setAttribute('remote', 'true');
+    }
+    else {
+      frame.QueryInterface(Ci.nsIMozBrowserFrame);
+      frame.createRemoteFrameLoader(null);
+    }
   }
 
-  document.documentElement.appendChild(frame);
+
 
   // If browser is remote it won't have a `docShell`.
   if (!remote) {
     let docShell = getDocShell(frame);
     docShell.allowAuth = options.allowAuth || false;
     docShell.allowJavascript = options.allowJavascript || false;
     docShell.allowPlugins = options.allowPlugins || false;
   }
--- a/addon-sdk/source/lib/sdk/indexed-db.js
+++ b/addon-sdk/source/lib/sdk/indexed-db.js
@@ -4,17 +4,16 @@
 
 "use strict";
 
 module.metadata = {
   "stability": "experimental"
 };
 
 const { Cc, Ci } = require("chrome");
-const { extend } = require("./core/heritage");
 const { id } = require("./self");
 
 // placeholder, copied from bootstrap.js
 let sanitizeId = function(id){
   let uuidRe =
     /^\{([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\}$/;
 
   let domain = id.
@@ -41,17 +40,17 @@ if (typeof(indexedDB) === "undefined")
 let principaluri = Cc["@mozilla.org/network/io-service;1"].
               getService(Ci.nsIIOService).
               newURI(PSEUDOURI, null, null);
 
 let principal = Cc["@mozilla.org/scriptsecuritymanager;1"].
 	               getService(Ci.nsIScriptSecurityManager).
 	               getCodebasePrincipal(principaluri);
 
-exports.indexedDB = extend({}, {   // freeze the object
+exports.indexedDB = Object.freeze({
   open: indexedDB.openForPrincipal.bind(indexedDB, principal),
   deleteDatabase: indexedDB.deleteForPrincipal.bind(indexedDB, principal),
   cmp: indexedDB.cmp
 });
 
 exports.IDBKeyRange = IDBKeyRange;
 exports.DOMException = Ci.nsIDOMDOMException;
 exports.IDBCursor = Ci.nsIIDBCursor;
--- a/addon-sdk/source/lib/sdk/page-mod.js
+++ b/addon-sdk/source/lib/sdk/page-mod.js
@@ -20,16 +20,17 @@ const { validateOptions : validate } = r
 const { Cc, Ci } = require('chrome');
 const { merge } = require('./util/object');
 const { readURISync } = require('./net/url');
 const { windowIterator } = require('./deprecated/window-utils');
 const { isBrowser, getFrames } = require('./window/utils');
 const { getTabs, getTabContentWindow, getTabForContentWindow,
         getURI: getTabURI } = require('./tabs/utils');
 const { has, hasAny } = require('./util/array');
+const { ignoreWindow } = require('sdk/private-browsing/utils');
 
 const styleSheetService = Cc["@mozilla.org/content/style-sheet-service;1"].
                             getService(Ci.nsIStyleSheetService);
 
 const USER_SHEET = styleSheetService.USER_SHEET;
 
 const io = Cc['@mozilla.org/network/io-service;1'].
               getService(Ci.nsIIOService);
@@ -348,16 +349,22 @@ const PageModManager = Registry.resolve(
     let window = document.defaultView;
     // XML documents don't have windows, and we don't yet support them.
     if (!window)
       return;
     // We apply only on documents in tabs of Firefox
     if (!getTabForContentWindow(window))
       return;
 
+    // When the tab is private, only addons with 'private-browsing' flag in
+    // their package.json can apply content script to private documents
+    if (ignoreWindow(window)) {
+      return;
+    }
+
     for (let rule in RULES)
       if (RULES[rule].test(document.URL))
         this._emit(rule, window);
   },
   off: function off(topic, listener) {
     this.removeListener(topic, listener);
     if (!this._listeners(topic).length)
       delete RULES[topic];
--- a/addon-sdk/source/lib/sdk/panel.js
+++ b/addon-sdk/source/lib/sdk/panel.js
@@ -1,41 +1,42 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
 "use strict";
 
 // The panel module currently supports only Firefox.
 // See: https://bugzilla.mozilla.org/show_bug.cgi?id=jetpack-panel-apps
 module.metadata = {
   "stability": "stable",
   "engines": {
     "Firefox": "*"
   }
 };
 
-const { Cc, Ci } = require("chrome");
-
+const { Ci } = require("chrome");
 const { validateOptions: valid } = require('./deprecated/api-utils');
 const { Symbiont } = require('./content/content');
 const { EventEmitter } = require('./deprecated/events');
-const timer = require('./timers');
+const { setTimeout } = require('./timers');
 const runtime = require('./system/runtime');
-const { getMostRecentBrowserWindow } = require('./window/utils');
 const { getDocShell } = require("./frame/utils");
-
-const windowMediator = Cc['@mozilla.org/appshell/window-mediator;1'].
-                       getService(Ci.nsIWindowMediator);
+const { getWindow } = require('./panel/window');
+const { isPrivateBrowsingSupported } = require('./self');
+const { isWindowPBSupported } = require('./private-browsing/utils');
 
 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
       ON_SHOW = 'popupshown',
       ON_HIDE = 'popuphidden',
       validNumber = { is: ['number', 'undefined', 'null'] };
 
+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.
  */
 const Panel = Symbiont.resolve({
   constructor: '_init',
   _onInit: '_onSymbiontInit',
   destroy: '_symbiontDestructor',
   _documentUnload: '_workerDocumentUnload'
@@ -110,17 +111,25 @@ const Panel = Symbiont.resolve({
   _height: 240,
 
   /* Public API: Panel.isShowing */
   get isShowing() !!this._xulPanel && this._xulPanel.state == "open",
 
   /* Public API: Panel.show */
   show: function show(anchor) {
     anchor = anchor || null;
-    let document = getWindow(anchor).document;
+    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;
     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.
@@ -138,16 +147,17 @@ const Panel = Symbiont.resolve({
       '</bindings>';
       xulPanel.style.MozBinding = 'url("data:text/xml;charset=utf-8,' +
         document.defaultView.encodeURIComponent(binding) + '")';
 
       let frame = document.createElementNS(XUL_NS, 'iframe');
       frame.setAttribute('type', 'content');
       frame.setAttribute('flex', '1');
       frame.setAttribute('transparent', 'transparent');
+
       if (runtime.OS === "Darwin") {
         frame.style.borderRadius = "6px";
         frame.style.padding = "1px";
       }
 
       // 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,");
@@ -198,17 +208,17 @@ const Panel = Symbiont.resolve({
     // 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";
 
     // Wait for the XBL binding to be constructed
     function waitForBinding() {
       if (!xulPanel.openPopup) {
-        timer.setTimeout(waitForBinding, 50);
+        setTimeout(waitForBinding, 50);
         return;
       }
       xulPanel.openPopup(anchor, position, x, y);
     }
     waitForBinding();
 
     return this._public;
   },
@@ -358,45 +368,8 @@ const Panel = Symbiont.resolve({
       // document
       this._workerCleanup();
       this._initFrame(this._frame);
     }
   }
 });
 exports.Panel = function(options) Panel(options)
 exports.Panel.prototype = Panel.prototype;
-
-function getWindow(anchor) {
-  let window;
-
-  if (anchor) {
-    let anchorWindow = anchor.ownerDocument.defaultView.top;
-    let anchorDocument = anchorWindow.document;
-
-    let enumerator = windowMediator.getEnumerator("navigator:browser");
-    while (enumerator.hasMoreElements()) {
-      let enumWindow = enumerator.getNext();
-
-      // Check if the anchor is in this browser window.
-      if (enumWindow == anchorWindow) {
-        window = anchorWindow;
-        break;
-      }
-
-      // Check if the anchor is in a browser tab in this browser window.
-      let browser = enumWindow.gBrowser.getBrowserForDocument(anchorDocument);
-      if (browser) {
-        window = enumWindow;
-        break;
-      }
-
-      // Look in other subdocuments (sidebar, etc.)?
-    }
-  }
-
-  // If we didn't find the anchor's window (or we have no anchor),
-  // return the most recent browser window.
-  if (!window)
-    window = getMostRecentBrowserWindow();
-
-  return window;
-}
-
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/lib/sdk/panel/window.js
@@ -0,0 +1,51 @@
+/* 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');
+
+function getWindow(anchor) {
+  let window;
+  let windows = getWindows("navigator:browser", {
+    includePrivate: isPrivateBrowsingSupported
+  });
+
+  if (anchor) {
+    let anchorWindow = anchor.ownerDocument.defaultView.top;
+    let anchorDocument = anchorWindow.document;
+
+    // loop thru supported windows
+    for each(let enumWindow in windows) {
+      // Check if the anchor is in this browser window.
+      if (enumWindow == anchorWindow) {
+        window = anchorWindow;
+        break;
+      }
+
+      // Check if the anchor is in a browser tab in this browser window.
+      let browser = enumWindow.gBrowser.getBrowserForDocument(anchorDocument);
+      if (browser) {
+        window = enumWindow;
+        break;
+      }
+
+      // Look in other subdocuments (sidebar, etc.)?
+    }
+  }
+
+  // If we didn't find the anchor's window (or we have no anchor),
+  // return the most recent browser window.
+  if (!window)
+    window = getMostRecentBrowserWindow();
+
+  // if the window is not supported, then it should be ignored
+  if (ignoreWindow(window)) {
+  	return null;
+  }
+
+  return window;
+}
+exports.getWindow = getWindow;
--- a/addon-sdk/source/lib/sdk/private-browsing.js
+++ b/addon-sdk/source/lib/sdk/private-browsing.js
@@ -18,17 +18,17 @@ onStateChange('start', function onStart(
   emit(exports, 'start');
 });
 
 onStateChange('stop', function onStop() {
   emit(exports, 'stop');
 });
 
 Object.defineProperty(exports, "isActive", {
-	get: deprecateFunction(getMode, 'require("private-browsing").isActive is deprecated.')
+  get: deprecateFunction(getMode, 'require("private-browsing").isActive is deprecated.')
 });
 
 exports.activate = function activate() setMode(true);
 exports.deactivate = function deactivate() setMode(false);
 
 exports.on = deprecateEvents(on.bind(null, exports));
 exports.once = deprecateEvents(once.bind(null, exports));
 exports.removeListener = deprecateEvents(function removeListener(type, listener) {
@@ -43,16 +43,27 @@ exports.isPrivate = function(thing) {
   // then check if the window is private
   if (!!thing) {
     // if the thing is a window, and the window is private
     // then return true
     if (isWindowPrivate(thing)) {
       return true;
     }
 
+    // does the thing have an associated tab?
+    // page-mod instances do..
+    if (thing.tab) {
+      let tabWindow = getOwnerWindow(thing.tab);
+      if (tabWindow) {
+        let isThingPrivate = isWindowPrivate(tabWindow);
+        if (isThingPrivate)
+          return isThingPrivate;
+      }
+    }
+
     // can we find an associated window?
     let window = getOwnerWindow(thing);
     if (window)
       return isWindowPrivate(window);
   }
 
   // if we get here, and global private browsing
   // is available, and it is true, then return
--- a/addon-sdk/source/lib/sdk/selection.js
+++ b/addon-sdk/source/lib/sdk/selection.js
@@ -13,20 +13,20 @@ module.metadata = {
 
 const { Ci, Cc } = require("chrome"),
     { setTimeout } = require("./timers"),
     { emit, off } = require("./event/core"),
     { Class, obscure } = require("./core/heritage"),
     { EventTarget } = require("./event/target"),
     { ns } = require("./core/namespace"),
     { when: unload } = require("./system/unload"),
+    { ignoreWindow } = require('./private-browsing/utils'),
     { getTabs, getTabContentWindow, getTabForContentWindow,
       getAllTabContentWindows } = require('./tabs/utils'),
-    { getMostRecentBrowserWindow,
-      windows, getFocusedWindow, getFocusedElement } = require("./window/utils"),
+    winUtils = require("./window/utils"),
     events = require("./system/events");
 
 // The selection types
 const HTML = 0x01,
       TEXT = 0x02,
       DOM  = 0x03; // internal use only
 
 // A more developer-friendly message than the caught exception when is not
@@ -96,32 +96,62 @@ const selectionListener = {
  * Empty selections are skipped - see `safeGetRange` for further details.
  *
  * If discontiguous selections are in a text field, only the first one
  * is returned because the text field selection APIs doesn't support
  * multiple selections.
  */
 function iterator() {
     let selection = getSelection(DOM);
-    let count = selection.rangeCount || (getElementWithSelection() ? 1 : 0);
+    let count = 0;
+
+    if (selection)
+      count = selection.rangeCount || (getElementWithSelection() ? 1 : 0);
 
     for (let i = 0; i < count; i++) {
       let sel = Selection(i);
 
       if (sel.text)
         yield Selection(i);
     }
 }
 
 const selectionIterator = obscure({
   __iterator__: iterator, // for...in; for each...in
   iterator: iterator // for....of
 });
 
 /**
+ * Returns the most recent focused window.
+ * if private browsing window is most recent and not supported,
+ * then ignore it and return `null`, because the focused window
+ * can't be targeted.
+ */
+function getFocusedWindow() {
+  let window = winUtils.getFocusedWindow();
+
+  return ignoreWindow(window) ? null : window;
+}
+
+/**
+ * Returns the focused element in the most recent focused window
+ * if private browsing window is most recent and not supported,
+ * then ignore it and return `null`, because the focused element
+ * can't be targeted.
+ */
+function getFocusedElement() {
+  let element = winUtils.getFocusedElement();
+
+  if (!element || ignoreWindow(element.ownerDocument.defaultView))
+    return null;
+
+  return element;
+}
+
+/**
  * Returns the current selection from most recent content window. Depending on
  * the specified |type|, the value returned can be a string of text, stringified
  * HTML, or a DOM selection object as described at
  * https://developer.mozilla.org/en/DOM/Selection.
  *
  * @param type
  *        Specifies the return type of the selection. Valid values are the one
  *        of the constants HTML, TEXT, or DOM.
@@ -343,20 +373,21 @@ function removeSelectionListener(window)
   window.removeEventListener("select", selectionListener.onSelect, true);
 
   delete selections(window).selection;
 };
 
 function onContent(event) {
   let window = event.subject.defaultView;
 
-  // We are not interested in documents without valid defaultView (e.g. XML), or
-  // not in a tab (e.g. Panel).
-   if (window && getTabForContentWindow(window))
+  // We are not interested in documents without valid defaultView (e.g. XML)
+  // that aren't in a tab (e.g. Panel); or in private windows
+   if (window && getTabForContentWindow(window) && !ignoreWindow(window)) {
     addSelectionListener(window);
+  }
 }
 
 // Adds Selection listener to new documents
 // Note that strong reference is needed for documents that are loading slowly or
 // where the server didn't close the connection (e.g. "comet").
 events.on("document-element-inserted", onContent, true);
 
 // Adds Selection listeners to existing documents
--- a/addon-sdk/source/lib/sdk/tabs/tab-firefox.js
+++ b/addon-sdk/source/lib/sdk/tabs/tab-firefox.js
@@ -58,17 +58,20 @@ const TabTrait = Trait.compose(EventEmit
     // Since we will have to identify tabs by a DOM elements facade function
     // is used as constructor that collects all the instances and makes sure
     // that they more then one wrapper is not created per tab.
     return this;
   },
   destroy: function destroy() {
     this._removeAllListeners();
     if (this._tab) {
-      this._browser.removeEventListener(EVENTS.ready.dom, this._onReady, true);
+      let browser = this._browser;
+      // The tab may already be removed from DOM -or- not yet added
+      if (browser)
+        browser.removeEventListener(EVENTS.ready.dom, this._onReady, true);
       this._tab = null;
       TABS.splice(TABS.indexOf(this), 1);
     }
   },
 
   /**
    * Internal listener that emits public event 'ready' when the page of this
    * tab is loaded.
@@ -216,20 +219,25 @@ const TabTrait = Trait.compose(EventEmit
     this._window.gBrowser.reloadTab(this._tab);
   }
 });
 
 function getChromeTab(tab) {
   return getOwnerWindow(viewNS(tab).tab);
 }
 
-function Tab(options) {
+function Tab(options, existingOnly) {
   let chromeTab = options.tab;
   for each (let tab in TABS) {
     if (chromeTab == tab._tab)
       return tab._public;
   }
+  // If called asked to return only existing wrapper,
+  // we should return null here as no matching Tab object has been found
+  if (existingOnly)
+    return null;
+
   let tab = TabTrait(options);
   TABS.push(tab);
   return tab._public;
 }
 Tab.prototype = TabTrait.prototype;
 exports.Tab = Tab;
--- a/addon-sdk/source/lib/sdk/test/assert.js
+++ b/addon-sdk/source/lib/sdk/test/assert.js
@@ -55,18 +55,24 @@ AssertionError.prototype = Object.create
       ].join(" ");
     }
     return value;
   }}
 });
 exports.AssertionError = AssertionError;
 
 function Assert(logger) {
-  return Object.create(Assert.prototype, { _log: { value: logger }});
+  let assert = Object.create(Assert.prototype, { _log: { value: logger }});
+
+  assert.fail = assert.fail.bind(assert);
+  assert.pass = assert.pass.bind(assert);
+
+  return assert;
 }
+
 Assert.prototype = {
   fail: function fail(e) {
     if (!e || typeof(e) !== 'object') {
       this._log.fail(e);
       return;
     }
     let message = e.message;
     try {
--- a/addon-sdk/source/lib/sdk/test/loader.js
+++ b/addon-sdk/source/lib/sdk/test/loader.js
@@ -1,22 +1,26 @@
 /* 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, resolveURI, Require,
         unload, override, descriptor  } = require('../loader/cuddlefish');
+const addonWindow = require('../addon/window');
 const { PlainTextConsole } = require("sdk/console/plain-text");
 
 function CustomLoader(module, globals, packaging) {
   let options = packaging || require("@loader/options");
   options = override(options, {
-    globals: override(require('../system/globals'), globals || {})
+    globals: override(require('../system/globals'), globals || {}),
+    modules: override(options.modules || {}, {
+      'sdk/addon/window': addonWindow
+     })
   });
 
   let loader = Loader(options);
   return Object.create(loader, descriptor({
     require: Require(loader, module),
     sandbox: function(id) {
       let requirement = loader.resolve(id, module.id);
       let uri = resolveURI(requirement, loader.mapping);
--- a/addon-sdk/source/lib/sdk/window/utils.js
+++ b/addon-sdk/source/lib/sdk/window/utils.js
@@ -49,16 +49,21 @@ function isWindowPrivate(win) {
 }
 exports.isWindowPrivate = isWindowPrivate;
 
 function getMostRecentBrowserWindow() {
   return getMostRecentWindow(BROWSER);
 }
 exports.getMostRecentBrowserWindow = getMostRecentBrowserWindow;
 
+function getHiddenWindow() {
+  return appShellService.hiddenDOMWindow;
+}
+exports.getHiddenWindow = getHiddenWindow;
+
 function getMostRecentWindow(type) {
   return WM.getMostRecentWindow(type);
 }
 exports.getMostRecentWindow = getMostRecentWindow;
 
 /**
  * Returns the ID of the window's current inner window.
  */
@@ -84,16 +89,22 @@ function getXULWindow(window) {
   return window.QueryInterface(Ci.nsIInterfaceRequestor).
     getInterface(Ci.nsIWebNavigation).
     QueryInterface(Ci.nsIDocShellTreeItem).
     treeOwner.QueryInterface(Ci.nsIInterfaceRequestor).
     getInterface(Ci.nsIXULWindow);
 };
 exports.getXULWindow = getXULWindow;
 
+function getDOMWindow(xulWindow) {
+  return xulWindow.QueryInterface(Ci.nsIInterfaceRequestor).
+    getInterface(Ci.nsIDOMWindow);
+}
+exports.getDOMWindow = getDOMWindow;
+
 /**
  * Returns `nsIBaseWindow` for the given `nsIDOMWindow`.
  */
 function getBaseWindow(window) {
   return window.QueryInterface(Ci.nsIInterfaceRequestor).
     getInterface(Ci.nsIWebNavigation).
     QueryInterface(Ci.nsIDocShell).
     QueryInterface(Ci.nsIDocShellTreeItem).
@@ -158,39 +169,41 @@ function serializeFeatures(options) {
  *    Optional name that is assigned to the window.
  * @params {Object} options.features
  *    Map of key, values like: `{ width: 10, height: 15, chrome: true, private: true }`.
  */
 function open(uri, options) {
   options = options || {};
   let newWindow = windowWatcher.
     openWindow(options.parent || null,
-               uri,
+               uri || URI_BROWSER,
                options.name || null,
                serializeFeatures(options.features || {}),
                options.args || null);
 
   return newWindow;
 }
+
+
 exports.open = open;
 
 function onFocus(window) {
-  let deferred = defer();
+  let { resolve, promise } = defer();
 
   if (isFocused(window)) {
-    deferred.resolve(window);
+    resolve(window);
   }
   else {
     window.addEventListener("focus", function focusListener() {
       window.removeEventListener("focus", focusListener, true);
-      deferred.resolve(window);
+      resolve(window);
     }, true);
   }
 
-  return deferred.promise;
+  return promise;
 }
 exports.onFocus = onFocus;
 
 function isFocused(window) {
   const FM = Cc["@mozilla.org/focus-manager;1"].
                 getService(Ci.nsIFocusManager);
 
   let childTargetWindow = {};
@@ -200,26 +213,27 @@ function isFocused(window) {
   let focusedChildWindow = {};
   if (FM.activeWindow) {
     FM.getFocusedElementForWindow(FM.activeWindow, true, focusedChildWindow);
     focusedChildWindow = focusedChildWindow.value;
   }
 
   return (focusedChildWindow === childTargetWindow);
 }
+exports.isFocused = isFocused;
 
 /**
  * Opens a top level window and returns it's `nsIDOMWindow` representation.
  * Same as `open` but with more features
  * @param {Object} options
  *
  */
 function openDialog(options) {
   options = options || {};
-  
+
   let features = options.features || FEATURES;
   if (!!options.private &&
       !array.has(features.toLowerCase().split(','), 'private')) {
     features = features.split(',').concat('private').join(',');
   }
 
   let browser = getMostRecentBrowserWindow();
 
@@ -290,26 +304,28 @@ function isXULBrowser(window) {
   return !!(isBrowser(window) && window.XULBrowserWindow);
 }
 exports.isXULBrowser = isXULBrowser;
 
 /**
  * Returns the most recent focused window
  */
 function getFocusedWindow() {
-  let window = getMostRecentBrowserWindow();
+  let window = WM.getMostRecentWindow(BROWSER);
+
   return window ? window.document.commandDispatcher.focusedWindow : null;
 }
 exports.getFocusedWindow = getFocusedWindow;
 
 /**
  * Returns the focused element in the most recent focused window
  */
 function getFocusedElement() {
-  let window = getMostRecentBrowserWindow();
+  let window = WM.getMostRecentWindow(BROWSER);
+
   return window ? window.document.commandDispatcher.focusedElement : null;
 }
 exports.getFocusedElement = getFocusedElement;
 
 function getFrames(window) {
   return Array.slice(window.frames).reduce(function(frames, frame) {
     return frames.concat(frame, getFrames(frame))
   }, [])
--- a/addon-sdk/source/lib/sdk/windows/tabs-firefox.js
+++ b/addon-sdk/source/lib/sdk/windows/tabs-firefox.js
@@ -93,18 +93,25 @@ const WindowTabTracker = Trait.compose({
   _onTabEvent: function _onTabEvent(type, tab) {
     // Accept only tabs for the watched window, and ignore private tabs
     // if addon doesn't have private permission
     if (this._window === getOwnerWindow(tab) && !ignoreWindow(this._window)) {
       let options = this._tabOptions.shift() || {};
       options.tab = tab;
       options.window = this._public;
 
-      // creating tab wrapper and adding listener to "ready" events.
-      let wrappedTab = Tab(options);
+      // Ignore zombie tabs on open that have already been removed
+      if (type == "open" && !tab.linkedBrowser)
+        return;
+
+      // Create a tab wrapper on open event, otherwise, just fetch existing
+      // tab object
+      let wrappedTab = Tab(options, type !== "open");
+      if (!wrappedTab)
+        return;
 
       // Setting up an event listener for ready events.
       if (type === "open")
         wrappedTab.on("ready", this._onTabReady);
 
       this._emitEvent(type, wrappedTab);
     }
   },
--- a/addon-sdk/source/python-lib/cuddlefish/__init__.py
+++ b/addon-sdk/source/python-lib/cuddlefish/__init__.py
@@ -138,18 +138,18 @@ parser_groups = (
                                       type="json",
                                       metavar=None,
                                       default="{}",
                                       cmds=['run', 'xpi'])),
         (("", "--parseable",), dict(dest="parseable",
                                     help="display test output in a parseable format",
                                     action="store_true",
                                     default=False,
-                                    cmds=['test', 'testex', 'testpkgs',
-                                          'testall'])),
+                                    cmds=['run', 'test', 'testex', 'testpkgs',
+                                          'testaddons', 'testall'])),
         ]
      ),
 
     ("Experimental Command-Specific Options", [
         (("-a", "--app",), dict(dest="app",
                                 help=("app to run: firefox (default), fennec, "
                                       "fennec-on-device, xulrunner or "
                                       "thunderbird"),
@@ -655,26 +655,26 @@ def run(arguments=sys.argv[1:], target_c
 
         target_cfg_json = os.path.join(options.pkgdir, 'package.json')
         target_cfg = packaging.get_config_in_dir(options.pkgdir)
 
     # At this point, we're either building an XPI or running Jetpack code in
     # a Mozilla application (which includes running tests).
 
     use_main = False
-    inherited_options = ['verbose', 'enable_e10s']
+    inherited_options = ['verbose', 'enable_e10s', 'parseable']
     enforce_timeouts = False
 
     if command == "xpi":
         use_main = True
     elif command == "test":
         if 'tests' not in target_cfg:
             target_cfg['tests'] = []
         inherited_options.extend(['iterations', 'filter', 'profileMemory',
-                                  'stopOnError', 'parseable'])
+                                  'stopOnError'])
         enforce_timeouts = True
     elif command == "run":
         use_main = True
     else:
         assert 0, "shouldn't get here"
 
     if use_main and 'main' not in target_cfg:
         # If the user supplies a template dir, then the main
--- a/addon-sdk/source/test/addons/private-browsing-supported/main.js
+++ b/addon-sdk/source/test/addons/private-browsing-supported/main.js
@@ -29,24 +29,24 @@ exports.testGetOwnerWindow = function(as
   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); 
+        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'); 
+                                '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');
         }
       }
 
@@ -159,12 +159,17 @@ if (!is('Fennec')) {
         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);
       }
     })
   }
 }
 
-merge(module.exports, require('./windows'));
-merge(module.exports, require('./tabs'));
+merge(module.exports,
+  require('./test-windows'),
+  require('./test-tabs'),
+  require('./test-page-mod'),
+  require('./test-selection'),
+  require('./test-panel')
+);
 
 require('sdk/test/runner').runTestsFromModule(module);
deleted file mode 100644
--- a/addon-sdk/source/test/addons/private-browsing-supported/tabs.js
+++ /dev/null
@@ -1,30 +0,0 @@
-const tabs = require('sdk/tabs');
-const { is } = require('sdk/system/xul-app');
-const { isPrivate } = require('sdk/private-browsing');
-const pbUtils = require('sdk/private-browsing/utils');
-const { getOwnerWindow } = require('sdk/private-browsing/window/utils');
-
-exports.testPrivateTabsAreListed = function (assert, done) {
-  let originalTabCount = tabs.length;
-
-  tabs.open({
-    url: 'about:blank',
-    isPrivate: true,
-    onOpen: function(tab) {
-      let win = getOwnerWindow(tab);
-      // PWPB case
-      if (pbUtils.isWindowPBSupported || pbUtils.isTabPBSupported) {
-        assert.ok(isPrivate(tab), "tab is private");
-        assert.equal(tabs.length, originalTabCount + 1,
-                     'New private window\'s tab are visible in tabs list');
-      }
-      else {
-      // Global case, openDialog didn't opened a private window/tab
-        assert.ok(!isPrivate(tab), "tab isn't private");
-        assert.equal(tabs.length, originalTabCount + 1,
-                     'New non-private window\'s tab is visible in tabs list');
-      }
-      tab.close(done);
-    }
-  });
-}
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/test/addons/private-browsing-supported/test-page-mod.js
@@ -0,0 +1,111 @@
+const { getMostRecentBrowserWindow } = require('sdk/window/utils');
+const { PageMod } = require("sdk/page-mod");
+const { getActiveTab, setTabURL, openTab, closeTab } = require('sdk/tabs/utils');
+const xulApp = require('sdk/system/xul-app');
+const windowHelpers = require('sdk/window/helpers');
+const { defer } = require("sdk/core/promise");
+const { isPrivate } = require('sdk/private-browsing');
+const { isTabPBSupported, isWindowPBSupported } = require('sdk/private-browsing/utils');
+
+function openWebpage(url, enablePrivate) {
+  let { promise, resolve, reject } = defer();
+
+  if (xulApp.is("Fennec")) {
+    let chromeWindow = getMostRecentBrowserWindow();
+    let rawTab = openTab(chromeWindow, url, {
+      isPrivate: enablePrivate
+    });
+
+    resolve(function() {
+      closeTab(rawTab)
+    });
+
+    return promise;
+  }
+  else {
+    windowHelpers.open("", {
+      features: {
+        toolbar: true,
+        private: enablePrivate
+      }
+    }).then(function(chromeWindow) {
+      if (isPrivate(chromeWindow) !== !!enablePrivate)
+        reject(new Error("Window should have Private set to " + !!enablePrivate));
+
+      let tab = getActiveTab(chromeWindow);
+      setTabURL(tab, url);
+
+      resolve(function(){
+        windowHelpers.close(chromeWindow);
+      });
+    });
+
+    return promise;
+  }
+}
+
+exports["test page-mod on private tab"] = function (assert, done) {
+  // Only set private mode when explicitely supported.
+  // (fennec 19 has some intermediate PB support where isTabPBSupported
+  // will be false, but isPrivate(worker.tab) will be true if we open a private
+  // tab)
+  let setPrivate = isTabPBSupported || isWindowPBSupported;
+
+  let id = Date.now().toString(36);
+  let frameUri = "data:text/html;charset=utf-8," + id;
+  let uri = "data:text/html;charset=utf-8," +
+            encodeURIComponent(id + "<iframe src='" + frameUri + "'><iframe>");
+
+  let count = 0;
+
+  openWebpage(uri, setPrivate).then(function(close) {
+    PageMod({
+      include: [uri, frameUri],
+
+      onAttach: function(worker) {
+        assert.ok(worker.tab.url == uri || worker.tab.url == frameUri,
+                  "Got a worker attached to the private window tab");
+
+        if (setPrivate) {
+          assert.ok(isPrivate(worker), "The worker is really private");
+          assert.ok(isPrivate(worker.tab), "The document is really private");
+        }
+        else {
+          assert.ok(!isPrivate(worker),
+                    "private browsing isn't supported, " +
+                    "so that the worker isn't private");
+          assert.ok(!isPrivate(worker.tab),
+                    "private browsing isn't supported, " +
+                    "so that the document isn't private");
+        }
+
+        if (++count == 2) {
+          this.destroy();
+          close();
+          done();
+        }
+      }
+    });
+  }).then(null, assert.fail);
+};
+
+exports["test page-mod on non-private tab"] = function (assert, done) {
+  let id = Date.now().toString(36);
+  let url = "data:text/html;charset=utf-8," + encodeURIComponent(id);
+
+  openWebpage(url, false).then(function(close){
+    PageMod({
+      include: url,
+      onAttach: function(worker) {
+        assert.equal(worker.tab.url, url,
+                     "Got a worker attached to the private window tab");
+        assert.ok(!isPrivate(worker), "The worker is really non-private");
+        assert.ok(!isPrivate(worker.tab), "The document is really non-private");
+
+        this.destroy();
+        close();
+        done();
+      }
+    });
+  }).then(null, assert.fail);
+}
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/test/addons/private-browsing-supported/test-panel.js
@@ -0,0 +1,16 @@
+'use strict';
+
+const { isWindowPBSupported } = require('sdk/private-browsing/utils');
+
+if (isWindowPBSupported) {
+  exports.testRequirePanel = function (assert) {
+    try {
+  	  require('panel');
+    }
+    catch(e) {
+  	  assert.ok(e.message.match(/Bug 816257/), 'Bug 816257 is mentioned');
+      return;
+    }
+    assert.fail('the panel module should throw an error');
+  }
+}
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/test/addons/private-browsing-supported/test-selection.js
@@ -0,0 +1,458 @@
+/* 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 HTML = "<html>\
+  <body>\
+    <div>foo</div>\
+    <div>and</div>\
+    <textarea>noodles</textarea>\
+  </body>\
+</html>";
+
+const URL = "data:text/html;charset=utf-8," + encodeURIComponent(HTML);
+
+const FRAME_HTML = "<iframe src='" + URL + "'><iframe>";
+const FRAME_URL = "data:text/html;charset=utf-8," + encodeURIComponent(FRAME_HTML);
+
+const { defer } = require("sdk/core/promise");
+const { browserWindows } = require("sdk/windows");
+const tabs = require("sdk/tabs");
+const { setTabURL, getActiveTab, getTabContentWindow, closeTab, getTabs,
+  getTabTitle } = require("sdk/tabs/utils");
+const { getMostRecentBrowserWindow, isFocused } = require("sdk/window/utils");
+const { open: openNewWindow, close: closeWindow, focus } = require("sdk/window/helpers");
+const { Loader } = require("sdk/test/loader");
+const { merge } = require("sdk/util/object");
+const { isPrivate } = require("sdk/private-browsing");
+
+// General purpose utility functions
+
+/**
+ * Opens the url given and return a promise, that will be resolved with the
+ * content window when the document is ready.
+ *
+ * I believe this approach could be useful in most of our unit test, that
+ * requires to open a tab and need to access to its content.
+ */
+function open(url, options) {
+  let { promise, resolve } = defer();
+
+  if (options && typeof(options) === "object") {
+    openNewWindow("", {
+      features: merge({ toolbar: true }, options)
+    }).then(function(chromeWindow) {
+      if (isPrivate(chromeWindow) !== !!options.private)
+        throw new Error("Window should have Private set to " + !!options.private);
+
+      let tab = getActiveTab(chromeWindow);
+
+      tab.addEventListener("load", function ready(event) {
+        let { document } = getTabContentWindow(this);
+
+        if (document.readyState === "complete" && document.URL === url) {
+          this.removeEventListener(event.type, ready);
+
+          if (options.title)
+            document.title = options.title;
+
+          resolve(document.defaultView);
+        }
+      })
+
+      setTabURL(tab, url);
+    });
+
+    return promise;
+  };
+
+  tabs.open({
+    url: url,
+    onReady: function(tab) {
+      // Unfortunately there is no way to get a XUL Tab from SDK Tab on Firefox,
+      // only on Fennec. We should implement `tabNS` also on Firefox in order
+      // to have that.
+
+      // Here we assuming that the most recent browser window is the one we're
+      // doing the test, and the active tab is the one we just opened.
+      let window = getTabContentWindow(getActiveTab(getMostRecentBrowserWindow()));
+
+      resolve(window);
+    }
+  });
+
+  return promise;
+};
+
+/**
+ * Close the Active Tab
+ */
+function close(window) {
+  let { promise, resolve } = defer();
+
+  if (window && typeof(window.close) === "function") {
+    closeWindow(window).then(function() resolve());
+  }
+  else {
+    // Here we assuming that the most recent browser window is the one we're
+    // doing the test, and the active tab is the one we just opened.
+    closeTab(getActiveTab(getMostRecentBrowserWindow()));
+    resolve();
+  }
+
+  return promise;
+}
+
+/**
+ * Reload the window given and return a promise, that will be resolved with the
+ * content window after a small delay.
+ */
+function reload(window) {
+  let { promise, resolve } = defer();
+
+  // Here we assuming that the most recent browser window is the one we're
+  // doing the test, and the active tab is the one we just opened.
+  let tab = tabs.activeTab;
+
+  tab.once("ready", function () {
+    resolve(window);
+  });
+
+  window.location.reload(true);
+
+  return promise;
+}
+
+// Selection's unit test utility function
+
+/**
+ * Select the first div in the page, adding the range to the selection.
+ */
+function selectFirstDiv(window) {
+  let div = window.document.querySelector("div");
+  let selection = window.getSelection();
+  let range = window.document.createRange();
+
+  if (selection.rangeCount > 0)
+    selection.removeAllRanges();
+
+  range.selectNode(div);
+  selection.addRange(range);
+
+  return window;
+}
+
+/**
+ * Select all divs in the page, adding the ranges to the selection.
+ */
+function selectAllDivs(window) {
+  let divs = window.document.getElementsByTagName("div");
+  let selection = window.getSelection();
+
+  if (selection.rangeCount > 0)
+    selection.removeAllRanges();
+
+  for (let i = 0; i < divs.length; i++) {
+    let range = window.document.createRange();
+
+    range.selectNode(divs[i]);
+    selection.addRange(range);
+  }
+
+  return window;
+}
+
+/**
+ * Select the textarea content
+ */
+function selectTextarea(window) {
+  let selection = window.getSelection();
+  let textarea = window.document.querySelector("textarea");
+
+  if (selection.rangeCount > 0)
+    selection.removeAllRanges();
+
+  textarea.setSelectionRange(0, textarea.value.length);
+  textarea.focus();
+
+  return window;
+}
+
+/**
+ * Select the content of the first div
+ */
+function selectContentFirstDiv(window) {
+  let div = window.document.querySelector("div");
+  let selection = window.getSelection();
+  let range = window.document.createRange();
+
+  if (selection.rangeCount > 0)
+    selection.removeAllRanges();
+
+  range.selectNodeContents(div);
+  selection.addRange(range);
+
+  return window;
+}
+
+/**
+ * Dispatch the selection event for the selection listener added by
+ * `nsISelectionPrivate.addSelectionListener`
+ */
+function dispatchSelectionEvent(window) {
+  // We modify the selection in order to dispatch the selection's event, by
+  // contract the selection by one character. So if the text selected is "foo"
+  // will be "fo".
+  window.getSelection().modify("extend", "backward", "character");
+
+  return window;
+}
+
+/**
+ * Dispatch the selection event for the selection listener added by
+ * `window.onselect` / `window.addEventListener`
+ */
+function dispatchOnSelectEvent(window) {
+  let { document } = window;
+  let textarea = document.querySelector("textarea");
+  let event = document.createEvent("UIEvents");
+
+  event.initUIEvent("select", true, true, window, 1);
+
+  textarea.dispatchEvent(event);
+
+  return window;
+}
+
+// Test cases
+
+exports["test PWPB Selection Listener"] = function(assert, done) {
+  let loader = Loader(module);
+  let selection = loader.require("sdk/selection");
+
+  open(URL, {private: true, title: "PWPB Selection Listener"}).
+    then(function(window) {
+      selection.once("select", function() {
+        assert.equal(browserWindows.length, 2, "there should be only two windows open.");
+        assert.equal(getTabs().length, 2, "there should be only two tabs open: '" +
+                     getTabs().map(function(tab) getTabTitle(tab)).join("', '") +
+                     "'."
+        );
+
+        // window should be focused, but force the focus anyhow.. see bug 841823
+        focus(window).then(function() {
+          // check state of window
+          assert.ok(isFocused(window), "the window is focused");
+          assert.ok(isPrivate(window), "the window should be a private window");
+
+          assert.equal(selection.text, "fo");
+
+          close(window).
+            then(loader.unload).
+            then(done, assert.fail);
+        });
+      });
+      return window;
+    }).
+    then(selectContentFirstDiv).
+    then(dispatchSelectionEvent).
+    then(null, assert.fail);
+};
+
+exports["test PWPB Textarea OnSelect Listener"] = function(assert, done) {
+  let loader = Loader(module);
+  let selection = loader.require("sdk/selection");
+
+  open(URL, {private: true, title: "PWPB OnSelect Listener"}).
+    then(function(window) {
+      selection.once("select", function() {
+        assert.equal(browserWindows.length, 2, "there should be only two windows open.");
+        assert.equal(getTabs().length, 2, "there should be only two tabs open: '" +
+                     getTabs().map(function(tab) getTabTitle(tab)).join("', '") +
+                     "'."
+        );
+
+        // window should be focused, but force the focus anyhow.. see bug 841823
+        focus(window).then(function() {
+          assert.equal(selection.text, "noodles");
+
+          close(window).
+            then(loader.unload).
+            then(done, assert.fail);
+        });
+      });
+      return window;
+    }).
+    then(selectTextarea).
+    then(dispatchOnSelectEvent).
+    then(null, assert.fail);
+};
+
+exports["test PWPB Single DOM Selection"] = function(assert, done) {
+  let loader = Loader(module);
+  let selection = loader.require("sdk/selection");
+
+  open(URL, {private: true, title: "PWPB Single DOM Selection"}).
+    then(selectFirstDiv).
+    then(focus).then(function() {
+      assert.equal(selection.isContiguous, true,
+        "selection.isContiguous with single DOM Selection works.");
+
+      assert.equal(selection.text, "foo",
+        "selection.text with single DOM Selection works.");
+
+      assert.equal(selection.html, "<div>foo</div>",
+        "selection.html with single DOM Selection works.");
+
+      let selectionCount = 0;
+      for each (let sel in selection) {
+        selectionCount++;
+
+        assert.equal(sel.text, "foo",
+          "iterable selection.text with single DOM Selection works.");
+
+        assert.equal(sel.html, "<div>foo</div>",
+          "iterable selection.html with single DOM Selection works.");
+      }
+
+      assert.equal(selectionCount, 1,
+        "One iterable selection");
+    }).then(close).then(loader.unload).then(done, assert.fail);
+}
+
+exports["test PWPB Textarea Selection"] = function(assert, done) {
+  let loader = Loader(module);
+  let selection = loader.require("sdk/selection");
+
+  open(URL, {private: true, title: "PWPB Textarea Listener"}).
+    then(selectTextarea).
+    then(focus).
+    then(function() {
+
+      assert.equal(selection.isContiguous, true,
+        "selection.isContiguous with Textarea Selection works.");
+
+      assert.equal(selection.text, "noodles",
+        "selection.text with Textarea Selection works.");
+
+      assert.strictEqual(selection.html, null,
+        "selection.html with Textarea Selection works.");
+
+      let selectionCount = 0;
+      for each (let sel in selection) {
+        selectionCount++;
+
+        assert.equal(sel.text, "noodles",
+          "iterable selection.text with Textarea Selection works.");
+
+        assert.strictEqual(sel.html, null,
+          "iterable selection.html with Textarea Selection works.");
+      }
+
+      assert.equal(selectionCount, 1,
+        "One iterable selection");
+
+    }).then(close).then(loader.unload).then(done, assert.fail);
+};
+
+exports["test PWPB Set HTML in Multiple DOM Selection"] = function(assert, done) {
+  let loader = Loader(module);
+  let selection = loader.require("sdk/selection");
+
+  open(URL, {private: true, title: "PWPB Set HTML in Multiple DOM Selection"}).
+    then(selectAllDivs).
+    then(focus).
+    then(function() {
+      let html = "<span>b<b>a</b>r</span>";
+
+      let expectedText = ["bar", "and"];
+      let expectedHTML = [html, "<div>and</div>"];
+
+      selection.html = html;
+
+      assert.equal(selection.text, expectedText[0],
+        "set selection.text with DOM Selection works.");
+
+      assert.equal(selection.html, expectedHTML[0],
+        "selection.html with DOM Selection works.");
+
+      let selectionCount = 0;
+      for each (let sel in selection) {
+
+        assert.equal(sel.text, expectedText[selectionCount],
+          "iterable selection.text with multiple DOM Selection works.");
+
+        assert.equal(sel.html, expectedHTML[selectionCount],
+          "iterable selection.html with multiple DOM Selection works.");
+
+        selectionCount++;
+      }
+
+      assert.equal(selectionCount, 2,
+        "Two iterable selections");
+    }).then(close).then(loader.unload).then(done, assert.fail);
+};
+
+exports["test PWPB Set Text in Textarea Selection"] = function(assert, done) {
+  let loader = Loader(module);
+  let selection = loader.require("sdk/selection");
+
+  open(URL, {private: true, title: "test PWPB Set Text in Textarea Selection"}).
+    then(selectTextarea).
+    then(focus).
+    then(function() {
+
+      let text = "bar";
+
+      selection.text = text;
+
+      assert.equal(selection.text, text,
+        "set selection.text with Textarea Selection works.");
+
+      assert.strictEqual(selection.html, null,
+        "selection.html with Textarea Selection works.");
+
+      let selectionCount = 0;
+      for each (let sel in selection) {
+        selectionCount++;
+
+        assert.equal(sel.text, text,
+          "iterable selection.text with Textarea Selection works.");
+
+        assert.strictEqual(sel.html, null,
+          "iterable selection.html with Textarea Selection works.");
+      }
+
+      assert.equal(selectionCount, 1,
+        "One iterable selection");
+
+    }).then(close).then(loader.unload).then(done, assert.fail);
+};
+
+// If the platform doesn't support the PBPW, we're replacing PBPW tests
+if (!require("sdk/private-browsing/utils").isWindowPBSupported) {
+  module.exports = {
+    "test PBPW Unsupported": function Unsupported (assert) {
+      assert.pass("Private Window Per Browsing is not supported on this platform.");
+    }
+  }
+}
+
+// If the module doesn't support the app we're being run in, require() will
+// throw.  In that case, remove all tests above from exports, and add one dummy
+// test that passes.
+try {
+  require("sdk/selection");
+}
+catch (err) {
+  if (!/^Unsupported Application/.test(err.message))
+    throw err;
+
+  module.exports = {
+    "test Unsupported Application": function Unsupported (assert) {
+      assert.pass(err.message);
+    }
+  }
+}
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/test/addons/private-browsing-supported/test-tabs.js
@@ -0,0 +1,32 @@
+'use strict';
+
+const tabs = require('sdk/tabs');
+const { is } = require('sdk/system/xul-app');
+const { isPrivate } = require('sdk/private-browsing');
+const pbUtils = require('sdk/private-browsing/utils');
+const { getOwnerWindow } = require('sdk/private-browsing/window/utils');
+
+exports.testPrivateTabsAreListed = function (assert, done) {
+  let originalTabCount = tabs.length;
+
+  tabs.open({
+    url: 'about:blank',
+    isPrivate: true,
+    onOpen: function(tab) {
+      let win = getOwnerWindow(tab);
+      // PWPB case
+      if (pbUtils.isWindowPBSupported || pbUtils.isTabPBSupported) {
+        assert.ok(isPrivate(tab), "tab is private");
+        assert.equal(tabs.length, originalTabCount + 1,
+                     'New private window\'s tab are visible in tabs list');
+      }
+      else {
+      // Global case, openDialog didn't opened a private window/tab
+        assert.ok(!isPrivate(tab), "tab isn't private");
+        assert.equal(tabs.length, originalTabCount + 1,
+                     'New non-private window\'s tab is visible in tabs list');
+      }
+      tab.close(done);
+    }
+  });
+}
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/test/addons/private-browsing-supported/test-windows.js
@@ -0,0 +1,255 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+'use strict';
+
+const { Cc, Ci } = require('chrome');
+const { isPrivate } = require('sdk/private-browsing');
+const { isWindowPBSupported } = require('sdk/private-browsing/utils');
+const { onFocus, getMostRecentWindow, getWindowTitle,
+        getFrames, windows, open: openWindow, isWindowPrivate } = require('sdk/window/utils');
+const { open, close, focus, promise } = require('sdk/window/helpers');
+const { browserWindows } = require("sdk/windows");
+const winUtils = require("sdk/deprecated/window-utils");
+const { fromIterator: toArray } = require('sdk/util/array');
+const tabs = require('sdk/tabs');
+
+const WM = Cc['@mozilla.org/appshell/window-mediator;1'].getService(Ci.nsIWindowMediator);
+
+const BROWSER = 'chrome://browser/content/browser.xul';
+
+function makeEmptyBrowserWindow(options) {
+  options = options || {};
+  return open(BROWSER, {
+    features: {
+      chrome: true,
+      private: !!options.private
+    }
+  });
+}
+
+exports.testWindowTrackerIgnoresPrivateWindows = function(assert, done) {
+  var myNonPrivateWindow, myPrivateWindow;
+  var finished = false;
+  var privateWindow;
+  var privateWindowClosed = false;
+  var privateWindowOpened = false;
+
+  let wt = winUtils.WindowTracker({
+    onTrack: function(window) {
+      if (window === myPrivateWindow) {
+        assert.equal(isPrivate(window), isWindowPBSupported);
+        privateWindowOpened = true;
+      }
+    },
+    onUntrack: function(window) {
+      if (window === myPrivateWindow && isWindowPBSupported) {
+        privateWindowClosed = true;
+      }
+
+      if (window === myNonPrivateWindow) {
+        assert.equal(privateWindowClosed, isWindowPBSupported);
+        assert.ok(privateWindowOpened);
+        wt.unload();
+        done();
+      }
+    }
+  });
+
+  // make a new private window
+  myPrivateWindow = openWindow(BROWSER, {
+  	features: {
+      private: true
+    }
+  });
+  promise(myPrivateWindow, 'load').then(function(window) {
+    assert.equal(isPrivate(window), isWindowPBSupported, 'private window isPrivate');
+    assert.equal(isWindowPrivate(window), isWindowPBSupported);
+    assert.ok(getFrames(window).length > 1, 'there are frames for private window');
+    assert.equal(getWindowTitle(window), window.document.title,
+                 'getWindowTitle works');
+
+    close(myPrivateWindow).then(function() {
+      assert.pass('private window was closed');
+      makeEmptyBrowserWindow().then(function(window) {
+        myNonPrivateWindow = window;
+        assert.notDeepEqual(myPrivateWindow, myNonPrivateWindow);
+        assert.pass('opened new window');
+        close(myNonPrivateWindow).then(function() {
+          assert.pass('non private window was closed');
+        })
+      });
+    });
+  });
+};
+
+// Test setting activeWIndow and onFocus for private windows
+exports.testSettingActiveWindowDoesNotIgnorePrivateWindow = function(assert, done) {
+  let browserWindow = WM.getMostRecentWindow("navigator:browser");
+  let testSteps;
+
+  assert.equal(winUtils.activeBrowserWindow, browserWindow,
+               "Browser window is the active browser window.");
+  assert.ok(!isPrivate(browserWindow), "Browser window is not private.");
+
+  // make a new private window
+  makeEmptyBrowserWindow({
+    private: true
+  }).then(focus).then(function(window) {
+    let continueAfterFocus = function(window) onFocus(window).then(nextTest);
+
+    // PWPB case
+    if (isWindowPBSupported) {
+      assert.ok(isPrivate(window), "window is private");
+      assert.notDeepEqual(winUtils.activeBrowserWindow, browserWindow);
+    }
+    // Global case
+    else {
+      assert.ok(!isPrivate(window), "window is not private");
+    }
+
+    assert.strictEqual(winUtils.activeBrowserWindow, window,
+                 "Correct active browser window pb supported");
+    assert.notStrictEqual(browserWindow, window,
+                 "The window is not the old browser window");
+
+    testSteps = [
+      function() {
+        // test setting a non private window
+        continueAfterFocus(winUtils.activeWindow = browserWindow);
+      },
+      function() {
+        assert.strictEqual(winUtils.activeWindow, browserWindow,
+                           "Correct active window [1]");
+        assert.strictEqual(winUtils.activeBrowserWindow, browserWindow,
+                           "Correct active browser window [1]");
+
+        // test focus(window)
+        focus(window).then(nextTest);
+      },
+      function(w) {
+        assert.strictEqual(w, window, 'require("sdk/window/helpers").focus on window works');
+        assert.strictEqual(winUtils.activeBrowserWindow, window,
+                           "Correct active browser window [2]");
+        assert.strictEqual(winUtils.activeWindow, window,
+                           "Correct active window [2]");
+
+        // test setting a private window
+        continueAfterFocus(winUtils.activeWindow = window);
+      },
+      function() {
+        assert.deepEqual(winUtils.activeBrowserWindow, window,
+                         "Correct active browser window [3]");
+        assert.deepEqual(winUtils.activeWindow, window,
+                         "Correct active window [3]");
+
+        // just to get back to original state
+        continueAfterFocus(winUtils.activeWindow = browserWindow);
+      },
+      function() {
+        assert.deepEqual(winUtils.activeBrowserWindow, browserWindow,
+                         "Correct active browser window when pb mode is supported [4]");
+        assert.deepEqual(winUtils.activeWindow, browserWindow,
+                         "Correct active window when pb mode is supported [4]");
+
+        close(window).then(done);
+      }
+    ];
+
+    function nextTest() {
+      let args = arguments;
+      if (testSteps.length) {
+        require('sdk/timers').setTimeout(function() {
+          (testSteps.shift()).apply(null, args);
+        }, 0);
+      }
+    }
+    nextTest();
+  });
+};
+
+exports.testActiveWindowDoesNotIgnorePrivateWindow = function(assert, done) {
+  // make a new private window
+  makeEmptyBrowserWindow({
+    private: true
+  }).then(focus).then(function(window) {
+    // PWPB case
+    if (isWindowPBSupported) {
+      assert.equal(isPrivate(winUtils.activeWindow), true,
+                   "active window is private");
+      assert.equal(isPrivate(winUtils.activeBrowserWindow), true,
+                   "active browser window is private");
+      assert.ok(isWindowPrivate(window), "window is private");
+      assert.ok(isPrivate(window), "window is private");
+
+      // pb mode is supported
+      assert.ok(
+        isWindowPrivate(winUtils.activeWindow),
+        "active window is private when pb mode is supported");
+      assert.ok(
+        isWindowPrivate(winUtils.activeBrowserWindow),
+        "active browser window is private when pb mode is supported");
+      assert.ok(isPrivate(winUtils.activeWindow),
+                "active window is private when pb mode is supported");
+      assert.ok(isPrivate(winUtils.activeBrowserWindow),
+        "active browser window is private when pb mode is supported");
+    }
+    // Global case
+    else {
+      assert.equal(isPrivate(winUtils.activeWindow), false,
+                   "active window is not private");
+      assert.equal(isPrivate(winUtils.activeBrowserWindow), false,
+                   "active browser window is not private");
+      assert.equal(isWindowPrivate(window), false, "window is not private");
+      assert.equal(isPrivate(window), false, "window is not private");
+    }
+
+    close(window).then(done);
+  });
+}
+
+exports.testWindowIteratorIgnoresPrivateWindows = function(assert, done) {
+  // make a new private window
+  makeEmptyBrowserWindow({
+    private: true
+  }).then(focus).then(function(window) {
+    assert.equal(isWindowPrivate(window), isWindowPBSupported);
+    assert.ok(toArray(winUtils.windowIterator()).indexOf(window) > -1,
+              "window is in windowIterator()");
+
+    close(window).then(done);
+  });
+};
+
+// test that it is not possible to find a private window in
+// windows module's iterator
+exports.testWindowIteratorPrivateDefault = function(assert, done) {
+  // there should only be one window open here, if not give us the
+  // the urls
+  if (browserWindows.length > 1) {
+    for each (let tab in tabs) {
+      assert.fail("TAB URL: " + tab.url);
+    }
+  }
+  else {
+    assert.equal(browserWindows.length, 1, 'only one window open');
+  }
+
+  open('chrome://browser/content/browser.xul', {
+    features: {
+      private: true,
+      chrome: true
+    }
+  }).then(focus).then(function(window) {
+    // test that there is a private window opened
+    assert.equal(isPrivate(window), isWindowPBSupported, 'there is a private window open');
+    assert.equal(isPrivate(winUtils.activeWindow), isWindowPBSupported);
+    assert.equal(isPrivate(getMostRecentWindow()), isWindowPBSupported);
+    assert.equal(isPrivate(browserWindows.activeWindow), isWindowPBSupported);
+
+    assert.equal(browserWindows.length, 2, '2 windows open');
+    assert.equal(windows(null, { includePrivate: true }).length, 2);
+
+    close(window).then(done);
+  });
+};
deleted file mode 100644
--- a/addon-sdk/source/test/addons/private-browsing-supported/windows.js
+++ /dev/null
@@ -1,245 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-'use strict';
-
-const { Cc, Ci } = require('chrome');
-const { isPrivate } = require('sdk/private-browsing');
-const { isWindowPBSupported } = require('sdk/private-browsing/utils');
-const { onFocus, getMostRecentWindow, getWindowTitle,
-        getFrames, windows, open: openWindow, isWindowPrivate } = require('sdk/window/utils');
-const { open, close, focus, promise } = require('sdk/window/helpers');
-const { browserWindows } = require("sdk/windows");
-const winUtils = require("sdk/deprecated/window-utils");
-const { fromIterator: toArray } = require('sdk/util/array');
-
-const WM = Cc['@mozilla.org/appshell/window-mediator;1'].getService(Ci.nsIWindowMediator);
-
-const BROWSER = 'chrome://browser/content/browser.xul';
-
-function makeEmptyBrowserWindow(options) {
-  options = options || {};
-  return open(BROWSER, {
-    features: {
-      chrome: true,
-      private: !!options.private
-    }
-  });
-}
-
-exports.testWindowTrackerIgnoresPrivateWindows = function(assert, done) {
-  var myNonPrivateWindow, myPrivateWindow;
-  var finished = false;
-  var privateWindow;
-  var privateWindowClosed = false;
-  var privateWindowOpened = false;
-
-  let wt = winUtils.WindowTracker({
-    onTrack: function(window) {
-      if (window === myPrivateWindow) {
-        assert.equal(isPrivate(window), isWindowPBSupported);
-        privateWindowOpened = true;
-      }
-    },
-    onUntrack: function(window) {
-      if (window === myPrivateWindow && isWindowPBSupported) {
-        privateWindowClosed = true;
-      }
-
-      if (window === myNonPrivateWindow) {
-        assert.equal(privateWindowClosed, isWindowPBSupported);
-        assert.ok(privateWindowOpened);
-        wt.unload();
-        done();
-      }
-    }
-  });
-
-  // make a new private window
-  myPrivateWindow = openWindow(BROWSER, {
-  	features: {
-      private: true
-    }
-  });
-  promise(myPrivateWindow, 'load').then(function(window) {
-    assert.equal(isPrivate(window), isWindowPBSupported, 'private window isPrivate');
-    assert.equal(isWindowPrivate(window), isWindowPBSupported);
-    assert.ok(getFrames(window).length > 1, 'there are frames for private window');
-    assert.equal(getWindowTitle(window), window.document.title,
-                 'getWindowTitle works');
-
-    close(myPrivateWindow).then(function() {
-      assert.pass('private window was closed');
-      makeEmptyBrowserWindow().then(function(window) {
-        myNonPrivateWindow = window;
-        assert.notDeepEqual(myPrivateWindow, myNonPrivateWindow);
-        assert.pass('opened new window');
-        close(myNonPrivateWindow).then(function() {
-          assert.pass('non private window was closed');
-        })
-      });
-    });
-  });
-};
-
-// Test setting activeWIndow and onFocus for private windows
-exports.testSettingActiveWindowDoesNotIgnorePrivateWindow = function(assert, done) {
-  let browserWindow = WM.getMostRecentWindow("navigator:browser");
-  let testSteps;
-
-  assert.equal(winUtils.activeBrowserWindow, browserWindow,
-               "Browser window is the active browser window.");
-  assert.ok(!isPrivate(browserWindow), "Browser window is not private.");
-
-  // make a new private window
-  makeEmptyBrowserWindow({
-    private: true
-  }).then(focus).then(function(window) {
-    let continueAfterFocus = function(window) onFocus(window).then(nextTest);
-
-    // PWPB case
-    if (isWindowPBSupported) {
-      assert.ok(isPrivate(window), "window is private");
-      assert.notDeepEqual(winUtils.activeBrowserWindow, browserWindow);
-    }
-    // Global case
-    else {
-      assert.ok(!isPrivate(window), "window is not private");
-    }
-
-    assert.strictEqual(winUtils.activeBrowserWindow, window,
-                 "Correct active browser window pb supported");
-    assert.notStrictEqual(browserWindow, window,
-                 "The window is not the old browser window");
-
-    testSteps = [
-      function() {
-        // test setting a non private window
-        continueAfterFocus(winUtils.activeWindow = browserWindow);
-      },
-      function() {
-        assert.strictEqual(winUtils.activeWindow, browserWindow,
-                           "Correct active window [1]");
-        assert.strictEqual(winUtils.activeBrowserWindow, browserWindow,
-                           "Correct active browser window [1]");
-
-        // test focus(window)
-        focus(window).then(nextTest);
-      },
-      function(w) {
-        assert.strictEqual(w, window, 'require("sdk/window/helpers").focus on window works');
-        assert.strictEqual(winUtils.activeBrowserWindow, window,
-                           "Correct active browser window [2]");
-        assert.strictEqual(winUtils.activeWindow, window,
-                           "Correct active window [2]");
-
-        // test setting a private window
-        continueAfterFocus(winUtils.activeWindow = window);
-      },
-      function() {
-        assert.deepEqual(winUtils.activeBrowserWindow, window,
-                         "Correct active browser window [3]");
-        assert.deepEqual(winUtils.activeWindow, window,
-                         "Correct active window [3]");
-
-        // just to get back to original state
-        continueAfterFocus(winUtils.activeWindow = browserWindow);
-      },
-      function() {
-        assert.deepEqual(winUtils.activeBrowserWindow, browserWindow,
-                         "Correct active browser window when pb mode is supported [4]");
-        assert.deepEqual(winUtils.activeWindow, browserWindow,
-                         "Correct active window when pb mode is supported [4]");
-
-        close(window).then(done);
-      }
-    ];
-
-    function nextTest() {
-      let args = arguments;
-      if (testSteps.length) {
-        require('sdk/timers').setTimeout(function() {
-          (testSteps.shift()).apply(null, args);
-        }, 0);
-      }
-    }
-    nextTest();
-  });
-};
-
-exports.testActiveWindowDoesNotIgnorePrivateWindow = function(assert, done) {
-  // make a new private window
-  makeEmptyBrowserWindow({
-    private: true
-  }).then(function(window) {
-    // PWPB case
-    if (isWindowPBSupported) {
-      assert.equal(isPrivate(winUtils.activeWindow), true,
-                   "active window is private");
-      assert.equal(isPrivate(winUtils.activeBrowserWindow), true,
-                   "active browser window is private");
-      assert.ok(isWindowPrivate(window), "window is private");
-      assert.ok(isPrivate(window), "window is private");
-
-      // pb mode is supported
-      assert.ok(
-        isWindowPrivate(winUtils.activeWindow),
-        "active window is private when pb mode is supported");
-      assert.ok(
-        isWindowPrivate(winUtils.activeBrowserWindow),
-        "active browser window is private when pb mode is supported");
-      assert.ok(isPrivate(winUtils.activeWindow),
-                "active window is private when pb mode is supported");
-      assert.ok(isPrivate(winUtils.activeBrowserWindow),
-        "active browser window is private when pb mode is supported");
-    }
-    // Global case
-    else {
-      assert.equal(isPrivate(winUtils.activeWindow), false,
-                   "active window is not private");
-      assert.equal(isPrivate(winUtils.activeBrowserWindow), false,
-                   "active browser window is not private");
-      assert.equal(isWindowPrivate(window), false, "window is not private");
-      assert.equal(isPrivate(window), false, "window is not private");
-    }
-
-    close(window).then(done);
-  });
-}
-
-exports.testWindowIteratorIgnoresPrivateWindows = function(assert, done) {
-  // make a new private window
-  makeEmptyBrowserWindow({
-    private: true
-  }).then(function(window) {
-    assert.equal(isWindowPrivate(window), isWindowPBSupported);
-    assert.ok(toArray(winUtils.windowIterator()).indexOf(window) > -1,
-              "window is in windowIterator()");
-
-    close(window).then(done);
-  });
-};
-
-// test that it is not possible to find a private window in
-// windows module's iterator
-exports.testWindowIteratorPrivateDefault = function(assert, done) {
-  assert.equal(browserWindows.length, 1, 'only one window open');
-
-  open('chrome://browser/content/browser.xul', {
-    features: {
-      private: true,
-      chrome: true
-    }
-  }).then(function(window) {
-    // test that there is a private window opened
-    assert.equal(isPrivate(window), isWindowPBSupported, 'there is a private window open');
-    assert.equal(isPrivate(winUtils.activeWindow), isWindowPBSupported);
-    assert.equal(isPrivate(getMostRecentWindow()), isWindowPBSupported);
-    assert.equal(isPrivate(browserWindows.activeWindow), isWindowPBSupported);
-
-    assert.equal(browserWindows.length, 2, '2 windows open');
-    assert.equal(windows(null, { includePrivate: true }).length, 2);
-
-    close(window).then(done);
-  });
-};
--- a/addon-sdk/source/test/private-browsing/tabs.js
+++ b/addon-sdk/source/test/private-browsing/tabs.js
@@ -7,17 +7,17 @@ const { getOwnerWindow } = require('sdk/
 
 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');
 
   let rawTab = openTab(chromeWindow, 'data:text/html,<h1>Hi!</h1>', {
-  	isPrivate: true
+    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));
 
   closeTab(rawTab);
--- a/addon-sdk/source/test/test-cuddlefish.js
+++ b/addon-sdk/source/test/test-cuddlefish.js
@@ -8,19 +8,17 @@ const { Loader, Require, unload, overrid
 const packaging = require('@loader/options');
 
 exports['test loader'] = function(assert) {
   var prints = [];
   function print(message) {
     prints.push(message);
   }
 
-  let options = JSON.parse(JSON.stringify(packaging));
-
-  let loader = Loader(override(options, {
+  let loader = Loader(override(packaging, {
     globals: {
       print: print,
       foo: 1
     }
   }));
   let require = Require(loader, module);
 
   var fixture = require('./loader/fixture');
--- a/addon-sdk/source/test/test-heritage.js
+++ b/addon-sdk/source/test/test-heritage.js
@@ -74,63 +74,16 @@ exports['test inheritance'] = function(a
   assert.ok(Descendant() instanceof Descendant,
             'instantiates correctly');
   assert.ok(Descendant() instanceof Ancestor,
             'Inherits for passed `extends`');
   assert.equal(Descendant().method(), 'hello descendant',
                'propreties inherited');
 };
 
-exports['test prototype immutability'] = function(assert) {
-  let Foo = Class({
-    name: 'hello',
-    rename: function rename(name) {
-      this.name = name;
-    }
-  });
-
-  /* Disable until release with Bug 674195 fix is shipped
-  assert.ok(Object.isFrozen(Foo), 'Foo is frozen');
-  assert.ok(Object.isFrozen(Foo.prototype), 'Foo prototype is frozen');
-  assert.ok(Object.isFrozen(Object.getPrototypeOf(Foo.prototype)),
-            'Class.prototype is frozen');
-  assert.equal(Object.getPrototypeOf(Object.getPrototypeOf(Foo.prototype)),
-               null, 'prototype of Class.prototype is null');
-  */
-
-  assert.throws(function() {
-    var override = function() {};
-    Foo.prototype.extend = override;
-    if (Foo.prototype.extend !== override)
-      throw Error('Property was not set');
-  }, 'Can not change prototype properties');
-
-  assert.throws(function() {
-    Foo.prototype.foo = 'bar';
-    if (Foo.prototype.foo !== 'bar')
-      throw Error('Property was not set');
-  }, 'Can not add prototype properties');
-
-  assert.throws(function() {
-    delete Foo.prototype.name;
-    if ('name' in Foo.prototype)
-      throw Error('Property was not deleted');
-  }, 'Can not remove prototype properties');
-
-  var Bar = Class({
-    extends: Foo,
-    rename: function rename() {
-      return this.name;
-    }
-  });
-
-  assert.equal(Bar().rename(), 'hello',
-               'properties may be overided on decedents');
-};
-
 exports['test immunity against __proto__'] = function(assert) {
   let Foo = Class({ name: 'foo', hacked: false });
 
   let Bar = Class({ extends: Foo, name: 'bar' });
 
   assert.throws(function() {
     Foo.prototype.__proto__ = { hacked: true };
     if (Foo() instanceof Base && !Foo().hacked)
@@ -139,45 +92,16 @@ exports['test immunity against __proto__
 
   assert.throws(function() {
     Foo.prototype.__proto__ = { hacked: true };
     if (Bar() instanceof Foo && !Bar().hacked)
       throw Error('can not change prototype chain');
   }, 'prototype chain of decedants immune to __proto__ hacks');
 };
 
-exports['test instance mutability'] = function(assert) {
-  let Foo = Class({
-    name: 'foo',
-    initialize: function initialize(number) {
-      this.number = number;
-    }
-  });
-
-  let f1 = Foo();
-
-  assert.throws(function() {
-    f1.initialize = 'f1';
-    if (f1.name !== 'f1')
-      throw Error('Property was not set');
-  }, 'can not change prototype properties');
-
-  f1.alias = 'f1';
-  assert.equal(f1.alias, 'f1', 'instance is mutable');
-
-  delete f1.alias;
-  assert.ok(!('alias' in f1), 'own properties are deletable');
-
-  f1.initialize(1);
-  assert.equal(f1.number, 1, 'method can mutate instances own properties');
-
-  f1.name = 'bar';
-  assert.equal(f1.name, 'bar', 'data properties are mutable on instance');
-};
-
 exports['test super'] = function(assert) {
   var Foo = Class({
     initialize: function initialize(options) {
       this.name = options.name;
     }
   });
 
   var Bar = Class({
--- a/addon-sdk/source/test/test-page-mod.js
+++ b/addon-sdk/source/test/test-page-mod.js
@@ -1,24 +1,28 @@
 /* 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 pageMod = require("sdk/page-mod");
+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, getFrames, getMostRecentBrowserWindow } = require('sdk/window/utils');
+const { open, openDialog, getFrames, getMostRecentBrowserWindow } = require('sdk/window/utils');
 const windowUtils = require('sdk/deprecated/window-utils');
-const { getTabContentWindow, getActiveTab, openTab, closeTab } = require('sdk/tabs/utils');
-const { is } = require('sdk/system/xul-app');
+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 { isPrivate } = require('sdk/private-browsing');
+const { isTabPBSupported, isWindowPBSupported } = require('sdk/private-browsing/utils');
+const promise = require("sdk/core/promise");
 
 /* 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);
@@ -131,17 +135,17 @@ exports.testPageModIncludes = function(t
             done();
           });
     }
     );
 };
 
 exports.testPageModErrorHandling = function(test) {
   test.assertRaises(function() {
-      new pageMod.PageMod();
+      new PageMod();
     },
     'pattern is undefined',
     "PageMod() throws when 'include' option is not specified.");
 };
 
 /* Tests for internal functions. */
 exports.testCommunication1 = function(test) {
   let workerDone = false,
@@ -334,17 +338,16 @@ exports.testHistory = function(test) {
     }
   );
 };
 
 exports.testRelatedTab = function(test) {
   test.waitUntilDone();
 
   let tab;
-  let { PageMod } = require("sdk/page-mod");
   let pageMod = new PageMod({
     include: "about:*",
     onAttach: function(worker) {
       test.assert(!!worker.tab, "Worker.tab exists");
       test.assertEqual(tab, worker.tab, "Worker.tab is valid");
       pageMod.destroy();
       tab.close(function() {
         test.done();
@@ -543,17 +546,17 @@ exports.testAttachToTabsOnly = function(
     let hiddenFrames = require('sdk/frame/hidden-frame');
     let hiddenFrame = hiddenFrames.add(hiddenFrames.HiddenFrame({
       onReady: function () {
         let element = this.element;
         element.addEventListener('DOMContentLoaded', function onload() {
           element.removeEventListener('DOMContentLoaded', onload, false);
           hiddenFrames.remove(hiddenFrame);
 
-          if (!is("Fennec")) {
+          if (!xulApp.is("Fennec")) {
             openToplevelWindow();
           }
           else {
             openBrowserIframe(); 
           }
         }, false);
         element.setAttribute('src', 'data:text/html;charset=utf-8,foo');
       }
@@ -950,17 +953,17 @@ exports.testExistingOnFrames = function(
   let window = getTabContentWindow(tab);
 
   function wait4Iframes() {
     if (window.document.readyState != "complete" ||
         getFrames(window).length != 2) {
       return;
     }
 
-    let pagemodOnExisting = pageMod.PageMod({
+    let pagemodOnExisting = PageMod({
       include: ["*", "data:*"],
       attachTo: ["existing", "frame"],
       contentScriptWhen: 'ready',
       onAttach: function(worker) {
         // need to ignore urls that are not part of the test, because other
         // tests are not closing their tabs when they complete..
         if (urls.indexOf(worker.url) == -1)
           return;
@@ -985,17 +988,17 @@ exports.testExistingOnFrames = function(
             pagemodOffExisting.destroy();
             closeTab(tab);
             test.done();
           }, 0);
         }
       }
     });
 
-    let pagemodOffExisting = pageMod.PageMod({
+    let pagemodOffExisting = PageMod({
       include: ["*", "data:*"],
       attachTo: ["frame"],
       contentScriptWhen: 'ready',
       onAttach: function(mod) {
         test.fail('pagemodOffExisting page-mod should not have been attached');
       }
     });
   }
@@ -1046,8 +1049,67 @@ exports.testEvents = function(test) {
       test.assert(
         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:*",
+    onAttach: function(worker) {
+      if (isTabPBSupported || isWindowPBSupported) {
+        // When PB isn't supported, the page-mod will apply to all document
+        // as all of them will be non-private
+        test.assertEqual(worker.tab.url,
+                         nonPrivateUri,
+                         "page-mod should only attach to the non-private tab");
+      }
+      test.assert(!isPrivate(worker),
+                  "The worker is really non-private");
+      test.assert(!isPrivate(worker.tab),
+                  "The document is really non-private");
+      pageMod.destroy();
+      page1.close().then(page2.close).then(test.done.bind(test));
+    }
+  });
+
+  let page1 = openWebpage(privateUri, true);
+  let page2 = openWebpage(nonPrivateUri, false);
+}
--- a/addon-sdk/source/test/test-panel.js
+++ b/addon-sdk/source/test/test-panel.js
@@ -1,17 +1,36 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 'use strict';
 
-let { Cc, Ci } = require("chrome");
+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 { defer } = require('sdk/core/promise');
+const { getMostRecentBrowserWindow } = require('sdk/window/utils');
+
+const SVG_URL = self.data.url('mofo_logo.SVG');
+
+function makeEmptyPrivateBrowserWindow(options) {
+  options = options || {};
+  return open('chrome://browser/content/browser.xul', {
+    features: {
+      chrome: true,
+      toolbar: true,
+      private: true
+    }
+  });
+}
 
 exports["test Panel"] = function(assert, done) {
   const { Panel } = require('sdk/panel');
 
   let panel = Panel({
     contentURL: "about:buildconfig",
     contentScript: "self.postMessage(1); self.on('message', function() self.postMessage(2));",
     onMessage: function (message) {
@@ -451,18 +470,16 @@ exports["test Content URL Option"] = fun
   }
 
   assert.throws(function () Panel({ contentURL: "foo" }),
                     /The `contentURL` option must be a valid URL./,
                     "Panel throws an exception if contentURL is not a URL.");
 };
 
 exports["test SVG Document"] = function(assert) {
-  let SVG_URL = self.data.url("mofo_logo.SVG");
-
   let panel = require("sdk/panel").Panel({ contentURL: SVG_URL });
 
   panel.show();
   panel.hide();
   panel.destroy();
 
   assert.pass("contentURL accepts a svg document");
   assert.equal(panel.contentURL, SVG_URL,
@@ -505,16 +522,147 @@ exports["test console.log in Panel"] = f
   function onMessage(type, message) {
     assert.equal(type, 'log', 'console.log() works');
     assert.equal(message, text, 'console.log() works');
     panel.destroy();
     done();
   }
 };
 
+if (isWindowPBSupported) {
+  exports.testPanelDoesNotShowInPrivateWindowNoAnchor = function(assert, done) {
+    let loader = Loader(module);
+    let { Panel } = loader.require("sdk/panel");
+    let browserWindow = getMostRecentBrowserWindow();
+
+    assert.equal(isPrivate(browserWindow), false, 'open window is not private');
+
+    let panel = Panel({
+      contentURL: SVG_URL
+    });
+
+    testShowPanel(assert, panel).
+      then(makeEmptyPrivateBrowserWindow).
+      then(focus).
+      then(function(window) {
+        assert.equal(isPrivate(window), true, 'opened window is private');
+        assert.pass('private window was focused');
+        return window;
+      }).
+      then(function(window) {
+        let { promise, resolve } = defer();
+        let showTries = 0;
+        let showCount = 0;
+
+        panel.on('show', function runTests() {
+          showCount++;
+
+          if (showTries == 2) {
+            panel.removeListener('show', runTests);
+            assert.equal(showCount, 1, 'show count is correct - 1');
+            resolve(window);
+          }
+        });
+        showTries++;
+        panel.show();
+        showTries++;
+        panel.show(browserWindow.gBrowser);
+
+        return promise;
+      }).
+      then(function(window) {
+        assert.equal(panel.isShowing, true, 'panel is still showing');
+        panel.hide();
+        assert.equal(panel.isShowing, false, 'panel is hidden');
+        return window;
+      }).
+      then(close).
+      then(function() {
+        assert.pass('private window was closed');
+      }).
+      then(testShowPanel.bind(null, assert, panel)).
+      then(done, assert.fail.bind(assert));
+  }
+
+  exports.testPanelDoesNotShowInPrivateWindowWithAnchor = function(assert, done) {
+    let loader = Loader(module);
+    let { Panel } = loader.require("sdk/panel");
+    let browserWindow = getMostRecentBrowserWindow();
+
+    assert.equal(isPrivate(browserWindow), false, 'open window is not private');
+
+    let panel = Panel({
+      contentURL: SVG_URL
+    });
+
+    testShowPanel(assert, panel).
+      then(makeEmptyPrivateBrowserWindow).
+      then(focus).
+      then(function(window) {
+        assert.equal(isPrivate(window), true, 'opened window is private');
+        assert.pass('private window was focused');
+        return window;
+      }).
+      then(function(window) {
+        let { promise, resolve } = defer();
+        let showTries = 0;
+        let showCount = 0;
+
+        panel.on('show', function runTests() {
+          showCount++;
+
+          if (showTries == 2) {
+            panel.removeListener('show', runTests);
+            assert.equal(showCount, 1, 'show count is correct - 1');
+            resolve(window);
+          }
+        });
+        showTries++;
+        panel.show(window.gBrowser);
+        showTries++;
+        panel.show(browserWindow.gBrowser);
+
+        return promise;
+      }).
+      then(function(window) {
+        assert.equal(panel.isShowing, true, 'panel is still showing');
+        panel.hide();
+        assert.equal(panel.isShowing, false, 'panel is hidden');
+        return window;
+      }).
+      then(close).
+      then(function() {
+        assert.pass('private window was closed');
+      }).
+      then(testShowPanel.bind(null, assert, panel)).
+      then(done, assert.fail.bind(assert));
+  }
+}
+
+function testShowPanel(assert, panel) {
+  let { promise, resolve } = defer();
+
+  assert.ok(!panel.isShowing, 'the panel is not showing [1]');
+
+  panel.once('show', function() {
+    assert.ok(panel.isShowing, 'the panel is showing');
+
+    panel.once('hide', function() {
+      assert.ok(!panel.isShowing, 'the panel is not showing [2]');
+
+      resolve(null);
+    });
+
+    panel.hide();
+  })
+  panel.show();
+
+  return promise;
+}
+
 try {
   require("sdk/panel");
 }
 catch (e) {
   if (!/^Unsupported Application/.test(e.message))
     throw e;
 
   module.exports = {
--- a/addon-sdk/source/test/test-promise.js
+++ b/addon-sdk/source/test/test-promise.js
@@ -1,18 +1,20 @@
 /* vim:set ts=2 sw=2 sts=2 expandtab */
 /*jshint asi: true undef: true es5: true node: true devel: true
          forin: true */
 /*global define: true */
 
 'use strict';
 
 var core = require('sdk/core/promise'),
-    defer = core.defer, resolve = core.resolve, reject = core.reject,
-    promised = core.promised
+    defer = core.defer, resolve = core.resolve, reject = core.reject, all = core.all,
+    promised = core.promised;
+
+var { setTimeout } = require('sdk/timers');
 
 exports['test all observers are notified'] = function(assert, done) {
   var expected = 'Taram pam param!'
   var deferred = defer()
   var pending = 10, i = 0
 
   function resolved(value) {
     assert.equal(value, expected, 'value resolved as expected: #' + pending)
@@ -319,9 +321,73 @@ exports['test arrays should not flatten'
 
   combine(a.promise, b.promise).then(done)
 
 
   a.resolve('Hello')
   b.resolve([ 'my', 'friend' ])
 }
 
+exports['test `all` for all promises'] = function (assert, done) {
+  all([
+    resolve(5), resolve(7), resolve(10)
+  ]).then(function (val) {
+    assert.equal(
+      val[0] === 5 &&
+      val[1] === 7 &&
+      val[2] === 10
+    , true, 'return value contains resolved promises values');
+    done();
+  }, function () {
+    assert.fail('should not call reject function');
+  });
+};
+
+exports['test `all` aborts upon first reject'] = function (assert, done) {
+  all([
+    resolve(5), reject('error'), delayedResolve()
+  ]).then(function (val) {
+    assert.fail('Successful resolve function should not be called');
+  }, function (reason) {
+    assert.equal(reason, 'error', 'should reject the `all` promise');
+    done();
+  });
+
+  function delayedResolve () {
+    var deferred = defer();
+    setTimeout(deferred.resolve, 50);
+    return deferred.promise;
+  }
+};
+
+exports['test `all` with array containing non-promise'] = function (assert, done) {
+  all([
+    resolve(5), resolve(10), 925
+  ]).then(function (val) {
+    assert.equal(val[2], 925, 'non-promises should pass-through value');
+    done();
+  }, function () {
+    assert.fail('should not be rejected');
+  });
+};
+
+exports['test `all` should resolve with an empty array'] = function (assert, done) {
+  all([]).then(function (val) {
+    assert.equal(Array.isArray(val), true, 'should return array in resolved');
+    assert.equal(val.length, 0, 'array should be empty in resolved');
+    done();
+  }, function () {
+    assert.fail('should not be rejected');
+  });
+};
+
+exports['test `all` with multiple rejected'] = function (assert, done) {
+  all([
+    reject('error1'), reject('error2'), reject('error3')
+  ]).then(function (value) {
+    assert.fail('should not be successful');
+  }, function (reason) {
+    assert.equal(reason, 'error1', 'should reject on first promise reject');
+    done();
+  });
+};
+
 require("test").run(exports)
--- a/addon-sdk/source/test/test-selection.js
+++ b/addon-sdk/source/test/test-selection.js
@@ -14,34 +14,63 @@ const HTML = "<html>\
 
 const URL = "data:text/html;charset=utf-8," + encodeURIComponent(HTML);
 
 const FRAME_HTML = "<iframe src='" + URL + "'><iframe>";
 const FRAME_URL = "data:text/html;charset=utf-8," + encodeURIComponent(FRAME_HTML);
 
 const { defer } = require("sdk/core/promise");
 const tabs = require("sdk/tabs");
+const { setTabURL } = require("sdk/tabs/utils");
 const { getActiveTab, getTabContentWindow, closeTab } = require("sdk/tabs/utils")
 const { getMostRecentBrowserWindow } = require("sdk/window/utils");
+const { open: openNewWindow } = require("sdk/window/helpers");
 const { Loader } = require("sdk/test/loader");
 const { setTimeout } = require("sdk/timers");
 const { Cu } = require("chrome");
+const { merge } = require("sdk/util/object");
+const { isPrivate } = require("sdk/private-browsing");
 
 // General purpose utility functions
 
 /**
  * Opens the url given and return a promise, that will be resolved with the
  * content window when the document is ready.
  *
  * I believe this approach could be useful in most of our unit test, that
  * requires to open a tab and need to access to its content.
  */
-function open(url) {
+function open(url, options) {
   let { promise, resolve } = defer();
 
+  if (options && typeof(options) === "object") {
+    openNewWindow("", {
+      features: merge({ toolbar: true }, options)
+    }).then(function(chromeWindow) {
+      if (isPrivate(chromeWindow) !== !!options.private)
+        throw new Error("Window should have Private set to " + !!options.private);
+
+      let tab = getActiveTab(chromeWindow);
+
+      tab.addEventListener("load", function ready(event) {
+        let { document } = getTabContentWindow(this);
+
+        if (document.readyState === "complete" && document.URL === url) {
+          this.removeEventListener(event.type, ready);
+
+          resolve(document.defaultView);
+        }
+      })
+
+      setTabURL(tab, url);
+    });
+
+    return promise;
+  };
+
   tabs.open({
     url: url,
     onReady: function(tab) {
       // Unfortunately there is no way to get a XUL Tab from SDK Tab on Firefox,
       // only on Fennec. We should implement `tabNS` also on Firefox in order
       // to have that.
 
       // Here we assuming that the most recent browser window is the one we're
@@ -53,20 +82,26 @@ function open(url) {
   });
 
   return promise;
 };
 
 /**
  * Close the Active Tab
  */
-function close() {
-  // Here we assuming that the most recent browser window is the one we're
-  // doing the test, and the active tab is the one we just opened.
-  closeTab(getActiveTab(getMostRecentBrowserWindow()));
+function close(window) {
+  if (window && window.top && typeof(window.top).close === "function") {
+    window.top.close();
+  } else {
+    // Here we assuming that the most recent browser window is the one we're
+    // doing the test, and the active tab is the one we just opened.
+    let tab = getActiveTab(getMostRecentBrowserWindow());
+
+    closeTab(tab);
+  }
 }
 
 /**
  * Reload the window given and return a promise, that will be resolved with the
  * content window after a small delay.
  */
 function reload(window) {
   let { promise, resolve } = defer();
@@ -209,30 +244,34 @@ function selectContentFirstDiv(window) {
  * Dispatch the selection event for the selection listener added by
  * `nsISelectionPrivate.addSelectionListener`
  */
 function dispatchSelectionEvent(window) {
   // We modify the selection in order to dispatch the selection's event, by
   // contract the selection by one character. So if the text selected is "foo"
   // will be "fo".
   window.getSelection().modify("extend", "backward", "character");
+
+  return window;
 }
 
 /**
  * Dispatch the selection event for the selection listener added by
  * `window.onselect` / `window.addEventListener`
  */
 function dispatchOnSelectEvent(window) {
   let { document } = window;
   let textarea = document.querySelector("textarea");
   let event = document.createEvent("UIEvents");
 
   event.initUIEvent("select", true, true, window, 1);
 
   textarea.dispatchEvent(event);
+
+  return window;
 }
 
 /**
  * Creates empty ranges and add them to selections
  */
 function createEmptySelections(window) {
   selectAllDivs(window);
 
@@ -261,17 +300,17 @@ exports["test No Selection"] = function(
 
     let selectionCount = 0;
     for each (let sel in selection)
       selectionCount++;
 
     assert.equal(selectionCount, 0,
       "No iterable selections");
 
-  }).then(close).then(loader.unload).then(done);
+  }).then(close).then(loader.unload).then(done, assert.fail);
 };
 
 exports["test Single DOM Selection"] = function(assert, done) {
   let loader = Loader(module);
   let selection = loader.require("sdk/selection");
 
   open(URL).then(selectFirstDiv).then(function() {
 
@@ -293,17 +332,17 @@ exports["test Single DOM Selection"] = f
 
       assert.equal(sel.html, "<div>foo</div>",
         "iterable selection.html with single DOM Selection works.");
     }
 
     assert.equal(selectionCount, 1,
       "One iterable selection");
 
-  }).then(close).then(loader.unload).then(done);
+  }).then(close).then(loader.unload).then(done, assert.fail);
 };
 
 exports["test Multiple DOM Selection"] = function(assert, done) {
   let loader = Loader(module);
   let selection = loader.require("sdk/selection");
 
   open(URL).then(selectAllDivs).then(function() {
     let expectedText = ["foo", "and"];
@@ -327,17 +366,17 @@ exports["test Multiple DOM Selection"] =
         "iterable selection.text with multiple DOM Selection works.");
 
       selectionCount++;
     }
 
     assert.equal(selectionCount, 2,
       "Two iterable selections");
 
-  }).then(close).then(loader.unload).then(done);
+  }).then(close).then(loader.unload).then(done, assert.fail);
 };
 
 exports["test Textarea Selection"] = function(assert, done) {
   let loader = Loader(module);
   let selection = loader.require("sdk/selection");
 
   open(URL).then(selectTextarea).then(function() {
 
@@ -359,17 +398,17 @@ exports["test Textarea Selection"] = fun
 
       assert.strictEqual(sel.html, null,
         "iterable selection.html with Textarea Selection works.");
     }
 
     assert.equal(selectionCount, 1,
       "One iterable selection");
 
-  }).then(close).then(loader.unload).then(done);
+  }).then(close).then(loader.unload).then(done, assert.fail);
 };
 
 exports["test Set Text in Multiple DOM Selection"] = function(assert, done) {
   let loader = Loader(module);
   let selection = loader.require("sdk/selection");
 
   open(URL).then(selectAllDivs).then(function() {
     let expectedText = ["bar", "and"];
@@ -393,17 +432,17 @@ exports["test Set Text in Multiple DOM S
         "iterable selection.html with multiple DOM Selection works.");
 
       selectionCount++;
     }
 
     assert.equal(selectionCount, 2,
       "Two iterable selections");
 
-  }).then(close).then(loader.unload).then(done);
+  }).then(close).then(loader.unload).then(done, assert.fail);
 };
 
 exports["test Set HTML in Multiple DOM Selection"] = function(assert, done) {
   let loader = Loader(module);
   let selection = loader.require("sdk/selection");
 
   open(URL).then(selectAllDivs).then(function() {
     let html = "<span>b<b>a</b>r</span>";
@@ -429,17 +468,17 @@ exports["test Set HTML in Multiple DOM S
         "iterable selection.html with multiple DOM Selection works.");
 
       selectionCount++;
     }
 
     assert.equal(selectionCount, 2,
       "Two iterable selections");
 
-  }).then(close).then(loader.unload).then(done);
+  }).then(close).then(loader.unload).then(done, assert.fail);
 };
 
 exports["test Set HTML as text in Multiple DOM Selection"] = function(assert, done) {
   let loader = Loader(module);
   let selection = loader.require("sdk/selection");
 
   open(URL).then(selectAllDivs).then(function() {
     let text = "<span>b<b>a</b>r</span>";
@@ -466,17 +505,17 @@ exports["test Set HTML as text in Multip
         "iterable selection.html with multiple DOM Selection works.");
 
       selectionCount++;
     }
 
     assert.equal(selectionCount, 2,
       "Two iterable selections");
 
-  }).then(close).then(loader.unload).then(done);
+  }).then(close).then(loader.unload).then(done, assert.fail);
 };
 
 exports["test Set Text in Textarea Selection"] = function(assert, done) {
   let loader = Loader(module);
   let selection = loader.require("sdk/selection");
 
   open(URL).then(selectTextarea).then(function() {
 
@@ -499,17 +538,17 @@ exports["test Set Text in Textarea Selec
 
       assert.strictEqual(sel.html, null,
         "iterable selection.html with Textarea Selection works.");
     }
 
     assert.equal(selectionCount, 1,
       "One iterable selection");
 
-  }).then(close).then(loader.unload).then(done);
+  }).then(close).then(loader.unload).then(done, assert.fail);
 };
 
 exports["test Set HTML in Textarea Selection"] = function(assert, done) {
   let loader = Loader(module);
   let selection = loader.require("sdk/selection");
 
   open(URL).then(selectTextarea).then(function() {
 
@@ -534,17 +573,17 @@ exports["test Set HTML in Textarea Selec
 
       assert.strictEqual(sel.html, null,
         "iterable selection.html with Textarea Selection works.");
     }
 
     assert.equal(selectionCount, 1,
       "One iterable selection");
 
-  }).then(close).then(loader.unload).then(done);
+  }).then(close).then(loader.unload).then(done, assert.fail);
 };
 
 exports["test Empty Selections"] = function(assert, done) {
   let loader = Loader(module);
   let selection = loader.require("sdk/selection");
 
   open(URL).then(createEmptySelections).then(function(){
     assert.equal(selection.isContiguous, false,
@@ -558,17 +597,17 @@ exports["test Empty Selections"] = funct
 
     let selectionCount = 0;
     for each (let sel in selection)
       selectionCount++;
 
     assert.equal(selectionCount, 0,
       "No iterable selections");
 
-  }).then(close).then(loader.unload).then(done);
+  }).then(close).then(loader.unload).then(done, assert.fail);
 }
 
 
 exports["test No Selection Exception"] = function(assert, done) {
   const NO_SELECTION = /It isn't possible to change the selection/;
 
   let loader = Loader(module);
   let selection = loader.require("sdk/selection");
@@ -579,17 +618,17 @@ exports["test No Selection Exception"] =
     assert.throws(function() {
       selection.text = "bar";
     }, NO_SELECTION);
 
     assert.throws(function() {
       selection.html = "bar";
     }, NO_SELECTION);
 
-  }).then(close).then(loader.unload).then(done);
+  }).then(close).then(loader.unload).then(done, assert.fail);
 };
 
 exports["test for...of without selections"] = function(assert, done) {
   let loader = Loader(module);
   let selection = loader.require("sdk/selection");
 
   open(URL).then(function() {
     let selectionCount = 0;
@@ -602,17 +641,17 @@ exports["test for...of without selection
 
   }).then(close).then(loader.unload).then(null, function(error) {
     // iterable are not supported yet in Firefox 16, for example, but
     // they are in Firefox 17.
     if (error.message.indexOf("is not iterable") > -1)
       assert.pass("`iterable` method not supported in this application");
     else
       assert.fail(error);
-  }).then(done);
+  }).then(done, assert.fail);
 }
 
 exports["test for...of with selections"] = function(assert, done) {
   let loader = Loader(module);
   let selection = loader.require("sdk/selection");
 
   open(URL).then(selectAllDivs).then(function(){
     let expectedText = ["foo", "and"];
@@ -635,47 +674,47 @@ exports["test for...of with selections"]
 
   }).then(close).then(loader.unload).then(null, function(error) {
     // iterable are not supported yet in Firefox 16, for example, but
     // they are in Firefox 17.
     if (error.message.indexOf("is not iterable") > -1)
       assert.pass("`iterable` method not supported in this application");
     else
       assert.fail(error);
-  }).then(done)
+  }).then(done, assert.fail)
 }
 
 exports["test Selection Listener"] = function(assert, done) {
   let loader = Loader(module);
   let selection = loader.require("sdk/selection");
 
   selection.once("select", function() {
     assert.equal(selection.text, "fo");
     done();
   });
 
   open(URL).then(selectContentFirstDiv).
     then(dispatchSelectionEvent).
     then(close).
-    then(loader.unload);
+    then(loader.unload, assert.fail);
 };
 
 exports["test Textarea OnSelect Listener"] = function(assert, done) {
   let loader = Loader(module);
   let selection = loader.require("sdk/selection");
 
   selection.once("select", function() {
     assert.equal(selection.text, "noodles");
     done();
   });
 
   open(URL).then(selectTextarea).
     then(dispatchOnSelectEvent).
     then(close).
-    then(loader.unload);
+    then(loader.unload, assert.fail);
 };
 
 exports["test Selection listener removed on unload"] = function(assert, done) {
   let loader = Loader(module);
   let selection = loader.require("sdk/selection");
 
   selection.once("select", function() {
     assert.fail("Shouldn't be never called");
@@ -684,17 +723,17 @@ exports["test Selection listener removed
   loader.unload();
 
   assert.pass();
 
   open(URL).
     then(selectContentFirstDiv).
     then(dispatchSelectionEvent).
     then(close).
-    then(done)
+    then(done, assert.fail);
 };
 
 exports["test Textarea onSelect Listener removed on unload"] = function(assert, done) {
   let loader = Loader(module);
   let selection = loader.require("sdk/selection");
 
   selection.once("select", function() {
     assert.fail("Shouldn't be never called");
@@ -703,17 +742,17 @@ exports["test Textarea onSelect Listener
   loader.unload();
 
   assert.pass();
 
   open(URL).
     then(selectTextarea).
     then(dispatchOnSelectEvent).
     then(close).
-    then(done)
+    then(done, assert.fail);
 };
 
 
 exports["test Selection Listener on existing document"] = function(assert, done) {
   let loader = Loader(module);
 
   open(URL).then(function(window){
     let selection = loader.require("sdk/selection");
@@ -722,17 +761,17 @@ exports["test Selection Listener on exis
       assert.equal(selection.text, "fo");
       done();
     });
 
     return window;
   }).then(selectContentFirstDiv).
     then(dispatchSelectionEvent).
     then(close).
-    then(loader.unload)
+    then(loader.unload, assert.fail);
 };
 
 
 exports["test Textarea OnSelect Listener on existing document"] = function(assert, done) {
   let loader = Loader(module);
 
   open(URL).then(function(window){
     let selection = loader.require("sdk/selection");
@@ -741,51 +780,51 @@ exports["test Textarea OnSelect Listener
       assert.equal(selection.text, "noodles");
       done();
     });
 
     return window;
   }).then(selectTextarea).
     then(dispatchOnSelectEvent).
     then(close).
-    then(loader.unload)
+    then(loader.unload, assert.fail);
 };
 
 exports["test Selection Listener on document reload"] = function(assert, done) {
   let loader = Loader(module);
   let selection = loader.require("sdk/selection");
 
   selection.once("select", function() {
     assert.equal(selection.text, "fo");
     done();
   });
 
   open(URL).
     then(reload).
     then(selectContentFirstDiv).
     then(dispatchSelectionEvent).
     then(close).
-    then(loader.unload);
+    then(loader.unload, assert.fail);
 };
 
 exports["test Textarea OnSelect Listener on document reload"] = function(assert, done) {
   let loader = Loader(module);
   let selection = loader.require("sdk/selection");
 
   selection.once("select", function() {
     assert.equal(selection.text, "noodles");
     done();
   });
 
   open(URL).
     then(reload).
     then(selectTextarea).
     then(dispatchOnSelectEvent).
     then(close).
-    then(loader.unload);
+    then(loader.unload, assert.fail);
 };
 
 exports["test Selection Listener on frame"] = function(assert, done) {
   let loader = Loader(module);
   let selection = loader.require("sdk/selection");
 
   selection.once("select", function() {
     assert.equal(selection.text, "fo");
@@ -793,17 +832,17 @@ exports["test Selection Listener on fram
   });
 
   open(FRAME_URL).
     then(hideAndShowFrame).
     then(getFrameWindow).
     then(selectContentFirstDiv).
     then(dispatchSelectionEvent).
     then(close).
-    then(loader.unload)
+    then(loader.unload, assert.fail);
 };
 
 exports["test Textarea onSelect Listener on frame"] = function(assert, done) {
   let loader = Loader(module);
   let selection = loader.require("sdk/selection");
 
   selection.once("select", function() {
     assert.equal(selection.text, "noodles");
@@ -811,17 +850,114 @@ exports["test Textarea onSelect Listener
   });
 
   open(FRAME_URL).
     then(hideAndShowFrame).
     then(getFrameWindow).
     then(selectTextarea).
     then(dispatchOnSelectEvent).
     then(close).
-    then(loader.unload)
+    then(loader.unload, assert.fail);
+};
+
+
+exports["test PBPW Selection Listener"] = function(assert, done) {
+  let loader = Loader(module);
+  let selection = loader.require("sdk/selection");
+
+  selection.once("select", function() {
+    assert.fail("Shouldn't be never called");
+  });
+
+  assert.pass();
+
+  open(URL, {private: true}).
+    then(selectContentFirstDiv).
+    then(dispatchSelectionEvent).
+    then(close).
+    then(loader.unload).
+    then(done, assert.fail);
+};
+
+exports["test PBPW Textarea OnSelect Listener"] = function(assert, done) {
+  let loader = Loader(module);
+  let selection = loader.require("sdk/selection");
+
+  selection.once("select", function() {
+    assert.fail("Shouldn't be never called");
+  });
+
+  assert.pass();
+
+  open(URL, {private: true}).
+    then(selectTextarea).
+    then(dispatchOnSelectEvent).
+    then(close).
+    then(loader.unload).
+    then(done, assert.fail);
+};
+
+
+exports["test PBPW Single DOM Selection"] = function(assert, done) {
+  let loader = Loader(module);
+  let selection = loader.require("sdk/selection");
+
+  open(URL, {private: true}).then(selectFirstDiv).then(function(window) {
+
+    assert.equal(selection.isContiguous, false,
+      "selection.isContiguous with single DOM Selection in PBPW works.");
+
+    assert.equal(selection.text, null,
+      "selection.text with single DOM Selection in PBPW works.");
+
+    assert.equal(selection.html, null,
+      "selection.html with single DOM Selection in PBPW works.");
+
+    let selectionCount = 0;
+    for each (let sel in selection)
+      selectionCount++;
+
+    assert.equal(selectionCount, 0,
+      "No iterable selection in PBPW");
+
+    return window;
+  }).then(close).then(loader.unload).then(done, assert.fail);
+};
+
+exports["test PBPW Textarea Selection"] = function(assert, done) {
+  let loader = Loader(module);
+  let selection = loader.require("sdk/selection");
+
+  open(URL, {private: true}).then(selectTextarea).then(function(window) {
+
+    assert.equal(selection.isContiguous, false,
+      "selection.isContiguous with Textarea Selection in PBPW works.");
+
+    assert.equal(selection.text, null,
+      "selection.text with Textarea Selection in PBPW works.");
+
+    assert.strictEqual(selection.html, null,
+      "selection.html with Textarea Selection in PBPW works.");
+
+    let selectionCount = 0;
+    for each (let sel in selection) {
+      selectionCount++;
+
+      assert.equal(sel.text, null,
+        "iterable selection.text with Textarea Selection in PBPW works.");
+
+      assert.strictEqual(sel.html, null,
+        "iterable selection.html with Textarea Selection in PBPW works.");
+    }
+
+    assert.equal(selectionCount, 0,
+      "No iterable selection in PBPW");
+
+    return window;
+  }).then(close).then(loader.unload).then(done, assert.fail);
 };
 
 // TODO: test Selection Listener on long-held connection (Bug 661884)
 //
 //  I didn't find a way to do so with httpd, using `processAsync` I'm able to
 //  Keep the connection but not to flush the buffer to the client in two steps,
 //  that is what I need for this test (e.g. flush "Hello" to the client, makes
 //  selection when the connection is still hold, and check that the listener
@@ -830,16 +966,27 @@ exports["test Textarea onSelect Listener
 //  Because this test is needed to the refactoring of context-menu as well, I
 //  believe we will find a proper solution quickly.
 /*
 exports["test Selection Listener on long-held connection"] = function(assert, done) {
 
 };
 */
 
+// If the platform doesn't support the PBPW, we're replacing PBPW tests
+if (!require("sdk/private-browsing/utils").isWindowPBSupported) {
+  Object.keys(module.exports).forEach(function(key) {
+    if (key.indexOf("test PBPW") === 0) {
+      module.exports[key] = function Unsupported (assert) {
+        assert.pass("Private Window Per Browsing is not supported on this platform.");
+      }
+    }
+  });
+}
+
 // If the module doesn't support the app we're being run in, require() will
 // throw.  In that case, remove all tests above from exports, and add one dummy
 // test that passes.
 try {
   require("sdk/selection");
 }
 catch (err) {
   if (!/^Unsupported Application/.test(err.message))
--- a/addon-sdk/source/test/test-tabs-common.js
+++ b/addon-sdk/source/test/test-tabs-common.js
@@ -1,20 +1,21 @@
 /* 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 { 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 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({
@@ -347,8 +348,42 @@ exports.testPrivateAreNotListed = functi
 
     win.addEventListener("unload", function onunload() {
       win.removeEventListener('unload', onunload);
       test.done();
     });
     win.close();
   });
 }
+
+// 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) {
+  test.waitUntilDone();
+  let { loader, messages } = LoaderWithHookedConsole(module, onMessage);
+  let concurrentTabs = loader.require("sdk/tabs");
+  concurrentTabs.on("open", function () {
+    test.fail("Concurrent loader manager receive a tabs `open` event");
+    // It shouldn't receive such event as the other loader will just open
+    // and destroy the tab without giving a change to other loader to even know
+    // about the existance of this tab.
+  });
+  function onMessage(type, msg) {
+    test.fail("Unexpected mesage on concurrent loader: " + msg);
+  }
+
+  tabs.open({
+    url: 'about:blank',
+    onOpen: function(tab) {
+      tab.close(function () {
+        test.pass("Tab succesfully removed");
+        // Let a chance to the concurrent loader to receive a TabOpen event
+        // on the next event loop turn
+        setTimeout(function () {
+          loader.unload();
+          test.done();
+        }, 0);
+      });
+    }
+  });
+}