Merge m-c to s-c.
authorRichard Newman <rnewman@mozilla.com>
Fri, 07 Jun 2013 13:04:39 -0700
changeset 134462 6006abb39d8e11fb3e9ec9ffd17c10e7da2b6ed3
parent 134461 99311ea7ddce1c8cc2aec25d42b3b6f55d958657 (current diff)
parent 134408 eebeea69662ce56b58b272a9a1d721edeb99cd7d (diff)
child 134463 efbe547a797253f49ff294e9acffd8dbc50049fd
child 155471 9d393cd3c17c77ebfadd59dba3590e34488dd26f
push id29226
push usereakhgari@mozilla.com
push dateSun, 09 Jun 2013 23:03:45 +0000
treeherdermozilla-inbound@5b76afd81329 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone24.0a1
first release with
nightly linux32
6006abb39d8e / 24.0a1 / 20130608031212 / files
nightly linux64
6006abb39d8e / 24.0a1 / 20130608031212 / files
nightly mac
6006abb39d8e / 24.0a1 / 20130608031212 / files
nightly win32
6006abb39d8e / 24.0a1 / 20130608031212 / files
nightly win64
6006abb39d8e / 24.0a1 / 20130608031212 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to s-c.
addon-sdk/source/lib/sdk/content/content-proxy.js
addon-sdk/source/test/test-content-proxy.js
b2g/config/panda-gaia-central/README
b2g/config/panda-gaia-central/config.json
b2g/config/panda-gaia-central/releng-pandaboard.tt
browser/themes/linux/devtools/arrows.png
browser/themes/osx/devtools/arrows.png
browser/themes/windows/devtools/arrows.png
js/src/jsprobes.cpp
js/src/jsprobes.h
layout/reftests/flexbox/flexbox-align-self-baseline-horiz-1-ref.xhtml
layout/reftests/flexbox/flexbox-align-self-baseline-horiz-1.xhtml
layout/reftests/flexbox/flexbox-align-self-horiz-1-block.xhtml
layout/reftests/flexbox/flexbox-align-self-horiz-1-ref.xhtml
layout/reftests/flexbox/flexbox-align-self-horiz-1-table.xhtml
layout/reftests/flexbox/flexbox-align-self-horiz-2-ref.xhtml
layout/reftests/flexbox/flexbox-align-self-horiz-2.xhtml
layout/reftests/flexbox/flexbox-align-self-horiz-3-ref.xhtml
layout/reftests/flexbox/flexbox-align-self-horiz-3.xhtml
layout/reftests/flexbox/flexbox-align-self-horiz-4-ref.xhtml
layout/reftests/flexbox/flexbox-align-self-horiz-4.xhtml
layout/reftests/flexbox/flexbox-align-self-horiz-5-ref.xhtml
layout/reftests/flexbox/flexbox-align-self-horiz-5.xhtml
layout/reftests/flexbox/flexbox-align-self-vert-1-ref.xhtml
layout/reftests/flexbox/flexbox-align-self-vert-1.xhtml
layout/reftests/flexbox/flexbox-align-self-vert-2-ref.xhtml
layout/reftests/flexbox/flexbox-align-self-vert-2.xhtml
layout/reftests/flexbox/flexbox-align-self-vert-3-ref.xhtml
layout/reftests/flexbox/flexbox-align-self-vert-3.xhtml
layout/reftests/flexbox/flexbox-align-self-vert-4-ref.xhtml
layout/reftests/flexbox/flexbox-align-self-vert-4.xhtml
layout/reftests/flexbox/flexbox-align-self-vert-rtl-1-ref.xhtml
layout/reftests/flexbox/flexbox-align-self-vert-rtl-1.xhtml
layout/reftests/flexbox/flexbox-align-self-vert-rtl-2-ref.xhtml
layout/reftests/flexbox/flexbox-align-self-vert-rtl-2.xhtml
layout/reftests/flexbox/flexbox-align-self-vert-rtl-3-ref.xhtml
layout/reftests/flexbox/flexbox-align-self-vert-rtl-3.xhtml
layout/reftests/flexbox/flexbox-align-self-vert-rtl-4-ref.xhtml
layout/reftests/flexbox/flexbox-align-self-vert-rtl-4.xhtml
layout/reftests/flexbox/flexbox-baseline-align-self-baseline-horiz-1-ref.html
layout/reftests/flexbox/flexbox-baseline-align-self-baseline-horiz-1.html
layout/reftests/flexbox/flexbox-baseline-align-self-baseline-vert-1-ref.html
layout/reftests/flexbox/flexbox-baseline-align-self-baseline-vert-1.html
layout/reftests/flexbox/flexbox-baseline-empty-1-ref.html
layout/reftests/flexbox/flexbox-baseline-empty-1a.html
layout/reftests/flexbox/flexbox-baseline-empty-1b.html
layout/reftests/flexbox/flexbox-baseline-multi-item-horiz-1-ref.html
layout/reftests/flexbox/flexbox-baseline-multi-item-horiz-1.html
layout/reftests/flexbox/flexbox-baseline-multi-item-vert-1-ref.html
layout/reftests/flexbox/flexbox-baseline-multi-item-vert-1.html
layout/reftests/flexbox/flexbox-baseline-single-item-1-ref.html
layout/reftests/flexbox/flexbox-baseline-single-item-1a.html
layout/reftests/flexbox/flexbox-baseline-single-item-1b.html
layout/reftests/flexbox/flexbox-basic-block-horiz-1-ref.xhtml
layout/reftests/flexbox/flexbox-basic-block-horiz-1.xhtml
layout/reftests/flexbox/flexbox-basic-block-vert-1-ref.xhtml
layout/reftests/flexbox/flexbox-basic-block-vert-1.xhtml
layout/reftests/flexbox/flexbox-basic-canvas-horiz-1-ref.xhtml
layout/reftests/flexbox/flexbox-basic-canvas-horiz-1.xhtml
layout/reftests/flexbox/flexbox-basic-canvas-vert-1-ref.xhtml
layout/reftests/flexbox/flexbox-basic-canvas-vert-1.xhtml
layout/reftests/flexbox/flexbox-basic-fieldset-horiz-1-ref.xhtml
layout/reftests/flexbox/flexbox-basic-fieldset-horiz-1.xhtml
layout/reftests/flexbox/flexbox-basic-fieldset-vert-1-ref.xhtml
layout/reftests/flexbox/flexbox-basic-fieldset-vert-1.xhtml
layout/reftests/flexbox/flexbox-basic-iframe-horiz-1-ref.xhtml
layout/reftests/flexbox/flexbox-basic-iframe-horiz-1.xhtml
layout/reftests/flexbox/flexbox-basic-iframe-vert-1-ref.xhtml
layout/reftests/flexbox/flexbox-basic-iframe-vert-1.xhtml
layout/reftests/flexbox/flexbox-basic-img-horiz-1-ref.xhtml
layout/reftests/flexbox/flexbox-basic-img-horiz-1.xhtml
layout/reftests/flexbox/flexbox-basic-img-vert-1-ref.xhtml
layout/reftests/flexbox/flexbox-basic-img-vert-1.xhtml
layout/reftests/flexbox/flexbox-basic-textarea-horiz-1-ref.xhtml
layout/reftests/flexbox/flexbox-basic-textarea-horiz-1.xhtml
layout/reftests/flexbox/flexbox-basic-textarea-vert-1-ref.xhtml
layout/reftests/flexbox/flexbox-basic-textarea-vert-1.xhtml
layout/reftests/flexbox/flexbox-basic-video-horiz-1-ref.xhtml
layout/reftests/flexbox/flexbox-basic-video-horiz-1.xhtml
layout/reftests/flexbox/flexbox-basic-video-vert-1-ref.xhtml
layout/reftests/flexbox/flexbox-basic-video-vert-1.xhtml
layout/reftests/flexbox/flexbox-items-as-stacking-contexts-1-ref.xhtml
layout/reftests/flexbox/flexbox-items-as-stacking-contexts-1.xhtml
layout/reftests/flexbox/flexbox-items-as-stacking-contexts-2-ref.html
layout/reftests/flexbox/flexbox-items-as-stacking-contexts-2.html
layout/reftests/flexbox/flexbox-items-as-stacking-contexts-3-ref.html
layout/reftests/flexbox/flexbox-items-as-stacking-contexts-3.html
layout/reftests/flexbox/flexbox-justify-content-horiz-1-ref.xhtml
layout/reftests/flexbox/flexbox-justify-content-horiz-1.xhtml
layout/reftests/flexbox/flexbox-justify-content-horiz-2-ref.xhtml
layout/reftests/flexbox/flexbox-justify-content-horiz-2.xhtml
layout/reftests/flexbox/flexbox-justify-content-horiz-3-ref.xhtml
layout/reftests/flexbox/flexbox-justify-content-horiz-3.xhtml
layout/reftests/flexbox/flexbox-justify-content-horiz-4-ref.xhtml
layout/reftests/flexbox/flexbox-justify-content-horiz-4.xhtml
layout/reftests/flexbox/flexbox-justify-content-horiz-5-ref.xhtml
layout/reftests/flexbox/flexbox-justify-content-horiz-5.xhtml
layout/reftests/flexbox/flexbox-justify-content-vert-1-ref.xhtml
layout/reftests/flexbox/flexbox-justify-content-vert-1.xhtml
layout/reftests/flexbox/flexbox-justify-content-vert-2-ref.xhtml
layout/reftests/flexbox/flexbox-justify-content-vert-2.xhtml
layout/reftests/flexbox/flexbox-justify-content-vert-3-ref.xhtml
layout/reftests/flexbox/flexbox-justify-content-vert-3.xhtml
layout/reftests/flexbox/flexbox-justify-content-vert-4-ref.xhtml
layout/reftests/flexbox/flexbox-justify-content-vert-4.xhtml
layout/reftests/flexbox/flexbox-justify-content-vert-5-ref.xhtml
layout/reftests/flexbox/flexbox-justify-content-vert-5.xhtml
layout/reftests/flexbox/flexbox-margin-auto-horiz-1-ref.xhtml
layout/reftests/flexbox/flexbox-margin-auto-horiz-1.xhtml
layout/reftests/flexbox/flexbox-margin-auto-horiz-2-ref.xhtml
layout/reftests/flexbox/flexbox-margin-auto-horiz-2.xhtml
layout/reftests/flexbox/flexbox-mbp-horiz-1-ref.xhtml
layout/reftests/flexbox/flexbox-mbp-horiz-1-reverse-ref.xhtml
layout/reftests/flexbox/flexbox-mbp-horiz-1-reverse.xhtml
layout/reftests/flexbox/flexbox-mbp-horiz-1-rtl-reverse.xhtml
layout/reftests/flexbox/flexbox-mbp-horiz-1-rtl.xhtml
layout/reftests/flexbox/flexbox-mbp-horiz-1.xhtml
layout/reftests/flexbox/flexbox-mbp-horiz-2-ref.xhtml
layout/reftests/flexbox/flexbox-mbp-horiz-2a.xhtml
layout/reftests/flexbox/flexbox-mbp-horiz-2b.xhtml
layout/reftests/flexbox/flexbox-mbp-horiz-3-ref.xhtml
layout/reftests/flexbox/flexbox-mbp-horiz-3-reverse-ref.xhtml
layout/reftests/flexbox/flexbox-mbp-horiz-3-reverse.xhtml
layout/reftests/flexbox/flexbox-mbp-horiz-3.xhtml
layout/reftests/flexbox/flexbox-mbp-horiz-4-ref.xhtml
layout/reftests/flexbox/flexbox-mbp-horiz-4.xhtml
layout/reftests/flexbox/flexbox-overflow-horiz-1-ref.html
layout/reftests/flexbox/flexbox-overflow-horiz-1.html
layout/reftests/flexbox/flexbox-overflow-horiz-2-ref.html
layout/reftests/flexbox/flexbox-overflow-horiz-2.html
layout/reftests/flexbox/flexbox-overflow-horiz-3-ref.html
layout/reftests/flexbox/flexbox-overflow-horiz-3.html
layout/reftests/flexbox/flexbox-overflow-vert-1-ref.html
layout/reftests/flexbox/flexbox-overflow-vert-1.html
layout/reftests/flexbox/flexbox-overflow-vert-2-ref.html
layout/reftests/flexbox/flexbox-overflow-vert-2.html
layout/reftests/flexbox/flexbox-overflow-vert-3-ref.html
layout/reftests/flexbox/flexbox-overflow-vert-3.html
layout/reftests/flexbox/flexbox-paint-ordering-1-ref.xhtml
layout/reftests/flexbox/flexbox-paint-ordering-1.xhtml
layout/reftests/flexbox/flexbox-paint-ordering-2-ref.xhtml
layout/reftests/flexbox/flexbox-paint-ordering-2.xhtml
layout/reftests/flexbox/flexbox-sizing-horiz-1-ref.xhtml
layout/reftests/flexbox/flexbox-sizing-horiz-1.xhtml
layout/reftests/flexbox/flexbox-sizing-horiz-2-ref.xhtml
layout/reftests/flexbox/flexbox-sizing-horiz-2.xhtml
layout/reftests/flexbox/flexbox-sizing-vert-1-ref.xhtml
layout/reftests/flexbox/flexbox-sizing-vert-1.xhtml
layout/reftests/flexbox/flexbox-sizing-vert-2-ref.xhtml
layout/reftests/flexbox/flexbox-sizing-vert-2.xhtml
layout/reftests/flexbox/flexbox-table-fixup-1-ref.xhtml
layout/reftests/flexbox/flexbox-table-fixup-1a.xhtml
layout/reftests/flexbox/flexbox-table-fixup-1b.xhtml
layout/reftests/flexbox/flexbox-whitespace-handling-1-ref.xhtml
layout/reftests/flexbox/flexbox-whitespace-handling-1a.xhtml
layout/reftests/flexbox/flexbox-whitespace-handling-1b.xhtml
layout/reftests/flexbox/flexbox-whitespace-handling-2-ref.xhtml
layout/reftests/flexbox/flexbox-whitespace-handling-2.xhtml
layout/reftests/flexbox/flexbox-with-pseudo-elements-1-ref.html
layout/reftests/flexbox/flexbox-with-pseudo-elements-1.html
layout/reftests/flexbox/flexbox-with-pseudo-elements-2-ref.html
layout/reftests/flexbox/flexbox-with-pseudo-elements-2.html
layout/reftests/flexbox/flexbox-with-pseudo-elements-3-ref.html
layout/reftests/flexbox/flexbox-with-pseudo-elements-3.html
mobile/android/chrome/content/browser.js
--- a/CLOBBER
+++ b/CLOBBER
@@ -12,9 +12,9 @@
 #          O               O
 #          |               |
 #          O <-- Clobber   O  <-- Clobber
 #
 # Note: The description below will be part of the error message shown to users.
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
-Bug 875929 removed a file from js/src/moz.build and apparently the build system didn't notice.
+Bug 879831 needed to clobber for the removal of jsprobes.cpp
--- 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);
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -548,20 +548,17 @@ pref("javascript.options.mem.log", false
 pref("javascript.options.mem.gc_incremental_slice_ms", 30);
 
 pref("javascript.options.mem.gc_high_frequency_heap_growth_max", 150);
 pref("javascript.options.mem.gc_high_frequency_heap_growth_min", 120);
 pref("javascript.options.mem.gc_high_frequency_high_limit_mb", 40);
 pref("javascript.options.mem.gc_high_frequency_low_limit_mb", 10);
 pref("javascript.options.mem.gc_low_frequency_heap_growth", 120);
 pref("javascript.options.mem.high_water_mark", 6);
-pref("javascript.options.mem.gc_allocation_threshold_mb", 3);
-
-// Allocation Threshold for workers
-pref("dom.workers.mem.gc_allocation_threshold_mb", 3);
+pref("javascript.options.mem.gc_allocation_threshold_mb", 1);
 
 // Show/Hide scrollbars when active/inactive
 pref("ui.showHideScrollbars", 1);
 
 // Enable the ProcessPriorityManager, and give processes with no visible
 // documents a 1s grace period before they're eligible to be marked as
 // background.
 pref("dom.ipc.processPriorityManager.enabled", true);
deleted file mode 120000
--- a/b2g/config/panda-gaia-central/README
+++ /dev/null
@@ -1,1 +0,0 @@
-../panda/README
\ No newline at end of file
deleted file mode 100644
--- a/b2g/config/panda-gaia-central/config.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
-    "config_version": 1,
-    "tooltool_manifest": "releng-pandaboard.tt",
-    "mock_target": "mozilla-centos6-i386",
-    "mock_packages": ["ccache", "make", "bison", "flex", "gcc", "g++", "mpfr", "zlib-devel", "ncurses-devel", "zip", "autoconf213", "glibc-static", "perl-Digest-SHA", "wget", "alsa-lib", "atk", "cairo", "dbus-glib", "fontconfig", "freetype", "glib2", "gtk2", "libXRender", "libXt", "pango", "mozilla-python27-mercurial", "openssh-clients", "nss-devel"],
-    "mock_files": [["/home/cltbld/.ssh", "/home/mock_mozilla/.ssh"]],
-    "build_targets": ["boottarball", "systemtarball", "userdatatarball", "package-tests"],
-    "upload_files": [
-        "{workdir}/out/target/product/panda/*.tar.bz2",
-        "{workdir}/out/target/product/panda/tests/*.zip",
-        "{objdir}/dist/b2g-*.crashreporter-symbols.zip",
-        "{srcdir}/b2g/config/panda/README",
-        "{workdir}/sources.xml"
-    ],
-    "gecko_l10n_root": "http://hg.mozilla.org/l10n-central",
-    "gaia": {
-        "vcs": "hgtool",
-        "repo": "http://hg.mozilla.org/integration/gaia-central",
-        "l10n": {
-            "vcs": "hgtool",
-            "root": "http://hg.mozilla.org/gaia-l10n"
-        }
-    },
-    "upload_platform": "panda_gaia_central"
-}
deleted file mode 120000
--- a/b2g/config/panda-gaia-central/releng-pandaboard.tt
+++ /dev/null
@@ -1,1 +0,0 @@
-../panda/releng-pandaboard.tt
\ No newline at end of file
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -388,17 +388,21 @@ pref("browser.link.open_newwindow", 3);
 // 1-3: see browser.link.open_newwindow for interpretation
 pref("browser.link.open_newwindow.override.external", -1);
 
 // 0: no restrictions - divert everything
 // 1: don't divert window.open at all
 // 2: don't divert window.open with features
 pref("browser.link.open_newwindow.restriction", 2);
 
-// Disable opening a new window via window.open if browser is in fullscreen mode
+// If true, this pref causes windows opened by window.open to be forced into new
+// tabs (rather than potentially opening separate windows, depending on
+// window.open arguments) when the browser is in fullscreen mode.
+// We set this differently on Mac because the fullscreen implementation there is
+// different.
 #ifdef XP_MACOSX
 pref("browser.link.open_newwindow.disabled_in_fullscreen", true);
 #else
 pref("browser.link.open_newwindow.disabled_in_fullscreen", false);
 #endif
 
 // Tabbed browser
 pref("browser.tabs.closeWindowWithLastTab", true);
