Bug 880558 - Uplift addon-sdk to firefox r=me
authorWes Kocher <wkocher@mozilla.com>
Fri, 07 Jun 2013 09:43:59 -0700
changeset 145879 b57750ca97bfc326812d257f71e29a7017dad7a8
parent 145878 cab2b2d81f3c270839f48662671504531bd0366f
child 145880 eebeea69662ce56b58b272a9a1d721edeb99cd7d
child 145888 498f79c24e709600cacb28a295ba034d34d190b2
push id2697
push userbbajaj@mozilla.com
push dateMon, 05 Aug 2013 18:49:53 +0000
treeherdermozilla-beta@dfec938c7b63 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersme
bugs880558
milestone24.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 880558 - Uplift addon-sdk to firefox r=me
addon-sdk/source/lib/sdk/addon-page.js
addon-sdk/source/lib/sdk/content/content-proxy.js
addon-sdk/source/lib/sdk/content/events.js
addon-sdk/source/lib/sdk/content/worker.js
addon-sdk/source/lib/sdk/deprecated/api-utils.js
addon-sdk/source/lib/sdk/deprecated/list.js
addon-sdk/source/lib/sdk/event/chrome.js
addon-sdk/source/lib/sdk/lang/functional.js
addon-sdk/source/lib/sdk/net/xhr.js
addon-sdk/source/lib/sdk/request.js
addon-sdk/source/lib/sdk/tabs/tab-firefox.js
addon-sdk/source/lib/sdk/tabs/utils.js
addon-sdk/source/lib/sdk/test/httpd.js
addon-sdk/source/lib/sdk/util/list.js
addon-sdk/source/lib/sdk/window/events.js
addon-sdk/source/lib/sdk/window/utils.js
addon-sdk/source/lib/sdk/windows/loader.js
addon-sdk/source/python-lib/cuddlefish/packaging.py
addon-sdk/source/test/addons/content-permissions/main.js
addon-sdk/source/test/addons/content-permissions/package.json
addon-sdk/source/test/private-browsing/windows.js
addon-sdk/source/test/tabs/test-firefox-tabs.js
addon-sdk/source/test/test-addon-page.js
addon-sdk/source/test/test-api-utils.js
addon-sdk/source/test/test-content-events.js
addon-sdk/source/test/test-content-proxy.js
addon-sdk/source/test/test-content-script.js
addon-sdk/source/test/test-deprecated-list.js
addon-sdk/source/test/test-functional.js
addon-sdk/source/test/test-list.js
addon-sdk/source/test/test-page-mod.js
addon-sdk/source/test/test-xhr.js
--- a/addon-sdk/source/lib/sdk/addon-page.js
+++ b/addon-sdk/source/lib/sdk/addon-page.js
@@ -1,28 +1,34 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 'use strict';
 
 module.metadata = {
-  'stability': 'experimental'
+  'stability': 'deprecated'
 };
 
 const { WindowTracker } = require('./deprecated/window-utils');
 const { isXULBrowser } = require('./window/utils');
 const { add, remove } = require('./util/array');
 const { getTabs, closeTab, getURI } = require('./tabs/utils');
 const { data } = require('./self');
 const { ns } = require("./core/namespace");
 
 const addonURL = data.url('index.html');
 
 const windows = ns();
 