--- a/browser/base/content/browser-fullZoom.js
+++ b/browser/base/content/browser-fullZoom.js
@@ -427,24 +427,32 @@ var FullZoom = {
    * captured state is no longer current, then the zoom should not be changed.
    * Doing so would either change the zoom of the wrong tab or clobber an
    * earlier zoom change that occurred after the operation started.
    *
    * @param browser  The browser associated with the state.  If not given, the
    *                 currently selected browser is used.
    */
   _getState: function FullZoom__getState(browser) {
-    let browser = browser || gBrowser.selectedBrowser;
-    return { uri: browser.currentURI, token: this._zoomChangeToken };
+    browser = browser || gBrowser.selectedBrowser;
+    return {
+      // Due to async content pref service callbacks, this method can get called
+      // after the window has closed, so gBrowser.selectedBrowser may be null.
+      uri: browser ? browser.currentURI : null,
+      token: this._zoomChangeToken,
+     };
   },
 
   /**
    * Returns true if the given state is current.
    */
   _isStateCurrent: function FullZoom__isStateCurrent(state) {
+    // If either state has no URI, then the given state can't be current.
+    // currState.uri will be null when this method is called after the window
+    // has closed, which can happen due to async content pref service callbacks.
     let currState = this._getState();
     return currState.token === state.token &&
            currState.uri && state.uri &&
            this._cps2.extractDomain(currState.uri.spec) ==
              this._cps2.extractDomain(state.uri.spec);
   },
 
   _ensureValid: function FullZoom__ensureValid(aValue) {
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -1375,32 +1375,31 @@
             var position = this.tabs.length - 1;
             var uniqueId = "panel" + Date.now() + position;
             notificationbox.id = uniqueId;
             t.linkedPanel = uniqueId;
             t.linkedBrowser = b;
             t._tPos = position;
             this.tabContainer._setPositionalAttributes();
 
+            // Prevent the superfluous initial load of a blank document
+            // if we're going to load something other than about:blank.
+            if (!uriIsAboutBlank) {
+              b.setAttribute("nodefaultsrc", "true");
+            }
+
             // NB: this appendChild call causes us to run constructors for the
             // browser element, which fires off a bunch of notifications. Some
             // of those notifications can cause code to run that inspects our
             // state, so it is important that the tab element is fully
             // initialized by this point.
             this.mPanelContainer.appendChild(notificationbox);
 
             this.tabContainer.updateVisibility();
 
-            if (!uriIsAboutBlank) {
-              // Stop the existing about:blank load.  Otherwise, if aURI
-              // doesn't stop in-progress loads on its own, we'll get into
-              // trouble with multiple parallel loads running at once.
-              b.stop();
-            }
-
             // wire up a progress listener for the new browser object.
             var tabListener = this.mTabProgressListener(t, b, uriIsAboutBlank);
             const filter = Components.classes["@mozilla.org/appshell/component/browser-status-filter;1"]
                                      .createInstance(Components.interfaces.nsIWebProgress);
             filter.addProgressListener(tabListener, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
             b.webProgress.addProgressListener(filter, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
             this.mTabListeners[position] = tabListener;
             this.mTabFilters[position] = filter;
--- a/browser/base/content/test/browser_bug678392.js
+++ b/browser/base/content/test/browser_bug678392.js
@@ -30,20 +30,20 @@ function test() {
   is(gHistorySwipeAnimation.active, true, "History swipe animation support " +
      "was successfully initialized when supported.");
 
   cleanupArray();
   load(gBrowser.selectedTab, HTTPROOT + "browser_bug678392-2.html", test0);
 }
 
 function load(aTab, aUrl, aCallback) {
-  aTab.linkedBrowser.addEventListener("pageshow", function onpageshow(aEvent) {
-    aEvent.currentTarget.removeEventListener("pageshow", onpageshow, false);
+  aTab.linkedBrowser.addEventListener("load", function onload(aEvent) {
+    aEvent.currentTarget.removeEventListener("load", onload, true);
     waitForFocus(aCallback, content);
-  }, false);
+  }, true);
   aTab.linkedBrowser.loadURI(aUrl);
 }
 
 function cleanupArray() {
   let arr = gHistorySwipeAnimation._trackedSnapshots;
   while (arr.length > 0) {
     delete arr[0].browser.snapshots[arr[0].index]; // delete actual snapshot
     arr.splice(0, 1);
--- a/browser/base/content/test/browser_tabopen_reflows.js
+++ b/browser/base/content/test/browser_tabopen_reflows.js
@@ -3,20 +3,16 @@
 
 XPCOMUtils.defineLazyGetter(this, "docShell", () => {
   return window.QueryInterface(Ci.nsIInterfaceRequestor)
                .getInterface(Ci.nsIWebNavigation)
                .QueryInterface(Ci.nsIDocShell);
 });
 
 const EXPECTED_REFLOWS = [
-  // b.stop() call in tabbrowser.addTab()
-  "stop@chrome://global/content/bindings/browser.xml|" +
-    "addTab@chrome://browser/content/tabbrowser.xml|",
-
   // tabbrowser.adjustTabstrip() call after tabopen animation has finished
   "adjustTabstrip@chrome://browser/content/tabbrowser.xml|" +
     "_handleNewTab@chrome://browser/content/tabbrowser.xml|" +
     "onxbltransitionend@chrome://browser/content/tabbrowser.xml|",
 
   // switching focus in updateCurrentBrowser() causes reflows
   "updateCurrentBrowser@chrome://browser/content/tabbrowser.xml|" +
     "onselect@chrome://browser/content/browser.xul|",
@@ -25,17 +21,30 @@ const EXPECTED_REFLOWS = [
   "openLinkIn@chrome://browser/content/utilityOverlay.js|" +
     "openUILinkIn@chrome://browser/content/utilityOverlay.js|" +
     "BrowserOpenTab@chrome://browser/content/browser.js|",
 
   // accessing element.scrollPosition in _fillTrailingGap() flushes layout
   "get_scrollPosition@chrome://global/content/bindings/scrollbox.xml|" +
     "_fillTrailingGap@chrome://browser/content/tabbrowser.xml|" +
     "_handleNewTab@chrome://browser/content/tabbrowser.xml|" +
-    "onxbltransitionend@chrome://browser/content/tabbrowser.xml|"
+    "onxbltransitionend@chrome://browser/content/tabbrowser.xml|",
+
+  // The TabView iframe causes reflows in the parent document.
+  "iQClass_height@chrome://browser/content/tabview.js|" +
+    "GroupItem_getContentBounds@chrome://browser/content/tabview.js|" +
+    "GroupItem_shouldStack@chrome://browser/content/tabview.js|" +
+    "GroupItem_arrange@chrome://browser/content/tabview.js|" +
+    "GroupItem_add@chrome://browser/content/tabview.js|" +
+    "GroupItems_newTab@chrome://browser/content/tabview.js|" +
+    "TabItem__reconnect@chrome://browser/content/tabview.js|" +
+    "TabItem@chrome://browser/content/tabview.js|" +
+    "TabItems_link@chrome://browser/content/tabview.js|" +
+    "@chrome://browser/content/tabview.js|" +
+    "addTab@chrome://browser/content/tabbrowser.xml|"
 ];
 
 const PREF_PRELOAD = "browser.newtab.preload";
 
 /*
  * This test ensures that there are no unexpected
  * uninterruptible reflows when opening new tabs.
  */
--- a/browser/devtools/debugger/debugger-controller.js
+++ b/browser/devtools/debugger/debugger-controller.js
@@ -574,17 +574,17 @@ StackFrames.prototype = {
 
     // Make sure all the previous stackframes are removed before re-adding them.
     DebuggerView.StackFrames.empty();
 
     for (let frame of this.activeThread.cachedFrames) {
       this._addFrame(frame);
     }
     if (this.currentFrame == null) {
-      this.selectFrame(0);
+      DebuggerView.StackFrames.selectedDepth = 0;
     }
     if (this.activeThread.moreFrames) {
       DebuggerView.StackFrames.dirty = true;
     }
   },
 
   /**
    * Handler for the thread client's framescleared notification.
@@ -634,18 +634,16 @@ StackFrames.prototype = {
 
     // Check if the frame does not represent the evaluation of debuggee code.
     if (!environment) {
       return;
     }
 
     // Move the editor's caret to the proper url and line.
     DebuggerView.updateEditor(url, line);
-    // Highlight the stack frame at the specified depth.
-    DebuggerView.StackFrames.highlightFrame(aDepth);
     // Highlight the breakpoint at the specified url and line if it exists.
     DebuggerView.Sources.highlightBreakpoint(url, line);
     // Don't display the watch expressions textbox inputs in the pane.
     DebuggerView.WatchExpressions.toggleContents(false);
     // Start recording any added variables or properties in any scope.
     DebuggerView.Variables.createHierarchy();
     // Clear existing scopes and create each one dynamically.
     DebuggerView.Variables.empty();
--- a/browser/devtools/debugger/debugger-panes.js
+++ b/browser/devtools/debugger/debugger-panes.js
@@ -50,16 +50,18 @@ create({ constructor: SourcesView, proto
     this.node.addEventListener("select", this._onSourceSelect, false);
     this.node.addEventListener("click", this._onSourceClick, false);
     this._cbPanel.addEventListener("popupshowing", this._onConditionalPopupShowing, false);
     this._cbPanel.addEventListener("popupshown", this._onConditionalPopupShown, false);
     this._cbPanel.addEventListener("popuphiding", this._onConditionalPopupHiding, false);
     this._cbTextbox.addEventListener("input", this._onConditionalTextboxInput, false);
     this._cbTextbox.addEventListener("keypress", this._onConditionalTextboxKeyPress, false);
 
+    this.autoFocusOnSelection = false;
+
     // Show an empty label by default.
     this.empty();
   },
 
   /**
    * Destruction function, called when the debugger is closed.
    */
   destroy: function() {
--- a/browser/devtools/debugger/debugger-toolbar.js
+++ b/browser/devtools/debugger/debugger-toolbar.js
@@ -343,20 +343,19 @@ create({ constructor: ChromeGlobalsView,
 /**
  * Functions handling the stackframes UI.
  */
 function StackFramesView() {
   dumpn("StackFramesView was instantiated");
 
   this._framesCache = new Map(); // Can't use a WeakMap because keys are numbers.
   this._onStackframeRemoved = this._onStackframeRemoved.bind(this);
-  this._onClick = this._onClick.bind(this);
+  this._onSelect = this._onSelect.bind(this);
   this._onScroll = this._onScroll.bind(this);
   this._afterScroll = this._afterScroll.bind(this);
-  this._selectFrame = this._selectFrame.bind(this);
 }
 
 create({ constructor: StackFramesView, proto: MenuContainer.prototype }, {
   /**
    * Initialization function, called when the debugger is started.
    */
   initialize: function() {
     dumpn("Initializing the StackFramesView");
@@ -365,28 +364,31 @@ create({ constructor: StackFramesView, p
     let menupopup = this._menupopup = document.createElement("menupopup");
     commandset.id = "stackframesCommandset";
     menupopup.id = "stackframesMenupopup";
 
     document.getElementById("debuggerPopupset").appendChild(menupopup);
     document.getElementById("debuggerCommands").appendChild(commandset);
 
     this.node = new BreadcrumbsWidget(document.getElementById("stackframes"));
-    this.node.addEventListener("mousedown", this._onClick, false);
+    this.node.addEventListener("select", this._onSelect, false);
     this.node.addEventListener("scroll", this._onScroll, true);
     window.addEventListener("resize", this._onScroll, true);
+
+    this.autoFocusOnFirstItem = false;
+    this.autoFocusOnSelection = false;
   },
 
   /**
    * Destruction function, called when the debugger is closed.
    */
   destroy: function() {
     dumpn("Destroying the StackFramesView");
 
-    this.node.removeEventListener("mousedown", this._onClick, false);
+    this.node.removeEventListener("select", this._onSelect, false);
     this.node.removeEventListener("scroll", this._onScroll, true);
     window.removeEventListener("resize", this._onScroll, true);
   },
 
   /**
    * Adds a frame in this stackframes container.
    *
    * @param string aFrameTitle
@@ -418,31 +420,21 @@ create({ constructor: StackFramesView, p
       // menuitem and command are also destroyed.
       finalize: this._onStackframeRemoved
     });
 
     this._framesCache.set(aDepth, stackframeItem);
   },
 
   /**
-   * Highlights a frame in this stackframes container.
-   *
+   * Selects the frame at the specified depth in this container.
    * @param number aDepth
-   *        The frame depth specified by the debugger controller.
    */
-  highlightFrame: function(aDepth) {
-    let selectedItem = this.selectedItem = this._framesCache.get(aDepth);
-
-    for (let item in this) {
-      if (item != selectedItem) {
-        item.attachment.popup.menuitem.removeAttribute("checked");
-      } else {
-        item.attachment.popup.menuitem.setAttribute("checked", "");
-      }
-    }
+  set selectedDepth(aDepth) {
+    this.selectedItem = this._framesCache.get(aDepth);
   },
 
   /**
    * Specifies if the active thread has more frames that need to be loaded.
    */
   dirty: false,
 
   /**
@@ -506,17 +498,17 @@ create({ constructor: StackFramesView, p
         STACK_FRAMES_POPUP_SOURCE_URL_TRIM_SECTION) + SEARCH_LINE_FLAG + aLineNumber;
 
     let prefix = "sf-cMenu-"; // "stackframes context menu"
     let commandId = prefix + aDepth + "-" + "-command";
     let menuitemId = prefix + aDepth + "-" + "-menuitem";
 
     let command = document.createElement("command");
     command.id = commandId;
-    command.addEventListener("command", this._selectFrame.bind(this, aDepth), false);
+    command.addEventListener("command", () => this.selectedDepth = aDepth, false);
 
     let menuitem = document.createElement("menuitem");
     menuitem.id = menuitemId;
     menuitem.className = "dbg-stackframe-menuitem";
     menuitem.setAttribute("type", "checkbox");
     menuitem.setAttribute("command", commandId);
     menuitem.setAttribute("tooltiptext", aSourceLocation);
 
@@ -567,27 +559,31 @@ create({ constructor: StackFramesView, p
     dumpn("Finalizing stackframe item: " + aItem);
 
     let { popup, depth } = aItem.attachment;
     this._destroyMenuEntry(popup);
     this._framesCache.delete(depth);
   },
 
   /**
-   * The click listener for the stackframes container.
+   * The select listener for the stackframes container.
    */
-  _onClick: function(e) {
-    if (e && e.button != 0) {
-      // Only allow left-click to trigger this event.
-      return;
-    }
-    let item = this.getItemForElement(e.target);
-    if (item) {
-      // The container is not empty and we clicked on an actual item.
-      this._selectFrame(item.attachment.depth);
+  _onSelect: function(e) {
+    let stackframeItem = this.selectedItem;
+    if (stackframeItem) {
+      // The container is not empty and an actual item was selected.
+      gStackFrames.selectFrame(stackframeItem.attachment.depth);
+
+      for (let otherItem in this) {
+        if (otherItem != stackframeItem) {
+          otherItem.attachment.popup.menuitem.removeAttribute("checked");
+        } else {
+          otherItem.attachment.popup.menuitem.setAttribute("checked", "");
+        }
+      }
     }
   },
 
   /**
    * The scroll listener for the stackframes container.
    */
   _onScroll: function() {
     // Update the stackframes container only if we have to.
@@ -612,26 +608,16 @@ create({ constructor: StackFramesView, p
       list.ensureElementIsVisible(this.getItemAtIndex(CALL_STACK_PAGE_SIZE - 1).target);
       this.dirty = false;
 
       // Loads more stack frames from the debugger server cache.
       DebuggerController.StackFrames.addMoreFrames();
     }
   },
 
-  /**
-   * Requests selection of a frame from the controller.
-   *
-   * @param number aDepth
-   *        The depth of the frame in the stack.
-   */
-  _selectFrame: function(aDepth) {
-    DebuggerController.StackFrames.selectFrame(aDepth);
-  },
-
   _framesCache: null,
   _commandset: null,
   _menupopup: null,
   _scrollTimeout: null
 });
 
 /**
  * Utility functions for handling stackframes.
--- a/browser/devtools/debugger/debugger-view.js
+++ b/browser/devtools/debugger/debugger-view.js
@@ -349,19 +349,17 @@ let DebuggerView = {
           ", flags: " + aFlags.toSource());
 
     // If the currently displayed source is the requested one, update.
     if (this.Sources.selectedValue == aUrl) {
       set(aLine);
     }
     // If the requested source exists, display it and update.
     else if (this.Sources.containsValue(aUrl) && !aFlags.noSwitch) {
-      this.Sources.node.preventFocusOnSelection = true;
       this.Sources.selectedValue = aUrl;
-      this.Sources.node.preventFocusOnSelection = false;
       set(aLine);
     }
     // Dumb request, invalidate the caret position and debug location.
     else {
       set(0);
     }
 
     // Updates the source editor's caret position and debug location.
--- a/browser/devtools/debugger/test/Makefile.in
+++ b/browser/devtools/debugger/test/Makefile.in
@@ -49,16 +49,17 @@ MOCHITEST_BROWSER_TESTS = \
 	browser_dbg_propertyview-filter-06.js \
 	browser_dbg_propertyview-filter-07.js \
 	browser_dbg_propertyview-filter-08.js \
 	browser_dbg_propertyview-reexpand.js \
 	$(filter disabled-temporarily--bug-782179, browser_dbg_reload-same-script.js) \
 	browser_dbg_reload-preferred-script.js \
 	browser_dbg_pane-collapse.js \
 	browser_dbg_panesize-inner.js \
+	browser_dbg_breadcrumbs-access.js \
 	browser_dbg_stack-01.js \
 	browser_dbg_stack-02.js \
 	browser_dbg_stack-03.js \
 	browser_dbg_stack-04.js \
 	browser_dbg_stack-05.js \
 	browser_dbg_location-changes.js \
 	browser_dbg_location-changes-new.js \
 	browser_dbg_location-changes-blank.js \
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/browser_dbg_breadcrumbs-access.js
@@ -0,0 +1,154 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const TAB_URL = EXAMPLE_URL + "browser_dbg_script-switching.html";
+
+let gPane = null;
+let gTab = null;
+let gDebuggee = null;
+let gDebugger = null;
+
+function test()
+{
+  let scriptShown = false;
+  let framesAdded = false;
+  let resumed = false;
+  let testStarted = false;
+
+  debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
+    gTab = aTab;
+    gDebuggee = aDebuggee;
+    gPane = aPane;
+    gDebugger = gPane.panelWin;
+    resumed = true;
+
+    gDebugger.addEventListener("Debugger:SourceShown", onScriptShown);
+
+    gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
+      framesAdded = true;
+      executeSoon(startTest);
+    });
+
+    executeSoon(function() {
+      gDebuggee.firstCall();
+    });
+  });
+
+  function onScriptShown(aEvent)
+  {
+    scriptShown = aEvent.detail.url.indexOf("-02.js") != -1;
+    executeSoon(startTest);
+  }
+
+  function startTest()
+  {
+    if (scriptShown && framesAdded && resumed && !testStarted) {
+      gDebugger.removeEventListener("Debugger:SourceShown", onScriptShown);
+      testStarted = true;
+      Services.tm.currentThread.dispatch({ run: performTest }, 0);
+    }
+  }
+
+  function performTest()
+  {
+    let editor = gDebugger.DebuggerView.editor;
+    let sources = gDebugger.DebuggerView.Sources;
+    let stackframes = gDebugger.DebuggerView.StackFrames;
+
+    is(editor.getCaretPosition().line, 5,
+      "The source editor caret position was incorrect (1).");
+    is(sources.selectedLabel, "test-script-switching-02.js",
+      "The currently selected source is incorrect (1).");
+    is(stackframes.selectedIndex, 3,
+      "The currently selected stackframe is incorrect (1).");
+
+    EventUtils.sendKey("DOWN", gDebugger);
+    is(editor.getCaretPosition().line, 6,
+      "The source editor caret position was incorrect (2).");
+    is(sources.selectedLabel, "test-script-switching-02.js",
+      "The currently selected source is incorrect (2).");
+    is(stackframes.selectedIndex, 3,
+      "The currently selected stackframe is incorrect (2).");
+
+    EventUtils.sendKey("UP", gDebugger);
+    is(editor.getCaretPosition().line, 5,
+      "The source editor caret position was incorrect (3).");
+    is(sources.selectedLabel, "test-script-switching-02.js",
+      "The currently selected source is incorrect (3).");
+    is(stackframes.selectedIndex, 3,
+      "The currently selected stackframe is incorrect (3).");
+
+
+    EventUtils.sendMouseEvent({ type: "mousedown" },
+      stackframes.selectedItem.target,
+      gDebugger);
+
+
+    EventUtils.sendKey("UP", gDebugger);
+    is(editor.getCaretPosition().line, 5,
+      "The source editor caret position was incorrect (4).");
+    is(sources.selectedLabel, "test-script-switching-02.js",
+      "The currently selected source is incorrect (4).");
+    is(stackframes.selectedIndex, 2,
+      "The currently selected stackframe is incorrect (4).");
+
+    gDebugger.addEventListener("Debugger:SourceShown", function _onEvent(aEvent) {
+      gDebugger.removeEventListener(aEvent.type, _onEvent);
+
+      is(editor.getCaretPosition().line, 4,
+        "The source editor caret position was incorrect (5).");
+      is(sources.selectedLabel, "test-script-switching-01.js",
+        "The currently selected source is incorrect (5).");
+      is(stackframes.selectedIndex, 1,
+        "The currently selected stackframe is incorrect (5).");
+
+      EventUtils.sendKey("UP", gDebugger);
+
+      is(editor.getCaretPosition().line, 4,
+        "The source editor caret position was incorrect (6).");
+      is(sources.selectedLabel, "test-script-switching-01.js",
+        "The currently selected source is incorrect (6).");
+      is(stackframes.selectedIndex, 0,
+        "The currently selected stackframe is incorrect (6).");
+
+      gDebugger.addEventListener("Debugger:SourceShown", function _onEvent(aEvent) {
+        gDebugger.removeEventListener(aEvent.type, _onEvent);
+
+        is(editor.getCaretPosition().line, 5,
+          "The source editor caret position was incorrect (7).");
+        is(sources.selectedLabel, "test-script-switching-02.js",
+          "The currently selected source is incorrect (7).");
+        is(stackframes.selectedIndex, 3,
+          "The currently selected stackframe is incorrect (7).");
+
+        gDebugger.addEventListener("Debugger:SourceShown", function _onEvent(aEvent) {
+          gDebugger.removeEventListener(aEvent.type, _onEvent);
+
+          is(editor.getCaretPosition().line, 4,
+            "The source editor caret position was incorrect (8).");
+          is(sources.selectedLabel, "test-script-switching-01.js",
+            "The currently selected source is incorrect (8).");
+          is(stackframes.selectedIndex, 0,
+            "The currently selected stackframe is incorrect (8).");
+
+          closeDebuggerAndFinish();
+        });
+
+        EventUtils.sendKey("HOME", gDebugger);
+      });
+
+      EventUtils.sendKey("END", gDebugger);
+    });
+
+    EventUtils.sendKey("UP", gDebugger);
+  }
+
+  registerCleanupFunction(function() {
+    removeTab(gTab);
+    gPane = null;
+    gTab = null;
+    gDebuggee = null;
+    gDebugger = null;
+  });
+}
--- a/browser/devtools/netmonitor/netmonitor-controller.js
+++ b/browser/devtools/netmonitor/netmonitor-controller.js
@@ -526,16 +526,24 @@ let L10N = new ViewHelpers.L10N(NET_STRI
  * Shortcuts for accessing various network monitor preferences.
  */
 let Prefs = new ViewHelpers.Prefs("devtools.netmonitor", {
   networkDetailsWidth: ["Int", "panes-network-details-width"],
   networkDetailsHeight: ["Int", "panes-network-details-height"]
 });
 
 /**
+ * Returns true if this is document is in RTL mode.
+ * @return boolean
+ */
+XPCOMUtils.defineLazyGetter(window, "isRTL", function() {
+  return window.getComputedStyle(document.documentElement, null).direction == "rtl";
+});
+
+/**
  * Convenient way of emitting events from the panel window.
  */
 EventEmitter.decorate(this);
 
 /**
  * Preliminary setup for the NetMonitorController object.
  */
 NetMonitorController.TargetEventsHandler = new TargetEventsHandler();
--- a/browser/devtools/netmonitor/netmonitor-view.js
+++ b/browser/devtools/netmonitor/netmonitor-view.js
@@ -892,32 +892,33 @@ create({ constructor: RequestsMenuView, 
 
     // Apply CSS transforms to each waterfall in this container totalTime
     // accurately translate and resize as needed.
     for (let [, { target, attachment }] of this._cache) {
       let timingsNode = $(".requests-menu-timings", target);
       let startCapNode = $(".requests-menu-timings-cap.start", target);
       let endCapNode = $(".requests-menu-timings-cap.end", target);
       let totalNode = $(".requests-menu-timings-total", target);
+      let direction = window.isRTL ? -1 : 1;
 
       // Render the timing information at a specific horizontal translation
       // based on the delta to the first monitored event network.
-      let translateX = "translateX(" + attachment.startedDeltaMillis + "px)";
+      let translateX = "translateX(" + (direction * attachment.startedDeltaMillis) + "px)";
 
       // Based on the total time passed until the last request, rescale
       // all the waterfalls to a reasonable size.
       let scaleX = "scaleX(" + scale + ")";
 
       // Certain nodes should not be scaled, even if they're children of
       // another scaled node. In this case, apply a reversed transformation.
       let revScaleX = "scaleX(" + (1 / scale) + ")";
 
       timingsNode.style.transform = scaleX + " " + translateX;
-      startCapNode.style.transform = revScaleX + " translateX(0.5px)";
-      endCapNode.style.transform = revScaleX + " translateX(-0.5px)";
+      startCapNode.style.transform = revScaleX + " translateX(" + (direction * 0.5) + "px)";
+      endCapNode.style.transform = revScaleX + " translateX(" + (direction * -0.5) + "px)";
       totalNode.style.transform = revScaleX;
     }
   },
 
   /**
    * Creates the labels displayed on the waterfall header in this container.
    *
    * @param number aScale
@@ -942,20 +943,21 @@ create({ constructor: RequestsMenuView, 
       if (scaledStep < REQUESTS_WATERFALL_HEADER_TICKS_SPACING_MIN) {
         timingStep <<= 1;
         continue;
       }
       optimalTickIntervalFound = true;
 
       // Insert one label for each division on the current scale.
       let fragment = document.createDocumentFragment();
+      let direction = window.isRTL ? -1 : 1;
 
       for (let x = 0; x < availableWidth; x += scaledStep) {
         let divisionMS = (x / aScale).toFixed(0);
-        let translateX = "translateX(" + (x | 0) + "px)";
+        let translateX = "translateX(" + ((direction * x) | 0) + "px)";
 
         let node = document.createElement("label");
         let text = L10N.getFormatStr("networkMenu.divisionMS", divisionMS);
         node.className = "plain requests-menu-timings-division";
         node.style.transform = translateX;
 
         node.setAttribute("value", text);
         fragment.appendChild(node);
@@ -1004,17 +1006,18 @@ create({ constructor: RequestsMenuView, 
         continue;
       }
       optimalTickIntervalFound = true;
 
       // Insert one pixel for each division on each scale.
       for (let i = 1; i <= REQUESTS_WATERFALL_BACKGROUND_TICKS_SCALES; i++) {
         let increment = scaledStep * Math.pow(2, i);
         for (let x = 0; x < canvasWidth; x += increment) {
-          data32[x | 0] = (alphaComponent << 24) | (b << 16) | (g << 8) | r;
+          let position = (window.isRTL ? canvasWidth - x : x) | 0;
+          data32[position] = (alphaComponent << 24) | (b << 16) | (g << 8) | r;
         }
         alphaComponent += REQUESTS_WATERFALL_BACKGROUND_TICKS_OPACITY_ADD;
       }
     }
 
     // Flush the image data and cache the waterfall background.
     pixelArray.set(buf8);
     ctx.putImageData(imageData, 0, 0);
@@ -1030,16 +1033,19 @@ create({ constructor: RequestsMenuView, 
       waterfallNode.style.backgroundImage = this._cachedWaterfallBackground;
     }
   },
 
   /**
    * Hides the overflowing columns in the requests table.
    */
   _hideOverflowingColumns: function() {
+    if (window.isRTL) {
+      return;
+    }
     let table = $("#network-table");
     let toolbar = $("#requests-menu-toolbar");
     let columns = [
       ["#requests-menu-waterfall-header-box", "waterfall-overflows"],
       ["#requests-menu-size-header-box", "size-overflows"],
       ["#requests-menu-type-header-box", "type-overflows"],
       ["#requests-menu-domain-header-box", "domain-overflows"]
     ];
@@ -1197,17 +1203,21 @@ create({ constructor: RequestsMenuView, 
    * @return number
    */
   get _waterfallWidth() {
     if (this._cachedWaterfallWidth == 0) {
       let container = $("#requests-menu-toolbar");
       let waterfall = $("#requests-menu-waterfall-header-box");
       let containerBounds = container.getBoundingClientRect();
       let waterfallBounds = waterfall.getBoundingClientRect();
-      this._cachedWaterfallWidth = containerBounds.width - waterfallBounds.left;
+      if (!window.isRTL) {
+        this._cachedWaterfallWidth = containerBounds.width - waterfallBounds.left;
+      } else {
+        this._cachedWaterfallWidth = waterfallBounds.right;
+      }
     }
     return this._cachedWaterfallWidth;
   },
 
   _cache: null,
   _summary: null,
   _canvas: null,
   _ctx: null,
--- a/browser/devtools/netmonitor/netmonitor.css
+++ b/browser/devtools/netmonitor/netmonitor.css
@@ -1,13 +1,17 @@
 /* vim:set ts=2 sw=2 sts=2 et: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#toolbar-labels {
+  overflow: hidden;
+}
+
 #details-pane-toggle[disabled] {
   visibility: hidden;
 }
 
 #response-content-image-box {
   overflow: auto;
 }
 
--- a/browser/devtools/shared/widgets/BreadcrumbsWidget.jsm
+++ b/browser/devtools/shared/widgets/BreadcrumbsWidget.jsm
@@ -6,16 +6,17 @@
 "use strict";
 
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 const ENSURE_SELECTION_VISIBLE_DELAY = 50; // ms
 
 Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
+Cu.import("resource:///modules/devtools/shared/event-emitter.js");
 
 this.EXPORTED_SYMBOLS = ["BreadcrumbsWidget"];
 
 /**
  * A breadcrumb-like list of items.
  *
  * You can use this widget alone, but it works great with a MenuContainer!
  * In that case, you should never need to access the methods in the
@@ -40,25 +41,30 @@ this.BreadcrumbsWidget = function Breadc
   this._parent = aNode;
 
   // Create an internal arrowscrollbox container.
   this._list = this.document.createElement("arrowscrollbox");
   this._list.className = "breadcrumbs-widget-container";
   this._list.setAttribute("flex", "1");
   this._list.setAttribute("orient", "horizontal");
   this._list.setAttribute("clicktoscroll", "true")
+  this._list.addEventListener("keypress", e => this.emit("keyPress", e), false);
+  this._list.addEventListener("mousedown", e => this.emit("mousePress", e), false);
   this._parent.appendChild(this._list);
 
   // By default, hide the arrows. We let the arrowscrollbox show them
   // in case of overflow.
   this._list._scrollButtonUp.collapsed = true;
   this._list._scrollButtonDown.collapsed = true;
   this._list.addEventListener("underflow", this._onUnderflow.bind(this), false);
   this._list.addEventListener("overflow", this._onOverflow.bind(this), false);
 
+  // This widget emits events that can be handled in a MenuContainer.
+  EventEmitter.decorate(this);
+
   // Delegate some of the associated node's methods to satisfy the interface
   // required by MenuContainer instances.
   ViewHelpers.delegateWidgetAttributeMethods(this, aNode);
   ViewHelpers.delegateWidgetEventMethods(this, aNode);
 };
 
 BreadcrumbsWidget.prototype = {
   /**
--- a/browser/devtools/shared/widgets/SideMenuWidget.jsm
+++ b/browser/devtools/shared/widgets/SideMenuWidget.jsm
@@ -74,24 +74,16 @@ this.SideMenuWidget = function SideMenuW
 
 SideMenuWidget.prototype = {
   /**
    * Specifies if groups in this container should be sorted alphabetically.
    */
   sortedGroups: true,
 
   /**
-   * Specifies if when an item is selected in this container (via the
-   * selectedItem setter), that respective item should be focused as well.
-   * You can enable this flag, for example, to maintain a certain node
-   * selected but visually indicate a different selection in this container.
-   */
-  preventFocusOnSelection: false,
-
-  /**
    * Specifies if this container should try to keep the selected item visible.
    * (For example, when new items are added the selection is brought into view).
    */
   maintainSelectionVisible: true,
 
   /**
    * Specifies that the container viewport should be "stuck" to the
    * bottom. That is, the container is automatically scrolled down to
@@ -137,19 +129,16 @@ SideMenuWidget.prototype = {
 
     let group = this._getMenuGroupForName(aGroup);
     let item = this._getMenuItemForGroup(group, aContents, aTooltip);
     let element = item.insertSelfAt(aIndex);
 
     if (this.maintainSelectionVisible) {
       this.ensureSelectionIsVisible({ withGroup: true, delayed: true });
     }
-    if (this._orderedMenuElementsArray.length == 1) {
-      this._list.focus();
-    }
     if (maintainScrollAtBottom) {
       this._list.scrollTop = this._list.scrollHeight;
     }
 
     return element;
   },
 
   /**
@@ -221,17 +210,16 @@ SideMenuWidget.prototype = {
     if (!aChild) {
       this._selectedItem = null;
     }
     for (let node of menuArray) {
       if (node == aChild) {
         node.classList.add("selected");
         node.parentNode.classList.add("selected");
         this._selectedItem = node;
-        !this.preventFocusOnSelection && node.focus();
       } else {
         node.classList.remove("selected");
         node.parentNode.classList.remove("selected");
       }
     }
 
     // Repeated calls to ensureElementIsVisible would interfere with each other
     // and may sometimes result in incorrect scroll positions.
--- a/browser/devtools/shared/widgets/ViewHelpers.jsm
+++ b/browser/devtools/shared/widgets/ViewHelpers.jsm
@@ -582,17 +582,17 @@ MenuContainer.prototype = {
   /**
    * Gets the element node or widget associated with this container.
    * @return nsIDOMNode | object
    */
   get node() this._container,
 
   /**
    * Prepares an item to be added to this container. This allows for a large
-   * number of items to be batched up before alphabetically sorted and added.
+   * number of items to be batched up before being sorted and added.
    *
    * If the "staged" flag is not set to true, the item will be immediately
    * inserted at the correct position in this container, so that all the items
    * remain sorted. This can (possibly) be much slower than batching up
    * multiple items.
    *
    * By default, this container assumes that all the items should be displayed
    * sorted by their label. This can be overridden with the "index" flag,
@@ -620,23 +620,23 @@ MenuContainer.prototype = {
    *         The item associated with the displayed element if an unstaged push,
    *         undefined if the item was staged for a later commit.
    */
   push: function(aContents, aOptions = {}) {
     let item = new MenuItem(aOptions.attachment, aContents);
 
     // Batch the item to be added later.
     if (aOptions.staged) {
-      // Commit operations will ignore any specified index.
+      // An ulterior commit operation will ignore any specified index.
       delete aOptions.index;
       return void this._stagedItems.push({ item: item, options: aOptions });
     }
     // Find the target position in this container and insert the item there.
     if (!("index" in aOptions)) {
-      return this._insertItemAt(this._findExpectedIndex(item), item, aOptions);
+      return this._insertItemAt(this._findExpectedIndexFor(item), item, aOptions);
     }
     // Insert the item at the specified index. If negative or out of bounds,
     // the item will be simply appended.
     return this._insertItemAt(aOptions.index, item, aOptions);
   },
 
   /**
    * Flushes all the prepared items into this container.
@@ -664,24 +664,23 @@ MenuContainer.prototype = {
   /**
    * Updates this container to reflect the information provided by the
    * currently selected item.
    *
    * @return boolean
    *         True if a selected item was available, false otherwise.
    */
   refresh: function() {
-    let selectedValue = this.selectedValue;
-    if (!selectedValue) {
+    let selectedItem = this.selectedItem;
+    if (!selectedItem) {
       return false;
     }
-    let entangledLabel = this.getItemByValue(selectedValue)._label;
     this._container.removeAttribute("notice");
-    this._container.setAttribute("label", entangledLabel);
-    this._container.setAttribute("tooltiptext", selectedValue);
+    this._container.setAttribute("label", selectedItem._label);
+    this._container.setAttribute("tooltiptext", selectedItem._value);
     return true;
   },
 
   /**
    * Immediately removes the specified item from this container.
    *
    * @param MenuItem aItem
    *        The item associated with the element to remove.
@@ -941,17 +940,23 @@ MenuContainer.prototype = {
    * Selects the element with the entangled item in this container.
    * @param MenuItem aItem
    */
   set selectedItem(aItem) {
     // A falsy item is allowed to invalidate the current selection.
     let targetElement = aItem ? aItem._target : null;
     let prevElement = this._container.selectedItem;
 
-    // Prevent selecting the same item again, so return early.
+    // Make sure the currently selected item's target element is also focused.
+    if (this.autoFocusOnSelection && targetElement) {
+      targetElement.focus();
+    }
+
+    // Prevent selecting the same item again and avoid dispatching
+    // a redundant selection event, so return early.
     if (targetElement == prevElement) {
       return;
     }
     this._container.selectedItem = targetElement;
     ViewHelpers.dispatchEvent(targetElement || prevElement, "select", aItem);
   },
 
   /**
@@ -977,16 +982,52 @@ MenuContainer.prototype = {
   /**
    * Selects the element with the specified value in this container.
    * @param string aValue
    */
   set selectedValue(aValue)
     this.selectedItem = this._itemsByValue.get(aValue),
 
   /**
+   * Focus this container the first time an element is inserted?
+   *
+   * If this flag is set to true, then when the first item is inserted in
+   * this container (and thus it's the only item available), its corresponding
+   * target element is focused as well.
+   */
+  autoFocusOnFirstItem: true,
+
+  /**
+   * Focus on selection?
+   *
+   * If this flag is set to true, then whenever an item is selected in
+   * this container (e.g. via the selectedIndex or selectedItem setters),
+   * its corresponding target element is focused as well.
+   *
+   * You can disable this flag, for example, to maintain a certain node
+   * focused but visually indicate a different selection in this container.
+   */
+  autoFocusOnSelection: true,
+
+  /**
+   * Focus on input (e.g. mouse click)?
+   *
+   * If this flag is set to true, then whenever an item receives user input in
+   * this container, its corresponding target element is focused as well.
+   */
+  autoFocusOnInput: true,
+
+  /**
+   * The number of elements in this container to jump when Page Up or Page Down
+   * keys are pressed. If falsy, then the page size will be based on the
+   * number of visible items in the container.
+   */
+  pageSize: 0,
+
+  /**
    * Focuses the first visible item in this container.
    */
   focusFirstVisibleItem: function() {
     this.focusItemAtDelta(-this.itemCount);
   },
 
   /**
    * Focuses the last visible item in this container.
@@ -1318,17 +1359,17 @@ MenuContainer.prototype = {
    * Finds the expected item index in this container based on the default
    * sort predicate.
    *
    * @param MenuItem aItem
    *        The item to get the expected index for.
    * @return number
    *         The expected item index.
    */
-  _findExpectedIndex: function(aItem) {
+  _findExpectedIndexFor: function(aItem) {
     let itemCount = this.itemCount;
 
     for (let i = 0; i < itemCount; i++) {
       if (this._currentSortPredicate(this.getItemAtIndex(i), aItem) > 0) {
         return i;
       }
     }
     return itemCount;
@@ -1362,16 +1403,19 @@ MenuContainer.prototype = {
       aItem._value,
       aItem._description,
       aItem.attachment));
 
     // Handle any additional options after entangling the item.
     if (!this._currentFilterPredicate(aItem)) {
       aItem._target.hidden = true;
     }
+    if (this.autoFocusOnFirstItem && this._itemsByElement.size == 1) {
+      aItem._target.focus();
+    }
     if (aOptions.attributes) {
       aItem.setAttributes(aOptions.attributes, aItem._target);
     }
     if (aOptions.finalize) {
       aItem.finalize = aOptions.finalize;
     }
 
     // Return the item associated with the displayed element.
@@ -1420,36 +1464,31 @@ MenuContainer.prototype = {
    */
   _unlinkItem: function(aItem) {
     this._itemsByLabel.delete(aItem._label);
     this._itemsByValue.delete(aItem._value);
     this._itemsByElement.delete(aItem._target);
   },
 
   /**
-   * The number of elements in this container to jump when Page Up or Page Down
-   * keys are pressed. If falsy, then the page size will be based on the
-   * number of visible items in the container.
-   */
-  pageSize: 0,
-
-  /**
    * The keyPress event listener for this container.
    * @param string aName
    * @param KeyboardEvent aEvent
    */
   _onWidgetKeyPress: function(aName, aEvent) {
     // Prevent scrolling when pressing navigation keys.
     ViewHelpers.preventScrolling(aEvent);
 
     switch (aEvent.keyCode) {
       case aEvent.DOM_VK_UP:
+      case aEvent.DOM_VK_LEFT:
         this.focusPrevItem();
         return;
       case aEvent.DOM_VK_DOWN:
+      case aEvent.DOM_VK_RIGHT:
         this.focusNextItem();
         return;
       case aEvent.DOM_VK_PAGE_UP:
         this.focusItemAtDelta(-(this.pageSize || (this.itemCount / PAGE_SIZE_ITEM_COUNT_RATIO)));
         return;
       case aEvent.DOM_VK_PAGE_DOWN:
         this.focusItemAtDelta(+(this.pageSize || (this.itemCount / PAGE_SIZE_ITEM_COUNT_RATIO)));
         return;
@@ -1467,20 +1506,23 @@ MenuContainer.prototype = {
    * @param string aName
    * @param MouseEvent aEvent
    */
   _onWidgetMousePress: function(aName, aEvent) {
     if (aEvent.button != 0) {
       // Only allow left-click to trigger this event.
       return;
     }
+
     let item = this.getItemForElement(aEvent.target);
     if (item) {
       // The container is not empty and we clicked on an actual item.
       this.selectedItem = item;
+      // Make sure the current event's target element is also focused.
+      this.autoFocusOnInput && item._target.focus();
     }
   },
 
   /**
    * The predicate used when filtering items. By default, all items in this
    * view are visible.
    *
    * @param MenuItem aItem
--- a/browser/devtools/shared/widgets/widgets.css
+++ b/browser/devtools/shared/widgets/widgets.css
@@ -4,16 +4,20 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* BreacrumbsWidget */
 
 .breadcrumbs-widget-item {
   direction: ltr;
 }
 
+.breadcrumbs-widget-item {
+  -moz-user-focus: normal;
+}
+
 /* SideMenuWidget */
 
 .side-menu-widget-container {
   overflow-x: hidden;
   overflow-y: auto;
 }
 
 .side-menu-widget-item-contents {
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_603750_websocket.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_603750_websocket.js
@@ -4,73 +4,36 @@
  * http://creativecommons.org/publicdomain/zero/1.0/
  *
  * Contributor(s):
  *  Mihai Șucan <mihai.sucan@gmail.com>
  *
  * ***** END LICENSE BLOCK ***** */
 
 const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-bug-603750-websocket.html";
-const pref_ws = "network.websocket.enabled";
-const pref_block = "network.websocket.override-security-block";
 
-let errors = 0;
-let lastWindowId = 0;
-let oldPref_ws;
-
-let TestObserver = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
-
-  observe: function test_observe(aSubject)
-  {
-    if (!(aSubject instanceof Ci.nsIScriptError)) {
-      return;
-    }
-
-    is(aSubject.category, "Web Socket", "received a Web Socket error");
-    isnot(aSubject.sourceName.indexOf("test-bug-603750-websocket.js"), -1,
-          "sourceName is correct");
-
-    if (++errors == 2) {
-      executeSoon(performTest);
-    }
-    else {
-      lastWindowId = aSubject.outerWindowID;
-    }
-  }
-};
-
-function tabLoad(aEvent) {
-  browser.removeEventListener(aEvent.type, tabLoad, true);
-
-  openConsole(null, function(aHud) {
-    hud = aHud;
-    Services.console.registerListener(TestObserver);
-    content.location = TEST_URI;
-  });
+function test() {
+  addTab("data:text/html;charset=utf-8,Web Console test for bug 603750: Web Socket errors");
+  browser.addEventListener("load", function tabLoad() {
+    browser.removeEventListener("load", tabLoad, true);
+    openConsole(null, (hud) => {
+      content.location = TEST_URI;
+      info("waiting for websocket errors");
+      waitForMessages({
+        webconsole: hud,
+        messages: [
+          {
+            text: "ws://0.0.0.0:81",
+            source: { url: "test-bug-603750-websocket.js" },
+            category: CATEGORY_JS,
+            severity: SEVERITY_ERROR,
+          },
+          {
+            text: "ws://0.0.0.0:82",
+            source: { url: "test-bug-603750-websocket.js" },
+            category: CATEGORY_JS,
+            severity: SEVERITY_ERROR,
+          },
+        ]}).then(finishTest);
+    });
+  }, true);
 }
 
-function performTest() {
-  Services.console.unregisterListener(TestObserver);
-  Services.prefs.setBoolPref(pref_ws, oldPref_ws);
-
-  waitForSuccess({
-    name: "websocket error messages displayed",
-    validatorFn: function()
-    {
-      let textContent = hud.outputNode.textContent;
-      return textContent.indexOf("ws://0.0.0.0:81") > -1 &&
-             textContent.indexOf("ws://0.0.0.0:82") > -1;
-    },
-    successFn: finishTest,
-    failureFn: finishTest,
-  });
-}
-
-function test() {
-  oldPref_ws = Services.prefs.getBoolPref(pref_ws);
-
-  Services.prefs.setBoolPref(pref_ws, true);
-
-  addTab("data:text/html;charset=utf-8,Web Console test for bug 603750: Web Socket errors");
-  browser.addEventListener("load", tabLoad, true);
-}
-
--- a/browser/devtools/webconsole/test/head.js
+++ b/browser/devtools/webconsole/test/head.js
@@ -876,16 +876,18 @@ function getMessageElementText(aElement)
  *            Provide this if you want to match a console.time() message.
  *            - consoleTimeEnd: same as above, but for console.timeEnd().
  *            - consoleDir: boolean, set to |true| to match a console.dir()
  *            message.
  *            - longString: boolean, set to |true} to match long strings in the
  *            message.
  *            - objects: boolean, set to |true| if you expect inspectable
  *            objects in the message.
+ *            - source: object that can hold one property: url. This is used to
+ *            match the source URL of the message.
  * @return object
  *         A Promise object is returned once the messages you want are found.
  *         The promise is resolved with the array of rule objects you give in
  *         the |messages| property. Each objects is the same as provided, with
  *         additional properties:
  *         - matched: a Set of web console messages that matched the rule.
  *         - clickableElements: a list of inspectable objects. This is available
  *         if any of the following properties are present in the rule:
@@ -976,17 +978,17 @@ function waitForMessages(aOptions)
 
     return true;
   }
 
   function checkConsoleTimeEnd(aRule, aElement)
   {
     let elemText = getMessageElementText(aElement);
     let time = aRule.consoleTimeEnd;
-    let regex = new RegExp(time + ": \\d+ms");
+    let regex = new RegExp(time + ": -?\\d+ms");
 
     if (!checkText(regex, elemText)) {
       return false;
     }
 
     aRule.category = CATEGORY_WEBDEV;
     aRule.severity = SEVERITY_LOG;
 
@@ -1008,16 +1010,26 @@ function waitForMessages(aOptions)
     if (!iframe) {
       ok(false, "console.dir message has no iframe");
       return false;
     }
 
     return true;
   }
 
+  function checkSource(aRule, aElement)
+  {
+    let location = aElement.querySelector(".webconsole-location");
+    if (!location) {
+      return false;
+    }
+
+    return checkText(aRule.source.url, location.getAttribute("title"));
+  }
+
   function checkMessage(aRule, aElement)
   {
     let elemText = getMessageElementText(aElement);
 
     if (aRule.text && !checkText(aRule.text, elemText)) {
       return false;
     }
 
@@ -1036,16 +1048,20 @@ function waitForMessages(aOptions)
     if (aRule.consoleTimeEnd && !checkConsoleTimeEnd(aRule, aElement)) {
       return false;
     }
 
     if (aRule.consoleDir && !checkConsoleDir(aRule, aElement)) {
       return false;
     }
 
+    if (aRule.source && !checkSource(aRule, aElement)) {
+      return false;
+    }
+
     let partialMatch = !!(aRule.consoleTrace || aRule.consoleTime ||
                           aRule.consoleTimeEnd);
 
     if (aRule.category && aElement.category != aRule.category) {
       if (partialMatch) {
         is(aElement.category, aRule.category,
            "message category for rule: " + displayRule(aRule));
         displayErrorContext(aRule, aElement);
--- a/browser/devtools/webconsole/webconsole.js
+++ b/browser/devtools/webconsole/webconsole.js
@@ -1187,24 +1187,44 @@ WebConsoleFrame.prototype = {
   {
     // Warnings and legacy strict errors become warnings; other types become
     // errors.
     let severity = SEVERITY_ERROR;
     if (aScriptError.warning || aScriptError.strict) {
       severity = SEVERITY_WARNING;
     }
 
+    let objectActors = new Set();
+
+    // Gather the actor IDs.
+    for (let prop of ["errorMessage", "lineText"]) {
+      let grip = aScriptError[prop];
+      if (WebConsoleUtils.isActorGrip(grip)) {
+        objectActors.add(grip.actor);
+      }
+    }
+
+    let errorMessage = aScriptError.errorMessage;
+    if (errorMessage.type && errorMessage.type == "longString") {
+      errorMessage = errorMessage.initial;
+    }
+
     let node = this.createMessageNode(aCategory, severity,
-                                      aScriptError.errorMessage,
+                                      errorMessage,
                                       aScriptError.sourceName,
                                       aScriptError.lineNumber, null, null,
                                       aScriptError.timeStamp);
     if (aScriptError.private) {
       node.setAttribute("private", true);
     }
+
+    if (objectActors.size > 0) {
+      node._objectActors = objectActors;
+    }
+
     return node;
   },
 
   /**
    * Handle PageError objects received from the server. This method outputs the
    * given error.
    *
    * @param nsIScriptError aPageError
@@ -1220,20 +1240,42 @@ WebConsoleFrame.prototype = {
    * Handle log messages received from the server. This method outputs the given
    * message.
    *
    * @param object aPacket
    *        The message packet received from the server.
    */
   handleLogMessage: function WCF_handleLogMessage(aPacket)
   {
-    this.outputMessage(CATEGORY_JS, () => {
-      return this.createMessageNode(CATEGORY_JS, SEVERITY_LOG, aPacket.message,
-                                    null, null, null, null, aPacket.timeStamp);
-    });
+    if (aPacket.message) {
+      this.outputMessage(CATEGORY_JS, this._reportLogMessage, [aPacket]);
+    }
+  },
+
+  /**
+   * Display log messages received from the server.
+   *
+   * @private
+   * @param object aPacket
+   *        The message packet received from the server.
+   * @return nsIDOMElement
+   *         The message element to render for the given log message.
+   */
+  _reportLogMessage: function WCF__reportLogMessage(aPacket)
+  {
+    let msg = aPacket.message;
+    if (msg.type && msg.type == "longString") {
+      msg = msg.initial;
+    }
+    let node = this.createMessageNode(CATEGORY_JS, SEVERITY_LOG, msg, null,
+                                      null, null, null, aPacket.timeStamp);
+    if (WebConsoleUtils.isActorGrip(aPacket.message)) {
+      node._objectActors = new Set([aPacket.message.actor]);
+    }
+    return node;
   },
 
   /**
    * Log network event.
    *
    * @param object aActorId
    *        The network event actor ID to log.
    * @return nsIDOMElement|null
@@ -1987,16 +2029,32 @@ WebConsoleFrame.prototype = {
     else if (category == CATEGORY_WEBDEV &&
              methodOrNode == this.logConsoleAPIMessage) {
       args[0].arguments.forEach((aValue) => {
         if (WebConsoleUtils.isActorGrip(aValue)) {
           this._releaseObject(aValue.actor);
         }
       });
     }
+    else if (category == CATEGORY_JS &&
+             methodOrNode == this.reportPageError) {
+      let pageError = args[1];
+      for (let prop of ["errorMessage", "lineText"]) {
+        let grip = pageError[prop];
+        if (WebConsoleUtils.isActorGrip(grip)) {
+          this._releaseObject(grip.actor);
+        }
+      }
+    }
+    else if (category == CATEGORY_JS &&
+             methodOrNode == this._reportLogMessage) {
+      if (WebConsoleUtils.isActorGrip(args[0].message)) {
+        this._releaseObject(args[0].message.actor);
+      }
+    }
   },
 
   /**
    * Ensures that the number of message nodes of type aCategory don't exceed that
    * category's line limit by removing old messages as needed.
    *
    * @param integer aCategory
    *        The category of message nodes to prune if needed.
--- a/browser/metro/base/content/contenthandlers/FindHandler.js
+++ b/browser/metro/base/content/contenthandlers/FindHandler.js
@@ -35,16 +35,19 @@ var FindHandler = {
         break;
     }
 
     if (findResult == Ci.nsITypeAheadFind.FIND_NOTFOUND) {
       sendAsyncMessage("FindAssist:Show", { rect: null , result: findResult });
       return;
     }
 
+    if (!this._fastFind.currentWindow)
+      return;
+
     let selection = this._fastFind.currentWindow.getSelection();
     if (!selection.rangeCount || selection.isCollapsed) {
       // The selection can be into an input or a textarea element
       let nodes = content.document.querySelectorAll("input[type='text'], textarea");
       for (let i = 0; i < nodes.length; i++) {
         let node = nodes[i];
         if (node instanceof Ci.nsIDOMNSEditableElement && node.editor) {
           selection = node.editor.selectionController.getSelection(Ci.nsISelectionController.SELECTION_NORMAL);
--- a/browser/metro/base/content/helperui/FindHelperUI.js
+++ b/browser/metro/base/content/helperui/FindHelperUI.js
@@ -1,13 +1,17 @@
 // -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
 /* 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/. */
 
+/* We don't support zooming yet, disable Animated zoom by clamping it to the default zoom. */
+const kBrowserFindZoomLevelMin = 1;
+const kBrowserFindZoomLevelMax = 1;
+
 var FindHelperUI = {
   type: "find",
   commands: {
     next: "cmd_findNext",
     previous: "cmd_findPrevious",
     close: "cmd_findClose"
   },
 
@@ -170,18 +174,18 @@ var FindHelperUI = {
     if (!aElementRect || !autozoomEnabled)
       return;
 
     if (Browser.selectedTab.allowZoom) {
       let zoomLevel = Browser._getZoomLevelForRect(aElementRect);
 
       // Clamp the zoom level relatively to the default zoom level of the page
       let defaultZoomLevel = Browser.selectedTab.getDefaultZoomLevel();
-      zoomLevel = Util.clamp(zoomLevel, (defaultZoomLevel * kBrowserFormZoomLevelMin),
-                                        (defaultZoomLevel * kBrowserFormZoomLevelMax));
+      zoomLevel = Util.clamp(zoomLevel, (defaultZoomLevel * kBrowserFindZoomLevelMin),
+                                        (defaultZoomLevel * kBrowserFindZoomLevelMax));
       zoomLevel = Browser.selectedTab.clampZoomLevel(zoomLevel);
 
       let zoomRect = Browser._getZoomRectForPoint(aElementRect.center().x, aElementRect.y, zoomLevel);
       AnimatedZoom.animateTo(zoomRect);
     } else {
       // Even if zooming is disabled we could need to reposition the view in
       // order to keep the element on-screen
       let zoomRect = Browser._getZoomRectForPoint(aElementRect.center().x, aElementRect.y, getBrowser().scale);
--- a/browser/metro/base/content/helperui/FormHelperUI.js
+++ b/browser/metro/base/content/helperui/FormHelperUI.js
@@ -3,19 +3,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /*
  * Responsible for filling in form information.
  *  - displays select popups
  *  - Provides autocomplete box for input fields.
  */
 
-const kBrowserFormZoomLevelMin = 0.8;
-const kBrowserFormZoomLevelMax = 2.0;
-
 var FormHelperUI = {
   _debugEvents: false,
   _currentBrowser: null,
   _currentElement: null,
   _currentCaretRect: null,
   _currentElementRect: null,
   _open: false,
 
deleted file mode 100644
index 308b94ca0bbc7b39c742604b8baba829d26b91e0..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/themes/linux/devtools/netmonitor.css
+++ b/browser/themes/linux/devtools/netmonitor.css
@@ -24,22 +24,28 @@
 .requests-menu-header:first-child {
   -moz-padding-start: 4px;
 }
 
 .requests-menu-subitem {
   padding: 4px;
 }
 
-.requests-menu-header:not(:last-child),
-.requests-menu-subitem:not(:last-child) {
+.requests-menu-header:not(:last-child):-moz-locale-dir(ltr),
+.requests-menu-subitem:not(:last-child):-moz-locale-dir(ltr) {
   -moz-border-end: 1px solid hsla(210,8%,5%,.25);
   box-shadow: 1px 0 0 hsla(210,16%,76%,.1);
 }
 
+.requests-menu-header:not(:last-child):-moz-locale-dir(rtl),
+.requests-menu-subitem:not(:last-child):-moz-locale-dir(rtl) {
+  -moz-border-end: 1px solid hsla(210,8%,5%,.25);
+  box-shadow: -1px 0 0 hsla(210,16%,76%,.1);
+}
+
 .requests-menu-header-button {
   -moz-appearance: none;
   background: none;
   min-width: 20px;
   min-height: 31px; /* Remaining 1px comes from border of the toolbar. */
   margin: 0;
   border: none;
   padding: 0;
@@ -166,63 +172,101 @@ box.requests-menu-status[code^="5"] {
 }
 
 .requests-menu-timings-division {
   width: 100px;
   padding-top: 2px;
   -moz-padding-start: 4px;
   -moz-border-start: 1px dotted #999;
   font-size: 75%;
-  text-align: left;
   pointer-events: none;
 }
 
 .requests-menu-timings-division:not(:first-child) {
   -moz-margin-start: -100px !important; /* Don't affect layout. */
 }
 
+.requests-menu-timings-division:-moz-locale-dir(ltr) {
+  transform-origin: left center;
+}
+
+.requests-menu-timings-division:-moz-locale-dir(rtl) {
+  transform-origin: right center;
+}
+
 /* Network requests table: waterfall items */
 
 .requests-menu-subitem.requests-menu-waterfall {
   -moz-padding-start: 4px;
   -moz-padding-end: 4px;
   background-repeat: repeat-y; /* Background created on a <canvas> in js. */
   margin-top: -1px; /* Compensate borders. */
   margin-bottom: -1px;
 }
 
-.requests-menu-timings {
+.requests-menu-subitem.requests-menu-waterfall:-moz-locale-dir(rtl) {
+  background-position: right center;
+}
+
+.requests-menu-timings:-moz-locale-dir(ltr) {
   transform-origin: left center;
 }
 
+.requests-menu-timings:-moz-locale-dir(rtl) {
+  transform-origin: right center;
+}
+
+.requests-menu-timings-total:-moz-locale-dir(ltr) {
+  transform-origin: left center;
+}
+
+.requests-menu-timings-total:-moz-locale-dir(rtl) {
+  transform-origin: right center;
+}
+
 .requests-menu-timings-total {
   -moz-padding-start: 8px;
   font-size: 85%;
   font-weight: 600;
-  transform-origin: left center;
 }
 
 .requests-menu-timings-cap {
   width: 4px;
   height: 10px;
   border: 1px solid #fff;
 }
 
 .requests-menu-timings-cap.start {
   -moz-border-end: none;
+}
+
+.requests-menu-timings-cap.end {
+  -moz-border-start: none;
+}
+
+.requests-menu-timings-cap.start:-moz-locale-dir(ltr) {
   border-radius: 4px 0 0 4px;
   transform-origin: right center;
 }
 
-.requests-menu-timings-cap.end {
-  -moz-border-start: none;
+.requests-menu-timings-cap.start:-moz-locale-dir(rtl) {
   border-radius: 0 4px 4px 0;
   transform-origin: left center;
 }
 
+.requests-menu-timings-cap.end:-moz-locale-dir(ltr) {
+  border-radius: 0 4px 4px 0;
+  transform-origin: left center;
+}
+
+.requests-menu-timings-cap.end:-moz-locale-dir(rtl) {
+  border-radius: 4px 0 0 4px;
+  transform-origin: right center;
+}
+
 .requests-menu-timings-box {
   height: 10px;
   border-top: 1px solid #fff;
   border-bottom: 1px solid #fff;
 }
 
 .requests-menu-timings-box.blocked,
 .requests-menu-timings-cap.blocked {
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -118,17 +118,16 @@ browser.jar:
   skin/classic/browser/tabview/edit-light.png         (tabview/edit-light.png)
   skin/classic/browser/tabview/search.png             (tabview/search.png)
   skin/classic/browser/tabview/stack-expander.png     (tabview/stack-expander.png)
   skin/classic/browser/tabview/tabview.png            (tabview/tabview.png)
   skin/classic/browser/tabview/tabview.css            (tabview/tabview.css)
 * skin/classic/browser/devtools/common.css            (devtools/common.css)
   skin/classic/browser/devtools/dark-theme.css        (devtools/dark-theme.css)
   skin/classic/browser/devtools/light-theme.css       (devtools/light-theme.css)
-  skin/classic/browser/devtools/arrows.png            (devtools/arrows.png)
   skin/classic/browser/devtools/widgets.css           (devtools/widgets.css)
   skin/classic/browser/devtools/commandline.png       (devtools/commandline.png)
   skin/classic/browser/devtools/command-paintflashing.png  (devtools/command-paintflashing.png)
   skin/classic/browser/devtools/command-responsivemode.png (devtools/command-responsivemode.png)
   skin/classic/browser/devtools/command-scratchpad.png (devtools/command-scratchpad.png)
   skin/classic/browser/devtools/command-tilt.png      (devtools/command-tilt.png)
   skin/classic/browser/devtools/alerticon-warning.png (devtools/alerticon-warning.png)
   skin/classic/browser/devtools/goto-mdn.png          (devtools/goto-mdn.png)
deleted file mode 100644
index 308b94ca0bbc7b39c742604b8baba829d26b91e0..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/themes/osx/devtools/netmonitor.css
+++ b/browser/themes/osx/devtools/netmonitor.css
@@ -24,22 +24,28 @@
 .requests-menu-header:first-child {
   -moz-padding-start: 4px;
 }
 
 .requests-menu-subitem {
   padding: 4px;
 }
 
-.requests-menu-header:not(:last-child),
-.requests-menu-subitem:not(:last-child) {
+.requests-menu-header:not(:last-child):-moz-locale-dir(ltr),
+.requests-menu-subitem:not(:last-child):-moz-locale-dir(ltr) {
   -moz-border-end: 1px solid hsla(210,8%,5%,.25);
   box-shadow: 1px 0 0 hsla(210,16%,76%,.1);
 }
 
+.requests-menu-header:not(:last-child):-moz-locale-dir(rtl),
+.requests-menu-subitem:not(:last-child):-moz-locale-dir(rtl) {
+  -moz-border-end: 1px solid hsla(210,8%,5%,.25);
+  box-shadow: -1px 0 0 hsla(210,16%,76%,.1);
+}
+
 .requests-menu-header-button {
   -moz-appearance: none;
   background: none;
   min-width: 20px;
   min-height: 31px; /* Remaining 1px comes from border of the toolbar. */
   margin: 0;
   border: none;
   padding: 0;
@@ -166,63 +172,101 @@ box.requests-menu-status[code^="5"] {
 }
 
 .requests-menu-timings-division {
   width: 100px;
   padding-top: 2px;
   -moz-padding-start: 4px;
   -moz-border-start: 1px dotted #999;
   font-size: 75%;
-  text-align: left;
   pointer-events: none;
 }
 
 .requests-menu-timings-division:not(:first-child) {
   -moz-margin-start: -100px !important; /* Don't affect layout. */
 }
 
+.requests-menu-timings-division:-moz-locale-dir(ltr) {
+  transform-origin: left center;
+}
+
+.requests-menu-timings-division:-moz-locale-dir(rtl) {
+  transform-origin: right center;
+}
+
 /* Network requests table: waterfall items */
 
 .requests-menu-subitem.requests-menu-waterfall {
   -moz-padding-start: 4px;
   -moz-padding-end: 4px;
   background-repeat: repeat-y; /* Background created on a <canvas> in js. */
   margin-top: -1px; /* Compensate borders. */
   margin-bottom: -1px;
 }
 
-.requests-menu-timings {
+.requests-menu-subitem.requests-menu-waterfall:-moz-locale-dir(rtl) {
+  background-position: right center;
+}
+
+.requests-menu-timings:-moz-locale-dir(ltr) {
   transform-origin: left center;
 }
 
+.requests-menu-timings:-moz-locale-dir(rtl) {
+  transform-origin: right center;
+}
+
+.requests-menu-timings-total:-moz-locale-dir(ltr) {
+  transform-origin: left center;
+}
+
+.requests-menu-timings-total:-moz-locale-dir(rtl) {
+  transform-origin: right center;
+}
+
 .requests-menu-timings-total {
   -moz-padding-start: 8px;
   font-size: 85%;
   font-weight: 600;
-  transform-origin: left center;
 }
 
 .requests-menu-timings-cap {
   width: 4px;
   height: 10px;
   border: 1px solid #fff;
 }
 
 .requests-menu-timings-cap.start {
   -moz-border-end: none;
+}
+
+.requests-menu-timings-cap.end {
+  -moz-border-start: none;
+}
+
+.requests-menu-timings-cap.start:-moz-locale-dir(ltr) {
   border-radius: 4px 0 0 4px;
   transform-origin: right center;
 }
 
-.requests-menu-timings-cap.end {
-  -moz-border-start: none;
+.requests-menu-timings-cap.start:-moz-locale-dir(rtl) {
   border-radius: 0 4px 4px 0;
   transform-origin: left center;
 }
 
+.requests-menu-timings-cap.end:-moz-locale-dir(ltr) {
+  border-radius: 0 4px 4px 0;
+  transform-origin: left center;
+}
+
+.requests-menu-timings-cap.end:-moz-locale-dir(rtl) {
+  border-radius: 4px 0 0 4px;
+  transform-origin: right center;
+}
+
 .requests-menu-timings-box {
   height: 10px;
   border-top: 1px solid #fff;
   border-bottom: 1px solid #fff;
 }
 
 .requests-menu-timings-box.blocked,
 .requests-menu-timings-cap.blocked {
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -207,17 +207,16 @@ browser.jar:
   skin/classic/browser/tabview/search.png                   (tabview/search.png)
   skin/classic/browser/tabview/stack-expander.png           (tabview/stack-expander.png)
   skin/classic/browser/tabview/tabview.png                  (tabview/tabview.png)
   skin/classic/browser/tabview/tabview.css                  (tabview/tabview.css)
 * skin/classic/browser/devtools/common.css                  (devtools/common.css)
   skin/classic/browser/devtools/dark-theme.css              (devtools/dark-theme.css)
   skin/classic/browser/devtools/light-theme.css             (devtools/light-theme.css)
   skin/classic/browser/devtools/widgets.css                 (devtools/widgets.css)
-  skin/classic/browser/devtools/arrows.png                  (devtools/arrows.png)
   skin/classic/browser/devtools/commandline.png             (devtools/commandline.png)
   skin/classic/browser/devtools/command-paintflashing.png   (devtools/command-paintflashing.png)
   skin/classic/browser/devtools/command-responsivemode.png  (devtools/command-responsivemode.png)
   skin/classic/browser/devtools/command-scratchpad.png      (devtools/command-scratchpad.png)
   skin/classic/browser/devtools/command-tilt.png            (devtools/command-tilt.png)
   skin/classic/browser/devtools/alerticon-warning.png       (devtools/alerticon-warning.png)
   skin/classic/browser/devtools/goto-mdn.png                (devtools/goto-mdn.png)
   skin/classic/browser/devtools/ruleview.css                (devtools/ruleview.css)
deleted file mode 100644
index 308b94ca0bbc7b39c742604b8baba829d26b91e0..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/themes/windows/devtools/netmonitor.css
+++ b/browser/themes/windows/devtools/netmonitor.css
@@ -24,22 +24,28 @@
 .requests-menu-header:first-child {
   -moz-padding-start: 4px;
 }
 
 .requests-menu-subitem {
   padding: 4px;
 }
 
-.requests-menu-header:not(:last-child),
-.requests-menu-subitem:not(:last-child) {
+.requests-menu-header:not(:last-child):-moz-locale-dir(ltr),
+.requests-menu-subitem:not(:last-child):-moz-locale-dir(ltr) {
   -moz-border-end: 1px solid hsla(210,8%,5%,.25);
   box-shadow: 1px 0 0 hsla(210,16%,76%,.1);
 }
 
+.requests-menu-header:not(:last-child):-moz-locale-dir(rtl),
+.requests-menu-subitem:not(:last-child):-moz-locale-dir(rtl) {
+  -moz-border-end: 1px solid hsla(210,8%,5%,.25);
+  box-shadow: -1px 0 0 hsla(210,16%,76%,.1);
+}
+
 .requests-menu-header-button {
   -moz-appearance: none;
   background: none;
   min-width: 20px;
   min-height: 32px; /* Remaining 1px comes from border of the toolbar. */
   margin: 0;
   border: none;
   padding: 0;
@@ -166,63 +172,101 @@ box.requests-menu-status[code^="5"] {
 }
 
 .requests-menu-timings-division {
   width: 100px;
   padding-top: 1px;
   -moz-padding-start: 4px;
   -moz-border-start: 1px dotted #999;
   font-size: 90%;
-  text-align: left;
   pointer-events: none;
 }
 
 .requests-menu-timings-division:not(:first-child) {
   -moz-margin-start: -100px !important; /* Don't affect layout. */
 }
 
+.requests-menu-timings-division:-moz-locale-dir(ltr) {
+  transform-origin: left center;
+}
+
+.requests-menu-timings-division:-moz-locale-dir(rtl) {
+  transform-origin: right center;
+}
+
 /* Network requests table: waterfall items */
 
 .requests-menu-subitem.requests-menu-waterfall {
   -moz-padding-start: 4px;
   -moz-padding-end: 4px;
   background-repeat: repeat-y; /* Background created on a <canvas> in js. */
   margin-top: -1px; /* Compensate borders. */
   margin-bottom: -1px;
 }
 
-.requests-menu-timings {
+.requests-menu-subitem.requests-menu-waterfall:-moz-locale-dir(rtl) {
+  background-position: right center;
+}
+
+.requests-menu-timings:-moz-locale-dir(ltr) {
   transform-origin: left center;
 }
 
+.requests-menu-timings:-moz-locale-dir(rtl) {
+  transform-origin: right center;
+}
+
+.requests-menu-timings-total:-moz-locale-dir(ltr) {
+  transform-origin: left center;
+}
+
+.requests-menu-timings-total:-moz-locale-dir(rtl) {
+  transform-origin: right center;
+}
+
 .requests-menu-timings-total {
   -moz-padding-start: 8px;
   font-size: 85%;
   font-weight: 600;
-  transform-origin: left center;
 }
 
 .requests-menu-timings-cap {
   width: 4px;
   height: 10px;
   border: 1px solid #fff;
 }
 
 .requests-menu-timings-cap.start {
   -moz-border-end: none;
+}
+
+.requests-menu-timings-cap.end {
+  -moz-border-start: none;
+}
+
+.requests-menu-timings-cap.start:-moz-locale-dir(ltr) {
   border-radius: 4px 0 0 4px;
   transform-origin: right center;
 }
 
-.requests-menu-timings-cap.end {
-  -moz-border-start: none;
+.requests-menu-timings-cap.start:-moz-locale-dir(rtl) {
   border-radius: 0 4px 4px 0;
   transform-origin: left center;
 }
 
+.requests-menu-timings-cap.end:-moz-locale-dir(ltr) {
+  border-radius: 0 4px 4px 0;
+  transform-origin: left center;
+}
+
+.requests-menu-timings-cap.end:-moz-locale-dir(rtl) {
+  border-radius: 4px 0 0 4px;
+  transform-origin: right center;
+}
+
 .requests-menu-timings-box {
   height: 10px;
   border-top: 1px solid #fff;
   border-bottom: 1px solid #fff;
 }
 
 .requests-menu-timings-box.blocked,
 .requests-menu-timings-cap.blocked {
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -145,17 +145,16 @@ browser.jar:
         skin/classic/browser/tabview/stack-expander.png             (tabview/stack-expander.png)
         skin/classic/browser/tabview/tabview.png                    (tabview/tabview.png)
         skin/classic/browser/tabview/tabview-inverted.png           (tabview/tabview-inverted.png)
         skin/classic/browser/tabview/tabview.css                    (tabview/tabview.css)
 *       skin/classic/browser/devtools/common.css                    (devtools/common.css)
         skin/classic/browser/devtools/dark-theme.css                (devtools/dark-theme.css)
         skin/classic/browser/devtools/light-theme.css               (devtools/light-theme.css)
         skin/classic/browser/devtools/widgets.css                   (devtools/widgets.css)
-        skin/classic/browser/devtools/arrows.png                    (devtools/arrows.png)
         skin/classic/browser/devtools/commandline.png               (devtools/commandline.png)
         skin/classic/browser/devtools/alerticon-warning.png         (devtools/alerticon-warning.png)
         skin/classic/browser/devtools/goto-mdn.png                  (devtools/goto-mdn.png)
         skin/classic/browser/devtools/ruleview.css                  (devtools/ruleview.css)
         skin/classic/browser/devtools/commandline.css               (devtools/commandline.css)
         skin/classic/browser/devtools/command-paintflashing.png     (devtools/command-paintflashing.png)
         skin/classic/browser/devtools/command-responsivemode.png    (devtools/command-responsivemode.png)
         skin/classic/browser/devtools/command-scratchpad.png        (devtools/command-scratchpad.png)
@@ -398,17 +397,16 @@ browser.jar:
         skin/classic/aero/browser/tabview/stack-expander.png         (tabview/stack-expander.png)
         skin/classic/aero/browser/tabview/tabview.png                (tabview/tabview.png)
         skin/classic/aero/browser/tabview/tabview-inverted.png       (tabview/tabview-inverted.png)
         skin/classic/aero/browser/tabview/tabview.css                (tabview/tabview.css)
 *       skin/classic/aero/browser/devtools/common.css                (devtools/common.css)
         skin/classic/aero/browser/devtools/dark-theme.css            (devtools/dark-theme.css)
         skin/classic/aero/browser/devtools/light-theme.css           (devtools/light-theme.css)
         skin/classic/aero/browser/devtools/widgets.css               (devtools/widgets.css)
-        skin/classic/aero/browser/devtools/arrows.png                (devtools/arrows.png)
         skin/classic/aero/browser/devtools/commandline.png           (devtools/commandline.png)
         skin/classic/aero/browser/devtools/command-paintflashing.png  (devtools/command-paintflashing.png)
         skin/classic/aero/browser/devtools/command-responsivemode.png (devtools/command-responsivemode.png)
         skin/classic/aero/browser/devtools/command-scratchpad.png    (devtools/command-scratchpad.png)
         skin/classic/aero/browser/devtools/command-tilt.png          (devtools/command-tilt.png)
         skin/classic/aero/browser/devtools/checkbox-dark.png         (devtools/checkbox-dark.png)
         skin/classic/aero/browser/devtools/checkbox-light.png        (devtools/checkbox-light.png)
         skin/classic/aero/browser/devtools/alerticon-warning.png     (devtools/alerticon-warning.png)
--- a/caps/src/nsScriptSecurityManager.cpp
+++ b/caps/src/nsScriptSecurityManager.cpp
@@ -2505,18 +2505,17 @@ nsScriptSecurityManager::GetScriptSecuri
         nsresult rv;
         rv = ssManager->Init();
         NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to initialize nsScriptSecurityManager");
         if (NS_FAILED(rv)) {
             return nullptr;
         }
  
         rv = nsXPConnect::XPConnect()->
-            SetDefaultSecurityManager(ssManager,
-                                      nsIXPCSecurityManager::HOOK_ALL);
+            SetDefaultSecurityManager(ssManager);
         if (NS_FAILED(rv)) {
             NS_WARNING("Failed to install xpconnect security manager!");
             return nullptr;
         }
 
         ClearOnShutdown(&gScriptSecMan);
         gScriptSecMan = ssManager;
     }
--- a/content/base/src/nsFrameLoader.cpp
+++ b/content/base/src/nsFrameLoader.cpp
@@ -313,16 +313,25 @@ nsFrameLoader::LoadFrame()
   NS_ENSURE_TRUE(mOwnerContent, NS_ERROR_NOT_INITIALIZED);
 
   nsAutoString src;
   GetURL(src);
 
   src.Trim(" \t\n\r");
 
   if (src.IsEmpty()) {
+    // If the frame is a XUL element and has the attribute 'nodefaultsrc=true'
+    // then we will not use 'about:blank' as fallback but return early without
+    // starting a load if no 'src' attribute is given (or it's empty).
+    if (mOwnerContent->IsXUL() &&
+        mOwnerContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::nodefaultsrc,
+                                   nsGkAtoms::_true, eCaseMatters)) {
+      return NS_OK;
+    }
+
     src.AssignLiteral("about:blank");
   }
 
   nsIDocument* doc = mOwnerContent->OwnerDoc();
   if (doc->IsStaticDocument()) {
     return NS_OK;
   }
 
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -602,16 +602,17 @@ GK_ATOM(negate, "negate")
 GK_ATOM(never, "never")
 GK_ATOM(_new, "new")
 GK_ATOM(newline, "newline")
 GK_ATOM(nextBidi, "NextBidi")
 GK_ATOM(no, "no")
 GK_ATOM(noautohide, "noautohide")
 GK_ATOM(nobr, "nobr")
 GK_ATOM(node, "node")
+GK_ATOM(nodefaultsrc, "nodefaultsrc")
 GK_ATOM(nodeSet, "node-set")
 GK_ATOM(noembed, "noembed")
 GK_ATOM(noframes, "noframes")
 GK_ATOM(nohref, "nohref")
 GK_ATOM(none, "none")
 GK_ATOM(noresize, "noresize")
 GK_ATOM(normal, "normal")
 GK_ATOM(normalizeSpace, "normalize-space")
--- a/content/base/src/nsRange.cpp
+++ b/content/base/src/nsRange.cpp
@@ -657,16 +657,21 @@ nsRange::ContentRemoved(nsIDocument* aDo
     if (aIndexInContainer < mEndOffset) {
       --mEndOffset;
     }
   }
   else if (nsContentUtils::ContentIsDescendantOf(mEndParent, aChild)) {
     gravitateEnd = true;
   }
 
+  if (!mEnableGravitationOnElementRemoval) {
+    // Do not gravitate.
+    return;
+  }
+
   if (gravitateStart || gravitateEnd) {
     DoSetRange(gravitateStart ? container : mStartParent.get(),
                gravitateStart ? aIndexInContainer : mStartOffset,
                gravitateEnd ? container : mEndParent.get(),
                gravitateEnd ? aIndexInContainer : mEndOffset,
                mRoot);
   }
   if (container->IsSelectionDescendant() &&
--- a/content/base/src/nsRange.h
+++ b/content/base/src/nsRange.h
@@ -43,16 +43,17 @@ public:
     , mStartOffset(0)
     , mEndOffset(0)
     , mIsPositioned(false)
     , mIsDetached(false)
     , mMaySpanAnonymousSubtrees(false)
     , mInSelection(false)
     , mStartOffsetWasIncremented(false)
     , mEndOffsetWasIncremented(false)
+    , mEnableGravitationOnElementRemoval(true)
 #ifdef DEBUG
     , mAssertNextInsertOrAppendIndex(-1)
     , mAssertNextInsertOrAppendNode(nullptr)
 #endif
   {
     SetIsDOMBinding();
     MOZ_ASSERT(aNode, "range isn't in a document!");
     mOwner = aNode->OwnerDoc();
@@ -67,16 +68,30 @@ public:
                               nsIDOMRange** aRange);
   static nsresult CreateRange(nsINode* aStartParent, int32_t aStartOffset,
                               nsINode* aEndParent, int32_t aEndOffset,
                               nsRange** aRange);
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsRange, nsIDOMRange)
 
+  /**
+   * The DOM Range spec requires that when a node is removed from its parent,
+   * and the node's subtree contains the start or end point of a range, that
+   * start or end point is moved up to where the node was removed from its
+   * parent.
+   * For some internal uses of Ranges it's useful to disable that behavior,
+   * so that a range of children within a single parent is preserved even if
+   * that parent is removed from the document tree.
+   */
+  void SetEnableGravitationOnElementRemoval(bool aEnable)
+  {
+    mEnableGravitationOnElementRemoval = aEnable;
+  }
+
   // nsIDOMRange interface
   NS_DECL_NSIDOMRANGE
   
   nsINode* GetRoot() const
   {
     return mRoot;
   }
 
@@ -294,16 +309,17 @@ protected:
   int32_t mEndOffset;
 
   bool mIsPositioned;
   bool mIsDetached;
   bool mMaySpanAnonymousSubtrees;
   bool mInSelection;
   bool mStartOffsetWasIncremented;
   bool mEndOffsetWasIncremented;
+  bool mEnableGravitationOnElementRemoval;
 #ifdef DEBUG
   int32_t  mAssertNextInsertOrAppendIndex;
   nsINode* mAssertNextInsertOrAppendNode;
 #endif
 };
 
 inline nsISupports*
 ToCanonicalSupports(nsRange* aRange)
--- a/content/canvas/src/WebGLContext.cpp
+++ b/content/canvas/src/WebGLContext.cpp
@@ -616,16 +616,17 @@ WebGLContext::Render(gfxContext *ctx, gf
     if (!gl)
         return NS_OK;
 
     nsRefPtr<gfxImageSurface> surf = new gfxImageSurface(gfxIntSize(mWidth, mHeight),
                                                          gfxASurface::ImageFormatARGB32);
     if (surf->CairoStatus() != 0)
         return NS_ERROR_FAILURE;
 
+    gl->MakeCurrent();
     gl->ReadScreenIntoImageSurface(surf);
 
     bool srcPremultAlpha = mOptions.premultipliedAlpha;
     bool dstPremultAlpha = aFlags & RenderFlagPremultAlpha;
 
     if (!srcPremultAlpha && dstPremultAlpha) {
         gfxUtils::PremultiplyImageSurface(surf);
     } else if (srcPremultAlpha && !dstPremultAlpha) {
--- a/content/html/content/public/HTMLMediaElement.h
+++ b/content/html/content/public/HTMLMediaElement.h
@@ -39,16 +39,17 @@ typedef uint16_t nsMediaNetworkState;
 typedef uint16_t nsMediaReadyState;
 
 namespace mozilla {
 class MediaResource;
 class MediaDecoder;
 }
 
 class nsITimer;
+class nsRange;
 
 namespace mozilla {
 namespace dom {
 
 class MediaError;
 
 class HTMLMediaElement : public nsGenericHTMLElement,
                          public nsIObserver,
@@ -877,17 +878,17 @@ protected:
 
   // The current media load ID. This is incremented every time we start a
   // new load. Async events note the ID when they're first sent, and only fire
   // if the ID is unchanged when they come to fire.
   uint32_t mCurrentLoadID;
 
   // Points to the child source elements, used to iterate through the children
   // when selecting a resource to load.
-  nsCOMPtr<nsIDOMRange> mSourcePointer;
+  nsRefPtr<nsRange> mSourcePointer;
 
   // Points to the document whose load we're blocking. This is the document
   // we're bound to when loading starts.
   nsCOMPtr<nsIDocument> mLoadBlockedDoc;
 
   // Contains names of events that have been raised while in the bfcache.
   // These events get re-dispatched when the bfcache is exited.
   nsTArray<nsString> mPendingEvents;
--- a/content/html/content/src/HTMLMediaElement.cpp
+++ b/content/html/content/src/HTMLMediaElement.cpp
@@ -3354,16 +3354,19 @@ nsIContent* HTMLMediaElement::GetNextSou
   nsCOMPtr<nsIDOMNode> thisDomNode = do_QueryObject(this);
 
   mSourceLoadCandidate = nullptr;
 
   nsresult rv = NS_OK;
   if (!mSourcePointer) {
     // First time this has been run, create a selection to cover children.
     mSourcePointer = new nsRange(this);
+    // If this media element is removed from the DOM, don't gravitate the
+    // range up to its ancestor, leave it attached to the media element.
+    mSourcePointer->SetEnableGravitationOnElementRemoval(false);
 
     rv = mSourcePointer->SelectNodeContents(thisDomNode);
     if (NS_FAILED(rv)) return nullptr;
 
     rv = mSourcePointer->Collapse(true);
     if (NS_FAILED(rv)) return nullptr;
   }
 
--- a/content/media/AudioNodeEngine.cpp
+++ b/content/media/AudioNodeEngine.cpp
@@ -108,19 +108,19 @@ AudioBlockPanStereoToStereo(const float 
                             float aGainL, float aGainR, bool aIsOnTheLeft,
                             float aOutputL[WEBAUDIO_BLOCK_SIZE],
                             float aOutputR[WEBAUDIO_BLOCK_SIZE])
 {
   uint32_t i;
 
   if (aIsOnTheLeft) {
     for (i = 0; i < WEBAUDIO_BLOCK_SIZE; ++i) {
-      *aOutputL++ = *aInputL++ + *aInputR++ * aGainL;
+      *aOutputL++ = *aInputL++ + *aInputR * aGainL;
       *aOutputR++ = *aInputR++ * aGainR;
     }
   } else {
     for (i = 0; i < WEBAUDIO_BLOCK_SIZE; ++i) {
-      *aOutputL++ = *aInputL++ * aGainL;
+      *aOutputL++ = *aInputL * aGainL;
       *aOutputR++ = *aInputR++ + *aInputL++ * aGainR;
     }
   }
 }
 }
--- a/content/media/omx/MPAPI.h
+++ b/content/media/omx/MPAPI.h
@@ -7,16 +7,25 @@
 #define MPAPI_h_
 
 #include <stdint.h>
 #include "GonkIOSurfaceImage.h"
 
 namespace MPAPI {
 
 struct VideoPlane {
+  VideoPlane() :
+    mData(nullptr),
+    mStride(0),
+    mWidth(0),
+    mHeight(0),
+    mOffset(0),
+    mSkip(0)
+  {}
+
   void *mData;
   int32_t mStride;
   int32_t mWidth;
   int32_t mHeight;
   int32_t mOffset;
   int32_t mSkip;
 };
 
@@ -29,16 +38,27 @@ struct VideoFrame {
   int32_t mStride;
   int32_t mSliceHeight;
   int32_t mRotation;
   VideoPlane Y;
   VideoPlane Cb;
   VideoPlane Cr;
   nsRefPtr<mozilla::layers::GraphicBufferLocked> mGraphicBuffer;
 
+  VideoFrame() :
+    mTimeUs(0),
+    mKeyFrame(false),
+    mShouldSkip(false),
+    mData(nullptr),
+    mSize(0),
+    mStride(0),
+    mSliceHeight(0),
+    mRotation(0)
+  {}
+
   void Set(int64_t aTimeUs, bool aKeyFrame,
            void *aData, size_t aSize, int32_t aStride, int32_t aSliceHeight, int32_t aRotation,
            void *aYData, int32_t aYStride, int32_t aYWidth, int32_t aYHeight, int32_t aYOffset, int32_t aYSkip,
            void *aCbData, int32_t aCbStride, int32_t aCbWidth, int32_t aCbHeight, int32_t aCbOffset, int32_t aCbSkip,
            void *aCrData, int32_t aCrStride, int32_t aCrWidth, int32_t aCrHeight, int32_t aCrOffset, int32_t aCrSkip)
   {
     mTimeUs = aTimeUs;
     mKeyFrame = aKeyFrame;
--- a/content/media/omx/MediaOmxReader.cpp
+++ b/content/media/omx/MediaOmxReader.cpp
@@ -135,16 +135,21 @@ bool MediaOmxReader::DecodeVideoFrame(bo
     MPAPI::VideoFrame frame;
     frame.mGraphicBuffer = nullptr;
     frame.mShouldSkip = false;
     if (!mOmxDecoder->ReadVideo(&frame, aTimeThreshold, aKeyframeSkip, doSeek)) {
       mVideoQueue.Finish();
       return false;
     }
 
+    // Ignore empty buffer which stagefright media read will sporadically return
+    if (frame.mSize == 0 && !frame.mGraphicBuffer) {
+      return true;
+    }
+
     parsed++;
     if (frame.mShouldSkip && mSkipCount < MAX_DROPPED_FRAMES) {
       mSkipCount++;
       return true;
     }
 
     mSkipCount = 0;
 
--- a/content/media/omx/OmxDecoder.cpp
+++ b/content/media/omx/OmxDecoder.cpp
@@ -556,21 +556,25 @@ bool OmxDecoder::ReadVideo(VideoFrame *a
     MediaSource::ReadOptions options;
     options.setSeekTo(aTimeUs, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
     err = mVideoSource->read(&mVideoBuffer, &options);
     {
       Mutex::Autolock autoLock(mSeekLock);
       mIsVideoSeeking = false;
       ReleaseAllPendingVideoBuffersLocked();
     }
+
+    aDoSeek = false;
   } else {
     err = mVideoSource->read(&mVideoBuffer);
   }
 
-  if (err == OK && mVideoBuffer->range_length() > 0) {
+  aFrame->mSize = 0;
+
+  if (err == OK) {
     int64_t timeUs;
     int32_t unreadable;
     int32_t keyFrame;
 
     if (!mVideoBuffer->meta_data()->findInt64(kKeyTime, &timeUs) ) {
       NS_WARNING("OMX decoder did not return frame time");
       return false;
     }
@@ -597,51 +601,52 @@ bool OmxDecoder::ReadVideo(VideoFrame *a
 
       mozilla::layers::SurfaceDescriptor descWrapper(newDescriptor);
       aFrame->mGraphicBuffer = new mozilla::layers::VideoGraphicBuffer(this, mVideoBuffer, descWrapper);
       aFrame->mRotation = mVideoRotation;
       aFrame->mTimeUs = timeUs;
       aFrame->mKeyFrame = keyFrame;
       aFrame->Y.mWidth = mVideoWidth;
       aFrame->Y.mHeight = mVideoHeight;
-    } else {
+    } else if (mVideoBuffer->range_length() > 0) {
       char *data = static_cast<char *>(mVideoBuffer->data()) + mVideoBuffer->range_offset();
       size_t length = mVideoBuffer->range_length();
 
       if (unreadable) {
         LOG(PR_LOG_DEBUG, "video frame is unreadable");
       }
 
       if (!ToVideoFrame(aFrame, timeUs, data, length, keyFrame)) {
         return false;
       }
     }
 
     if (aKeyframeSkip && timeUs < aTimeUs) {
       aFrame->mShouldSkip = true;
     }
-
   }
   else if (err == INFO_FORMAT_CHANGED) {
     // If the format changed, update our cached info.
     if (!SetVideoFormat()) {
       return false;
     } else {
       return ReadVideo(aFrame, aTimeUs, aKeyframeSkip, aDoSeek);
     }
   }
   else if (err == ERROR_END_OF_STREAM) {
     return false;
   }
-  else if (err == UNKNOWN_ERROR) {
-    // This sometimes is used to mean "out of memory", but regardless,
-    // don't keep trying to decode if the decoder doesn't want to.
-    return false;
+  else if (err == -ETIMEDOUT) {
+    LOG(PR_LOG_DEBUG, "OmxDecoder::ReadVideo timed out, will retry");
+    return true;
   }
-  else if (err != OK && err != -ETIMEDOUT) {
+  else {
+    // UNKNOWN_ERROR is sometimes is used to mean "out of memory", but
+    // regardless, don't keep trying to decode if the decoder doesn't want to.
+    LOG(PR_LOG_DEBUG, "OmxDecoder::ReadVideo failed, err=%d", err);
     return false;
   }
 
   return true;
 }
 
 bool OmxDecoder::ReadAudio(AudioFrame *aFrame, int64_t aSeekTimeUs)
 {
@@ -659,16 +664,17 @@ bool OmxDecoder::ReadAudio(AudioFrame *a
       err = mAudioSource->read(&mAudioBuffer, &options);
     } else {
       err = mAudioSource->read(&mAudioBuffer);
     }
   }
   mAudioMetadataRead = false;
 
   aSeekTimeUs = -1;
+  aFrame->mSize = 0;
 
   if (err == OK && mAudioBuffer && mAudioBuffer->range_length() != 0) {
     int64_t timeUs;
     if (!mAudioBuffer->meta_data()->findInt64(kKeyTime, &timeUs))
       return false;
 
     return ToAudioFrame(aFrame, timeUs,
                         mAudioBuffer->data(),
@@ -684,20 +690,22 @@ bool OmxDecoder::ReadAudio(AudioFrame *a
       return ReadAudio(aFrame, aSeekTimeUs);
     }
   }
   else if (err == ERROR_END_OF_STREAM) {
     if (aFrame->mSize == 0) {
       return false;
     }
   }
-  else if (err == UNKNOWN_ERROR) {
-    return false;
+  else if (err == -ETIMEDOUT) {
+    LOG(PR_LOG_DEBUG, "OmxDecoder::ReadAudio timed out, will retry");
+    return true;
   }
-  else if (err != OK && err != -ETIMEDOUT) {
+  else if (err != OK) {
+    LOG(PR_LOG_DEBUG, "OmxDecoder::ReadAudio failed, err=%d", err);
     return false;
   }
 
   return true;
 }
 
 nsresult OmxDecoder::Play() {
   if (!mPaused) {
--- a/content/media/plugins/MPAPI.h
+++ b/content/media/plugins/MPAPI.h
@@ -21,16 +21,25 @@ enum ColorFormat {
  */
 class BufferCallback {
 public:
   virtual void *operator()(size_t aWidth, size_t aHeight,
                            ColorFormat aColorFormat) = 0;
 };
 
 struct VideoPlane {
+  VideoPlane() :
+    mData(0),
+    mStride(0),
+    mWidth(0),
+    mHeight(0),
+    mOffset(0),
+    mSkip(0)
+  {}
+
   void *mData;
   int32_t mStride;
   int32_t mWidth;
   int32_t mHeight;
   int32_t mOffset;
   int32_t mSkip;
 };
 
@@ -41,16 +50,26 @@ struct VideoFrame {
   size_t mSize;
   int32_t mStride;
   int32_t mSliceHeight;
   int32_t mRotation;
   VideoPlane Y;
   VideoPlane Cb;
   VideoPlane Cr;
 
+  VideoFrame() :
+    mTimeUs(0),
+    mKeyFrame(false),
+    mData(0),
+    mSize(0),
+    mStride(0),
+    mSliceHeight(0),
+    mRotation(0)
+  {}
+
   void Set(int64_t aTimeUs, bool aKeyFrame,
            void *aData, size_t aSize, int32_t aStride, int32_t aSliceHeight, int32_t aRotation,
            void *aYData, int32_t aYStride, int32_t aYWidth, int32_t aYHeight, int32_t aYOffset, int32_t aYSkip,
            void *aCbData, int32_t aCbStride, int32_t aCbWidth, int32_t aCbHeight, int32_t aCbOffset, int32_t aCbSkip,
            void *aCrData, int32_t aCrStride, int32_t aCrWidth, int32_t aCrHeight, int32_t aCrOffset, int32_t aCrSkip)
   {
     mTimeUs = aTimeUs;
     mKeyFrame = aKeyFrame;
new file mode 100644
--- /dev/null
+++ b/content/media/test/crashtests/865537-1.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<html>
+<body onload="doTest()">
+<base href="../unknown">
+<div id="test3"></div>
+<video id="test4"><source src="white.webm"></video>
+<script>
+function doTest() {
+  test3.appendChild(test4);
+}
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/media/test/crashtests/880129.html
@@ -0,0 +1,9 @@
+<script>
+try { o1 = new window.AudioContext(3, 2, 44100); } catch(e) { }
+try { o6 = o1.createBufferSource(); } catch(e) { }
+try { o15 = o1.createAnalyser(); } catch(e) { }
+try { o15.fftSize = 32; } catch(e) { }
+try { o6.connect(o15,0,0) } catch(e) { }
+try { o27 = o1.createPanner(); } catch(e) { }
+try { o6.buffer = function(){var buffer = o1.createBuffer(2, 1148, o1.sampleRate);for(var c=0; c<2; c++) {for(var i=0; i<1148; i++) {buffer.getChannelData(c)[i] = 0;}}return buffer;}() } catch(e) { }
+</script>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/content/media/test/crashtests/880202.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<script>
+var Context0= new window.OfflineAudioContext(15,12119,44100)
+var BufferSource1=Context0.createBufferSource();
+var Gain0=Context0.createGain();
+var Panner0=Context0.createPanner();
+Context0.listener.setPosition(29,135,158);
+
+BufferSource1.connect(Gain0);
+
+BufferSource1.buffer=function(){
+	var channels=3; 
+	var length=53325;
+	var Buffer=Context0.createBuffer(channels,length,Context0.sampleRate);
+	for(var y=0;y<channels;y++){
+		var bufferData= Buffer.getChannelData(y);
+		for (var i = 0; i < length; ++i) { 
+			bufferData[i] = i*(270);
+		}
+	};
+	return Buffer;
+}();
+
+Gain0.connect(Panner0);
+Panner0.panningModel=0;
+
+
+setTimeout(function(){
+document.documentElement.removeAttribute("class");
+},500)
+
+</script>
new file mode 100644
--- /dev/null
+++ b/content/media/test/crashtests/880342-1.html
@@ -0,0 +1,208 @@
+<script>
+try { o1 = new window.AudioContext(2, 10, 1019159); } catch(e) { }
+try { o2 = o1.createBufferSource(); } catch(e) { }
+try { o3 = o1.createConvolver(); } catch(e) { }
+try { o4 = o1.createChannelMerger(2); } catch(e) { }
+try { o5 = o1.createAnalyser(); } catch(e) { }
+try { o6 = o1.createPanner(); } catch(e) { }
+try { o7 = o1.createDynamicsCompressor(); } catch(e) { }
+try { o8 = o1.createWaveShaper(); } catch(e) { }
+try { o9 = o1.createChannelSplitter(6); } catch(e) { }
+try { o10 = o1.createScriptProcessor(8192, 2, 3); } catch(e) { }
+try { o1.listener.setPosition(-144115188075855870, 0, -5e-324) } catch(e) { }
+try { o3.buffer = function(){var buffer = o1.createBuffer(2, 741, o1.sampleRate);for(var c=0; c<2; c++) {for(var i=0; i<741; i++) {buffer.getChannelData(c)[i] = 1;}}return buffer;}(); } catch(e) { }
+try { o10.onaudioprocess = function(e) { /* onEvent */ }; } catch(e) { }
+try { o3.connect(o5,0,0) } catch(e) { }
+try { o9.channelCountMode = 'max'; } catch(e) { }
+try { o10.onaudioprocess = function(e) { /* onEvent */ }; } catch(e) { }
+try { o7.channelInterpretation = 'speakers'; } catch(e) { }
+try { o2.buffer = function(){var buffer = o1.createBuffer(2, 1887, 63346);for(var c=0; c<2; c++) {for(var i=0; i<1887; i++) {buffer.getChannelData(c)[i] = 1;}}return buffer;}() } catch(e) { }
+try { o2.connect(o1.destination); } catch(e) { }
+try { o2.playbackRate.exponentialRampToValueAtTime(3.819464020334534, 32) } catch(e) { }
+try { o5.connect(o9,2,0) } catch(e) { }
+try { o9.channelCountMode = 'explicit'; } catch(e) { }
+try { o1.listener.speedOfSound = 72057594037927940; } catch(e) { }
+try { o1.destination.channelCountMode = 'explicit'; } catch(e) { }
+try { o7.threshold.value = 0.0646435404346891590021684237399313133209944; } catch(e) { }
+try { o7.connect(o10,0,0) } catch(e) { }
+try { o1.destination.channelInterpretation = 'speakers'; } catch(e) { }
+try { o3.connect(o4,0,0) } catch(e) { }
+try { o7.release.value = 23.030355486273447; } catch(e) { }
+try { o9.connect(o9,4,0) } catch(e) { }
+try { o6.channelCountMode = 'max'; } catch(e) { }
+try { o7.threshold.value = 0.6395867363641939418172910336579661816358566284179687500; } catch(e) { }
+try { o2.connect(o9,3,0) } catch(e) { }
+try { o10.onaudioprocess = function(e) { /* onEvent */ }; } catch(e) { }
+try { o7.connect(o10,0,0) } catch(e) { }
+try { o5.connect(o5,0,0) } catch(e) { }
+try { o2.playbackRate.value = 5e-324; } catch(e) { }
+try { o4.channelInterpretation = 'discrete'; } catch(e) { }
+try { o2.playbackRate.value = 1.2211628339508704; } catch(e) { }
+try { o2.channelCountMode = 'clamped-max'; } catch(e) { }
+try { o1.listener.dopplerFactor = 32; } catch(e) { }
+try { o2.buffer = function(){var buffer = o1.createBuffer(22, 1811, 21177);for(var c=0; c<22; c++) {for(var i=0; i<1811; i++) {buffer.getChannelData(c)[i] = 1;}}return buffer;}() } catch(e) { }
+try { o2.connect(o1.destination); } catch(e) { }
+try { o5.channelCountMode = 'max'; } catch(e) { }
+try { o1.listener.speedOfSound = 1024; } catch(e) { }
+try { o2.playbackRate.value = 9.999999999999998e-91; } catch(e) { }
+try { o1.listener.speedOfSound = 16; } catch(e) { }
+try { o8.curve = new Float32Array(256); } catch(e) { }
+try { o1.listener.setVelocity(0, 128, 4) } catch(e) { }
+try { o2.buffer = function(){var buffer = o1.createBuffer(23, 483, o1.sampleRate);for(var c=0; c<23; c++) {for(var i=0; i<483; i++) {buffer.getChannelData(c)[i] = 0.026;}}return buffer;}() } catch(e) { }
+try { o2.connect(o1.destination); } catch(e) { }
+try { o2.buffer = function(){var buffer = o1.createBuffer(3, 945, 43803);for(var c=0; c<3; c++) {for(var i=0; i<945; i++) {buffer.getChannelData(c)[i] = -1;}}return buffer;}() } catch(e) { }
+try { o2.connect(o1.destination); } catch(e) { }
+try { o2.buffer = function(){var buffer = o1.createBuffer(19, 997, 23781);for(var c=0; c<19; c++) {for(var i=0; i<997; i++) {buffer.getChannelData(c)[i] = -1;}}return buffer;}() } catch(e) { }
+try { o2.connect(o1.destination); } catch(e) { }
+try { o1.listener.speedOfSound = -8; } catch(e) { }
+try { o3.channelCountMode = 'explicit'; } catch(e) { }
+try { o2.buffer = function(){var buffer = o1.createBuffer(3, 1740, o1.sampleRate);for(var c=0; c<3; c++) {for(var i=0; i<1740; i++) {buffer.getChannelData(c)[i] = 8;}}return buffer;}() } catch(e) { }
+try { o2.connect(o1.destination); } catch(e) { }
+try { o2.buffer = function(){var buffer = o1.createBuffer(1, 1057, 30602);for(var c=0; c<1; c++) {for(var i=0; i<1057; i++) {buffer.getChannelData(c)[i] = -1;}}return buffer;}() } catch(e) { }
+try { o2.connect(o1.destination); } catch(e) { }
+try { o2.buffer = function(){var buffer = o1.createBuffer(3, 78, o1.sampleRate);for(var c=0; c<3; c++) {for(var i=0; i<78; i++) {buffer.getChannelData(c)[i] = 10;}}return buffer;}() } catch(e) { }
+try { o2.connect(o1.destination); } catch(e) { }
+try { o1.listener.setPosition(9.999999999999995e-275, 0.0001, 2) } catch(e) { }
+try { o2.buffer = function(){var buffer = o1.createBuffer(1, 863, o1.sampleRate);for(var c=0; c<1; c++) {for(var i=0; i<863; i++) {buffer.getChannelData(c)[i] = 1;}}return buffer;}() } catch(e) { }
+try { o2.connect(o1.destination); } catch(e) { }
+try { o8.channelCount = 3; } catch(e) { }
+try { o2.buffer = function(){var buffer = o1.createBuffer(2, 1821, o1.sampleRate);for(var c=0; c<2; c++) {for(var i=0; i<1821; i++) {buffer.getChannelData(c)[i] = 0.3655;}}return buffer;}() } catch(e) { }
+try { o2.connect(o1.destination); } catch(e) { }
+try { o6.connect(o6,0,0) } catch(e) { }
+try { o1.listener.setOrientation(128, -0.479527496, 128, 1, 1, -16) } catch(e) { }
+try { o4.connect(o8,0,0) } catch(e) { }
+try { o7.reduction.exponentialRampToValueAtTime(1.7976931348623157e+308, 2048) } catch(e) { }
+try { o1.listener.dopplerFactor = 0.5754; } catch(e) { }
+try { o7.release.value = 0; } catch(e) { }
+try { o5.getFloatFrequencyData(new Float32Array(2048)) } catch(e) { }
+try { o2.buffer = function(){var buffer = o1.createBuffer(2, 120, 42419);for(var c=0; c<2; c++) {for(var i=0; i<120; i++) {buffer.getChannelData(c)[i] = -1;}}return buffer;}() } catch(e) { }
+try { o2.connect(o1.destination); } catch(e) { }
+try { o3.buffer = function(){var buffer = o1.createBuffer(1, 1620, o1.sampleRate);for(var c=0; c<1; c++) {for(var i=0; i<1620; i++) {buffer.getChannelData(c)[i] = -1;}}return buffer;}(); } catch(e) { }
+try { o2.buffer = function(){var buffer = o1.createBuffer(1, 823, 77239);for(var c=0; c<1; c++) {for(var i=0; i<823; i++) {buffer.getChannelData(c)[i] = -1;}}return buffer;}() } catch(e) { }
+try { o2.connect(o1.destination); } catch(e) { }
+try { o1.destination.channelInterpretation = 'speakers'; } catch(e) { }
+try { o1.listener.setPosition(2048, 0.000522899209, -4503599627370495) } catch(e) { }
+try { o2.buffer = function(){var buffer = o1.createBuffer(3, 84028, 75483);for(var c=0; c<3; c++) {for(var i=0; i<84028; i++) {buffer.getChannelData(c)[i] = -1;}}return buffer;}() } catch(e) { }
+try { o2.connect(o1.destination); } catch(e) { }
+try { o2.playbackRate.setTargetAtTime(5e-324, 9.999999999999998e-104, 0) } catch(e) { }
+try { o7.attack.value = 9.999999999999994e-236; } catch(e) { }
+try { o1.listener.dopplerFactor = 1024; } catch(e) { }
+try { o3.connect(o7,0,0) } catch(e) { }
+try { o2.playbackRate.linearRampToValueAtTime(0, 0) } catch(e) { }
+try { o4.connect(o10,0,0) } catch(e) { }
+try { o1.listener.setVelocity(0.682, 32, 1e-76) } catch(e) { }
+try { o2.buffer = function(){var buffer = o1.createBuffer(3, 201, o1.sampleRate);for(var c=0; c<3; c++) {for(var i=0; i<201; i++) {buffer.getChannelData(c)[i] = 4;}}return buffer;}() } catch(e) { }
+try { o2.connect(o1.destination); } catch(e) { }
+try { o2.connect(o6,0,0) } catch(e) { }
+try { o5.getByteFrequencyData(new Uint8Array(32)) } catch(e) { }
+try { o3.connect(o6,0,0) } catch(e) { }
+try { o7.channelCount = 1; } catch(e) { }
+try { o2.playbackRate.setValueCurveAtTime(new Float32Array(512), 8, 1) } catch(e) { }
+try { o3.connect(o4,0,1) } catch(e) { }
+try { o2.buffer = function(){var buffer = o1.createBuffer(3, 1996, o1.sampleRate);for(var c=0; c<3; c++) {for(var i=0; i<1996; i++) {buffer.getChannelData(c)[i] = -1;}}return buffer;}() } catch(e) { }
+try { o2.connect(o1.destination); } catch(e) { }
+try { o1.listener.dopplerFactor = 2; } catch(e) { }
+try { o2.buffer = function(){var buffer = o1.createBuffer(2, 983, 60517);for(var c=0; c<2; c++) {for(var i=0; i<983; i++) {buffer.getChannelData(c)[i] = 1;}}return buffer;}() } catch(e) { }
+try { o2.connect(o1.destination); } catch(e) { }
+try { o6.coneInnerAngle = 360; } catch(e) { }
+try { o8.curve = new Float32Array(512); } catch(e) { }
+try { o1.listener.setPosition(32, 16, 9.999999999999995e-280) } catch(e) { }
+try { o6.connect(o6,0,0) } catch(e) { }
+try { o1.listener.setPosition(2097151, 512, 5e-324) } catch(e) { }
+try { o7.channelCountMode = 'clamped-max'; } catch(e) { }
+try { o1.destination.channelCountMode = 'max'; } catch(e) { }
+try { o1.listener.setPosition(0, 1000000, 64) } catch(e) { }
+try { o4.connect(o5,0,0) } catch(e) { }
+try { o9.connect(o4,0,1) } catch(e) { }
+try { o7.attack.setValueCurveAtTime(new Float32Array(8), 256, 2048) } catch(e) { }
+try { o9.channelCountMode = 'explicit'; } catch(e) { }
+try { o1.listener.dopplerFactor = 5e-324; } catch(e) { }
+try { o4.connect(o10,0,0) } catch(e) { }
+try { o7.ratio.cancelScheduledValues(1) } catch(e) { }
+try { o2.buffer = function(){var buffer = o1.createBuffer(1, 1590, 9733);for(var c=0; c<1; c++) {for(var i=0; i<1590; i++) {buffer.getChannelData(c)[i] = 2;}}return buffer;}() } catch(e) { }
+try { o2.connect(o1.destination); } catch(e) { }
+try { o1.destination.channelCount = 2; } catch(e) { }
+try { o1.destination.channelInterpretation = 'speakers'; } catch(e) { }
+try { o5.channelCountMode = 'max'; } catch(e) { }
+try { o9.channelCountMode = 'clamped-max'; } catch(e) { }
+try { o7.attack.setTargetAtTime(0.11499421161482907549622467513472656719386577606201171875000, 1, 1.7976931348623157e+308) } catch(e) { }
+try { o2.playbackRate.value = 1; } catch(e) { }
+try { o3.normalize = false; } catch(e) { }
+try { o2.buffer = function(){var buffer = o1.createBuffer(2, 1866, o1.sampleRate);for(var c=0; c<2; c++) {for(var i=0; i<1866; i++) {buffer.getChannelData(c)[i] = 0.174908422905697580329587026426452212035655975341797;}}return buffer;}(); } catch(e) { }
+try { o2.playbackRate.value = 1.0; } catch(e) { }
+try { o7.threshold.value = 100; } catch(e) { }
+try { o1.listener.setVelocity(961.4441892370145, 9.999999999999999e-157, 1) } catch(e) { }
+try { o5.getByteFrequencyData(new Uint8Array(2)) } catch(e) { }
+try { o6.connect(o10,0,0) } catch(e) { }
+try { o2.buffer = function(){var buffer = o1.createBuffer(3, 1176, o1.sampleRate);for(var c=0; c<3; c++) {for(var i=0; i<1176; i++) {buffer.getChannelData(c)[i] = 9.753993292300242;}}return buffer;}() } catch(e) { }
+try { o2.connect(o1.destination); } catch(e) { }
+try { o2.playbackRate.linearRampToValueAtTime(0.8687, 32) } catch(e) { }
+try { o5.channelCountMode = 'explicit'; } catch(e) { }
+try { o2.buffer = function(){var buffer = o1.createBuffer(13, 703, 12723);for(var c=0; c<13; c++) {for(var i=0; i<703; i++) {buffer.getChannelData(c)[i] = -1;}}return buffer;}() } catch(e) { }
+try { o2.connect(o1.destination); } catch(e) { }
+try { o2.loopEnd = 1.7976931348623157e+308; } catch(e) { }
+try { o3.connect(o7,0,0) } catch(e) { }
+try { o1.destination.channelCountMode = 'clamped-max'; } catch(e) { }
+try { o1.destination.channelInterpretation = 'discrete'; } catch(e) { }
+try { o2.connect(o10,0,0) } catch(e) { }
+try { o3.normalize = false; } catch(e) { }
+try { /* switch-case did not return anything. */ } catch(e) { }
+try { o10.connect(o8,0,0) } catch(e) { }
+try { o2.buffer = function(){var buffer = o1.createBuffer(3, 132, 88507);for(var c=0; c<3; c++) {for(var i=0; i<132; i++) {buffer.getChannelData(c)[i] = 1.7976931348623157e+308;}}return buffer;}() } catch(e) { }
+try { o2.connect(o1.destination); } catch(e) { }
+try { o2.loopStart = 1.7976931348623157e+308; } catch(e) { }
+try { o1.listener.dopplerFactor = 1024; } catch(e) { }
+try { o2.buffer = function(){var buffer = o1.createBuffer(1, 40, o1.sampleRate);for(var c=0; c<1; c++) {for(var i=0; i<40; i++) {buffer.getChannelData(c)[i] = 1.3785770335346594;}}return buffer;}() } catch(e) { }
+try { o2.connect(o1.destination); } catch(e) { }
+try { o4.connect(o9,5,0) } catch(e) { }
+try { o8.curve = new Float32Array(16); } catch(e) { }
+try { o10.onaudioprocess = function(e) { /* onEvent */ }; } catch(e) { }
+try { o6.channelCountMode = 'explicit'; } catch(e) { }
+try { o2.playbackRate.value = 0; } catch(e) { }
+try { o4.channelCount = 3; } catch(e) { }
+try { o2.buffer = function(){var buffer = o1.createBuffer(3, 220, o1.sampleRate);for(var c=0; c<3; c++) {for(var i=0; i<220; i++) {buffer.getChannelData(c)[i] = -1;}}return buffer;}() } catch(e) { }
+try { o2.connect(o1.destination); } catch(e) { }
+try { o1.destination.channelInterpretation = 'discrete'; } catch(e) { }
+try { o1.listener.setPosition(70368744177664, 2048, 1.7976931348623157e+308) } catch(e) { }
+try { o2.playbackRate.setValueCurveAtTime(new Float32Array(128), 1.7976931348623157e+308, 0.75) } catch(e) { }
+try { o5.fftSize = 118; } catch(e) { }
+try { o5.minDecibels = 1; } catch(e) { }
+try { o7.release.value = 5e-324; } catch(e) { }
+try { o2.channelCount = 3; } catch(e) { }
+try { o2.channelCountMode = 'clamped-max'; } catch(e) { }
+try { o3.normalize = false; } catch(e) { }
+try { o1.listener.speedOfSound = 2; } catch(e) { }
+try { o2.loopStart = 32; } catch(e) { }
+try { o3.channelCountMode = 'max'; } catch(e) { }
+try { o1.listener.setPosition(0.37976588317653304, -274877906943, 64) } catch(e) { }
+try { o8.curve = new Float32Array(4); } catch(e) { }
+try { o1.listener.setVelocity(16, -1024, 0) } catch(e) { }
+try { o5.minDecibels = 10000; } catch(e) { }
+try { o2.playbackRate.value = 5e-324; } catch(e) { }
+try { o3.connect(o9,1,0) } catch(e) { }
+try { o10.onaudioprocess = function(e) { /* onEvent */ }; } catch(e) { }
+try { o5.connect(o8,0,0) } catch(e) { }
+try { o2.buffer = function(){var buffer = o1.createBuffer(3, 554, o1.sampleRate);for(var c=0; c<3; c++) {for(var i=0; i<554; i++) {buffer.getChannelData(c)[i] = 0.000001;}}return buffer;}() } catch(e) { }
+try { o2.connect(o1.destination); } catch(e) { }
+try { o7.release.value = 0; } catch(e) { }
+try { o2.buffer = function(){var buffer = o1.createBuffer(1, 1289, 29583);for(var c=0; c<1; c++) {for(var i=0; i<1289; i++) {buffer.getChannelData(c)[i] = 5e-324;}}return buffer;}() } catch(e) { }
+try { o2.connect(o1.destination); } catch(e) { }
+try { o1.listener.setOrientation(1024, 64, 16, 1024, 1, 68719476735) } catch(e) { }
+try { o1.listener.setOrientation(512, 10000000, 274877906944, 9.999999999999998e-113, -8, 3.6191134923595274) } catch(e) { }
+try { o2.buffer = function(){var buffer = o1.createBuffer(1, 1453, 15258);for(var c=0; c<1; c++) {for(var i=0; i<1453; i++) {buffer.getChannelData(c)[i] = -1;}}return buffer;}() } catch(e) { }
+try { o2.connect(o1.destination); } catch(e) { }
+try { o8.channelInterpretation = 'discrete'; } catch(e) { }
+try { o5.channelInterpretation = 'discrete'; } catch(e) { }
+try { o7.connect(o9,4,0) } catch(e) { }
+try { o8.connect(o10,0,0) } catch(e) { }
+try { o1.listener.setPosition(18014398509481984, 16, 0.505129302503804056279079759406158700585365295410156250000) } catch(e) { }
+try { o10.connect(o9,5,0) } catch(e) { }
+try { o2.loop = false; } catch(e) { }
+try { o2.buffer = function(){var buffer = o1.createBuffer(11, 1673, o1.sampleRate);for(var c=0; c<11; c++) {for(var i=0; i<1673; i++) {buffer.getChannelData(c)[i] = 0;}}return buffer;}() } catch(e) { }
+try { o2.connect(o1.destination); } catch(e) { }
+try { o2.buffer = function(){var buffer = o1.createBuffer(2, 577, o1.sampleRate);for(var c=0; c<2; c++) {for(var i=0; i<577; i++) {buffer.getChannelData(c)[i] = 0.5611;}}return buffer;}() } catch(e) { }
+try { o2.connect(o1.destination); } catch(e) { }
+try { o1.listener.setOrientation(64, 2049, 5e-324, 0.1777, 2, 7) } catch(e) { }
+try { o5.maxDecibels = 128; } catch(e) { }
+try { o2.start(0) } catch(e) { }
+</script>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/content/media/test/crashtests/880342-2.html
@@ -0,0 +1,8 @@
+<script>
+try { o1 = new window.AudioContext(2, 10, 1019159); } catch(e) { }
+try { o2 = o1.createBufferSource(); } catch(e) { }
+try { o3 = o1.createConvolver(); } catch(e) { }
+try { o3.buffer = function(){var buffer = o1.createBuffer(2, 741, o1.sampleRate);for(var c=0; c<2; c++) {for(var i=0; i<741; i++) {buffer.getChannelData(c)[i] = 1;}}return buffer;}(); } catch(e) { }
+try { o2.buffer = function(){var buffer = o1.createBuffer(2, 120, 42419);for(var c=0; c<2; c++) {for(var i=0; i<120; i++) {buffer.getChannelData(c)[i] = -1;}}return buffer;}() } catch(e) { }
+try { o3.buffer = function(){var buffer = o1.createBuffer(1, 1620, o1.sampleRate);for(var c=0; c<1; c++) {for(var i=0; i<1620; i++) {buffer.getChannelData(c)[i] = -1;}}return buffer;}(); } catch(e) { }
+</script>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/content/media/test/crashtests/880384.html
@@ -0,0 +1,8 @@
+<script>
+o1 = new window.AudioContext(3, 256, 44100);
+o2 = o1.createBufferSource();
+o3 = o1.createConvolver();
+o3.normalize = false;
+o3.buffer = function(){var buffer = o1.createBuffer(2, 1051, o1.sampleRate);for(var c=0; c<2; c++) {for(var i=0; i<1051; i++) {buffer.getChannelData(c)[i] = 1;}}return buffer;}();
+o3.buffer = function(){var buffer = o1.createBuffer(2, 1112, o1.sampleRate);for(var c=0; c<2; c++) {for(var i=0; i<1112; i++) {buffer.getChannelData(c)[i] = 1;}}return buffer;}();
+</script>
new file mode 100644
--- /dev/null
+++ b/content/media/test/crashtests/880404.html
@@ -0,0 +1,6 @@
+<script>
+o1 = new window.OfflineAudioContext(2, 32, 44100);
+o12 = o1.createConvolver();
+o12.buffer = function(){var buffer = o1.createBuffer(1, 78, o1.sampleRate);for(var c=0; c<1; c++) {for(var i=0; i<78; i++) {buffer.getChannelData(c)[i] = 1;}}return buffer;}();
+o1.startRendering();
+</script>
\ No newline at end of file
--- a/content/media/test/crashtests/crashtests.list
+++ b/content/media/test/crashtests/crashtests.list
@@ -10,16 +10,17 @@ load 492286-1.xhtml
 load 576612-1.html
 skip-if(Android||B2G) load 691096-1.html # Android sound API can't handle playing large number of sounds at once, bug 852821 for B2G
 load 752784-1.html
 skip-if(Android||B2G) HTTP load 795892-1.html # load failed, bug 833371 for B2G
 skip-if(Android||B2G) load 789075-1.html # load failed, bug 833371 for B2G
 load 844563.html
 load 846612.html
 load 852838.html
+load 865537-1.html
 load 868504.html
 load 874869.html
 load 874915.html
 load 874934.html
 load 874952.html
 load 875144.html
 load 875596.html
 load 875911.html
@@ -32,8 +33,14 @@ load 876249.html
 load 876252.html
 load 876834.html
 load 877820.html
 load 878014.html
 load 878328.html
 load 878407.html
 load 878478.html
 load 877527.html
+load 880129.html
+skip-if(B2G) load 880202.html # load failed, bug 833371 for B2G
+load 880342-1.html
+load 880342-2.html
+load 880384.html
+load 880404.html
--- a/content/media/webaudio/AnalyserNode.cpp
+++ b/content/media/webaudio/AnalyserNode.cpp
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/AnalyserNode.h"
 #include "mozilla/dom/AnalyserNodeBinding.h"
 #include "AudioNodeEngine.h"
 #include "AudioNodeStream.h"
 #include "mozilla/Mutex.h"
+#include "mozilla/PodOperations.h"
 #include "kiss_fft/kiss_fftr.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_ISUPPORTS_INHERITED0(AnalyserNode, AudioNode)
 
 class AnalyserNodeEngine : public AudioNodeEngine
@@ -270,31 +271,36 @@ AnalyserNode::AllocateBuffer()
   return result;
 }
 
 void
 AnalyserNode::AppendChunk(const AudioChunk& aChunk)
 {
   const uint32_t bufferSize = mBuffer.Length();
   const uint32_t channelCount = aChunk.mChannelData.Length();
-  const uint32_t chunkCount = aChunk.mDuration;
+  uint32_t chunkDuration = aChunk.mDuration;
   MOZ_ASSERT((bufferSize & (bufferSize - 1)) == 0); // Must be a power of two!
   MOZ_ASSERT(channelCount > 0);
-  MOZ_ASSERT(chunkCount == WEBAUDIO_BLOCK_SIZE);
+  MOZ_ASSERT(chunkDuration == WEBAUDIO_BLOCK_SIZE);
 
-  memcpy(mBuffer.Elements() + mWriteIndex, aChunk.mChannelData[0], sizeof(float) * chunkCount);
+  if (chunkDuration > bufferSize) {
+    // Copy a maximum bufferSize samples.
+    chunkDuration = bufferSize;
+  }
+
+  PodCopy(mBuffer.Elements() + mWriteIndex, static_cast<const float*>(aChunk.mChannelData[0]), chunkDuration);
   for (uint32_t i = 1; i < channelCount; ++i) {
     AudioBlockAddChannelWithScale(static_cast<const float*>(aChunk.mChannelData[i]), 1.0f,
                                   mBuffer.Elements() + mWriteIndex);
   }
   if (channelCount > 1) {
     AudioBlockInPlaceScale(mBuffer.Elements() + mWriteIndex, 1,
                            1.0f / aChunk.mChannelData.Length());
   }
-  mWriteIndex += chunkCount;
+  mWriteIndex += chunkDuration;
   MOZ_ASSERT(mWriteIndex <= bufferSize);
   if (mWriteIndex >= bufferSize) {
     mWriteIndex = 0;
   }
 }
 
 }
 }
--- a/dom/apps/src/Webapps.jsm
+++ b/dom/apps/src/Webapps.jsm
@@ -3126,17 +3126,19 @@ this.DOMApplicationRegistry = {
     }
 
     observers.forEach(function (observer) {
       try {
         observer.observe(subject, topic, data);
       } catch(e) { }
     });
     // Send back an answer to the child.
-    ppmm.broadcastAsyncMessage("Webapps:ClearBrowserData:Return", msg);
+    if (msg) {
+      ppmm.broadcastAsyncMessage("Webapps:ClearBrowserData:Return", msg);
+    }
   },
 
   registerBrowserElementParentForApp: function(bep, appId) {
     let mm = bep._mm;
 
     // Make a listener function that holds on to this appId.
     let listener = this.receiveAppMessage.bind(this, appId);
 
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -1452,19 +1452,21 @@ nsGlobalWindow::FreeInnerObjects()
 
   // Make sure that this is called before we null out the document and
   // other members that the window destroyed observers could
   // re-create.
   NotifyDOMWindowDestroyed(this);
 
   // Kill all of the workers for this window.
   // We push a cx so that exceptions get reported in the right DOM Window.
-  nsIScriptContext *scx = GetContextInternal();
-  AutoPushJSContext cx(scx ? scx->GetNativeContext() : nsContentUtils::GetSafeJSContext());
-  mozilla::dom::workers::CancelWorkersForWindow(cx, this);
+  {
+    nsIScriptContext *scx = GetContextInternal();
+    AutoPushJSContext cx(scx ? scx->GetNativeContext() : nsContentUtils::GetSafeJSContext());
+    mozilla::dom::workers::CancelWorkersForWindow(cx, this);
+  }
 
   // Close all offline storages for this window.
   quota::QuotaManager* quotaManager = quota::QuotaManager::Get();
   if (quotaManager) {
     quotaManager->AbortCloseStoragesForWindow(this);
   }
 
   ClearAllTimeouts();
@@ -10934,20 +10936,22 @@ nsGlobalWindow::SuspendTimeouts(uint32_t
     nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
     if (ac) {
       for (uint32_t i = 0; i < mEnabledSensors.Length(); i++)
         ac->RemoveWindowListener(mEnabledSensors[i], this);
     }
     DisableGamepadUpdates();
 
     // Suspend all of the workers for this window.
-  // We push a cx so that exceptions get reported in the right DOM Window.
-    nsIScriptContext *scx = GetContextInternal();
-    AutoPushJSContext cx(scx ? scx->GetNativeContext() : nsContentUtils::GetSafeJSContext());
-    mozilla::dom::workers::SuspendWorkersForWindow(cx, this);
+    // We push a cx so that exceptions get reported in the right DOM Window.
+    {
+      nsIScriptContext *scx = GetContextInternal();
+      AutoPushJSContext cx(scx ? scx->GetNativeContext() : nsContentUtils::GetSafeJSContext());
+      mozilla::dom::workers::SuspendWorkersForWindow(cx, this);
+    }
 
     TimeStamp now = TimeStamp::Now();
     for (nsTimeout *t = mTimeouts.getFirst(); t; t = t->getNext()) {
       // Set mTimeRemaining to be the time remaining for this timer.
       if (t->mWhen > now)
         t->mTimeRemaining = t->mWhen - now;
       else
         t->mTimeRemaining = TimeDuration(0);
@@ -11029,17 +11033,17 @@ nsGlobalWindow::ResumeTimeouts(bool aTha
     for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
       mAudioContexts[i]->Resume();
     }
 
     // Resume all of the workers for this window.
     // We push a cx so that exceptions get reported in the right DOM Window.
     nsIScriptContext *scx = GetContextInternal();
     AutoPushJSContext cx(scx ? scx->GetNativeContext() : nsContentUtils::GetSafeJSContext());
-    mozilla::dom::workers::ResumeWorkersForWindow(cx, this);
+    mozilla::dom::workers::ResumeWorkersForWindow(scx, this);
 
     // Restore all of the timeouts, using the stored time remaining
     // (stored in timeout->mTimeRemaining).
 
     TimeStamp now = TimeStamp::Now();
 
 #ifdef DEBUG
     bool _seenDummyTimeout = false;
--- a/dom/bindings/DOMJSProxyHandler.cpp
+++ b/dom/bindings/DOMJSProxyHandler.cpp
@@ -30,18 +30,18 @@ bool
 DefineStaticJSVals(JSContext* cx)
 {
   return InternJSString(cx, s_length_id, "length");
 }
 
 
 int HandlerFamily;
 
-js::ListBaseShadowsResult
-DOMListShadows(JSContext* cx, JSHandleObject proxy, JSHandleId id)
+js::DOMProxyShadowsResult
+DOMProxyShadows(JSContext* cx, JSHandleObject proxy, JSHandleId id)
 {
   JS::Value v = js::GetProxyExtra(proxy, JSPROXYSLOT_EXPANDO);
   if (v.isObject()) {
     JSBool hasOwn;
     if (!JS_AlreadyHasOwnPropertyById(cx, &v.toObject(), id, &hasOwn))
       return js::ShadowCheckFailed;
 
     return hasOwn ? js::Shadows : js::DoesntShadow;
@@ -54,25 +54,25 @@ DOMListShadows(JSContext* cx, JSHandleOb
   bool hasOwn;
   if (!GetProxyHandler(proxy)->hasOwn(cx, proxy, id, &hasOwn))
     return js::ShadowCheckFailed;
 
   return hasOwn ? js::Shadows : js::DoesntShadowUnique;
 }
 
 // Store the information for the specialized ICs.
-struct SetListBaseInformation
+struct SetDOMProxyInformation
 {
-  SetListBaseInformation() {
-    js::SetListBaseInformation((void*) &HandlerFamily,
-                               js::JSSLOT_PROXY_EXTRA + JSPROXYSLOT_EXPANDO, DOMListShadows);
+  SetDOMProxyInformation() {
+    js::SetDOMProxyInformation((void*) &HandlerFamily,
+                               js::JSSLOT_PROXY_EXTRA + JSPROXYSLOT_EXPANDO, DOMProxyShadows);
   }
 };
 
-SetListBaseInformation gSetListBaseInformation;
+SetDOMProxyInformation gSetDOMProxyInformation;
 
 // static
 JSObject*
 DOMProxyHandler::GetAndClearExpandoObject(JSObject* obj)
 {
   MOZ_ASSERT(IsDOMProxy(obj), "expected a DOM proxy object");
   JS::Value v = js::GetProxyExtra(obj, JSPROXYSLOT_EXPANDO);
   if (v.isUndefined()) {
--- a/dom/media/PeerConnection.js
+++ b/dom/media/PeerConnection.js
@@ -760,20 +760,39 @@ RTCPeerConnection.prototype = {
     let sdp = this._getPC().remoteDescription;
     if (sdp.length == 0) {
       return null;
     }
     return new this._win.mozRTCSessionDescription({ type: this._remoteType,
                                                     sdp: sdp });
   },
 
-  get signalingState()     { return "stable"; }, // not yet implemented
   get iceGatheringState()  { return this._iceGatheringState; },
   get iceConnectionState() { return this._iceConnectionState; },
 
+  // Corresponds to constants in IPeerConnection.idl
+  _signalingStateMap: [
+    'invalid',
+    'stable',
+    'have-local-offer',
+    'have-remote-offer',
+    'have-local-pranswer',
+    'have-remote-pranswer',
+    'closed'
+  ],
+
+  get signalingState() {
+    // checking for our local pc closed indication
+    // before invoking the pc methods.
+    if(this._closed) {
+      return "closed";
+    }
+    return this._signalingStateMap[this._getPC().signalingState];
+  },
+
   changeIceGatheringState: function(state) {
     this._iceGatheringState = state;
   },
 
   changeIceConnectionState: function(state) {
     this._iceConnectionState = state;
     this.dispatchEvent(new this._win.Event("iceconnectionstatechange"));
   },
@@ -985,24 +1004,20 @@ PeerConnectionObserver.prototype = {
   },
 
   onAddIceCandidateError: function(code, message) {
     this._dompc._pendingType = null;
     this.callCB(this._dompc._onAddIceCandidateError, new RTCError(code, message));
     this._dompc._executeNext();
   },
 
-  onStateChange: function(state) {
-    if (state != Ci.IPeerConnectionObserver.kIceState) {
-      return;
-    }
-
-    switch (this._dompc._pc.iceState) {
+  handleIceStateChanges: function(iceState) {
+    switch (iceState) {
       case Ci.IPeerConnection.kIceWaiting:
-        this._dompc.changeIceConnectionState("completed");
+        this._dompc.changeIceConnectionState("new");
         this.callCB(this._dompc.ongatheringchange, "complete");
         this.callCB(this._onicechange, "starting");
         // Now that the PC is ready to go, execute any pending operations.
         this._dompc._executeNext();
         break;
       case Ci.IPeerConnection.kIceChecking:
         this._dompc.changeIceConnectionState("checking");
         this.callCB(this._onicechange, "checking");
@@ -1016,17 +1031,43 @@ PeerConnectionObserver.prototype = {
         this._dompc.changeIceConnectionState("connected");
         this.callCB(this._onicechange, "connected");
         break;
       case Ci.IPeerConnection.kIceFailed:
         this._dompc.changeIceConnectionState("failed");
         this.callCB(this._onicechange, "failed");
         break;
       default:
-        // Unknown state!
+        // Unknown ICE state!
+        this._dompc.reportWarning("Unhandled ice state: " + iceState, null, 0);
+        break;
+    }
+  },
+
+  onStateChange: function(state) {
+    switch (state) {
+      case Ci.IPeerConnectionObserver.kSignalingState:
+        this.callCB(this._dompc.onsignalingstatechange,
+                    this._dompc.signalingState);
+        break;
+
+      case Ci.IPeerConnectionObserver.kIceState:
+        this.handleIceStateChanges(this._dompc._pc.iceState);
+        break;
+
+      case Ci.IPeerConnectionObserver.kSdpState:
+        // No-op
+        break;
+
+      case Ci.IPeerConnectionObserver.kSipccState:
+        // No-op
+        break;
+
+      default:
+        this._dompc.reportWarning("Unhandled state type: " + state, null, 0);
         break;
     }
   },
 
   onAddStream: function(stream) {
     this.dispatchEvent(new this._dompc._win.MediaStreamEvent("addstream",
                                                              { stream: stream }));
   },
--- a/dom/media/bridge/IPeerConnection.idl
+++ b/dom/media/bridge/IPeerConnection.idl
@@ -27,16 +27,17 @@ interface IPeerConnectionManager : nsISu
 [scriptable, uuid(85ba28da-53d0-401d-afed-9cad69f727ff)]
 interface IPeerConnectionObserver : nsISupports
 {
   /* Constants */
   const long kReadyState = 0x1;
   const long kIceState = 0x2;
   const long kSdpState = 0x3;
   const long kSipccState = 0x4;
+  const long kSignalingState = 0x5;
 
   /* JSEP callbacks */
   void onCreateOfferSuccess(in string offer);
   void onCreateOfferError(in unsigned long name, in string message);
   void onCreateAnswerSuccess(in string answer);
   void onCreateAnswerError(in unsigned long name, in string message);
   void onSetLocalDescriptionSuccess();
   void onSetRemoteDescriptionSuccess();
@@ -86,16 +87,25 @@ interface IPeerConnection : nsISupports
 
   /* for readyState on Peer Connection */
   const long kNew = 0;
   const long kNegotiating = 1;
   const long kActive = 2;
   const long kClosing = 3;
   const long kClosed = 4;
 
+  /* RTCSignalingState from WebRTC spec */
+  const long kSignalingInvalid            = 0;
+  const long kSignalingStable             = 1;
+  const long kSignalingHaveLocalOffer     = 2;
+  const long kSignalingHaveRemoteOffer    = 3;
+  const long kSignalingHaveLocalPranswer  = 4;
+  const long kSignalingHaveRemotePranswer = 5;
+  const long kSignalingClosed             = 6;
+
   /* for 'type' in DataChannelInit dictionary */
   const unsigned short kDataChannelReliable = 0;
   const unsigned short kDataChannelPartialReliableRexmit = 1;
   const unsigned short kDataChannelPartialReliableTimed = 2;
 
   /* Constants for 'name' in error callbacks */
   const unsigned long kNoError                          = 0; // Test driver only
   const unsigned long kInvalidConstraintsType           = 1;
@@ -139,16 +149,17 @@ interface IPeerConnection : nsISupports
   void close();
 
   /* Attributes */
   readonly attribute string localDescription;
   readonly attribute string remoteDescription;
 
   readonly attribute unsigned long iceState;
   readonly attribute unsigned long readyState;
+  readonly attribute unsigned long signalingState;
   readonly attribute unsigned long sipccState;
 
   /* Data channels */
   nsIDOMDataChannel createDataChannel(in ACString label, in ACString protocol,
     in unsigned short type, in boolean outOfOrderAllowed,
     in unsigned short maxTime, in unsigned short maxNum,
     in boolean externalNegotiated, in unsigned short stream);
   void connectDataConnection(in unsigned short localport,
--- a/dom/media/tests/mochitest/Makefile.in
+++ b/dom/media/tests/mochitest/Makefile.in
@@ -30,16 +30,23 @@ MOCHITEST_FILES = \
   test_peerConnection_basicAudioVideo.html \
   test_peerConnection_basicAudioVideoCombined.html \
   test_peerConnection_basicVideo.html \
   test_peerConnection_errorCallbacks.html \
   test_peerConnection_offerRequiresReceiveAudio.html \
   test_peerConnection_offerRequiresReceiveVideo.html \
   test_peerConnection_offerRequiresReceiveVideoAudio.html \
   test_peerConnection_throwInCallbacks.html \
+  test_peerConnection_setLocalAnswerInStable.html \
+  test_peerConnection_setRemoteAnswerInStable.html \
+  test_peerConnection_setLocalAnswerInHaveLocalOffer.html \
+  test_peerConnection_setRemoteOfferInHaveLocalOffer.html \
+  test_peerConnection_setLocalOfferInHaveRemoteOffer.html \
+  test_peerConnection_setRemoteAnswerInHaveRemoteOffer.html \
+  test_peerConnection_addCandidateInHaveLocalOffer.html \
   test_peerConnection_bug822674.html \
   test_peerConnection_bug825703.html \
   test_peerConnection_bug827843.html \
   test_peerConnection_bug834153.html \
   test_peerConnection_bug835370.html \
   head.js \
   mediaStreamPlayback.js \
   pc.js \
--- a/dom/media/tests/mochitest/head.js
+++ b/dom/media/tests/mochitest/head.js
@@ -177,16 +177,28 @@ function checkMediaStreamTracks(constrai
 function unexpectedCallbackAndFinish(error) {
   /**
    * @param {object} aObj
    *        The object fired back from the callback
    */
   return function(aObj) {
     var where = error.fileName + ":" + error.lineNumber;
     if (aObj && aObj.name && aObj.message) {
-      ok(false, "Unexpected error callback from " + where + " with name = '" +
+      ok(false, "Unexpected callback/event from " + where + " with name = '" +
                 aObj.name + "', message = '" + aObj.message + "'");
     } else {
-      ok(false, "Unexpected error callback from " + where + " with " + aObj);
+      ok(false, "Unexpected callback/event from " + where + " with " + aObj);
     }
     SimpleTest.finish();
   }
 }
+
+/**
+ * Generates a callback function suitable for putting int a success
+ * callback in circumstances where success is unexpected. The callback,
+ * if activated, will kill off the test gracefully.
+ */
+
+function unexpectedSuccessCallbackAndFinish(error, reason) {
+  return function() {
+    unexpectedCallbackAndFinish(error)(message);
+  }
+}
--- a/dom/media/tests/mochitest/pc.js
+++ b/dom/media/tests/mochitest/pc.js
@@ -242,61 +242,71 @@ var commandsPeerConnection = [
     'PC_REMOTE_GUM',
     function (test) {
       test.pcRemote.getAllUserMedia(function () {
         test.next();
       });
     }
   ],
   [
+    'PC_CHECK_INITIAL_SIGNALINGSTATE',
+    function (test) {
+      is(test.pcLocal.signalingState,"stable", "Initial local signalingState is stable");
+      is(test.pcRemote.signalingState,"stable", "Initial remote signalingState is stable");
+      test.next();
+    }
+  ],
+  [
     'PC_LOCAL_CREATE_OFFER',
     function (test) {
       test.pcLocal.createOffer(function () {
+        is(test.pcLocal.signalingState, "stable", "Local create offer does not change signaling state");
         test.next();
       });
     }
   ],
   [
     'PC_LOCAL_SET_LOCAL_DESCRIPTION',
     function (test) {
-      test.pcLocal.setLocalDescription(test.pcLocal._last_offer, function () {
-        test.next();
-      });
+      test.expectStateChange(test.pcLocal, "have-local-offer", test);
+      test.pcLocal.setLocalDescription(test.pcLocal._last_offer,
+        test.checkStateInCallback(test.pcLocal, "have-local-offer", test));
     }
   ],
   [
     'PC_REMOTE_SET_REMOTE_DESCRIPTION',
     function (test) {
-      test.pcRemote.setRemoteDescription(test.pcLocal._last_offer, function () {
-        test.next();
-      });
+      test.expectStateChange(test.pcRemote, "have-remote-offer", test);
+      test.pcRemote.setRemoteDescription(test.pcLocal._last_offer,
+        test.checkStateInCallback(test.pcRemote, "have-remote-offer", test));
     }
   ],
   [
     'PC_REMOTE_CREATE_ANSWER',
     function (test) {
       test.pcRemote.createAnswer(function () {
+        is(test.pcRemote.signalingState, "have-remote-offer", "Remote create offer does not change signaling state");
         test.next();
       });
     }
   ],
   [
     'PC_LOCAL_SET_REMOTE_DESCRIPTION',
     function (test) {
-      test.pcLocal.setRemoteDescription(test.pcRemote._last_answer, function () {
-        test.next();
-      });
+      test.expectStateChange(test.pcLocal, "stable", test);
+      test.pcLocal.setRemoteDescription(test.pcRemote._last_answer,
+        test.checkStateInCallback(test.pcLocal, "stable", test));
     }
   ],
   [
     'PC_REMOTE_SET_LOCAL_DESCRIPTION',
     function (test) {
-      test.pcRemote.setLocalDescription(test.pcRemote._last_answer, function () {
-        test.next();
-      });
+      test.expectStateChange(test.pcRemote, "stable", test);
+      test.pcRemote.setLocalDescription(test.pcRemote._last_answer,
+        test.checkStateInCallback(test.pcRemote, "stable", test));
     }
   ],
   [
     'PC_LOCAL_CHECK_MEDIA',
     function (test) {
       test.pcLocal.checkMedia(test.pcRemote.constraints);
       test.next();
     }
@@ -389,16 +399,74 @@ PeerConnectionTest.prototype.teardown = 
     this.pcRemote.close();
     this.pcRemote = null;
   }
 
   info("Test finished");
   SimpleTest.finish();
 };
 
+/**
+ * Sets up the "onsignalingstatechange" handler for the indicated peerconnection
+ * as a one-shot test. If the test.commandSuccess flag is set when the event
+ * happens, then the next test in the command chain is triggered. After
+ * running, this sets the event handler so that it will fail the test if
+ * it fires again before we expect it. This is intended to be used in
+ * conjunction with checkStateInCallback, below.
+ *
+ * @param {pcw} PeerConnectionWrapper
+ *        The peer connection to expect a state change on
+ * @param {state} string
+ *        The state that we expect to change to
+ * @param {test} PeerConnectionTest
+ *        The test strucure currently in use.
+ */
+PeerConnectionTest.prototype.expectStateChange =
+function PCT_expectStateChange(pcw, state, test) {
+  pcw.signalingChangeEvent = false;
+  pcw._pc.onsignalingstatechange = function() {
+    pcw._pc.onsignalingstatechange = unexpectedCallbackAndFinish(new Error);
+    is(pcw._pc.signalingState, state, pcw.label + ": State is " + state + " in onsignalingstatechange");
+    pcw.signalingChangeEvent = true;
+    if (pcw.commandSuccess) {
+      test.next();
+    } else {
+      info("Waiting for success callback...");
+    }
+  };
+}
+
+/**
+ * Returns a function, suitable for use as a success callback, that
+ * checks the signaling state of the PC; and, if the signalingstatechange
+ * event has already fired, moves on to the next test case. This is
+ * intended to be used in conjunction with expectStateChange, above.
+ *
+ * @param {pcw} PeerConnectionWrapper
+ *        The peer connection to expect a state change on
+ * @param {state} string
+ *        The state that we expect to change to
+ * @param {test} PeerConnectionTest
+ *        The test strucure currently in use.
+ */
+
+PeerConnectionTest.prototype.checkStateInCallback =
+function PCT_checkStateInCallback(pcw, state, test) {
+  pcw.commandSuccess = false;
+  return function() {
+    pcw.commandSuccess = true;
+    is(pcw.signalingState, state, pcw.label + ": State is " + state + " in success callback");
+    if (pcw.signalingChangeEvent) {
+      test.next();
+    } else {
+      info("Waiting for signalingstatechange event...");
+    }
+  };
+}
+
 
 /**
  * This class handles acts as a wrapper around a PeerConnection instance.
  *
  * @constructor
  * @param {string} label
  *        Description for the peer connection instance
  * @param {object} configuration
@@ -415,16 +483,19 @@ function PeerConnectionWrapper(label, co
   info("Creating new PeerConnectionWrapper: " + this.label);
   this._pc = new mozRTCPeerConnection(this.configuration);
 
   var self = this;
   this._pc.onaddstream = function (event) {
     // Bug 834835: Assume type is video until we get get{Audio,Video}Tracks.
     self.attachMedia(event.stream, 'video', 'remote');
   };
+
+  // Make sure no signaling state changes are fired until we expect them to
+  this._pc.onsignalingstatechange = unexpectedCallbackAndFinish(new Error);
 }
 
 PeerConnectionWrapper.prototype = {
 
   /**
    * Returns the local description.
    *
    * @returns {object} The local description
@@ -458,16 +529,25 @@ PeerConnectionWrapper.prototype = {
    * @param {object} desc
    *        The new remote description
    */
   set remoteDescription(desc) {
     this._pc.remoteDescription = desc;
   },
 
   /**
+   * Returns the remote signaling state.
+   *
+   * @returns {object} The local description
+   */
+  get signalingState() {
+    return this._pc.signalingState;
+  },
+
+  /**
    * Callback when we get media from either side. Also an appropriate
    * HTML media element will be created.
    *
    * @param {MediaStream} stream
    *        Media stream to handle
    * @param {string} type
    *        The type of media stream ('audio' or 'video')
    * @param {string} side
@@ -567,32 +647,107 @@ PeerConnectionWrapper.prototype = {
     var self = this;
     this._pc.setLocalDescription(desc, function () {
       info("Successfully set the local description for " + self.label);
       onSuccess();
     }, unexpectedCallbackAndFinish(new Error));
   },
 
   /**
+   * Tries to set the local description and expect failure. Automatically
+   * causes the test case to fail if the call succeeds.
+   *
+   * @param {object} desc
+   *        mozRTCSessionDescription for the local description request
+   * @param {function} onFailure
+   *        Callback to execute if the call fails.
+   */
+  setLocalDescriptionAndFail : function PCW_setLocalDescriptionAndFail(desc, onFailure) {
+    var self = this;
+    this._pc.setLocalDescription(desc,
+      unexpectedSuccessCallbackAndFinish(new Error, "setLocalDescription should have failed."),
+      function (err) {
+        info("As expected, failed to set the local description for " + self.label);
+        onFailure(err);
+    });
+  },
+
+  /**
    * Sets the remote description and automatically handles the failure case.
    *
    * @param {object} desc
    *        mozRTCSessionDescription for the remote description request
    * @param {function} onSuccess
    *        Callback to execute if the remote description was set successfully
    */
   setRemoteDescription : function PCW_setRemoteDescription(desc, onSuccess) {
     var self = this;
     this._pc.setRemoteDescription(desc, function () {
       info("Successfully set remote description for " + self.label);
       onSuccess();
     }, unexpectedCallbackAndFinish(new Error));
   },
 
   /**
+   * Tries to set the remote description and expect failure. Automatically
+   * causes the test case to fail if the call succeeds.
+   *
+   * @param {object} desc
+   *        mozRTCSessionDescription for the remote description request
+   * @param {function} onFailure
+   *        Callback to execute if the call fails.
+   */
+  setRemoteDescriptionAndFail : function PCW_setRemoteDescriptionAndFail(desc, onFailure) {
+    var self = this;
+    this._pc.setRemoteDescription(desc,
+      unexpectedSuccessCallbackAndFinish(new Error, "setRemoteDescription should have failed."),
+      function (err) {
+        info("As expected, failed to set the remote description for " + self.label);
+        onFailure(err);
+    });
+  },
+
+  /**
+   * Adds an ICE candidate and automatically handles the failure case.
+   *
+   * @param {object} candidate
+   *        SDP candidate
+   * @param {function} onSuccess
+   *        Callback to execute if the local description was set successfully
+   */
+  addIceCandidate : function PCW_addIceCandidate(candidate, onSuccess) {
+    var self = this;
+
+    this._pc.addIceCandidate(candidate, function () {
+      info("Successfully added an ICE candidate to " + self.label);
+      onSuccess();
+    }, unexpectedCallbackAndFinish(new Error));
+  },
+
+  /**
+   * Tries to add an ICE candidate and expects failure. Automatically
+   * causes the test case to fail if the call succeeds.
+   *
+   * @param {object} candidate
+   *        SDP candidate
+   * @param {function} onFailure
+   *        Callback to execute if the call fails.
+   */
+  addIceCandidateAndFail : function PCW_addIceCandidateAndFail(candidate, onFailure) {
+    var self = this;
+
+    this._pc.addIceCandidate(candidate,
+      unexpectedSuccessCallbackAndFinish(new Error, "addIceCandidate should have failed."),
+      function (err) {
+        info("As expected, failed to add an ICE candidate to " + self.label);
+        onFailure(err);
+    }) ;
+  },
+
+  /**
    * Checks that we are getting the media we expect.
    *
    * @param {object} constraintsRemote
    *        The media constraints of the remote peer connection object
    */
   checkMedia : function PCW_checkMedia(constraintsRemote) {
     is(this._pc.localStreams.length, this.constraints.length,
        this.label + ' has ' + this.constraints.length + ' local streams');
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_peerConnection_addCandidateInHaveLocalOffer.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
+  <script type="application/javascript" src="pc.js"></script>
+</head>
+<body>
+<pre id="test">
+<script type="application/javascript">
+  createHTML({
+    bug: "784519",
+    title: "addCandidate (answer) in 'have-local-offer'"
+  });
+
+  var test;
+  runTest(function () {
+    test = new PeerConnectionTest();
+    test.setMediaConstraints([{audio: true}], [{audio: true}]);
+    test.chain.removeAfter("PC_LOCAL_SET_LOCAL_DESCRIPTION");
+
+    test.chain.append([[
+      "PC_LOCAL_ADD_CANDIDATE",
+      function (test) {
+        test.pcLocal.addIceCandidateAndFail(
+          mozRTCIceCandidate(
+            {candidate:"1 1 UDP 2130706431 192.168.2.1 50005 typ host",
+             sdpMLineIndex: 1}),
+          function(err) {
+            is(err.name, "INVALID_STATE", "Error is INVALID_STATE");
+            test.next();
+          } );
+      }
+    ]]);
+
+    test.run();
+  });
+</script>
+</pre>
+</body>
+</html>
--- a/dom/media/tests/mochitest/test_peerConnection_bug825703.html
+++ b/dom/media/tests/mochitest/test_peerConnection_bug825703.html
@@ -49,17 +49,17 @@
     makePC({ iceServers: [{ url:"" }] }, false);
 
     makePC({ iceServers: [{ url:"http:0.0.0.0" }] }, false);
 
     makePC({ iceServers: [
                 { url:"stun:0.0.0.0" },
                 { url:"stuns:x.net", foo:"" },
                 { url:"turn:[::192.9.5.5]:42", username:"p", credential:"p" },
-                { url:"turns:x.org:42", username:"p", credential:"p" }
+                { url:"turns:x.org:42?transport=udp", username:"p", credential:"p" }
                 ]}, true);
 
     pcs = null;
     SimpleTest.finish();
   });
 </script>
 </pre>
 </body>
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_peerConnection_setLocalAnswerInHaveLocalOffer.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
+  <script type="application/javascript" src="pc.js"></script>
+</head>
+<body>
+<pre id="test">
+<script type="application/javascript">
+  createHTML({
+    bug: "784519",
+    title: "setLocalDescription (answer) in 'have-local-offer'"
+  });
+
+  var test;
+  runTest(function () {
+    test = new PeerConnectionTest();
+    test.setMediaConstraints([{audio: true}], [{audio: true}]);
+    test.chain.removeAfter("PC_LOCAL_SET_LOCAL_DESCRIPTION");
+
+    test.chain.append([[
+      "PC_LOCAL_SET_LOCAL_ANSWER",
+      function (test) {
+        test.pcLocal._last_offer.type="answer";
+        test.pcLocal.setLocalDescriptionAndFail(test.pcLocal._last_offer,
+          function(err) {
+            is(err.name, "INVALID_STATE", "Error is INVALID_STATE");
+            test.next();
+          } );
+      }
+    ]]);
+
+    test.run();
+  });
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_peerConnection_setLocalAnswerInStable.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
+  <script type="application/javascript" src="pc.js"></script>
+</head>
+<body>
+<pre id="test">
+<script type="application/javascript">
+  createHTML({
+    bug: "784519",
+    title: "setLocalDescription (answer) in 'stable'"
+  });
+
+  var test;
+  runTest(function () {
+    test = new PeerConnectionTest();
+    test.setMediaConstraints([{audio: true}], [{audio: true}]);
+    test.chain.removeAfter("PC_LOCAL_CREATE_OFFER");
+
+    test.chain.append([[
+      "PC_LOCAL_SET_LOCAL_ANSWER",
+      function (test) {
+        test.pcLocal._last_offer.type="answer";
+        test.pcLocal.setLocalDescriptionAndFail(test.pcLocal._last_offer,
+          function(err) {
+            is(err.name, "INVALID_STATE", "Error is INVALID_STATE");
+            test.next();
+          } );
+      }
+    ]]);
+
+    test.run();
+  });
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_peerConnection_setLocalOfferInHaveRemoteOffer.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
+  <script type="application/javascript" src="pc.js"></script>
+</head>
+<body>
+<pre id="test">
+<script type="application/javascript">
+  createHTML({
+    bug: "784519",
+    title: "setLocalDescription (offer) in 'have-remote-offer'"
+  });
+
+  var test;
+  runTest(function () {
+    test = new PeerConnectionTest();
+    test.setMediaConstraints([{audio: true}], [{audio: true}]);
+    test.chain.removeAfter("PC_REMOTE_SET_REMOTE_DESCRIPTION");
+
+    test.chain.append([[
+      "PC_REMOTE_SET_LOCAL_OFFER",
+      function (test) {
+        test.pcRemote.setLocalDescriptionAndFail(test.pcLocal._last_offer,
+          function(err) {
+            is(err.name, "INVALID_STATE", "Error is INVALID_STATE");
+            test.next();
+          } );
+      }
+    ]]);
+
+    test.run();
+  });
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_peerConnection_setRemoteAnswerInHaveRemoteOffer.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
+  <script type="application/javascript" src="pc.js"></script>
+</head>
+<body>
+<pre id="test">
+<script type="application/javascript">
+  createHTML({
+    bug: "784519",
+    title: "setRemoteDescription (answer) in 'have-remote-offer'"
+  });
+
+  var test;
+  runTest(function () {
+    test = new PeerConnectionTest();
+    test.setMediaConstraints([{audio: true}], [{audio: true}]);
+    test.chain.removeAfter("PC_REMOTE_SET_REMOTE_DESCRIPTION");
+
+    test.chain.append([[
+      "PC_REMOTE_SET_REMOTE_ANSWER",
+      function (test) {
+        test.pcLocal._last_offer.type="answer";
+        test.pcRemote.setRemoteDescriptionAndFail(test.pcLocal._last_offer,
+          function(err) {
+            is(err.name, "INVALID_STATE", "Error is INVALID_STATE");
+            test.next();
+          } );
+      }
+    ]]);
+
+    test.run();
+  });
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_peerConnection_setRemoteAnswerInStable.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
+  <script type="application/javascript" src="pc.js"></script>
+</head>
+<body>
+<pre id="test">
+<script type="application/javascript">
+  createHTML({
+    bug: "784519",
+    title: "setRemoteDescription (answer) in 'stable'"
+  });
+
+  var test;
+  runTest(function () {
+    test = new PeerConnectionTest();
+    test.setMediaConstraints([{audio: true}], [{audio: true}]);
+    test.chain.removeAfter("PC_LOCAL_CREATE_OFFER");
+
+    test.chain.append([[
+      "PC_LOCAL_SET_REMOTE_ANSWER",
+      function (test) {
+        test.pcLocal._last_offer.type="answer";
+        test.pcLocal.setRemoteDescriptionAndFail(test.pcLocal._last_offer,
+          function(err) {
+            is(err.name, "INVALID_STATE", "Error is INVALID_STATE");
+            test.next();
+          } );
+      }
+    ]]);
+
+    test.run();
+  });
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_peerConnection_setRemoteOfferInHaveLocalOffer.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"></script>
+  <script type="application/javascript" src="mediaStreamPlayback.js"></script>
+  <script type="application/javascript" src="pc.js"></script>
+</head>
+<body>
+<pre id="test">
+<script type="application/javascript">
+  createHTML({
+    bug: "784519",
+    title: "setRemoteDescription (offer) in 'have-local-offer'"
+  });
+
+  var test;
+  runTest(function () {
+    test = new PeerConnectionTest();
+    test.setMediaConstraints([{audio: true}], [{audio: true}]);
+    test.chain.removeAfter("PC_LOCAL_SET_LOCAL_DESCRIPTION");
+
+    test.chain.append([[
+      "PC_LOCAL_SET_REMOTE_OFFER",
+      function (test) {
+        test.pcLocal.setRemoteDescriptionAndFail(test.pcLocal._last_offer,
+          function(err) {
+            is(err.name, "INVALID_STATE", "Error is INVALID_STATE");
+            test.next();
+          } );
+      }
+    ]]);
+
+    test.run();
+  });
+</script>
+</pre>
+</body>
+</html>
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -1,54 +1,52 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* 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/. */
 
-#include "mozilla/Util.h"
-
 #include "RuntimeService.h"
 
 #include "nsIContentSecurityPolicy.h"
 #include "nsIDOMChromeWindow.h"
 #include "nsIEffectiveTLDService.h"
 #include "nsIObserverService.h"
 #include "nsIPlatformCharset.h"
 #include "nsIPrincipal.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsISupportsPriority.h"
 #include "nsITimer.h"
 #include "nsPIDOMWindow.h"
-#include "nsLayoutStatics.h"
 
+#include <algorithm>
+#include "GeckoProfiler.h"
 #include "jsdbgapi.h"
 #include "jsfriendapi.h"
+#include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/EventTargetBinding.h"
+#include "mozilla/DebugOnly.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/Util.h"
+#include <Navigator.h>
 #include "nsContentUtils.h"
-#include "nsCxPusher.h"
 #include "nsDOMJSUtils.h"
-#include <Navigator.h>
+#include "nsLayoutStatics.h"
 #include "nsNetUtil.h"
 #include "nsServiceManagerUtils.h"
 #include "nsThreadUtils.h"
 #include "nsXPCOM.h"
 #include "nsXPCOMPrivate.h"
+#include "OSFileConstants.h"
 #include "xpcpublic.h"
 
 #include "Events.h"
 #include "Worker.h"
 #include "WorkerPrivate.h"
 
-#include "OSFileConstants.h"
-#include <algorithm>
-
-#include "GeckoProfiler.h"
-
 using namespace mozilla;
 using namespace mozilla::dom;
 
 USING_WORKERS_NAMESPACE
 
 using mozilla::MutexAutoLock;
 using mozilla::MutexAutoUnlock;
 using mozilla::Preferences;
@@ -67,29 +65,32 @@ using mozilla::Preferences;
 #define WORKER_CONTEXT_NATIVE_STACK_LIMIT 128 * sizeof(size_t) * 1024
 
 // The maximum number of threads to use for workers, overridable via pref.
 #define MAX_WORKERS_PER_DOMAIN 10
 
 MOZ_STATIC_ASSERT(MAX_WORKERS_PER_DOMAIN >= 1,
                   "We should allow at least one worker per domain.");
 
-// The default number of seconds that close handlers will be allowed to run.
+// The default number of seconds that close handlers will be allowed to run for
+// content workers.
 #define MAX_SCRIPT_RUN_TIME_SEC 10
 
 // The number of seconds that idle threads can hang around before being killed.
 #define IDLE_THREAD_TIMEOUT_SEC 30
 
 // The maximum number of threads that can be idle at one time.
 #define MAX_IDLE_THREADS 20
 
-#define PREF_WORKERS_ENABLED "dom.workers.enabled"
-#define PREF_WORKERS_MAX_PER_DOMAIN "dom.workers.maxPerDomain"
-#define PREF_WORKERS_GCZEAL "dom.workers.gczeal"
-#define PREF_MAX_SCRIPT_RUN_TIME "dom.max_script_run_time"
+#define PREF_WORKERS_PREFIX "dom.workers."
+#define PREF_WORKERS_ENABLED PREF_WORKERS_PREFIX "enabled"
+#define PREF_WORKERS_MAX_PER_DOMAIN PREF_WORKERS_PREFIX "maxPerDomain"
+
+#define PREF_MAX_SCRIPT_RUN_TIME_CONTENT "dom.max_script_run_time"
+#define PREF_MAX_SCRIPT_RUN_TIME_CHROME "dom.max_chrome_script_run_time"
 
 #define GC_REQUEST_OBSERVER_TOPIC "child-gc-request"
 #define MEMORY_PRESSURE_OBSERVER_TOPIC "memory-pressure"
 
 #define BROADCAST_ALL_WORKERS(_func, ...)                                      \
   PR_BEGIN_MACRO                                                               \
     AssertIsOnMainThread();                                                    \
                                                                                \
@@ -104,28 +105,38 @@ MOZ_STATIC_ASSERT(MAX_WORKERS_PER_DOMAIN
       AutoSafeJSContext cx;                                                    \
       JSAutoRequest ar(cx);                                                    \
       for (uint32_t index = 0; index < workers.Length(); index++) {            \
         workers[index]-> _func (cx, __VA_ARGS__);                              \
       }                                                                        \
     }                                                                          \
   PR_END_MACRO
 
+// Prefixes for observing preference changes.
+#define PREF_JS_OPTIONS_PREFIX "javascript.options."
+#define PREF_WORKERS_OPTIONS_PREFIX PREF_WORKERS_PREFIX "options."
+#define PREF_MEM_OPTIONS_PREFIX "mem."
+#define PREF_JIT_HARDENING "jit_hardening"
+#define PREF_GCZEAL "gcZeal"
+
 namespace {
 
 const uint32_t kNoIndex = uint32_t(-1);
 
 const uint32_t kRequiredJSContextOptions =
   JSOPTION_DONT_REPORT_UNCAUGHT | JSOPTION_NO_SCRIPT_RVAL;
 
 uint32_t gMaxWorkersPerDomain = MAX_WORKERS_PER_DOMAIN;
 
 // Does not hold an owning reference.
 RuntimeService* gRuntimeService = nullptr;
 
+// Only non-null during the call to Init.
+RuntimeService* gRuntimeServiceDuringInit = nullptr;
+
 enum {
   ID_Worker = 0,
   ID_ChromeWorker,
   ID_Event,
   ID_MessageEvent,
   ID_ErrorEvent,
 
   ID_COUNT
@@ -143,110 +154,438 @@ const char* gStringChars[] = {
 
   // XXX Don't care about ProgressEvent since it should never leak to the main
   // thread.
 };
 
 MOZ_STATIC_ASSERT(NS_ARRAY_LENGTH(gStringChars) == ID_COUNT,