+require("./util/deprecate").deprecateUsage(
+  "The addon-page module is deprecated." +
+  "In the new Firefox UI design all pages will include navigational elements;" +
+  "once the new design ships, using the addon-page module will not have any effect."
+);
+
 WindowTracker({
   onTrack: function onTrack(window) {
     if (!isXULBrowser(window) || windows(window).hideChromeForLocation)
       return;
 
     let { XULBrowserWindow } = window;
     let { hideChromeForLocation } = XULBrowserWindow;
 
deleted file mode 100644
--- a/addon-sdk/source/lib/sdk/content/content-proxy.js
+++ /dev/null
@@ -1,870 +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";
-
-/* Trick the linker in order to avoid error on `Components.interfaces` usage.
-   We are tricking the linker with `require('./content-proxy.js')` from
-   worjer.js in order to ensure shipping this file! But then the linker think
-   that this file is going to be used as a CommonJS module where we forbid usage
-   of `Components`.
-*/
-let Ci = Components['interfaces'];
-
-/**
- * Access key that allows privileged code to unwrap proxy wrappers through 
- * valueOf:
- *   let xpcWrapper = proxyWrapper.valueOf(UNWRAP_ACCESS_KEY);
- * This key should only be used by proxy unit test.
- */
- const UNWRAP_ACCESS_KEY = {};
-
-
- /**
- * Returns a closure that wraps arguments before calling the given function,
- * which can be given to native functions that accept a function, such that when
- * the closure is called, the given function is called with wrapped arguments.
- *
- * @param fun {Function}
- *        the function for which to create a closure wrapping its arguments
- * @param obj {Object}
- *        target object from which `fun` comes from
- *        (optional, for debugging purpose)
- * @param name {String}
- *        name of the attribute from which `fun` is binded on `obj`
- *        (optional, for debugging purpose)
- *
- * Example:
- *   function contentScriptListener(event) {}
- *   let wrapper = ContentScriptFunctionWrapper(contentScriptListener);
- *   xray.addEventListener("...", wrapper, false);
- * -> Allow to `event` to be wrapped
- */
-function ContentScriptFunctionWrapper(fun, obj, name) {
-  if ("___proxy" in fun && typeof fun.___proxy == "function")
-    return fun.___proxy;
-  
-  let wrappedFun = function () {
-    let args = [];
-    for (let i = 0, l = arguments.length; i < l; i++)
-      args.push(wrap(arguments[i]));
-    
-    //console.log("Called from native :"+obj+"."+name);
-    //console.log(">args "+arguments.length);
-    //console.log(fun);
-    
-    // Native code can execute this callback with `this` being the wrapped 
-    // function. For example, window.mozRequestAnimationFrame.
-    if (this == wrappedFun)
-      return fun.apply(fun, args);
-    
-    return fun.apply(wrap(this), args);
-  };
-  
-  Object.defineProperty(fun, "___proxy", {value : wrappedFun,
-                                          writable : false,
-                                          enumerable : false,
-                                          configurable : false});
-  
-  return wrappedFun;
-}
-
-/**
- * Returns a closure that unwraps arguments before calling the `fun` function,
- * which can be used to build a wrapper for a native function that accepts
- * wrapped arguments, since native function only accept unwrapped arguments.
- *
- * @param fun {Function}
- *        the function to wrap
- * @param originalObject {Object}
- *        target object from which `fun` comes from
- *        (optional, for debugging purpose)
- * @param name {String}
- *        name of the attribute from which `fun` is binded on `originalObject`
- *        (optional, for debugging purpose)
- *
- * Example:
- *   wrapper.appendChild = NativeFunctionWrapper(xray.appendChild, xray);
- *   wrapper.appendChild(anotherWrapper);
- * -> Allow to call xray.appendChild with unwrapped version of anotherWrapper
- */
-function NativeFunctionWrapper(fun, originalObject, name) {
-  return function () {
-    let args = [];
-    let obj = this && typeof this.valueOf == "function" ?
-              this.valueOf(UNWRAP_ACCESS_KEY) : this;
-
-    for (let i = 0, l = arguments.length; i < l; i++)
-      args.push( unwrap(arguments[i], obj, name) );
-    
-    //if (name != "toString")
-    //console.log(">>calling native ["+(name?name:'#closure#')+"]: \n"+fun.apply+"\n"+obj+"\n("+args.join(', ')+")\nthis :"+obj+"from:"+originalObject+"\n");
-    
-    // Need to use Function.prototype.apply.apply because XMLHttpRequest 
-    // is a function (typeof return 'function') and fun.apply is null :/
-    let unwrapResult = Function.prototype.apply.apply(fun, [obj, args]);
-    let result = wrap(unwrapResult, obj, name);
-    
-    //console.log("<< "+rr+" -> "+r);
-    
-    return result;
-  };
-}
-
-/*
- * Unwrap a JS value that comes from the content script.
- * Mainly converts proxy wrapper to XPCNativeWrapper.
- */
-function unwrap(value, obj, name) {
-  //console.log("unwrap : "+value+" ("+name+")");
-  if (!value)
-    return value;
-  let type = typeof value;  
-  
-  // In case of proxy, unwrap them recursively 
-  // (it should not be recursive, just in case of)
-  if (["object", "function"].indexOf(type) !== -1 && 
-      "__isWrappedProxy" in value) {
-    while("__isWrappedProxy" in value)
-      value = value.valueOf(UNWRAP_ACCESS_KEY);
-    return value;
-  }
-  
-  // In case of functions we need to return a wrapper that converts native 
-  // arguments applied to this function into proxies.
-  if (type == "function")
-    return ContentScriptFunctionWrapper(value, obj, name);
-  
-  // We must wrap objects coming from content script too, as they may have
-  // a function that will be called by a native method.
-  // For example:
-  //   addEventListener(..., { handleEvent: function(event) {} }, ...);
-  if (type == "object")
-    return ContentScriptObjectWrapper(value);
-
-  if (["string", "number", "boolean"].indexOf(type) !== -1)
-    return value;
-  //console.log("return non-wrapped to native : "+typeof value+" -- "+value);
-  return value;
-}
-
-/**
- * Returns an XrayWrapper proxy object that allow to wrap any of its function
- * though `ContentScriptFunctionWrapper`. These proxies are given to
- * XrayWrappers in order to automatically wrap values when they call a method
- * of these proxies. So that they are only used internaly and content script,
- * nor web page have ever access to them. As a conclusion, we can consider
- * this code as being safe regarding web pages overload.
- *
- *
- * @param obj {Object}
- *        object coming from content script context to wrap
- *
- * Example:
- *   let myListener = { handleEvent: function (event) {} };
- *   node.addEventListener("click", myListener, false);
- *   `event` has to be wrapped, so handleEvent has to be wrapped using
- *   `ContentScriptFunctionWrapper` function.
- *   In order to do so, we build this new kind of proxies.
- */
-function ContentScriptObjectWrapper(obj) {
-  if ("___proxy" in obj && typeof obj.___proxy == "object")
-    return obj.___proxy;
-
-  function valueOf(key) {
-    if (key === UNWRAP_ACCESS_KEY)
-      return obj;
-    return this;
-  }
-
-  let proxy = Proxy.create({
-    // Fundamental traps
-    getPropertyDescriptor:  function(name) {
-      return Object.getOwnPropertyDescriptor(obj, name);
-    },
-    defineProperty: function(name, desc) {
-      return Object.defineProperty(obj, name, desc);
-    },
-    getOwnPropertyNames: function () {
-      return Object.getOwnPropertyNames(obj);
-    },
-    delete: function(name) {
-      return delete obj[name];
-    },
-    // derived traps
-    has: function(name) {
-      return name === "__isXrayWrapperProxy" ||
-             name in obj;
-    },
-    hasOwn: function(name) {
-      return Object.prototype.hasOwnProperty.call(obj, name);
-    },
-    get: function(receiver, name) {
-      if (name == "valueOf")
-        return valueOf;
-      let value = obj[name];
-      if (!value)
-        return value;
-
-      return unwrap(value);
-    },
-    set: function(receiver, name, val) {
-      obj[name] = val;
-      return true;
-    },
-    enumerate: function() {
-      var result = [];
-      for each (let name in obj) {
-        result.push(name);
-      };
-      return result;
-    },
-    keys: function() {
-      return Object.keys(obj);
-    }
-  });
-
-  Object.defineProperty(obj, "___proxy", {value : proxy,
-                                          writable : false,
-                                          enumerable : false,
-                                          configurable : false});
-
-  return proxy;
-}
-
-// List of all existing typed arrays.
-//   Can be found here:
-//   http://mxr.mozilla.org/mozilla-central/source/js/src/jsapi.cpp#1790
-const typedArraysCtor = [
-  ArrayBuffer,
-  Int8Array,
-  Uint8Array,
-  Int16Array,
-  Uint16Array,
-  Int32Array,
-  Uint32Array,
-  Float32Array,
-  Float64Array,
-  Uint8ClampedArray
-];
-
-/*
- * Wrap a JS value coming from the document by building a proxy wrapper.
- */
-function wrap(value, obj, name, debug) {
-  if (!value)
-    return value;
-  let type = typeof value;
-  if (type == "object") {
-    // Bug 671016: Typed arrays don't need to be proxified.
-    // We avoid checking the whole constructor list on all objects
-    // by doing this check only on non-extensible objects:
-    if (!Object.isExtensible(value) &&
-        typedArraysCtor.indexOf(value.constructor) !== -1)
-      return value;
-
-    // Bug 715755: do not proxify COW wrappers
-    // These wrappers throw an exception when trying to access
-    // any attribute that is not in a white list
-    try {
-      ("nonExistantAttribute" in value);
-    }
-    catch(e) {
-      if (e.message.indexOf("Permission denied to access property") !== -1)
-        return value;
-    }
-
-    // We may have a XrayWrapper proxy.
-    // For example:
-    //   let myListener = { handleEvent: function () {} };
-    //   node.addEventListener("click", myListener, false);
-    // When native code want to call handleEvent,
-    // we go though ContentScriptFunctionWrapper that calls `wrap(this)`
-    // `this` is the XrayWrapper proxy of myListener.
-    // We return this object without building a CS proxy as it is already
-    // a value coming from the CS.
-    if ("__isXrayWrapperProxy" in value)
-      return value.valueOf(UNWRAP_ACCESS_KEY);
-
-    // Unwrap object before wrapping it.
-    // It should not happen with CS proxy objects.
-    while("__isWrappedProxy" in value) {
-      value = value.valueOf(UNWRAP_ACCESS_KEY);
-    }
-
-    if (XPCNativeWrapper.unwrap(value) !== value)
-      return getProxyForObject(value);
-    // In case of Event, HTMLCollection or NodeList or ???
-    // XPCNativeWrapper.unwrap(value) === value
-    // but it's still a XrayWrapper so let's build a proxy
-    return getProxyForObject(value);
-  }
-  if (type == "function") {
-    if (XPCNativeWrapper.unwrap(value) !== value
-        || (typeof value.toString === "function" && 
-            value.toString().match(/\[native code\]/))) {
-      return getProxyForFunction(value, NativeFunctionWrapper(value, obj, name));
-    }
-    return value;
-  }
-  if (type == "string")
-    return value;
-  if (type == "number")
-    return value;
-  if (type == "boolean")
-    return value;
-  //console.log("return non-wrapped to wrapped : "+value);
-  return value;
-}
-
-/* 
- * Wrap an object from the document to a proxy wrapper
- */
-function getProxyForObject(obj) {
-  if (typeof obj != "object") {
-    let msg = "tried to proxify something other than an object: " + typeof obj;
-    console.warn(msg);
-    throw msg;
-  }
-  if ("__isWrappedProxy" in obj) {
-    return obj;
-  }
-  // Check if there is a proxy cached on this wrapper,
-  // but take care of prototype ___proxy attribute inheritance!
-  if (obj && obj.___proxy && obj.___proxy.valueOf(UNWRAP_ACCESS_KEY) === obj) {
-    return obj.___proxy;
-  }
-  
-  let proxy = Proxy.create(handlerMaker(obj));
-  
-  Object.defineProperty(obj, "___proxy", {value : proxy,
-                                          writable : false,
-                                          enumerable : false,
-                                          configurable : false});
-  return proxy;
-}
-
-/* 
- * Wrap a function from the document to a proxy wrapper
- */
-function getProxyForFunction(fun, callTrap) {
-  if (typeof fun != "function") {
-    let msg = "tried to proxify something other than a function: " + typeof fun;
-    console.warn(msg);
-    throw msg;
-  }
-  if ("__isWrappedProxy" in fun)
-    return obj;
-  if ("___proxy" in fun)
-    return fun.___proxy;
-  
-  let proxy = Proxy.createFunction(handlerMaker(fun), callTrap);
-  
-  Object.defineProperty(fun, "___proxy", {value : proxy,
-                                          writable : false,
-                                          enumerable : false,
-                                          configurable : false});
-  
-  return proxy;
-}
-
-/* 
- * Check if a DOM attribute name is an event name.
- */
-function isEventName(id) {
-  if (id.indexOf("on") != 0 || id.length == 2) 
-    return false;
-  // Taken from:
-  // http://mxr.mozilla.org/mozilla-central/source/dom/base/nsDOMClassInfo.cpp#7616
-  switch (id[2]) {
-    case 'a' :
-      return (id == "onabort" ||
-              id == "onafterscriptexecute" ||
-              id == "onafterprint");
-    case 'b' :
-      return (id == "onbeforeunload" ||
-              id == "onbeforescriptexecute" ||
-              id == "onblur" ||
-              id == "onbeforeprint");
-    case 'c' :
-      return (id == "onchange"       ||
-              id == "onclick"        ||
-              id == "oncontextmenu"  ||
-              id == "oncopy"         ||
-              id == "oncut"          ||
-              id == "oncanplay"      ||
-              id == "oncanplaythrough");
-    case 'd' :
-      return (id == "ondblclick"     || 
-              id == "ondrag"         ||
-              id == "ondragend"      ||
-              id == "ondragenter"    ||
-              id == "ondragleave"    ||
-              id == "ondragover"     ||
-              id == "ondragstart"    ||
-              id == "ondrop"         ||
-              id == "ondurationchange");
-    case 'e' :
-      return (id == "onerror" ||
-              id == "onemptied" ||
-              id == "onended");
-    case 'f' :
-      return id == "onfocus";
-    case 'h' :
-      return id == "onhashchange";
-    case 'i' :
-      return (id == "oninput" ||
-              id == "oninvalid");
-    case 'k' :
-      return (id == "onkeydown"      ||
-              id == "onkeypress"     ||
-              id == "onkeyup");
-    case 'l' :
-      return (id == "onload"           ||
-              id == "onloadeddata"     ||
-              id == "onloadedmetadata" ||
-              id == "onloadstart");
-    case 'm' :
-      return (id == "onmousemove"    ||
-              id == "onmouseout"     ||
-              id == "onmouseover"    ||
-              id == "onmouseup"      ||
-              id == "onmousedown"    ||
-              id == "onmessage");
-    case 'p' :
-      return (id == "onpaint"        ||
-              id == "onpageshow"     ||
-              id == "onpagehide"     ||
-              id == "onpaste"        ||
-              id == "onpopstate"     ||
-              id == "onpause"        ||
-              id == "onplay"         ||
-              id == "onplaying"      ||
-              id == "onprogress");
-    case 'r' :
-      return (id == "onreadystatechange" ||
-              id == "onreset"            ||
-              id == "onresize"           ||
-              id == "onratechange");
-    case 's' :
-      return (id == "onscroll"       ||
-              id == "onselect"       ||
-              id == "onsubmit"       || 
-              id == "onseeked"       ||
-              id == "onseeking"      ||
-              id == "onstalled"      ||
-              id == "onsuspend");
-    case 't':
-      return id == "ontimeupdate" 
-      /* 
-        // TODO: Make it work for mobile version
-        ||
-        (nsDOMTouchEvent::PrefEnabled() &&
-         (id == "ontouchstart" ||
-          id == "ontouchend" ||
-          id == "ontouchmove" ||
-          id == "ontouchenter" ||
-          id == "ontouchleave" ||
-          id == "ontouchcancel"))*/;
-      
-    case 'u' :
-      return id == "onunload";
-    case 'v':
-      return id == "onvolumechange";
-    case 'w':
-      return id == "onwaiting";
-    }
-  
-  return false;
-}
-
-// XrayWrappers miss some attributes.
-// Here is a list of functions that return a value when it detects a miss:
-const NODES_INDEXED_BY_NAME = ["IMG", "FORM", "APPLET", "EMBED", "OBJECT"];
-const xRayWrappersMissFixes = [
-
-  // Fix bug with XPCNativeWrapper on HTMLCollection
-  // We can only access array item once, then it's undefined :o
-  function (obj, name) {
-    let i = parseInt(name);
-    if (obj.toString().match(/HTMLCollection|NodeList/) && 
-        i >= 0 && i < obj.length) {
-      return wrap(XPCNativeWrapper(obj.wrappedJSObject[name]), obj, name);
-    }
-    return null;
-  },
-
-  // Trap access to document["form name"] 
-  // that may refer to an existing form node
-  // http://mxr.mozilla.org/mozilla-central/source/dom/base/nsDOMClassInfo.cpp#9285
-  function (obj, name) {
-    if ("nodeType" in obj && obj.nodeType == 9) {
-      let node = obj.wrappedJSObject[name];
-      // List of supported tag:
-      // http://mxr.mozilla.org/mozilla-central/source/content/html/content/src/nsGenericHTMLElement.cpp#1267
-      if (node && NODES_INDEXED_BY_NAME.indexOf(node.tagName) != -1)
-        return wrap(XPCNativeWrapper(node));
-    }
-    return null;
-  },
-
-  // Trap access to window["frame name"] and window.frames[i]
-  // that refer to an (i)frame internal window object
-  // http://mxr.mozilla.org/mozilla-central/source/dom/base/nsDOMClassInfo.cpp#6824
-  function (obj, name) {
-    if (typeof obj == "object" && "document" in obj) {
-      // Ensure that we are on a window object
-      try {
-        obj.QueryInterface(Ci.nsIDOMWindow);
-      }
-      catch(e) {
-        return null;
-      }
-
-      // Integer case:
-      let i = parseInt(name);
-      if (i >= 0 && i in obj) {
-        return wrap(XPCNativeWrapper(obj[i]));
-      }
-
-      // String name case:
-      if (name in obj.wrappedJSObject) {
-        let win = obj.wrappedJSObject[name];
-        let nodes = obj.document.getElementsByName(name);
-        for (let i = 0, l = nodes.length; i < l; i++) {
-          let node = nodes[i];
-          if ("contentWindow" in node && node.contentWindow == win)
-            return wrap(node.contentWindow);
-        }
-      }
-    }
-    return null;
-  },
-
-  // Trap access to form["node name"]
-  // http://mxr.mozilla.org/mozilla-central/source/dom/base/nsDOMClassInfo.cpp#9477
-  function (obj, name) {
-    if (typeof obj == "object" && "tagName" in obj && obj.tagName == "FORM") {
-      let match = obj.wrappedJSObject[name];
-      let nodes = obj.ownerDocument.getElementsByName(name);
-      for (let i = 0, l = nodes.length; i < l; i++) {
-        let node = nodes[i];
-        if (node == match)
-          return wrap(node);
-      }
-    }
-    return null;
-  }
-
-];
-
-// XrayWrappers have some buggy methods.
-// Here is the list of them with functions returning some replacement 
-// for a given object `obj`:
-const xRayWrappersMethodsFixes = {
-  // postMessage method is checking the Javascript global
-  // and it expects it to be a window object.
-  // But in our case, the global object is our sandbox global object.
-  // See nsGlobalWindow::CallerInnerWindow():
-  // http://mxr.mozilla.org/mozilla-central/source/dom/base/nsGlobalWindow.cpp#5893
-  // nsCOMPtr<nsPIDOMWindow> win = do_QueryWrappedNative(wrapper);
-  //   win is null
-  postMessage: function (obj) {
-    // Ensure that we are on a window object
-    try {
-      obj.QueryInterface(Ci.nsIDOMWindow);
-    }
-    catch(e) {
-      return null;
-    }
-
-    // Create a wrapper that is going to call `postMessage` through `eval`
-    let f = function postMessage(message, targetOrigin) {
-      let jscode = "this.postMessage(";
-      if (typeof message != "string")
-        jscode += JSON.stringify(message);
-      else
-        jscode += "'" + message.toString().replace(/['\\]/g,"\\$&") + "'";
-
-      targetOrigin = targetOrigin.toString().replace(/['\\]/g,"\\$&");
-
-      jscode += ", '" + targetOrigin + "')";
-      return this.wrappedJSObject.eval(jscode);
-    };
-    return getProxyForFunction(f, NativeFunctionWrapper(f));
-  },
-  
-  // Fix mozMatchesSelector uses that is broken on XrayWrappers
-  // when we use document.documentElement.mozMatchesSelector.call(node, expr)
-  // It's only working if we call mozMatchesSelector on the node itself.
-  // SEE BUG 658909: mozMatchesSelector returns incorrect results with XrayWrappers
-  mozMatchesSelector: function (obj) {
-    // Ensure that we are on an object to expose this buggy method
-    try {
-      // Bug 707576 removed nsIDOMNSElement.
-      // Can be simplified as soon as Firefox 11 become the minversion
-      obj.QueryInterface("nsIDOMElement" in Ci ? Ci.nsIDOMElement :
-                                                 Ci.nsIDOMNSElement);
-    }
-    catch(e) {
-      return null;
-    }
-    // We can't use `wrap` function as `f` is not a native function,
-    // so wrap it manually:
-    let f = function mozMatchesSelector(selectors) {
-      return this.mozMatchesSelector(selectors);
-    };
-
-    return getProxyForFunction(f, NativeFunctionWrapper(f));
-  },
-
-  // Bug 679054: History API doesn't work with Proxy objects. We have to pass
-  // regular JS objects on `pushState` and `replaceState` methods.
-  // In addition, the first argument has to come from the same compartment.
-  pushState: function (obj) {
-    // Ensure that we are on an object that expose History API
-    try {
-      obj.QueryInterface(Ci.nsIDOMHistory);
-    }
-    catch(e) {
-      return null;
-    }
-    let f = function fix() {
-      // Call native method with JSON objects
-      // (need to convert `arguments` to an array via `slice`)
-      return this.pushState.apply(this, JSON.parse(JSON.stringify(Array.slice(arguments))));
-    };
-
-    return getProxyForFunction(f, NativeFunctionWrapper(f));
-  },
-  replaceState: function (obj) {
-    // Ensure that we are on an object that expose History API
-    try {
-      obj.QueryInterface(Ci.nsIDOMHistory);
-    }
-    catch(e) {
-      return null;
-    }
-    let f = function fix() {
-      // Call native method with JSON objects
-      // (need to convert `arguments` to an array via `slice`)
-      return this.replaceState.apply(this, JSON.parse(JSON.stringify(Array.slice(arguments))));
-    };
-
-    return getProxyForFunction(f, NativeFunctionWrapper(f));
-  },
-
-  // Bug 769006: nsIDOMMutationObserver.observe fails with proxy as options
-  // attributes
-  observe: function observe(obj) {
-    // Ensure that we are on a DOMMutation object
-    try {
-      // nsIDOMMutationObserver starts with FF14
-      if ("nsIDOMMutationObserver" in Ci)
-        obj.QueryInterface(Ci.nsIDOMMutationObserver);
-      else
-        return null;
-    }
-    catch(e) {
-      return null;
-    }
-    return function nsIDOMMutationObserverObserveFix(target, options) {
-      // Gets native/unwrapped this
-      let self = this && typeof this.valueOf == "function" ?
-                 this.valueOf(UNWRAP_ACCESS_KEY) : this;
-      // Unwrap the xraywrapper target out of JS proxy
-      let targetXray = unwrap(target);
-      // But do not wrap `options` through ContentScriptObjectWrapper
-      let result = wrap(self.observe(targetXray, options));
-      // Finally wrap result into JS proxies
-      return wrap(result);
-    };
-  }
-};
-
-/* 
- * Generate handler for proxy wrapper
- */
-function handlerMaker(obj) {
-  // Overloaded attributes dictionary
-  let overload = {};
-  // Expando attributes dictionary (i.e. onclick, onfocus, on* ...)
-  let expando = {};
-  // Cache of methods overloaded to fix XrayWrapper bug
-  let methodFixes = {};
-  return {
-    // Fundamental traps
-    getPropertyDescriptor:  function(name) {
-      return Object.getOwnPropertyDescriptor(obj, name);
-    },
-    defineProperty: function(name, desc) {
-      return Object.defineProperty(obj, name, desc);
-    },
-    getOwnPropertyNames: function () {
-      return Object.getOwnPropertyNames(obj);
-    },
-    delete: function(name) {
-      delete expando[name];
-      delete overload[name];
-      return delete obj[name];
-    },
-    
-    // derived traps
-    has: function(name) {
-      if (name == "___proxy") return false;
-      if (isEventName(name)) {
-        // XrayWrappers throw exception when we try to access expando attributes
-        // even on "name in wrapper". So avoid doing it!
-        return name in expando;
-      }
-      return name in obj || name in overload || name == "__isWrappedProxy" ||
-             undefined !== this.get(null, name);
-    },
-    hasOwn: function(name) {
-      return Object.prototype.hasOwnProperty.call(obj, name);
-    },
-    get: function(receiver, name) {
-      if (name == "___proxy")
-        return undefined;
-      
-      // Overload toString in order to avoid returning "[XrayWrapper [object HTMLElement]]"
-      // or "[object Function]" for function's Proxy
-      if (name == "toString") {
-        // Bug 714778: we should not pass obj.wrappedJSObject.toString
-        // in order to avoid sharing its proxy between two contents scripts.
-        // (not that `unwrappedObj` can be equal to `obj` when `obj` isn't
-        // an xraywrapper)
-        let unwrappedObj = XPCNativeWrapper.unwrap(obj);
-        return wrap(function () {
-          return unwrappedObj.toString.call(
-                   this.valueOf(UNWRAP_ACCESS_KEY), arguments);
-        }, obj, name);
-      }
-
-      // Offer a way to retrieve XrayWrapper from a proxified node through `valueOf`
-      if (name == "valueOf")
-        return function (key) {
-          if (key === UNWRAP_ACCESS_KEY)
-            return obj;
-          return this;
-        };
-      
-      // Return overloaded value if there is one.
-      // It allows to overload native methods like addEventListener that
-      // are not saved, even on the wrapper itself.
-      // (And avoid some methods like toSource from being returned here! [__proto__ test])
-      if (name in overload &&
-          overload[name] != Object.getPrototypeOf(overload)[name] &&
-          name != "__proto__") {
-        return overload[name];
-      }
-      
-      // Catch exceptions thrown by XrayWrappers when we try to access on* 
-      // attributes like onclick, onfocus, ...
-      if (isEventName(name)) {
-        //console.log("expando:"+obj+" - "+obj.nodeType);
-        return name in expando ? expando[name].original : undefined;
-      }
-      
-      // Overload some XrayWrappers method in order to fix its bugs
-      if (name in methodFixes && 
-          methodFixes[name] != Object.getPrototypeOf(methodFixes)[name] &&
-          name != "__proto__")
-        return methodFixes[name];
-      if (Object.keys(xRayWrappersMethodsFixes).indexOf(name) !== -1) {
-        let fix = xRayWrappersMethodsFixes[name](obj);
-        if (fix)
-          return methodFixes[name] = fix;
-      }
-      
-      let o = obj[name];
-      
-      // XrayWrapper miss some attributes, try to catch these and return a value
-      if (!o) {
-        for each(let atttributeFixer in xRayWrappersMissFixes) {
-          let fix = atttributeFixer(obj, name);
-          if (fix)
-            return fix;
-        }
-      }
-
-      // Generic case
-      return wrap(o, obj, name);
-      
-    },
-    
-    set: function(receiver, name, val) {
-
-      if (isEventName(name)) {
-        //console.log("SET on* attribute : " + name + " / " + val + "/" + obj);
-        let shortName = name.replace(/^on/,"");
-        
-        // Unregister previously set listener
-        if (expando[name]) {
-          obj.removeEventListener(shortName, expando[name], true);
-          delete expando[name];
-        }
-        
-        // Only accept functions
-        if (typeof val != "function")
-          return false;
-        
-        // Register a new listener
-        let original = val;
-        val = ContentScriptFunctionWrapper(val);
-        expando[name] = val;
-        val.original = original;
-        obj.addEventListener(name.replace(/^on/, ""), val, true);
-        return true;
-      }
-      
-      obj[name] = val;
-      
-      // Handle native method not overloaded on XrayWrappers:
-      //   obj.addEventListener = val; -> obj.addEventlistener = native method
-      // And, XPCNativeWrapper bug where nested values appear to be wrapped:
-      // obj.customNestedAttribute = val -> obj.customNestedAttribute !== val
-      //                                    obj.customNestedAttribute = "waive wrapper something"
-      // SEE BUG 658560: Fix identity problem with CrossOriginWrappers
-      // TODO: check that DOM can't be updated by the document itself and so overloaded value becomes wrong
-      //       but I think such behavior is limited to primitive type
-      if ((typeof val == "function" || typeof val == "object") && name) {
-        overload[name] = val;
-      }
-      
-      return true;
-    },
-    
-    enumerate: function() {
-      var result = [];
-      for each (let name in Object.keys(obj)) {
-        result.push(name);
-      };
-      return result;
-    },
-    
-    keys: function() {
-      return Object.keys(obj);
-    }
-  };
-};
-
-
-/* 
- * Wrap an object from the document to a proxy wrapper.
- */
-function create(object) {
-  if ("wrappedJSObject" in object)
-    object = object.wrappedJSObject;
-  let xpcWrapper = XPCNativeWrapper(object);
-  // If we can't build an XPCNativeWrapper, it doesn't make sense to build
-  // a proxy. All proxy code is based on having such wrapper that store
-  // different JS attributes set.
-  // (we can't build XPCNativeWrapper when object is from the same
-  // principal/domain)
-  if (object === xpcWrapper) {
-    return object;
-  }
-  return getProxyForObject(xpcWrapper);
-}
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/lib/sdk/content/events.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 } = require("chrome");
+const { open } = require("../event/dom");
+const { observe } = require("../event/chrome");
+const { filter, merge, map, expand } = require("../event/utils");
+const { windows } = require("../window/utils");
+const { events: windowEvents } = require("sdk/window/events");
+
+// Note: Please note that even though pagehide event is included
+// it's not observable reliably since it's not always triggered
+// when closing tabs. Implementation can be imrpoved once that
+// event will be necessary.
+let TYPES = ["DOMContentLoaded", "load", "pageshow", "pagehide"];
+
+let insert = observe("document-element-inserted");
+let windowCreate = merge([
+  observe("content-document-global-created"),
+  observe("chrome-document-global-created")
+]);
+let create = map(windowCreate, function({target, data, type}) {
+  return { target: target.document, type: type, data: data }
+});
+
+function readStates({document}) {
+  // Map supported event types to a streams of those events on the given
+  // `window` for the inserted document and than merge these streams into
+  // single form stream off all window state change events.
+  let stateChanges = TYPES.map(function(type) {
+    return open(document, type, { capture: true });
+  });
+
+  // Since load events on document occur for every loded resource
+  return filter(merge(stateChanges), function({target}) {
+    return target instanceof Ci.nsIDOMDocument
+  })
+}
+
+
+let opened = windows(null, { includePrivate: true });
+let state = merge(opened.map(readStates));
+
+
+let futureReady = filter(windowEvents, function({type})
+                                        type === "DOMContentLoaded");
+let futureWindows = map(futureReady, function({target}) target);
+let futureState = expand(futureWindows, readStates);
+
+exports.events = merge([insert, create, state, futureState]);
--- a/addon-sdk/source/lib/sdk/content/worker.js
+++ b/addon-sdk/source/lib/sdk/content/worker.js
@@ -22,42 +22,38 @@ const { merge } = require('../util/objec
 const xulApp = require("../system/xul-app");
 const { getInnerId } = require("../window/utils")
 const USE_JS_PROXIES = !xulApp.versionInRange(xulApp.platformVersion,
                                               "17.0a2", "*");
 const { getTabForWindow } = require('../tabs/helpers');
 const { getTabForContentWindow } = require('../tabs/utils');
 
 /* Trick the linker in order to ensure shipping these files in the XPI.
-  require('./content-proxy.js');
   require('./content-worker.js');
   Then, retrieve URL of these files in the XPI:
 */
 let prefix = module.uri.split('worker.js')[0];
-const CONTENT_PROXY_URL = prefix + 'content-proxy.js';
 const CONTENT_WORKER_URL = prefix + 'content-worker.js';
 
+// Fetch additional list of domains to authorize access to for each content
+// script. It is stored in manifest `metadata` field which contains
+// package.json data. This list is originaly defined by authors in
+// `permissions` attribute of their package.json addon file.
+const permissions = require('@loader/options').metadata['permissions'] || {};
+const EXPANDED_PRINCIPALS = permissions['cross-domain-content'] || [];
+
 const JS_VERSION = '1.8';
 
 const ERR_DESTROYED =
   "Couldn't find the worker to receive this message. " +
   "The script may not be initialized yet, or may already have been unloaded.";
 
 const ERR_FROZEN = "The page is currently hidden and can no longer be used " +
                    "until it is visible again.";
 
-/**
- * This key is not exported and should only be used for proxy tests.
- * The following `PRIVATE_KEY` is used in addon module scope in order to tell
- * Worker API to expose `UNWRAP_ACCESS_KEY` in content script.
- * This key allows test-content-proxy.js to unwrap proxy with valueOf:
- *   let xpcWrapper = proxyWrapper.valueOf(UNWRAP_ACCESS_KEY);
- */
-const PRIVATE_KEY = {};
-
 
 const WorkerSandbox = EventEmitter.compose({
 
   /**
    * Emit a message to the worker content sandbox
    */
   emit: function emit() {
     // First ensure having a regular array
@@ -78,21 +74,16 @@ const WorkerSandbox = EventEmitter.compo
   /**
    * Synchronous version of `emit`.
    * /!\ Should only be used when it is strictly mandatory /!\
    *     Doesn't ensure passing only JSON values.
    *     Mainly used by context-menu in order to avoid breaking it.
    */
   emitSync: function emitSync() {
     let args = Array.slice(arguments);
-    // Bug 732716: Ensure wrapping xrays sent to the content script
-    // otherwise it will have access to raw xraywrappers and content script
-    // will assume it is an user object coming from the content script sandbox
-    if ("_wrap" in this)
-      args = args.map(this._wrap);
     return this._emitToContent(args);
   },
 
   /**
    * Tells if content script has at least one listener registered for one event,
    * through `self.on('xxx', ...)`.
    * /!\ Shouldn't be used. Implemented to avoid breaking context-menu API.
    */
@@ -123,37 +114,49 @@ const WorkerSandbox = EventEmitter.compo
     // Ensure that `emit` has always the right `this`
     this.emit = this.emit.bind(this);
     this.emitSync = this.emitSync.bind(this);
 
     // We receive a wrapped window, that may be an xraywrapper if it's content
     let window = worker._window;
     let proto = window;
 
+    // Eventually use expanded principal sandbox feature, if some are given.
+    //
+    // But prevent it when the Worker isn't used for a content script but for
+    // injecting `addon` object into a Panel, Widget, ... scope.
+    // That's because:
+    // 1/ It is useless to use multiple domains as the worker is only used
+    // to communicate with the addon,
+    // 2/ By using it it would prevent the document to have access to any JS
+    // value of the worker. As JS values coming from multiple domain principals
+    // can't be accessed by "mono-principals" (principal with only one domain).
+    // Even if this principal is for a domain that is specified in the multiple
+    // domain principal.
+    let principals  = window;
+    let wantXHRConstructor = false;
+    if (EXPANDED_PRINCIPALS.length > 0 && !worker._injectInDocument) {
+      principals = EXPANDED_PRINCIPALS.concat(window);
+      // We have to replace XHR constructor of the content document
+      // with a custom cross origin one, automagically added by platform code:
+      delete proto.XMLHttpRequest;
+      wantXHRConstructor = true;
+    }
+
     // Instantiate trusted code in another Sandbox in order to prevent content
     // script from messing with standard classes used by proxy and API code.
-    let apiSandbox = sandbox(window, { wantXrays: true, sameZoneAs: window });
+    let apiSandbox = sandbox(principals, { wantXrays: true, sameZoneAs: window });
     apiSandbox.console = console;
 
-    // Build content proxies only if the document has a non-system principal
-    // And only on old firefox versions that doesn't ship bug 738244
-    if (USE_JS_PROXIES && XPCNativeWrapper.unwrap(window) !== window) {
-      // Execute the proxy code
-      load(apiSandbox, CONTENT_PROXY_URL);
-      // Get a reference of the window's proxy
-      proto = apiSandbox.create(window);
-      // Keep a reference to `wrap` function for `emitSync` usage
-      this._wrap = apiSandbox.wrap;
-    }
-
     // Create the sandbox and bind it to window in order for content scripts to
     // have access to all standard globals (window, document, ...)
-    let content = this._sandbox = sandbox(window, {
+    let content = this._sandbox = sandbox(principals, {
       sandboxPrototype: proto,
       wantXrays: true,
+      wantXHRConstructor: wantXHRConstructor,
       sameZoneAs: window
     });
     // We have to ensure that window.top and window.parent are the exact same
     // object than window object, i.e. the sandbox global object. But not
     // always, in case of iframes, top and parent are another window object.
     let top = window.top === window ? content : content.top;
     let parent = window.parent === window ? content : content.parent;
     merge(content, {
@@ -226,22 +229,16 @@ const WorkerSandbox = EventEmitter.compo
 
     // self.port.emit calls
     this.on("event", function portEmit(name, args) {
       // destroyed?
       if (self._addonWorker)
         self._addonWorker._onContentScriptEvent.apply(self._addonWorker, arguments);
     });
 
-    // Internal feature that is only used by SDK tests:
-    // Expose unlock key to content script context.
-    // See `PRIVATE_KEY` definition for more information.
-    if (apiSandbox && worker._expose_key)
-      content.UNWRAP_ACCESS_KEY = apiSandbox.UNWRAP_ACCESS_KEY;
-
     // Inject `addon` global into target document if document is trusted,
     // `addon` in document is equivalent to `self` in content script.
     if (worker._injectInDocument) {
       let win = window.wrappedJSObject ? window.wrappedJSObject : window;
       Object.defineProperty(win, "addon", {
           value: content.self
         }
       );
@@ -309,17 +306,16 @@ const WorkerSandbox = EventEmitter.compo
         Array.isArray(contentScript) ? contentScript.join(';\n') : contentScript
       );
     }
   },
   destroy: function destroy() {
     this.emitSync("detach");
     this._sandbox = null;
     this._addonWorker = null;
-    this._wrap = null;
   },
 
   /**
    * JavaScript sandbox where all the content scripts are evaluated.
    * {Sandbox}
    */
   _sandbox: null,
 
@@ -468,21 +464,16 @@ const Worker = EventEmitter.compose({
       this.contentScriptFile = options.contentScriptFile;
     if ('contentScriptOptions' in options)
       this.contentScriptOptions = options.contentScriptOptions;
     if ('contentScript' in options)
       this.contentScript = options.contentScript;
 
     this._setListeners(options);
 
-    // Internal feature that is only used by SDK unit tests.
-    // See `PRIVATE_KEY` definition for more information.
-    if ('exposeUnlockKey' in options && options.exposeUnlockKey === PRIVATE_KEY)
-      this._expose_key = true;
-
     unload.ensure(this._public, "destroy");
 
     // Ensure that worker._port is initialized for contentWorker to be able
     // to send events during worker initialization.
     this.port;
 
     this._documentUnload = this._documentUnload.bind(this);
     this._pageShow = this._pageShow.bind(this);
--- a/addon-sdk/source/lib/sdk/deprecated/api-utils.js
+++ b/addon-sdk/source/lib/sdk/deprecated/api-utils.js
@@ -79,19 +79,19 @@ exports.publicConstructor = function pub
  *         options and whose values are the corresponding return values of map
  *         or the corresponding values in options.  Note that any keys not
  *         shared by both requirements and options are not in the returned
  *         object.
  */
 exports.validateOptions = function validateOptions(options, requirements) {
   options = options || {};
   let validatedOptions = {};
-  let mapThrew = false;
 
   for (let key in requirements) {
+    let mapThrew = false;
     let req = requirements[key];
     let [optsVal, keyInOpts] = (key in options) ?
                                [options[key], true] :
                                [undefined, false];
     if (req.map) {
       try {
         optsVal = req.map(optsVal);
       }
--- a/addon-sdk/source/lib/sdk/deprecated/list.js
+++ b/addon-sdk/source/lib/sdk/deprecated/list.js
@@ -106,13 +106,20 @@ const List = Trait.resolve({ toString: n
    * We cant reuse `_iterator` that is defined by `Iterable` since it provides
    * iteration in an arbitrary order.
    * @see https://developer.mozilla.org/en/JavaScript/Reference/Statements/for...in
    * @param {Boolean} onKeys
    */
   __iterator__: function __iterator__(onKeys, onKeyValue) {
     let array = this._keyValueMap.slice(0),
         i = -1;
-    for each(let element in array)
+    for (let element of array)
       yield onKeyValue ? [++i, element] : onKeys ? ++i : element;
+  },
+  iterator: function iterator() {
+    let array = this._keyValueMap.slice(0);
+
+    for (let element of array)
+      yield element;
   }
+
 });
 exports.List = List;
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/lib/sdk/event/chrome.js
@@ -0,0 +1,54 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+module.metadata = {
+  "stability": "unstable"
+};
+
+const { Cc, Ci, Cr } = require("chrome");
+const { emit, on, off } = require("./core");
+const { addObserver } = Cc['@mozilla.org/observer-service;1'].
+                        getService(Ci.nsIObserverService);
+
+// Simple class that can be used to instantiate event channel that
+// implements `nsIObserver` interface. It's will is used by `observe`
+// function as observer + event target. It basically proxies observer
+// notifications as to it's registered listeners.
+function ObserverChannel() {}
+Object.freeze(Object.defineProperties(ObserverChannel.prototype, {
+  QueryInterface: {
+    value: function(iid) {
+      if (!iid.equals(Ci.nsIObserver) &&
+          !iid.equals(Ci.nsISupportsWeakReference) &&
+          !iid.equals(Ci.nsISupports))
+        throw Cr.NS_ERROR_NO_INTERFACE;
+      return this;
+    }
+  },
+  observe: {
+    value: function(subject, topic, data) {
+      emit(this, "data", {
+        type: topic,
+        target: subject,
+        data: data
+      });
+    }
+  }
+}));
+
+function observe(topic) {
+  let observerChannel = new ObserverChannel();
+
+  // Note: `nsIObserverService` will not hold a weak reference to a
+  // observerChannel (since third argument is `true`). There for if it
+  // will be GC-ed with all it's event listeners once no other references
+  // will be held.
+  addObserver(observerChannel, topic, true);
+
+  return observerChannel;
+}
+
+exports.observe = observe;
--- a/addon-sdk/source/lib/sdk/lang/functional.js
+++ b/addon-sdk/source/lib/sdk/lang/functional.js
@@ -69,20 +69,50 @@ function partial(fn) {
     throw new TypeError(String(fn) + " is not a function");
 
   let args = Array.slice(arguments, 1);
 
   return function() fn.apply(this, args.concat(Array.slice(arguments)));
 }
 exports.partial = partial;
 
-exports.curry = deprecateFunction(partial,
-  'curry is deprecated, ' +
-  'please use require("sdk/lang/functional").partial instead.'
-);
+/**
+ * Returns function with implicit currying, which will continue currying until
+ * expected number of argument is collected. Expected number of arguments is
+ * determined by `fn.length`. Using this with variadic functions is stupid,
+ * so don't do it.
+ *
+ * @examples
+ *
+ * var sum = curry(function(a, b) {
+ *   return a + b
+ * })
+ * console.log(sum(2, 2)) // 4
+ * console.log(sum(2)(4)) // 6
+ */
+var curry = new function() {
+  function currier(fn, arity, params) {
+    // Function either continues to curry arguments or executes function
+    // if desired arguments have being collected.
+    return function curried() {
+      var input = Array.slice(arguments);
+      // Prepend all curried arguments to the given arguments.
+      if (params) input.unshift.apply(input, params);
+      // If expected number of arguments has being collected invoke fn,
+      // othrewise return curried version Otherwise continue curried.
+      return (input.length >= arity) ? fn.apply(this, input) :
+             currier(fn, arity, input);
+    };
+  }
+
+  return function curry(fn) {
+    return currier(fn, fn.length);
+  }
+};
+exports.curry = curry;
 
 /**
  * Returns the composition of a list of functions, where each function consumes
  * the return value of the function that follows. In math terms, composing the
  * functions `f()`, `g()`, and `h()` produces `f(g(h()))`.
  * @example
  *
  *   var greet = function(name) { return "hi: " + name; };
--- a/addon-sdk/source/lib/sdk/net/xhr.js
+++ b/addon-sdk/source/lib/sdk/net/xhr.js
@@ -2,154 +2,35 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 module.metadata = {
   "stability": "unstable"
 };
 
+const { deprecateFunction } = require("../util/deprecate");
 const { Cc, Ci } = require("chrome");
-const memory = require('../deprecated/memory');
-const { when: unload } = require("../system/unload");
-
-// ## Implementation Notes ##
-// 
-// Making `XMLHttpRequest` objects available to Jetpack code involves a
-// few key principles universal to all low-level module implementations:
-//
-// * **Unloadability**. A Jetpack-based extension using this module can be 
-//   asked to unload itself at any time, e.g. because the user decides to
-//   uninstall or disable the extension. This means we need to keep track of
-//   all in-progress reqests and abort them on unload.
-//
-// * **Developer-Ergonomic Tracebacks**. Whenever an exception is raised
-//   by a Jetpack-based extension, we want it to be logged in a
-//   place that is specific to that extension--so that a developer
-//   can distinguish it from an error on a web page or in another
-//   extension, for instance. We also want it to be logged with a
-//   full stack traceback, which the Mozilla platform doesn't usually
-//   do.
-//
-//   Because of this, we don't actually want to give the Mozilla
-//   platform's "real" XHR implementation to clients, but instead provide
-//   a simple wrapper that trivially delegates to the implementation in
-//   all cases except where callbacks are involved: whenever Mozilla
-//   platform code calls into the extension, such as during the XHR's
-//   `onreadystatechange` callback, we want to wrap the client's callback
-//   in a try-catch clause that traps any exceptions raised by the
-//   callback and logs them via console.exception() instead of allowing
-//   them to propagate back into Mozilla platform code.
+const XMLHttpRequest = require("../addon/window").window.XMLHttpRequest;
 
-// This is a private list of all active requests, so we know what to
-// abort if we're asked to unload.
-var requests = [];
-
-// Events on XHRs that we should listen for, so we know when to remove
-// a request from our private list.
-const TERMINATE_EVENTS = ["load", "error", "abort"];
-
-// Read-only properties of XMLHttpRequest objects that we want to
-// directly delegate to.
-const READ_ONLY_PROPS = ["readyState", "responseText", "responseXML",
-                         "status", "statusText"];
-
-// Methods of XMLHttpRequest that we want to directly delegate to.
-const DELEGATED_METHODS = ["abort", "getAllResponseHeaders",
-                           "getResponseHeader", "overrideMimeType",
-                           "send", "sendAsBinary", "setRequestHeader",
-                           "open"];
-
-var getRequestCount = exports.getRequestCount = function getRequestCount() {
-  return requests.length;
-};
-
-var XMLHttpRequest = exports.XMLHttpRequest = function XMLHttpRequest() {
-  let self = this;
-  let req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
-            .createInstance(Ci.nsIXMLHttpRequest);
-  // For the sake of simplicity, don't tie this request to any UI.
-  req.mozBackgroundRequest = true;
-
-  memory.track(req, "XMLHttpRequest");
-
-  this._req = req;
-  this._orsc = null;
-  this._cleanup = this._cleanup.bind(this);
-
-  requests.push(this);
+Object.defineProperties(XMLHttpRequest.prototype, {
+  mozBackgroundRequest: {
+    value: true,
+  },
+  forceAllowThirdPartyCookie: {
+    configurable: true,
+    value: deprecateFunction(function() {
+      forceAllowThirdPartyCookie(this);
 
-  TERMINATE_EVENTS.forEach(function(name) {
-    self._req.addEventListener(name, self._cleanup, false);
-  });
-};
-
-XMLHttpRequest.prototype = {
-  _cleanup: function _cleanup() {
-    this.onreadystatechange = null;
+    }, "`xhr.forceAllowThirdPartyCookie()` is deprecated, please use" +
+       "`require('sdk/net/xhr').forceAllowThirdPartyCookie(request)` instead")
+  }
+});
+exports.XMLHttpRequest = XMLHttpRequest;
 
-    let index = requests.indexOf(this);
-    if (index != -1) {
-      let self = this;
-      TERMINATE_EVENTS.forEach(function(name) {
-        self._req.removeEventListener(name, self._cleanup, false);
-      });
-      requests.splice(index, 1);
-    }
-  },
-  _unload: function _unload() {
-    this._req.abort();
-    this._cleanup();
-  },
-  addEventListener: function addEventListener(name, func) {
-    this._req.addEventListener(name, func);
-  },
-  removeEventListener: function removeEventListener(name, func) {
-    this._req.removeEventListener(name, func);
-  },
-  set upload(newValue) {
-    throw new Error("not implemented");
-  },
-  forceAllowThirdPartyCookie: function forceAllowThirdPartyCookie() {
-    if (this._req.channel instanceof Ci.nsIHttpChannelInternal)
-      this._req.channel.forceAllowThirdPartyCookie = true;
-  },
-  get onreadystatechange() {
-    return this._orsc;
-  },
-  set onreadystatechange(cb) {
-    this._orsc = cb;
-    if (cb) {
-      var self = this;
-      this._req.onreadystatechange = function() {
-        try {
-          self._orsc.apply(self, arguments);
-        }
-        catch (e) {
-          console.exception(e);
-        }
-      };
-    }
-    else {
-      this._req.onreadystatechange = null;
-    }
-  }
-};
+function forceAllowThirdPartyCookie(xhr) {
+  if (xhr.channel instanceof Ci.nsIHttpChannelInternal)
+    xhr.channel.forceAllowThirdPartyCookie = true;
+}
+exports.forceAllowThirdPartyCookie = forceAllowThirdPartyCookie;
 
-READ_ONLY_PROPS.forEach(
-   function(name) {
-     XMLHttpRequest.prototype.__defineGetter__(
-       name,
-       function() {
-         return this._req[name];
-       });
-   });
-
-DELEGATED_METHODS.forEach(
-  function(name) {
-    XMLHttpRequest.prototype[name] = function() {
-      return this._req[name].apply(this._req, arguments);
-    };
-  });
-
-unload(function() {
-  requests.slice().forEach(function(request) { request._unload(); });
-});
+// No need to handle add-on unloads as addon/window is closed at unload
+// and it will take down all the associated requests.
\ No newline at end of file
--- a/addon-sdk/source/lib/sdk/request.js
+++ b/addon-sdk/source/lib/sdk/request.js
@@ -9,17 +9,17 @@ module.metadata = {
 };
 
 const { ns } = require("./core/namespace");
 const { emit } = require("./event/core");
 const { merge } = require("./util/object");
 const { stringify } = require("./querystring");
 const { EventTarget } = require("./event/target");
 const { Class } = require("./core/heritage");
-const { XMLHttpRequest } = require("./net/xhr");
+const { XMLHttpRequest, forceAllowThirdPartyCookie } = require("./net/xhr");
 const apiUtils = require("./deprecated/api-utils");
 const { isValidURI } = require("./url.js");
 
 const response = ns();
 const request = ns();
 
 // Instead of creating a new validator for each request, just make one and
 // reuse it.
@@ -70,17 +70,18 @@ function runRequest(mode, target) {
   let data = stringify(content);
   // If the URL already has ? in it, then we want to just use &
   if (isGetOrHead && data)
     url = url + (/\?/.test(url) ? "&" : "?") + data;
 
   // open the request
   xhr.open(mode, url);
 
-  xhr.forceAllowThirdPartyCookie();
+
+  forceAllowThirdPartyCookie(xhr);
 
   // request header must be set after open, but before send
   xhr.setRequestHeader("Content-Type", contentType);
 
   // set other headers
   Object.keys(headers).forEach(function(name) {
     xhr.setRequestHeader(name, headers[name]);
   });
--- a/addon-sdk/source/lib/sdk/tabs/tab-firefox.js
+++ b/addon-sdk/source/lib/sdk/tabs/tab-firefox.js
@@ -120,17 +120,17 @@ const TabTrait = Trait.compose(EventEmit
     }
   },
   /**
    * Internal tab event router. Window will emit tab related events for all it's
    * tabs, this listener will propagate all the events for this tab to it's
    * listeners.
    */
   _onEvent: function _onEvent(type, tab) {
-    if (tab == this._public)
+    if (viewNS(tab).tab == this._tab)
       this._emit(type, tab);
   },
   /**
    * Browser DOM element where page of this tab is currently loaded.
    */
   get _browser() getBrowserForTab(this._tab),
   /**
    * Window DOM element containing this tab.
@@ -243,18 +243,22 @@ const TabTrait = Trait.compose(EventEmit
     if (!this._tab)
       return;
     activateTab(this._tab);
   }),
   /**
    * Close the tab
    */
   close: function close(callback) {
-    if (!this._tab)
+    // Bug 699450: the tab may already have been detached
+    if (!this._tab || !this._tab.parentNode) {
+      if (callback)
+        callback();
       return;
+    }
     if (callback)
       this.once(EVENTS.close.name, callback);
     this._window.gBrowser.removeTab(this._tab);
   },
   /**
    * Reload the tab
    */
   reload: function reload() {
--- a/addon-sdk/source/lib/sdk/tabs/utils.js
+++ b/addon-sdk/source/lib/sdk/tabs/utils.js
@@ -128,23 +128,31 @@ function isTabOpen(tab) {
   // try normal case then fennec case
   return !!((tab.linkedBrowser) || getWindowHoldingTab(tab));
 }
 exports.isTabOpen = isTabOpen;
 
 function closeTab(tab) {
   let gBrowser = getTabBrowserForTab(tab);
   // normal case?
-  if (gBrowser)
+  if (gBrowser) {
+    // Bug 699450: the tab may already have been detached
+    if (!tab.parentNode)
+      return;
     return gBrowser.removeTab(tab);
+  }
 
   let window = getWindowHoldingTab(tab);
   // fennec?
-  if (window && window.BrowserApp)
+  if (window && window.BrowserApp) {
+    // Bug 699450: the tab may already have been detached
+    if (!tab.browser)
+      return;
     return window.BrowserApp.closeTab(tab);
+  }
   return null;
 }
 exports.closeTab = closeTab;
 
 function getURI(tab) {
   if (tab.browser) // fennec
     return tab.browser.currentURI.spec;
   return tab.linkedBrowser.currentURI.spec;
@@ -200,24 +208,30 @@ function getAllTabContentWindows() {
   return getTabs().map(getTabContentWindow);
 }
 exports.getAllTabContentWindows = getAllTabContentWindows;
 
 // gets the tab containing the provided window
 function getTabForContentWindow(window) {
   // Retrieve the topmost frame container. It can be either <xul:browser>,
   // <xul:iframe/> or <html:iframe/>. But in our case, it should be xul:browser.
-  let browser = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                   .getInterface(Ci.nsIWebNavigation)
-                   .QueryInterface(Ci.nsIDocShell)
-                   .chromeEventHandler;
+  let browser;
+  try {
+    browser = window.QueryInterface(Ci.nsIInterfaceRequestor)
+                    .getInterface(Ci.nsIWebNavigation)
+                    .QueryInterface(Ci.nsIDocShell)
+                    .chromeEventHandler;
+  } catch(e) {
+    // Bug 699450: The tab may already have been detached so that `window` is
+    // in a almost destroyed state and can't be queryinterfaced anymore.
+  }
 
   // Is null for toplevel documents
   if (!browser) {
-    return false;
+    return null;
   }
 
   // Retrieve the owner window, should be browser.xul one
   let chromeWindow = browser.ownerDocument.defaultView;
 
   // Ensure that it is top-level browser window.
   // We need extra checks because of Mac hidden window that has a broken
   // `gBrowser` global attribute.
--- a/addon-sdk/source/lib/sdk/test/httpd.js
+++ b/addon-sdk/source/lib/sdk/test/httpd.js
@@ -2691,17 +2691,17 @@ ServerHandler.prototype =
         }
       }
       catch (e)
       {
         fis.close();
         throw e;
       }
 
-      function writeMore()
+      let writeMore = function writeMore()
       {
         gThreadManager.currentThread
                       .dispatch(writeData, Ci.nsIThread.DISPATCH_NORMAL);
       }
 
       var input = new BinaryInputStream(fis);
       var output = new BinaryOutputStream(response.bodyOutputStream);
       var writeData =
--- a/addon-sdk/source/lib/sdk/util/list.js
+++ b/addon-sdk/source/lib/sdk/util/list.js
@@ -40,16 +40,23 @@ const List = Class({
    * @see https://developer.mozilla.org/en/JavaScript/Reference/Statements/for...in
    * @param {Boolean} onKeys
    */
   __iterator__: function __iterator__(onKeys, onKeyValue) {
     let array = listNS(this).keyValueMap.slice(0),
                 i = -1;
     for each(let element in array)
       yield onKeyValue ? [++i, element] : onKeys ? ++i : element;
+  },
+  iterator: function iterator() {
+    let array = listNS(this).keyValueMap.slice(0),
+                i = -1;
+
+    for (let element of array)
+      yield element;
   }
 });
 exports.List = List;
 
 function addListItem(that, value) {
   let list = listNS(that).keyValueMap,
       index = list.indexOf(value);
 
--- a/addon-sdk/source/lib/sdk/window/events.js
+++ b/addon-sdk/source/lib/sdk/window/events.js
@@ -3,78 +3,44 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 module.metadata = {
   "stability": "unstable"
 };
 
 const { Ci } = require("chrome");
-const events = require("../system/events");
-const { on, off, emit } = require("../event/core");
+const { observe } = require("../event/chrome");
+const { open } = require("../event/dom");
 const { windows } = require("../window/utils");
-
-// Object represents event channel on which all top level window events
-// will be dispatched, allowing users to react to those evens.
-const channel = {};
-exports.events = channel;
-
-const types = {
-  domwindowopened: "open",
-  domwindowclosed: "close",
-}
-
-// Utility function to query observer notification subject to get DOM window.
-function nsIDOMWindow($) $.QueryInterface(Ci.nsIDOMWindow);
-
-// Utility function used as system event listener that is invoked every time
-// top level window is open. This function does two things:
-// 1. Registers event listeners to track when document becomes interactive and
-//    when it's done loading. This will become obsolete once Bug 843910 is
-//    fixed.
-// 2. Forwards event to an exported event stream.
-function onOpen(event) {
-  observe(nsIDOMWindow(event.subject));
-  dispatch(event);
-}
+const { filter, merge, map, expand } = require("../event/utils");
 
 // Function registers single shot event listeners for relevant window events
 // that forward events to exported event stream.
-function observe(window) {
-  function listener(event) {
-    if (event.target === window.document) {
-      window.removeEventListener(event.type, listener, true);
-      emit(channel, "data", { type: event.type, target: window });
-    }
-  }
-
-  // Note: we do not remove listeners on unload since on add-on unload we
-  // nuke add-on sandbox that should allow GC-ing listeners. This also has
-  // positive effects on add-on / firefox unloads.
-  window.addEventListener("DOMContentLoaded", listener, true);
-  window.addEventListener("load", listener, true);
-  // TODO: Also add focus event listener so that can be forwarded to event
-  // stream. It can be part of Bug 854982.
-}
-
-// Utility function that takes system notification event and forwards it to a
-// channel in restructured form.
-function dispatch({ type: topic, subject }) {
-  emit(channel, "data", {
-    topic: topic,
-    type: types[topic],
-    target: nsIDOMWindow(subject)
+function eventsFor(window) {
+  let interactive = open(window, "DOMContentLoaded", { capture: true });
+  let complete = open(window, "load", { capture: true });
+  let states = merge([interactive, complete]);
+  let changes = filter(states, function({target}) target === window.document);
+  return map(changes, function({type, target}) {
+    return { type: type, target: target.defaultView }
   });
 }
 
 // In addition to observing windows that are open we also observe windows
 // that are already already opened in case they're in process of loading.
 let opened = windows(null, { includePrivate: true });
-opened.forEach(observe);
+let currentEvents = merge(opened.map(eventsFor));
 
-// Register system event listeners to forward messages on exported event
-// stream. Note that by default only weak refs are kept by system events
-// module so they will be GC-ed once add-on unloads and no manual cleanup
-// is required. Also note that listeners are intentionally not inlined since
-// to avoid premature GC-ing. Currently refs are kept by module scope and there
-// for they remain alive.
-events.on("domwindowopened", onOpen);
-events.on("domwindowclosed", dispatch);
+// Register system event listeners for top level window open / close.
+function rename({type, target, data}) {
+  return { type: rename[type], target: target, data: data }
+}
+rename.domwindowopened = "open";
+rename.domwindowclosed = "close";
+
+let openEvents = map(observe("domwindowopened"), rename);
+let closeEvents = map(observe("domwindowclosed"), rename);
+let futureEvents = expand(openEvents, function({target}) eventsFor(target));
+
+let channel = merge([currentEvents, futureEvents,
+                     openEvents, closeEvents]);
+exports.events = channel;
--- a/addon-sdk/source/lib/sdk/window/utils.js
+++ b/addon-sdk/source/lib/sdk/window/utils.js
@@ -17,17 +17,17 @@ const windowWatcher = Cc['@mozilla.org/e
 const appShellService = Cc['@mozilla.org/appshell/appShellService;1'].
                         getService(Ci.nsIAppShellService);
 const WM = Cc['@mozilla.org/appshell/window-mediator;1'].
            getService(Ci.nsIWindowMediator);
 
 const BROWSER = 'navigator:browser',
       URI_BROWSER = 'chrome://browser/content/browser.xul',
       NAME = '_blank',
-      FEATURES = 'chrome,all,dialog=no';
+      FEATURES = 'chrome,all,dialog=no,non-private';
 
 function isWindowPrivate(win) {
   if (!win)
     return false;
 
   // if the pbService is undefined, the PrivateBrowsingUtils.jsm is available,
   // and the app is Firefox, then assume per-window private browsing is
   // enabled.
@@ -238,19 +238,31 @@ exports.isFocused = isFocused;
  * 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 featureAry = features.toLowerCase().split(',');
+
+  if (!!options.private) {
+    // add private flag if private window is desired
+    if (!array.has(featureAry, 'private')) {
+      featureAry.push('private');
+    }
+
+    // remove the non-private flag ig a private window is desired
+    let nonPrivateIndex = featureAry.indexOf('non-private');
+    if (nonPrivateIndex >= 0) {
+      featureAry.splice(nonPrivateIndex, 1);
+    }
+
+    features = featureAry.join(',');
   }
 
   let browser = getMostRecentBrowserWindow();
 
   // if there is no browser then do nothing
   if (!browser)
     return undefined;
 
--- a/addon-sdk/source/lib/sdk/windows/loader.js
+++ b/addon-sdk/source/lib/sdk/windows/loader.js
@@ -34,17 +34,19 @@ const WindowLoader = Trait.compose({
   /**
    * Internal listener that is called when `_window`'s DOM 'unload' event
    * is dispatched. Please note that this trait will not handle exceptions that
    * may be thrown by this method so method itself should take care of
    * handling them.
    */
   _onUnload: Trait.required,
   _load: function _load() {
-    if (this.__window) return;
+    if (this.__window)
+      return;
+
     this._window = openDialog({
       private: this._isPrivate,
       args: this._tabOptions.map(function(options) options.url).join("|")
     });
   },
   /**
    * Private window who's load event is being tracked. Once window is loaded
    * `_onLoad` is called.
--- a/addon-sdk/source/python-lib/cuddlefish/packaging.py
+++ b/addon-sdk/source/python-lib/cuddlefish/packaging.py
@@ -108,23 +108,31 @@ def find_packages_with_module(pkg_cfg, n
 
 def resolve_dirs(pkg_cfg, dirnames):
     for dirname in dirnames:
         yield resolve_dir(pkg_cfg, dirname)
 
 def resolve_dir(pkg_cfg, dirname):
     return os.path.join(pkg_cfg.root_dir, dirname)
 
+def validate_permissions(perms):
+    if (perms.get('cross-domain-content') and
+        not isinstance(perms.get('cross-domain-content'), list)):
+        raise ValueError("Error: `cross-domain-content` permissions in \
+ package.json file must be an array of strings:\n  %s" % perms)
+
 def get_metadata(pkg_cfg, deps):
     metadata = Bunch()
     for pkg_name in deps:
         cfg = pkg_cfg.packages[pkg_name]
         metadata[pkg_name] = Bunch()
         for prop in METADATA_PROPS:
             if cfg.get(prop):
+                if prop == 'permissions':
+                    validate_permissions(cfg[prop])
                 metadata[pkg_name][prop] = cfg[prop]
     return metadata
 
 def set_section_dir(base_json, name, base_path, dirnames, allow_root=False):
     resolved = compute_section_dir(base_json, base_path, dirnames, allow_root)
     if resolved:
         base_json[name] = os.path.abspath(resolved)
 
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/test/addons/content-permissions/main.js
@@ -0,0 +1,86 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+const xulApp = require("xul-app");
+const { PageMod } = require("page-mod");
+const tabs = require("tabs");
+
+exports.testCrossDomainIframe = function(assert, done) {
+  let serverPort = 8099;
+  let server = require("sdk/test/httpd").startServerAsync(serverPort);
+  server.registerPathHandler("/iframe", function handle(request, response) {
+    response.write("<html><body>foo</body></html>");
+  });
+
+  let pageMod = PageMod({
+    include: "about:*",
+    contentScript: "new " + function ContentScriptScope() {
+      self.on("message", function (url) {
+        let iframe = document.createElement("iframe");
+        iframe.addEventListener("load", function onload() {
+          iframe.removeEventListener("load", onload, false);
+          self.postMessage(iframe.contentWindow.document.body.innerHTML);
+        }, false);
+        iframe.setAttribute("src", url);
+        document.documentElement.appendChild(iframe);
+      });
+    },
+    onAttach: function(w) {
+      w.on("message", function (body) {
+        assert.equal(body, "foo", "received iframe html content");
+        pageMod.destroy();
+        w.tab.close();
+        server.stop(done);
+      });
+      w.postMessage("http://localhost:8099/iframe");
+    }
+  });
+
+  tabs.open("about:credits");
+};
+
+exports.testCrossDomainXHR = function(assert, done) {
+  let serverPort = 8099;
+  let server = require("sdk/test/httpd").startServerAsync(serverPort);
+  server.registerPathHandler("/xhr", function handle(request, response) {
+    response.write("foo");
+  });
+
+  let pageMod = PageMod({
+    include: "about:*",
+    contentScript: "new " + function ContentScriptScope() {
+      self.on("message", function (url) {
+        let request = new XMLHttpRequest();
+        request.overrideMimeType("text/plain");
+        request.open("GET", url, true);
+        request.onload = function () {
+          self.postMessage(request.responseText);
+        };
+        request.send(null);
+      });
+    },
+    onAttach: function(w) {
+      w.on("message", function (body) {
+        assert.equal(body, "foo", "received XHR content");
+        pageMod.destroy();
+        w.tab.close();
+        server.stop(done);
+      });
+      w.postMessage("http://localhost:8099/xhr");
+    }
+  });
+
+  tabs.open("about:credits");
+};
+
+if (!xulApp.versionInRange(xulApp.platformVersion, "17.0a2", "*")) {
+  module.exports = {
+    "test Unsupported Application": function Unsupported (assert) {
+      assert.pass("This firefox version doesn't support cross-domain-content permission.");
+    }
+  };
+}
+
+require("sdk/test/runner").runTestsFromModule(module);
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/test/addons/content-permissions/package.json
@@ -0,0 +1,6 @@
+{
+  "id": "content-permissions",
+  "permissions": {
+    "cross-domain-content": ["http://localhost:8099"]
+  }
+}
--- a/addon-sdk/source/test/private-browsing/windows.js
+++ b/addon-sdk/source/test/private-browsing/windows.js
@@ -1,18 +1,19 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 'use strict';
 
 const { pb, pbUtils } = require('./helper');
-const { openDialog, open } = require('sdk/window/utils');
-const { promise, close } = require('sdk/window/helpers');
+const { onFocus, openDialog, open } = require('sdk/window/utils');
+const { open: openPromise, close, focus, promise } = require('sdk/window/helpers');
 const { isPrivate } = require('sdk/private-browsing');
 const { browserWindows: windows } = require('sdk/windows');
+const { defer } = require('sdk/core/promise');
 
 // test openDialog() from window/utils with private option
 // test isActive state in pwpb case
 // test isPrivate on ChromeWindow
 exports.testPerWindowPrivateBrowsingGetter = function(assert, done) {
   let win = openDialog({
     private: true
   });
@@ -48,27 +49,54 @@ exports.testPerWindowPrivateBrowsingGett
 
     close(win).then(function() {
       assert.equal(pb.isActive, false, 'PB mode is not active');
       done();
     });
   });
 }
 
-exports.testIsPrivateOnWindowOn = function(assert, done) {
+exports.testIsPrivateOnWindowOpen = function(assert, done) {
   windows.open({
     isPrivate: true,
     onOpen: function(window) {
       assert.equal(isPrivate(window), false, 'isPrivate for a window is true when it should be');
       assert.equal(isPrivate(window.tabs[0]), false, 'isPrivate for a tab is false when it should be');
       window.close(done);
     }
   });
 }
 
+exports.testIsPrivateOnWindowOpenFromPrivate = function(assert, done) {
+    // open a private window
+    openPromise(null, {
+      features: {
+        private: true,
+        chrome: true,
+        titlebar: true,
+        toolbar: true
+      }
+    }).then(focus).then(function(window) {
+      let { promise, resolve } = defer();
+
+      assert.equal(isPrivate(window), true, 'the only open window is private');
+
+      windows.open({
+        url: 'about:blank',
+        onOpen: function(w) {
+          assert.equal(isPrivate(w), false, 'new test window is not private');
+          w.close(function() resolve(window));
+        }
+      });
+
+      return promise;
+    }).then(close).
+       then(done, assert.fail);
+}
+
 exports.testIsPrivateOnWindowOff = function(assert, done) {
   windows.open({
     onOpen: function(window) {
       assert.equal(isPrivate(window), false, 'isPrivate for a window is false when it should be');
       assert.equal(isPrivate(window.tabs[0]), false, 'isPrivate for a tab is false when it should be');
       window.close(done);
     }
   })
--- a/addon-sdk/source/test/tabs/test-firefox-tabs.js
+++ b/addon-sdk/source/test/tabs/test-firefox-tabs.js
@@ -282,22 +282,35 @@ exports.testTabClose = function(test) {
   openBrowserWindow(function(window, browser) {
     let tabs = require("sdk/tabs");
     let url = "data:text/html;charset=utf-8,foo";
 
     test.assertNotEqual(tabs.activeTab.url, url, "tab is not the active tab");
     tabs.on('ready', function onReady(tab) {
       tabs.removeListener('ready', onReady);
       test.assertEqual(tabs.activeTab.url, tab.url, "tab is now the active tab");
+      let secondOnCloseCalled = false;
       tab.close(function() {
         closeBrowserWindow(window, function() {
+          test.assert(secondOnCloseCalled,
+            "The immediate second call to tab.close gots its callback fired");
           test.assertNotEqual(tabs.activeTab.url, url, "tab is no longer the active tab");
           test.done()
         });
       });
+
+      // Bug 699450: Multiple calls to tab should not throw
+      try {
+        tab.close(function () {
+          secondOnCloseCalled = true;
+        });
+      }
+      catch(e) {
+        test.fail("second call to tab.close() thrown an exception: " + e);
+      }
       test.assertNotEqual(tabs.activeTab.url, url, "tab is no longer the active tab");
     });
 
     tabs.open(url);
   });
 };
 
 // TEST: tab.move()
@@ -434,26 +447,64 @@ exports.testOpenInNewWindow = function(t
       inNewWindow: true,
       onReady: function(tab) {
         let newWindow = cache[cache.length - 1];
         test.assertEqual(cache.length, startWindowCount + 1, "a new window was opened");
         test.assertEqual(activeWindow, newWindow, "new window is active");
         test.assertEqual(tab.url, url, "URL of the new tab matches");
         test.assertEqual(newWindow.content.location, url, "URL of new tab in new window matches");
         test.assertEqual(tabs.activeTab.url, url, "URL of activeTab matches");
-        for (var i in cache) cache[i] = null;
+        for (let i in cache) cache[i] = null;
         wt.unload();
         closeBrowserWindow(newWindow, function() {
           closeBrowserWindow(window, function() test.done());
         });
       }
     });
   });
 };
 
+// Test tab.open inNewWindow + onOpen combination
+exports.testOpenInNewWindowOnOpen = function(test) {
+  test.waitUntilDone();
+  let tabs = require("sdk/tabs");
+
+  openBrowserWindow(function(window, browser) {
+    let cache = [];
+    let windowUtils = require("sdk/deprecated/window-utils");
+    let wt = new windowUtils.WindowTracker({
+      onTrack: function(win) {
+        cache.push(win);
+      },
+      onUntrack: function(win) {
+        cache.splice(cache.indexOf(win), 1)
+      }
+    });
+    let startWindowCount = cache.length;
+
+    let url = "data:text/html;charset=utf-8,newwindow";
+    tabs.open({
+      url: url,
+      inNewWindow: true,
+      onOpen: function(tab) {
+        let newWindow = cache[cache.length - 1];
+        test.assertEqual(cache.length, startWindowCount + 1, "a new window was opened");
+        test.assertEqual(activeWindow, newWindow, "new window is active");
+
+        for (let i in cache) cache[i] = null;
+        wt.unload();
+
+        closeBrowserWindow(newWindow, function() {
+          closeBrowserWindow(window, function() test.done());
+        });
+      }
+    });
+  });
+};
+
 // TEST: onOpen event handler
 exports.testTabsEvent_onOpen = function(test) {
   test.waitUntilDone();
   openBrowserWindow(function(window, browser) {
     var tabs = require("sdk/tabs");
     let url = "data:text/html;charset=utf-8,1";
     let eventCount = 0;
 
--- a/addon-sdk/source/test/test-addon-page.js
+++ b/addon-sdk/source/test/test-addon-page.js
@@ -2,122 +2,142 @@
  * 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 { isTabOpen, activateTab, openTab,
         closeTab, getURI } = require('sdk/tabs/utils');
 const windows = require('sdk/deprecated/window-utils');
-const { Loader } = require('sdk/test/loader');
+const { LoaderWithHookedConsole } = require('sdk/test/loader');
 const { setTimeout } = require('sdk/timers');
 const { is } = require('sdk/system/xul-app');
 const tabs = require('sdk/tabs');
+const isAustralis = "gCustomizeMode" in windows.activeBrowserWindow;
 
 let uri = require('sdk/self').data.url('index.html');
 
 function isChromeVisible(window) {
   let x = window.document.documentElement.getAttribute('disablechrome')
   return x !== 'true';
 }
 
+exports['test add-on page deprecation message'] = function(assert) {
+  let { loader, messages } = LoaderWithHookedConsole(module);
+  loader.require('sdk/addon-page');
+
+  assert.equal(messages.length, 1, "only one error is dispatched");
+  assert.equal(messages[0].type, "error", "the console message is an error");
+
+  let msg = messages[0].msg;
+
+  assert.ok(msg.indexOf("DEPRECATED") === 0,
+            "The message is deprecation message");
+
+  loader.unload();
+};
+
 exports['test that add-on page has no chrome'] = function(assert, done) {
-  let loader = Loader(module);
+  let { loader } = LoaderWithHookedConsole(module);
   loader.require('sdk/addon-page');
 
   let window = windows.activeBrowserWindow;
   let tab = openTab(window, uri);
 
   assert.ok(isChromeVisible(window), 'chrome is visible for non addon page');
 
   // need to do this in another turn to make sure event listener
   // that sets property has time to do that.
   setTimeout(function() {
     activateTab(tab);
 
-    assert.equal(isChromeVisible(window), is('Fennec'), 'chrome is not visible for addon page');
+    assert.equal(isChromeVisible(window), is('Fennec') || isAustralis,
+      'chrome is not visible for addon page');
 
     closeTab(tab);
     assert.ok(isChromeVisible(window), 'chrome is visible again');
     loader.unload();
     done();
   });
 };
 
 exports['test that add-on page with hash has no chrome'] = function(assert, done) {
-  let loader = Loader(module);
+  let { loader } = LoaderWithHookedConsole(module);
   loader.require('sdk/addon-page');
 
   let window = windows.activeBrowserWindow;
   let tab = openTab(window, uri + "#foo");
 
   assert.ok(isChromeVisible(window), 'chrome is visible for non addon page');
 
   // need to do this in another turn to make sure event listener
   // that sets property has time to do that.
   setTimeout(function() {
     activateTab(tab);
 
-    assert.equal(isChromeVisible(window), is('Fennec'), 'chrome is not visible for addon page');
+    assert.equal(isChromeVisible(window), is('Fennec') || isAustralis,
+      'chrome is not visible for addon page');
 
     closeTab(tab);
     assert.ok(isChromeVisible(window), 'chrome is visible again');
     loader.unload();
     done();
   });
 };
 
 exports['test that add-on page with querystring has no chrome'] = function(assert, done) {
-  let loader = Loader(module);
+  let { loader } = LoaderWithHookedConsole(module);
   loader.require('sdk/addon-page');
 
   let window = windows.activeBrowserWindow;
   let tab = openTab(window, uri + '?foo=bar');
 
   assert.ok(isChromeVisible(window), 'chrome is visible for non addon page');
 
   // need to do this in another turn to make sure event listener
   // that sets property has time to do that.
   setTimeout(function() {
     activateTab(tab);
 
-    assert.equal(isChromeVisible(window), is('Fennec'), 'chrome is not visible for addon page');
+    assert.equal(isChromeVisible(window), is('Fennec') || isAustralis,
+      'chrome is not visible for addon page');
 
     closeTab(tab);
     assert.ok(isChromeVisible(window), 'chrome is visible again');
     loader.unload();
     done();
   });
 };
 
 exports['test that add-on page with hash and querystring has no chrome'] = function(assert, done) {
-  let loader = Loader(module);
+  let { loader } = LoaderWithHookedConsole(module);
   loader.require('sdk/addon-page');
 
   let window = windows.activeBrowserWindow;
   let tab = openTab(window, uri + '#foo?foo=bar');
 
   assert.ok(isChromeVisible(window), 'chrome is visible for non addon page');
 
   // need to do this in another turn to make sure event listener
   // that sets property has time to do that.
   setTimeout(function() {
     activateTab(tab);
 
-    assert.equal(isChromeVisible(window), is('Fennec'), 'chrome is not visible for addon page');
+    assert.equal(isChromeVisible(window), is('Fennec') || isAustralis,
+      'chrome is not visible for addon page');
 
     closeTab(tab);
     assert.ok(isChromeVisible(window), 'chrome is visible again');
     loader.unload();
     done();
   });
 };
 
 exports['test that malformed uri is not an addon-page'] = function(assert, done) {
-  let loader = Loader(module);
+  let { loader } = LoaderWithHookedConsole(module);
   loader.require('sdk/addon-page');
 
   let window = windows.activeBrowserWindow;
   let tab = openTab(window, uri + 'anguage');
 
   // need to do this in another turn to make sure event listener
   // that sets property has time to do that.
   setTimeout(function() {
@@ -127,17 +147,17 @@ exports['test that malformed uri is not 
 
     closeTab(tab);
     loader.unload();
     done();
   });
 };
 
 exports['test that add-on pages are closed on unload'] = function(assert, done) {
-  let loader = Loader(module);
+  let { loader } = LoaderWithHookedConsole(module);
   loader.require('sdk/addon-page');
 
   tabs.open({
     url: uri,
     onReady: function listener(tab) {
       loader.unload();
       assert.ok(!isTabOpen(tab), 'add-on page tabs are closed on unload');
 
--- a/addon-sdk/source/test/test-api-utils.js
+++ b/addon-sdk/source/test/test-api-utils.js
@@ -170,16 +170,28 @@ exports.testValidateMapWithMissingKey = 
   val = apiUtils.validateOptions({ }, {
     foo: {
       map: function (v) { throw "bar" }
     }
   });
   assertObjsEqual(test, val, { });
 };
 
+exports.testValidateMapWithMissingKeyAndThrown = function (test) {
+  let val = apiUtils.validateOptions({}, {
+    bar: {
+      map: function(v) { throw "bar" }
+    },
+    baz: {
+      map: function(v) "foo"
+    }
+  });
+  assertObjsEqual(test, val, { baz: "foo" });
+};
+
 exports.testAddIterator = function testAddIterator(test) {
   let obj = {};
   let keys = ["foo", "bar", "baz"];
   let vals = [1, 2, 3];
   let keysVals = [["foo", 1], ["bar", 2], ["baz", 3]];
   apiUtils.addIterator(
     obj,
     function keysValsGen() {
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/test/test-content-events.js
@@ -0,0 +1,113 @@
+/* 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 { getMostRecentBrowserWindow, getInnerId } = require("sdk/window/utils");
+const { openTab, closeTab, getBrowserForTab } = require("sdk/tabs/utils");
+const { defer } = require("sdk/core/promise");
+const { curry, identity, partial } = require("sdk/lang/functional");
+
+let when = curry(function(options, tab) {
+  let type = options.type || options;
+  let capture = options.captuer || false;
+  let target = getBrowserForTab(tab);
+  let { promise, resolve } = defer();
+
+  target.addEventListener(type, function handler(event) {
+    if (!event.target.defaultView.frameElement) {
+      target.removeEventListener(type, handler, capture);
+      resolve(tab);
+    }
+  }, capture);
+
+  return promise;
+});
+
+let use = function(value) function() value;
+
+
+let open = curry(function(url, window) openTab(window, url));
+let close = function(tab) {
+  let promise = when("pagehide", tab);
+  closeTab(tab);
+  return promise;
+}
+
+exports["test multiple tabs"] = function(assert, done) {
+  let loader = Loader(module);
+  let { events } = loader.require("sdk/content/events");
+  let { on, off } = loader.require("sdk/event/core");
+  let actual = [];
+  on(events, "data", function({type, target, timeStamp}) {
+    // ignore about:blank pages.
+    if (target.URL !== "about:blank") actual.push(type + " -> " + target.URL)
+  });
+
+  let window =  getMostRecentBrowserWindow();
+  let firstTab = open("data:text/html,first-tab", window);
+
+  when("pageshow", firstTab).
+    then(close).
+    then(use(window)).
+    then(open("data:text/html,second-tab")).
+    then(when("pageshow")).
+    then(close).
+    then(function() {
+      assert.deepEqual(actual, [
+        "document-element-inserted -> data:text/html,first-tab",
+        "DOMContentLoaded -> data:text/html,first-tab",
+        "load -> data:text/html,first-tab",
+        "pageshow -> data:text/html,first-tab",
+        "document-element-inserted -> data:text/html,second-tab",
+        "DOMContentLoaded -> data:text/html,second-tab",
+        "load -> data:text/html,second-tab",
+        "pageshow -> data:text/html,second-tab"
+      ], "all events dispatche as expeced")
+    }, function(reason) {
+      assert.fail(Error(reason));
+    }).then(function() {
+      loader.unload();
+      done();
+    });
+};
+
+exports["test nested frames"] = function(assert, done) {
+  let loader = Loader(module);
+  let { events } = loader.require("sdk/content/events");
+  let { on, off } = loader.require("sdk/event/core");
+  let actual = [];
+  on(events, "data", function({type, target, timeStamp}) {
+    // ignore about:blank pages.
+    if (target.URL !== "about:blank") actual.push(type + " -> " + target.URL)
+  });
+
+  let window =  getMostRecentBrowserWindow();
+  let uri = encodeURI("data:text/html,<iframe src='data:text/html,iframe'>");
+  let tab = open(uri, window);
+
+  when("pageshow", tab).
+    then(close).
+    then(function() {
+      assert.deepEqual(actual, [
+        "document-element-inserted -> " + uri,
+        "content-document-global-created -> data:text/html,iframe",
+        "DOMContentLoaded -> " + uri,
+        "document-element-inserted -> data:text/html,iframe",
+        "DOMContentLoaded -> data:text/html,iframe",
+        "load -> data:text/html,iframe",
+        "pageshow -> data:text/html,iframe",
+        "load -> " + uri,
+        "pageshow -> " + uri
+      ], "events where dispatched")
+    }, function(reason) {
+      assert.fail(Error(reason))
+    }).then(function() {
+      loader.unload();
+      done();
+    });
+};
+
+require("test").run(exports);
deleted file mode 100644
--- a/addon-sdk/source/test/test-content-proxy.js
+++ /dev/null
@@ -1,932 +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/. */
-
-const hiddenFrames = require("sdk/frame/hidden-frame");
-const xulApp = require("sdk/system/xul-app");
-
-const USE_JS_PROXIES = !xulApp.versionInRange(xulApp.platformVersion,
-                                              "17.0a2", "*");
-
-const { Loader } = require('sdk/test/loader');
-
-/*
- * Utility function that allow to easily run a proxy test with a clean
- * new HTML document. See first unit test for usage.
- */
-function createProxyTest(html, callback) {
-  return function (assert, done) {
-    let url = 'data:text/html;charset=utf-8,' + encodeURI(html);
-
-    let hiddenFrame = hiddenFrames.add(hiddenFrames.HiddenFrame({
-      onReady: function () {
-
-        function onDOMReady() {
-          hiddenFrame.element.removeEventListener("DOMContentLoaded", onDOMReady,
-                                                  false);
-
-          let xrayWindow = hiddenFrame.element.contentWindow;
-          let rawWindow = xrayWindow.wrappedJSObject;
-
-          let isDone = false;
-          let helper = {
-            xrayWindow: xrayWindow,
-            rawWindow: rawWindow,
-            createWorker: function (contentScript) {
-              return createWorker(assert, xrayWindow, contentScript, helper.done);
-            },
-            done: function () {
-              if (isDone)
-                return;
-              isDone = true;
-              hiddenFrames.remove(hiddenFrame);
-              done();
-            }
-          }
-          callback(helper, assert);
-        }
-
-        hiddenFrame.element.addEventListener("DOMContentLoaded", onDOMReady, false);
-        hiddenFrame.element.setAttribute("src", url);
-
-      }
-    }));
-  };
-}
-
-function createWorker(assert, xrayWindow, contentScript, done) {
-  // We have to use Sandboxed loader in order to get access to the private
-  // unlock key `PRIVATE_KEY`. This key should not be used anywhere else.
-  // See `PRIVATE_KEY` definition in worker.js
-  let loader = Loader(module);
-  let Worker = loader.require("sdk/content/worker").Worker;
-  let key = loader.sandbox("sdk/content/worker").PRIVATE_KEY;
-  let worker = Worker({
-    exposeUnlockKey : USE_JS_PROXIES ? key : null,
-    window: xrayWindow,
-    contentScript: [
-      'new ' + function () {
-        assert = function assert(v, msg) {
-          self.port.emit("assert", {assertion:v, msg:msg});
-        }
-        done = function done() {
-          self.port.emit("done");
-        }
-      },
-      contentScript
-    ]
-  });
-
-  worker.port.on("done", done);
-  worker.port.on("assert", function (data) {
-    assert.ok(data.assertion, data.msg);
-  });
-
-  return worker;
-}
-
-/* Examples for the `createProxyTest` uses */
-
-let html = "<script>var documentGlobal = true</script>";
-
-exports["test Create Proxy Test"] = createProxyTest(html, function (helper, assert) {
-  // You can get access to regular `test` object in second argument of
-  // `createProxyTest` method:
-  assert.ok(helper.rawWindow.documentGlobal,
-              "You have access to a raw window reference via `helper.rawWindow`");
-  assert.ok(!("documentGlobal" in helper.xrayWindow),
-              "You have access to an XrayWrapper reference via `helper.xrayWindow`");
-
-  // If you do not create a Worker, you have to call helper.done(),
-  // in order to say when your test is finished
-  helper.done();
-});
-
-exports["test Create Proxy Test With Worker"] = createProxyTest("", function (helper) {
-
-  helper.createWorker(
-    "new " + function WorkerScope() {
-      assert(true, "You can do assertions in your content script");
-      // And if you create a worker, you either have to call `done`
-      // from content script or helper.done()
-      done();
-    }
-  );
-
-});
-
-exports["test Create Proxy Test With Events"] = createProxyTest("", function (helper, assert) {
-
-  let worker = helper.createWorker(
-    "new " + function WorkerScope() {
-      self.port.emit("foo");
-    }
-  );
-
-  worker.port.on("foo", function () {
-    assert.pass("You can use events");
-    // And terminate your test with helper.done:
-    helper.done();
-  });
-
-});
-
-if (USE_JS_PROXIES) {
-  // Verify that the attribute `exposeUnlockKey`, that allow this test
-  // to identify proxies, works correctly.
-  // See `PRIVATE_KEY` definition in worker.js
-  exports["test Key Access"] = createProxyTest("", function(helper) {
-
-    helper.createWorker(
-      'new ' + function ContentScriptScope() {
-        assert("UNWRAP_ACCESS_KEY" in window, "have access to `UNWRAP_ACCESS_KEY`");
-        done();
-      }
-    );
-
-  });
-}
-
-// Bug 714778: There was some issue around `toString` functions
-//             that ended up being shared between content scripts
-exports["test Shared To String Proxies"] = createProxyTest("", function(helper) {
-
-  let worker = helper.createWorker(
-    'new ' + function ContentScriptScope() {
-      // We ensure that `toString` can't be modified so that nothing could
-      // leak to/from the document and between content scripts
-      // It only applies to JS proxies, there isn't any such issue with xrays.
-      //document.location.toString = function foo() {};
-      document.location.toString.foo = "bar";
-      if ('UNWRAP_ACCESS_KEY' in window)
-        assert(!("foo" in document.location.toString), "document.location.toString can't be modified");
-      else
-        assert("foo" in document.location.toString, "document.location.toString can be modified");
-      assert(document.location.toString() == "data:text/html;charset=utf-8,",
-             "First document.location.toString()");
-      self.postMessage("next");
-    }
-  );
-  worker.on("message", function () {
-    helper.createWorker(
-      'new ' + function ContentScriptScope2() {
-        assert(!("foo" in document.location.toString),
-               "document.location.toString is different for each content script");
-        assert(document.location.toString() == "data:text/html;charset=utf-8,",
-               "Second document.location.toString()");
-        done();
-      }
-    );
-  });
-});
-
-
-// Ensure that postMessage is working correctly across documents with an iframe
-let html = '<iframe id="iframe" name="test" src="data:text/html;charset=utf-8," />';
-exports["test postMessage"] = createProxyTest(html, function (helper, assert) {
-  let ifWindow = helper.xrayWindow.document.getElementById("iframe").contentWindow;
-  // Listen without proxies, to check that it will work in regular case
-  // simulate listening from a web document.
-  ifWindow.addEventListener("message", function listener(event) {
-    ifWindow.removeEventListener("message", listener, false);
-    // As we are in system principal, event is an XrayWrapper
-    if (USE_JS_PROXIES) {
-      assert.equal(event.source, ifWindow,
-                       "event.source is the iframe window");
-    }
-    else {
-      // JS proxies had different behavior than xrays, xrays use current
-      // compartments when calling postMessage method. Whereas js proxies
-      // was using postMessage method compartment, not the caller one.
-      assert.equal(event.source, helper.xrayWindow,
-                       "event.source is the top window");
-    }
-    assert.equal(event.origin, "null", "origin is null");
-
-    assert.equal(event.data, "{\"foo\":\"bar\\n \\\"escaped\\\".\"}",
-                     "message data is correct");
-
-    helper.done();
-  }, false);
-
-  helper.createWorker(
-    'new ' + function ContentScriptScope() {
-      assert(postMessage === postMessage,
-          "verify that we doesn't generate multiple functions for the same method");
-
-      var json = JSON.stringify({foo : "bar\n \"escaped\"."});
-
-      document.getElementById("iframe").contentWindow.postMessage(json, "*");
-    }
-  );
-});
-
-let html = '<input id="input2" type="checkbox" />';
-exports["test Object Listener"] = createProxyTest(html, function (helper) {
-
-  helper.createWorker(
-    'new ' + function ContentScriptScope() {
-      // Test objects being given as event listener
-      let input = document.getElementById("input2");
-      let myClickListener = {
-        called: false,
-        handleEvent: function(event) {
-          assert(this === myClickListener, "`this` is the original object");
-          assert(!this.called, "called only once");
-          this.called = true;
-          if ('UNWRAP_ACCESS_KEY' in window)
-            assert(event.valueOf() !== event.valueOf(UNWRAP_ACCESS_KEY), "event is wrapped");
-          assert(event.target, input, "event.target is the wrapped window");
-          done();
-        }
-      };
-
-      window.addEventListener("click", myClickListener, true);
-      input.click();
-      window.removeEventListener("click", myClickListener, true);
-    }
-  );
-
-});
-
-exports["test Object Listener 2"] = createProxyTest("", function (helper) {
-
-  helper.createWorker(
-    'new ' + function ContentScriptScope() {
-      // Verify object as DOM event listener
-      let myMessageListener = {
-        called: false,
-        handleEvent: function(event) {
-          window.removeEventListener("message", myMessageListener, true);
-
-          assert(this == myMessageListener, "`this` is the original object");
-          assert(!this.called, "called only once");
-          this.called = true;
-          if ('UNWRAP_ACCESS_KEY' in window)
-            assert(event.valueOf() !== event.valueOf(UNWRAP_ACCESS_KEY), "event is wrapped");
-          assert(event.target == document.defaultView, "event.target is the wrapped window");
-          assert(event.source == document.defaultView, "event.source is the wrapped window");
-          assert(event.origin == "null", "origin is null");
-          assert(event.data == "ok", "message data is correct");
-          done();
-        }
-      };
-
-      window.addEventListener("message", myMessageListener, true);
-      document.defaultView.postMessage("ok", '*');
-    }
-  );
-
-});
-
-let html = '<input id="input" type="text" /><input id="input3" type="checkbox" />' +
-             '<input id="input2" type="checkbox" />';
-
-/* Disable test to keep tree green until Bug 756214 is fixed.
-exports.testStringOverload = createProxyTest(html, function (helper, test) {
-  // Proxy - toString error
-  let originalString = "string";
-  let p = Proxy.create({
-    get: function(receiver, name) {
-      if (name == "binded")
-        return originalString.toString.bind(originalString);
-      return originalString[name];
-    }
-  });
-  assert.okRaises(function () {
-    p.toString();
-  },
-  /String.prototype.toString called on incompatible Proxy/,
-  "toString can't be called with this being the proxy");
-  assert.equal(p.binded(), "string", "but it works if we bind this to the original string");
-
-  helper.createWorker(
-    'new ' + function ContentScriptScope() {
-      // RightJS is hacking around String.prototype, and do similar thing:
-      // Pass `this` from a String prototype method.
-      // It is funny because typeof this == "object"!
-      // So that when we pass `this` to a native method,
-      // our proxy code can fail on another even more crazy thing.
-      // See following test to see what fails around proxies.
-      String.prototype.update = function () {
-        assert(typeof this == "object", "in update, `this` is an object");
-        assert(this.toString() == "input", "in update, `this.toString works");
-        return document.querySelectorAll(this);
-      };
-      assert("input".update().length == 3, "String.prototype overload works");
-      done();
-    }
-  );
-});
-*/
-
-exports["test MozMatchedSelector"] = createProxyTest("", function (helper) {
-  helper.createWorker(
-    'new ' + function ContentScriptScope() {
-      // Check mozMatchesSelector XrayWrappers bug:
-      // mozMatchesSelector returns bad results when we are not calling it from the node itself
-      // SEE BUG 658909: mozMatchesSelector returns incorrect results with XrayWrappers
-      assert(document.createElement( "div" ).mozMatchesSelector("div"),
-             "mozMatchesSelector works while being called from the node");
-      assert(document.documentElement.mozMatchesSelector.call(
-               document.createElement( "div" ),
-               "div"
-             ),
-             "mozMatchesSelector works while being called from a " +
-             "function reference to " +
-             "document.documentElement.mozMatchesSelector.call");
-      done();
-    }
-  );
-});
-
-exports["test Events Overload"] = createProxyTest("", function (helper) {
-
-  helper.createWorker(
-    'new ' + function ContentScriptScope() {
-      // If we add a "____proxy" attribute on XrayWrappers in order to store
-      // the related proxy to create an unique proxy for each wrapper;
-      // we end up setting this attribute to prototype objects :x
-      // And so, instances created with such prototype will be considered
-      // as equal to the prototype ...
-      //   // Internal method that return the proxy for a given XrayWrapper
-      //   function proxify(obj) {
-      //     if (obj._proxy) return obj._proxy;
-      //     return obj._proxy = Proxy.create(...);
-      //   }
-      //
-      //   // Get a proxy of an XrayWrapper prototype object
-      //   let proto = proxify(xpcProto);
-      //
-      //   // Use this proxy as a prototype
-      //   function Constr() {}
-      //   Constr.proto = proto;
-      //
-      //   // Try to create an instance using this prototype
-      //   let xpcInstance = new Constr();
-      //   let wrapper = proxify(xpcInstance)
-      //
-      //   xpcProto._proxy = proto and as xpcInstance.__proto__ = xpcProto,
-      //   xpcInstance._proxy = proto ... and profixy(xpcInstance) = proto :(
-      //
-      let proto = window.document.createEvent('HTMLEvents').__proto__;
-      window.Event.prototype = proto;
-      let event = document.createEvent('HTMLEvents');
-      assert(event !== proto, "Event should not be equal to its prototype");
-      event.initEvent('dataavailable', true, true);
-      assert(event.type === 'dataavailable', "Events are working fine");
-      done();
-    }
-  );
-
-});
-
-exports["test Nested Attributes"] = createProxyTest("", function (helper) {
-
-  helper.createWorker(
-    'new ' + function ContentScriptScope() {
-      // XrayWrappers has a bug when you set an attribute on it,
-      // in some cases, it creates an unnecessary wrapper that introduces
-      // a different object that refers to the same original object
-      // Check that our wrappers don't reproduce this bug
-      // SEE BUG 658560: Fix identity problem with CrossOriginWrappers
-      let o = {sandboxObject:true};
-      window.nested = o;
-      o.foo = true;
-      assert(o === window.nested, "Nested attribute to sandbox object should not be proxified");
-      window.nested = document;
-      assert(window.nested === document, "Nested attribute to proxy should not be double proxified");
-      done();
-    }
-  );
-
-});
-
-exports["test Form nodeName"] = createProxyTest("", function (helper) {
-
-  helper.createWorker(
-    'new ' + function ContentScriptScope() {
-      let body = document.body;
-      // Check form[nodeName]
-      let form = document.createElement("form");
-      let input = document.createElement("input");
-      input.setAttribute("name", "test");
-      form.appendChild(input);
-      body.appendChild(form);
-      assert(form.test == input, "form[nodeName] is valid");
-      body.removeChild(form);
-      done();
-    }
-  );
-
-});
-
-exports["test localStorage"] = createProxyTest("", function (helper, assert) {
-
-  let worker = helper.createWorker(
-    'new ' + function ContentScriptScope() {
-      // Check localStorage:
-      assert(window.localStorage, "has access to localStorage");
-      window.localStorage.name = 1;
-      assert(window.localStorage.name == 1, "localStorage appears to work");
-
-      self.port.on("step2", function () {
-        window.localStorage.clear();
-        assert(window.localStorage.name == undefined, "localStorage really, really works");
-        done();
-      });
-      self.port.emit("step1");
-    }
-  );
-
-  worker.port.on("step1", function () {
-    assert.equal(helper.rawWindow.localStorage.name, 1, "localStorage really works");
-    worker.port.emit("step2");
-  });
-
-});
-
-exports["test Auto Unwrap Custom Attributes"] = createProxyTest("", function (helper) {
-
-  helper.createWorker(
-    'new ' + function ContentScriptScope() {
-      let body = document.body;
-      // Setting a custom object to a proxy attribute is not wrapped when we get it afterward
-      let object = {custom: true, enumerable: false};
-      body.customAttribute = object;
-      if ('UNWRAP_ACCESS_KEY' in window)
-        assert(body.customAttribute.valueOf() === body.customAttribute.valueOf(UNWRAP_ACCESS_KEY), "custom JS attributes are not wrapped");
-      assert(object === body.customAttribute, "custom JS attributes are not wrapped");
-      done();
-    }
-  );
-
-});
-
-exports["test Object Tag"] = createProxyTest("", function (helper) {
-
-  helper.createWorker(
-    'new ' + function ContentScriptScope() {
-      // <object>, <embed> and other tags return typeof 'function'
-      let flash = document.createElement("object");
-      assert(typeof flash == "function", "<object> is typeof 'function'");
-      assert(flash.toString().match(/\[object HTMLObjectElement.*\]/), "<object> is HTMLObjectElement");
-      assert("setAttribute" in flash, "<object> has a setAttribute method");
-      done();
-    }
-  );
-
-});
-
-exports["test Highlight toString Behavior"] = createProxyTest("", function (helper, assert) {
-  // We do not have any workaround this particular use of toString
-  // applied on <object> elements. So disable this test until we found one!
-  //assert.equal(helper.rawWindow.Object.prototype.toString.call(flash), "[object HTMLObjectElement]", "<object> is HTMLObjectElement");
-  function f() {};
-  let funToString = Object.prototype.toString.call(f);
-  assert.ok(/\[object Function.*\]/.test(funToString), "functions are functions 1");
-
-  // This is how jquery call toString:
-  let strToString = helper.rawWindow.Object.prototype.toString.call("");
-  assert.ok(/\[object String.*\]/.test(strToString), "strings are strings");
-
-  let o = {__exposedProps__:{}};
-  let objToString = helper.rawWindow.Object.prototype.toString.call(o);
-  assert.ok(/\[object Object.*\]/.test(objToString), "objects are objects");
-
-  // Make sure to pass a function from the same compartments
-  // or toString will return [object Object] on FF8+
-  let f2 = helper.rawWindow.eval("(function () {})");
-  let funToString2 = helper.rawWindow.Object.prototype.toString.call(f2);
-  assert.ok(/\[object Function.*\]/.test(funToString2), "functions are functions 2");
-
-  helper.done();
-});
-
-exports["test Document TagName"] = createProxyTest("", function (helper) {
-
-  helper.createWorker(
-    'new ' + function ContentScriptScope() {
-      let body = document.body;
-      // Check document[tagName]
-      let div = document.createElement("div");
-      div.setAttribute("name", "test");
-      body.appendChild(div);
-      assert(!document.test, "document[divName] is undefined");
-      body.removeChild(div);
-
-      let form = document.createElement("form");
-      form.setAttribute("name", "test");
-      body.appendChild(form);
-      assert(document.test == form, "document[formName] is valid");
-      body.removeChild(form);
-
-      let img = document.createElement("img");
-      img.setAttribute("name", "test");
-      body.appendChild(img);
-      assert(document.test == img, "document[imgName] is valid");
-      body.removeChild(img);
-      done();
-    }
-  );
-
-});
-
-let html = '<iframe id="iframe" name="test" src="data:text/html;charset=utf-8," />';
-exports["test Window Frames"] = createProxyTest(html, function (helper) {
-
-  helper.createWorker(
-    'let glob = this; new ' + function ContentScriptScope() {
-      // Check window[frameName] and window.frames[i]
-      let iframe = document.getElementById("iframe");
-      //assert(window.frames.length == 1, "The iframe is reported in window.frames check1");
-      //assert(window.frames[0] == iframe.contentWindow, "The iframe is reported in window.frames check2");
-      //console.log(window.test+ "-"+iframe.contentWindow);
-      //console.log(window);
-      assert(window.test == iframe.contentWindow, "window[frameName] is valid");
-      done();
-    }
-  );
-
-});
-
-exports["test Collections"] = createProxyTest("", function (helper) {
-
-  helper.createWorker(
-    'new ' + function ContentScriptScope() {
-      // Highlight XPCNativeWrapper bug with HTMLCollection
-      // tds[0] is only defined on first access :o
-      let body = document.body;
-      let div = document.createElement("div");
-      body.appendChild(div);
-      div.innerHTML = "<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>";
-      let tds = div.getElementsByTagName("td");
-      assert(tds[0] == tds[0], "We can get array element multiple times");
-      body.removeChild(div);
-      done();
-    }
-  );
-
-});
-
-let html = '<input id="input" type="text" /><input id="input3" type="checkbox" />' +
-             '<input id="input2" type="checkbox" />';
-exports["test Collections 2"] = createProxyTest(html, function (helper) {
-
-  helper.createWorker(
-    'new ' + function ContentScriptScope() {
-      // Verify that NodeList/HTMLCollection are working fine
-      let body = document.body;
-      let inputs = body.getElementsByTagName("input");
-      assert(body.childNodes.length == 3, "body.childNodes length is correct");
-      assert(inputs.length == 3, "inputs.length is correct");
-      assert(body.childNodes[0] == inputs[0], "body.childNodes[0] is correct");
-      assert(body.childNodes[1] == inputs[1], "body.childNodes[1] is correct");
-      assert(body.childNodes[2] == inputs[2], "body.childNodes[2] is correct");
-      let count = 0;
-      for(let i in body.childNodes) {
-        count++;
-      }
-      // JS proxies were broken, we can iterate over some other items:
-      // length, item and iterator
-      let expectedCount;
-      if ('UNWRAP_ACCESS_KEY' in window)
-        expectedCount = 3;
-      else
-        expectedCount = 6;
-      assert(count == expectedCount, "body.childNodes is iterable");
-      done();
-    }
-  );
-
-});
-
-exports["test valueOf"] = createProxyTest("", function (helper) {
-
-  if (USE_JS_PROXIES) {
-    helper.createWorker(
-      'new ' + function ContentScriptScope() {
-        // Check internal use of valueOf() for JS proxies API
-        assert(/\[object Window.*\]/.test(window.valueOf().toString()),
-               "proxy.valueOf() returns the wrapped version");
-        assert(/\[object Window.*\]/.test(window.valueOf({}).toString()),
-               "proxy.valueOf({}) returns the wrapped version");
-        done();
-      }
-    );
-  }
-  else {
-    helper.createWorker(
-      'new ' + function ContentScriptScope() {
-        // Bug 787013: Until this bug is fixed, we are missing some methods
-        // on JS objects that comes from global `Object` object
-        assert(!('valueOf' in window), "valueOf is missing");
-        assert(!('toLocateString' in window), "toLocaleString is missing");
-        done();
-      }
-    );
-  }
-
-});
-
-exports["test XMLHttpRequest"] = createProxyTest("", function (helper) {
-
-  helper.createWorker(
-    'new ' + function ContentScriptScope() {
-      // XMLHttpRequest doesn't support XMLHttpRequest.apply,
-      // that may break our proxy code
-      assert(window.XMLHttpRequest(), "we are able to instantiate XMLHttpRequest object");
-      done();
-    }
-  );
-
-});
-
-exports["test XPathResult"] = createProxyTest("", function (helper, assert) {
-  const XPathResultTypes = ["ANY_TYPE",
-                            "NUMBER_TYPE", "STRING_TYPE", "BOOLEAN_TYPE",
-                            "UNORDERED_NODE_ITERATOR_TYPE",
-                            "ORDERED_NODE_ITERATOR_TYPE",
-                            "UNORDERED_NODE_SNAPSHOT_TYPE",
-                            "ORDERED_NODE_SNAPSHOT_TYPE",
-                            "ANY_UNORDERED_NODE_TYPE",
-                            "FIRST_ORDERED_NODE_TYPE"];
-
-  // Check XPathResult bug with constants being undefined on XPCNativeWrapper
-  let xpcXPathResult = helper.xrayWindow.XPathResult;
-
-  XPathResultTypes.forEach(function(type, i) {
-    assert.equal(xpcXPathResult.wrappedJSObject[type],
-                     helper.rawWindow.XPathResult[type],
-                     "XPathResult's constants are valid on unwrapped node");
-
-    assert.equal(xpcXPathResult[type], i,
-                     "XPathResult's constants are defined on " +
-                     "XPCNativeWrapper (platform bug #)");
-  });
-
-  let value = helper.rawWindow.XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE;
-  let worker = helper.createWorker(
-    'new ' + function ContentScriptScope() {
-      self.port.on("value", function (value) {
-        // Check that our work around is working:
-        assert(window.XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE === value,
-               "XPathResult works correctly on Proxies");
-        done();
-      });
-    }
-  );
-  worker.port.emit("value", value);
-});
-
-exports["test Prototype Inheritance"] = createProxyTest("", function (helper) {
-
-  helper.createWorker(
-    'new ' + function ContentScriptScope() {
-      // Verify that inherited prototype function like initEvent
-      // are handled correctly. (e2.type will return an error if it's not the case)
-      let event1 = document.createEvent( 'MouseEvents' );
-      event1.initEvent( "click", true, true );
-      let event2 = document.createEvent( 'MouseEvents' );
-      event2.initEvent( "click", true, true );
-      assert(event2.type == "click", "We are able to create an event");
-      done();
-    }
-  );
-
-});
-
-exports["test Functions"] = createProxyTest("", function (helper) {
-
-  helper.rawWindow.callFunction = function callFunction(f) f();
-  helper.rawWindow.isEqual = function isEqual(a, b) a == b;
-  // bug 784116: workaround in order to allow proxy code to cache proxies on
-  // these functions:
-  helper.rawWindow.callFunction.__exposedProps__ = {__proxy: 'rw'};
-  helper.rawWindow.isEqual.__exposedProps__ = {__proxy: 'rw'};
-
-  helper.createWorker(
-    'new ' + function ContentScriptScope() {
-      // Check basic usage of functions
-      let closure2 = function () {return "ok";};
-      assert(window.wrappedJSObject.callFunction(closure2) == "ok", "Function references work");
-
-      // Ensure that functions are cached when being wrapped to native code
-      let closure = function () {};
-      assert(window.wrappedJSObject.isEqual(closure, closure), "Function references are cached before being wrapped to native");
-      done();
-    }
-  );
-
-});
-
-let html = '<input id="input2" type="checkbox" />';
-exports["test Listeners"] = createProxyTest(html, function (helper) {
-
-  helper.createWorker(
-    'new ' + function ContentScriptScope() {
-      // Verify listeners:
-      let input = document.getElementById("input2");
-      assert(input, "proxy.getElementById works");
-
-      function onclick() {};
-      input.onclick = onclick;
-      assert(input.onclick === onclick, "on* attributes are equal to original function set");
-
-      let addEventListenerCalled = false;
-      let expandoCalled = false;
-      input.addEventListener("click", function onclick(event) {
-        input.removeEventListener("click", onclick, true);
-
-        assert(!addEventListenerCalled, "closure given to addEventListener is called once");
-        if (addEventListenerCalled)
-          return;
-        addEventListenerCalled = true;
-
-        assert(!event.target.ownerDocument.defaultView.documentGlobal, "event object is still wrapped and doesn't expose document globals");
-        if ('UNWRAP_ACCESS_KEY' in window)
-          assert("__isWrappedProxy" in event.target, "event object is a proxy");
-
-        let input2 = document.getElementById("input2");
-
-        input.onclick = function (event) {
-          input.onclick = null;
-          assert(!expandoCalled, "closure set to expando is called once");
-          if (expandoCalled) return;
-          expandoCalled = true;
-
-          assert(!event.target.ownerDocument.defaultView.documentGlobal, "event object is still wrapped and doesn't expose document globals");
-          if ('UNWRAP_ACCESS_KEY' in window)
-            assert("__isWrappedProxy" in event.target, "event object is a proxy");
-
-          setTimeout(function () {
-            input.click();
-            done();
-          }, 0);
-
-        }
-
-        setTimeout(function () {
-          input.click();
-        }, 0);
-
-      }, true);
-
-      input.click();
-    }
-  );
-
-});
-
-exports["test MozRequestAnimationFrame"] = createProxyTest("", function (helper) {
-
-  helper.createWorker(
-    'new ' + function ContentScriptScope() {
-      window.mozRequestAnimationFrame(function callback() {
-        assert(callback == this, "callback is equal to `this`");
-        done();
-      });
-    }
-  );
-
-});
-
-exports["testGlobalScope"] = createProxyTest("", function (helper) {
-
-  helper.createWorker(
-    'let toplevelScope = true;' +
-    'assert(window.toplevelScope, "variables in toplevel scope are set to `window` object");' +
-    'assert(this.toplevelScope, "variables in toplevel scope are set to `this` object");' +
-    'done();'
-  );
-
-});
-
-if (USE_JS_PROXIES) {
-  // Bug 671016: Typed arrays should not be proxified
-  exports["test Typed ArraysX"] = createProxyTest("", function (helper) {
-
-    helper.createWorker(
-      'new ' + function ContentScriptScope() {
-        let canvas = document.createElement("canvas");
-        let context = canvas.getContext("2d");
-        let imageData = context.getImageData(0,0, 1, 1);
-        let unwrappedData;
-        if ('UNWRAP_ACCESS_KEY' in window)
-          unwrappedData = imageData.valueOf(UNWRAP_ACCESS_KEY).data
-        else
-          unwrappedData = imageData.wrappedJSObject.data;
-        let data = imageData.data;
-        dump(unwrappedData+" === "+data+"\n");
-        assert(unwrappedData === data, "Typed array isn't proxified")
-        done();
-      }
-    );
-
-  });
-}
-
-// Bug 715755: proxy code throw an exception on COW
-// Create an http server in order to simulate real cross domain documents
-exports["test Cross Domain Iframe"] = createProxyTest("", function (helper) {
-  let serverPort = 8099;
-  let server = require("sdk/test/httpd").startServerAsync(serverPort);
-  server.registerPathHandler("/", function handle(request, response) {
-    // Returns the webpage that receive a message and forward it back to its
-    // parent document by appending ' world'.
-    let content = "<html><head><meta charset='utf-8'></head>\n";
-    content += "<script>\n";
-    content += "  window.addEventListener('message', function (event) {\n";
-    content += "    parent.postMessage(event.data + ' world', '*');\n";
-    content += "  }, true);\n";
-    content += "</script>\n";
-    content += "<body></body>\n";
-    content += "</html>\n";
-    response.write(content);
-  });
-
-  let worker = helper.createWorker(
-    'new ' + function ContentScriptScope() {
-      // Waits for the server page url
-      self.on("message", function (url) {
-        // Creates an iframe with this page
-        let iframe = document.createElement("iframe");
-        iframe.addEventListener("load", function onload() {
-          iframe.removeEventListener("load", onload, true);
-          try {
-            // Try to communicate with iframe's content
-            window.addEventListener("message", function onmessage(event) {
-              window.removeEventListener("message", onmessage, true);
-
-              assert(event.data == "hello world", "COW works properly");
-              self.port.emit("end");
-            }, true);
-            iframe.contentWindow.postMessage("hello", "*");
-          } catch(e) {
-            assert(false, "COW fails : "+e.message);
-          }
-        }, true);
-        iframe.setAttribute("src", url);
-        document.body.appendChild(iframe);
-      });
-    }
-  );
-
-  worker.port.on("end", function () {
-    server.stop(helper.done);
-  });
-
-  worker.postMessage("http://localhost:" + serverPort + "/");
-
-});
-
-// Bug 769006: Ensure that MutationObserver works fine with proxies
-let html = '<a href="foo">link</a>';
-exports["test MutationObvserver"] = createProxyTest(html, function (helper) {
-
-  helper.createWorker(
-    'new ' + function ContentScriptScope() {
-      if (typeof MutationObserver == "undefined") {
-        assert(true, "No MutationObserver for this FF version");
-        done();
-        return;
-      }
-      let link = document.getElementsByTagName("a")[0];
-
-      // Register a Mutation observer
-      let obs = new MutationObserver(function(mutations){
-        // Ensure that mutation data are valid
-        assert(mutations.length == 1, "only one attribute mutation");
-        let mutation = mutations[0];
-        assert(mutation.type == "attributes", "check `type`");
-        assert(mutation.target == link, "check `target`");
-        assert(mutation.attributeName == "href", "check `attributeName`");
-        assert(mutation.oldValue == "foo", "check `oldValue`");
-        obs.disconnect();
-        done();
-      });
-      obs.observe(document, {
-        subtree: true,
-        attributes: true,
-        attributeOldValue: true,
-        attributeFilter: ["href"]
-      });
-
-      // Modify the DOM
-      link.setAttribute("href", "bar");
-    }
-  );
-
-});
-
-if (require("sdk/system/xul-app").is("Fennec")) {
-  module.exports = {
-    "test Unsupported Test": function UnsupportedTest (assert) {
-        assert.pass(
-          "Skipping this test until Fennec support is implemented." +
-          "See bug 806813");
-    }
-  }
-}
-
-require("test").run(exports);
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/test/test-content-script.js
@@ -0,0 +1,843 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const hiddenFrames = require("sdk/frame/hidden-frame");
+
+const { Loader } = require('sdk/test/loader');
+
+/*
+ * Utility function that allow to easily run a proxy test with a clean
+ * new HTML document. See first unit test for usage.
+ */
+function createProxyTest(html, callback) {
+  return function (assert, done) {
+    let url = 'data:text/html;charset=utf-8,' + encodeURI(html);
+
+    let hiddenFrame = hiddenFrames.add(hiddenFrames.HiddenFrame({
+      onReady: function () {
+
+        function onDOMReady() {
+          hiddenFrame.element.removeEventListener("DOMContentLoaded", onDOMReady,
+                                                  false);
+
+          let xrayWindow = hiddenFrame.element.contentWindow;
+          let rawWindow = xrayWindow.wrappedJSObject;
+
+          let isDone = false;
+          let helper = {
+            xrayWindow: xrayWindow,
+            rawWindow: rawWindow,
+            createWorker: function (contentScript) {
+              return createWorker(assert, xrayWindow, contentScript, helper.done);
+            },
+            done: function () {
+              if (isDone)
+                return;
+              isDone = true;
+              hiddenFrames.remove(hiddenFrame);
+              done();
+            }
+          }
+          callback(helper, assert);
+        }
+
+        hiddenFrame.element.addEventListener("DOMContentLoaded", onDOMReady, false);
+        hiddenFrame.element.setAttribute("src", url);
+
+      }
+    }));
+  };
+}
+
+function createWorker(assert, xrayWindow, contentScript, done) {
+  let loader = Loader(module);
+  let Worker = loader.require("sdk/content/worker").Worker;
+  let worker = Worker({
+    window: xrayWindow,
+    contentScript: [
+      'new ' + function () {
+        assert = function assert(v, msg) {
+          self.port.emit("assert", {assertion:v, msg:msg});
+        }
+        done = function done() {
+          self.port.emit("done");
+        }
+      },
+      contentScript
+    ]
+  });
+
+  worker.port.on("done", done);
+  worker.port.on("assert", function (data) {
+    assert.ok(data.assertion, data.msg);
+  });
+
+  return worker;
+}
+
+/* Examples for the `createProxyTest` uses */
+
+let html = "<script>var documentGlobal = true</script>";
+
+exports["test Create Proxy Test"] = createProxyTest(html, function (helper, assert) {
+  // You can get access to regular `test` object in second argument of
+  // `createProxyTest` method:
+  assert.ok(helper.rawWindow.documentGlobal,
+              "You have access to a raw window reference via `helper.rawWindow`");
+  assert.ok(!("documentGlobal" in helper.xrayWindow),
+              "You have access to an XrayWrapper reference via `helper.xrayWindow`");
+
+  // If you do not create a Worker, you have to call helper.done(),
+  // in order to say when your test is finished
+  helper.done();
+});
+
+exports["test Create Proxy Test With Worker"] = createProxyTest("", function (helper) {
+
+  helper.createWorker(
+    "new " + function WorkerScope() {
+      assert(true, "You can do assertions in your content script");
+      // And if you create a worker, you either have to call `done`
+      // from content script or helper.done()
+      done();
+    }
+  );
+
+});
+
+exports["test Create Proxy Test With Events"] = createProxyTest("", function (helper, assert) {
+
+  let worker = helper.createWorker(
+    "new " + function WorkerScope() {
+      self.port.emit("foo");
+    }
+  );
+
+  worker.port.on("foo", function () {
+    assert.pass("You can use events");
+    // And terminate your test with helper.done:
+    helper.done();
+  });
+
+});
+
+// Bug 714778: There was some issue around `toString` functions
+//             that ended up being shared between content scripts
+exports["test Shared To String Proxies"] = createProxyTest("", function(helper) {
+
+  let worker = helper.createWorker(
+    'new ' + function ContentScriptScope() {
+      // We ensure that `toString` can't be modified so that nothing could
+      // leak to/from the document and between content scripts
+      // It only applies to JS proxies, there isn't any such issue with xrays.
+      //document.location.toString = function foo() {};
+      document.location.toString.foo = "bar";
+      assert("foo" in document.location.toString, "document.location.toString can be modified");
+      assert(document.location.toString() == "data:text/html;charset=utf-8,",
+             "First document.location.toString()");
+      self.postMessage("next");
+    }
+  );
+  worker.on("message", function () {
+    helper.createWorker(
+      'new ' + function ContentScriptScope2() {
+        assert(!("foo" in document.location.toString),
+               "document.location.toString is different for each content script");
+        assert(document.location.toString() == "data:text/html;charset=utf-8,",
+               "Second document.location.toString()");
+        done();
+      }
+    );
+  });
+});
+
+
+// Ensure that postMessage is working correctly across documents with an iframe
+let html = '<iframe id="iframe" name="test" src="data:text/html;charset=utf-8," />';
+exports["test postMessage"] = createProxyTest(html, function (helper, assert) {
+  let ifWindow = helper.xrayWindow.document.getElementById("iframe").contentWindow;
+  // Listen without proxies, to check that it will work in regular case
+  // simulate listening from a web document.
+  ifWindow.addEventListener("message", function listener(event) {
+    ifWindow.removeEventListener("message", listener, false);
+    // As we are in system principal, event is an XrayWrapper
+    // xrays use current compartments when calling postMessage method.
+    // Whereas js proxies was using postMessage method compartment,
+    // not the caller one.
+    assert.equal(event.source, helper.xrayWindow,
+                 "event.source is the top window");
+    assert.equal(event.origin, "null", "origin is null");
+
+    assert.equal(event.data, "{\"foo\":\"bar\\n \\\"escaped\\\".\"}",
+                     "message data is correct");
+
+    helper.done();
+  }, false);
+
+  helper.createWorker(
+    'new ' + function ContentScriptScope() {
+      assert(postMessage === postMessage,
+          "verify that we doesn't generate multiple functions for the same method");
+
+      var json = JSON.stringify({foo : "bar\n \"escaped\"."});
+
+      document.getElementById("iframe").contentWindow.postMessage(json, "*");
+    }
+  );
+});
+
+let html = '<input id="input2" type="checkbox" />';
+exports["test Object Listener"] = createProxyTest(html, function (helper) {
+
+  helper.createWorker(
+    'new ' + function ContentScriptScope() {
+      // Test objects being given as event listener
+      let input = document.getElementById("input2");
+      let myClickListener = {
+        called: false,
+        handleEvent: function(event) {
+          assert(this === myClickListener, "`this` is the original object");
+          assert(!this.called, "called only once");
+          this.called = true;
+          assert(event.target, input, "event.target is the wrapped window");
+          done();
+        }
+      };
+
+      window.addEventListener("click", myClickListener, true);
+      input.click();
+      window.removeEventListener("click", myClickListener, true);
+    }
+  );
+
+});
+
+exports["test Object Listener 2"] = createProxyTest("", function (helper) {
+
+  helper.createWorker(
+    'new ' + function ContentScriptScope() {
+      // Verify object as DOM event listener
+      let myMessageListener = {
+        called: false,
+        handleEvent: function(event) {
+          window.removeEventListener("message", myMessageListener, true);
+
+          assert(this == myMessageListener, "`this` is the original object");
+          assert(!this.called, "called only once");
+          this.called = true;
+          assert(event.target == document.defaultView, "event.target is the wrapped window");
+          assert(event.source == document.defaultView, "event.source is the wrapped window");
+          assert(event.origin == "null", "origin is null");
+          assert(event.data == "ok", "message data is correct");
+          done();
+        }
+      };
+
+      window.addEventListener("message", myMessageListener, true);
+      document.defaultView.postMessage("ok", '*');
+    }
+  );
+
+});
+
+let html = '<input id="input" type="text" /><input id="input3" type="checkbox" />' +
+             '<input id="input2" type="checkbox" />';
+
+/* Disable test to keep tree green until Bug 756214 is fixed.
+exports.testStringOverload = createProxyTest(html, function (helper, test) {
+  // Proxy - toString error
+  let originalString = "string";
+  let p = Proxy.create({
+    get: function(receiver, name) {
+      if (name == "binded")
+        return originalString.toString.bind(originalString);
+      return originalString[name];
+    }
+  });
+  assert.okRaises(function () {
+    p.toString();
+  },
+  /String.prototype.toString called on incompatible Proxy/,
+  "toString can't be called with this being the proxy");
+  assert.equal(p.binded(), "string", "but it works if we bind this to the original string");
+
+  helper.createWorker(
+    'new ' + function ContentScriptScope() {
+      // RightJS is hacking around String.prototype, and do similar thing:
+      // Pass `this` from a String prototype method.
+      // It is funny because typeof this == "object"!
+      // So that when we pass `this` to a native method,
+      // our proxy code can fail on another even more crazy thing.
+      // See following test to see what fails around proxies.
+      String.prototype.update = function () {
+        assert(typeof this == "object", "in update, `this` is an object");
+        assert(this.toString() == "input", "in update, `this.toString works");
+        return document.querySelectorAll(this);
+      };
+      assert("input".update().length == 3, "String.prototype overload works");
+      done();
+    }
+  );
+});
+*/
+
+exports["test MozMatchedSelector"] = createProxyTest("", function (helper) {
+  helper.createWorker(
+    'new ' + function ContentScriptScope() {
+      // Check mozMatchesSelector XrayWrappers bug:
+      // mozMatchesSelector returns bad results when we are not calling it from the node itself
+      // SEE BUG 658909: mozMatchesSelector returns incorrect results with XrayWrappers
+      assert(document.createElement( "div" ).mozMatchesSelector("div"),
+             "mozMatchesSelector works while being called from the node");
+      assert(document.documentElement.mozMatchesSelector.call(
+               document.createElement( "div" ),
+               "div"
+             ),
+             "mozMatchesSelector works while being called from a " +
+             "function reference to " +
+             "document.documentElement.mozMatchesSelector.call");
+      done();
+    }
+  );
+});
+
+exports["test Events Overload"] = createProxyTest("", function (helper) {
+
+  helper.createWorker(
+    'new ' + function ContentScriptScope() {
+      // If we add a "____proxy" attribute on XrayWrappers in order to store
+      // the related proxy to create an unique proxy for each wrapper;
+      // we end up setting this attribute to prototype objects :x
+      // And so, instances created with such prototype will be considered
+      // as equal to the prototype ...
+      //   // Internal method that return the proxy for a given XrayWrapper
+      //   function proxify(obj) {
+      //     if (obj._proxy) return obj._proxy;
+      //     return obj._proxy = Proxy.create(...);
+      //   }
+      //
+      //   // Get a proxy of an XrayWrapper prototype object
+      //   let proto = proxify(xpcProto);
+      //
+      //   // Use this proxy as a prototype
+      //   function Constr() {}
+      //   Constr.proto = proto;
+      //
+      //   // Try to create an instance using this prototype
+      //   let xpcInstance = new Constr();
+      //   let wrapper = proxify(xpcInstance)
+      //
+      //   xpcProto._proxy = proto and as xpcInstance.__proto__ = xpcProto,
+      //   xpcInstance._proxy = proto ... and profixy(xpcInstance) = proto :(
+      //
+      let proto = window.document.createEvent('HTMLEvents').__proto__;
+      window.Event.prototype = proto;
+      let event = document.createEvent('HTMLEvents');
+      assert(event !== proto, "Event should not be equal to its prototype");
+      event.initEvent('dataavailable', true, true);
+      assert(event.type === 'dataavailable', "Events are working fine");
+      done();
+    }
+  );
+
+});
+
+exports["test Nested Attributes"] = createProxyTest("", function (helper) {
+
+  helper.createWorker(
+    'new ' + function ContentScriptScope() {
+      // XrayWrappers has a bug when you set an attribute on it,
+      // in some cases, it creates an unnecessary wrapper that introduces
+      // a different object that refers to the same original object
+      // Check that our wrappers don't reproduce this bug
+      // SEE BUG 658560: Fix identity problem with CrossOriginWrappers
+      let o = {sandboxObject:true};
+      window.nested = o;
+      o.foo = true;
+      assert(o === window.nested, "Nested attribute to sandbox object should not be proxified");
+      window.nested = document;
+      assert(window.nested === document, "Nested attribute to proxy should not be double proxified");
+      done();
+    }
+  );
+
+});
+
+exports["test Form nodeName"] = createProxyTest("", function (helper) {
+
+  helper.createWorker(
+    'new ' + function ContentScriptScope() {
+      let body = document.body;
+      // Check form[nodeName]
+      let form = document.createElement("form");
+      let input = document.createElement("input");
+      input.setAttribute("name", "test");
+      form.appendChild(input);
+      body.appendChild(form);
+      assert(form.test == input, "form[nodeName] is valid");
+      body.removeChild(form);
+      done();
+    }
+  );
+
+});
+
+exports["test localStorage"] = createProxyTest("", function (helper, assert) {
+
+  let worker = helper.createWorker(
+    'new ' + function ContentScriptScope() {
+      // Check localStorage:
+      assert(window.localStorage, "has access to localStorage");
+      window.localStorage.name = 1;
+      assert(window.localStorage.name == 1, "localStorage appears to work");
+
+      self.port.on("step2", function () {
+        window.localStorage.clear();
+        assert(window.localStorage.name == undefined, "localStorage really, really works");
+        done();
+      });
+      self.port.emit("step1");
+    }
+  );
+
+  worker.port.on("step1", function () {
+    assert.equal(helper.rawWindow.localStorage.name, 1, "localStorage really works");
+    worker.port.emit("step2");
+  });
+
+});
+
+exports["test Auto Unwrap Custom Attributes"] = createProxyTest("", function (helper) {
+
+  helper.createWorker(
+    'new ' + function ContentScriptScope() {
+      let body = document.body;
+      // Setting a custom object to a proxy attribute is not wrapped when we get it afterward
+      let object = {custom: true, enumerable: false};
+      body.customAttribute = object;
+      assert(object === body.customAttribute, "custom JS attributes are not wrapped");
+      done();
+    }
+  );
+
+});
+
+exports["test Object Tag"] = createProxyTest("", function (helper) {
+
+  helper.createWorker(
+    'new ' + function ContentScriptScope() {
+      // <object>, <embed> and other tags return typeof 'function'
+      let flash = document.createElement("object");
+      assert(typeof flash == "function", "<object> is typeof 'function'");
+      assert(flash.toString().match(/\[object HTMLObjectElement.*\]/), "<object> is HTMLObjectElement");
+      assert("setAttribute" in flash, "<object> has a setAttribute method");
+      done();
+    }
+  );
+
+});
+
+exports["test Highlight toString Behavior"] = createProxyTest("", function (helper, assert) {
+  // We do not have any workaround this particular use of toString
+  // applied on <object> elements. So disable this test until we found one!
+  //assert.equal(helper.rawWindow.Object.prototype.toString.call(flash), "[object HTMLObjectElement]", "<object> is HTMLObjectElement");
+  function f() {};
+  let funToString = Object.prototype.toString.call(f);
+  assert.ok(/\[object Function.*\]/.test(funToString), "functions are functions 1");
+
+  // This is how jquery call toString:
+  let strToString = helper.rawWindow.Object.prototype.toString.call("");
+  assert.ok(/\[object String.*\]/.test(strToString), "strings are strings");
+
+  let o = {__exposedProps__:{}};
+  let objToString = helper.rawWindow.Object.prototype.toString.call(o);
+  assert.ok(/\[object Object.*\]/.test(objToString), "objects are objects");
+
+  // Make sure to pass a function from the same compartments
+  // or toString will return [object Object] on FF8+
+  let f2 = helper.rawWindow.eval("(function () {})");
+  let funToString2 = helper.rawWindow.Object.prototype.toString.call(f2);
+  assert.ok(/\[object Function.*\]/.test(funToString2), "functions are functions 2");
+
+  helper.done();
+});
+
+exports["test Document TagName"] = createProxyTest("", function (helper) {
+
+  helper.createWorker(
+    'new ' + function ContentScriptScope() {
+      let body = document.body;
+      // Check document[tagName]
+      let div = document.createElement("div");
+      div.setAttribute("name", "test");
+      body.appendChild(div);
+      assert(!document.test, "document[divName] is undefined");
+      body.removeChild(div);
+
+      let form = document.createElement("form");
+      form.setAttribute("name", "test");
+      body.appendChild(form);
+      assert(document.test == form, "document[formName] is valid");
+      body.removeChild(form);
+
+      let img = document.createElement("img");
+      img.setAttribute("name", "test");
+      body.appendChild(img);
+      assert(document.test == img, "document[imgName] is valid");
+      body.removeChild(img);
+      done();
+    }
+  );
+
+});
+
+let html = '<iframe id="iframe" name="test" src="data:text/html;charset=utf-8," />';
+exports["test Window Frames"] = createProxyTest(html, function (helper) {
+
+  helper.createWorker(
+    'let glob = this; new ' + function ContentScriptScope() {
+      // Check window[frameName] and window.frames[i]
+      let iframe = document.getElementById("iframe");
+      //assert(window.frames.length == 1, "The iframe is reported in window.frames check1");
+      //assert(window.frames[0] == iframe.contentWindow, "The iframe is reported in window.frames check2");
+      //console.log(window.test+ "-"+iframe.contentWindow);
+      //console.log(window);
+      assert(window.test == iframe.contentWindow, "window[frameName] is valid");
+      done();
+    }
+  );
+
+});
+
+exports["test Collections"] = createProxyTest("", function (helper) {
+
+  helper.createWorker(
+    'new ' + function ContentScriptScope() {
+      // Highlight XPCNativeWrapper bug with HTMLCollection
+      // tds[0] is only defined on first access :o
+      let body = document.body;
+      let div = document.createElement("div");
+      body.appendChild(div);
+      div.innerHTML = "<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>";
+      let tds = div.getElementsByTagName("td");
+      assert(tds[0] == tds[0], "We can get array element multiple times");
+      body.removeChild(div);
+      done();
+    }
+  );
+
+});
+
+let html = '<input id="input" type="text" /><input id="input3" type="checkbox" />' +
+             '<input id="input2" type="checkbox" />';
+exports["test Collections 2"] = createProxyTest(html, function (helper) {
+
+  helper.createWorker(
+    'new ' + function ContentScriptScope() {
+      // Verify that NodeList/HTMLCollection are working fine
+      let body = document.body;
+      let inputs = body.getElementsByTagName("input");
+      assert(body.childNodes.length == 3, "body.childNodes length is correct");
+      assert(inputs.length == 3, "inputs.length is correct");
+      assert(body.childNodes[0] == inputs[0], "body.childNodes[0] is correct");
+      assert(body.childNodes[1] == inputs[1], "body.childNodes[1] is correct");
+      assert(body.childNodes[2] == inputs[2], "body.childNodes[2] is correct");
+      let count = 0;
+      for(let i in body.childNodes) {
+        count++;
+      }
+      assert(count == 6, "body.childNodes is iterable");
+      done();
+    }
+  );
+
+});
+
+exports["test valueOf"] = createProxyTest("", function (helper) {
+
+    helper.createWorker(
+      'new ' + function ContentScriptScope() {
+        // Bug 787013: Until this bug is fixed, we are missing some methods
+        // on JS objects that comes from global `Object` object
+        assert(!('valueOf' in window), "valueOf is missing");
+        assert(!('toLocateString' in window), "toLocaleString is missing");
+        done();
+      }
+    );
+
+});
+
+exports["test XMLHttpRequest"] = createProxyTest("", function (helper) {
+
+  helper.createWorker(
+    'new ' + function ContentScriptScope() {
+      // XMLHttpRequest doesn't support XMLHttpRequest.apply,
+      // that may break our proxy code
+      assert(window.XMLHttpRequest(), "we are able to instantiate XMLHttpRequest object");
+      done();
+    }
+  );
+
+});
+
+exports["test XPathResult"] = createProxyTest("", function (helper, assert) {
+  const XPathResultTypes = ["ANY_TYPE",
+                            "NUMBER_TYPE", "STRING_TYPE", "BOOLEAN_TYPE",
+                            "UNORDERED_NODE_ITERATOR_TYPE",
+                            "ORDERED_NODE_ITERATOR_TYPE",
+                            "UNORDERED_NODE_SNAPSHOT_TYPE",
+                            "ORDERED_NODE_SNAPSHOT_TYPE",
+                            "ANY_UNORDERED_NODE_TYPE",
+                            "FIRST_ORDERED_NODE_TYPE"];
+
+  // Check XPathResult bug with constants being undefined on XPCNativeWrapper
+  let xpcXPathResult = helper.xrayWindow.XPathResult;
+
+  XPathResultTypes.forEach(function(type, i) {
+    assert.equal(xpcXPathResult.wrappedJSObject[type],
+                     helper.rawWindow.XPathResult[type],
+                     "XPathResult's constants are valid on unwrapped node");
+
+    assert.equal(xpcXPathResult[type], i,
+                     "XPathResult's constants are defined on " +
+                     "XPCNativeWrapper (platform bug #)");
+  });
+
+  let value = helper.rawWindow.XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE;
+  let worker = helper.createWorker(
+    'new ' + function ContentScriptScope() {
+      self.port.on("value", function (value) {
+        // Check that our work around is working:
+        assert(window.XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE === value,
+               "XPathResult works correctly on Proxies");
+        done();
+      });
+    }
+  );
+  worker.port.emit("value", value);
+});
+
+exports["test Prototype Inheritance"] = createProxyTest("", function (helper) {
+
+  helper.createWorker(
+    'new ' + function ContentScriptScope() {
+      // Verify that inherited prototype function like initEvent
+      // are handled correctly. (e2.type will return an error if it's not the case)
+      let event1 = document.createEvent( 'MouseEvents' );
+      event1.initEvent( "click", true, true );
+      let event2 = document.createEvent( 'MouseEvents' );
+      event2.initEvent( "click", true, true );
+      assert(event2.type == "click", "We are able to create an event");
+      done();
+    }
+  );
+
+});
+
+exports["test Functions"] = createProxyTest("", function (helper) {
+
+  helper.rawWindow.callFunction = function callFunction(f) f();
+  helper.rawWindow.isEqual = function isEqual(a, b) a == b;
+  // bug 784116: workaround in order to allow proxy code to cache proxies on
+  // these functions:
+  helper.rawWindow.callFunction.__exposedProps__ = {__proxy: 'rw'};
+  helper.rawWindow.isEqual.__exposedProps__ = {__proxy: 'rw'};
+
+  helper.createWorker(
+    'new ' + function ContentScriptScope() {
+      // Check basic usage of functions
+      let closure2 = function () {return "ok";};
+      assert(window.wrappedJSObject.callFunction(closure2) == "ok", "Function references work");
+
+      // Ensure that functions are cached when being wrapped to native code
+      let closure = function () {};
+      assert(window.wrappedJSObject.isEqual(closure, closure), "Function references are cached before being wrapped to native");
+      done();
+    }
+  );
+
+});
+
+let html = '<input id="input2" type="checkbox" />';
+exports["test Listeners"] = createProxyTest(html, function (helper) {
+
+  helper.createWorker(
+    'new ' + function ContentScriptScope() {
+      // Verify listeners:
+      let input = document.getElementById("input2");
+      assert(input, "proxy.getElementById works");
+
+      function onclick() {};
+      input.onclick = onclick;
+      assert(input.onclick === onclick, "on* attributes are equal to original function set");
+
+      let addEventListenerCalled = false;
+      let expandoCalled = false;
+      input.addEventListener("click", function onclick(event) {
+        input.removeEventListener("click", onclick, true);
+
+        assert(!addEventListenerCalled, "closure given to addEventListener is called once");
+        if (addEventListenerCalled)
+          return;
+        addEventListenerCalled = true;
+
+        assert(!event.target.ownerDocument.defaultView.documentGlobal, "event object is still wrapped and doesn't expose document globals");
+
+        let input2 = document.getElementById("input2");
+
+        input.onclick = function (event) {
+          input.onclick = null;
+          assert(!expandoCalled, "closure set to expando is called once");
+          if (expandoCalled) return;
+          expandoCalled = true;
+
+          assert(!event.target.ownerDocument.defaultView.documentGlobal, "event object is still wrapped and doesn't expose document globals");
+
+          setTimeout(function () {
+            input.click();
+            done();
+          }, 0);
+
+        }
+
+        setTimeout(function () {
+          input.click();
+        }, 0);
+
+      }, true);
+
+      input.click();
+    }
+  );
+
+});
+
+exports["test MozRequestAnimationFrame"] = createProxyTest("", function (helper) {
+
+  helper.createWorker(
+    'new ' + function ContentScriptScope() {
+      window.mozRequestAnimationFrame(function callback() {
+        assert(callback == this, "callback is equal to `this`");
+        done();
+      });
+    }
+  );
+
+});
+
+exports["testGlobalScope"] = createProxyTest("", function (helper) {
+
+  helper.createWorker(
+    'let toplevelScope = true;' +
+    'assert(window.toplevelScope, "variables in toplevel scope are set to `window` object");' +
+    'assert(this.toplevelScope, "variables in toplevel scope are set to `this` object");' +
+    'done();'
+  );
+
+});
+
+// Bug 715755: proxy code throw an exception on COW
+// Create an http server in order to simulate real cross domain documents
+exports["test Cross Domain Iframe"] = createProxyTest("", function (helper) {
+  let serverPort = 8099;
+  let server = require("sdk/test/httpd").startServerAsync(serverPort);
+  server.registerPathHandler("/", function handle(request, response) {
+    // Returns the webpage that receive a message and forward it back to its
+    // parent document by appending ' world'.
+    let content = "<html><head><meta charset='utf-8'></head>\n";
+    content += "<script>\n";
+    content += "  window.addEventListener('message', function (event) {\n";
+    content += "    parent.postMessage(event.data + ' world', '*');\n";
+    content += "  }, true);\n";
+    content += "</script>\n";
+    content += "<body></body>\n";
+    content += "</html>\n";
+    response.write(content);
+  });
+
+  let worker = helper.createWorker(
+    'new ' + function ContentScriptScope() {
+      // Waits for the server page url
+      self.on("message", function (url) {
+        // Creates an iframe with this page
+        let iframe = document.createElement("iframe");
+        iframe.addEventListener("load", function onload() {
+          iframe.removeEventListener("load", onload, true);
+          try {
+            // Try to communicate with iframe's content
+            window.addEventListener("message", function onmessage(event) {
+              window.removeEventListener("message", onmessage, true);
+
+              assert(event.data == "hello world", "COW works properly");
+              self.port.emit("end");
+            }, true);
+            iframe.contentWindow.postMessage("hello", "*");
+          } catch(e) {
+            assert(false, "COW fails : "+e.message);
+          }
+        }, true);
+        iframe.setAttribute("src", url);
+        document.body.appendChild(iframe);
+      });
+    }
+  );
+
+  worker.port.on("end", function () {
+    server.stop(helper.done);
+  });
+
+  worker.postMessage("http://localhost:" + serverPort + "/");
+
+});
+
+// Bug 769006: Ensure that MutationObserver works fine with proxies
+let html = '<a href="foo">link</a>';
+exports["test MutationObvserver"] = createProxyTest(html, function (helper) {
+
+  helper.createWorker(
+    'new ' + function ContentScriptScope() {
+      if (typeof MutationObserver == "undefined") {
+        assert(true, "No MutationObserver for this FF version");
+        done();
+        return;
+      }
+      let link = document.getElementsByTagName("a")[0];
+
+      // Register a Mutation observer
+      let obs = new MutationObserver(function(mutations){
+        // Ensure that mutation data are valid
+        assert(mutations.length == 1, "only one attribute mutation");
+        let mutation = mutations[0];
+        assert(mutation.type == "attributes", "check `type`");
+        assert(mutation.target == link, "check `target`");
+        assert(mutation.attributeName == "href", "check `attributeName`");
+        assert(mutation.oldValue == "foo", "check `oldValue`");
+        obs.disconnect();
+        done();
+      });
+      obs.observe(document, {
+        subtree: true,
+        attributes: true,
+        attributeOldValue: true,
+        attributeFilter: ["href"]
+      });
+
+      // Modify the DOM
+      link.setAttribute("href", "bar");
+    }
+  );
+
+});
+
+if (require("sdk/system/xul-app").is("Fennec")) {
+  module.exports = {
+    "test Unsupported Test": function UnsupportedTest (assert) {
+        assert.pass(
+          "Skipping this test until Fennec support is implemented." +
+          "See bug 806813");
+    }
+  }
+}
+
+require("test").run(exports);
--- a/addon-sdk/source/test/test-deprecated-list.js
+++ b/addon-sdk/source/test/test-deprecated-list.js
@@ -45,16 +45,26 @@ exports['test:test for each'] = function
 
   test.assertEqual(3, fixture.length, 'length is 3');
   let i = 3;
   for each (let value in fixture) {
     test.assertEqual(i--, value, 'value should match');
   }
 };
 
+exports['test:test for of'] = function(test) {
+  let fixture = new List(3, 2, 1);
+
+  test.assertEqual(3, fixture.length, 'length is 3');
+  let i = 3;
+  for (let value of fixture) {
+    test.assertEqual(i--, value, 'value should match');
+  }
+};
+
 exports['test:test toString'] = function(test) {
   let fixture = List(3, 2, 1);
 
   test.assertEqual(
     'List(3,2,1)',
     fixture + '',
     'toString must output array like result'
   )
--- a/addon-sdk/source/test/test-functional.js
+++ b/addon-sdk/source/test/test-functional.js
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 
 const { setTimeout } = require('sdk/timers');
 const utils = require('sdk/lang/functional');
-const { invoke, defer, partial, compose, memoize, once, delay, wrap } = utils;
+const { invoke, defer, partial, compose, memoize, once, delay, wrap, curry } = utils;
 const { LoaderWithHookedConsole } = require('sdk/test/loader');
 
 exports['test forwardApply'] = function(assert) {
   function sum(b, c) this.a + b + c
   assert.equal(invoke(sum, [2, 3], { a: 1 }), 6,
                'passed arguments and pseoude-variable are used');
 
   assert.equal(invoke(sum.bind({ a: 2 }), [2, 3], { a: 1 }), 7,
@@ -39,30 +39,25 @@ exports['test partial function'] = funct
   foo.sum7 = partial(sum, 7);
   foo.sum8and4 = partial(sum, 8, 4);
 
   assert.equal(foo.sum7(2), 14, 'partial one arguments works');
 
   assert.equal(foo.sum8and4(), 17, 'partial both arguments works');
 };
 
-exports['test curry function'] = function(assert) {
-  let { loader, messages } = LoaderWithHookedConsole(module);
-  let { curry } = loader.require('sdk/lang/functional');
-
-  function sum(b, c) this.a + b + c;
+exports["test curry defined numeber of arguments"] = function(assert) {
+  var sum = curry(function(a, b, c) {
+    return a + b + c;
+  });
 
-  let foo = { a : 5 };
-
-  foo.sum7 = curry(sum, 7);
-
-  assert.equal(messages.length, 1, "only one error is dispatched");
-  assert.ok(messages[0].msg.indexOf('curry is deprecated') > -1);
-
-  loader.unload();
+  assert.equal(sum(2, 2, 1), 5, "sum(2, 2, 1) => 5");
+  assert.equal(sum(2, 4)(1), 7, "sum(2, 4)(1) => 7");
+  assert.equal(sum(2)(4, 2), 8, "sum(2)(4, 2) => 8");
+  assert.equal(sum(2)(4)(3), 9, "sum(2)(4)(3) => 9");
 };
 
 exports['test compose'] = function(assert) {
   let greet = function(name) { return 'hi: ' + name; };
   let exclaim = function(sentence) { return sentence + '!'; };
 
   assert.equal(compose(exclaim, greet)('moe'), 'hi: moe!',
                'can compose a function that takes another');
--- a/addon-sdk/source/test/test-list.js
+++ b/addon-sdk/source/test/test-list.js
@@ -16,28 +16,41 @@ exports.testList = function(test) {
   }
 
   let count = 0;
   for each (let ele in list) {
     test.assertEqual(ele, 1, 'ele is correct');
     test.assertEqual(++count, 1, 'count is correct');
   }
 
+  count = 0;
+  for (let ele of list) {
+    test.assertEqual(ele, 1, 'ele is correct');
+    test.assertEqual(++count, 1, 'count is correct');
+  }
+
   removeListItem(list, 1);
   test.assertEqual(list.length, 0, 'remove worked');
 };
 
 exports.testImplementsList = function(test) {
   let List2 = Class({
     implements: [List],
     initialize: function() {
       List.prototype.initialize.apply(this, [0, 1, 2]);
     }
   });
   let list2 = List2();
   let count = 0;
+
   for each (let ele in list2) {
     test.assertEqual(ele, count++, 'ele is correct');
   }
+
+  count = 0;
+  for (let ele of list2) {
+    test.assertEqual(ele, count++, 'ele is correct');
+  }
+
   addListItem(list2, 3);
   test.assertEqual(list2.length, 4, '3 was added');
   test.assertEqual(list2[list2.length-1], 3, '3 was added');
 }
--- a/addon-sdk/source/test/test-page-mod.js
+++ b/addon-sdk/source/test/test-page-mod.js
@@ -1132,8 +1132,34 @@ exports["test page-mod on private tab in
 
   let page1;
   pb.once('start', function() {
     test.pass('global pb start');
     tabs.open({ url: privateUri });
   });
   pb.activate();
 }
+
+// Bug 699450: Calling worker.tab.close() should not lead to exception
+exports.testWorkerTabClose = function(test) {
+  let callbackDone;
+  testPageMod(test, "about:", [{
+      include: "about:",
+      contentScript: '',
+      onAttach: function(worker) {
+        console.log("call close");
+        worker.tab.close(function () {
+          // On Fennec, tab is completely destroyed right after close event is
+          // dispatch, so we need to wait for the next event loop cycle to
+          // check for tab nulliness.
+          timer.setTimeout(function () {
+            test.assert(!worker.tab,
+                        "worker.tab should be null right after tab.close()");
+            callbackDone();
+          }, 0);
+        });
+      }
+    }],
+    function(win, done) {
+      callbackDone = done;
+    }
+  );
+};
--- a/addon-sdk/source/test/test-xhr.js
+++ b/addon-sdk/source/test/test-xhr.js
@@ -1,92 +1,86 @@
 /* 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 xhr = require('sdk/net/xhr');
-const { Loader } = require('sdk/test/loader');
-const xulApp = require('sdk/system/xul-app');
+const { XMLHttpRequest } = require('sdk/net/xhr');
+const { LoaderWithHookedConsole } = require('sdk/test/loader');
 const { data } = require('sdk/self');
 
-// TODO: rewrite test below
-/* Test is intentionally disabled until platform bug 707256 is fixed.
-exports.testAbortedXhr = function(test) {
-  var req = new xhr.XMLHttpRequest();
-  test.assertEqual(xhr.getRequestCount(), 1);
+
+exports.testAPIExtension = function(assert) {
+  let { loader, messages } = LoaderWithHookedConsole(module);
+  let { XMLHttpRequest } = loader.require("sdk/net/xhr");
+
+  let xhr = new XMLHttpRequest();
+  assert.equal(typeof(xhr.forceAllowThirdPartyCookie), "function",
+               "forceAllowThirdPartyCookie is defined");
+  assert.equal(xhr.forceAllowThirdPartyCookie(), undefined,
+               "function can be called");
+
+  assert.ok(messages[0].msg.indexOf("`xhr.forceAllowThirdPartyCookie()` is deprecated") >= 0,
+            "deprecation warning was dumped");
+  assert.ok(xhr.mozBackgroundRequest, "is background request");
+
+  loader.unload();
+};
+
+exports.testAbortedXhr = function(assert, done) {
+  let req = new XMLHttpRequest();
+  req.open('GET', data.url('testLocalXhr.json'));
+  req.addEventListener("abort", function() {
+    assert.pass("request was aborted");
+    done();
+  });
+  req.send(null);
   req.abort();
-  test.assertEqual(xhr.getRequestCount(), 0);
 };
-*/
 
 exports.testLocalXhr = function(assert, done) {
-  var req = new xhr.XMLHttpRequest();
+  let req = new XMLHttpRequest();
   let ready = false;
 
   req.overrideMimeType('text/plain');
   req.open('GET', data.url('testLocalXhr.json'));
   req.onreadystatechange = function() {
     if (req.readyState == 4 && (req.status == 0 || req.status == 200)) {
       ready = true;
       assert.equal(req.responseText, '{}\n', 'XMLHttpRequest should get local files');
     }
   };
   req.addEventListener('load', function onload() {
     req.removeEventListener('load', onload);
     assert.pass('addEventListener for load event worked');
     assert.ok(ready, 'onreadystatechange listener worked');
-    assert.equal(xhr.getRequestCount(), 0, 'request count is 0');
     done();
   });
   req.send(null);
-
-  assert.equal(xhr.getRequestCount(), 1, 'request count is 1');
 };
 
-exports.testUnload = function(assert) {
-  var loader = Loader(module);
-  var sbxhr = loader.require('sdk/net/xhr');
-  var req = new sbxhr.XMLHttpRequest();
-
-  req.overrideMimeType('text/plain');
-  req.open("GET", module.uri);
-  req.send(null);
-
-  assert.equal(sbxhr.getRequestCount(), 1, 'request count is 1');
-  loader.unload();
-  assert.equal(sbxhr.getRequestCount(), 0, 'request count is 0');
-};
 
 exports.testResponseHeaders = function(assert, done) {
-  var req = new xhr.XMLHttpRequest();
+  let req = new XMLHttpRequest();
 
   req.overrideMimeType('text/plain');
   req.open('GET', module.uri);
   req.onreadystatechange = function() {
     if (req.readyState == 4 && (req.status == 0 || req.status == 200)) {
       var headers = req.getAllResponseHeaders();
-      if (xulApp.satisfiesVersion(xulApp.platformVersion, '>=13.0a1')) {
-        headers = headers.split("\r\n");
-        if (headers.length == 1) {
-          headers = headers[0].split("\n");
+      headers = headers.split("\r\n");
+      if (headers.length == 1) {
+        headers = headers[0].split("\n");
+      }
+      for (let i in headers) {
+        if (headers[i] && headers[i].search('Content-Type') >= 0) {
+          assert.equal(headers[i], 'Content-Type: text/plain',
+                       'XHR\'s headers are valid');
         }
-        for (let i in headers) {
-          if (headers[i] && headers[i].search('Content-Type') >= 0) {
-            assert.equal(headers[i], 'Content-Type: text/plain',
-                         'XHR\'s headers are valid');
-          }
-        }
-      }
-      else {
-        assert.ok(headers === null || headers === '',
-                  'XHR\'s headers are empty');
       }
 
       done();
     }
   };
   req.send(null);
-
-  assert.equal(xhr.getRequestCount(), 1, 'request count is 1');
 }
 
 require('test').run(exports);