Merge m-c to b2ginbound, a=merge
authorWes Kocher <wkocher@mozilla.com>
Thu, 05 Nov 2015 18:46:25 -0800
changeset 293055 26b93adf313eb68d7690493fa8c08c0571b1e3df
parent 293054 15bd8ceb6c246fc61e940aef57de983463a847c9 (current diff)
parent 293044 cc48981c026c50fdf80d47b040ae1fb8fe99ad07 (diff)
child 293056 6877bc59092d1ca38694c4e4b154c3a01be8aadd
push id8824
push userraliiev@mozilla.com
push dateMon, 14 Dec 2015 20:18:56 +0000
treeherdermozilla-aurora@e2031358e2a6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone45.0a1
Merge m-c to b2ginbound, a=merge
toolkit/themes/osx/mozapps/extensions/category-searchengines.png
toolkit/themes/windows/mozapps/extensions/category-searchengines.png
--- a/addon-sdk/moz.build
+++ b/addon-sdk/moz.build
@@ -277,16 +277,17 @@ EXTRA_JS_MODULES.commonjs.sdk.core += [
     'source/lib/sdk/core/reference.js',
 ]
 
 EXTRA_JS_MODULES.commonjs.sdk.deprecated.events += [
     'source/lib/sdk/deprecated/events/assembler.js',
 ]
 
 EXTRA_JS_MODULES.commonjs.sdk.dom += [
+    'source/lib/sdk/dom/events-shimmed.js',
     'source/lib/sdk/dom/events.js',
 ]
 
 EXTRA_JS_MODULES.commonjs.sdk.dom.events += [
     'source/lib/sdk/dom/events/keys.js',
 ]
 
 EXTRA_JS_MODULES.commonjs.sdk.event += [
@@ -403,16 +404,17 @@ EXTRA_JS_MODULES.commonjs.sdk.remote += 
 EXTRA_JS_MODULES.commonjs.sdk.stylesheet += [
     'source/lib/sdk/stylesheet/style.js',
     'source/lib/sdk/stylesheet/utils.js',
 ]
 
 EXTRA_JS_MODULES.commonjs.sdk.system += [
     'source/lib/sdk/system/child_process.js',
     'source/lib/sdk/system/environment.js',
+    'source/lib/sdk/system/events-shimmed.js',
     'source/lib/sdk/system/events.js',
     'source/lib/sdk/system/globals.js',
     'source/lib/sdk/system/process.js',
     'source/lib/sdk/system/runtime.js',
     'source/lib/sdk/system/unload.js',
     'source/lib/sdk/system/xul-app.js',
     'source/lib/sdk/system/xul-app.jsm',
 ]
--- a/addon-sdk/source/lib/framescript/content.jsm
+++ b/addon-sdk/source/lib/framescript/content.jsm
@@ -29,16 +29,18 @@ cpmm.sendAsyncMessage('sdk/remote/proces
 cpmm.addMessageListener('sdk/remote/process/load', ({ data: { modulePath, loaderID, options, reason } }) => {
   if (modulePath != PATH)
     return;
 
   // During startup races can mean we get a second load message
   if (addons.has(loaderID))
     return;
 
+  options.waiveInterposition = true;
+
   let loader = Loader.Loader(options);
   let addon = {
     loader,
     require: Loader.Require(loader, { id: 'LoaderHelper' }),
   }
   addons.set(loaderID, addon);
 
   cpmm.sendAsyncMessage('sdk/remote/process/attach', {
--- a/addon-sdk/source/lib/sdk/content/page-mod.js
+++ b/addon-sdk/source/lib/sdk/content/page-mod.js
@@ -13,18 +13,17 @@ const { Disposable } = require('../core/
 const { WeakReference } = require('../core/reference');
 const { WorkerChild } = require('./worker-child');
 const { EventTarget } = require('../event/target');
 const { on, emit, once, setListeners } = require('../event/core');
 const { on: domOn, removeListener: domOff } = require('../dom/events');
 const { isRegExp, isUndefined } = require('../lang/type');
 const { merge } = require('../util/object');
 const { isBrowser, getFrames } = require('../window/utils');
-const { getTabs, getTabContentWindow, getTabForContentWindow,
-        getURI: getTabURI } = require('../tabs/utils');
+const { getTabs, getURI: getTabURI } = require('../tabs/utils');
 const { ignoreWindow } = require('../private-browsing/utils');
 const { Style } = require("../stylesheet/style");
 const { attach, detach } = require("../content/mod");
 const { has, hasAny } = require("../util/array");
 const { Rules } = require("../util/rules");
 const { List, addListItem, removeListItem } = require('../util/list');
 const { when } = require("../system/unload");
 const { uuid } = require('../util/uuid');
--- a/addon-sdk/source/lib/sdk/content/sandbox.js
+++ b/addon-sdk/source/lib/sdk/content/sandbox.js
@@ -13,17 +13,17 @@ const { on, off, emit } = require('../ev
 const { events } = require('./sandbox/events');
 const { requiresAddonGlobal } = require('./utils');
 const { delay: async } = require('../lang/functional');
 const { Ci, Cu, Cc } = require('chrome');
 const timer = require('../timers');
 const { URL } = require('../url');
 const { sandbox, evaluate, load } = require('../loader/sandbox');
 const { merge } = require('../util/object');
-const { getTabForContentWindow } = require('../tabs/utils');
+const { getTabForContentWindowNoShim } = require('../tabs/utils');
 const { getInnerId } = require('../window/utils');
 const { PlainTextConsole } = require('../console/plain-text');
 const { data } = require('../self');const { isChildLoader } = require('../remote/core');
 // WeakMap of sandboxes so we can access private values
 const sandboxes = new WeakMap();
 
 /* Trick the linker in order to ensure shipping these files in the XPI.
   require('./content-worker.js');
@@ -52,17 +52,17 @@ const JS_VERSION = '1.8';
 function isWindowInTab(window) {
   if (isChildLoader) {
     let { frames } = require('../remote/child');
     let frame = frames.getFrameForWindow(window.top);
     return frame && frame.isTab;
   }
   else {
     // The deprecated sync worker API still does everything in the main process
-    return getTabForContentWindow(window);
+    return getTabForContentWindowNoShim(window);
   }
 }
 
 const WorkerSandbox = Class({
   implements: [ EventTarget ],
 
   /**
    * Emit a message to the worker content sandbox
--- a/addon-sdk/source/lib/sdk/content/worker.js
+++ b/addon-sdk/source/lib/sdk/content/worker.js
@@ -9,21 +9,21 @@ module.metadata = {
 
 const { emit } = require('../event/core');
 const { omit, merge } = require('../util/object');
 const { Class } = require('../core/heritage');
 const { method } = require('../lang/functional');
 const { getInnerId } = require('../window/utils');
 const { EventTarget } = require('../event/target');
 const { isPrivate } = require('../private-browsing/utils');
-const { getTabForBrowser, getTabForContentWindow, getBrowserForTab } = require('../tabs/utils');
+const { getTabForBrowser, getTabForContentWindowNoShim, getBrowserForTab } = require('../tabs/utils');
 const { attach, connect, detach, destroy, makeChildOptions } = require('./utils');
 const { ensure } = require('../system/unload');
 const { on: observe } = require('../system/events');
-const { Ci } = require('chrome');
+const { Ci, Cu } = require('chrome');
 const { modelFor: tabFor } = require('sdk/model/core');
 const { remoteRequire, processes, frames } = require('../remote/parent');
 remoteRequire('sdk/content/worker-child');
 
 const workers = new WeakMap();
 var modelFor = (worker) => workers.get(worker);
 
 const ERR_DESTROYED = "Couldn't find the worker to receive this message. " +
@@ -112,23 +112,28 @@ const Worker = Class({
 
   detach: method(detach),
   destroy: method(destroy),
 })
 exports.Worker = Worker;
 
 attach.define(Worker, function(worker, window) {
   // This method of attaching should be deprecated
+
+  if (Cu.isCrossProcessWrapper(window))
+    throw new Error("Attaching worker to a window from another " +
+                    "process directly is not supported.");
+
   let model = modelFor(worker);
   if (model.attached)
     detach(worker);
 
   model.window = window;
   let frame = null;
-  let tab = getTabForContentWindow(window.top);
+  let tab = getTabForContentWindowNoShim(window);
   if (tab)
     frame = frames.getFrameForBrowser(getBrowserForTab(tab));
 
   let childOptions = makeChildOptions(model.options);
   childOptions.windowId = getInnerId(window);
 
   processes.port.emit('sdk/worker/create', childOptions);
 
--- a/addon-sdk/source/lib/sdk/core/observer.js
+++ b/addon-sdk/source/lib/sdk/core/observer.js
@@ -4,24 +4,27 @@
 
 "use strict";
 
 module.metadata = {
   "stability": "experimental"
 };
 
 
-const { Cc, Ci, Cr } = require("chrome");
+const { Cc, Ci, Cr, Cu } = require("chrome");
 const { Class } = require("./heritage");
 const { isWeak } = require("./reference");
 const method = require("../../method/core");
 
-const { addObserver, removeObserver } = Cc['@mozilla.org/observer-service;1'].
-                                          getService(Ci.nsIObserverService);
+const observerService = Cc['@mozilla.org/observer-service;1'].
+                          getService(Ci.nsIObserverService);
 
+const { ShimWaiver } = Cu.import("resource://gre/modules/ShimWaiver.jsm");
+const addObserver = ShimWaiver.getProperty(observerService, "addObserver");
+const removeObserver = ShimWaiver.getProperty(observerService, "removeObserver");
 
 // This is a method that will be invoked when notification observer
 // subscribed to occurs.
 const observe = method("observer/observe");
 exports.observe = observe;
 
 // Method to subscribe to the observer notification.
 const subscribe = method("observe/subscribe");
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/lib/sdk/dom/events-shimmed.js
@@ -0,0 +1,18 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+module.metadata = {
+  'stability': 'unstable'
+};
+
+const events = require('./events.js');
+
+exports.emit = (element, type, obj) => events.emit(element, type, obj, true);
+exports.on = (element, type, listener, capture) => events.on(element, type, listener, capture, true);
+exports.once = (element, type, listener, capture) => events.once(element, type, listener, capture, true);
+exports.removeListener = (element, type, listener, capture) => events.removeListener(element, type, listener, capture, true);
+exports.removed = events.removed;
+exports.when = (element, eventName, capture) => events.when(element, eventName, capture ? capture : false, true);
--- a/addon-sdk/source/lib/sdk/dom/events.js
+++ b/addon-sdk/source/lib/sdk/dom/events.js
@@ -3,16 +3,19 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 module.metadata = {
   "stability": "unstable"
 };
 
+const { Cu } = require("chrome");
+const { ShimWaiver } = Cu.import("resource://gre/modules/ShimWaiver.jsm");
+
 // Utility function that returns copy of the given `text` with last character
 // removed if it is `"s"`.
 function singularify(text) {
   return text[text.length - 1] === "s" ? text.substr(0, text.length - 1) : text;
 }
 
 // Utility function that takes event type, argument is passed to
 // `document.createEvent` and returns name of the initializer method of the
@@ -39,20 +42,24 @@ function getInitializerName(category) {
  *    If true, indicates that the user wishes to initiate capture. After
  *    initiating capture, all events of the specified type will be dispatched
  *    to the registered listener before being dispatched to any `EventTarget`s
  *    beneath it in the DOM tree. Events which are bubbling upward through
  *    the tree will not trigger a listener designated to use capture.
  *    See [DOM Level 3 Events](http://www.w3.org/TR/DOM-Level-3-Events/#event-flow)
  *    for a detailed explanation.
  */
-function on(element, type, listener, capture) {
+function on(element, type, listener, capture, shimmed = false) {
   // `capture` defaults to `false`.
   capture = capture || false;
-  element.addEventListener(type, listener, capture);
+  if (shimmed) {
+    element.addEventListener(type, listener, capture);
+  } else {
+    ShimWaiver.getProperty(element, "addEventListener")(type, listener, capture);
+  }
 }
 exports.on = on;
 
 /**
  * Registers an event `listener` on a given `element`, that will be called
  * only once, next time event of specified `type` is dispatched on the
  * `element`.
  * @param {Element} element
@@ -68,21 +75,21 @@ exports.on = on;
  *    If true, indicates that the user wishes to initiate capture. After
  *    initiating capture, all events of the specified type will be dispatched
  *    to the registered listener before being dispatched to any `EventTarget`s
  *    beneath it in the DOM tree. Events which are bubbling upward through
  *    the tree will not trigger a listener designated to use capture.
  *    See [DOM Level 3 Events](http://www.w3.org/TR/DOM-Level-3-Events/#event-flow)
  *    for a detailed explanation.
  */
-function once(element, type, listener, capture) {
+function once(element, type, listener, capture, shimmed = false) {
   on(element, type, function selfRemovableListener(event) {
-    removeListener(element, type, selfRemovableListener, capture);
+    removeListener(element, type, selfRemovableListener, capture, shimmed);
     listener.apply(this, arguments);
-  }, capture);
+  }, capture, shimmed);
 }
 exports.once = once;
 
 /**
  * Unregisters an event `listener` on a given `element` for the events of the
  * specified `type`.
  *
  * @param {Element} element
@@ -98,18 +105,22 @@ exports.once = once;
  *    If true, indicates that the user wishes to initiate capture. After
  *    initiating capture, all events of the specified type will be dispatched
  *    to the registered listener before being dispatched to any `EventTarget`s
  *    beneath it in the DOM tree. Events which are bubbling upward through
  *    the tree will not trigger a listener designated to use capture.
  *    See [DOM Level 3 Events](http://www.w3.org/TR/DOM-Level-3-Events/#event-flow)
  *    for a detailed explanation.
  */
-function removeListener(element, type, listener, capture) {
-  element.removeEventListener(type, listener, capture);
+function removeListener(element, type, listener, capture, shimmed = false) {
+  if (shimmed) {
+    element.removeEventListener(type, listener, capture);
+  } else {
+    ShimWaiver.getProperty(element, "removeEventListener")(type, listener, capture);
+  }
 }
 exports.removeListener = removeListener;
 
 /**
  * Emits event of the specified `type` and `category` on the given `element`.
  * Specified `settings` are used to initialize event before dispatching it.
  * @param {Element} element
  *    Dom element to dispatch event on.
@@ -123,23 +134,27 @@ exports.removeListener = removeListener;
  *    - `initializer`: If passed it will be used as name of the method used
  *      to initialize event. If omitted name will be generated from the
  *      `category` field by prefixing it with `"init"` and removing last
  *      character if it matches `"s"`.
  *    - `settings`: Array of settings that are forwarded to the event
  *      initializer after firs `type` argument.
  * @see https://developer.mozilla.org/En/DOM/Document.createEvent
  */
-function emit(element, type, { category, initializer, settings }) {
+function emit(element, type, { category, initializer, settings }, shimmed = false) {
   category = category || "UIEvents";
   initializer = initializer || getInitializerName(category);
   let document = element.ownerDocument;
   let event = document.createEvent(category);
   event[initializer].apply(event, [type].concat(settings));
-  element.dispatchEvent(event);
+  if (shimmed) {
+    element.dispatchEvent(event);
+  } else {
+    ShimWaiver.getProperty(element, "dispatchEvent")(event);
+  }
 };
 exports.emit = emit;
 
 // Takes DOM `element` and returns promise which is resolved
 // when given element is removed from it's parent node.
 const removed = element => {
   return new Promise(resolve => {
     const { MutationObserver } = element.ownerDocument.defaultView;
@@ -153,17 +168,25 @@ const removed = element => {
         }
       }
     });
     observer.observe(element.parentNode, {childList: true});
   });
 };
 exports.removed = removed;
 
-const when = (element, eventName, capture=false) => new Promise(resolve => {
+const when = (element, eventName, capture=false, shimmed=false) => new Promise(resolve => {
   const listener = event => {
-    element.removeEventListener(eventName, listener, capture);
+    if (shimmed) {
+      element.removeEventListener(eventName, listener, capture);
+    } else {
+      ShimWaiver.getProperty(element, "removeEventListener")(eventName, listener, capture);
+    }
     resolve(event);
   };
 
-  element.addEventListener(eventName, listener, capture);
+  if (shimmed) {
+    element.addEventListener(eventName, listener, capture);
+  } else {
+    ShimWaiver.getProperty(element, "addEventListener")(eventName, listener, capture);
+  }
 });
 exports.when = when;
--- a/addon-sdk/source/lib/sdk/event/chrome.js
+++ b/addon-sdk/source/lib/sdk/event/chrome.js
@@ -3,20 +3,25 @@
  * 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 { Cc, Ci, Cr, Cu } = require("chrome");
 const { emit, on, off } = require("./core");
-const { addObserver, removeObserver } = Cc["@mozilla.org/observer-service;1"].
-                        getService(Ci.nsIObserverService);
+var observerService = Cc["@mozilla.org/observer-service;1"]
+                      .getService(Ci.nsIObserverService);
+
+const { ShimWaiver } = Cu.import("resource://gre/modules/ShimWaiver.jsm");
+const addObserver = ShimWaiver.getProperty(observerService, "addObserver");
+const removeObserver = ShimWaiver.getProperty(observerService, "removeObserver");
+
 const { when: unload } = require("../system/unload");
 
 // 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, {
--- a/addon-sdk/source/lib/sdk/event/dom.js
+++ b/addon-sdk/source/lib/sdk/event/dom.js
@@ -9,24 +9,27 @@ module.metadata = {
 };
 
 const { Ci } = require("chrome");
 
 var { emit } = require("./core");
 var { when: unload } = require("../system/unload");
 var listeners = new Map();
 
+const { Cu } = require("chrome");
+const { ShimWaiver } = Cu.import("resource://gre/modules/ShimWaiver.jsm");
+
 var getWindowFrom = x =>
                     x instanceof Ci.nsIDOMWindow ? x :
                     x instanceof Ci.nsIDOMDocument ? x.defaultView :
                     x instanceof Ci.nsIDOMNode ? x.ownerDocument.defaultView :
                     null;
 
 function removeFromListeners() {
-  this.removeEventListener("DOMWindowClose", removeFromListeners);
+  ShimWaiver.getProperty(this, "removeEventListener")("DOMWindowClose", removeFromListeners);
   for (let cleaner of listeners.get(this))
     cleaner();
 
   listeners.delete(this);
 }
 
 // Simple utility function takes event target, event type and optional
 // `options.capture` and returns node style event stream that emits "data"
@@ -51,21 +54,21 @@ function open(target, type, options) {
 
   let cleaners = listeners.get(window);
   if (!cleaners) {
     cleaners = [];
     listeners.set(window, cleaners);
 
     // We need to remove from our map the `window` once is closed, to prevent
     // memory leak
-    window.addEventListener("DOMWindowClose", removeFromListeners);
+    ShimWaiver.getProperty(window, "addEventListener")("DOMWindowClose", removeFromListeners);
   }
 
-  cleaners.push(() => target.removeEventListener(type, listener, capture));
-  target.addEventListener(type, listener, capture);
+  cleaners.push(() => ShimWaiver.getProperty(target, "removeEventListener")(type, listener, capture));
+  ShimWaiver.getProperty(target, "addEventListener")(type, listener, capture);
 
   return output;
 }
 
 unload(() => {
   for (let window of listeners.keys())
     removeFromListeners.call(window);
 });
--- a/addon-sdk/source/lib/sdk/input/system.js
+++ b/addon-sdk/source/lib/sdk/input/system.js
@@ -4,18 +4,22 @@
 "use strict";
 
 const { Cc, Ci, Cr, Cu } = require("chrome");
 const { Input, start, stop, end, receive, outputs } = require("../event/utils");
 const { once, off } = require("../event/core");
 const { id: addonID } = require("../self");
 
 const unloadMessage = require("@loader/unload");
-const { addObserver, removeObserver } = Cc['@mozilla.org/observer-service;1'].
-                                          getService(Ci.nsIObserverService);
+const observerService = Cc['@mozilla.org/observer-service;1'].
+                          getService(Ci.nsIObserverService);
+const { ShimWaiver } = Cu.import("resource://gre/modules/ShimWaiver.jsm");
+const addObserver = ShimWaiver.getProperty(observerService, "addObserver");
+const removeObserver = ShimWaiver.getProperty(observerService, "removeObserver");
+
 
 const addonUnloadTopic = "sdk:loader:destroy";
 
 const isXrayWrapper = Cu.isXrayWrapper;
 // In the past SDK used to double-wrap notifications dispatched, which
 // made them awkward to use outside of SDK. At present they no longer
 // do that, although we still supported for legacy reasons.
 const isLegacyWrapper = x =>
--- a/addon-sdk/source/lib/sdk/keyboard/observer.js
+++ b/addon-sdk/source/lib/sdk/keyboard/observer.js
@@ -45,13 +45,14 @@ const Observer = Class({
   /**
    * Function handles all the supported events on all the windows that are
    * observed. Method is used to proxy events to the listeners registered on
    * this event emitter.
    * @param {Event} event
    *    Keyboard event being emitted.
    */
   handleEvent(event) {
-    emit(this, event.type, event, event.target.ownerDocument.defaultView);
+    emit(this, event.type, event, event.target.ownerDocument ? event.target.ownerDocument.defaultView
+                                                             : undefined);
   }
 });
 
 exports.observer = new Observer();
--- a/addon-sdk/source/lib/sdk/loader/sandbox.js
+++ b/addon-sdk/source/lib/sdk/loader/sandbox.js
@@ -7,17 +7,17 @@ module.metadata = {
   "stability": "experimental"
 };
 
 const { Cc, Ci, CC, Cu } = require('chrome');
 const systemPrincipal = CC('@mozilla.org/systemprincipal;1', 'nsIPrincipal')();
 const scriptLoader = Cc['@mozilla.org/moz/jssubscript-loader;1'].
                      getService(Ci.mozIJSSubScriptLoader);
 const self = require('sdk/self');
-const { getTabId, getTabForContentWindow } = require('../tabs/utils');
+const { getTabId } = require('../tabs/utils');
 const { getInnerId } = require('../window/utils');
 
 const { devtools } = Cu.import("resource://devtools/shared/Loader.jsm", {});
 const { require: devtoolsRequire } = devtools;
 const { addContentGlobal, removeContentGlobal } = devtoolsRequire("devtools/server/content-globals");
 
 /**
  * Make a new sandbox that inherits given `source`'s principals. Source can be
--- a/addon-sdk/source/lib/sdk/selection.js
+++ b/addon-sdk/source/lib/sdk/selection.js
@@ -15,17 +15,17 @@ module.metadata = {
 const { Ci, Cc } = require("chrome"),
     { setTimeout } = require("./timers"),
     { emit, off } = require("./event/core"),
     { Class, obscure } = require("./core/heritage"),
     { EventTarget } = require("./event/target"),
     { ns } = require("./core/namespace"),
     { when: unload } = require("./system/unload"),
     { ignoreWindow } = require('./private-browsing/utils'),
-    { getTabs, getTabContentWindow, getTabForContentWindow,
+    { getTabs, getTabForContentWindow,
       getAllTabContentWindows } = require('./tabs/utils'),
     winUtils = require("./window/utils"),
     events = require("./system/events");
 
 // The selection types
 const HTML = 0x01,
       TEXT = 0x02,
       DOM  = 0x03; // internal use only
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/lib/sdk/system/events-shimmed.js
@@ -0,0 +1,16 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+module.metadata = {
+  'stability': 'unstable'
+};
+
+const events = require('./events.js');
+
+exports.emit = (type, event) => events.emit(type, event, true);
+exports.on = (type, listener, strong) => events.on(type, listener, strong, true);
+exports.once = (type, listener) => events.once(type, listener, true);
+exports.off = (type, listener) => events.off(type, listener, true);
--- a/addon-sdk/source/lib/sdk/system/events.js
+++ b/addon-sdk/source/lib/sdk/system/events.js
@@ -7,18 +7,23 @@
 module.metadata = {
   'stability': 'unstable'
 };
 
 const { Cc, Ci, Cu } = require('chrome');
 const { Unknown } = require('../platform/xpcom');
 const { Class } = require('../core/heritage');
 const { ns } = require('../core/namespace');
-const { addObserver, removeObserver, notifyObservers } = 
+const observerService =
   Cc['@mozilla.org/observer-service;1'].getService(Ci.nsIObserverService);
+const { addObserver, removeObserver, notifyObservers } = observerService;
+const { ShimWaiver } = Cu.import("resource://gre/modules/ShimWaiver.jsm");
+const addObserverNoShim = ShimWaiver.getProperty(observerService, "addObserver");
+const removeObserverNoShim = ShimWaiver.getProperty(observerService, "removeObserver");
+const notifyObserversNoShim = ShimWaiver.getProperty(observerService, "notifyObservers");
 const unloadSubject = require('@loader/unload');
 
 const Subject = Class({
   extends: Unknown,
   initialize: function initialize(object) {
     // Double-wrap the object and set a property identifying the
     // wrappedJSObject as one of our wrappers to distinguish between
     // subjects that are one of our wrappers (which we should unwrap
@@ -28,32 +33,36 @@ const Subject = Class({
       observersModuleSubjectWrapper: true,
       object: object
     };
   },
   getScriptableHelper: function() {},
   getInterfaces: function() {}
 });
 
-function emit(type, event) {
+function emit(type, event, shimmed = false) {
   // From bug 910599
   // We must test to see if 'subject' or 'data' is a defined property
   // of the event object, but also allow primitives to be passed in,
   // which the `in` operator breaks, yet `null` is an object, hence
   // the long conditional
   let subject = event && typeof event === 'object' && 'subject' in event ?
     Subject(event.subject) :
     null;
   let data = event && typeof event === 'object' ?
     // An object either returns its `data` property or null
     ('data' in event ? event.data : null) :
     // All other types return themselves (and cast to strings/null
     // via observer service)
     event;
-  notifyObservers(subject, type, data);
+  if (shimmed) {
+    notifyObservers(subject, type, data);
+  } else {
+    notifyObserversNoShim(subject, type, data);
+  }
 }
 exports.emit = emit;
 
 const Observer = Class({
   extends: Unknown,
   initialize: function initialize(listener) {
     this.listener = listener;
   },
@@ -78,78 +87,95 @@ const Observer = Class({
     catch (error) {
       console.exception(error);
     }
   }
 });
 
 const subscribers = ns();
 
-function on(type, listener, strong) {
+function on(type, listener, strong, shimmed = false) {
   // Unless last optional argument is `true` we use a weak reference to a
   // listener.
   let weak = !strong;
   // Take list of observers associated with given `listener` function.
   let observers = subscribers(listener);
   // If `observer` for the given `type` is not registered yet, then
   // associate an `observer` and register it.
   if (!(type in observers)) {
     let observer = Observer(listener);
     observers[type] = observer;
-    addObserver(observer, type, weak);
+    if (shimmed) {
+      addObserver(observer, type, weak);
+    } else {
+      addObserverNoShim(observer, type, weak);
+    }
     // WeakRef gymnastics to remove all alive observers on unload
     let ref = Cu.getWeakReference(observer);
     weakRefs.set(observer, ref);
     stillAlive.set(ref, type);
+    wasShimmed.set(ref, shimmed);
   }
 }
 exports.on = on;
 
-function once(type, listener) {
+function once(type, listener, shimmed = false) {
   // Note: this code assumes order in which listeners are called, which is fine
   // as long as dispatch happens in same order as listener registration which
   // is the case now. That being said we should be aware that this may break
   // in a future if order will change.
-  on(type, listener);
+  on(type, listener, shimmed);
   on(type, function cleanup() {
-    off(type, listener);
-    off(type, cleanup);
-  }, true);
+    off(type, listener, shimmed);
+    off(type, cleanup, shimmed);
+  }, true, shimmed);
 }
 exports.once = once;
 
-function off(type, listener) {
+function off(type, listener, shimmed = false) {
   // Take list of observers as with the given `listener`.
   let observers = subscribers(listener);
   // If `observer` for the given `type` is registered, then
   // remove it & unregister.
   if (type in observers) {
     let observer = observers[type];
     delete observers[type];
-    removeObserver(observer, type);
+    if (shimmed) {
+      removeObserver(observer, type);
+    } else {
+      removeObserverNoShim(observer, type);
+    }
     stillAlive.delete(weakRefs.get(observer));
+    wasShimmed.delete(weakRefs.get(observer));
   }
 }
 exports.off = off;
 
 // must use WeakMap to keep reference to all the WeakRefs (!), see bug 986115
 var weakRefs = new WeakMap();
 
 // and we're out of beta, we're releasing on time!
-var stillAlive = new Map();   
+var stillAlive = new Map();
+
+var wasShimmed = new Map();
 
 on('sdk:loader:destroy', function onunload({ subject, data: reason }) {
   // using logic from ./unload, to avoid a circular module reference
   if (subject.wrappedJSObject === unloadSubject) {
-    off('sdk:loader:destroy', onunload);
+    off('sdk:loader:destroy', onunload, false);
 
     // don't bother
     if (reason === 'shutdown') 
       return;
 
     stillAlive.forEach( (type, ref) => {
       let observer = ref.get();
-      if (observer) 
-        removeObserver(observer, type);
+      if (observer) {
+        if (wasShimmed.get(ref)) {
+          removeObserver(observer, type);
+        } else {
+          removeObserverNoShim(observer, type);
+        }
+      }
     })
   }
   // a strong reference
-}, true);
+}, true, false);
--- a/addon-sdk/source/lib/sdk/tabs/helpers.js
+++ b/addon-sdk/source/lib/sdk/tabs/helpers.js
@@ -6,17 +6,17 @@
 module.metadata = {
   'stability': 'unstable'
 };
 
 
 // NOTE: This file should only export Tab instances
 
 
-const { getTabForContentWindow, getTabForBrowser: getRawTabForBrowser } = require('./utils');
+const { getTabForBrowser: getRawTabForBrowser } = require('./utils');
 const { modelFor } = require('../model/core');
 
 exports.getTabForRawTab = modelFor;
 
 function getTabForBrowser(browser) {
   return modelFor(getRawTabForBrowser(browser)) || null;
 }
 exports.getTabForBrowser = getTabForBrowser;
--- a/addon-sdk/source/lib/sdk/tabs/utils.js
+++ b/addon-sdk/source/lib/sdk/tabs/utils.js
@@ -6,20 +6,21 @@
 module.metadata = {
   'stability': 'unstable'
 };
 
 
 // NOTE: This file should only deal with xul/native tabs
 
 
-const { Ci } = require('chrome');
+const { Ci, Cu } = require('chrome');
 const { defer } = require("../lang/functional");
 const { windows, isBrowser } = require('../window/utils');
 const { isPrivateBrowsingSupported } = require('../self');
+const { ShimWaiver } = Cu.import("resource://gre/modules/ShimWaiver.jsm");
 
 // Bug 834961: ignore private windows when they are not supported
 function getWindows() {
   return windows(null, { includePrivate: isPrivateBrowsingSupported });
 }
 
 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
@@ -257,16 +258,26 @@ function getAllTabContentWindows() {
 exports.getAllTabContentWindows = getAllTabContentWindows;
 
 // gets the tab containing the provided window
 function getTabForContentWindow(window) {
   return getTabs().find(tab => getTabContentWindow(tab) === window.top) || null;
 }
 exports.getTabForContentWindow = getTabForContentWindow;
 
+// only sdk/selection.js is relying on shims
+function getTabForContentWindowNoShim(window) {
+  function getTabContentWindowNoShim(tab) {
+    let browser = getBrowserForTab(tab);
+    return ShimWaiver.getProperty(browser, "contentWindow");
+  }
+  return getTabs().find(tab => getTabContentWindowNoShim(tab) === window.top) || null;
+}
+exports.getTabForContentWindowNoShim = getTabForContentWindowNoShim;
+
 function getTabURL(tab) {
   return String(getBrowserForTab(tab).currentURI.spec);
 }
 exports.getTabURL = getTabURL;
 
 function setTabURL(tab, url) {
   let browser = getBrowserForTab(tab);
   browser.loadURI(String(url));
--- a/addon-sdk/source/lib/toolkit/loader.js
+++ b/addon-sdk/source/lib/toolkit/loader.js
@@ -233,17 +233,18 @@ const Sandbox = iced(function Sandbox(op
     sandboxName: options.name,
     principal: 'principal' in options ? options.principal : systemPrincipal,
     wantXrays: 'wantXrays' in options ? options.wantXrays : true,
     wantGlobalProperties: 'wantGlobalProperties' in options ?
                           options.wantGlobalProperties : [],
     sandboxPrototype: 'prototype' in options ? options.prototype : {},
     invisibleToDebugger: 'invisibleToDebugger' in options ?
                          options.invisibleToDebugger : false,
-    metadata: 'metadata' in options ? options.metadata : {}
+    metadata: 'metadata' in options ? options.metadata : {},
+    waiveIntereposition: !!options.waiveIntereposition
   };
 
   if (options.metadata && options.metadata.addonID) {
     options.addonId = options.metadata.addonID;
   }
 
   let sandbox = Cu.Sandbox(options.principal, options);
 
@@ -786,17 +787,17 @@ Loader.unload = unload;
 // - `resolve` Optional module `id` resolution function. If given it will be
 //   used to resolve module URIs, by calling it with require term, requirer
 //   module object (that has `uri` property) and `baseURI` of the loader.
 //   If `resolve` does not returns `uri` string exception will be thrown by
 //   an associated `require` call.
 function Loader(options) {
   let {
     modules, globals, resolve, paths, rootURI, manifest, requireMap, isNative,
-    metadata, sharedGlobal, sharedGlobalBlacklist, checkCompatibility
+    metadata, sharedGlobal, sharedGlobalBlacklist, checkCompatibility, waiveIntereposition
   } = override({
     paths: {},
     modules: {},
     globals: {
       get console() {
         // Import Console.jsm from here to prevent loading it until someone uses it
         let { ConsoleAPI } = Cu.import("resource://gre/modules/Console.jsm");
         let console = new ConsoleAPI({
@@ -806,17 +807,18 @@ function Loader(options) {
         return this.console;
       }
     },
     checkCompatibility: false,
     resolve: options.isNative ?
       // Make the returned resolve function have the same signature
       (id, requirer) => Loader.nodeResolve(id, requirer, { rootURI: rootURI }) :
       Loader.resolve,
-    sharedGlobalBlacklist: ["sdk/indexed-db"]
+    sharedGlobalBlacklist: ["sdk/indexed-db"],
+    waiveIntereposition: false
   }, options);
 
   // Create overrides defaults, none at the moment
   if (typeof manifest != "object" || !manifest) {
     manifest = {};
   }
   if (typeof manifest.jetpack != "object" || !manifest.jetpack) {
     manifest.jetpack = {
--- a/addon-sdk/source/mapping.json
+++ b/addon-sdk/source/mapping.json
@@ -12,17 +12,17 @@
   "l10n/locale": "sdk/l10n/locale",
   "l10n/prefs": "sdk/l10n/prefs",
   "list": "sdk/util/list",
   "loader": "sdk/loader/loader",
   "namespace": "sdk/core/namespace",
   "preferences-service": "sdk/preferences/service",
   "promise": "sdk/core/promise",
   "system": "sdk/system",
-  "system/events": "sdk/system/events",
+  "system/events": "sdk/system/events-shimmed",
   "tabs/tab": "sdk/tabs/tab",
   "tabs/utils": "sdk/tabs/utils",
   "timer": "sdk/timers",
   "traits": "sdk/deprecated/traits",
   "unload": "sdk/system/unload",
   "window-utils": "sdk/deprecated/window-utils",
   "window/utils": "sdk/window/utils",
   "windows/dom": "sdk/windows/dom",
@@ -35,17 +35,17 @@
   "file": "sdk/io/file",
   "runtime": "sdk/system/runtime",
   "xpcom": "sdk/platform/xpcom",
   "querystring": "sdk/querystring",
   "text-streams": "sdk/io/text-streams",
   "app-strings": "sdk/deprecated/app-strings",
   "environment": "sdk/system/environment",
   "keyboard/utils": "sdk/keyboard/utils",
-  "dom/events": "sdk/dom/events",
+  "dom/events": "sdk/dom/events-shimmed",
   "utils/data": "sdk/io/data",
   "test/assert": "sdk/test/assert",
   "hidden-frame": "sdk/frame/hidden-frame",
   "collection": "sdk/util/collection",
   "array": "sdk/util/array",
   "clipboard": "sdk/clipboard",
   "context-menu": "sdk/context-menu",
   "hotkeys": "sdk/hotkeys",
--- a/addon-sdk/source/test/addons/layout-change/lib/test-cuddlefish-loader.js
+++ b/addon-sdk/source/test/addons/layout-change/lib/test-cuddlefish-loader.js
@@ -137,17 +137,17 @@ exports["test compatibility"] = function
 
   assert.equal(require("querystring"),
                require("sdk/querystring"), "sdk/querystring -> querystring");
 
   assert.equal(require("tabs/utils"),
                require("sdk/tabs/utils"), "sdk/tabs/utils -> tabs/utils");
 
   assert.equal(require("dom/events"),
-               require("sdk/dom/events"), "sdk/dom/events -> dom/events");
+               require("sdk/dom/events-shimmed"), "sdk/dom/events-shimmed -> dom/events");
 
   assert.equal(require("tabs/tab.js"),
                require("sdk/tabs/tab"), "sdk/tabs/tab -> tabs/tab.js");
 
   assert.equal(require("environment"),
                require("sdk/system/environment"), "sdk/system/environment -> environment");
 
   assert.equal(require("test/assert"),
--- a/addon-sdk/source/test/test-content-events.js
+++ b/addon-sdk/source/test/test-content-events.js
@@ -37,96 +37,16 @@ var use = use = value => () => value;
 
 var open = curry((url, window) => openTab(window, url));
 var 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", handler);
-  function handler ({type, target, timeStamp}) {
-    eventFilter(type, target, () => {
-      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();
-      off(events, "data", handler);
-      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", handler);
-  function handler ({type, target, timeStamp}) {
-    eventFilter(type, target, () => {
-      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,
-        "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();
-      off(events, "data", handler);
-      done();
-    });
-};
-
 exports["test dead object errors"] = function(assert, done) {
   let system = require("sdk/system/events");
   let loader = Loader(module);
   let { events } = loader.require("sdk/content/events");
 
   // The dead object error is properly reported on console but
   // doesn't raise any test's exception
   function onMessage({ subject }) {
--- a/addon-sdk/source/test/test-dev-panel.js
+++ b/addon-sdk/source/test/test-dev-panel.js
@@ -9,17 +9,17 @@ module.metadata = {
   }
 };
 
 const { Tool } = require("dev/toolbox");
 const { Panel } = require("dev/panel");
 const { Class } = require("sdk/core/heritage");
 const { openToolbox, closeToolbox, getCurrentPanel } = require("dev/utils");
 const { MessageChannel } = require("sdk/messaging");
-const { when } = require("sdk/dom/events");
+const { when } = require("sdk/dom/events-shimmed");
 const { viewFor } = require("sdk/view/core");
 const { createView } = require("dev/panel/view");
 
 const iconURI = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAKQWlDQ1BJQ0MgUHJvZmlsZQAASA2dlndUU9kWh8+9N73QEiIgJfQaegkg0jtIFQRRiUmAUAKGhCZ2RAVGFBEpVmRUwAFHhyJjRRQLg4Ji1wnyEFDGwVFEReXdjGsJ7601896a/cdZ39nnt9fZZ+9917oAUPyCBMJ0WAGANKFYFO7rwVwSE8vE9wIYEAEOWAHA4WZmBEf4RALU/L09mZmoSMaz9u4ugGS72yy/UCZz1v9/kSI3QyQGAApF1TY8fiYX5QKUU7PFGTL/BMr0lSkyhjEyFqEJoqwi48SvbPan5iu7yZiXJuShGlnOGbw0noy7UN6aJeGjjAShXJgl4GejfAdlvVRJmgDl9yjT0/icTAAwFJlfzOcmoWyJMkUUGe6J8gIACJTEObxyDov5OWieAHimZ+SKBIlJYqYR15hp5ejIZvrxs1P5YjErlMNN4Yh4TM/0tAyOMBeAr2+WRQElWW2ZaJHtrRzt7VnW5mj5v9nfHn5T/T3IevtV8Sbsz55BjJ5Z32zsrC+9FgD2JFqbHbO+lVUAtG0GQOXhrE/vIADyBQC03pzzHoZsXpLE4gwnC4vs7GxzAZ9rLivoN/ufgm/Kv4Y595nL7vtWO6YXP4EjSRUzZUXlpqemS0TMzAwOl89k/fcQ/+PAOWnNycMsnJ/AF/GF6FVR6JQJhIlou4U8gViQLmQKhH/V4X8YNicHGX6daxRodV8AfYU5ULhJB8hvPQBDIwMkbj96An3rWxAxCsi+vGitka9zjzJ6/uf6Hwtcim7hTEEiU+b2DI9kciWiLBmj34RswQISkAd0oAo0gS4wAixgDRyAM3AD3iAAhIBIEAOWAy5IAmlABLJBPtgACkEx2AF2g2pwANSBetAEToI2cAZcBFfADXALDIBHQAqGwUswAd6BaQiC8BAVokGqkBakD5lC1hAbWgh5Q0FQOBQDxUOJkBCSQPnQJqgYKoOqoUNQPfQjdBq6CF2D+qAH0CA0Bv0BfYQRmALTYQ3YALaA2bA7HAhHwsvgRHgVnAcXwNvhSrgWPg63whfhG/AALIVfwpMIQMgIA9FGWAgb8URCkFgkAREha5EipAKpRZqQDqQbuY1IkXHkAwaHoWGYGBbGGeOHWYzhYlZh1mJKMNWYY5hWTBfmNmYQM4H5gqVi1bGmWCesP3YJNhGbjS3EVmCPYFuwl7ED2GHsOxwOx8AZ4hxwfrgYXDJuNa4Etw/XjLuA68MN4SbxeLwq3hTvgg/Bc/BifCG+Cn8cfx7fjx/GvyeQCVoEa4IPIZYgJGwkVBAaCOcI/YQRwjRRgahPdCKGEHnEXGIpsY7YQbxJHCZOkxRJhiQXUiQpmbSBVElqIl0mPSa9IZPJOmRHchhZQF5PriSfIF8lD5I/UJQoJhRPShxFQtlOOUq5QHlAeUOlUg2obtRYqpi6nVpPvUR9Sn0vR5Mzl/OX48mtk6uRa5Xrl3slT5TXl3eXXy6fJ18hf0r+pvy4AlHBQMFTgaOwVqFG4bTCPYVJRZqilWKIYppiiWKD4jXFUSW8koGStxJPqUDpsNIlpSEaQtOledK4tE20Otpl2jAdRzek+9OT6cX0H+i99AllJWVb5SjlHOUa5bPKUgbCMGD4M1IZpYyTjLuMj/M05rnP48/bNq9pXv+8KZX5Km4qfJUilWaVAZWPqkxVb9UU1Z2qbapP1DBqJmphatlq+9Uuq43Pp893ns+dXzT/5PyH6rC6iXq4+mr1w+o96pMamhq+GhkaVRqXNMY1GZpumsma5ZrnNMe0aFoLtQRa5VrntV4wlZnuzFRmJbOLOaGtru2nLdE+pN2rPa1jqLNYZ6NOs84TXZIuWzdBt1y3U3dCT0svWC9fr1HvoT5Rn62fpL9Hv1t/ysDQINpgi0GbwaihiqG/YZ5ho+FjI6qRq9Eqo1qjO8Y4Y7ZxivE+41smsImdSZJJjclNU9jU3lRgus+0zwxr5mgmNKs1u8eisNxZWaxG1qA5wzzIfKN5m/krCz2LWIudFt0WXyztLFMt6ywfWSlZBVhttOqw+sPaxJprXWN9x4Zq42Ozzqbd5rWtqS3fdr/tfTuaXbDdFrtOu8/2DvYi+yb7MQc9h3iHvQ732HR2KLuEfdUR6+jhuM7xjOMHJ3snsdNJp9+dWc4pzg3OowsMF/AX1C0YctFx4bgccpEuZC6MX3hwodRV25XjWuv6zE3Xjed2xG3E3dg92f24+ysPSw+RR4vHlKeT5xrPC16Il69XkVevt5L3Yu9q76c+Oj6JPo0+E752vqt9L/hh/QL9dvrd89fw5/rX+08EOASsCegKpARGBFYHPgsyCRIFdQTDwQHBu4IfL9JfJFzUFgJC/EN2hTwJNQxdFfpzGC4sNKwm7Hm4VXh+eHcELWJFREPEu0iPyNLIR4uNFksWd0bJR8VF1UdNRXtFl0VLl1gsWbPkRoxajCCmPRYfGxV7JHZyqffS3UuH4+ziCuPuLjNclrPs2nK15anLz66QX8FZcSoeGx8d3xD/iRPCqeVMrvRfuXflBNeTu4f7kufGK+eN8V34ZfyRBJeEsoTRRJfEXYljSa5JFUnjAk9BteB1sl/ygeSplJCUoykzqdGpzWmEtPi000IlYYqwK10zPSe9L8M0ozBDuspp1e5VE6JA0ZFMKHNZZruYjv5M9UiMJJslg1kLs2qy3mdHZZ/KUcwR5vTkmuRuyx3J88n7fjVmNXd1Z752/ob8wTXuaw6thdauXNu5Tnddwbrh9b7rj20gbUjZ8MtGy41lG99uit7UUaBRsL5gaLPv5sZCuUJR4b0tzlsObMVsFWzt3WazrWrblyJe0fViy+KK4k8l3JLr31l9V/ndzPaE7b2l9qX7d+B2CHfc3em681iZYlle2dCu4F2t5czyovK3u1fsvlZhW3FgD2mPZI+0MqiyvUqvakfVp+qk6oEaj5rmvep7t+2d2sfb17/fbX/TAY0DxQc+HhQcvH/I91BrrUFtxWHc4azDz+ui6rq/Z39ff0TtSPGRz0eFR6XHwo911TvU1zeoN5Q2wo2SxrHjccdv/eD1Q3sTq+lQM6O5+AQ4ITnx4sf4H++eDDzZeYp9qukn/Z/2ttBailqh1tzWibakNml7THvf6YDTnR3OHS0/m/989Iz2mZqzymdLz5HOFZybOZ93fvJCxoXxi4kXhzpXdD66tOTSna6wrt7LgZevXvG5cqnbvfv8VZerZ645XTt9nX297Yb9jdYeu56WX+x+aem172296XCz/ZbjrY6+BX3n+l37L972un3ljv+dGwOLBvruLr57/17cPel93v3RB6kPXj/Mejj9aP1j7OOiJwpPKp6qP6391fjXZqm99Oyg12DPs4hnj4a4Qy//lfmvT8MFz6nPK0a0RupHrUfPjPmM3Xqx9MXwy4yX0+OFvyn+tveV0auffnf7vWdiycTwa9HrmT9K3qi+OfrW9m3nZOjk03dp76anit6rvj/2gf2h+2P0x5Hp7E/4T5WfjT93fAn88ngmbWbm3/eE8/syOll+AAAACXBIWXMAAAsTAAALEwEAmpwYAAADqmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS40LjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIKICAgICAgICAgICAgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiCiAgICAgICAgICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyI+CiAgICAgICAgIDx4bXBNTTpEb2N1bWVudElEPnhtcC5kaWQ6M0ExNEY4NjZBNkU1MTFFMTlGMkFGQ0QyNTUyN0VDRjY8L3htcE1NOkRvY3VtZW50SUQ+CiAgICAgICAgIDx4bXBNTTpEZXJpdmVkRnJvbSByZGY6cGFyc2VUeXBlPSJSZXNvdXJjZSI+CiAgICAgICAgICAgIDxzdFJlZjppbnN0YW5jZUlEPnhtcC5paWQ6M0ExNEY4NjNBNkU1MTFFMTlGMkFGQ0QyNTUyN0VDRjY8L3N0UmVmOmluc3RhbmNlSUQ+CiAgICAgICAgICAgIDxzdFJlZjpkb2N1bWVudElEPnhtcC5kaWQ6M0ExNEY4NjRBNkU1MTFFMTlGMkFGQ0QyNTUyN0VDRjY8L3N0UmVmOmRvY3VtZW50SUQ+CiAgICAgICAgIDwveG1wTU06RGVyaXZlZEZyb20+CiAgICAgICAgIDx4bXBNTTpJbnN0YW5jZUlEPnhtcC5paWQ6M0ExNEY4NjVBNkU1MTFFMTlGMkFGQ0QyNTUyN0VDRjY8L3htcE1NOkluc3RhbmNlSUQ+CiAgICAgICAgIDx4bXA6Q3JlYXRvclRvb2w+QWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2g8L3htcDpDcmVhdG9yVG9vbD4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+ChetDDYAAACaSURBVEgNY2AYboAR3UNnzpx5DxQTQBP/YGJiIogmBuYSq54Ji2Z0S0BKsInBtGKTwxDDZhHMAKrSdLOIqq7GZxjj+fPnBf7+/bseqMgBn0IK5A4wMzMHMtHYEpD7HEB2gOLIAcSjMXCgW2IYtYjsqBwNutGgg4fAaGKABwWpDLoG3QFSXUeG+gNMoEoJqJGWloErPjIcR54WALqPHeiJgl15AAAAAElFTkSuQmCC";
 const makeHTML = fn =>
   "data:text/html;charset=utf-8,<script>(" + fn + ")();</script>";
 
 
--- a/addon-sdk/source/test/util.js
+++ b/addon-sdk/source/test/util.js
@@ -7,26 +7,30 @@ const {Cc, Ci} = require("chrome");
 const {getMostRecentBrowserWindow, open} = require("sdk/window/utils");
 const tabUtils = require("sdk/tabs/utils");
 const {when} = require("sdk/dom/events");
 
 
 var observerService = Cc["@mozilla.org/observer-service;1"]
                       .getService(Ci.nsIObserverService);
 
+const { ShimWaiver } = Cu.import("resource://gre/modules/ShimWaiver.jsm");
+const addObserver = ShimWaiver.getProperty(observerService, "addObserver");
+const removeObserver = ShimWaiver.getProperty(observerService, "removeObserver");
+
 const getActiveTab = (window=getMostRecentBrowserWindow()) =>
   tabUtils.getActiveTab(window)
 
 const openWindow = () => {
   const window = open();
   return new Promise((resolve) => {
-    observerService.addObserver({
+    addObserver({
       observe(subject, topic) {
         if (subject === window) {
-          observerService.removeObserver(this, topic);
+          removeObserver(this, topic);
           resolve(subject);
         }
       }
     }, "browser-delayed-startup-finished", false);
   });
 };
 exports.openWindow = openWindow;
 
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -1020,17 +1020,16 @@ pref("apz.pan_repaint_interval", 16);
 
 // APZ physics settings, tuned by UX designers
 pref("apz.fling_curve_function_x1", "0.41");
 pref("apz.fling_curve_function_y1", "0.0");
 pref("apz.fling_curve_function_x2", "0.80");
 pref("apz.fling_curve_function_y2", "1.0");
 pref("apz.fling_curve_threshold_inches_per_ms", "0.01");
 pref("apz.fling_friction", "0.0019");
-pref("apz.fling_snap_friction", "0.015");
 pref("apz.max_velocity_inches_per_ms", "0.07");
 pref("apz.touch_start_tolerance", "0.1");
 
 // Tweak default displayport values to reduce the risk of running out of
 // memory when zooming in
 pref("apz.x_skate_size_multiplier", "1.25");
 pref("apz.y_skate_size_multiplier", "1.5");
 pref("apz.x_stationary_size_multiplier", "1.5");
--- a/browser/base/content/aboutDialog.css
+++ b/browser/base/content/aboutDialog.css
@@ -26,16 +26,22 @@
   font-weight: bold;
   margin-top: 10px;
   -moz-margin-start: 0;
   -moz-user-select: text;
   -moz-user-focus: normal;
   cursor: text;
 }
 
+#version:-moz-locale-dir(rtl) {
+  direction: ltr;
+  text-align: right;
+  margin-right: 0;
+}
+
 #distribution,
 #distributionId {
   display: none;
   margin-top: 0;
   margin-bottom: 0;
 }
 
 .text-blurb {
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -264,17 +264,17 @@ tags = mcb
 [browser_bug832435.js]
 [browser_bug839103.js]
 [browser_bug880101.js]
 [browser_bug882977.js]
 [browser_bug902156.js]
 tags = mcb
 [browser_bug906190.js]
 tags = mcb
-skip-if = buildapp == "mulet" || e10s || os == "linux" # Bug 1093642 - test manipulates content and relies on content focus, Bug 1212520 - Re-enable on Linux
+skip-if = buildapp == "mulet" || e10s # Bug 1093642 - test manipulates content and relies on content focus
 [browser_mixedContentFromOnunload.js]
 tags = mcb
 [browser_mixedContentFramesOnHttp.js]
 tags = mcb
 [browser_bug970746.js]
 [browser_bug1015721.js]
 skip-if = os == 'win' || e10s # Bug 1159268 - Need a content-process safe version of synthesizeWheel
 [browser_bug1064280_changeUrlInPinnedTab.js]
--- a/browser/base/content/test/general/browser_bug906190.js
+++ b/browser/base/content/test/general/browser_bug906190.js
@@ -1,558 +1,231 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
 /*
- * Description of the Tests for
- *  - Bug 906190 - Persist "disable protection" option for Mixed Content Blocker in child tabs
- *
- * 1. Open a page from the same domain in a child tab
- *    - Load a html page which has mixed content
- *    - Doorhanger to disable protection appears - we disable it
- *    - Load a subpage from the same origin in a new tab simulating a click
- *    - Doorhanger should >> NOT << appear anymore!
- *
- * 2. Open a page from a different domain in a child tab
- *    - Load a html page which has mixed content
- *    - Doorhanger to disable protection appears - we disable it
- *    - Load a new page from a different origin in a new tab simulating a click
- *    - Doorhanger >> SHOULD << appear again!
- *
- * 3. [meta-refresh: same origin] Open a page from the same domain in a child tab
- *    - Load a html page which has mixed content
- *    - Doorhanger to disable protection appears - we disable it
- *    - Load a new page from the same origin in a new tab simulating a click
- *    - Redirect that page to another page from the same origin using meta-refresh
- *    - Doorhanger should >> NOT << appear again!
- *
- * 4. [meta-refresh: different origin] Open a page from a different domain in a child tab
- *    - Load a html page which has mixed content
- *    - Doorhanger to disable protection appears - we disable it
- *    - Load a new page from the same origin in a new tab simulating a click
- *    - Redirect that page to another page from a different origin using meta-refresh
- *    - Doorhanger >> SHOULD << appear again!
- *
- * 5. [302 redirect: same origin] Open a page from the same domain in a child tab
- *    - Load a html page which has mixed content
- *    - Doorhanger to disable protection appears - we disable it
- *    - Load a new page from the same origin in a new tab simulating a click
- *    - Redirect that page to another page from the same origin using 302 redirect
- *    - Doorhanger >> APPEARS << , but should >> NOT << appear again!
- *    - FOLLOW UP BUG 914860!
- *
- * 6. [302 redirect: different origin] Open a page from the same domain in a child tab
- *    - Load a html page which has mixed content
- *    - Doorhanger to disable protection appears - we disable it
- *    - Load a new page from the same origin in a new tab simulating a click
- *    - Redirect that page to another page from a different origin using 302 redirect
- *    - Doorhanger >> SHOULD << appear again!
- *
- * Note, for all tests we set gHttpTestRoot to use 'https' and we test both,
- *   - |CTRL-CLICK|, as well as
- *   - |RIGHT-CLICK->OPEN LINK IN TAB|.
+ * Tests the persistence of the "disable protection" option for Mixed Content
+ * Blocker in child tabs (bug 906190).
  */
 
-const PREF_ACTIVE = "security.mixed_content.block_active_content";
+requestLongerTimeout(2);
 
 // We use the different urls for testing same origin checks before allowing
 // mixed content on child tabs.
 const gHttpTestRoot1 = "https://test1.example.com/browser/browser/base/content/test/general/";
 const gHttpTestRoot2 = "https://test2.example.com/browser/browser/base/content/test/general/";
 
-var origBlockActive;
-var gTestWin = null;
-var mainTab = null;
-var curClickHandler = null;
-var curContextMenu = null;
-var curTestFunc = null;
-var curTestName = null;
-var curChildTabLink = null;
+/**
+ * For all tests, we load the pages over HTTPS and test both:
+ *   - |CTRL+CLICK|
+ *   - |RIGHT CLICK -> OPEN LINK IN TAB|
+ */
+function* doTest(parentTabSpec, childTabSpec, testTaskFn, waitForMetaRefresh) {
+  yield BrowserTestUtils.withNewTab({
+    gBrowser,
+    url: parentTabSpec,
+  }, function* (browser) {
+    // As a sanity check, test that active content has been blocked as expected.
+    yield assertMixedContentBlockingState(gBrowser, {
+      activeLoaded: false, activeBlocked: true, passiveLoaded: false,
+    });
 
-//------------------------ Helper Functions ---------------------
-
-registerCleanupFunction(function() {
-  // Set preferences back to their original values
-  Services.prefs.setBoolPref(PREF_ACTIVE, origBlockActive);
-});
-requestLongerTimeout(2);
+    // Disable the Mixed Content Blocker for the page, which reloads it.
+    let promiseReloaded = BrowserTestUtils.browserLoaded(browser);
+    gIdentityHandler.disableMixedContentProtection();
+    yield promiseReloaded;
 
-/*
- * Whenever we disable the Mixed Content Blocker of the page
- * we have to make sure that our condition is properly loaded.
- */
-function waitForCondition(condition, nextTest, errorMsg) {
-  var tries = 0;
-  var interval = setInterval(function() {
-    if (tries >= 30) {
-      ok(false, errorMsg);
-      moveOn();
+    // Wait for the script in the page to update the contents of the test div.
+    let testDiv = content.document.getElementById('mctestdiv');
+    yield promiseWaitForCondition(
+      () => testDiv.innerHTML == "Mixed Content Blocker disabled");
+
+    // Add the link for the child tab to the page.
+    let mainDiv = content.document.createElement("div");
+    mainDiv.innerHTML =
+      '<p><a id="linkToOpenInNewTab" href="' + childTabSpec + '">Link</a></p>';
+    content.document.body.appendChild(mainDiv);
+
+    // Execute the test in the child tabs with the two methods to open it.
+    for (let openFn of [simulateCtrlClick, simulateContextMenuOpenInTab]) {
+      let promiseTabLoaded = waitForSomeTabToLoad();
+      openFn(browser);
+      yield promiseTabLoaded;
+      gBrowser.selectTabAtIndex(2);
+
+      if (waitForMetaRefresh) {
+        yield waitForSomeTabToLoad();
+      }
+
+      yield testTaskFn();
+
+      gBrowser.removeCurrentTab();
     }
-    if (condition()) {
-      moveOn();
-    }
-    tries++;
-  }, 100);
-  var moveOn = function() {
-    clearInterval(interval); nextTest();
-  };
+  });
 }
 
-// The purpose of this function is to simulate |CTRL+CLICK|.
-// The clickHandler intercepts simulated user clicks and performs
-// the |contentAreaClick| which dispatches to handleLinkClick.
-var clickHandler = function (aEvent, aFunc) {
-  gTestWin.gBrowser.removeEventListener("click", curClickHandler, true);
-  gTestWin.contentAreaClick(aEvent, true);
-  waitForSomeTabToLoad(aFunc);
-  aEvent.preventDefault();
-  aEvent.stopPropagation();
+function simulateCtrlClick(browser) {
+  BrowserTestUtils.synthesizeMouseAtCenter("#linkToOpenInNewTab",
+                                           { ctrlKey: true, metaKey: true },
+                                           browser);
 }
 
-// The purpose of this function is to simulate |RIGHT-CLICK|->|OPEN LINK IN TAB|
-// Once the contextmenu opens, this functions selects 'open link in tab'
-// from the contextmenu which dispatches to the function openLinkInTab.
-var contextMenuOpenHandler = function(aEvent, aFunc) {
-  gTestWin.document.removeEventListener("popupshown", curContextMenu, false);
-  waitForSomeTabToLoad(aFunc);
-  var openLinkInTabCommand = gTestWin.document.getElementById("context-openlinkintab");
-  openLinkInTabCommand.doCommand();
-  aEvent.target.hidePopup();
-};
-
-function setUpTest(aTestName, aIDForNextTest, aFuncForNextTest, aChildTabLink) {
-  curTestName = aTestName;
-  curTestFunc = aFuncForNextTest;
-  curChildTabLink = aChildTabLink;
-
-  mainTab = gTestWin.gBrowser.selectedTab;
-  // get the link for the next test from the main page
-  let target = gTestWin.content.document.getElementById(aIDForNextTest).href;
-  gTestWin.gBrowser.addTab(target);
-  gTestWin.gBrowser.selectTabAtIndex(1);
-  waitForSomeTabToLoad(checkPopUpNotification);
+function simulateContextMenuOpenInTab(browser) {
+  BrowserTestUtils.waitForEvent(document, "popupshown", false, event => {
+    // These are operations that must be executed synchronously with the event.
+    document.getElementById("context-openlinkintab").doCommand();
+    event.target.hidePopup();
+    return true;
+  });
+  BrowserTestUtils.synthesizeMouseAtCenter("#linkToOpenInNewTab",
+                                           { type: "contextmenu", button: 2 },
+                                           browser);
 }
 
 // Waits for a load event somewhere in the browser but ignore events coming
 // from <xul:browser>s without a tab assigned. That are most likely browsers
 // that preload the new tab page.
-function waitForSomeTabToLoad(callback) {
-  gTestWin.gBrowser.addEventListener("load", function onLoad(event) {
-    let tab = gTestWin.gBrowser._getTabForContentWindow(event.target.defaultView.top);
-    if (tab) {
-      gTestWin.gBrowser.removeEventListener("load", onLoad, true);
-      callback();
-    }
-  }, true);
-}
-
-function checkPopUpNotification() {
-  waitForSomeTabToLoad(reloadedTabAfterDisablingMCB);
-
-  assertMixedContentBlockingState(gTestWin.gBrowser, {activeLoaded: false, activeBlocked: true, passiveLoaded: false});
-
-  // Disable Mixed Content Protection for the page (which reloads the page)
-  let {gIdentityHandler} = gTestWin.gBrowser.ownerGlobal;
-  gIdentityHandler.disableMixedContentProtection();
-}
-
-function reloadedTabAfterDisablingMCB() {
-  var expected = "Mixed Content Blocker disabled";
-  waitForCondition(
-    () => gTestWin.content.document.getElementById('mctestdiv').innerHTML == expected,
-    makeSureMCBisDisabled, "Error: Waited too long for mixed script to run in " + curTestName + "!");
-}
-
-function makeSureMCBisDisabled() {
-  var actual = gTestWin.content.document.getElementById('mctestdiv').innerHTML;
-  is(actual, "Mixed Content Blocker disabled", "OK: Made sure MCB is disabled in " + curTestName + "!");
-
-  // inject the provided link into the page, so we can test persistence of MCB
-  let doc = gTestWin.content.document;
-  let mainDiv = gTestWin.content.document.createElement("div");
-  mainDiv.innerHTML =
-    '<p><a id="' + curTestName + '" href="' + curChildTabLink + '">' +
-    curTestName + '</a></p>';
-  doc.body.appendChild(mainDiv);
-
-  curTestFunc();
-}
-
-//------------------------ Test 1 ------------------------------
-
-function test1() {
-  curClickHandler = function (e) { clickHandler(e, test1A) };
-  gTestWin.gBrowser.addEventListener("click", curClickHandler , true);
-
-  // simulating |CTRL-CLICK|
-  let targetElt = gTestWin.content.document.getElementById("Test1");
-  EventUtils.synthesizeMouseAtCenter(targetElt, { button: 1 }, gTestWin.content);
-}
-
-function test1A() {
-  gTestWin.gBrowser.selectTabAtIndex(2);
-
-  // The Doorhanger should appear but isMixedContentBlocked should be >> NOT << true,
-  // because our decision of disabling the mixed content blocker is persistent across tabs.
-  assertMixedContentBlockingState(gTestWin.gBrowser, {activeLoaded: true, activeBlocked: false, passiveLoaded: false});
-
-  var actual = gTestWin.content.document.getElementById('mctestdiv').innerHTML;
-  is(actual, "Mixed Content Blocker disabled", "OK: Executed mixed script in Test 1A");
-
-  gTestWin.gBrowser.removeCurrentTab();
-  test1B();
-}
-
-function test1B() {
-  curContextMenu = function (e) { contextMenuOpenHandler(e, test1C) };
-  gTestWin.document.addEventListener("popupshown", curContextMenu, false);
-
-  // simulating |RIGHT-CLICK -> OPEN LINK IN TAB|
-  let targetElt = gTestWin.content.document.getElementById("Test1");
-  // button 2 is a right click, hence the context menu pops up
-  EventUtils.synthesizeMouseAtCenter(targetElt, { type : "contextmenu", button : 2 } , gTestWin.content);
-}
-
-function test1C() {
-  gTestWin.gBrowser.selectTabAtIndex(2);
-
-  // The Doorhanger should appear but isMixedContentBlocked should be >> NOT << true,
-  // because our decision of disabling the mixed content blocker is persistent across tabs.
-  assertMixedContentBlockingState(gTestWin.gBrowser, {activeLoaded: true, activeBlocked: false, passiveLoaded: false});
-
-  var actual = gTestWin.content.document.getElementById('mctestdiv').innerHTML;
-  is(actual, "Mixed Content Blocker disabled", "OK: Executed mixed script in Test 1C");
-
-  // remove tabs
-  gTestWin.gBrowser.removeTab(gTestWin.gBrowser.tabs[2], {animate: false});
-  gTestWin.gBrowser.removeTab(gTestWin.gBrowser.tabs[1], {animate: false});
-  gTestWin.gBrowser.selectTabAtIndex(0);
-
-  var childTabLink = gHttpTestRoot2 + "file_bug906190_2.html";
-  setUpTest("Test2", "linkForTest2", test2, childTabLink);
-}
-
-//------------------------ Test 2 ------------------------------
-
-function test2() {
-  curClickHandler = function (e) { clickHandler(e, test2A) };
-  gTestWin.gBrowser.addEventListener("click", curClickHandler, true);
-
-  // simulating |CTRL-CLICK|
-  let targetElt = gTestWin.content.document.getElementById("Test2");
-  EventUtils.synthesizeMouseAtCenter(targetElt, { button: 1 }, gTestWin.content);
-}
-
-function test2A() {
-  gTestWin.gBrowser.selectTabAtIndex(2);
-
-  // The Doorhanger should appear and isMixedContentBlocked should be >> TRUE <<,
-  // because our decision of disabling the mixed content blocker should only persist if pages are from the same domain.
-  assertMixedContentBlockingState(gTestWin.gBrowser, {activeLoaded: false, activeBlocked: true, passiveLoaded: false});
-
-  var actual = gTestWin.content.document.getElementById('mctestdiv').innerHTML;
-  is(actual, "Mixed Content Blocker enabled", "OK: Blocked mixed script in Test 2A");
-
-  gTestWin.gBrowser.removeCurrentTab();
-  test2B();
-}
-
-function test2B() {
-  curContextMenu = function (e) { contextMenuOpenHandler(e, test2C) };
-  gTestWin.document.addEventListener("popupshown", curContextMenu, false);
-
-  // simulating |RIGHT-CLICK -> OPEN LINK IN TAB|
-  let targetElt = gTestWin.content.document.getElementById("Test2");
-  // button 2 is a right click, hence the context menu pops up
-  EventUtils.synthesizeMouseAtCenter(targetElt, { type : "contextmenu", button : 2 } , gTestWin.content);
-}
-
-function test2C() {
-  gTestWin.gBrowser.selectTabAtIndex(2);
-
-  // The Doorhanger should appear and isMixedContentBlocked should be >> TRUE <<,
-  // because our decision of disabling the mixed content blocker should only persist if pages are from the same domain.
-  assertMixedContentBlockingState(gTestWin.gBrowser, {activeLoaded: false, activeBlocked: true, passiveLoaded: false});
-
-  var actual = gTestWin.content.document.getElementById('mctestdiv').innerHTML;
-  is(actual, "Mixed Content Blocker enabled", "OK: Blocked mixed script in Test 2C");
-
-  // remove tabs
-  gTestWin.gBrowser.removeTab(gTestWin.gBrowser.tabs[2], {animate: false});
-  gTestWin.gBrowser.removeTab(gTestWin.gBrowser.tabs[1], {animate: false});
-  gTestWin.gBrowser.selectTabAtIndex(0);
-
-  // file_bug906190_3_4.html redirects to page test1.example.com/* using meta-refresh
-  var childTabLink = gHttpTestRoot1 + "file_bug906190_3_4.html";
-  setUpTest("Test3", "linkForTest3", test3, childTabLink);
-}
-
-//------------------------ Test 3 ------------------------------
-
-function test3() {
-  curClickHandler = function (e) { clickHandler(e, test3A) };
-  gTestWin.gBrowser.addEventListener("click", curClickHandler, true);
-  // simulating |CTRL-CLICK|
-  let targetElt = gTestWin.content.document.getElementById("Test3");
-  EventUtils.synthesizeMouseAtCenter(targetElt, { button: 1 }, gTestWin.content);
-}
-
-function test3A() {
-  // we need this indirection because the page is reloaded caused by meta-refresh
-  waitForSomeTabToLoad(test3B);
-}
-
-function test3B() {
-  gTestWin.gBrowser.selectTabAtIndex(2);
-
-  // The Doorhanger should appear but isMixedContentBlocked should be >> NOT << true!
-  assertMixedContentBlockingState(gTestWin.gBrowser, {activeLoaded: true, activeBlocked: false, passiveLoaded: false});
-
-  var actual = gTestWin.content.document.getElementById('mctestdiv').innerHTML;
-  is(actual, "Mixed Content Blocker disabled", "OK: Executed mixed script in Test 3B");
-
-  // remove tabs
-  gTestWin.gBrowser.removeCurrentTab();
-  test3C();
-}
-
-function test3C() {
-  curContextMenu = function (e) { contextMenuOpenHandler(e, test3D) };
-  gTestWin.document.addEventListener("popupshown", curContextMenu, false);
-
-  // simulating |RIGHT-CLICK -> OPEN LINK IN TAB|
-  let targetElt = gTestWin.content.document.getElementById("Test3");
-  EventUtils.synthesizeMouseAtCenter(targetElt, { type : "contextmenu", button : 2 } , gTestWin.content);
-}
-
-function test3D() {
-  // we need this indirection because the page is reloaded caused by meta-refresh
-  waitForSomeTabToLoad(test3E);
-}
-
-function test3E() {
-  gTestWin.gBrowser.selectTabAtIndex(2);
-
-  // The Doorhanger should appear but isMixedContentBlocked should be >> NOT << true!
-  assertMixedContentBlockingState(gTestWin.gBrowser, {activeLoaded: true, activeBlocked: false, passiveLoaded: false});
-
-  var actual = gTestWin.content.document.getElementById('mctestdiv').innerHTML;
-  is(actual, "Mixed Content Blocker disabled", "OK: Executed mixed script in Test 3E");
-
-  // remove tabs
-  gTestWin.gBrowser.removeTab(gTestWin.gBrowser.tabs[2], {animate: false});
-  gTestWin.gBrowser.removeTab(gTestWin.gBrowser.tabs[1], {animate: false});
-  gTestWin.gBrowser.selectTabAtIndex(0);
-
-  var childTabLink = gHttpTestRoot1 + "file_bug906190_3_4.html";
-  setUpTest("Test4", "linkForTest4", test4, childTabLink);
+function waitForSomeTabToLoad() {
+  return new Promise(resolve => {
+    gBrowser.addEventListener("load", function onLoad(event) {
+      let tab = gBrowser._getTabForContentWindow(event.target.defaultView.top);
+      if (tab) {
+        gBrowser.removeEventListener("load", onLoad, true);
+        resolve();
+      }
+    }, true);
+  });
 }
 
-//------------------------ Test 4 ------------------------------
-
-function test4() {
-  curClickHandler = function (e) { clickHandler(e, test4A) };
-  gTestWin.gBrowser.addEventListener("click", curClickHandler, true);
-
-  // simulating |CTRL-CLICK|
-  let targetElt = gTestWin.content.document.getElementById("Test4");
-  EventUtils.synthesizeMouseAtCenter(targetElt, { button: 1 }, gTestWin.content);
-}
-
-function test4A() {
-  // we need this indirection because the page is reloaded caused by meta-refresh
-  waitForSomeTabToLoad(test4B);
-}
-
-function test4B() {
-  gTestWin.gBrowser.selectTabAtIndex(2);
-
-  // The Doorhanger should appear and isMixedContentBlocked should be >> TRUE <<
-  assertMixedContentBlockingState(gTestWin.gBrowser, {activeLoaded: false, activeBlocked: true, passiveLoaded: false});
-
-  var actual = gTestWin.content.document.getElementById('mctestdiv').innerHTML;
-  is(actual, "Mixed Content Blocker enabled", "OK: Blocked mixed script in Test 4B");
+/**
+ * Ensure the Mixed Content Blocker is enabled.
+ */
+add_task(function* test_initialize() {
+  yield new Promise(resolve => SpecialPowers.pushPrefEnv({
+    "set": [["security.mixed_content.block_active_content", true]],
+  }, resolve));
+});
 
-  // remove tabs
-  gTestWin.gBrowser.removeCurrentTab();
-  test4C();
-}
-
-function test4C() {
-  curContextMenu = function (e) { contextMenuOpenHandler(e, test4D) };
-  gTestWin.document.addEventListener("popupshown", curContextMenu, false);
+/**
+ * 1. - Load a html page which has mixed content
+ *    - Doorhanger to disable protection appears - we disable it
+ *    - Load a subpage from the same origin in a new tab simulating a click
+ *    - Doorhanger should >> NOT << appear anymore!
+ */
+add_task(function* test_same_origin() {
+  yield doTest(gHttpTestRoot1 + "file_bug906190_1.html",
+               gHttpTestRoot1 + "file_bug906190_2.html", function* () {
+    // The doorhanger should appear but activeBlocked should be >> NOT << true,
+    // because our decision of disabling the mixed content blocker is persistent
+    // across tabs.
+    yield assertMixedContentBlockingState(gBrowser, {
+      activeLoaded: true, activeBlocked: false, passiveLoaded: false,
+    });
 
-  // simulating |RIGHT-CLICK -> OPEN LINK IN TAB|
-  let targetElt = gTestWin.content.document.getElementById("Test4");
-  EventUtils.synthesizeMouseAtCenter(targetElt, { type : "contextmenu", button : 2 } , gTestWin.content);
-}
-
-function test4D() {
-  // we need this indirection because the page is reloaded caused by meta-refresh
-  waitForSomeTabToLoad(test4E);
-}
-
-function test4E() {
-  gTestWin.gBrowser.selectTabAtIndex(2);
-
-  // The Doorhanger should appear and isMixedContentBlocked should be >> TRUE <<
-  assertMixedContentBlockingState(gTestWin.gBrowser, {activeLoaded: false, activeBlocked: true, passiveLoaded: false});
+    is(content.document.getElementById('mctestdiv').innerHTML,
+       "Mixed Content Blocker disabled", "OK: Executed mixed script");
+  });
+});
 
-  var actual = gTestWin.content.document.getElementById('mctestdiv').innerHTML;
-  is(actual, "Mixed Content Blocker enabled", "OK: Blocked mixed script in Test 4E");
-
-  // remove tabs
-  gTestWin.gBrowser.removeTab(gTestWin.gBrowser.tabs[2], {animate: false});
-  gTestWin.gBrowser.removeTab(gTestWin.gBrowser.tabs[1], {animate: false});
-  gTestWin.gBrowser.selectTabAtIndex(0);
-
-  // the sjs files returns a 302 redirect- note, same origins
-  var childTabLink = gHttpTestRoot1 + "file_bug906190.sjs";
-  setUpTest("Test5", "linkForTest5", test5, childTabLink);
-}
-
-//------------------------ Test 5 ------------------------------
-
-function test5() {
-  curClickHandler = function (e) { clickHandler(e, test5A) };
-  gTestWin.gBrowser.addEventListener("click", curClickHandler, true);
-
-  // simulating |CTRL-CLICK|
-  let targetElt = gTestWin.content.document.getElementById("Test5");
-  EventUtils.synthesizeMouseAtCenter(targetElt, { button: 1 }, gTestWin.content);
-}
-
-function test5A() {
-  gTestWin.gBrowser.selectTabAtIndex(2);
+/**
+ * 2. - Load a html page which has mixed content
+ *    - Doorhanger to disable protection appears - we disable it
+ *    - Load a new page from a different origin in a new tab simulating a click
+ *    - Doorhanger >> SHOULD << appear again!
+ */
+add_task(function* test_different_origin() {
+  yield doTest(gHttpTestRoot1 + "file_bug906190_2.html",
+               gHttpTestRoot2 + "file_bug906190_2.html", function* () {
+    // The doorhanger should appear and activeBlocked should be >> TRUE <<,
+    // because our decision of disabling the mixed content blocker should only
+    // persist if pages are from the same domain.
+    yield assertMixedContentBlockingState(gBrowser, {
+      activeLoaded: false, activeBlocked: true, passiveLoaded: false,
+    });
 
-  // The Doorhanger should appear but isMixedContentBlocked should be >> NOT << true
-  // Currently it is >> TRUE << - see follow up bug 914860
-  let {gIdentityHandler} = gTestWin.gBrowser.ownerGlobal;
-  todo(!gIdentityHandler._identityBox.classList.contains("mixedActiveBlocked"), "OK: Mixed Content is NOT being blocked in Test 5A!");
-
-  var actual = gTestWin.content.document.getElementById('mctestdiv').innerHTML;
-  todo_is(actual, "Mixed Content Blocker disabled", "OK: Executed mixed script in Test 5A!");
-
-  // remove tabs
-  gTestWin.gBrowser.removeCurrentTab();
-  test5B();
-}
+    is(content.document.getElementById('mctestdiv').innerHTML,
+       "Mixed Content Blocker enabled", "OK: Blocked mixed script");
+  });
+});
 
-function test5B() {
-  curContextMenu = function (e) { contextMenuOpenHandler(e, test5C) };
-  gTestWin.document.addEventListener("popupshown", curContextMenu, false);
-
-  // simulating |RIGHT-CLICK -> OPEN LINK IN TAB|
-  let targetElt = gTestWin.content.document.getElementById("Test5");
-  EventUtils.synthesizeMouseAtCenter(targetElt, { type : "contextmenu", button : 2 } , gTestWin.content);
-}
-
-function test5C() {
-  // move the tab again
-  gTestWin.gBrowser.selectTabAtIndex(2);
-
-  let {gIdentityHandler} = gTestWin.gBrowser.ownerGlobal;
+/**
+ * 3. - Load a html page which has mixed content
+ *    - Doorhanger to disable protection appears - we disable it
+ *    - Load a new page from the same origin in a new tab simulating a click
+ *    - Redirect to another page from the same origin using meta-refresh
+ *    - Doorhanger should >> NOT << appear again!
+ */
+add_task(function* test_same_origin_metarefresh_same_origin() {
+  // file_bug906190_3_4.html redirects to page test1.example.com/* using meta-refresh
+  yield doTest(gHttpTestRoot1 + "file_bug906190_1.html",
+               gHttpTestRoot1 + "file_bug906190_3_4.html", function* () {
+    // The doorhanger should appear but activeBlocked should be >> NOT << true!
+    yield assertMixedContentBlockingState(gBrowser, {
+      activeLoaded: true, activeBlocked: false, passiveLoaded: false,
+    });
 
-  // The Doorhanger should appear but isMixedContentBlocked should be >> NOT << true
-  // Currently it is >> TRUE << - see follow up bug 914860
-  todo(!gIdentityHandler._identityBox.classList.contains("mixedActiveBlocked"), "OK: Mixed Content is NOT being blocked in Test 5C!");
-
-  var actual = gTestWin.content.document.getElementById('mctestdiv').innerHTML;
-  todo_is(actual, "Mixed Content Blocker disabled", "OK: Executed mixed script in Test 5C!");
-
-  // remove tabs
-  gTestWin.gBrowser.removeTab(gTestWin.gBrowser.tabs[2], {animate: false});
-  gTestWin.gBrowser.removeTab(gTestWin.gBrowser.tabs[1], {animate: false});
-  gTestWin.gBrowser.selectTabAtIndex(0);
-
-  // the sjs files returns a 302 redirect - note, different origins
-  var childTabLink = gHttpTestRoot2 + "file_bug906190.sjs";
-  setUpTest("Test6", "linkForTest6", test6, childTabLink);
-}
-
-//------------------------ Test 6 ------------------------------
-
-function test6() {
-  curClickHandler = function (e) { clickHandler(e, test6A) };
-  gTestWin.gBrowser.addEventListener("click", curClickHandler, true);
+    is(content.document.getElementById('mctestdiv').innerHTML,
+       "Mixed Content Blocker disabled", "OK: Executed mixed script");
+  }, true);
+});
 
-  // simulating |CTRL-CLICK|
-  let targetElt = gTestWin.content.document.getElementById("Test6");
-  EventUtils.synthesizeMouseAtCenter(targetElt, { button: 1 }, gTestWin.content);
-}
-
-function test6A() {
-  gTestWin.gBrowser.selectTabAtIndex(2);
-
-  // The Doorhanger should appear and isMixedContentBlocked should be >> TRUE <<
-  assertMixedContentBlockingState(gTestWin.gBrowser, {activeLoaded: false, activeBlocked: true, passiveLoaded: false});
-
-  var actual = gTestWin.content.document.getElementById('mctestdiv').innerHTML;
-  is(actual, "Mixed Content Blocker enabled", "OK: Blocked mixed script in Test 6A");
+/**
+ * 4. - Load a html page which has mixed content
+ *    - Doorhanger to disable protection appears - we disable it
+ *    - Load a new page from the same origin in a new tab simulating a click
+ *    - Redirect to another page from a different origin using meta-refresh
+ *    - Doorhanger >> SHOULD << appear again!
+ */
+add_task(function* test_same_origin_metarefresh_different_origin() {
+  yield doTest(gHttpTestRoot2 + "file_bug906190_1.html",
+               gHttpTestRoot2 + "file_bug906190_3_4.html", function* () {
+    // The doorhanger should appear and activeBlocked should be >> TRUE <<.
+    yield assertMixedContentBlockingState(gBrowser, {
+      activeLoaded: false, activeBlocked: true, passiveLoaded: false,
+    });
 
-  // done
-  gTestWin.gBrowser.removeCurrentTab();
-  test6B();
-}
-
-function test6B() {
-  curContextMenu = function (e) { contextMenuOpenHandler(e, test6C) };
-  gTestWin.document.addEventListener("popupshown", curContextMenu, false);
-
-  // simulating |RIGHT-CLICK -> OPEN LINK IN TAB|
-  let targetElt = gTestWin.content.document.getElementById("Test6");
-  EventUtils.synthesizeMouseAtCenter(targetElt, { type : "contextmenu", button : 2 } , gTestWin.content);
-}
-
-function test6C() {
-  gTestWin.gBrowser.selectTabAtIndex(2);
+    is(content.document.getElementById('mctestdiv').innerHTML,
+       "Mixed Content Blocker enabled", "OK: Blocked mixed script");
+  }, true);
+});
 
-  // The Doorhanger should appear and isMixedContentBlocked should be >> TRUE <<
-  assertMixedContentBlockingState(gTestWin.gBrowser, {activeLoaded: false, activeBlocked: true, passiveLoaded: false});
-
-  var actual = gTestWin.content.document.getElementById('mctestdiv').innerHTML;
-  is(actual, "Mixed Content Blocker enabled", "OK: Blocked mixed script in Test 6C");
-
-  gTestWin.close();
-  finish();
-}
-
-//------------------------ SETUP ------------------------------
-
-function setupTestBrowserWindow() {
-  // Inject links in content.
-  let doc = gTestWin.content.document;
-  let mainDiv = doc.createElement("div");
-  mainDiv.innerHTML =
-    '<p><a id="linkForTest1" href="'+ gHttpTestRoot1 + 'file_bug906190_1.html">Test 1</a></p>' +
-    '<p><a id="linkForTest2" href="'+ gHttpTestRoot1 + 'file_bug906190_2.html">Test 2</a></p>' +
-    '<p><a id="linkForTest3" href="'+ gHttpTestRoot1 + 'file_bug906190_1.html">Test 3</a></p>' +
-    '<p><a id="linkForTest4" href="'+ gHttpTestRoot2 + 'file_bug906190_1.html">Test 4</a></p>' +
-    '<p><a id="linkForTest5" href="'+ gHttpTestRoot1 + 'file_bug906190_1.html">Test 5</a></p>' +
-    '<p><a id="linkForTest6" href="'+ gHttpTestRoot1 + 'file_bug906190_1.html">Test 6</a></p>';
-  doc.body.appendChild(mainDiv);
-}
+/**
+ * 5. - Load a html page which has mixed content
+ *    - Doorhanger to disable protection appears - we disable it
+ *    - Load a new page from the same origin in a new tab simulating a click
+ *    - Redirect to another page from the same origin using 302 redirect
+ *    - Doorhanger >> APPEARS << , but should >> NOT << appear again!
+ *    - FOLLOW UP BUG 914860!
+ */
+add_task(function* test_same_origin_302redirect_same_origin() {
+  // the sjs files returns a 302 redirect- note, same origins
+  yield doTest(gHttpTestRoot1 + "file_bug906190_1.html",
+               gHttpTestRoot1 + "file_bug906190.sjs", function* () {
+    // The doorhanger should appear but activeBlocked should be >> NOT << true.
+    // Currently it is >> TRUE << - see follow up bug 914860
+    todo(!gIdentityHandler._identityBox.classList.contains("mixedActiveBlocked"),
+         "OK: Mixed Content is NOT being blocked");
 
-function startTests() {
-  mainTab = gTestWin.gBrowser.selectedTab;
-  var childTabLink = gHttpTestRoot1 + "file_bug906190_2.html";
-  setUpTest("Test1", "linkForTest1", test1, childTabLink);
-}
-
-function test() {
-  // Performing async calls, e.g. 'onload', we have to wait till all of them finished
-  waitForExplicitFinish();
-
-  // Store original preferences so we can restore settings after testing
-  origBlockActive = Services.prefs.getBoolPref(PREF_ACTIVE);
-  Services.prefs.setBoolPref(PREF_ACTIVE, true);
+    todo_is(content.document.getElementById('mctestdiv').innerHTML,
+            "Mixed Content Blocker disabled", "OK: Executed mixed script");
+  });
+});
 
-  gTestWin = openDialog(location, "", "chrome,all,dialog=no", "about:blank");
-  whenDelayedStartupFinished(gTestWin, function () {
-    info("browser window opened");
-    waitForFocus(function() {
-      info("browser window focused");
-      waitForFocus(function() {
-        info("setting up browser...");
-        setupTestBrowserWindow();
-        info("running tests...");
-        executeSoon(startTests);
-      }, gTestWin.content, true);
-    }, gTestWin);
+/**
+ * 6. - Load a html page which has mixed content
+ *    - Doorhanger to disable protection appears - we disable it
+ *    - Load a new page from the same origin in a new tab simulating a click
+ *    - Redirect to another page from a different origin using 302 redirect
+ *    - Doorhanger >> SHOULD << appear again!
+ */
+add_task(function* test_same_origin_302redirect_different_origin() {
+  // the sjs files returns a 302 redirect - note, different origins
+  yield doTest(gHttpTestRoot2 + "file_bug906190_1.html",
+               gHttpTestRoot2 + "file_bug906190.sjs", function* () {
+    // The doorhanger should appear and activeBlocked should be >> TRUE <<.
+    yield assertMixedContentBlockingState(gBrowser, {
+      activeLoaded: false, activeBlocked: true, passiveLoaded: false,
+    });
+
+    is(content.document.getElementById('mctestdiv').innerHTML,
+       "Mixed Content Blocker enabled", "OK: Blocked mixed script");
   });
-}
+});
--- a/browser/base/content/test/general/head.js
+++ b/browser/base/content/test/general/head.js
@@ -749,24 +749,31 @@ function assertWebRTCIndicatorStatus(exp
     }
   }
 }
 
 /**
  * Test the state of the identity box and control center to make
  * sure they are correctly showing the expected mixed content states.
  *
+ * @note The checks are done synchronously, but new code should wait on the
+ *       returned Promise object to ensure the identity panel has closed.
+ *       Bug 1221114 is filed to fix the existing code.
+ *
  * @param tabbrowser
  * @param Object states
  *        MUST include the following properties:
  *        {
  *           activeLoaded: true|false,
  *           activeBlocked: true|false,
  *           passiveLoaded: true|false,
  *        }
+ *
+ * @return {Promise}
+ * @resolves When the operation has finished and the identity panel has closed.
  */
 function assertMixedContentBlockingState(tabbrowser, states = {}) {
   if (!tabbrowser || !("activeLoaded" in states) ||
       !("activeBlocked" in states) || !("passiveLoaded" in states))  {
     throw new Error("assertMixedContentBlockingState requires a browser and a states object");
   }
 
   let {passiveLoaded,activeLoaded,activeBlocked} = states;
@@ -893,16 +900,22 @@ function assertMixedContentBlockingState
   if (activeLoaded || activeBlocked || passiveLoaded) {
     doc.getElementById("identity-popup-security-expander").click();
     is(Array.filter(doc.querySelectorAll("[observes=identity-popup-mcb-learn-more]"),
                     element => !is_hidden(element)).length, 1,
        "The 'Learn more' link should be visible once.");
   }
 
   gIdentityHandler._identityPopup.hidden = true;
+
+  // Wait for the panel to be closed before continuing. The promisePopupHidden
+  // function cannot be used because it's unreliable unless promisePopupShown is
+  // also called before closing the panel. This cannot be done until all callers
+  // are made asynchronous (bug 1221114).
+  return new Promise(resolve => executeSoon(resolve));
 }
 
 function makeActionURI(action, params) {
   let url = "moz-action:" + action + "," + JSON.stringify(params);
   return NetUtil.newURI(url);
 }
 
 function is_hidden(element) {
--- a/browser/branding/unofficial/content/aboutDialog.css
+++ b/browser/branding/unofficial/content/aboutDialog.css
@@ -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/. */
 
 #aboutDialogContainer {
   background-image: url("chrome://branding/content/about-background.png");
   background-repeat: no-repeat;
-  background-color: #000;
+  background-color: rgb(10,17,37);
   color: #fff;
 }
 
 /* Use inverted spinner icon on the dark background */
 .update-throbber {
   list-style-image: url("chrome://global/skin/icons/loading-inverted.png");
 }
 @media (min-resolution: 1.1dppx) {
--- a/browser/components/loop/content/js/panel.js
+++ b/browser/components/loop/content/js/panel.js
@@ -916,37 +916,43 @@ loop.panel = (function(_, mozL10n) {
       window.addEventListener("GettingStartedSeen", this._gettingStartedSeen);
     },
 
     componentWillUnmount: function() {
       window.removeEventListener("LoopStatusChanged", this._onStatusChanged);
       window.removeEventListener("GettingStartedSeen", this._gettingStartedSeen);
     },
 
+    handleContextMenu: function(e) {
+      e.preventDefault();
+    },
+
     render: function() {
       var NotificationListView = sharedViews.NotificationListView;
 
       if (!this.state.gettingStartedSeen) {
         return (
-          React.createElement("div", {className: "fte-get-started-container"}, 
+          React.createElement("div", {className: "fte-get-started-container", 
+               onContextMenu: this.handleContextMenu}, 
             React.createElement(NotificationListView, {
               clearOnDocumentHidden: true, 
               notifications: this.props.notifications}), 
             React.createElement(GettingStartedView, {mozLoop: this.props.mozLoop}), 
             React.createElement(ToSView, {mozLoop: this.props.mozLoop})
           )
         );
       }
 
       if (!this.state.hasEncryptionKey) {
         return React.createElement(SignInRequestView, {mozLoop: this.props.mozLoop});
       }
 
       return (
-        React.createElement("div", {className: "panel-content"}, 
+        React.createElement("div", {className: "panel-content", 
+             onContextMenu: this.handleContextMenu}, 
           React.createElement("div", {className: "beta-ribbon"}), 
           React.createElement(NotificationListView, {
             clearOnDocumentHidden: true, 
             notifications: this.props.notifications}), 
             React.createElement(RoomList, {dispatcher: this.props.dispatcher, 
               mozLoop: this.props.mozLoop, 
               store: this.props.roomStore}), 
           React.createElement("div", {className: "footer"}, 
--- a/browser/components/loop/content/js/panel.jsx
+++ b/browser/components/loop/content/js/panel.jsx
@@ -916,37 +916,43 @@ loop.panel = (function(_, mozL10n) {
       window.addEventListener("GettingStartedSeen", this._gettingStartedSeen);
     },
 
     componentWillUnmount: function() {
       window.removeEventListener("LoopStatusChanged", this._onStatusChanged);
       window.removeEventListener("GettingStartedSeen", this._gettingStartedSeen);
     },
 
+    handleContextMenu: function(e) {
+      e.preventDefault();
+    },
+
     render: function() {
       var NotificationListView = sharedViews.NotificationListView;
 
       if (!this.state.gettingStartedSeen) {
         return (
-          <div className="fte-get-started-container">
+          <div className="fte-get-started-container"
+               onContextMenu={this.handleContextMenu}>
             <NotificationListView
               clearOnDocumentHidden={true}
               notifications={this.props.notifications} />
             <GettingStartedView mozLoop={this.props.mozLoop} />
             <ToSView mozLoop={this.props.mozLoop} />
           </div>
         );
       }
 
       if (!this.state.hasEncryptionKey) {
         return <SignInRequestView mozLoop={this.props.mozLoop} />;
       }
 
       return (
-        <div className="panel-content">
+        <div className="panel-content"
+             onContextMenu={this.handleContextMenu} >
           <div className="beta-ribbon" />
           <NotificationListView
             clearOnDocumentHidden={true}
             notifications={this.props.notifications} />
             <RoomList dispatcher={this.props.dispatcher}
               mozLoop={this.props.mozLoop}
               store={this.props.roomStore} />
           <div className="footer">
--- a/browser/components/loop/content/js/roomViews.js
+++ b/browser/components/loop/content/js/roomViews.js
@@ -757,16 +757,20 @@ loop.roomViews = (function(mozL10n) {
     componentDidUpdate: function(prevProps, prevState) {
       // Handle timestamp and window closing only when the call has terminated.
       if (prevState.roomState === ROOM_STATES.ENDED &&
           this.state.roomState === ROOM_STATES.ENDED) {
         this.props.onCallTerminated();
       }
     },
 
+    handleContextMenu: function(e) {
+      e.preventDefault();
+    },
+
     render: function() {
       if (this.state.roomName || this.state.roomContextUrls) {
         var roomTitle = this.state.roomName ||
                         this.state.roomContextUrls[0].description ||
                         this.state.roomContextUrls[0].location;
         this.setTitle(roomTitle);
       }
 
@@ -802,17 +806,18 @@ loop.roomViews = (function(mozL10n) {
               id: "edit",
               enabled: !this.state.showEditContext,
               visible: true,
               onClick: this.handleEditContextClick
             },
             { id: "help" }
           ];
           return (
-            React.createElement("div", {className: "room-conversation-wrapper desktop-room-wrapper"}, 
+            React.createElement("div", {className: "room-conversation-wrapper desktop-room-wrapper", 
+              onContextMenu: this.handleContextMenu}, 
               React.createElement(sharedViews.MediaLayoutView, {
                 dispatcher: this.props.dispatcher, 
                 displayScreenShare: false, 
                 isLocalLoading: this._isLocalLoading(), 
                 isRemoteLoading: this._isRemoteLoading(), 
                 isScreenShareLoading: false, 
                 localPosterUrl: this.props.localPosterUrl, 
                 localSrcMediaElement: this.state.localSrcMediaElement, 
--- a/browser/components/loop/content/js/roomViews.jsx
+++ b/browser/components/loop/content/js/roomViews.jsx
@@ -757,16 +757,20 @@ loop.roomViews = (function(mozL10n) {
     componentDidUpdate: function(prevProps, prevState) {
       // Handle timestamp and window closing only when the call has terminated.
       if (prevState.roomState === ROOM_STATES.ENDED &&
           this.state.roomState === ROOM_STATES.ENDED) {
         this.props.onCallTerminated();
       }
     },
 
+    handleContextMenu: function(e) {
+      e.preventDefault();
+    },
+
     render: function() {
       if (this.state.roomName || this.state.roomContextUrls) {
         var roomTitle = this.state.roomName ||
                         this.state.roomContextUrls[0].description ||
                         this.state.roomContextUrls[0].location;
         this.setTitle(roomTitle);
       }
 
@@ -802,17 +806,18 @@ loop.roomViews = (function(mozL10n) {
               id: "edit",
               enabled: !this.state.showEditContext,
               visible: true,
               onClick: this.handleEditContextClick
             },
             { id: "help" }
           ];
           return (
-            <div className="room-conversation-wrapper desktop-room-wrapper">
+            <div className="room-conversation-wrapper desktop-room-wrapper"
+              onContextMenu={this.handleContextMenu}>
               <sharedViews.MediaLayoutView
                 dispatcher={this.props.dispatcher}
                 displayScreenShare={false}
                 isLocalLoading={this._isLocalLoading()}
                 isRemoteLoading={this._isRemoteLoading()}
                 isScreenShareLoading={false}
                 localPosterUrl={this.props.localPosterUrl}
                 localSrcMediaElement={this.state.localSrcMediaElement}
--- a/browser/components/loop/test/desktop-local/panel_test.js
+++ b/browser/components/loop/test/desktop-local/panel_test.js
@@ -213,16 +213,26 @@ describe("loop.panel", function() {
       });
 
       afterEach(function() {
         delete navigator.mozLoop.logInToFxA;
         delete navigator.mozLoop.calls;
         navigator.mozLoop.fxAEnabled = true;
       });
 
+      it("should NOT show the context menu on right click", function() {
+        var prevent = sandbox.stub();
+        var view = createTestPanelView();
+        TestUtils.Simulate.contextMenu(
+          view.getDOMNode(),
+          { preventDefault: prevent }
+        );
+        sinon.assert.calledOnce(prevent);
+      });
+
       it("should trigger the FxA sign in/up process when clicking the link",
         function() {
           navigator.mozLoop.logInToFxA = sandbox.stub();
 
           var view = createTestPanelView();
 
           TestUtils.Simulate.click(
             view.getDOMNode().querySelector(".signin-link > a"));
--- a/browser/components/loop/test/desktop-local/roomViews_test.js
+++ b/browser/components/loop/test/desktop-local/roomViews_test.js
@@ -337,16 +337,26 @@ describe("loop.roomViews", function() {
         roomStore: roomStore,
         mozLoop: fakeMozLoop,
         onCallTerminated: onCallTerminatedStub
       }, props);
       return TestUtils.renderIntoDocument(
         React.createElement(loop.roomViews.DesktopRoomConversationView, props));
     }
 
+    it("should NOT show the context menu on right click", function() {
+      var prevent = sandbox.stub();
+      view = mountTestComponent();
+      TestUtils.Simulate.contextMenu(
+        view.getDOMNode(),
+        { preventDefault: prevent }
+      );
+      sinon.assert.calledOnce(prevent);
+    });
+
     it("should dispatch a setMute action when the audio mute button is pressed",
       function() {
         view = mountTestComponent();
 
         view.setState({ audioMuted: true });
 
         var muteBtn = view.getDOMNode().querySelector(".btn-mute-audio");
 
@@ -685,17 +695,16 @@ describe("loop.roomViews", function() {
                   location: "https://fakeurl.com"
                 }
               ]
           });
           view = mountTestComponent();
           expect(fakeWindow.document.title).to.equal("https://fakeurl.com");
         });
       });
-
     });
 
     describe("Edit Context", function() {
       it("should show the form when the edit button is clicked", function() {
         view = mountTestComponent();
         var node = view.getDOMNode();
 
         expect(node.querySelector(".room-context")).to.eql(null);
--- a/browser/components/loop/test/functional/config.py
+++ b/browser/components/loop/test/functional/config.py
@@ -1,11 +1,13 @@
+from os import environ
+
 # Loop server configuration
 CONTENT_SERVER_PORT = 3001
-LOOP_SERVER_PORT = 5001
+LOOP_SERVER_PORT = environ.get("LOOP_SERVER_PORT") or 5001
 LOOP_SERVER_URL = "http://localhost:" + str(LOOP_SERVER_PORT)
 FIREFOX_PREFERENCES = {
     "loop.server": LOOP_SERVER_URL + "/v0",
     "browser.dom.window.dump.enabled": True,
     # Some more changes might be necesarry to have this working in offline mode
     "media.peerconnection.use_document_iceservers": False,
     "media.peerconnection.ice.loopback": True,
     "devtools.chrome.enabled": True,
--- a/browser/components/sessionstore/SessionStorage.jsm
+++ b/browser/components/sessionstore/SessionStorage.jsm
@@ -25,47 +25,47 @@ function getPrincipalForFrame(docShell, 
 this.SessionStorage = Object.freeze({
   /**
    * Updates all sessionStorage "super cookies"
    * @param docShell
    *        That tab's docshell (containing the sessionStorage)
    * @param frameTree
    *        The docShell's FrameTree instance.
    * @return Returns a nested object that will have hosts as keys and per-host
-   *         session storage data as values. For example:
-   *         {"example.com": {"key": "value", "my_number": 123}}
+   *         session storage data as strings. For example:
+   *         {"example.com": {"key": "value", "my_number": "123"}}
    */
   collect: function (docShell, frameTree) {
     return SessionStorageInternal.collect(docShell, frameTree);
   },
 
   /**
    * Restores all sessionStorage "super cookies".
    * @param aDocShell
    *        A tab's docshell (containing the sessionStorage)
    * @param aStorageData
    *        A nested object with storage data to be restored that has hosts as
-   *        keys and per-host session storage data as values. For example:
-   *        {"example.com": {"key": "value", "my_number": 123}}
+   *        keys and per-host session storage data as strings. For example:
+   *        {"example.com": {"key": "value", "my_number": "123"}}
    */
   restore: function (aDocShell, aStorageData) {
     SessionStorageInternal.restore(aDocShell, aStorageData);
-  }
+  },
 });
 
 var SessionStorageInternal = {
   /**
    * Reads all session storage data from the given docShell.
    * @param docShell
    *        A tab's docshell (containing the sessionStorage)
    * @param frameTree
    *        The docShell's FrameTree instance.
    * @return Returns a nested object that will have hosts as keys and per-host
-   *         session storage data as values. For example:
-   *         {"example.com": {"key": "value", "my_number": 123}}
+   *         session storage data as strings. For example:
+   *         {"example.com": {"key": "value", "my_number": "123"}}
    */
   collect: function (docShell, frameTree) {
     let data = {};
     let visitedOrigins = new Set();
 
     frameTree.forEach(frame => {
       let principal = getPrincipalForFrame(docShell, frame);
       if (!principal) {
@@ -93,18 +93,18 @@ var SessionStorageInternal = {
   },
 
   /**
    * Writes session storage data to the given tab.
    * @param aDocShell
    *        A tab's docshell (containing the sessionStorage)
    * @param aStorageData
    *        A nested object with storage data to be restored that has hosts as
-   *        keys and per-host session storage data as values. For example:
-   *        {"example.com": {"key": "value", "my_number": 123}}
+   *        keys and per-host session storage data as strings. For example:
+   *        {"example.com": {"key": "value", "my_number": "123"}}
    */
   restore: function (aDocShell, aStorageData) {
     for (let origin of Object.keys(aStorageData)) {
       let data = aStorageData[origin];
       let principal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(origin);
       let storageManager = aDocShell.QueryInterface(Ci.nsIDOMStorageManager);
       let window = aDocShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
 
--- a/browser/components/sessionstore/content/content-sessionStore.js
+++ b/browser/components/sessionstore/content/content-sessionStore.js
@@ -11,20 +11,23 @@ function debug(msg) {
 var Cu = Components.utils;
 var Cc = Components.classes;
 var Ci = Components.interfaces;
 var Cr = Components.results;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
 Cu.import("resource://gre/modules/Timer.jsm", this);
 
+XPCOMUtils.defineLazyModuleGetter(this, "FormData",
+  "resource://gre/modules/FormData.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Preferences",
+  "resource://gre/modules/Preferences.jsm");
+
 XPCOMUtils.defineLazyModuleGetter(this, "DocShellCapabilities",
   "resource:///modules/sessionstore/DocShellCapabilities.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "FormData",
-  "resource://gre/modules/FormData.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PageStyle",
   "resource:///modules/sessionstore/PageStyle.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ScrollPosition",
   "resource://gre/modules/ScrollPosition.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "SessionHistory",
   "resource:///modules/sessionstore/SessionHistory.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "SessionStorage",
   "resource:///modules/sessionstore/SessionStorage.jsm");
@@ -34,16 +37,19 @@ var gFrameTree = new FrameTree(this);
 
 Cu.import("resource:///modules/sessionstore/ContentRestore.jsm", this);
 XPCOMUtils.defineLazyGetter(this, 'gContentRestore',
                             () => { return new ContentRestore(this) });
 
 // The current epoch.
 var gCurrentEpoch = 0;
 
+// A bound to the size of data to store for DOM Storage.
+const DOM_STORAGE_MAX_CHARS = 10000000; // 10M characters
+
 /**
  * Returns a lazy function that will evaluate the given
  * function |fn| only once and cache its return value.
  */
 function createLazy(fn) {
   let cached = false;
   let cachedValue = null;
 
@@ -518,19 +524,68 @@ var SessionStorageListener = {
   },
 
   observe: function () {
     // Collect data on the next tick so that any other observer
     // that needs to purge data can do its work first.
     setTimeout(() => this.collect(), 0);
   },
 
+  // Before DOM Storage can be written to disk, it needs to be serialized
+  // for sending across frames/processes, then again to be sent across
+  // threads, then again to be put in a buffer for the disk. Each of these
+  // serializations is an opportunity to OOM and (depending on the site of
+  // the OOM), either crash, lose all data for the frame or lose all data
+  // for the application.
+  //
+  // In order to avoid this, compute an estimate of the size of the
+  // object, and block SessionStorage items that are too large. As
+  // we also don't want to cause an OOM here, we use a quick and memory-
+  // efficient approximation: we compute the total sum of string lengths
+  // involved in this object.
+  estimateStorageSize: function(collected) {
+    if (!collected) {
+      return 0;
+    }
+
+    let size = 0;
+    for (let host of Object.keys(collected)) {
+      size += host.length;
+      let perHost = collected[host];
+      for (let key of Object.keys(perHost)) {
+        size += key.length;
+        let perKey = perHost[key];
+        size += perKey.length;
+      }
+    }
+
+    return size;
+  },
+
   collect: function () {
     if (docShell) {
-      MessageQueue.push("storage", () => SessionStorage.collect(docShell, gFrameTree));
+      MessageQueue.push("storage", () => {
+        let collected = SessionStorage.collect(docShell, gFrameTree);
+
+        if (collected == null) {
+          return collected;
+        }
+
+        let size = this.estimateStorageSize(collected);
+
+        MessageQueue.push("telemetry", () => ({ FX_SESSION_RESTORE_DOM_STORAGE_SIZE_ESTIMATE_CHARS: size }));
+        if (size > Preferences.get("browser.sessionstore.dom_storage_limit", DOM_STORAGE_MAX_CHARS)) {
+          // Rather than keeping the old storage, which wouldn't match the rest
+          // of the state of the page, empty the storage. DOM storage will be
+          // recollected the next time and stored if it is now small enough.
+          return {};
+        }
+
+        return collected;
+      });
     }
   },
 
   onFrameTreeCollected: function () {
     this.collect();
   },
 
   onFrameTreeReset: function () {
@@ -660,38 +715,44 @@ var MessageQueue = {
     // through a CPOW. RPC messages are the only synchronous messages that the
     // child is allowed to send to the parent while it is handling a CPOW
     // request.
     let sendMessage = sync ? sendRpcMessage : sendAsyncMessage;
 
     let durationMs = Date.now();
 
     let data = {};
+    let telemetry = {};
     for (let [key, id] of this._lastUpdated) {
       // There is no data for the given key anymore because
       // the parent process already marked it as received.
       if (!this._data.has(key)) {
         continue;
       }
 
       if (startID > id) {
         // If the |id| passed by the parent process is higher than the one
         // stored in |_lastUpdated| for the given key we know that the parent
         // received all necessary data and we can remove it from the map.
         this._data.delete(key);
         continue;
       }
 
-      data[key] = this._data.get(key)();
+      let value = this._data.get(key)();
+      if (key == "telemetry") {
+        for (let histogramId of Object.keys(value)) {
+          telemetry[histogramId] = value[histogramId];
+        }
+      } else {
+        data[key] = value;
+      }
     }
 
     durationMs = Date.now() - durationMs;
-    let telemetry = {
-      FX_SESSION_RESTORE_CONTENT_COLLECT_DATA_LONGEST_OP_MS: durationMs
-    }
+    telemetry.FX_SESSION_RESTORE_CONTENT_COLLECT_DATA_LONGEST_OP_MS = durationMs;
 
     try {
       // Send all data to the parent process.
       sendMessage("SessionStore:update", {
         id: this._id, data, telemetry, flushID,
         isFinal: options.isFinal || false,
         epoch: gCurrentEpoch
       });
--- a/browser/components/sessionstore/test/browser.ini
+++ b/browser/components/sessionstore/test/browser.ini
@@ -99,17 +99,16 @@ skip-if = buildapp == 'mulet'
 [browser_purge_shistory.js]
 skip-if = e10s
 [browser_replace_load.js]
 [browser_restore_redirect.js]
 [browser_scrollPositions.js]
 [browser_sessionHistory.js]
 [browser_sessionStorage.js]
 [browser_swapDocShells.js]
-skip-if = e10s # See bug 918634
 [browser_switch_remoteness.js]
 run-if = e10s
 [browser_upgrade_backup.js]
 [browser_windowRestore_perwindowpb.js]
 [browser_248970_b_perwindowpb.js]
 # Disabled because of leaks.
 # Re-enabling and rewriting this test is tracked in bug 936919.
 skip-if = true
--- a/browser/components/sessionstore/test/browser_sessionStorage.html
+++ b/browser/components/sessionstore/test/browser_sessionStorage.html
@@ -15,12 +15,13 @@
         let isSecure = args.indexOf("secure") > -1;
         let scheme = isSecure ? "https" : "http";
         iframe.setAttribute("src", scheme + "://example.com" + location.pathname + "?" + rand);
         document.body.appendChild(iframe);
       }
 
       if (sessionStorage.length === 0) {
         sessionStorage.test = (isOuter ? "outer" : "inner") + "-value-" + rand;
+        document.title = sessionStorage.test;
       }
     </script>
   </body>
 </html>
--- a/browser/components/sessionstore/test/browser_sessionStorage.js
+++ b/browser/components/sessionstore/test/browser_sessionStorage.js
@@ -178,11 +178,51 @@ add_task(function respect_privacy_level(
   // With privacy_level=0 the duplicated |tab2| should persist all data.
   [{state: {storage}}] = JSON.parse(ss.getClosedTabData(window));
   is(storage["http://mochi.test:8888"].test, OUTER_VALUE,
     "http sessionStorage data has been saved");
   is(storage["https://example.com"].test, INNER_VALUE,
     "https sessionStorage data has been saved");
 });
 
+// Test that we record the size of messages.
+add_task(function* test_telemetry() {
+  Services.telemetry.canRecordExtended = true;
+  let histogram = Services.telemetry.getHistogramById("FX_SESSION_RESTORE_DOM_STORAGE_SIZE_ESTIMATE_CHARS");
+  let snap1 = histogram.snapshot();
+
+  let tab = gBrowser.addTab(URL);
+  let browser = tab.linkedBrowser;
+  yield promiseBrowserLoaded(browser);
+
+  // Flush to make sure chrome received all data.
+  yield TabStateFlusher.flush(browser);
+  let snap2 = histogram.snapshot();
+
+  Assert.ok(snap2.counts[5] > snap1.counts[5]);
+  yield promiseRemoveTab(tab);
+  Services.telemetry.canRecordExtended = false;
+});
+
+// Lower the size limit for DOM Storage content. Check that DOM Storage
+// is not updated, but that other things remain updated.
+add_task(function* test_large_content() {
+  Services.prefs.setIntPref("browser.sessionstore.dom_storage_limit", 5);
+
+  let tab = gBrowser.addTab(URL);
+  let browser = tab.linkedBrowser;
+  yield promiseBrowserLoaded(browser);
+
+  // Flush to make sure chrome received all data.
+  yield TabStateFlusher.flush(browser);
+
+  let state = JSON.parse(ss.getTabState(tab));
+  info(JSON.stringify(state, null, "\t"));
+  Assert.equal(state.storage, null, "We have no storage for the tab");
+  Assert.equal(state.entries[0].title, OUTER_VALUE);
+  yield promiseRemoveTab(tab);
+
+  Services.prefs.clearUserPref("browser.sessionstore.dom_storage_limit");
+});
+
 function purgeDomainData(browser, domain) {
   return sendMessage(browser, "ss-test:purgeDomainData", domain);
 }
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -612,17 +612,17 @@ menuitem:not([type]):not(.menuitem-toolt
 :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@)) > .toolbarbutton-menubutton-button > .toolbarbutton-icon {
   padding: 3px 7px;
 }
 
 :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1 > .toolbarbutton-icon,
 :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1 > .toolbarbutton-menubutton-button > .toolbarbutton-icon,
 :-moz-any(#TabsToolbar, #nav-bar) #bookmarks-menu-button[cui-areatype="toolbar"] > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon {
   /* horizontal padding + border + actual icon width */
-  max-width: 32px;
+  max-width: 32px !important /* bug 561154 */;
 }
 
 #nav-bar #PanelUI-menu-button {
   -moz-padding-start: 5px;
   -moz-padding-end: 5px;
 }
 
 .findbar-button:not(:-moz-any([checked="true"],[disabled="true"])):hover > .toolbarbutton-text,
--- a/devtools/client/shared/widgets/CubicBezierWidget.js
+++ b/devtools/client/shared/widgets/CubicBezierWidget.js
@@ -22,44 +22,48 @@
 
 // Based on www.cubic-bezier.com by Lea Verou
 // See https://github.com/LeaVerou/cubic-bezier
 
 "use strict";
 
 const EventEmitter = require("devtools/shared/event-emitter");
 const {setTimeout, clearTimeout} = require("sdk/timers");
-const {PREDEFINED, PRESETS, DEFAULT_PRESET_CATEGORY} = require("devtools/client/shared/widgets/CubicBezierPresets");
+const {
+  PREDEFINED,
+  PRESETS,
+  DEFAULT_PRESET_CATEGORY
+} = require("devtools/client/shared/widgets/CubicBezierPresets");
 const {Cc, Ci} = require('chrome');
 loader.lazyGetter(this, "DOMUtils", () => {
   return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
 });
 
 /**
  * CubicBezier data structure helper
  * Accepts an array of coordinates and exposes a few useful getters
  * @param {Array} coordinates i.e. [.42, 0, .58, 1]
  */
 function CubicBezier(coordinates) {
   if (!coordinates) {
-    throw "No offsets were defined";
+    throw new Error("No offsets were defined");
   }
 
   this.coordinates = coordinates.map(n => +n);
 
   for (let i = 4; i--;) {
     let xy = this.coordinates[i];
-    if (isNaN(xy) || (!(i%2) && (xy < 0 || xy > 1))) {
-      throw "Wrong coordinate at " + i + "(" + xy + ")";
+    if (isNaN(xy) || (!(i % 2) && (xy < 0 || xy > 1))) {
+      throw new Error(`Wrong coordinate at ${i}(${xy})`);
     }
   }
 
   this.coordinates.toString = function() {
     return this.map(n => {
-      return (Math.round(n * 100)/100 + '').replace(/^0\./, '.');
+      return (Math.round(n * 100) / 100 + "").replace(/^0\./, ".");
     }) + "";
   };
 }
 
 exports.CubicBezier = CubicBezier;
 
 CubicBezier.prototype = {
   get P1() {
@@ -68,35 +72,36 @@ CubicBezier.prototype = {
 
   get P2() {
     return this.coordinates.slice(2);
   },
 
   toString: function() {
     // Check first if current coords are one of css predefined functions
     let predefName = Object.keys(PREDEFINED)
-                           .find(key => coordsAreEqual(PREDEFINED[key], this.coordinates));
+                           .find(key => coordsAreEqual(PREDEFINED[key],
+                                                       this.coordinates));
 
-    return predefName || 'cubic-bezier(' + this.coordinates + ')';
+    return predefName || "cubic-bezier(" + this.coordinates + ")";
   }
 };
 
 /**
  * Bezier curve canvas plotting class
  * @param {DOMNode} canvas
  * @param {CubicBezier} bezier
  * @param {Array} padding Amount of horizontal,vertical padding around the graph
  */
 function BezierCanvas(canvas, bezier, padding) {
   this.canvas = canvas;
   this.bezier = bezier;
   this.padding = getPadding(padding);
 
   // Convert to a cartesian coordinate system with axes from 0 to 1
-  this.ctx = this.canvas.getContext('2d');
+  this.ctx = this.canvas.getContext("2d");
   let p = this.padding;
 
   this.ctx.scale(canvas.width * (1 - p[1] - p[3]),
                  -canvas.height * (1 - p[0] - p[2]));
   this.ctx.translate(p[3] / (1 - p[1] - p[3]),
                      -1 - p[0] / (1 - p[0] - p[2]));
 }
 
@@ -106,49 +111,49 @@ BezierCanvas.prototype = {
   /**
    * Get P1 and P2 current top/left offsets so they can be positioned
    * @return {Array} Returns an array of 2 {top:String,left:String} objects
    */
   get offsets() {
     let p = this.padding, w = this.canvas.width, h = this.canvas.height;
 
     return [{
-      left: w * (this.bezier.coordinates[0] * (1 - p[3] - p[1]) - p[3]) + 'px',
-      top: h * (1 - this.bezier.coordinates[1] * (1 - p[0] - p[2]) - p[0]) + 'px'
+      left: w * (this.bezier.coordinates[0] * (1 - p[3] - p[1]) - p[3]) + "px",
+      top: h * (1 - this.bezier.coordinates[1] * (1 - p[0] - p[2]) - p[0]) + "px"
     }, {
-      left: w * (this.bezier.coordinates[2] * (1 - p[3] - p[1]) - p[3]) + 'px',
-      top: h * (1 - this.bezier.coordinates[3] * (1 - p[0] - p[2]) - p[0]) + 'px'
+      left: w * (this.bezier.coordinates[2] * (1 - p[3] - p[1]) - p[3]) + "px",
+      top: h * (1 - this.bezier.coordinates[3] * (1 - p[0] - p[2]) - p[0]) + "px"
     }];
   },
 
   /**
    * Convert an element's left/top offsets into coordinates
    */
   offsetsToCoordinates: function(element) {
     let p = this.padding, w = this.canvas.width, h = this.canvas.height;
 
     // Convert padding percentage to actual padding
-    p = p.map(function(a, i) { return a * (i % 2? w : h)});
+    p = p.map((a, i) => a * (i % 2 ? w : h));
 
     return [
       (parseFloat(element.style.left) - p[3]) / (w + p[1] + p[3]),
       (h - parseFloat(element.style.top) - p[2]) / (h - p[0] - p[2])
     ];
   },
 
   /**
    * Draw the cubic bezier curve for the current coordinates
    */
-  plot: function(settings={}) {
+  plot: function(settings = {}) {
     let xy = this.bezier.coordinates;
 
     let defaultSettings = {
-      handleColor: '#666',
+      handleColor: "#666",
       handleThickness: .008,
-      bezierColor: '#4C9ED9',
+      bezierColor: "#4C9ED9",
       bezierThickness: .015,
       drawHandles: true
     };
 
     for (let setting in settings) {
       defaultSettings[setting] = settings[setting];
     }
 
@@ -163,55 +168,56 @@ BezierCanvas.prototype = {
       // Draw control handles
       this.ctx.beginPath();
       this.ctx.fillStyle = defaultSettings.handleColor;
       this.ctx.lineWidth = defaultSettings.handleThickness;
       this.ctx.strokeStyle = defaultSettings.handleColor;
 
       this.ctx.moveTo(0, 0);
       this.ctx.lineTo(xy[0], xy[1]);
-      this.ctx.moveTo(1,1);
+      this.ctx.moveTo(1, 1);
       this.ctx.lineTo(xy[2], xy[3]);
 
       this.ctx.stroke();
       this.ctx.closePath();
 
-      var circle = function(ctx, cx, cy, r) {
+      let circle = (ctx, cx, cy, r) => {
         ctx.beginPath();
-        ctx.arc(cx, cy, r, 0, 2*Math.PI, !1);
+        ctx.arc(cx, cy, r, 0, 2 * Math.PI, !1);
         ctx.closePath();
       };
 
       circle(this.ctx, xy[0], xy[1], 1.5 * defaultSettings.handleThickness);
       this.ctx.fill();
       circle(this.ctx, xy[2], xy[3], 1.5 * defaultSettings.handleThickness);
       this.ctx.fill();
     }
 
     // Draw bezier curve
     this.ctx.beginPath();
     this.ctx.lineWidth = defaultSettings.bezierThickness;
     this.ctx.strokeStyle = defaultSettings.bezierColor;
-    this.ctx.moveTo(0,0);
-    this.ctx.bezierCurveTo(xy[0], xy[1], xy[2], xy[3], 1,1);
+    this.ctx.moveTo(0, 0);
+    this.ctx.bezierCurveTo(xy[0], xy[1], xy[2], xy[3], 1, 1);
     this.ctx.stroke();
     this.ctx.closePath();
   }
 };
 
 /**
  * Cubic-bezier widget. Uses the BezierCanvas class to draw the curve and
  * adds the control points and user interaction
  * @param {DOMNode} parent The container where the graph should be created
  * @param {Array} coordinates Coordinates of the curve to be drawn
  *
  * Emits "updated" events whenever the curve is changed. Along with the event is
  * sent a CubicBezier object
  */
-function CubicBezierWidget(parent, coordinates=PRESETS["ease-in"]["ease-in-sine"]) {
+function CubicBezierWidget(parent,
+                           coordinates = PRESETS["ease-in"]["ease-in-sine"]) {
   EventEmitter.decorate(this);
 
   this.parent = parent;
   let {curve, p1, p2} = this._initMarkup();
 
   this.curveBoundingBox = curve.getBoundingClientRect();
   this.curve = curve;
   this.p1 = p1;
@@ -332,58 +338,58 @@ CubicBezierWidget.prototype = {
       x = Math.min(Math.max(left, x), left + self.curveBoundingBox.width);
 
       point.style.left = x - left + "px";
       point.style.top = y - top + "px";
 
       self._updateFromPoints();
     };
 
-    doc.onmouseup = function () {
+    doc.onmouseup = function() {
       point.focus();
       doc.onmousemove = doc.onmouseup = null;
     };
   },
 
   _onPointKeyDown: function(event) {
     let point = event.target;
     let code = event.keyCode;
 
     if (code >= 37 && code <= 40) {
       event.preventDefault();
 
       // Arrow keys pressed
-      let left = parseInt(point.style.left);
-      let top = parseInt(point.style.top);
+      let left = parseInt(point.style.left, 10);
+      let top = parseInt(point.style.top, 10);
       let offset = 3 * (event.shiftKey ? 10 : 1);
 
       switch (code) {
-        case 37: point.style.left = left - offset + 'px'; break;
-        case 38: point.style.top = top - offset + 'px'; break;
-        case 39: point.style.left = left + offset + 'px'; break;
-        case 40: point.style.top = top + offset + 'px'; break;
+        case 37: point.style.left = left - offset + "px"; break;
+        case 38: point.style.top = top - offset + "px"; break;
+        case 39: point.style.left = left + offset + "px"; break;
+        case 40: point.style.top = top + offset + "px"; break;
       }
 
       this._updateFromPoints();
     }
   },
 
   _onCurveClick: function(event) {
     this.curveBoundingBox = this.curve.getBoundingClientRect();
 
     let left = this.curveBoundingBox.left;
     let top = this.curveBoundingBox.top;
     let x = event.pageX - left;
     let y = event.pageY - top;
 
     // Find which point is closer
     let distP1 = distance(x, y,
-      parseInt(this.p1.style.left), parseInt(this.p1.style.top));
+      parseInt(this.p1.style.left, 10), parseInt(this.p1.style.top, 10));
     let distP2 = distance(x, y,
-      parseInt(this.p2.style.left), parseInt(this.p2.style.top));
+      parseInt(this.p2.style.left, 10), parseInt(this.p2.style.top, 10));
 
     let point = distP1 < distP2 ? this.p1 : this.p2;
     point.style.left = x + "px";
     point.style.top = y + "px";
 
     this._updateFromPoints();
   },
 
@@ -428,17 +434,18 @@ CubicBezierWidget.prototype = {
     this.p1.style.left = offsets[0].left;
     this.p1.style.top = offsets[0].top;
     this.p2.style.left = offsets[1].left;
     this.p2.style.top = offsets[1].top;
   },
 
   /**
    * Set new coordinates for the control point and redraw the curve
-   * @param {String} value A string value. E.g. "linear", "cubic-bezier(0,0,1,1)"
+   * @param {String} value A string value. E.g. "linear",
+   * "cubic-bezier(0,0,1,1)"
    */
   set cssCubicBezierValue(value) {
     if (!value) {
       return;
     }
 
     value = value.trim();
 
@@ -458,17 +465,18 @@ CubicBezierWidget.prototype = {
 
     this.curve = this.p1 = this.p2 = null;
   }
 };
 
 /**
  * CubicBezierPreset widget.
  * Builds a menu of presets from CubicBezierPresets
- * @param {DOMNode} parent The container where the preset panel should be created
+ * @param {DOMNode} parent The container where the preset panel should be
+ * created
  *
  * Emits "new-coordinate" event along with the coordinates
  * whenever a preset is selected.
  */
 function CubicBezierPresetWidget(parent) {
   this.parent = parent;
 
   let {presetPane, presets, categories} = this._initMarkup();
@@ -547,16 +555,17 @@ CubicBezierPresetWidget.prototype = {
     let doc = this.parent.ownerDocument;
 
     let category = doc.createElement("div");
     category.id = categoryLabel;
     category.classList.add("category");
 
     let categoryDisplayLabel = this._normalizeCategoryLabel(categoryLabel);
     category.textContent = categoryDisplayLabel;
+    category.setAttribute("title", categoryDisplayLabel);
 
     return category;
   },
 
   _normalizeCategoryLabel: function(categoryLabel) {
     return categoryLabel.replace("/-/g", " ");
   },
 
@@ -594,16 +603,17 @@ CubicBezierPresetWidget.prototype = {
     });
     preset.appendChild(curve);
 
     // Create preset label
     let presetLabelElem = doc.createElement("p");
     let presetDisplayLabel = this._normalizePresetLabel(categoryLabel, presetLabel);
     presetLabelElem.textContent = presetDisplayLabel;
     preset.appendChild(presetLabelElem);
+    preset.setAttribute("title", presetDisplayLabel);
 
     return preset;
   },
 
   _normalizePresetLabel: function(categoryLabel, presetLabel) {
     return presetLabel.replace(categoryLabel + "-", "").replace("/-/g", " ");
   },
 
@@ -681,24 +691,22 @@ CubicBezierPresetWidget.prototype = {
     // show the default category.
     if (!category) {
       category = this.parent.querySelector("#" + DEFAULT_PRESET_CATEGORY);
     }
 
     // If the new coordinates do match a preset,
     // set its category and preset button as active.
     Object.keys(PRESETS).forEach(categoryLabel => {
-
       Object.keys(PRESETS[categoryLabel]).forEach(presetLabel => {
         if (coordsAreEqual(PRESETS[categoryLabel][presetLabel], coordinates)) {
           category = this.parent.querySelector("#" + categoryLabel);
           preset = this.parent.querySelector("#" + presetLabel);
         }
       });
-
     });
 
     this.activeCategory = category;
     this.activePreset = preset;
   },
 
   destroy: function() {
     this._removeEvents();
@@ -783,17 +791,17 @@ TimingFunctionPreviewWidget.prototype = 
     this.autoRestartAnimation = setTimeout(this.restartAnimation.bind(this),
       this.PREVIEW_DURATION * 2);
   }
 };
 
 // Helpers
 
 function getPadding(padding) {
-  let p = typeof padding === 'number'? [padding] : padding;
+  let p = typeof padding === "number" ? [padding] : padding;
 
   if (p.length === 1) {
     p[1] = p[0];
   }
 
   if (p.length === 2) {
     p[2] = p[0];
   }
--- a/devtools/client/shared/widgets/Tooltip.js
+++ b/devtools/client/shared/widgets/Tooltip.js
@@ -825,17 +825,17 @@ Tooltip.prototype = {
   },
 
   /**
    * Fill the tooltip with a new instance of the cubic-bezier widget
    * initialized with the given value, and return a promise that resolves to
    * the instance of the widget
    */
   setCubicBezierContent: function(bezier) {
-    let dimensions = {width: "410", height: "360"};
+    let dimensions = {width: "500", height: "360"};
     let panel = this.panel;
     return this.setIFrameContent(dimensions, CUBIC_BEZIER_FRAME).then(onLoaded);
 
     function onLoaded(iframe) {
       let win = iframe.contentWindow.wrappedJSObject;
       let def = promise.defer();
       let container = win.document.getElementById("container");
       let widget = new CubicBezierWidget(container, bezier);
--- a/devtools/client/shared/widgets/cubic-bezier-frame.xhtml
+++ b/devtools/client/shared/widgets/cubic-bezier-frame.xhtml
@@ -10,17 +10,17 @@
   <link rel="stylesheet" href="chrome://devtools/skin/common.css" type="text/css"/>
   <link rel="stylesheet" href="chrome://devtools/content/shared/widgets/cubic-bezier.css" type="text/css"/>
   <script type="application/javascript;version=1.8" src="chrome://devtools/content/shared/theme-switching.js"/>
   <style>
     html, body {
       margin: 0;
       padding: 0;
       overflow: hidden;
-      width: 410px;
+      width: 500px;
       height: 370px;
     }
   </style>
 </head>
 <body role="application">
   <div id="container"></div>
 </body>
 </html>
--- a/devtools/client/shared/widgets/cubic-bezier.css
+++ b/devtools/client/shared/widgets/cubic-bezier.css
@@ -2,17 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* Based on Lea Verou www.cubic-bezier.com
    See https://github.com/LeaVerou/cubic-bezier */
 
 #container {
   display: flex;
-  width: 410px;
+  width: 500px;
   height: 370px;
   flex-direction: row-reverse;
   overflow: hidden;
 }
 
 .display-wrap {
   width: 50%;
   height: 100%;
@@ -74,17 +74,17 @@ canvas#curve {
   background: linear-gradient(-45deg, transparent 49.7%, #eee 49.7%, #eee 50.3%, transparent 50.3%) center no-repeat;
 }
 
 /* Timing Function Preview Widget */
 
 .timing-function-preview {
   position: absolute;
   bottom: 20px;
-  right: 27px;
+  right: 45px;
   width: 150px;
 }
 
 .timing-function-preview .scale {
   position: absolute;
   top: 6px;
   left: 0;
   z-index: 1;
@@ -158,16 +158,18 @@ canvas#curve {
 .category {
   padding: 5px 0px;
   width: 33.33%;
   text-align: center;
   text-transform: capitalize;
   border-right: 1px solid var(--theme-splitter-color);
   cursor: default;
   color: var(--theme-body-color);
+  text-overflow: ellipsis;
+  overflow: hidden;
 }
 
 .category:hover {
   background-color: var(--theme-tab-toolbar-background);
 }
 
 .active-category {
   background-color: var(--theme-selection-background);
@@ -211,20 +213,22 @@ canvas#curve {
   margin: 0 auto;
 }
 
 .theme-dark .preset canvas {
   border-color: #444e58;
 }
 
 .preset p {
-  font-size: 0.9em;
+  font-size: 80%;
   margin: 2px auto 0px auto;
   color: var(--theme-body-color-alt);
   text-transform: capitalize;
+  text-overflow: ellipsis;
+  overflow: hidden;
 }
 
 .active-preset p, .active-preset:hover p {
   color: var(--theme-body-color);
 }
 
 .preset:hover canvas {
   border-color: var(--theme-selection-background);
--- a/devtools/server/actors/webconsole.js
+++ b/devtools/server/actors/webconsole.js
@@ -1529,18 +1529,17 @@ WebConsoleActor.prototype =
    * @return object
    *         The object that can be sent to the remote client.
    */
   prepareConsoleMessageForRemote:
   function WCA_prepareConsoleMessageForRemote(aMessage, aUseObjectGlobal = true)
   {
     let result = WebConsoleUtils.cloneObject(aMessage);
 
-    result.workerType = CONSOLE_WORKER_IDS.indexOf(result.innerID) == -1
-                          ? 'none' : result.innerID;
+    result.workerType = WebConsoleUtils.getWorkerType(result) || "none";
 
     delete result.wrappedJSObject;
     delete result.ID;
     delete result.innerID;
     delete result.consoleID;
 
     result.arguments = Array.map(aMessage.arguments || [], (aObj) => {
       let dbgObj = this.makeDebuggeeValue(aObj, aUseObjectGlobal);
--- a/devtools/shared/webconsole/test/chrome.ini
+++ b/devtools/shared/webconsole/test/chrome.ini
@@ -1,26 +1,28 @@
 [DEFAULT]
 tags = devtools
 skip-if = buildapp == 'b2g'
 support-files =
   common.js
   data.json
   data.json^headers^
+  helper_serviceworker.js
   network_requests_iframe.html
   sandboxed_iframe.html
   console-test-worker.js
 
 [test_basics.html]
 [test_bug819670_getter_throws.html]
 [test_cached_messages.html]
 [test_commands_other.html]
 [test_commands_registration.html]
 [test_consoleapi.html]
 [test_consoleapi_innerID.html]
+[test_console_serviceworker.html]
 [test_console_styling.html]
 [test_file_uri.html]
 [test_reflow.html]
 [test_jsterm.html]
 [test_jsterm_autocomplete.html]
 [test_jsterm_cd_iframe.html]
 [test_jsterm_last_result.html]
 [test_jsterm_queryselector.html]
new file mode 100644
--- /dev/null
+++ b/devtools/shared/webconsole/test/helper_serviceworker.js
@@ -0,0 +1,2 @@
+
+console.log("Hello from serviceworker");
new file mode 100644
--- /dev/null
+++ b/devtools/shared/webconsole/test/test_console_serviceworker.html
@@ -0,0 +1,95 @@
+<!DOCTYPE HTML>
+<html lang="en">
+<head>
+  <meta charset="utf8">
+  <title>Test for the Console API and Service Workers</title>
+  <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript;version=1.8" src="common.js"></script>
+  <!-- Any copyright is dedicated to the Public Domain.
+     - http://creativecommons.org/publicdomain/zero/1.0/ -->
+</head>
+<body>
+<p>Test for the Console API and Service Workers</p>
+
+<script class="testbody" type="text/javascript;version=1.8">
+SimpleTest.waitForExplicitFinish();
+
+let SERVICE_WORKER_URL = "https://example.com/chrome/devtools/shared/webconsole/test/helper_serviceworker.js";
+let FRAME_URL = "https://example.com/chrome/devtools/shared/webconsole/test/sandboxed_iframe.html";
+
+let swClosed = new Promise(() => {});
+let expectedConsoleCalls = [
+    {
+      level: "log",
+      filename: /helper_serviceworker/,
+      arguments: ["Hello from serviceworker"],
+    }
+];
+let consoleCalls = [];
+
+let startTest = Task.async(function*() {
+  removeEventListener("load", startTest);
+
+  yield new Promise(resolve => {
+    SpecialPowers.pushPrefEnv({"set": [
+      ["devtools.webconsole.filter.serviceworkers", true]
+    ]}, resolve);
+  });
+
+  attachConsoleToTab(["ConsoleAPI"], onAttach);
+});
+addEventListener("load", startTest);
+
+function onAttach(state, response) {
+  onConsoleAPICall = onConsoleAPICall.bind(null, state);
+  state.dbgClient.addListener("consoleAPICall", onConsoleAPICall);
+
+  info("Loading a ServiceWorker that will use console API");
+  swClosed = new Promise(resolve => {
+    let iframe = document.createElement("iframe");
+    iframe.onload = function() {
+      let win = iframe.contentWindow;
+      info("Registering the service worker");
+      win.navigator.serviceWorker.register(SERVICE_WORKER_URL).then(swr => {
+
+        info("Service worker registered.  Unregistering");
+        swr.unregister().then(() => {
+          resolve();
+        });
+      }, error => {
+        info("Error registering service worker: " + error);
+      });
+    };
+    iframe.src = FRAME_URL;
+
+    document.body.appendChild(iframe);
+  });
+}
+
+function onConsoleAPICall(state, type, packet) {
+  info("received message level: " + packet.message.level);
+  is(packet.from, state.actor, "console API call actor");
+
+  consoleCalls.push(packet.message);
+  if (consoleCalls.length != expectedConsoleCalls.length) {
+    return;
+  }
+
+  state.dbgClient.removeListener("consoleAPICall", onConsoleAPICall);
+
+  expectedConsoleCalls.forEach(function(aMessage, aIndex) {
+    info("checking received console call #" + aIndex);
+    checkConsoleAPICall(consoleCalls[aIndex], expectedConsoleCalls[aIndex]);
+  });
+
+  consoleCalls = [];
+
+  closeDebugger(state, function() {
+    swClosed.then(() => {
+      SimpleTest.finish();
+    });
+  });
+}
+</script>
+</body>
+</html>
--- a/devtools/shared/webconsole/utils.js
+++ b/devtools/shared/webconsole/utils.js
@@ -47,16 +47,27 @@ var WebConsoleUtils = {
   {
     let str = Cc["@mozilla.org/supports-string;1"].
               createInstance(Ci.nsISupportsString);
     str.data = aString;
     return str;
   },
 
   /**
+   * Given a message, return one of CONSOLE_WORKER_IDS if it matches
+   * one of those.
+   *
+   * @return string
+   */
+  getWorkerType: function(message) {
+    let id = message ? message.innerID : null;
+    return CONSOLE_WORKER_IDS[CONSOLE_WORKER_IDS.indexOf(id)] || null;
+  },
+
+  /**
    * Clone an object.
    *
    * @param object aObject
    *        The object you want cloned.
    * @param boolean aRecursive
    *        Tells if you want to dig deeper into the object, to clone
    *        recursively.
    * @param function [aFilter]
@@ -111,16 +122,39 @@ var WebConsoleUtils = {
     let style = win.getComputedStyle(aFrom);
     aTo.style.fontFamily = style.getPropertyCSSValue("font-family").cssText;
     aTo.style.fontSize = style.getPropertyCSSValue("font-size").cssText;
     aTo.style.fontWeight = style.getPropertyCSSValue("font-weight").cssText;
     aTo.style.fontStyle = style.getPropertyCSSValue("font-style").cssText;
   },
 
   /**
+   * Recursively gather a list of window locations given
+   * a top level window.
+   *
+   * @param nsIDOMWindow aWindow
+   * @return Array
+   *         list of window locations as strings
+   */
+  getLocationsForFrames: function(aWindow)
+  {
+    let location = aWindow.location.toString();
+    let locations = [location];
+
+    if (aWindow.frames) {
+      for (let i = 0; i < aWindow.frames.length; i++) {
+        let frame = aWindow.frames[i];
+        locations = locations.concat(this.getLocationsForFrames(frame));
+      }
+    }
+
+    return locations;
+  },
+
+  /**
    * Gets the ID of the inner window of this DOM window.
    *
    * @param nsIDOMWindow aWindow
    * @return integer
    *         Inner ID for the given aWindow.
    */
   getInnerWindowId: function WCU_getInnerWindowId(aWindow)
   {
@@ -930,31 +964,64 @@ ConsoleAPIListener.prototype =
     if (!this.owner) {
       return;
     }
 
     // Here, wrappedJSObject is not a security wrapper but a property defined
     // by the XPCOM component which allows us to unwrap the XPCOM interface and
     // access the underlying JSObject.
     let apiMessage = aMessage.wrappedJSObject;
-    if (this.window && CONSOLE_WORKER_IDS.indexOf(apiMessage.innerID) == -1) {
-      let msgWindow = Services.wm.getCurrentInnerWindowWithId(apiMessage.innerID);
-      if (!msgWindow || !isWindowIncluded(this.window, msgWindow)) {
-        // Not the same window!
-        return;
-      }
-    }
-    if (this.consoleID && apiMessage.consoleID != this.consoleID) {
+
+    if (!this.isMessageRelevant(apiMessage)) {
       return;
     }
 
     this.owner.onConsoleAPICall(apiMessage);
   },
 
   /**
+   * Given a message, return true if this window should show it and false
+   * if it should be ignored.
+   *
+   * @param message
+   *        The message from the Storage Service
+   * @return bool
+   *         Do we care about this message?
+   */
+  isMessageRelevant: function(message) {
+    let workerType = WebConsoleUtils.getWorkerType(message);
+
+    if (this.window && workerType === "ServiceWorker") {
+      // For messages from Service Workers, message.ID is the
+      // scope, which can be used to determine whether it's controlling
+      // a window.
+      let scope = message.ID;
+      let locations = WebConsoleUtils.getLocationsForFrames(this.window);
+
+      if (!locations.some(loc => loc.startsWith(scope))) {
+        return false;
+      }
+    }
+
+    if (this.window && !workerType) {
+      let msgWindow = Services.wm.getCurrentInnerWindowWithId(message.innerID);
+      if (!msgWindow || !isWindowIncluded(this.window, msgWindow)) {
+        // Not the same window!
+        return false;
+      }
+    }
+
+    if (this.consoleID && message.consoleID !== this.consoleID) {
+      return false;
+    }
+
+    return true;
+  },
+
+  /**
    * Get the cached messages for the current inner window and its (i)frames.
    *
    * @param boolean [aIncludePrivate=false]
    *        Tells if you want to also retrieve messages coming from private
    *        windows. Defaults to false.
    * @return array
    *         The array of cached messages.
    */
@@ -974,19 +1041,19 @@ ConsoleAPIListener.prototype =
         messages = messages.concat(ConsoleAPIStorage.getEvents(id));
       });
     }
 
     CONSOLE_WORKER_IDS.forEach((id) => {
       messages = messages.concat(ConsoleAPIStorage.getEvents(id));
     });
 
-    if (this.consoleID) {
-      messages = messages.filter((m) => m.consoleID == this.consoleID);
-    }
+    messages = messages.filter(msg => {
+      return this.isMessageRelevant(msg);
+    });
 
     if (aIncludePrivate) {
       return messages;
     }
 
     return messages.filter((m) => !m.private);
   },
 
--- a/docshell/test/browser/browser.ini
+++ b/docshell/test/browser/browser.ini
@@ -42,70 +42,50 @@ support-files =
   browser_timelineMarkers-frame-03.js
   browser_timelineMarkers-frame-04.js
   browser_timelineMarkers-frame-05.js
   head.js
   frame-head.js
 
 [browser_bug1206879.js]
 [browser_bug134911.js]
-skip-if = e10s # Bug ?????? - BrowserSetForcedCharacterSet() in browser.js references docShell
 [browser_bug234628-1.js]
-skip-if = e10s # Bug ?????? - event handler checks event.target is the content document and test e10s-utils doesn't do that.
 [browser_bug234628-10.js]
-skip-if = e10s # Bug ?????? - event handler checks event.target is the content document and test e10s-utils doesn't do that.
 [browser_bug234628-11.js]
-skip-if = e10s # Bug ?????? - event handler checks event.target is the content document and test e10s-utils doesn't do that.
 [browser_bug234628-2.js]
-skip-if = e10s # Bug ?????? - event handler checks event.target is the content document and test e10s-utils doesn't do that.
 [browser_bug234628-3.js]
-skip-if = e10s # Bug ?????? - event handler checks event.target is the content document and test e10s-utils doesn't do that.
 [browser_bug234628-4.js]
-skip-if = e10s # Bug ?????? - event handler checks event.target is the content document and test e10s-utils doesn't do that.
 [browser_bug234628-5.js]
-skip-if = e10s # Bug ?????? - event handler checks event.target is the content document and test e10s-utils doesn't do that.
 [browser_bug234628-6.js]
-skip-if = e10s # Bug ?????? - event handler checks event.target is the content document and test e10s-utils doesn't do that.
 [browser_bug234628-7.js]
-skip-if = e10s # Bug ?????? - event handler checks event.target is the content document and test e10s-utils doesn't do that.
 [browser_bug234628-8.js]
-skip-if = e10s # Bug ?????? - event handler checks event.target is the content document and test e10s-utils doesn't do that.
 [browser_bug234628-9.js]
-skip-if = e10s # Bug ?????? - event handler checks event.target is the content document and test e10s-utils doesn't do that.
 [browser_bug349769.js]
 skip-if = e10s # Bug ?????? - test touches content (newBrowser.contentDocument.nodePrincipal)
 [browser_bug388121-1.js]
 skip-if = e10s # Bug ?????? - test touches content (newBrowser.contentDocument.nodePrincipal)
 [browser_bug388121-2.js]
-skip-if = e10s # Bug ?????? - test touches content (newBrowser.contentDocument.nodePrincipal)
 [browser_bug420605.js]
-skip-if = e10s # Bug 933103 - mochitest's EventUtils.synthesizeMouse functions not e10s friendly
 [browser_bug422543.js]
-skip-if = e10s # Bug ?????? - obscure test failures (shistory has a new entry - Got initial, expected newentry)
+skip-if = e10s # Bug 1220927 - Test tries to do addSHistoryListener on content.
 [browser_bug441169.js]
 skip-if = buildapp == 'mulet'
 [browser_bug503832.js]
 skip-if = buildapp == 'mulet'
 [browser_bug554155.js]
 [browser_bug655270.js]
-skip-if = e10s # Bug ?????? - PlacesUtils.history.addObserver notifications don't seem to fire
 [browser_bug655273.js]
 [browser_bug670318.js]
-skip-if = e10s # Bug 916974 - browser.sessionHistory is null
+skip-if = e10s # Bug 1220927 - Test tries to do addSHistoryListener on content.
 [browser_bug673467.js]
-skip-if = e10s # Bug ?????? - test touches content (adds event listener to content document's iframe)
 [browser_bug852909.js]
-skip-if = e10s # Bug ?????? - event handler checks event.target is the content document and test e10s-utils doesn't do that.
 [browser_bug92473.js]
-skip-if = e10s # Bug ?????? - event handler checks event.target is the content document and test e10s-utils doesn't do that.
 [browser_uriFixupIntegration.js]
 [browser_loadDisallowInherit.js]
-skip-if = e10s
 [browser_loadURI.js]
-skip-if = e10s # Bug ?????? - event handler checks event.target is the content document and test e10s-utils doesn't do that.
 [browser_multiple_pushState.js]
 [browser_onbeforeunload_navigation.js]
 skip-if = e10s
 [browser_search_notification.js]
 [browser_timelineMarkers-01.js]
 [browser_timelineMarkers-02.js]
 [browser_timelineMarkers-03.js]
 [browser_timelineMarkers-04.js]
--- a/docshell/test/browser/browser_bug852909.js
+++ b/docshell/test/browser/browser_bug852909.js
@@ -8,26 +8,26 @@ function test() {
 }
 
 function image(event) {
   if (event.target != gBrowser.contentDocument) {
     return;
   }
   gBrowser.selectedBrowser.removeEventListener("load", image, true);
 
-  ok(!gBrowser.docShell.mayEnableCharacterEncodingMenu, "Docshell should say the menu should be disabled for images.");
+  ok(!gBrowser.selectedTab.mayEnableCharacterEncodingMenu, "Docshell should say the menu should be disabled for images.");
 
   gBrowser.removeCurrentTab();
   gBrowser.selectedTab = gBrowser.addTab(rootDir + "file_bug852909.pdf");
   gBrowser.selectedBrowser.addEventListener("load", pdf, true);
 }
 
 function pdf(event) {
   if (event.target != gBrowser.contentDocument) {
     return;
   }
   gBrowser.selectedBrowser.removeEventListener("load", pdf, true);
 
-  ok(!gBrowser.docShell.mayEnableCharacterEncodingMenu, "Docshell should say the menu should be disabled for PDF.js.");
+  ok(!gBrowser.selectedTab.mayEnableCharacterEncodingMenu, "Docshell should say the menu should be disabled for PDF.js.");
 
   gBrowser.removeCurrentTab();
   finish();
 }
--- a/dom/asmjscache/test/file_slow.js
+++ b/dom/asmjscache/test/file_slow.js
@@ -13,17 +13,17 @@ function f2(stdlib, foreign, buffer) {
         for (; (i|0) < (n|0); i=(i+1)|0)
             sum = (sum + (i32[(i<<2)>>2]|0))|0;
         return sum|0;
     }
     return main;
 }
 if (this.jsFuns)
     ok(jsFuns.isAsmJSModule(f2), "f2 is an asm.js module");
-var i32 = new Int32Array(1024);
+var i32 = new Int32Array(16384); // Smallest allowed buffer size is 64KBy
 for (var i = 0; i < i32.length; i++)
     i32[i] = i;
 var f2Main = f2(this, null, i32.buffer);
 if (this.jsFuns)
     ok(jsFuns.isAsmJSFunction(f2Main), "f2.main is an asm.js function");
 if (f2Main(4) !== 6)
     throw "f2Main(4)";
 if (f2Main(100) !== 4950)
--- a/dom/base/Console.cpp
+++ b/dom/base/Console.cpp
@@ -493,26 +493,30 @@ private:
     if (aOuterWindow) {
       mCallData->SetIDs(aOuterWindow->WindowID(), aInnerWindow->WindowID());
     } else {
       ConsoleStackEntry frame;
       if (mCallData->mTopStackFrame) {
         frame = *mCallData->mTopStackFrame;
       }
 
-      nsString id;
+      nsString id = frame.mFilename;
+      nsString innerID;
       if (mWorkerPrivate->IsSharedWorker()) {
-        id = NS_LITERAL_STRING("SharedWorker");
+        innerID = NS_LITERAL_STRING("SharedWorker");
       } else if (mWorkerPrivate->IsServiceWorker()) {
-        id = NS_LITERAL_STRING("ServiceWorker");
+        innerID = NS_LITERAL_STRING("ServiceWorker");
+        // Use scope as ID so the webconsole can decide if the message should
+        // show up per tab
+        id.AssignWithConversion(mWorkerPrivate->WorkerName());
       } else {
-        id = NS_LITERAL_STRING("Worker");
+        innerID = NS_LITERAL_STRING("Worker");
       }
 
-      mCallData->SetIDs(frame.mFilename, id);
+      mCallData->SetIDs(id, innerID);
     }
 
     // Now we could have the correct window (if we are not window-less).
     mClonedData.mParent = aInnerWindow;
 
     ProcessCallData(aCx);
     mCallData->CleanupJSObjects();
 
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -327,23 +327,16 @@ nsIdentifierMapEntry::RemoveContentChang
     return;
   ChangeCallback cc = { aCallback, aData, aForImage };
   mChangeCallbacks->RemoveEntry(cc);
   if (mChangeCallbacks->Count() == 0) {
     mChangeCallbacks = nullptr;
   }
 }
 
-// XXX Workaround for bug 980560 to maintain the existing broken semantics
-template<>
-struct nsIStyleRule::COMTypeInfo<css::Rule, void> {
-  static const nsIID kIID;
-};
-const nsIID nsIStyleRule::COMTypeInfo<css::Rule, void>::kIID = NS_ISTYLE_RULE_IID;
-
 namespace mozilla {
 namespace dom {
 
 static PLDHashOperator
 CustomDefinitionsTraverse(CustomElementHashKey* aKey,
                           CustomElementDefinition* aDefinition,
                           void* aArg)
 {
@@ -2494,21 +2487,16 @@ AppendSheetsToStyleSet(nsStyleSet* aStyl
 
 void
 nsDocument::FillStyleSet(nsStyleSet* aStyleSet)
 {
   NS_PRECONDITION(aStyleSet, "Must have a style set");
   NS_PRECONDITION(aStyleSet->SheetCount(SheetType::Doc) == 0,
                   "Style set already has document sheets?");
 
-  // We could consider moving this to nsStyleSet::Init, to match its
-  // handling of the eAnimationSheet and eTransitionSheet levels.
-  aStyleSet->DirtyRuleProcessors(SheetType::PresHint);
-  aStyleSet->DirtyRuleProcessors(SheetType::StyleAttr);
-
   int32_t i;
   for (i = mStyleSheets.Count() - 1; i >= 0; --i) {
     nsIStyleSheet* sheet = mStyleSheets[i];
     if (sheet->IsApplicable()) {
       aStyleSet->AddDocStyleSheet(sheet, this);
     }
   }
 
@@ -5236,61 +5224,61 @@ nsDocument::DocumentStatesChanged(EventS
   mGotDocumentState &= ~aStateMask;
   mDocumentState &= ~aStateMask;
 
   NS_DOCUMENT_NOTIFY_OBSERVERS(DocumentStatesChanged, (this, aStateMask));
 }
 
 void
 nsDocument::StyleRuleChanged(nsIStyleSheet* aSheet,
-                             nsIStyleRule* aOldStyleRule,
-                             nsIStyleRule* aNewStyleRule)
+                             css::Rule* aOldStyleRule,
+                             css::Rule* aNewStyleRule)
 {
   NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleChanged,
                                (this, aSheet,
                                 aOldStyleRule, aNewStyleRule));
 
   if (StyleSheetChangeEventsEnabled()) {
-    nsCOMPtr<css::Rule> rule = do_QueryInterface(aNewStyleRule);
     DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent,
                                "StyleRuleChanged",
                                mRule,
-                               rule ? rule->GetDOMRule() : nullptr);
+                               aNewStyleRule ? aNewStyleRule->GetDOMRule()
+                                             : nullptr);
   }
 }
 
 void
 nsDocument::StyleRuleAdded(nsIStyleSheet* aSheet,
-                           nsIStyleRule* aStyleRule)
+                           css::Rule* aStyleRule)
 {
   NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleAdded,
                                (this, aSheet, aStyleRule));
 
   if (StyleSheetChangeEventsEnabled()) {
-    nsCOMPtr<css::Rule> rule = do_QueryInterface(aStyleRule);
     DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent,
                                "StyleRuleAdded",
                                mRule,
-                               rule ? rule->GetDOMRule() : nullptr);
+                               aStyleRule ? aStyleRule->GetDOMRule()
+                                          : nullptr);
   }
 }
 
 void
 nsDocument::StyleRuleRemoved(nsIStyleSheet* aSheet,
-                             nsIStyleRule* aStyleRule)
+                             css::Rule* aStyleRule)
 {
   NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleRemoved,
                                (this, aSheet, aStyleRule));
 
   if (StyleSheetChangeEventsEnabled()) {
-    nsCOMPtr<css::Rule> rule = do_QueryInterface(aStyleRule);
     DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent,
                                "StyleRuleRemoved",
                                mRule,
-                               rule ? rule->GetDOMRule() : nullptr);
+                               aStyleRule ? aStyleRule->GetDOMRule()
+                                          : nullptr);
   }
 }
 
 #undef DO_STYLESHEET_NOTIFICATION
 
 already_AddRefed<AnonymousContent>
 nsIDocument::InsertAnonymousContent(Element& aElement, ErrorResult& aRv)
 {
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -895,22 +895,22 @@ public:
 
   virtual void ContentStateChanged(nsIContent* aContent,
                                    mozilla::EventStates aStateMask)
                                      override;
   virtual void DocumentStatesChanged(
                  mozilla::EventStates aStateMask) override;
 
   virtual void StyleRuleChanged(nsIStyleSheet* aStyleSheet,
-                                nsIStyleRule* aOldStyleRule,
-                                nsIStyleRule* aNewStyleRule) override;
+                                mozilla::css::Rule* aOldStyleRule,
+                                mozilla::css::Rule* aNewStyleRule) override;
   virtual void StyleRuleAdded(nsIStyleSheet* aStyleSheet,
-                              nsIStyleRule* aStyleRule) override;
+                              mozilla::css::Rule* aStyleRule) override;
   virtual void StyleRuleRemoved(nsIStyleSheet* aStyleSheet,
-                                nsIStyleRule* aStyleRule) override;
+                                mozilla::css::Rule* aStyleRule) override;
 
   virtual void FlushPendingNotifications(mozFlushType aType) override;
   virtual void FlushExternalResources(mozFlushType aType) override;
   virtual void SetXMLDeclaration(const char16_t *aVersion,
                                  const char16_t *aEncoding,
                                  const int32_t aStandalone) override;
   virtual void GetXMLDeclaration(nsAString& aVersion,
                                  nsAString& aEncoding,
--- a/dom/base/nsDocumentEncoder.cpp
+++ b/dom/base/nsDocumentEncoder.cpp
@@ -46,16 +46,17 @@
 #include "nsTArray.h"
 #include "nsIFrame.h"
 #include "nsStringBuffer.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/ShadowRoot.h"
 #include "mozilla/dom/EncodingUtils.h"
 #include "nsContainerFrame.h"
 #include "nsBlockFrame.h"
+#include "nsComputedDOMStyle.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 nsresult NS_NewDomSelection(nsISelection **aDomSelection);
 
 enum nsRangeIterationDirection {
   kDirectionOut = -1,
@@ -1423,40 +1424,49 @@ nsHTMLCopyEncoder::SetSelection(nsISelec
   // here that multi-range selections are table cell selections, in which case
   // the common parent is somewhere in the table and we don't really care where.
   nsresult rv = aSelection->GetRangeAt(0, getter_AddRefs(range));
   NS_ENSURE_SUCCESS(rv, rv);
   if (!range)
     return NS_ERROR_NULL_POINTER;
   range->GetCommonAncestorContainer(getter_AddRefs(commonParent));
 
-  // Thunderbird's msg compose code abuses the HTML copy encoder and gets
-  // confused if mIsTextWidget ends up becoming true, so for now we skip
-  // this logic in Thunderbird.
-#ifndef MOZ_THUNDERBIRD
   for (nsCOMPtr<nsIContent> selContent(do_QueryInterface(commonParent));
        selContent;
        selContent = selContent->GetParent())
   {
     // checking for selection inside a plaintext form widget
     if (selContent->IsAnyOfHTMLElements(nsGkAtoms::input, nsGkAtoms::textarea))
     {
       mIsTextWidget = true;
       break;
     }
+#ifdef MOZ_THUNDERBIRD
+    else if (selContent->IsElement()) {
+      nsRefPtr<nsStyleContext> styleContext =
+        nsComputedDOMStyle::GetStyleContextForElementNoFlush(
+          selContent->AsElement(), nullptr, nullptr);
+      if (styleContext) {
+        const nsStyleText* textStyle = styleContext->StyleText();
+        if (textStyle->mWhiteSpace == NS_STYLE_WHITESPACE_PRE_WRAP) {
+          mIsTextWidget = true;
+        }
+      }
+    }
+    break;
+#endif
   }
 
   // normalize selection if we are not in a widget
   if (mIsTextWidget) 
   {
     mSelection = aSelection;
     mMimeType.AssignLiteral("text/plain");
     return NS_OK;
   }
-#endif
 
   // also consider ourselves in a text widget if we can't find an html document
   nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(mDocument);
   if (!(htmlDoc && mDocument->IsHTMLDocument())) {
     mIsTextWidget = true;
     mSelection = aSelection;
     // mMimeType is set to text/plain when encoding starts.
     return NS_OK;
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -67,17 +67,16 @@ class nsILoadContext;
 class nsIObjectLoadingContent;
 class nsIObserver;
 class nsIPresShell;
 class nsIPrincipal;
 class nsIRequest;
 class nsIRunnable;
 class nsIStreamListener;
 class nsIStructuredCloneContainer;
-class nsIStyleRule;
 class nsIStyleSheet;
 class nsIURI;
 class nsIVariant;
 class nsLocation;
 class nsViewManager;
 class nsPresContext;
 class nsRange;
 class nsScriptLoader;
@@ -96,16 +95,17 @@ class ErrorResult;
 class EventStates;
 class PendingAnimationTracker;
 class SVGAttrAnimationRuleProcessor;
 template<typename> class OwningNonNull;
 
 namespace css {
 class Loader;
 class ImageLoader;
+class Rule;
 } // namespace css
 
 namespace gfx {
 class VRHMDInfo;
 } // namespace gfx
 
 namespace dom {
 class AnonymousContent;
@@ -151,18 +151,18 @@ template<typename> class Sequence;
 
 template<typename, typename> class CallbackObjectHolder;
 typedef CallbackObjectHolder<NodeFilter, nsIDOMNodeFilter> NodeFilterHolder;
 
 } // namespace dom
 } // namespace mozilla
 
 #define NS_IDOCUMENT_IID \
-{ 0x5f51e18c, 0x9e0e, 0x4dc0, \
-  { 0x9f, 0x08, 0x7a, 0x32, 0x65, 0x52, 0xea, 0x11 } }
+{ 0x4307abe8, 0x5386, 0x4024, \
+  { 0xa2, 0xfe, 0x4a, 0x80, 0xe8, 0x47, 0x46, 0x90 } }
 
 // Enum for requesting a particular type of document when creating a doc
 enum DocumentFlavor {
   DocumentFlavorLegacyGuess, // compat with old code until made HTML5-compliant
   DocumentFlavorHTML, // HTMLDocument with HTMLness bit set to true
   DocumentFlavorSVG, // SVGDocument
   DocumentFlavorPlain, // Just a Document
 };
@@ -1268,22 +1268,22 @@ public:
   // Notify that a document state has changed.
   // This should only be called by callers whose state is also reflected in the
   // implementation of nsDocument::GetDocumentState.
   virtual void DocumentStatesChanged(mozilla::EventStates aStateMask) = 0;
 
   // Observation hooks for style data to propagate notifications
   // to document observers
   virtual void StyleRuleChanged(nsIStyleSheet* aStyleSheet,
-                                nsIStyleRule* aOldStyleRule,
-                                nsIStyleRule* aNewStyleRule) = 0;
+                                mozilla::css::Rule* aOldStyleRule,
+                                mozilla::css::Rule* aNewStyleRule) = 0;
   virtual void StyleRuleAdded(nsIStyleSheet* aStyleSheet,
-                              nsIStyleRule* aStyleRule) = 0;
+                              mozilla::css::Rule* aStyleRule) = 0;
   virtual void StyleRuleRemoved(nsIStyleSheet* aStyleSheet,
-                                nsIStyleRule* aStyleRule) = 0;
+                                mozilla::css::Rule* aStyleRule) = 0;
 
   /**
    * Flush notifications for this document and its parent documents
    * (since those may affect the layout of this one).
    */
   virtual void FlushPendingNotifications(mozFlushType aType) = 0;
 
   /**
--- a/dom/base/nsIDocumentObserver.h
+++ b/dom/base/nsIDocumentObserver.h
@@ -7,22 +7,27 @@
 #define nsIDocumentObserver_h___
 
 #include "mozilla/EventStates.h"
 #include "nsISupports.h"
 #include "nsIMutationObserver.h"
 
 class nsIContent;
 class nsIStyleSheet;
-class nsIStyleRule;
 class nsIDocument;
 
+namespace mozilla {
+namespace css {
+class Rule;
+} // namespace css
+} // namespace mozilla
+
 #define NS_IDOCUMENT_OBSERVER_IID \
-{ 0x900bc4bc, 0x8b6c, 0x4cba, \
- { 0x82, 0xfa, 0x56, 0x8a, 0x80, 0xff, 0xfd, 0x3e } }
+{ 0xce1d53d0, 0x4739, 0x44e5, \
+  { 0xb4, 0xae, 0x60, 0xe8, 0x82, 0xcb, 0x73, 0x1b } }
 
 typedef uint32_t nsUpdateType;
 
 #define UPDATE_CONTENT_MODEL 0x00000001
 #define UPDATE_STYLE         0x00000002
 #define UPDATE_ALL (UPDATE_CONTENT_MODEL | UPDATE_STYLE)
 
 // Document observer interface
@@ -151,48 +156,48 @@ public:
    * @param aOldStyleRule The rule being removed.  This rule may not be
    *                      fully valid anymore -- however, it can still
    *                      be used for pointer comparison and
    *                      |QueryInterface|.
    * @param aNewStyleRule The rule being added.
    */
   virtual void StyleRuleChanged(nsIDocument *aDocument,
                                 nsIStyleSheet* aStyleSheet,
-                                nsIStyleRule* aOldStyleRule,
-                                nsIStyleRule* aNewStyleRule) = 0;
+                                mozilla::css::Rule* aOldStyleRule,
+                                mozilla::css::Rule* aNewStyleRule) = 0;
 
   /**
    * A StyleRule has just been added to a style sheet.
    * This method is called automatically when the rule gets
    * added to the sheet. The style sheet passes this
    * notification to the document. The notification is passed on 
    * to all of the document observers.
    *
    * @param aDocument The document being observed
    * @param aStyleSheet the StyleSheet that has been modified
    * @param aStyleRule the rule that was added
    */
   virtual void StyleRuleAdded(nsIDocument *aDocument,
                               nsIStyleSheet* aStyleSheet,
-                              nsIStyleRule* aStyleRule) = 0;
+                              mozilla::css::Rule* aStyleRule) = 0;
 
   /**
    * A StyleRule has just been removed from a style sheet.
    * This method is called automatically when the rule gets
    * removed from the sheet. The style sheet passes this
    * notification to the document. The notification is passed on 
    * to all of the document observers.
    *
    * @param aDocument The document being observed
    * @param aStyleSheet the StyleSheet that has been modified
    * @param aStyleRule the rule that was removed
    */
   virtual void StyleRuleRemoved(nsIDocument *aDocument,
                                 nsIStyleSheet* aStyleSheet,
-                                nsIStyleRule* aStyleRule) = 0;
+                                mozilla::css::Rule* aStyleRule) = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocumentObserver, NS_IDOCUMENT_OBSERVER_IID)
 
 #define NS_DECL_NSIDOCUMENTOBSERVER_BEGINUPDATE                              \
     virtual void BeginUpdate(nsIDocument* aDocument,                         \
                              nsUpdateType aUpdateType) override;
 
@@ -227,28 +232,28 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocumen
 #define NS_DECL_NSIDOCUMENTOBSERVER_STYLESHEETAPPLICABLESTATECHANGED         \
     virtual void StyleSheetApplicableStateChanged(nsIDocument* aDocument,    \
                                                   nsIStyleSheet* aStyleSheet,\
                                                   bool aApplicable) override;
 
 #define NS_DECL_NSIDOCUMENTOBSERVER_STYLERULECHANGED                         \
     virtual void StyleRuleChanged(nsIDocument* aDocument,                    \
                                   nsIStyleSheet* aStyleSheet,                \
-                                  nsIStyleRule* aOldStyleRule,               \
-                                  nsIStyleRule* aNewStyleRule) override;
+                                  mozilla::css::Rule* aOldStyleRule,         \
+                                  mozilla::css::Rule* aNewStyleRule) override;
 
 #define NS_DECL_NSIDOCUMENTOBSERVER_STYLERULEADDED                           \
     virtual void StyleRuleAdded(nsIDocument* aDocument,                      \
                                 nsIStyleSheet* aStyleSheet,                  \
-                                nsIStyleRule* aStyleRule) override;
+                                mozilla::css::Rule* aStyleRule) override;
 
 #define NS_DECL_NSIDOCUMENTOBSERVER_STYLERULEREMOVED                         \
     virtual void StyleRuleRemoved(nsIDocument* aDocument,                    \
                                   nsIStyleSheet* aStyleSheet,                \
-                                  nsIStyleRule* aStyleRule) override;
+                                  mozilla::css::Rule* aStyleRule) override;
 
 #define NS_DECL_NSIDOCUMENTOBSERVER                                          \
     NS_DECL_NSIDOCUMENTOBSERVER_BEGINUPDATE                                  \
     NS_DECL_NSIDOCUMENTOBSERVER_ENDUPDATE                                    \
     NS_DECL_NSIDOCUMENTOBSERVER_BEGINLOAD                                    \
     NS_DECL_NSIDOCUMENTOBSERVER_ENDLOAD                                      \
     NS_DECL_NSIDOCUMENTOBSERVER_CONTENTSTATECHANGED                          \
     NS_DECL_NSIDOCUMENTOBSERVER_DOCUMENTSTATESCHANGED                        \
@@ -316,26 +321,26 @@ void                                    
 _class::StyleSheetApplicableStateChanged(nsIDocument* aDocument,          \
                                          nsIStyleSheet* aStyleSheet,      \
                                          bool aApplicable)              \
 {                                                                         \
 }                                                                         \
 void                                                                      \
 _class::StyleRuleChanged(nsIDocument* aDocument,                          \
                          nsIStyleSheet* aStyleSheet,                      \
-                         nsIStyleRule* aOldStyleRule,                     \
-                         nsIStyleRule* aNewStyleRule)                     \
+                         mozilla::css::Rule* aOldStyleRule,               \
+                         mozilla::css::Rule* aNewStyleRule)               \
 {                                                                         \
 }                                                                         \
 void                                                                      \
 _class::StyleRuleAdded(nsIDocument* aDocument,                            \
                        nsIStyleSheet* aStyleSheet,                        \
-                       nsIStyleRule* aStyleRule)                          \
+                       mozilla::css::Rule* aStyleRule)                    \
 {                                                                         \
 }                                                                         \
 void                                                                      \
 _class::StyleRuleRemoved(nsIDocument* aDocument,                          \
                          nsIStyleSheet* aStyleSheet,                      \
-                         nsIStyleRule* aStyleRule)                        \
+                         mozilla::css::Rule* aStyleRule)                  \
 {                                                                         \
 }
 
 #endif /* nsIDocumentObserver_h___ */
--- a/dom/base/nsINode.cpp
+++ b/dom/base/nsINode.cpp
@@ -17,16 +17,17 @@
 #include "mozilla/CORSMode.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/InternalMutationEvent.h"
 #include "mozilla/Likely.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/TimeStamp.h"
+#include "mozilla/css/StyleRule.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/Event.h"
 #include "mozilla/dom/ShadowRoot.h"
 #include "nsAttrValueOrString.h"
 #include "nsBindingManager.h"
 #include "nsCCUncollectableMarker.h"
 #include "nsContentCreatorFunctions.h"
 #include "nsContentList.h"
--- a/dom/base/test/browser.ini
+++ b/dom/base/test/browser.ini
@@ -19,10 +19,10 @@ tags = mcb
 [browser_messagemanager_targetframeloader.js]
 [browser_pagehide_on_tab_close.js]
 skip-if = e10s # this tests non-e10s behavior. it's not expected to work in e10s.
 [browser_messagemanager_unload.js]
 [browser_state_notifications.js]
 # skip-if = e10s # Bug ?????? - content-document-* notifications come while document's URI is still about:blank, but test expects real URL.
 skip-if = true # Intermittent failures - bug 987493. Restore the skip-if above once fixed
 [browser_bug1058164.js]
-skip-if = e10s # We need bug 918634 to land before this can be tested with e10s.
+skip-if = e10s # Bug 1220752.
 [browser_use_counters.js]
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -797,17 +797,16 @@ skip-if = toolkit == 'android' || e10s #
 skip-if = buildapp == 'b2g' || buildapp == 'mulet' || toolkit == 'android' || e10s # TC: Bug 1144079 - Re-enable Mulet mochitests and reftests taskcluster-specific disables.
 [test_websocket_basic.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android'
 [test_websocket_hello.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android'
 [test_websocket_permessage_deflate.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android'
 [test_x-frame-options.html]
-skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # b2g(observerservice issue) b2g-debug(observerservice issue) b2g-desktop(observerservice issue)
 [test_xbl_userdata.xhtml]
 [test_xhr_abort_after_load.html]
 skip-if = toolkit == 'android'
 [test_xhr_forbidden_headers.html]
 [test_xhr_progressevents.html]
 skip-if = toolkit == 'android'
 [test_xhr_send.html]
 [test_xhr_send_readystate.html]
--- a/dom/base/test/test_x-frame-options.html
+++ b/dom/base/test/test_x-frame-options.html
@@ -9,48 +9,16 @@
 <p id="display"></p>
 <div id="content" style="display: none">
 
 </div>
 
 <iframe style="width:100%;height:300px;" id="harness"></iframe>
 <script class="testbody" type="text/javascript">
 
-function examiner() {
-  SpecialPowers.addObserver(this, "http-on-examine-response", false);
-}
-examiner.prototype  = {
-  observe: function(subject, topic, data) {
-    subject = SpecialPowers.wrap(subject);
-    if(!subject.QueryInterface)
-      return;
-
-    if (topic == "http-on-examine-response") {
-      var chan = subject.QueryInterface(SpecialPowers.Ci.nsIHttpChannel);
-      var uri = chan.URI
-      if (!uri.path.match(/^\/tests\/content\/base\/test\/file_x-frame-options_page\.sjs/))
-        return;
-      dump(">>>> PATH: "+uri.path+"\n");
-      dump(">>> REQUEST:\n>>> "+chan.requestMethod+" "+uri.asciiSpec+"\n");
-      dump(">>> RESPONSE HEADERS:\n");
-      chan.visitResponseHeaders({
-        visitHeader: function(header, value) {
-          dump(">>> "+header+": "+value+"\n");
-        }
-      });
-    }
-  },
-
-  remove: function() {
-    SpecialPowers.removeObserver(this, "http-on-examine-response");
-  }
-}
-
-window.examiner = new examiner();
-
 var path = "/tests/dom/base/test/";
 
 var testFramesLoaded = function() {
   var harness = SpecialPowers.wrap(document).getElementById("harness");
 
   // iframe from same origin, no X-F-O header - should load
   var frame = harness.contentDocument.getElementById("control1");
   var test1 = frame.contentDocument.getElementById("test").textContent;
@@ -166,18 +134,16 @@ var testFrameInDataURI = function() {
   var html = '<iframe id="sameorigin4" src="http://mochi.test:8888/tests/dom/base/test/file_x-frame-options_page.sjs?testid=sameorigin4&xfo=sameorigin"></iframe>';
   var win = window.open();
   win.onload = function() {
     var test = win.document.getElementById("sameorigin4")
               .contentDocument.getElementById("test");
     ok(test != null, "frame under data: URL should have loaded.");
     win.close();
 
-    // finalize test
-    window.examiner.remove();
     SimpleTest.finish();
    }
   win.location.href = "data:text/html,"+html;
 }
 
 SimpleTest.waitForExplicitFinish();
 
 // load the test harness
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -2130,18 +2130,18 @@ CanvasRenderingContext2D::SetShadowColor
 
   CurrentState().shadowColor = color;
 }
 
 //
 // filters
 //
 
-static already_AddRefed<StyleRule>
-CreateStyleRule(nsINode* aNode,
+static already_AddRefed<Declaration>
+CreateDeclaration(nsINode* aNode,
   const nsCSSProperty aProp1, const nsAString& aValue1, bool* aChanged1,
   const nsCSSProperty aProp2, const nsAString& aValue2, bool* aChanged2,
   ErrorResult& error)
 {
   RefPtr<StyleRule> rule;
 
   nsIPrincipal* principal = aNode->NodePrincipal();
   nsIDocument* document = aNode->OwnerDoc();
@@ -2164,29 +2164,29 @@ CreateStyleRule(nsINode* aNode,
                          rule->GetDeclaration(), aChanged1, false);
   }
 
   if (aProp2 != eCSSProperty_UNKNOWN) {
     parser.ParseProperty(aProp2, aValue2, docURL, baseURL, principal,
                          rule->GetDeclaration(), aChanged2, false);
   }
 
-  rule->RuleMatched();
-
-  return rule.forget();
+  RefPtr<Declaration> declaration = rule->GetDeclaration();
+  declaration->SetImmutable();
+  return declaration.forget();
 }
 
-static already_AddRefed<StyleRule>
-CreateFontStyleRule(const nsAString& aFont,
-                    nsINode* aNode,
-                    bool* aOutFontChanged,
-                    ErrorResult& error)
+static already_AddRefed<Declaration>
+CreateFontDeclaration(const nsAString& aFont,
+                      nsINode* aNode,
+                      bool* aOutFontChanged,
+                      ErrorResult& error)
 {
   bool lineHeightChanged;
-  return CreateStyleRule(aNode,
+  return CreateDeclaration(aNode,
     eCSSProperty_font, aFont, aOutFontChanged,
     eCSSProperty_line_height, NS_LITERAL_STRING("normal"), &lineHeightChanged,
     error);
 }
 
 static already_AddRefed<nsStyleContext>
 GetFontParentStyleContext(Element* aElement, nsIPresShell* presShell,
                           ErrorResult& error)
@@ -2200,19 +2200,19 @@ GetFontParentStyleContext(Element* aElem
       error.Throw(NS_ERROR_FAILURE);
       return nullptr;
     }
     return result.forget();
   }
 
   // otherwise inherit from default (10px sans-serif)
   bool changed;
-  RefPtr<css::StyleRule> parentRule =
-    CreateFontStyleRule(NS_LITERAL_STRING("10px sans-serif"),
-                        presShell->GetDocument(), &changed, error);
+  RefPtr<css::Declaration> parentRule =
+    CreateFontDeclaration(NS_LITERAL_STRING("10px sans-serif"),
+                          presShell->GetDocument(), &changed, error);
 
   if (error.Failed()) {
     return nullptr;
   }
 
   nsTArray<nsCOMPtr<nsIStyleRule>> parentRules;
   parentRules.AppendElement(parentRule);
   RefPtr<nsStyleContext> result =
@@ -2221,53 +2221,52 @@ GetFontParentStyleContext(Element* aElem
   if (!result) {
     error.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
   return result.forget();
 }
 
 static bool
-PropertyIsInheritOrInitial(StyleRule* aRule, const nsCSSProperty aProperty)
-{
-  css::Declaration* declaration = aRule->GetDeclaration();
+PropertyIsInheritOrInitial(Declaration* aDeclaration, const nsCSSProperty aProperty)
+{
   // We know the declaration is not !important, so we can use
   // GetNormalBlock().
   const nsCSSValue* filterVal =
-    declaration->GetNormalBlock()->ValueFor(aProperty);
+    aDeclaration->GetNormalBlock()->ValueFor(aProperty);
   return (!filterVal || (filterVal->GetUnit() == eCSSUnit_Unset ||
                          filterVal->GetUnit() == eCSSUnit_Inherit ||
                          filterVal->GetUnit() == eCSSUnit_Initial));
 }
 
 static already_AddRefed<nsStyleContext>
 GetFontStyleContext(Element* aElement, const nsAString& aFont,
                     nsIPresShell* presShell,
                     nsAString& aOutUsedFont,
                     ErrorResult& error)
 {
   bool fontParsedSuccessfully = false;
-  RefPtr<css::StyleRule> rule =
-    CreateFontStyleRule(aFont, presShell->GetDocument(),
-                        &fontParsedSuccessfully, error);
+  RefPtr<css::Declaration> decl =
+    CreateFontDeclaration(aFont, presShell->GetDocument(),
+                          &fontParsedSuccessfully, error);
 
   if (error.Failed()) {
     return nullptr;
   }
 
   if (!fontParsedSuccessfully) {
     // We got a syntax error.  The spec says this value must be ignored.
     return nullptr;
   }
 
   // In addition to unparseable values, the spec says we need to reject
   // 'inherit' and 'initial'. The easiest way to check for this is to look
   // at font-size-adjust, which the font shorthand resets to either 'none' or
   // '-moz-system-font'.
-  if (PropertyIsInheritOrInitial(rule, eCSSProperty_font_size_adjust)) {
+  if (PropertyIsInheritOrInitial(decl, eCSSProperty_font_size_adjust)) {
     return nullptr;
   }
 
   // have to get a parent style context for inherit-like relative
   // values (2em, bolder, etc.)
   RefPtr<nsStyleContext> parentContext =
     GetFontParentStyleContext(aElement, presShell, error);
 
@@ -2278,74 +2277,74 @@ GetFontStyleContext(Element* aElement, c
 
   MOZ_RELEASE_ASSERT(parentContext,
                      "GetFontParentStyleContext should have returned an error if it couldn't get a parent context.");
 
   MOZ_ASSERT(!presShell->IsDestroying(),
              "GetFontParentStyleContext should have returned an error if the presshell is being destroyed.");
 
   nsTArray<nsCOMPtr<nsIStyleRule>> rules;
-  rules.AppendElement(rule);
+  rules.AppendElement(decl);
   // add a rule to prevent text zoom from affecting the style
   rules.AppendElement(new nsDisableTextZoomStyleRule);
 
   nsStyleSet* styleSet = presShell->StyleSet();
   RefPtr<nsStyleContext> sc =
     styleSet->ResolveStyleForRules(parentContext, rules);
 
   // The font getter is required to be reserialized based on what we
   // parsed (including having line-height removed).  (Older drafts of
   // the spec required font sizes be converted to pixels, but that no
   // longer seems to be required.)
-  rule->GetDeclaration()->GetValue(eCSSProperty_font, aOutUsedFont);
+  decl->GetValue(eCSSProperty_font, aOutUsedFont);
 
   return sc.forget();
 }
 
-static already_AddRefed<StyleRule>
-CreateFilterStyleRule(const nsAString& aFilter,
-                      nsINode* aNode,
-                      bool* aOutFilterChanged,
-                      ErrorResult& error)
+static already_AddRefed<Declaration>
+CreateFilterDeclaration(const nsAString& aFilter,
+                        nsINode* aNode,
+                        bool* aOutFilterChanged,
+                        ErrorResult& error)
 {
   bool dummy;
-  return CreateStyleRule(aNode,
+  return CreateDeclaration(aNode,
     eCSSProperty_filter, aFilter, aOutFilterChanged,
     eCSSProperty_UNKNOWN, EmptyString(), &dummy,
     error);
 }
 
 static already_AddRefed<nsStyleContext>
-ResolveStyleForFilterRule(const nsAString& aFilterString,
-                          nsIPresShell* aPresShell,
-                          nsStyleContext* aParentContext,
-                          ErrorResult& error)
+ResolveStyleForFilter(const nsAString& aFilterString,
+                      nsIPresShell* aPresShell,
+                      nsStyleContext* aParentContext,
+                      ErrorResult& error)
 {
   nsIDocument* document = aPresShell->GetDocument();
   bool filterChanged = false;
-  RefPtr<css::StyleRule> rule =
-    CreateFilterStyleRule(aFilterString, document, &filterChanged, error);
+  RefPtr<css::Declaration> decl =
+    CreateFilterDeclaration(aFilterString, document, &filterChanged, error);
 
   if (error.Failed()) {
     return nullptr;
   }
 
   if (!filterChanged) {
     // Refuse to accept the filter, but do not throw an error.
     return nullptr;
   }
 
   // In addition to unparseable values, the spec says we need to reject
   // 'inherit' and 'initial'.
-  if (PropertyIsInheritOrInitial(rule, eCSSProperty_filter)) {
+  if (PropertyIsInheritOrInitial(decl, eCSSProperty_filter)) {
     return nullptr;
   }
 
   nsTArray<nsCOMPtr<nsIStyleRule>> rules;
-  rules.AppendElement(rule);
+  rules.AppendElement(decl);
 
   RefPtr<nsStyleContext> sc =
     aPresShell->StyleSet()->ResolveStyleForRules(aParentContext, rules);
 
   return sc.forget();
 }
 
 bool
@@ -2370,17 +2369,17 @@ CanvasRenderingContext2D::ParseFilter(co
     GetFontStyleContext(mCanvasElement, GetFont(),
                         presShell, usedFont, error);
   if (!parentContext) {
     error.Throw(NS_ERROR_FAILURE);
     return false;
   }
 
   RefPtr<nsStyleContext> sc =
-    ResolveStyleForFilterRule(aString, presShell, parentContext, error);
+    ResolveStyleForFilter(aString, presShell, parentContext, error);
 
   if (!sc) {
     return false;
   }
 
   aFilterChain = sc->StyleSVGReset()->mFilters;
   return true;
 }
--- a/dom/indexedDB/test/browser.ini
+++ b/dom/indexedDB/test/browser.ini
@@ -1,21 +1,22 @@
 [DEFAULT]
-skip-if = (buildapp != "browser") || e10s
+skip-if = (buildapp != "browser")
 support-files =
   head.js
   browser_forgetThisSiteAdd.html
   browser_forgetThisSiteGet.html
   browserHelpers.js
   browser_permissionsPrompt.html
   browser_permissionsSharedWorker.html
   browser_permissionsSharedWorker.js
   browser_permissionsWorker.html
   browser_permissionsWorker.js
   bug839193.js
   bug839193.xul
 
 [browser_forgetThisSite.js]
 [browser_permissionsPromptAllow.js]
 [browser_permissionsPromptDeny.js]
+skip-if = e10s # bug 1220748 - Attempts to touch content docshell to set usePrivateBrowsing.
 [browser_permissionsPromptWorker.js]
 [browser_perwindow_privateBrowsing.js]
 [browser_bug839193.js]
--- a/dom/interfaces/base/nsIServiceWorkerManager.idl
+++ b/dom/interfaces/base/nsIServiceWorkerManager.idl
@@ -16,27 +16,34 @@ interface nsIURI;
 interface nsIServiceWorkerUnregisterCallback : nsISupports
 {
   // aState is true if the unregistration succeded.
   // It's false if this ServiceWorkerRegistration doesn't exist.
   void unregisterSucceeded(in bool aState);
   void unregisterFailed();
 };
 
-[scriptable, builtinclass, uuid(f222dcad-3081-4699-bbc6-5615e20749C8)]
+[scriptable, builtinclass, uuid(1a1e71dd-0f78-4e2e-a2db-a946fe02cddf)]
+interface nsIServiceWorkerInfo : nsISupports
+{
+  readonly attribute DOMString scriptSpec;
+  readonly attribute DOMString cacheName;
+};
+
+[scriptable, builtinclass, uuid(e908bc07-0a4c-443f-a546-2109bf1f4A01)]
 interface nsIServiceWorkerRegistrationInfo : nsISupports
 {
   readonly attribute nsIPrincipal principal;
 
   readonly attribute DOMString scope;
   readonly attribute DOMString scriptSpec;
-  readonly attribute DOMString currentWorkerURL;
 
-  readonly attribute DOMString activeCacheName;
-  readonly attribute DOMString waitingCacheName;
+  readonly attribute nsIServiceWorkerInfo installingWorker;
+  readonly attribute nsIServiceWorkerInfo waitingWorker;
+  readonly attribute nsIServiceWorkerInfo activeWorker;
 };
 
 [scriptable, uuid(9e523e7c-ad6f-4df0-8077-c74aebbc679d)]
 interface nsIServiceWorkerManagerListener : nsISupports
 {
   void onRegister(in nsIServiceWorkerRegistrationInfo aInfo);
 
   void onUnregister(in nsIServiceWorkerRegistrationInfo aInfo);
--- a/dom/media/MediaStreamGraph.cpp
+++ b/dom/media/MediaStreamGraph.cpp
@@ -2412,16 +2412,17 @@ MediaInputPort::Init()
   mDest->AddInput(this);
   // mPortCount decremented via MediaInputPort::Destroy's message
   ++mDest->GraphImpl()->mPortCount;
 }
 
 void
 MediaInputPort::Disconnect()
 {
+  GraphImpl()->AssertOnGraphThreadOrNotRunning();
   NS_ASSERTION(!mSource == !mDest,
                "mSource must either both be null or both non-null");
   if (!mSource)
     return;
 
   mSource->RemoveConsumer(this);
   mDest->RemoveInput(this);
   mSource = nullptr;
--- a/dom/media/webaudio/AudioDestinationNode.cpp
+++ b/dom/media/webaudio/AudioDestinationNode.cpp
@@ -552,17 +552,16 @@ AudioDestinationNode::WindowAudioCapture
 
   if (captured != mCaptured) {
     if (captured) {
       nsCOMPtr<nsPIDOMWindow> window = Context()->GetParentObject();
       uint64_t id = window->WindowID();
       mCaptureStreamPort =
         mStream->Graph()->ConnectToCaptureStream(id, mStream);
     } else {
-      mCaptureStreamPort->Disconnect();
       mCaptureStreamPort->Destroy();
     }
     mCaptured = captured;
   }
 
   return NS_OK;
 }
 
--- a/dom/svg/DOMSVGLengthList.cpp
+++ b/dom/svg/DOMSVGLengthList.cpp
@@ -255,16 +255,23 @@ DOMSVGLengthList::InsertItemBefore(DOMSV
   }
 
   // Ensure we have enough memory so we can avoid complex error handling below:
   if (!mItems.SetCapacity(mItems.Length() + 1, fallible) ||
       !InternalList().SetCapacity(InternalList().Length() + 1)) {
     error.Throw(NS_ERROR_OUT_OF_MEMORY);
     return nullptr;
   }
+  if (AnimListMirrorsBaseList()) {
+    if (!mAList->mAnimVal->mItems.SetCapacity(
+          mAList->mAnimVal->mItems.Length() + 1, fallible)) {
+      error.Throw(NS_ERROR_OUT_OF_MEMORY);
+      return nullptr;
+    }
+  }
 
   AutoChangeLengthListNotifier notifier(this);
   // Now that we know we're inserting, keep animVal list in sync as necessary.
   MaybeInsertNullInAnimValListAt(index);
 
   InternalList().InsertItem(index, domItem->ToSVGLength());
   MOZ_ALWAYS_TRUE(mItems.InsertElementAt(index, domItem.get(), fallible));
 
--- a/dom/svg/DOMSVGNumberList.cpp
+++ b/dom/svg/DOMSVGNumberList.cpp
@@ -239,16 +239,23 @@ DOMSVGNumberList::InsertItemBefore(DOMSV
   RefPtr<DOMSVGNumber> domItem = aItem.HasOwner() ? aItem.Clone() : &aItem;
 
   // Ensure we have enough memory so we can avoid complex error handling below:
   if (!mItems.SetCapacity(mItems.Length() + 1, fallible) ||
       !InternalList().SetCapacity(InternalList().Length() + 1)) {
     error.Throw(NS_ERROR_OUT_OF_MEMORY);
     return nullptr;
   }
+  if (AnimListMirrorsBaseList()) {
+    if (!mAList->mAnimVal->mItems.SetCapacity(
+          mAList->mAnimVal->mItems.Length() + 1, fallible)) {
+      error.Throw(NS_ERROR_OUT_OF_MEMORY);
+      return nullptr;
+    }
+  }
 
   AutoChangeNumberListNotifier notifier(this);
   // Now that we know we're inserting, keep animVal list in sync as necessary.
   MaybeInsertNullInAnimValListAt(index);
 
   InternalList().InsertItem(index, domItem->ToSVGNumber());
   MOZ_ALWAYS_TRUE(mItems.InsertElementAt(index, domItem, fallible));
 
--- a/dom/svg/DOMSVGPathSegList.cpp
+++ b/dom/svg/DOMSVGPathSegList.cpp
@@ -377,16 +377,25 @@ DOMSVGPathSegList::InsertItemBefore(DOMS
 
   // Ensure we have enough memory so we can avoid complex error handling below:
   if (!mItems.SetCapacity(mItems.Length() + 1, fallible) ||
       !InternalList().mData.SetCapacity(InternalList().mData.Length() + 1 + argCount,
                                         fallible)) {
     aError.Throw(NS_ERROR_OUT_OF_MEMORY);
     return nullptr;
   }
+  if (AnimListMirrorsBaseList()) {
+    DOMSVGPathSegList *animVal =
+      GetDOMWrapperIfExists(InternalAList().GetAnimValKey());
+    if (!animVal->mItems.SetCapacity(
+          animVal->mItems.Length() + 1, fallible)) {
+      aError.Throw(NS_ERROR_OUT_OF_MEMORY);
+      return nullptr;
+    }
+  }
 
   AutoChangePathSegListNotifier notifier(this);
   // Now that we know we're inserting, keep animVal list in sync as necessary.
   MaybeInsertNullInAnimValListAt(aIndex, internalIndex, argCount);
 
   float segAsRaw[1 + NS_SVG_PATH_SEG_MAX_ARGS];
   domItem->ToSVGPathSegEncodedData(segAsRaw);
 
--- a/dom/svg/DOMSVGPointList.cpp
+++ b/dom/svg/DOMSVGPointList.cpp
@@ -313,16 +313,25 @@ DOMSVGPointList::InsertItemBefore(nsISVG
   }
 
   // Ensure we have enough memory so we can avoid complex error handling below:
   if (!mItems.SetCapacity(mItems.Length() + 1, fallible) ||
       !InternalList().SetCapacity(InternalList().Length() + 1)) {
     aError.Throw(NS_ERROR_OUT_OF_MEMORY);
     return nullptr;
   }
+  if (AnimListMirrorsBaseList()) {
+    DOMSVGPointList *animVal =
+      GetDOMWrapperIfExists(InternalAList().GetAnimValKey());
+    if (!animVal->mItems.SetCapacity(
+          animVal->mItems.Length() + 1, fallible)) {
+      aError.Throw(NS_ERROR_OUT_OF_MEMORY);
+      return nullptr;
+    }
+  }
 
   AutoChangePointListNotifier notifier(this);
   // Now that we know we're inserting, keep animVal list in sync as necessary.
   MaybeInsertNullInAnimValListAt(aIndex);
 
   InternalList().InsertItem(aIndex, domItem->ToSVGPoint());
   MOZ_ALWAYS_TRUE(mItems.InsertElementAt(aIndex, domItem, fallible));
 
--- a/dom/svg/DOMSVGTransformList.cpp
+++ b/dom/svg/DOMSVGTransformList.cpp
@@ -247,16 +247,23 @@ DOMSVGTransformList::InsertItemBefore(SV
   }
 
   // Ensure we have enough memory so we can avoid complex error handling below:
   if (!mItems.SetCapacity(mItems.Length() + 1, fallible) ||
       !InternalList().SetCapacity(InternalList().Length() + 1)) {
     error.Throw(NS_ERROR_OUT_OF_MEMORY);
     return nullptr;
   }
+  if (AnimListMirrorsBaseList()) {
+    if (!mAList->mAnimVal->mItems.SetCapacity(
+          mAList->mAnimVal->mItems.Length() + 1, fallible)) {
+      error.Throw(NS_ERROR_OUT_OF_MEMORY);
+      return nullptr;
+    }
+  }
 
   AutoChangeTransformListNotifier notifier(this);
   // Now that we know we're inserting, keep animVal list in sync as necessary.
   MaybeInsertNullInAnimValListAt(index);
 
   InternalList().InsertItem(index, domItem->ToSVGTransform());
   MOZ_ALWAYS_TRUE(mItems.InsertElementAt(index, domItem.get(), fallible));
 
--- a/dom/svg/nsSVGElement.cpp
+++ b/dom/svg/nsSVGElement.cpp
@@ -898,18 +898,19 @@ nsSVGElement::WalkContentStyleRules(nsRu
 {
 #ifdef DEBUG
 //  printf("nsSVGElement(%p)::WalkContentStyleRules()\n", this);
 #endif
   if (!mContentStyleRule)
     UpdateContentStyleRule();
 
   if (mContentStyleRule) {
-    mContentStyleRule->RuleMatched();
-    aRuleWalker->Forward(mContentStyleRule);
+    css::Declaration* declaration = mContentStyleRule->GetDeclaration();
+    declaration->SetImmutable();
+    aRuleWalker->Forward(declaration);
   }
 
   return NS_OK;
 }
 
 void
 nsSVGElement::WalkAnimatedContentStyleRules(nsRuleWalker* aRuleWalker)
 {
@@ -922,18 +923,19 @@ nsSVGElement::WalkAnimatedContentStyleRu
   if (!restyleManager->SkipAnimationRules()) {
     // update/walk the animated content style rule.
     css::StyleRule* animContentStyleRule = GetAnimatedContentStyleRule();
     if (!animContentStyleRule) {
       UpdateAnimatedContentStyleRule();
       animContentStyleRule = GetAnimatedContentStyleRule();
     }
     if (animContentStyleRule) {
-      animContentStyleRule->RuleMatched();
-      aRuleWalker->Forward(animContentStyleRule);
+      css::Declaration* declaration = animContentStyleRule->GetDeclaration();
+      declaration->SetImmutable();
+      aRuleWalker->Forward(declaration);
     }
   }
 }
 
 NS_IMETHODIMP_(bool)
 nsSVGElement::IsAttributeMapped(const nsIAtom* name) const
 {
   if (name == nsGkAtoms::lang) {
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -514,17 +514,17 @@ var interfaceNamesInGlobalScope =
     "HashChangeEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Headers",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "History",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "HDMIInputPort", b2g: true, permission: ["inputport"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "HMDVRDevice", disabled: true},
+    {name: "HMDVRDevice", android: true, release: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLAllCollection",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLAnchorElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLAppletElement",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "HTMLAreaElement",
@@ -942,17 +942,17 @@ var interfaceNamesInGlobalScope =
     {name: "PointerEvent", nightly: true, desktop: true, disabled: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "PopStateEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "PopupBlockedEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "PopupBoxObject", xbl: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "PositionSensorVRDevice", disabled: true},
+    {name: "PositionSensorVRDevice", android: true, release: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "PresentationDeviceInfoManager",
      disabled: true,
      permission: ["presentation-device-manage"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "Presentation", disabled: true, permission: ["presentation"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "PresentationAvailability", disabled: true, permission: ["presentation"]},
@@ -1394,23 +1394,25 @@ var interfaceNamesInGlobalScope =
     "UserProximityEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "ValidityState",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "VideoPlaybackQuality",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "VideoStreamTrack",
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "VRDevice", disabled: true},
-// IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "VRPositionState", disabled: true},
-// IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "VRFieldOfView", disabled: true},
-// IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "VRFieldOfViewReadOnly", disabled: true},
+    {name: "VRDevice", android: true, release: false},
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "VRPositionState", android: true, release: false},
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "VREyeParameters", android: true, release: false},
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "VRFieldOfView", android: true, release: false},
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "VRFieldOfViewReadOnly", android: true, release: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "VTTCue",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "VTTRegion", disabled: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "WaveShaperNode",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "WebGLActiveInfo",
--- a/dom/workers/ServiceWorkerContainer.cpp
+++ b/dom/workers/ServiceWorkerContainer.cpp
@@ -208,20 +208,18 @@ ServiceWorkerContainer::Register(const n
     aRv = CheckForSlashEscapedCharsInPath(scopeURI);
     if (NS_WARN_IF(aRv.Failed())) {
       return nullptr;
     }
   }
 
   // The spec says that the "client" passed to Register() must be the global
   // where the ServiceWorkerContainer was retrieved from.
-  nsCOMPtr<nsPIDOMWindow> window = GetOwner();
-  MOZ_ASSERT(window);
-  aRv = swm->Register(window, scopeURI, scriptURI, getter_AddRefs(promise));
-  if (aRv.Failed()) {
+  aRv = swm->Register(GetOwner(), scopeURI, scriptURI, getter_AddRefs(promise));
+  if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   RefPtr<Promise> ret = static_cast<Promise*>(promise.get());
   MOZ_ASSERT(ret);
   return ret.forget();
 }
 
@@ -323,14 +321,26 @@ ServiceWorkerContainer::GetScopeForUrl(c
                                        ErrorResult& aRv)
 {
   nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
   if (!swm) {
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
 
-  aRv = swm->GetScopeForUrl(GetOwner()->GetExtantDoc()->NodePrincipal(),
+  nsCOMPtr<nsPIDOMWindow> window = GetOwner();
+  if (NS_WARN_IF(!window)) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return;
+  }
+
+  nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
+  if (NS_WARN_IF(!doc)) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return;
+  }
+
+  aRv = swm->GetScopeForUrl(doc->NodePrincipal(),
                             aUrl, aScope);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -404,42 +404,39 @@ NS_IMETHODIMP
 ServiceWorkerRegistrationInfo::GetScriptSpec(nsAString& aScriptSpec)
 {
   AssertIsOnMainThread();
   CopyUTF8toUTF16(mScriptSpec, aScriptSpec);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-ServiceWorkerRegistrationInfo::GetCurrentWorkerURL(nsAString& aCurrentWorkerURL)
+ServiceWorkerRegistrationInfo::GetInstallingWorker(nsIServiceWorkerInfo **aResult)
 {
   AssertIsOnMainThread();
-  if (mActiveWorker) {
-    CopyUTF8toUTF16(mActiveWorker->ScriptSpec(), aCurrentWorkerURL);
-  }
+  nsCOMPtr<nsIServiceWorkerInfo> info = do_QueryInterface(mInstallingWorker);
+  info.forget(aResult);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-ServiceWorkerRegistrationInfo::GetActiveCacheName(nsAString& aActiveCacheName)
+ServiceWorkerRegistrationInfo::GetWaitingWorker(nsIServiceWorkerInfo **aResult)
 {
   AssertIsOnMainThread();
-  if (mActiveWorker) {
-    aActiveCacheName = mActiveWorker->CacheName();
-  }
+  nsCOMPtr<nsIServiceWorkerInfo> info = do_QueryInterface(mWaitingWorker);
+  info.forget(aResult);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-ServiceWorkerRegistrationInfo::GetWaitingCacheName(nsAString& aWaitingCacheName)
+ServiceWorkerRegistrationInfo::GetActiveWorker(nsIServiceWorkerInfo **aResult)
 {
   AssertIsOnMainThread();
-  if (mWaitingWorker) {
-    aWaitingCacheName = mWaitingWorker->CacheName();
-  }
+  nsCOMPtr<nsIServiceWorkerInfo> info = do_QueryInterface(mActiveWorker);
+  info.forget(aResult);
   return NS_OK;
 }
 
 already_AddRefed<ServiceWorkerInfo>
 ServiceWorkerRegistrationInfo::GetServiceWorkerInfoById(uint64_t aId)
 {
   RefPtr<ServiceWorkerInfo> serviceWorker;
   if (mInstallingWorker && mInstallingWorker->ID() == aId) {
@@ -1465,17 +1462,19 @@ NS_IMETHODIMP
 ServiceWorkerManager::Register(nsIDOMWindow* aWindow,
                                nsIURI* aScopeURI,
                                nsIURI* aScriptURI,
                                nsISupports** aPromise)
 {
   AssertIsOnMainThread();
 
   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
-  MOZ_ASSERT(window);
+  if (NS_WARN_IF(!window)) {
+    return NS_ERROR_DOM_INVALID_STATE_ERR;
+  }
 
   nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
   if (!doc) {
     return NS_ERROR_FAILURE;
   }
 
   // Don't allow service workers to register when the *document* is chrome for
   // now.
@@ -1770,24 +1769,25 @@ public:
 
 // If we return an error code here, the ServiceWorkerContainer will
 // automatically reject the Promise.
 NS_IMETHODIMP
 ServiceWorkerManager::GetRegistrations(nsIDOMWindow* aWindow,
                                        nsISupports** aPromise)
 {
   AssertIsOnMainThread();
-  MOZ_ASSERT(aWindow);
 
   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
-  MOZ_ASSERT(window);
+  if (NS_WARN_IF(!window)) {
+    return NS_ERROR_DOM_INVALID_STATE_ERR;
+  }
 
   nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
-  if (!doc) {
-    return NS_ERROR_FAILURE;
+  if (NS_WARN_IF(!doc)) {
+    return NS_ERROR_DOM_INVALID_STATE_ERR;
   }
 
   // Don't allow service workers to register when the *document* is chrome for
   // now.
   MOZ_ASSERT(!nsContentUtils::IsSystemPrincipal(doc->NodePrincipal()));
 
   nsCOMPtr<nsIGlobalObject> sgo = do_QueryInterface(window);
   ErrorResult result;
@@ -1874,24 +1874,25 @@ public:
 // If we return an error code here, the ServiceWorkerContainer will
 // automatically reject the Promise.
 NS_IMETHODIMP
 ServiceWorkerManager::GetRegistration(nsIDOMWindow* aWindow,
                                       const nsAString& aDocumentURL,
                                       nsISupports** aPromise)
 {
   AssertIsOnMainThread();
-  MOZ_ASSERT(aWindow);
 
   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
-  MOZ_ASSERT(window);
+  if (NS_WARN_IF(!window)) {
+    return NS_ERROR_DOM_INVALID_STATE_ERR;
+  }
 
   nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
-  if (!doc) {
-    return NS_ERROR_FAILURE;
+  if (NS_WARN_IF(!doc)) {
+    return NS_ERROR_DOM_INVALID_STATE_ERR;
   }
 
   // Don't allow service workers to register when the *document* is chrome for
   // now.
   MOZ_ASSERT(!nsContentUtils::IsSystemPrincipal(doc->NodePrincipal()));
 
   nsCOMPtr<nsIGlobalObject> sgo = do_QueryInterface(window);
   ErrorResult result;
@@ -2027,23 +2028,24 @@ ServiceWorkerManager::SendNotificationCl
                                                    NS_ConvertUTF8toUTF16(aScope));
 }
 
 NS_IMETHODIMP
 ServiceWorkerManager::GetReadyPromise(nsIDOMWindow* aWindow,
                                       nsISupports** aPromise)
 {
   AssertIsOnMainThread();
-  MOZ_ASSERT(aWindow);
 
   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
-  MOZ_ASSERT(window);
+  if (NS_WARN_IF(!window)) {
+    return NS_ERROR_DOM_INVALID_STATE_ERR;
+  }
 
   nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
-  if (!doc) {
+  if (NS_WARN_IF(!doc)) {
     return NS_ERROR_FAILURE;
   }
 
   // Don't allow service workers to register when the *document* is chrome for
   // now.
   MOZ_ASSERT(!nsContentUtils::IsSystemPrincipal(doc->NodePrincipal()));
 
   MOZ_ASSERT(!mPendingReadyPromises.Contains(window));
@@ -2972,17 +2974,17 @@ ServiceWorkerManager::GetServiceWorkerFo
                                                const nsAString& aScope,
                                                WhichServiceWorker aWhichWorker,
                                                nsISupports** aServiceWorker)
 {
   AssertIsOnMainThread();
 
   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
   if (NS_WARN_IF(!window)) {
-    return NS_ERROR_FAILURE;
+    return NS_ERROR_DOM_INVALID_STATE_ERR;
   }
 
   nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
   MOZ_ASSERT(doc);
 
   ///////////////////////////////////////////
   // Security check
   nsAutoCString scope = NS_ConvertUTF16toUTF8(aScope);
@@ -3237,17 +3239,17 @@ ServiceWorkerManager::GetDocumentRegistr
  */
 NS_IMETHODIMP
 ServiceWorkerManager::GetDocumentController(nsIDOMWindow* aWindow,
                                             nsISupports** aServiceWorker)
 {
   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
   MOZ_ASSERT(window);
   if (!window || !window->GetExtantDoc()) {
-    return NS_ERROR_FAILURE;
+    return NS_ERROR_DOM_INVALID_STATE_ERR;
   }
 
   nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
 
   RefPtr<ServiceWorkerRegistrationInfo> registration;
   nsresult rv = GetDocumentRegistration(doc, getter_AddRefs(registration));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
@@ -4047,16 +4049,34 @@ ServiceWorkerManager::NotifyListenersOnU
                                         nsIServiceWorkerRegistrationInfo* aInfo)
 {
   nsTArray<nsCOMPtr<nsIServiceWorkerManagerListener>> listeners(mListeners);
   for (size_t index = 0; index < listeners.Length(); ++index) {
     listeners[index]->OnUnregister(aInfo);
   }
 }
 
+NS_IMPL_ISUPPORTS(ServiceWorkerInfo, nsIServiceWorkerInfo)
+
+NS_IMETHODIMP
+ServiceWorkerInfo::GetScriptSpec(nsAString& aScriptSpec)
+{
+  AssertIsOnMainThread();
+  CopyUTF8toUTF16(mScriptSpec, aScriptSpec);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ServiceWorkerInfo::GetCacheName(nsAString& aCacheName)
+{
+  AssertIsOnMainThread();
+  aCacheName = mCacheName;
+  return NS_OK;
+}
+
 void
 ServiceWorkerInfo::AppendWorker(ServiceWorker* aWorker)
 {
   MOZ_ASSERT(aWorker);
 #ifdef DEBUG
   nsAutoString workerURL;
   aWorker->GetScriptURL(workerURL);
   MOZ_ASSERT(workerURL.Equals(NS_ConvertUTF8toUTF16(mScriptSpec)));
--- a/dom/workers/ServiceWorkerManager.h
+++ b/dom/workers/ServiceWorkerManager.h
@@ -163,17 +163,17 @@ public:
 };
 
 /*
  * Wherever the spec treats a worker instance and a description of said worker
  * as the same thing; i.e. "Resolve foo with
  * _GetNewestWorker(serviceWorkerRegistration)", we represent the description
  * by this class and spawn a ServiceWorker in the right global when required.
  */
-class ServiceWorkerInfo final
+class ServiceWorkerInfo final : public nsIServiceWorkerInfo
 {
 private:
   const ServiceWorkerRegistrationInfo* mRegistration;
   nsCString mScriptSpec;
   nsString mCacheName;
   ServiceWorkerState mState;
 
   // This id is shared with WorkerPrivate to match requests issued by service
@@ -192,17 +192,18 @@ private:
   ~ServiceWorkerInfo();
 
   // Generates a unique id for the service worker, with zero being treated as
   // invalid.
   uint64_t
   GetNextID() const;
 
 public:
-  NS_INLINE_DECL_REFCOUNTING(ServiceWorkerInfo)
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSISERVICEWORKERINFO
 
   class ServiceWorkerPrivate*
   WorkerPrivate() const
   {
     MOZ_ASSERT(mServiceWorkerPrivate);
     return mServiceWorkerPrivate;
   }
 
--- a/dom/workers/ServiceWorkerRegistration.cpp
+++ b/dom/workers/ServiceWorkerRegistration.cpp
@@ -684,17 +684,16 @@ ServiceWorkerRegistrationMainThread::Unr
 // Notification API extension.
 already_AddRefed<Promise>
 ServiceWorkerRegistrationMainThread::ShowNotification(JSContext* aCx,
                                                       const nsAString& aTitle,
                                                       const NotificationOptions& aOptions,
                                                       ErrorResult& aRv)
 {
   AssertIsOnMainThread();
-  MOZ_ASSERT(GetOwner());
   nsCOMPtr<nsPIDOMWindow> window = GetOwner();
   if (NS_WARN_IF(!window)) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
   if (NS_WARN_IF(!doc)) {
@@ -719,16 +718,20 @@ ServiceWorkerRegistrationMainThread::Sho
   return p.forget();
 }
 
 already_AddRefed<Promise>
 ServiceWorkerRegistrationMainThread::GetNotifications(const GetNotificationOptions& aOptions, ErrorResult& aRv)
 {
   AssertIsOnMainThread();
   nsCOMPtr<nsPIDOMWindow> window = GetOwner();
+  if (NS_WARN_IF(!window)) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return nullptr;
+  }
   return Notification::Get(window, aOptions, mScope, aRv);
 }
 
 already_AddRefed<PushManager>
 ServiceWorkerRegistrationMainThread::GetPushManager(ErrorResult& aRv)
 {
   AssertIsOnMainThread();
 
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -2342,17 +2342,17 @@ void AsyncPanZoomController::AcceptFling
     mY.SetVelocity(mY.GetVelocity() + aVelocity.y);
     aVelocity.y = 0;
   }
   SetState(FLING);
   FlingAnimation *fling = new FlingAnimation(*this,
       aOverscrollHandoffChain,
       !aHandoff);  // only apply acceleration if this is an initial fling
 
-  float friction = gfxPrefs::APZFlingSnapFriction();
+  float friction = gfxPrefs::APZFlingFriction();
   ParentLayerPoint velocity(mX.GetVelocity(), mY.GetVelocity());
   ParentLayerPoint predictedDelta;
   // "-velocity / log(1.0 - friction)" is the integral of the deceleration
   // curve modeled for flings in the "Axis" class.
   if (velocity.x != 0.0f) {
     predictedDelta.x = -velocity.x / log(1.0 - friction);
   }
   if (velocity.y != 0.0f) {
@@ -2687,18 +2687,21 @@ bool AsyncPanZoomController::SnapBackIfO
   // It's possible that we're already in the middle of an overscroll
   // animation - if so, don't start a new one.
   if (IsOverscrolled() && mState != OVERSCROLL_ANIMATION) {
     APZC_LOG("%p is overscrolled, starting snap-back\n", this);
     StartOverscrollAnimation(ParentLayerPoint(0, 0));
     return true;
   }
   // If we don't kick off an overscroll animation, we still need to ask the
-  // main thread to snap to any nearby snap points.
-  RequestSnap();
+  // main thread to snap to any nearby snap points, assuming we haven't already
+  // done so when we started this fling
+  if (mState != FLING) {
+    RequestSnap();
+  }
   return false;
 }
 
 bool AsyncPanZoomController::IsFlingingFast() const {
   ReentrantMonitorAutoEnter lock(mMonitor);
   if (mState == FLING &&
       GetVelocityVector().Length() > gfxPrefs::APZFlingStopOnTapThreshold()) {
     APZC_LOG("%p is moving fast\n", this);
@@ -2736,18 +2739,21 @@ void AsyncPanZoomController::RequestCont
       fabsf(marginDelta.top) < EPSILON &&
       fabsf(marginDelta.right) < EPSILON &&
       fabsf(marginDelta.bottom) < EPSILON &&
       fabsf(mLastPaintRequestMetrics.GetScrollOffset().x -
             aFrameMetrics.GetScrollOffset().x) < EPSILON &&
       fabsf(mLastPaintRequestMetrics.GetScrollOffset().y -
             aFrameMetrics.GetScrollOffset().y) < EPSILON &&
       aFrameMetrics.GetZoom() == mLastPaintRequestMetrics.GetZoom() &&
-      fabsf(aFrameMetrics.GetViewport().width - mLastPaintRequestMetrics.GetViewport().width) < EPSILON &&
-      fabsf(aFrameMetrics.GetViewport().height - mLastPaintRequestMetrics.GetViewport().height) < EPSILON) {
+      fabsf(aFrameMetrics.GetViewport().width -
+            mLastPaintRequestMetrics.GetViewport().width) < EPSILON &&
+      fabsf(aFrameMetrics.GetViewport().height -
+            mLastPaintRequestMetrics.GetViewport().height) < EPSILON &&
+      aFrameMetrics.GetScrollGeneration() == mLastPaintRequestMetrics.GetScrollGeneration()) {
     return;
   }
 
   SendAsyncScrollEvent();
   if (aThrottled) {
     mPaintThrottler->PostTask(
       FROM_HERE,
       UniquePtr<CancelableTask>(NewRunnableMethod(this,
@@ -3185,18 +3191,24 @@ void AsyncPanZoomController::NotifyLayer
     // A smooth scroll has been requested for animation on the compositor
     // thread.  This flag will be reset by the main thread when it receives
     // the scroll update acknowledgement.
 
     APZC_LOG("%p smooth scrolling from %s to %s\n", this,
       Stringify(mFrameMetrics.GetScrollOffset()).c_str(),
       Stringify(aLayerMetrics.GetSmoothScrollOffset()).c_str());
 
+    CancelAnimation();
+
+    // It's important to not send repaint requests between this
+    // CopySmoothScrollInfo call and the sending of the scroll update
+    // acknowledgement below, otherwise that repaint request will get rejected
+    // on the main thread but mLastPaintRequestMetrics will have the new scroll
+    // generation; this would leave the main thread out of date.
     mFrameMetrics.CopySmoothScrollInfoFrom(aLayerMetrics);
-    CancelAnimation();
     mLastDispatchedPaintMetrics = aLayerMetrics;
     StartSmoothScroll(ScrollSource::DOM);
 
     scrollOffsetUpdated = true; // Ensure that AcknowledgeScrollUpdate is called
   }
 
   if (scrollOffsetUpdated) {
     // Once layout issues a scroll offset update, it becomes impervious to
@@ -3549,15 +3561,17 @@ void AsyncPanZoomController::ShareCompos
         APZC_LOG("%p failed to share FrameMetrics with content process.", this);
       }
     }
   }
 }
 
 void AsyncPanZoomController::RequestSnap() {
   if (RefPtr<GeckoContentController> controller = GetGeckoContentController()) {
+    APZC_LOG("%p requesting snap near %s\n", this,
+        Stringify(mFrameMetrics.GetScrollOffset()).c_str());
     controller->RequestFlingSnap(mFrameMetrics.GetScrollId(),
                                  mFrameMetrics.GetScrollOffset());
   }
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/client/CanvasClient.cpp
+++ b/gfx/layers/client/CanvasClient.cpp
@@ -434,16 +434,20 @@ CanvasClientSharedSurface::UpdateRendere
   }
 
   mNewFront = newFront;
 }
 
 void
 CanvasClientSharedSurface::Updated()
 {
+  if (!mNewFront) {
+    return;
+  }
+
   auto forwarder = GetForwarder();
 
 #ifndef MOZ_WIDGET_GONK
   if (mFront) {
     if (mFront->GetFlags() & TextureFlags::RECYCLE) {
       mFront->WaitForCompositorRecycle();
     }
   }
--- a/gfx/layers/ipc/CompositorParent.cpp
+++ b/gfx/layers/ipc/CompositorParent.cpp
@@ -418,16 +418,20 @@ CompositorVsyncScheduler::Composite(Time
 
   DispatchTouchEvents(aVsyncTimestamp);
 
   if (mNeedsComposite || mAsapScheduling) {
     mNeedsComposite = false;
     mLastCompose = aVsyncTimestamp;
     ComposeToTarget(nullptr);
     mVsyncNotificationsSkipped = 0;
+
+    TimeDuration compositeFrameTotal = TimeStamp::Now() - aVsyncTimestamp;
+    mozilla::Telemetry::Accumulate(mozilla::Telemetry::COMPOSITE_FRAME_ROUNDTRIP_TIME,
+                                   compositeFrameTotal.ToMilliseconds());
   } else if (mVsyncNotificationsSkipped++ > gfxPrefs::CompositorUnobserveCount()) {
     UnobserveVsync();
   }
 }
 
 void
 CompositorVsyncScheduler::OnForceComposeToTarget()
 {
--- a/gfx/thebes/gfxBlur.cpp
+++ b/gfx/thebes/gfxBlur.cpp
@@ -444,28 +444,25 @@ CacheBlur(DrawTarget& aDT,
   BlurCacheData* data = new BlurCacheData(aBoxShadow, aExtendDest, key);
   if (!gBlurCache->RegisterEntry(data)) {
     delete data;
   }
 }
 
 // Blurs a small surface and creates the mask.
 static already_AddRefed<SourceSurface>
-CreateBlurMask(const IntSize& aRectSize,
+CreateBlurMask(const IntSize& aMinSize,
                RectCornerRadii* aCornerRadii,
                IntSize aBlurRadius,
                IntMargin& aExtendDestBy,
                IntMargin& aSliceBorder,
                DrawTarget& aDestDrawTarget)
 {
-  IntMargin slice;
   gfxAlphaBoxBlur blur;
-  IntSize minSize =
-    ComputeMinSizeForShadowShape(aCornerRadii, aBlurRadius, slice, aRectSize);
-  IntRect minRect(IntPoint(), minSize);
+  IntRect minRect(IntPoint(), aMinSize);
 
   gfxContext* blurCtx = blur.Init(ThebesRect(Rect(minRect)), IntSize(),
                                   aBlurRadius, nullptr, nullptr);
 
   if (!blurCtx) {
     return nullptr;
   }
 
@@ -483,17 +480,17 @@ CreateBlurMask(const IntSize& aRectSize,
   IntPoint topLeft;
   RefPtr<SourceSurface> result = blur.DoBlur(&aDestDrawTarget, &topLeft);
   if (!result) {
     return nullptr;
   }
 
   IntRect expandedMinRect(topLeft, result->GetSize());
   aExtendDestBy = expandedMinRect - minRect;
-  aSliceBorder = slice + aExtendDestBy;
+  aSliceBorder += aExtendDestBy;
 
   MOZ_ASSERT(aSliceBorder.LeftRight() <= expandedMinRect.width);
   MOZ_ASSERT(aSliceBorder.TopBottom() <= expandedMinRect.height);
 
   return result.forget();
 }
 
 static already_AddRefed<SourceSurface>
@@ -508,56 +505,72 @@ CreateBoxShadow(SourceSurface* aBlurMask
     return nullptr;
   }
 
   ColorPattern shadowColor(ToDeviceColor(aShadowColor));
   boxShadowDT->MaskSurface(shadowColor, aBlurMask, Point(0, 0));
   return boxShadowDT->Snapshot();
 }
 
-static SourceSurface*
+static already_AddRefed<SourceSurface>
 GetBlur(DrawTarget& aDT,
         const IntSize& aRectSize,
         const IntSize& aBlurRadius,
         RectCornerRadii* aCornerRadii,
         const Color& aShadowColor,
         IntMargin& aExtendDestBy,
-        IntMargin& aSlice)
+        IntMargin& aSlice,
+        gfxContext* aDestinationCtx)
 {
   if (!gBlurCache) {
     gBlurCache = new BlurCache();
   }
 
   IntSize minSize =
     ComputeMinSizeForShadowShape(aCornerRadii, aBlurRadius, aSlice, aRectSize);
 
+  // We can get seams using the min size rect when drawing to the destination rect
+  // if we have a non-pixel aligned destination transformation. In those cases,
+  // fallback to just rendering the destination rect.
+  Matrix destMatrix = ToMatrix(aDestinationCtx->CurrentMatrix());
+  bool useDestRect = !destMatrix.IsRectilinear() || destMatrix.HasNonIntegerTranslation();
+  if (useDestRect) {
+    minSize = aRectSize;
+  }
+
   BlurCacheData* cached = gBlurCache->Lookup(minSize, aBlurRadius,
                                              aCornerRadii, aShadowColor,
                                              aDT.GetBackendType());
-  if (cached) {
+  if (cached && !useDestRect) {
     // See CreateBlurMask() for these values
     aExtendDestBy = cached->mExtendDest;
     aSlice = aSlice + aExtendDestBy;
-    return cached->mBlur;
+    RefPtr<SourceSurface> blur = cached->mBlur;
+    return blur.forget();
   }
 
   RefPtr<SourceSurface> blurMask =
-    CreateBlurMask(aRectSize, aCornerRadii, aBlurRadius, aExtendDestBy, aSlice, aDT);
+    CreateBlurMask(minSize, aCornerRadii, aBlurRadius, aExtendDestBy, aSlice, aDT);
 
   if (!blurMask) {
     return nullptr;
   }
 
   RefPtr<SourceSurface> boxShadow = CreateBoxShadow(blurMask, aShadowColor);
   if (!boxShadow) {
     return nullptr;
   }
 
-  CacheBlur(aDT, minSize, aBlurRadius, aCornerRadii, aShadowColor, aExtendDestBy, boxShadow);
-  return boxShadow;
+  if (useDestRect) {
+    // Since we're just going to paint the actual rect to the destination
+    aSlice.SizeTo(0, 0, 0, 0);
+  } else {
+    CacheBlur(aDT, minSize, aBlurRadius, aCornerRadii, aShadowColor, aExtendDestBy, boxShadow);
+  }
+  return boxShadow.forget();
 }
 
 void
 gfxAlphaBoxBlur::ShutdownBlurCache()
 {
   delete gBlurCache;
   gBlurCache = nullptr;
 }
@@ -693,17 +706,18 @@ gfxAlphaBoxBlur::BlurRectangle(gfxContex
 
   IntRect rect = RoundedToInt(ToRect(aRect));
   IntMargin extendDestBy;
   IntMargin slice;
 
   RefPtr<SourceSurface> boxShadow = GetBlur(destDrawTarget,
                                             rect.Size(), blurRadius,
                                             aCornerRadii, aShadowColor,
-                                            extendDestBy, slice);
+                                            extendDestBy, slice,
+                                            aDestinationCtx);
   if (!boxShadow) {
     return;
   }
 
   destDrawTarget.PushClipRect(ToRect(aDirtyRect));
 
   // Copy the right parts from boxShadow into destDrawTarget. The middle parts
   // will be stretched, border-image style.
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -157,17 +157,16 @@ private:
   DECL_GFX_PREF(Live, "apz.fling_accel_supplemental_mult",     APZFlingAccelSupplementalMultiplier, float, 1.0f);
   DECL_GFX_PREF(Once, "apz.fling_curve_function_x1",           APZCurveFunctionX1, float, 0.0f);
   DECL_GFX_PREF(Once, "apz.fling_curve_function_x2",           APZCurveFunctionX2, float, 1.0f);
   DECL_GFX_PREF(Once, "apz.fling_curve_function_y1",           APZCurveFunctionY1, float, 0.0f);
   DECL_GFX_PREF(Once, "apz.fling_curve_function_y2",           APZCurveFunctionY2, float, 1.0f);
   DECL_GFX_PREF(Live, "apz.fling_curve_threshold_inches_per_ms", APZCurveThreshold, float, -1.0f);
   DECL_GFX_PREF(Once, "apz.fling_friction",                    APZFlingFriction, float, 0.002f);
   DECL_GFX_PREF(Live, "apz.fling_repaint_interval",            APZFlingRepaintInterval, int32_t, 75);
-  DECL_GFX_PREF(Once, "apz.fling_snap_friction",               APZFlingSnapFriction, float, 0.015f);
   DECL_GFX_PREF(Once, "apz.fling_stop_on_tap_threshold",       APZFlingStopOnTapThreshold, float, 0.05f);
   DECL_GFX_PREF(Once, "apz.fling_stopped_threshold",           APZFlingStoppedThreshold, float, 0.01f);
   DECL_GFX_PREF(Once, "apz.highlight_checkerboarded_areas",    APZHighlightCheckerboardedAreas, bool, false);
   DECL_GFX_PREF(Once, "apz.max_velocity_inches_per_ms",        APZMaxVelocity, float, -1.0f);
   DECL_GFX_PREF(Once, "apz.max_velocity_queue_size",           APZMaxVelocityQueueSize, uint32_t, 5);
   DECL_GFX_PREF(Live, "apz.min_skate_speed",                   APZMinSkateSpeed, float, 1.0f);
   DECL_GFX_PREF(Live, "apz.minimap.enabled",                   APZMinimap, bool, false);
   DECL_GFX_PREF(Live, "apz.num_paint_duration_samples",        APZNumPaintDurationSamples, int32_t, 3);
--- a/intl/locale/nsLocaleService.cpp
+++ b/intl/locale/nsLocaleService.cpp
@@ -12,16 +12,17 @@
 #include "nsAutoPtr.h"
 #include "nsILocale.h"
 #include "nsILocaleService.h"
 #include "nsLocale.h"
 #include "nsCRT.h"
 #include "prprf.h"
 #include "nsTArray.h"
 #include "nsString.h"
+#include "mozilla/UniquePtr.h"
 
 #include <ctype.h>
 
 #if defined(XP_WIN)
 #  include "nsWin32Locale.h"
 #elif defined(XP_MACOSX)
 #  include <Carbon/Carbon.h>
 #elif defined(XP_UNIX)
@@ -263,44 +264,44 @@ nsLocaleService::GetLocaleFromAcceptLang
   char* cPtr1;
   char* cPtr2;
   int i;
   int j;
   int countLang = 0;
   char	acceptLanguageList[NSILOCALE_MAX_ACCEPT_LANGUAGE][NSILOCALE_MAX_ACCEPT_LENGTH];
   nsresult	result;
 
-  nsAutoArrayPtr<char> input(new char[strlen(acceptLanguage)+1]);
+  auto input = MakeUnique<char[]>(strlen(acceptLanguage)+1);
 
-  strcpy(input, acceptLanguage);
-  cPtr1 = input-1;
-  cPtr2 = input;
+  strcpy(input.get(), acceptLanguage);
+  cPtr1 = input.get()-1;
+  cPtr2 = input.get();
 
   /* put in standard form */
   while (*(++cPtr1)) {
     if      (isalpha(*cPtr1))  *cPtr2++ = tolower(*cPtr1); /* force lower case */
     else if (isspace(*cPtr1))  ;                           /* ignore any space */
     else if (*cPtr1=='-')      *cPtr2++ = '_';             /* "-" -> "_"       */
     else if (*cPtr1=='*')      ;                           /* ignore "*"       */
     else                       *cPtr2++ = *cPtr1;          /* else unchanged   */
   }
   *cPtr2 = '\0';
 
   countLang = 0;
 
-  if (strchr(input,';')) {
+  if (strchr(input.get(), ';')) {
     /* deal with the quality values */
 
     float qvalue[NSILOCALE_MAX_ACCEPT_LANGUAGE];
     float qSwap;
     float bias = 0.0f;
     char* ptrLanguage[NSILOCALE_MAX_ACCEPT_LANGUAGE];
     char* ptrSwap;
 
-    cPtr = nsCRT::strtok(input,",",&cPtr2);
+    cPtr = nsCRT::strtok(input.get(),",",&cPtr2);
     while (cPtr) {
       qvalue[countLang] = 1.0f;
       /* add extra parens to get rid of warning */
       if ((cPtr1 = strchr(cPtr,';')) != nullptr) {
         PR_sscanf(cPtr1,";q=%f",&qvalue[countLang]);
         *cPtr1 = '\0';
       }
       if (strlen(cPtr)<NSILOCALE_MAX_ACCEPT_LANGUAGE) {     /* ignore if too long */
@@ -327,17 +328,17 @@ nsLocaleService::GetLocaleFromAcceptLang
     }
     for ( i=0 ; i<countLang ; i++ ) {
       PL_strncpyz(acceptLanguageList[i],ptrLanguage[i],NSILOCALE_MAX_ACCEPT_LENGTH);
     }
 
   } else {
     /* simple case: no quality values */
 
-    cPtr = nsCRT::strtok(input,",",&cPtr2);
+    cPtr = nsCRT::strtok(input.get(),",",&cPtr2);
     while (cPtr) {
       if (strlen(cPtr)<NSILOCALE_MAX_ACCEPT_LENGTH) {        /* ignore if too long */
         PL_strncpyz(acceptLanguageList[countLang++],cPtr,NSILOCALE_MAX_ACCEPT_LENGTH);
         if (countLang>=NSILOCALE_MAX_ACCEPT_LENGTH) break; /* quit if too many */
       }
       cPtr = nsCRT::strtok(cPtr2,",",&cPtr2);
     }
   }
--- a/intl/uconv/nsUTF8ConverterService.cpp
+++ b/intl/uconv/nsUTF8ConverterService.cpp
@@ -5,16 +5,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 "nsString.h"
 #include "nsUTF8ConverterService.h"
 #include "nsEscape.h"
 #include "nsAutoPtr.h"
 #include "nsIUnicodeDecoder.h"
 #include "mozilla/dom/EncodingUtils.h"
+#include "mozilla/UniquePtr.h"
 
 using mozilla::dom::EncodingUtils;
 
 NS_IMPL_ISUPPORTS(nsUTF8ConverterService, nsIUTF8ConverterService)
 
 static nsresult 
 ToUTF8(const nsACString &aString, const char *aCharset,
        bool aAllowSubstitution, nsACString &aResult)
@@ -35,23 +36,22 @@ ToUTF8(const nsACString &aString, const 
     unicodeDecoder->SetInputErrorBehavior(nsIUnicodeDecoder::kOnError_Signal);
 
   int32_t srcLen = aString.Length();
   int32_t dstLen;
   const nsAFlatCString& inStr = PromiseFlatCString(aString);
   rv = unicodeDecoder->GetMaxLength(inStr.get(), srcLen, &dstLen);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsAutoArrayPtr<char16_t> ustr(new char16_t[dstLen]);
+  auto ustr = mozilla::MakeUnique<char16_t[]>(dstLen);
   NS_ENSURE_TRUE(ustr, NS_ERROR_OUT_OF_MEMORY);
 
-  rv = unicodeDecoder->Convert(inStr.get(), &srcLen, ustr, &dstLen);
+  rv = unicodeDecoder->Convert(inStr.get(), &srcLen, ustr.get(), &dstLen);
   if (NS_SUCCEEDED(rv)){
-    // Tru64 Cxx needs an explicit get()
-    CopyUTF16toUTF8(Substring(ustr.get(), ustr + dstLen), aResult);
+    CopyUTF16toUTF8(Substring(ustr.get(), ustr.get() + dstLen), aResult);
   }
   return rv;
 }
 
 NS_IMETHODIMP  
 nsUTF8ConverterService::ConvertStringToUTF8(const nsACString &aString, 
                                             const char *aCharset, 
                                             bool aSkipCheck, 
--- a/intl/uconv/util/nsUnicodeDecodeHelper.cpp
+++ b/intl/uconv/util/nsUnicodeDecodeHelper.cpp
@@ -1,16 +1,16 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "unicpriv.h"
 #include "nsUnicodeDecodeHelper.h"
-#include "nsAutoPtr.h"
+#include "mozilla/UniquePtr.h"
 
 //----------------------------------------------------------------------
 // Class nsUnicodeDecodeHelper [implementation]
 nsresult nsUnicodeDecodeHelper::ConvertByTable(
                                      const char * aSrc, 
                                      int32_t * aSrcLength, 
                                      char16_t * aDest, 
                                      int32_t * aDestLength, 
@@ -219,16 +219,16 @@ nsresult nsUnicodeDecodeHelper::ConvertB
 
 nsresult nsUnicodeDecodeHelper::CreateFastTable(
                                      uMappingTable  * aMappingTable,
                                      char16_t * aFastTable, 
                                      int32_t aTableSize)
 {
   int32_t tableSize = aTableSize;
   int32_t buffSize = aTableSize;
-  nsAutoArrayPtr<char> buff(new char [buffSize]);
+  auto buff = mozilla::MakeUnique<char[]>(buffSize);
 
-  char * p = buff;
+  char * p = buff.get();
   for (int32_t i=0; i<aTableSize; i++) *(p++) = i;
-  return ConvertByTable(buff, &buffSize, aFastTable, &tableSize, 
+  return ConvertByTable(buff.get(), &buffSize, aFastTable, &tableSize, 
                         u1ByteCharset, nullptr, aMappingTable);
 }
 
--- a/js/src/asmjs/AsmJSCompile.cpp
+++ b/js/src/asmjs/AsmJSCompile.cpp
@@ -110,61 +110,16 @@ class ModuleCompiler
     int64_t usecBefore()            { return compileResults_->usecBefore(); }
 
     bool usesSignalHandlersForOOB() const   { return compileInputs_.usesSignalHandlersForOOB; }
     CompileRuntime* runtime() const         { return compileInputs_.runtime; }
     CompileCompartment* compartment() const { return compileInputs_.compartment; }
 
     /***************************************************** Mutable interface */
 
-    bool getOrCreateFunctionEntry(uint32_t funcIndex, Label** label)
-    {
-        return compileResults_->getOrCreateFunctionEntry(funcIndex, label);
-    }
-
-    bool finishGeneratingFunction(AsmFunction& func, CodeGenerator& codegen,
-                                  const AsmJSFunctionLabels& labels)
-    {
-        // If we have hit OOM then invariants which we assert below may not
-        // hold, so abort now.
-        if (masm().oom())
-            return false;
-
-        // Code range
-        unsigned line = func.lineno();
-        unsigned column = func.column();
-        PropertyName* funcName = func.name();
-        if (!compileResults_->addCodeRange(AsmJSModule::FunctionCodeRange(funcName, line, labels)))
-            return false;
-
-        // Script counts
-        jit::IonScriptCounts* counts = codegen.extractScriptCounts();
-        if (counts && !compileResults_->addFunctionCounts(counts)) {
-            js_delete(counts);
-            return false;
-        }
-
-        // Slow functions
-        if (func.compileTime() >= 250) {
-            ModuleCompileResults::SlowFunction sf(funcName, func.compileTime(), line, column);
-            if (!compileResults_->slowFunctions().append(Move(sf)))
-                return false;
-        }
-
-#if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
-        // Perf and profiling information
-        unsigned begin = labels.nonProfilingEntry.offset();
-        unsigned end = labels.endAfterOOL.offset();
-        AsmJSModule::ProfiledFunction profiledFunc(funcName, begin, end, line, column);
-        if (!compileResults_->addProfiledFunction(profiledFunc))
-            return false;
-#endif // defined(MOZ_VTUNE) || defined(JS_ION_PERF)
-        return true;
-    }
-
     void finish(ScopedJSDeletePtr<ModuleCompileResults>* results) {
         *results = compileResults_.forget();
     }
 };
 
 } // namespace js
 
 enum class AsmType : uint8_t {
@@ -186,17 +141,16 @@ class FunctionCompiler
   private:
     typedef HashMap<uint32_t, BlockVector, DefaultHasher<uint32_t>, SystemAllocPolicy> LabeledBlockMap;
     typedef HashMap<size_t, BlockVector, DefaultHasher<uint32_t>, SystemAllocPolicy> UnlabeledBlockMap;
     typedef Vector<size_t, 4, SystemAllocPolicy> PositionStack;
     typedef Vector<Type, 4, SystemAllocPolicy> LocalVarTypes;
 
     ModuleCompiler &         m_;
     LifoAlloc &              lifo_;
-    RetType                  retType_;
 
     const AsmFunction &      func_;
     size_t                   pc_;
 
     TempAllocator *          alloc_;
     MIRGraph *               graph_;
     CompileInfo *            info_;
     MIRGenerator *           mirGen_;
@@ -212,30 +166,29 @@ class FunctionCompiler
     LabeledBlockMap          labeledContinues_;
 
     LocalVarTypes            localVarTypes_;
 
   public:
     FunctionCompiler(ModuleCompiler& m, const AsmFunction& func, LifoAlloc& lifo)
       : m_(m),
         lifo_(lifo),
-        retType_(func.returnedType()),
         func_(func),
         pc_(0),
         alloc_(nullptr),
         graph_(nullptr),
         info_(nullptr),
         mirGen_(nullptr),
         curBlock_(nullptr)
     {}
 
-    ModuleCompiler &        m() const            { return m_; }
-    TempAllocator &         alloc() const        { return *alloc_; }
-    LifoAlloc &             lifo() const         { return lifo_; }
-    RetType                 returnedType() const { return retType_; }
+    ModuleCompiler & m() const            { return m_; }
+    TempAllocator &  alloc() const        { return *alloc_; }
+    LifoAlloc &      lifo() const         { return lifo_; }
+    RetType          returnedType() const { return func_.returnedType(); }
 
     bool init()
     {
         return unlabeledBreaks_.init() &&
                unlabeledContinues_.init() &&
                labeledBreaks_.init() &&
                labeledContinues_.init();
     }
@@ -798,17 +751,17 @@ class FunctionCompiler
   private:
     bool callPrivate(MAsmJSCall::Callee callee, const Call& call, MIRType returnType, MDefinition** def)
     {
         if (inDeadCode()) {
             *def = nullptr;
             return true;
         }
 
-        CallSiteDesc::Kind kind = CallSiteDesc::Kind(-1);  // initialize to silence GCC warning
+        CallSiteDesc::Kind kind = CallSiteDesc::Kind(-1);
         switch (callee.which()) {
           case MAsmJSCall::Callee::Internal: kind = CallSiteDesc::Relative; break;
           case MAsmJSCall::Callee::Dynamic:  kind = CallSiteDesc::Register; break;
           case MAsmJSCall::Callee::Builtin:  kind = CallSiteDesc::Register; break;
         }
 
         MAsmJSCall* ins = MAsmJSCall::New(alloc(), CallSiteDesc(call.lineno_, call.column_, kind),
                                           callee, call.regArgs_, returnType, call.spIncrement_);
@@ -816,20 +769,20 @@ class FunctionCompiler
             return false;
 
         curBlock_->add(ins);
         *def = ins;
         return true;
     }
 
   public:
-    bool internalCall(const Signature& sig, Label* entry, const Call& call, MDefinition** def)
+    bool internalCall(const Signature& sig, uint32_t funcIndex, const Call& call, MDefinition** def)
     {
         MIRType returnType = sig.retType().toMIRType();
-        return callPrivate(MAsmJSCall::Callee(entry), call, returnType, def);
+        return callPrivate(MAsmJSCall::Callee(AsmJSInternalCallee(funcIndex)), call, returnType, def);
     }
 
     bool funcPtrCall(const Signature& sig, uint32_t maskLit, uint32_t globalDataOffset, MDefinition* index,
                      const Call& call, MDefinition** def)
     {
         if (inDeadCode()) {
             *def = nullptr;
             return true;
@@ -1738,33 +1691,27 @@ ReadCallLineCol(FunctionCompiler& f, uin
     *line = f.readU32();
     *column = f.readU32();
 }
 
 static bool
 EmitInternalCall(FunctionCompiler& f, RetType retType, MDefinition** def)
 {
     uint32_t funcIndex = f.readU32();
-
-    Label* entry;
-    if (!f.m().getOrCreateFunctionEntry(funcIndex, &entry))
-        return false;
-
     const Signature& sig = *f.readSignature();
-
     MOZ_ASSERT_IF(sig.retType() != RetType::Void, sig.retType() == retType);
 
     uint32_t lineno, column;
     ReadCallLineCol(f, &lineno, &column);
 
     FunctionCompiler::Call call(f, lineno, column);
     if (!EmitCallArgs(f, sig, &call))
         return false;
 
-    return f.internalCall(sig, entry, call, def);
+    return f.internalCall(sig, funcIndex, call, def);
 }
 
 static bool
 EmitFuncPtrCall(FunctionCompiler& f, RetType retType, MDefinition** def)
 {
     uint32_t mask = f.readU32();
     uint32_t globalDataOffset = f.readU32();
 
@@ -3165,45 +3112,47 @@ js::GenerateAsmFunctionMIR(ModuleCompile
 
     f.checkPostconditions();
 
     func.accumulateCompileTime((PRMJ_Now() - before) / PRMJ_USEC_PER_MSEC);
     return true;
 }
 
 bool
-js::GenerateAsmFunctionCode(ModuleCompiler& m, AsmFunction& func, MIRGenerator& mir, LIRGraph& lir)
+js::GenerateAsmFunctionCode(ModuleCompiler& m, AsmFunction& func, MIRGenerator& mir, LIRGraph& lir,
+                            FunctionCompileResults* results)
 {
     JitContext jitContext(m.runtime(), /* CompileCompartment = */ nullptr, &mir.alloc());
 
     int64_t before = PRMJ_Now();
 
     // A single MacroAssembler is reused for all function compilations so
     // that there is a single linear code segment for each module. To avoid
     // spiking memory, a LifoAllocScope in the caller frees all MIR/LIR
     // after each function is compiled. This method is responsible for cleaning
     // out any dangling pointers that the MacroAssembler may have kept.
     m.masm().resetForNewCodeGenerator(mir.alloc());
 
     ScopedJSDeletePtr<CodeGenerator> codegen(js_new<CodeGenerator>(&mir, &lir, &m.masm()));
     if (!codegen)
         return false;
 
-    Label* funcEntry;
-    if (!m.getOrCreateFunctionEntry(func.funcIndex(), &funcEntry))
-        return false;
-
-    AsmJSFunctionLabels labels(*funcEntry, m.stackOverflowLabel());
+    Label entry;
+    AsmJSFunctionLabels labels(entry, m.stackOverflowLabel());
     if (!codegen->generateAsmJS(&labels))
         return false;
 
     func.accumulateCompileTime((PRMJ_Now() - before) / PRMJ_USEC_PER_MSEC);
 
-    if (!m.finishGeneratingFunction(func, *codegen, labels))
-        return false;
+    PropertyName* funcName = func.name();
+    unsigned line = func.lineno();
+
+    // Fill in the results of the function's compilation
+    AsmJSModule::FunctionCodeRange codeRange(funcName, line, labels);
+    results->finishCodegen(func, codeRange, *codegen->extractScriptCounts());
 
     // Unlike regular IonMonkey, which links and generates a new JitCode for
     // every function, we accumulate all the functions in the module in a
     // single MacroAssembler and link at end. Linking asm.js doesn't require a
     // CodeGenerator so we can destroy it now (via ScopedJSDeletePtr).
     return true;
 }
 
--- a/js/src/asmjs/AsmJSCompile.h
+++ b/js/src/asmjs/AsmJSCompile.h
@@ -22,16 +22,17 @@
 #include "jit/CompileWrappers.h"
 
 namespace js {
 
 class AsmFunction;
 class LifoAlloc;
 class ModuleCompiler;
 class ModuleCompileResults;
+class FunctionCompileResults;
 
 namespace jit {
     class LIRGraph;
     class MIRGenerator;
 }
 
 struct ModuleCompileInputs
 {
@@ -71,14 +72,15 @@ class MOZ_RAII AsmModuleCompilerScope
         return *m_;
     }
 
     ~AsmModuleCompilerScope();
 };
 
 bool CreateAsmModuleCompiler(ModuleCompileInputs mci, AsmModuleCompilerScope* scope);
 bool GenerateAsmFunctionMIR(ModuleCompiler& m, LifoAlloc& lifo, AsmFunction& func, jit::MIRGenerator** mir);
-bool GenerateAsmFunctionCode(ModuleCompiler& m, AsmFunction& func, jit::MIRGenerator& mir, jit::LIRGraph& lir);
+bool GenerateAsmFunctionCode(ModuleCompiler& m, AsmFunction& func, jit::MIRGenerator& mir,
+                             jit::LIRGraph& lir, FunctionCompileResults* results);
 void FinishAsmModuleCompilation(ModuleCompiler& m, ScopedJSDeletePtr<ModuleCompileResults>* results);
 
 } // namespace js
 
 #endif // jit_AsmJSCompile_h
--- a/js/src/asmjs/AsmJSGlobals.h
+++ b/js/src/asmjs/AsmJSGlobals.h
@@ -1041,108 +1041,68 @@ class AsmFunction
     const VarInitializerVector& varInitializers() const { return varInitializers_; }
     size_t numLocals() const { return argTypes_.length() + varInitializers_.length(); }
     wasm::RetType returnedType() const {
         MOZ_ASSERT(returnedType_ != wasm::RetType::Which(-1));
         return returnedType_;
     }
 };
 
-const size_t LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 1 << 12;
+class FunctionCompileResults
+{
+    const AsmFunction* func_;
+    jit::IonScriptCounts* counts_;
+    AsmJSModule::FunctionCodeRange codeRange_;
+
+  public:
+    FunctionCompileResults()
+      : func_(nullptr),
+        counts_(nullptr),
+        codeRange_()
+    {}
+
+    const AsmFunction& func() const { MOZ_ASSERT(func_); return *func_; }
+    const AsmJSModule::FunctionCodeRange& codeRange() const { return codeRange_; }
+    jit::IonScriptCounts* counts() const { return counts_; }
+
+    void finishCodegen(AsmFunction& func, AsmJSModule::FunctionCodeRange codeRange,
+                       jit::IonScriptCounts& counts)
+    {
+        func_ = &func;
+        codeRange_ = codeRange;
+        counts_ = &counts;
+    }
+};
 
 class ModuleCompileResults
 {
   public:
-    struct SlowFunction
-    {
-        SlowFunction(PropertyName* name, unsigned ms, unsigned line, unsigned column)
-         : name(name), ms(ms), line(line), column(column)
-        {}
-
-        PropertyName* name;
-        unsigned ms;
-        unsigned line;
-        unsigned column;
-    };
-
-    typedef Vector<SlowFunction                  , 0, SystemAllocPolicy> SlowFunctionVector;
-    typedef Vector<jit::Label*                   , 8, SystemAllocPolicy> LabelVector;
-    typedef Vector<AsmJSModule::FunctionCodeRange, 8, SystemAllocPolicy> FunctionCodeRangeVector;
-    typedef Vector<jit::IonScriptCounts*         , 0, SystemAllocPolicy> ScriptCountVector;
-#if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
-    typedef Vector<AsmJSModule::ProfiledFunction , 0, SystemAllocPolicy> ProfiledFunctionVector;
-#endif // defined(MOZ_VTUNE) || defined(JS_ION_PERF)
 
   private:
-    LifoAlloc               lifo_;
     jit::MacroAssembler     masm_;
 
-    SlowFunctionVector      slowFunctions_;
-    LabelVector             functionEntries_;
-    FunctionCodeRangeVector codeRanges_;
-    ScriptCountVector       functionCounts_;
-#if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
-    ProfiledFunctionVector  profiledFunctions_;
-#endif // defined(MOZ_VTUNE) || defined(JS_ION_PERF)
-
     jit::NonAssertingLabel stackOverflowLabel_;
     jit::NonAssertingLabel asyncInterruptLabel_;
     jit::NonAssertingLabel syncInterruptLabel_;
     jit::NonAssertingLabel onDetachedLabel_;
     jit::NonAssertingLabel onConversionErrorLabel_;
     jit::NonAssertingLabel onOutOfBoundsLabel_;
     int64_t                usecBefore_;
 
   public:
     ModuleCompileResults()
-      : lifo_(LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
-        masm_(jit::MacroAssembler::AsmJSToken()),
+      : masm_(jit::MacroAssembler::AsmJSToken()),
         usecBefore_(PRMJ_Now())
     {}
 
     jit::MacroAssembler& masm()           { return masm_; }
     jit::Label& stackOverflowLabel()      { return stackOverflowLabel_; }
     jit::Label& asyncInterruptLabel()     { return asyncInterruptLabel_; }
     jit::Label& syncInterruptLabel()      { return syncInterruptLabel_; }
     jit::Label& onOutOfBoundsLabel()      { return onOutOfBoundsLabel_; }
     jit::Label& onDetachedLabel()         { return onDetachedLabel_; }
     jit::Label& onConversionErrorLabel()  { return onConversionErrorLabel_; }
     int64_t usecBefore()                  { return usecBefore_; }
-
-    SlowFunctionVector& slowFunctions()   { return slowFunctions_; }
-
-    size_t numFunctionEntries() const     { return functionEntries_.length(); }
-    jit::Label* functionEntry(unsigned i) { return functionEntries_[i]; }
-
-    bool getOrCreateFunctionEntry(unsigned i, jit::Label** label) {
-        if (i == UINT32_MAX)
-            return false;
-        while (functionEntries_.length() <= i) {
-            jit::Label* newEntry = lifo_.new_<jit::Label>();
-            if (!newEntry || !functionEntries_.append(newEntry))
-                return false;
-        }
-        *label = functionEntries_[i];
-        return true;
-    }
-
-    size_t numCodeRanges() const { return codeRanges_.length(); }
-    bool addCodeRange(AsmJSModule::FunctionCodeRange range) { return codeRanges_.append(range); }
-    AsmJSModule::FunctionCodeRange& codeRange(unsigned i) { return codeRanges_[i]; }
-
-    size_t numFunctionCounts() const { return functionCounts_.length(); }
-    bool addFunctionCounts(jit::IonScriptCounts* counts) { return functionCounts_.append(counts); }
-    jit::IonScriptCounts* functionCount(unsigned i) { return functionCounts_[i]; }
-
-#if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
-    size_t numProfiledFunctions() const { return profiledFunctions_.length(); }
-    bool addProfiledFunction(AsmJSModule::ProfiledFunction func) {
-        return profiledFunctions_.append(func);
-    }
-    AsmJSModule::ProfiledFunction& profiledFunction(unsigned i) {
-        return profiledFunctions_[i];
-    }
-#endif // defined(MOZ_VTUNE) || defined(JS_ION_PERF)
 };
 
 } // namespace js
 
 #endif //jit_AsmJSGlobals_h
--- a/js/src/asmjs/AsmJSLink.cpp
+++ b/js/src/asmjs/AsmJSLink.cpp
@@ -464,27 +464,16 @@ ValidateConstant(JSContext* cx, AsmJSMod
     return true;
 }
 
 static bool
 LinkModuleToHeap(JSContext* cx, AsmJSModule& module, Handle<ArrayBufferObjectMaybeShared*> heap)
 {
     uint32_t heapLength = heap->byteLength();
 
-    if (IsDeprecatedAsmJSHeapLength(heapLength)) {
-        LinkFail(cx, "ArrayBuffer byteLengths smaller than 64KB are deprecated and "
-                     "will cause a link-time failure in the future");
-
-        // The goal of deprecation is to give apps some time before linking
-        // fails. However, if warnings-as-errors is turned on (which happens as
-        // part of asm.js testing) an exception may be raised.
-        if (cx->isExceptionPending())
-            return false;
-    }
-
     if (!IsValidAsmJSHeapLength(heapLength)) {
         ScopedJSFreePtr<char> msg(
             JS_smprintf("ArrayBuffer byteLength 0x%x is not a valid heap length. The next "
                         "valid length is 0x%x",
                         heapLength,
                         RoundUpToNextValidAsmJSHeapLength(heapLength)));
         return LinkFail(cx, msg.get());
     }
@@ -626,17 +615,16 @@ ChangeHeap(JSContext* cx, AsmJSModule& m
     }
 
     if (!module.hasArrayView()) {
         args.rval().set(BooleanValue(true));
         return true;
     }
 
     MOZ_ASSERT(IsValidAsmJSHeapLength(heapLength));
-    MOZ_ASSERT(!IsDeprecatedAsmJSHeapLength(heapLength));
 
     if (!ArrayBufferObject::prepareForAsmJS(cx, newBuffer, module.usesSignalHandlersForOOB()))
         return false;
 
     args.rval().set(BooleanValue(module.changeHeap(newBuffer, cx)));
     return true;
 }
 
--- a/js/src/asmjs/AsmJSModule.cpp
+++ b/js/src/asmjs/AsmJSModule.cpp
@@ -317,17 +317,19 @@ AsmJSModule::finish(ExclusiveContext* cx
     // Copy over metadata.
     staticLinkData_.interruptExitOffset = interruptLabel.offset();
     staticLinkData_.outOfBoundsExitOffset = outOfBoundsLabel.offset();
 
     // Heap-access metadata used for link-time patching and fault-handling.
     heapAccesses_ = masm.extractAsmJSHeapAccesses();
 
     // Call-site metadata used for stack unwinding.
-    callSites_ = masm.extractCallSites();
+    const CallSiteAndTargetVector& callSites = masm.callSites();
+    if (!callSites_.appendAll(callSites))
+        return false;
 
     MOZ_ASSERT(pod.functionBytes_ % AsmJSPageSize == 0);
 
     // Absolute link metadata: absolute addresses that refer to some fixed
     // address in the address space.
     AbsoluteLinkArray& absoluteLinks = staticLinkData_.absoluteLinks;
     for (size_t i = 0; i < masm.numAsmJSAbsoluteLinks(); i++) {
         AsmJSAbsoluteLink src = masm.asmJSAbsoluteLink(i);
--- a/js/src/asmjs/AsmJSModule.h
+++ b/js/src/asmjs/AsmJSModule.h
@@ -107,16 +107,20 @@ struct MOZ_STACK_CLASS AsmJSFunctionLabe
     jit::Label  profilingEntry;
     jit::Label& nonProfilingEntry;
     jit::Label  profilingJump;
     jit::Label  profilingEpilogue;
     jit::Label  profilingReturn;
     jit::Label  endAfterOOL;
     mozilla::Maybe<jit::Label> overflowThunk;
     jit::Label& overflowExit;
+
+  private:
+    AsmJSFunctionLabels(const AsmJSFunctionLabels&) = delete;
+    AsmJSFunctionLabels& operator=(const AsmJSFunctionLabels&) = delete;
 };
 
 // Represents the type and value of an asm.js numeric literal.
 //
 // A literal is a double iff the literal contains a decimal point (even if the
 // fractional part is 0). Otherwise, integers may be classified:
 //  fixnum: [0, 2^31)
 //  negative int: [-2^31, 0)
@@ -626,16 +630,20 @@ class AsmJSModule
     };
 
     class FunctionCodeRange : public CodeRange
     {
       private:
         PropertyName* name_;
 
       public:
+        FunctionCodeRange()
+          : CodeRange(), name_(nullptr)
+        {}
+
         FunctionCodeRange(PropertyName* name, uint32_t lineNumber, const AsmJSFunctionLabels& l)
           : CodeRange(UINT32_MAX, lineNumber, l), name_(name)
         {}
 
         PropertyName* name() const { return name_; }
 
         void initNameIndex(uint32_t nameIndex) {
             MOZ_ASSERT(nameIndex_ == UINT32_MAX);
@@ -1154,24 +1162,24 @@ class AsmJSModule
         return true;
     }
     bool addCodeRange(CodeRange::Kind kind, uint32_t begin, uint32_t end) {
         return codeRanges_.append(CodeRange(kind, begin, end));
     }
     bool addCodeRange(CodeRange::Kind kind, uint32_t begin, uint32_t pret, uint32_t end) {
         return codeRanges_.append(CodeRange(kind, begin, pret, end));
     }
-    bool addFunctionCodeRange(PropertyName* name, FunctionCodeRange&& codeRange)
+    bool addFunctionCodeRange(PropertyName* name, FunctionCodeRange codeRange)
     {
         MOZ_ASSERT(!isFinished());
         MOZ_ASSERT(name->isTenured());
         if (names_.length() >= UINT32_MAX)
             return false;
         codeRange.initNameIndex(names_.length());
-        return names_.append(name) && codeRanges_.append(Move(codeRange));
+        return names_.append(name) && codeRanges_.append(codeRange);
     }
     bool addBuiltinThunkCodeRange(AsmJSExit::BuiltinKind builtin, uint32_t begin,
                                   uint32_t profilingReturn, uint32_t end)
     {
         return builtinThunkOffsets_.append(begin) &&
                codeRanges_.append(CodeRange(builtin, begin, profilingReturn, end));
     }
     bool addExit(unsigned ffiIndex, unsigned* exitIndex) {
--- a/js/src/asmjs/AsmJSValidate.cpp
+++ b/js/src/asmjs/AsmJSValidate.cpp
@@ -579,16 +579,18 @@ TypedArrayLoadType(Scalar::Type viewType
         return Type::MaybeFloat;
       case Scalar::Float64:
         return Type::MaybeDouble;
       default:;
     }
     MOZ_CRASH("Unexpected array type");
 }
 
+const size_t LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 1 << 12;
+
 namespace {
 
 // The ModuleValidator encapsulates the entire validation of an asm.js module.
 // Its lifetime goes from the validation of the top components of an asm.js
 // module (all the globals), the emission of bytecode for all the functions in
 // the module and the validation of function's pointer tables. It also finishes
 // the compilation of all the module's stubs.
 //
@@ -880,22 +882,35 @@ class MOZ_STACK_CLASS ModuleValidator
                 hn = AddToHash(hn, args[i].which());
             return hn;
         }
         static bool match(const ExitDescriptor& lhs, const Lookup& rhs) {
             return lhs.name_ == rhs.name_ && *lhs.sig_ == *rhs.sig_;
         }
     };
 
+    struct SlowFunction
+    {
+        SlowFunction(PropertyName* name, unsigned ms, unsigned line, unsigned column)
+         : name(name), ms(ms), line(line), column(column)
+        {}
+
+        PropertyName* name;
+        unsigned ms;
+        unsigned line;
+        unsigned column;
+    };
+
   private:
     typedef HashMap<PropertyName*, Global*> GlobalMap;
     typedef HashMap<PropertyName*, MathBuiltin> MathNameMap;
     typedef HashMap<PropertyName*, AsmJSAtomicsBuiltinFunction> AtomicsNameMap;
     typedef HashMap<PropertyName*, AsmJSSimdOperation> SimdOperationNameMap;
     typedef Vector<ArrayView> ArrayViewVector;
+    typedef Vector<SlowFunction> SlowFunctionVector;
 
   public:
     typedef HashMap<ExitDescriptor, unsigned, ExitDescriptor> ExitMap;
 
   private:
     ExclusiveContext*                       cx_;
     AsmJSParser&                            parser_;
 
@@ -919,16 +934,19 @@ class MOZ_STACK_CLASS ModuleValidator
     uint32_t                                errorOffset_;
     bool                                    errorOverRecursed_;
 
     bool                                    canValidateChangeHeap_;
     bool                                    hasChangeHeap_;
     bool                                    supportsSimd_;
     bool                                    atomicsPresent_;
 
+    Vector<uint32_t>                        functionEntryOffsets_;
+    SlowFunctionVector                      slowFunctions_;
+
     ScopedJSDeletePtr<ModuleCompileResults> compileResults_;
     DebugOnly<bool>                         finishedFunctionBodies_;
 
   public:
     ModuleValidator(ExclusiveContext* cx, AsmJSParser& parser)
       : cx_(cx),
         parser_(parser),
         moduleLifo_(LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
@@ -944,16 +962,18 @@ class MOZ_STACK_CLASS ModuleValidator
         moduleFunctionName_(nullptr),
         errorString_(nullptr),
         errorOffset_(UINT32_MAX),
         errorOverRecursed_(false),
         canValidateChangeHeap_(false),
         hasChangeHeap_(false),
         supportsSimd_(cx->jitSupportsSimd()),
         atomicsPresent_(false),
+        functionEntryOffsets_(cx),
+        slowFunctions_(cx),
         compileResults_(nullptr),
         finishedFunctionBodies_(false)
     {
         MOZ_ASSERT(moduleFunctionNode_->pn_funbox == parser.pc->sc->asFunctionBox());
     }
 
     ~ModuleValidator() {
         if (errorString_) {
@@ -1394,16 +1414,19 @@ class MOZ_STACK_CLASS ModuleValidator
         return *functions_[i];
     }
     unsigned numFuncPtrTables() const {
         return funcPtrTables_.length();
     }
     FuncPtrTable& funcPtrTable(unsigned i) const {
         return *funcPtrTables_[i];
     }
+    uint32_t functionEntryOffset(unsigned i) {
+        return functionEntryOffsets_[i];
+    }
 
     const Global* lookupGlobal(PropertyName* name) const {
         if (GlobalMap::Ptr p = globals_.lookup(name))
             return p->value();
         return nullptr;
     }
 
     Func* lookupFunction(PropertyName* name) {
@@ -1440,19 +1463,53 @@ class MOZ_STACK_CLASS ModuleValidator
     // End-of-compilation utils
     MacroAssembler& masm()           { return compileResults_->masm(); }
     Label& stackOverflowLabel()      { return compileResults_->stackOverflowLabel(); }
     Label& asyncInterruptLabel()     { return compileResults_->asyncInterruptLabel(); }
     Label& syncInterruptLabel()      { return compileResults_->syncInterruptLabel(); }
     Label& onDetachedLabel()         { return compileResults_->onDetachedLabel(); }
     Label& onOutOfBoundsLabel()      { return compileResults_->onOutOfBoundsLabel(); }
     Label& onConversionErrorLabel()  { return compileResults_->onConversionErrorLabel(); }
-    Label* functionEntry(unsigned i) { return compileResults_->functionEntry(i); }
     ExitMap::Range allExits() const  { return exits_.all(); }
 
+    bool finishGeneratingFunction(FunctionCompileResults& results) {
+        const AsmFunction& func = results.func();
+        unsigned i = func.funcIndex();
+        if (functionEntryOffsets_.length() <= i && !functionEntryOffsets_.resize(i + 1))
+            return false;
+
+        AsmJSModule::FunctionCodeRange codeRange = results.codeRange();
+        functionEntryOffsets_[i] = codeRange.entry();
+
+        PropertyName* funcName = func.name();
+        unsigned line = func.lineno();
+        unsigned column = func.column();
+
+        // These must be done before the module is done with function bodies.
+        if (results.counts() && !module().addFunctionCounts(results.counts()))
+            return false;
+        if (!module().addFunctionCodeRange(codeRange.name(), codeRange))
+            return false;
+
+        unsigned compileTime = func.compileTime();
+        if (compileTime >= 250) {
+            if (!slowFunctions_.append(SlowFunction(funcName, compileTime, line, column)))
+                return false;
+        }
+
+#if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
+        // Perf and profiling information
+        AsmJSModule::ProfiledFunction pf(funcName, codeRange.entry(), codeRange.end(), line, column);
+        if (!module().addProfiledFunction(Move(pf)))
+            return false;
+#endif // defined(MOZ_VTUNE) || defined(JS_ION_PERF)
+
+        return true;
+    }
+
     bool finishGeneratingEntry(unsigned exportIndex, Label* begin) {
         MOZ_ASSERT(finishedFunctionBodies_);
         module_->exportedFunction(exportIndex).initCodeOffset(begin->offset());
         uint32_t end = masm().currentOffset();
         return module_->addCodeRange(AsmJSModule::CodeRange::Entry, begin->offset(), end);
     }
     bool finishGeneratingInterpExit(unsigned exitIndex, Label* begin, Label* profilingReturn) {
         MOZ_ASSERT(finishedFunctionBodies_);
@@ -1499,18 +1556,17 @@ class MOZ_STACK_CLASS ModuleValidator
         // Finally, convert all the function-pointer table elements into
         // RelativeLinks that will be patched by AsmJSModule::staticallyLink.
         for (unsigned tableIndex = 0; tableIndex < numFuncPtrTables(); tableIndex++) {
             ModuleValidator::FuncPtrTable& table = funcPtrTable(tableIndex);
             unsigned tableBaseOffset = module_->offsetOfGlobalData() + table.globalDataOffset();
             for (unsigned elemIndex = 0; elemIndex < table.numElems(); elemIndex++) {
                 AsmJSModule::RelativeLink link(AsmJSModule::RelativeLink::RawPointer);
                 link.patchAtOffset = tableBaseOffset + elemIndex * sizeof(uint8_t*);
-                Label* entry = functionEntry(table.elem(elemIndex).funcIndex());
-                link.targetOffset = entry->offset();
+                link.targetOffset = functionEntryOffset(table.elem(elemIndex).funcIndex());
                 if (!module_->addRelativeLink(link))
                     return false;
             }
         }
 
         *module = module_.forget();
         return true;
     }
@@ -1525,34 +1581,24 @@ class MOZ_STACK_CLASS ModuleValidator
             module_->setViewsAreShared();
         }
         module_->startFunctionBodies();
     }
     bool finishFunctionBodies(ScopedJSDeletePtr<ModuleCompileResults>* compileResults) {
         // Take ownership of compilation results
         compileResults_ = compileResults->forget();
 
-        // These must be done before the module is done with function bodies.
-        for (size_t i = 0; i < compileResults_->numFunctionCounts(); ++i) {
-            if (!module().addFunctionCounts(compileResults_->functionCount(i)))
-                return false;
-        }
-
-#if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
-        for (size_t i = 0; i < compileResults_->numProfiledFunctions(); ++i) {
-            if (!module().addProfiledFunction(Move(compileResults_->profiledFunction(i))))
-                return false;
-        }
-#endif // defined(MOZ_VTUNE) || defined(JS_ION_PERF)
-
-        // Hand in code ranges to the AsmJSModule
-        for (size_t i = 0; i < compileResults_->numCodeRanges(); ++i) {
-            AsmJSModule::FunctionCodeRange& codeRange = compileResults_->codeRange(i);
-            if (!module().addFunctionCodeRange(codeRange.name(), Move(codeRange)))
-                return false;
+        // Patch internal calls to their final positions
+        for (auto& cs : masm().callSites()) {
+            if (!cs.isInternal())
+                continue;
+            MOZ_ASSERT(cs.kind() == CallSiteDesc::Relative);
+            uint32_t callerOffset = cs.returnAddressOffset();
+            uint32_t calleeOffset = functionEntryOffset(cs.targetIndex());
+            masm().patchCall(callerOffset, calleeOffset);
         }
 
         // When an interrupt is triggered, all function code is mprotected and,
         // for sanity, stub code (particularly the interrupt stub) is not.
         // Protection works at page granularity, so we need to ensure that no
         // stub code gets into the function code pages.
         // TODO; this is no longer true and could be removed, see also
         // bug 1200609.
@@ -1564,29 +1610,28 @@ class MOZ_STACK_CLASS ModuleValidator
         return true;
     }
 
     void buildCompilationTimeReport(JS::AsmJSCacheResult cacheResult, ScopedJSFreePtr<char>* out) {
 #ifndef JS_MORE_DETERMINISTIC
         ScopedJSFreePtr<char> slowFuns;
         int64_t usecAfter = PRMJ_Now();
         int msTotal = (usecAfter - compileResults_->usecBefore()) / PRMJ_USEC_PER_MSEC;
-        ModuleCompileResults::SlowFunctionVector& slowFunctions = compileResults_->slowFunctions();
-        if (!slowFunctions.empty()) {
-            slowFuns.reset(JS_smprintf("; %d functions compiled slowly: ", slowFunctions.length()));
+        if (!slowFunctions_.empty()) {
+            slowFuns.reset(JS_smprintf("; %d functions compiled slowly: ", slowFunctions_.length()));
             if (!slowFuns)
                 return;
-            for (unsigned i = 0; i < slowFunctions.length(); i++) {
-                ModuleCompileResults::SlowFunction& func = slowFunctions[i];
+            for (unsigned i = 0; i < slowFunctions_.length(); i++) {
+                ModuleValidator::SlowFunction& func = slowFunctions_[i];
                 JSAutoByteString name;
                 if (!AtomToPrintableString(cx_, func.name, &name))
                     return;
                 slowFuns.reset(JS_smprintf("%s%s:%u:%u (%ums)%s", slowFuns.get(),
                                            name.ptr(), func.line, func.column, func.ms,
-                                           i+1 < slowFunctions.length() ? ", " : ""));
+                                           i+1 < slowFunctions_.length() ? ", " : ""));
                 if (!slowFuns)
                     return;
             }
         }
         const char* cacheString = "";
         switch (cacheResult) {
           case JS::AsmJSCache_Success:
             cacheString = "stored in cache";
@@ -6534,17 +6579,20 @@ CheckFunctionsSequential(ModuleValidator
 
             lir = GenerateLIR(mir);
             if (!lir)
                 return m.failOffset(func->srcBegin(), "internal compiler failure (probably out of memory)");
 
             func->accumulateCompileTime((PRMJ_Now() - before) / PRMJ_USEC_PER_MSEC);
         }
 
-        if (!GenerateAsmFunctionCode(mc, *func, *mir, *lir))
+        FunctionCompileResults results;
+        if (!GenerateAsmFunctionCode(mc, *func, *mir, *lir, &results))
+            return false;
+        if (!m.finishGeneratingFunction(results))
             return false;
     }
 
     if (!CheckAllFunctionsDefined(m))
         return false;
 
     FinishAsmModuleCompilation(mc, compileResults);
     return true;
@@ -6615,28 +6663,31 @@ GetFinishedCompilation(ModuleCompiler& m
         }
         HelperThreadState().wait(GlobalHelperThreadState::CONSUMER);
     }
 
     return nullptr;
 }
 
 static bool
-GetUsedTask(ModuleCompiler& m, ParallelGroupState& group, AsmJSParallelTask** outTask)
+GetUsedTask(ModuleValidator& m, ModuleCompiler& mc, ParallelGroupState& group, AsmJSParallelTask** outTask)
 {
     // Block until a used LifoAlloc becomes available.
-    AsmJSParallelTask* task = GetFinishedCompilation(m, group);
+    AsmJSParallelTask* task = GetFinishedCompilation(mc, group);
     if (!task)
         return false;
 
     auto& func = *reinterpret_cast<AsmFunction*>(task->func);
     func.accumulateCompileTime(task->compileTime);
 
     // Perform code generation on the main thread.
-    if (!GenerateAsmFunctionCode(m, func, *task->mir, *task->lir))
+    FunctionCompileResults results;
+    if (!GenerateAsmFunctionCode(mc, func, *task->mir, *task->lir, &results))
+        return false;
+    if (!m.finishGeneratingFunction(results))
         return false;
 
     group.compiledJobs++;
 
     // Clear the LifoAlloc for use by another helper.
     TempAllocator& tempAlloc = task->mir->alloc();
     tempAlloc.TempAllocator::~TempAllocator();
     task->lifo.releaseAll();
@@ -6678,17 +6729,17 @@ CheckFunctionsParallel(ModuleValidator& 
     AsmJSParallelTask* task = nullptr;
     for (unsigned i = 0;; i++) {
         TokenKind tk;
         if (!PeekToken(m.parser(), &tk))
             return false;
         if (tk != TOK_FUNCTION)
             break;
 
-        if (!task && !GetUnusedTask(group, i, &task) && !GetUsedTask(mc, group, &task))
+        if (!task && !GetUnusedTask(group, i, &task) && !GetUsedTask(m, mc, group, &task))
             return false;
 
         AsmFunction* func;
         if (!CheckFunction(m, task->lifo, &func))
             return false;
 
         // In the case of the change-heap function, no function is produced.
         if (!func)
@@ -6706,17 +6757,17 @@ CheckFunctionsParallel(ModuleValidator& 
 
         group.outstandingJobs++;
         task = nullptr;
     }
 
     // Block for all outstanding helpers to complete.
     while (group.outstandingJobs > 0) {
         AsmJSParallelTask* ignored = nullptr;
-        if (!GetUsedTask(mc, group, &ignored))
+        if (!GetUsedTask(m, mc, group, &ignored))
             return false;
     }
 
     if (!CheckAllFunctionsDefined(m))
         return false;
 
     MOZ_ASSERT(group.outstandingJobs == 0);
     MOZ_ASSERT(group.compiledJobs == m.numFunctions());
@@ -7176,17 +7227,19 @@ GenerateEntry(ModuleValidator& m, unsign
                 MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected stack arg type");
             }
             break;
         }
     }
 
     // Call into the real function.
     masm.assertStackAlignment(AsmJSStackAlignment);
-    masm.call(CallSiteDesc(CallSiteDesc::Relative), m.functionEntry(func.funcIndex()));
+    Label funcLabel;
+    funcLabel.bind(m.functionEntryOffset(func.funcIndex()));
+    masm.call(CallSiteDesc(CallSiteDesc::Relative), &funcLabel);
 
     // Recover the stack pointer value before dynamic alignment.
     masm.loadAsmJSActivation(scratch);
     masm.loadStackPtr(Address(scratch, AsmJSActivation::offsetOfEntrySP()));
     masm.setFramePushed(FramePushedForEntrySP);
 
     // Recover the 'argv' pointer which was saved before aligning the stack.
     masm.Pop(argv);
--- a/js/src/asmjs/AsmJSValidate.h
+++ b/js/src/asmjs/AsmJSValidate.h
@@ -46,19 +46,24 @@ typedef frontend::ParseContext<frontend:
 // indicates whether an error was reported which the caller should propagate.
 // If no error was reported, the function may still fail to validate as asm.js.
 // In this case, the parser.tokenStream has been advanced an indeterminate
 // amount and the entire function should be reparsed from the beginning.
 extern bool
 ValidateAsmJS(ExclusiveContext* cx, AsmJSParser& parser, frontend::ParseNode* stmtList,
              bool* validated);
 
+// The minimum heap length for asm.js.
+const size_t AsmJSMinHeapLength = 64 * 1024;
+
 // The assumed page size; dynamically checked in ValidateAsmJS.
 const size_t AsmJSPageSize = 4096;
 
+static_assert(AsmJSMinHeapLength % AsmJSPageSize == 0, "Invalid page size");
+
 #if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
 
 // Targets define AsmJSImmediateRange to be the size of an address immediate,
 // and AsmJSCheckedImmediateRange, to be the size of an address immediate that
 // can be supported by signal-handler OOB handling.
 static_assert(jit::AsmJSCheckedImmediateRange <= jit::AsmJSImmediateRange,
               "AsmJSImmediateRange should be the size of an unconstrained "
               "address immediate");
@@ -81,47 +86,39 @@ static const size_t AsmJSMappedSize = 4 
 //  the heap object's byteLength must be either
 //    2^n for n in [12, 24)
 //  or
 //    2^24 * n for n >= 1.
 
 inline uint32_t
 RoundUpToNextValidAsmJSHeapLength(uint32_t length)
 {
-    if (length <= 4 * 1024)
-        return 4 * 1024;
+    if (length <= AsmJSMinHeapLength)
+        return AsmJSMinHeapLength;
 
     if (length <= 16 * 1024 * 1024)
         return mozilla::RoundUpPow2(length);
 
     MOZ_ASSERT(length <= 0xff000000);
     return (length + 0x00ffffff) & ~0x00ffffff;
 }
 
 inline bool
 IsValidAsmJSHeapLength(uint32_t length)
 {
-    bool valid = length >= 4 * 1024 &&
+    bool valid = length >= AsmJSMinHeapLength &&
                  (IsPowerOfTwo(length) ||
                   (length & 0x00ffffff) == 0);
 
     MOZ_ASSERT_IF(valid, length % AsmJSPageSize == 0);
     MOZ_ASSERT_IF(valid, length == RoundUpToNextValidAsmJSHeapLength(length));
 
     return valid;
 }
 
-// For now, power-of-2 lengths in this range are accepted, but in the future
-// we'll change this to cause link-time failure.
-inline bool
-IsDeprecatedAsmJSHeapLength(uint32_t length)
-{
-    return length >= 4 * 1024 && length < 64 * 1024 && IsPowerOfTwo(length);
-}
-
 // Return whether asm.js optimization is inhibited by the platform or
 // dynamically disabled:
 extern bool
 IsAsmJSCompilationAvailable(JSContext* cx, unsigned argc, JS::Value* vp);
 
 } // namespace js
 
 #endif // jit_AsmJS_h
--- a/js/src/builtin/Iterator.js
+++ b/js/src/builtin/Iterator.js
@@ -96,17 +96,17 @@ function CreateListIterator(array) {
     let next = function() {
         if (!IsObject(this) || !IsListIterator(this))
             return callFunction(CallListIteratorMethodIfWrapped, this, "ListIteratorNext");
 
         if (ActiveFunction() !== UnsafeGetReservedSlot(this, ITERATOR_SLOT_NEXT_METHOD))
             ThrowTypeError(JSMSG_INCOMPATIBLE_METHOD, "next", "method", ToString(this));
 
         let array = UnsafeGetObjectFromReservedSlot(this, ITERATOR_SLOT_TARGET);
-        let index = UnsafeGetInt32FromReservedSlot(this, ITERATOR_SLOT_NEXT_INDEX);
+        let index = UnsafeGetReservedSlot(this, ITERATOR_SLOT_NEXT_INDEX);
 
         if (index >= ToLength(array.length)) {
             UnsafeSetReservedSlot(this, ITERATOR_SLOT_NEXT_INDEX, 1/0);
             return { value: undefined, done: true };
         }
 
         UnsafeSetReservedSlot(this, ITERATOR_SLOT_NEXT_INDEX, index + 1);
         return { value: array[index], done: false };
--- a/js/src/jit-test/tests/asm.js/neuter-during-arguments-coercion.js
+++ b/js/src/jit-test/tests/asm.js/neuter-during-arguments-coercion.js
@@ -10,17 +10,17 @@ function f(stdlib, foreign, buffer)
     v=v|0;
     i32[5] = v;
   }
   return set;
 }
 if (isAsmJSCompilationAvailable())
   assertEq(isAsmJSModule(f), true);
 
-var i32 = new Int32Array(4096);
+var i32 = new Int32Array(65536);
 var buffer = i32.buffer;
 var set = f(this, null, buffer);
 if (isAsmJSCompilationAvailable())
   assertEq(isAsmJSFunction(set), true);
 
 try
 {
   set({ valueOf: function() { neuter(buffer, "same-data"); return 17; } });
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug1220766.js
@@ -0,0 +1,3 @@
+iter = getSelfHostedValue("CreateListIterator")([]);
+iter.next();
+iter.next();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1221359.js
@@ -0,0 +1,7 @@
+if (!('oomTest' in this))
+    quit();
+
+oomTest(() => getBacktrace({
+    locals: true,
+    thisprops: true
+}));
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -13702,27 +13702,27 @@ class MAsmJSCall final
 {
   public:
     class Callee {
       public:
         enum Which { Internal, Dynamic, Builtin };
       private:
         Which which_;
         union {
-            Label* internal_;
+            AsmJSInternalCallee internal_;
             MDefinition* dynamic_;
             AsmJSImmKind builtin_;
         } u;
       public:
         Callee() {}
-        explicit Callee(Label* callee) : which_(Internal) { u.internal_ = callee; }
+        explicit Callee(AsmJSInternalCallee callee) : which_(Internal) { u.internal_ = callee; }
         explicit Callee(MDefinition* callee) : which_(Dynamic) { u.dynamic_ = callee; }
         explicit Callee(AsmJSImmKind callee) : which_(Builtin) { u.builtin_ = callee; }
         Which which() const { return which_; }
-        Label* internal() const { MOZ_ASSERT(which_ == Internal); return u.internal_; }
+        AsmJSInternalCallee internal() const { MOZ_ASSERT(which_ == Internal); return u.internal_; }
         MDefinition* dynamic() const { MOZ_ASSERT(which_ == Dynamic); return u.dynamic_; }
         AsmJSImmKind builtin() const { MOZ_ASSERT(which_ == Builtin); return u.builtin_; }
     };
 
   private:
     CallSiteDesc desc_;
     Callee callee_;
     FixedList<AnyRegister> argRegs_;
--- a/js/src/jit/MacroAssembler-inl.h
+++ b/js/src/jit/MacroAssembler-inl.h
@@ -74,25 +74,32 @@ MacroAssembler::PushWithPatch(ImmPtr imm
 }
 
 // ===============================================================
 // Simple call functions.
 
 void
 MacroAssembler::call(const CallSiteDesc& desc, const Register reg)
 {
-    call(reg);
-    append(desc, currentOffset(), framePushed());
+    CodeOffsetLabel l = call(reg);
+    append(desc, l, framePushed());
 }
 
 void
 MacroAssembler::call(const CallSiteDesc& desc, Label* label)
 {
-    call(label);
-    append(desc, currentOffset(), framePushed());
+    CodeOffsetLabel l = call(label);
+    append(desc, l, framePushed());
+}
+
+void
+MacroAssembler::call(const CallSiteDesc& desc, AsmJSInternalCallee callee)
+{
+    CodeOffsetLabel l = callWithPatch();
+    append(desc, l, framePushed(), callee.index);
 }
 
 // ===============================================================
 // ABI function calls.
 
 void
 MacroAssembler::passABIArg(Register reg)
 {
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -471,28 +471,32 @@ class MacroAssembler : public MacroAssem
     // Manipulated by the AutoGenericRegisterScope class.
     AllocatableRegisterSet debugTrackedRegisters_;
 #endif // DEBUG
 
   public:
     // ===============================================================
     // Simple call functions.
 
-    void call(Register reg) PER_SHARED_ARCH;
+    CodeOffsetLabel call(Register reg) PER_SHARED_ARCH;
+    CodeOffsetLabel call(Label* label) PER_SHARED_ARCH;
     void call(const Address& addr) DEFINED_ON(x86_shared);
-    void call(Label* label) PER_SHARED_ARCH;
     void call(ImmWord imm) PER_SHARED_ARCH;
     // Call a target native function, which is neither traceable nor movable.
     void call(ImmPtr imm) PER_SHARED_ARCH;
     void call(AsmJSImmPtr imm) PER_SHARED_ARCH;
     // Call a target JitCode, which must be traceable, and may be movable.
     void call(JitCode* c) PER_SHARED_ARCH;
 
     inline void call(const CallSiteDesc& desc, const Register reg);
     inline void call(const CallSiteDesc& desc, Label* label);
+    inline void call(const CallSiteDesc& desc, AsmJSInternalCallee callee);
+
+    CodeOffsetLabel callWithPatch() PER_SHARED_ARCH;
+    void patchCall(uint32_t callerOffset, uint32_t calleeOffset) PER_SHARED_ARCH;
 
     // Push the return address and make a call. On platforms where this function
     // is not defined, push the link register (pushReturnAddress) at the entry
     // point of the callee.
     void callAndPushReturnAddress(Register reg) DEFINED_ON(mips_shared, x86_shared);
     void callAndPushReturnAddress(Label* label) DEFINED_ON(mips_shared, x86_shared);
 
     void pushReturnAddress() DEFINED_ON(arm, arm64);
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -5093,27 +5093,29 @@ MacroAssembler::reserveStack(uint32_t am
     if (amount)
         ma_sub(Imm32(amount), sp);
     adjustFrame(amount);
 }
 
 // ===============================================================
 // Simple call functions.
 
-void
+CodeOffsetLabel
 MacroAssembler::call(Register reg)
 {
     as_blx(reg);
-}
-
-void
+    return CodeOffsetLabel(currentOffset());
+}
+
+CodeOffsetLabel
 MacroAssembler::call(Label* label)
 {
-    // For now, assume that it'll be nearby?
+    // For now, assume that it'll be nearby.
     as_bl(label, Always);
+    return CodeOffsetLabel(currentOffset());
 }
 
 void
 MacroAssembler::call(ImmWord imm)
 {
     call(ImmPtr((void*)imm.value));
 }
 
@@ -5143,16 +5145,30 @@ MacroAssembler::call(JitCode* c)
     else
         rs = L_LDR;
 
     ScratchRegisterScope scratch(*this);
     ma_movPatchable(ImmPtr(c->raw()), scratch, Always, rs);
     callJitNoProfiler(scratch);
 }
 
+CodeOffsetLabel
+MacroAssembler::callWithPatch()
+{
+    // For now, assume that it'll be nearby.
+    as_bl(BOffImm(), Always, /* documentation */ nullptr);
+    return CodeOffsetLabel(currentOffset());
+}
+void
+MacroAssembler::patchCall(uint32_t callerOffset, uint32_t calleeOffset)
+{
+    BufferOffset inst(callerOffset - 4);
+    as_bl(BufferOffset(calleeOffset).diffB<BOffImm>(inst), Always, inst);
+}
+
 void
 MacroAssembler::pushReturnAddress()
 {
     push(lr);
 }
 
 // ===============================================================
 // ABI function calls.
@@ -5286,13 +5302,13 @@ MacroAssembler::pushFakeReturnAddress(Re
     // could just do a Push(pc), and ignore the nop as well as the pool.
     enterNoPool(2);
     DebugOnly<uint32_t> offsetBeforePush = currentOffset();
     Push(pc); // actually pushes $pc + 8.
     ma_nop();
     uint32_t pseudoReturnOffset = currentOffset();
     leaveNoPool();
 
-    MOZ_ASSERT(pseudoReturnOffset - offsetBeforePush == 8);
+    MOZ_ASSERT_IF(!oom(), pseudoReturnOffset - offsetBeforePush == 8);
     return pseudoReturnOffset;
 }
 
 //}}} check_macroassembler_style
--- a/js/src/jit/arm64/MacroAssembler-arm64.cpp
+++ b/js/src/jit/arm64/MacroAssembler-arm64.cpp
@@ -497,28 +497,30 @@ MacroAssembler::reserveStack(uint32_t am
     // It would save some instructions if we had a fixed frame size.
     vixl::MacroAssembler::Claim(Operand(amount));
     adjustFrame(amount);
 }
 
 // ===============================================================
 // Simple call functions.
 
-void
+CodeOffsetLabel
 MacroAssembler::call(Register reg)
 {
     syncStackPtr();
     Blr(ARMRegister(reg, 64));
+    return CodeOffsetLabel(currentOffset());
 }
 
-void
+CodeOffsetLabel
 MacroAssembler::call(Label* label)
 {
     syncStackPtr();
     Bl(label);
+    return CodeOffsetLabel(currentOffset());
 }
 
 void
 MacroAssembler::call(ImmWord imm)
 {
     call(ImmPtr((void*)imm.value));
 }
 
@@ -546,16 +548,28 @@ MacroAssembler::call(JitCode* c)
     vixl::UseScratchRegisterScope temps(this);
     const ARMRegister scratch64 = temps.AcquireX();
     syncStackPtr();
     BufferOffset off = immPool64(scratch64, uint64_t(c->raw()));
     addPendingJump(off, ImmPtr(c->raw()), Relocation::JITCODE);
     blr(scratch64);
 }
 
+CodeOffsetLabel
+MacroAssembler::callWithPatch()
+{
+    MOZ_CRASH("NYI");
+    return CodeOffsetLabel();
+}
+void
+MacroAssembler::patchCall(uint32_t callerOffset, uint32_t calleeOffset)
+{
+    MOZ_CRASH("NYI");
+}
+
 void
 MacroAssembler::pushReturnAddress()
 {
     push(lr);
 }
 
 // ===============================================================
 // ABI function calls.
--- a/js/src/jit/mips-shared/MacroAssembler-mips-shared.cpp
+++ b/js/src/jit/mips-shared/MacroAssembler-mips-shared.cpp
@@ -850,27 +850,40 @@ MacroAssembler::Pop(const ValueOperand& 
     popValue(val);
     framePushed_ -= sizeof(Value);
 }
 
 
 // ===============================================================
 // Simple call functions.
 
-void
+CodeOffsetLabel
 MacroAssembler::call(Register reg)
 {
     as_jalr(reg);
     as_nop();
+    return CodeOffsetLabel(currentOffset());
 }
 
-void
+CodeOffsetLabel
 MacroAssembler::call(Label* label)
 {
     ma_bal(label);
+    return CodeOffsetLabel(currentOffset());
+}
+
+CodeOffsetLabel
+MacroAssembler::callWithPatch()
+{
+    MOZ_CRASH("NYI");
+}
+void
+MacroAssembler::patchCall(uint32_t callerOffset, uint32_t calleeOffset)
+{
+    MOZ_CRASH("NYI");
 }
 
 void
 MacroAssembler::call(AsmJSImmPtr target)
 {
     movePtr(target, CallReg);
     call(CallReg);
 }
--- a/js/src/jit/shared/Assembler-shared.h
+++ b/js/src/jit/shared/Assembler-shared.h
@@ -654,17 +654,17 @@ class CallSiteDesc
     };
     CallSiteDesc() {}
     explicit CallSiteDesc(Kind kind)
       : line_(0), column_(0), kind_(kind)
     {}
     CallSiteDesc(uint32_t line, uint32_t column, Kind kind)
       : line_(line), column_(column), kind_(kind)
     {
-        MOZ_ASSERT(column <= INT32_MAX);
+        MOZ_ASSERT(column_ == column, "column must fit in 31 bits");
     }
     uint32_t line() const { return line_; }
     uint32_t column() const { return column_; }
     Kind kind() const { return Kind(kind_); }
 };
 
 // Adds to CallSiteDesc the metadata necessary to walk the stack given an
 // initial stack-pointer.
@@ -689,16 +689,33 @@ class CallSite : public CallSiteDesc
     // function was called. In particular, this includes the pushed return
     // address on all archs (whether or not the call instruction pushes the
     // return address (x86/x64) or the prologue does (ARM/MIPS)).
     uint32_t stackDepth() const { return stackDepth_; }
 };
 
 typedef Vector<CallSite, 0, SystemAllocPolicy> CallSiteVector;
 
+class CallSiteAndTarget : public CallSite
+{
+    uint32_t targetIndex_;
+
+  public:
+    explicit CallSiteAndTarget(CallSite cs, uint32_t targetIndex)
+      : CallSite(cs), targetIndex_(targetIndex)
+    { }
+
+    static const uint32_t NOT_INTERNAL = UINT32_MAX;
+
+    bool isInternal() const { return targetIndex_ != NOT_INTERNAL; }
+    uint32_t targetIndex() const { MOZ_ASSERT(isInternal()); return targetIndex_; }
+};
+
+typedef Vector<CallSiteAndTarget, 0, SystemAllocPolicy> CallSiteAndTargetVector;
+
 // As an invariant across architectures, within asm.js code:
 //   $sp % AsmJSStackAlignment = (sizeof(AsmJSFrame) + masm.framePushed) % AsmJSStackAlignment
 // Thus, AsmJSFrame represents the bytes pushed after the call (which occurred
 // with a AsmJSStackAlignment-aligned StackPointer) that are not included in
 // masm.framePushed.
 struct AsmJSFrame
 {
     // The caller's saved frame pointer. In non-profiling mode, internal
@@ -911,20 +928,34 @@ class AsmJSAbsoluteAddress
 struct AsmJSAbsoluteLink
 {
     AsmJSAbsoluteLink(CodeOffsetLabel patchAt, AsmJSImmKind target)
       : patchAt(patchAt), target(target) {}
     CodeOffsetLabel patchAt;
     AsmJSImmKind target;
 };
 
+// Represents a call from an asm.js function to another asm.js function,
+// represented by the index of the callee in the Module Validator
+struct AsmJSInternalCallee
+{
+    uint32_t index;
+
+    // Provide a default constructor for embedding it in unions
+    AsmJSInternalCallee() = default;
+
+    explicit AsmJSInternalCallee(uint32_t calleeIndex)
+      : index(calleeIndex)
+    {}
+};
+
 // The base class of all Assemblers for all archs.
 class AssemblerShared
 {
-    Vector<CallSite, 0, SystemAllocPolicy> callsites_;
+    CallSiteAndTargetVector callsites_;
     Vector<AsmJSHeapAccess, 0, SystemAllocPolicy> asmJSHeapAccesses_;
     Vector<AsmJSGlobalAccess, 0, SystemAllocPolicy> asmJSGlobalAccesses_;
     Vector<AsmJSAbsoluteLink, 0, SystemAllocPolicy> asmJSAbsoluteLinks_;
 
   protected:
     Vector<CodeLabel, 0, SystemAllocPolicy> codeLabels_;
 
     bool enoughMemory_;
@@ -947,23 +978,25 @@ class AssemblerShared
     bool oom() const {
         return !enoughMemory_;
     }
 
     bool embedsNurseryPointers() const {
         return embedsNurseryPointers_;
     }
 
-    void append(const CallSiteDesc& desc, size_t currentOffset, size_t framePushed) {
+    void append(const CallSiteDesc& desc, CodeOffsetLabel label, size_t framePushed,
+                uint32_t targetIndex = CallSiteAndTarget::NOT_INTERNAL)
+    {
         // framePushed does not include sizeof(AsmJSFrame), so add it in here (see
         // CallSite::stackDepth).
-        CallSite callsite(desc, currentOffset, framePushed + sizeof(AsmJSFrame));
-        enoughMemory_ &= callsites_.append(callsite);
+        CallSite callsite(desc, label.offset(), framePushed + sizeof(AsmJSFrame));
+        enoughMemory_ &= callsites_.append(CallSiteAndTarget(callsite, targetIndex));
     }
-    CallSiteVector&& extractCallSites() { return Move(callsites_); }
+    const CallSiteAndTargetVector& callSites() const { return callsites_; }
 
     void append(AsmJSHeapAccess access) { enoughMemory_ &= asmJSHeapAccesses_.append(access); }
     AsmJSHeapAccessVector&& extractAsmJSHeapAccesses() { return Move(asmJSHeapAccesses_); }
 
     void append(AsmJSGlobalAccess access) { enoughMemory_ &= asmJSGlobalAccesses_.append(access); }
     size_t numAsmJSGlobalAccesses() const { return asmJSGlobalAccesses_.length(); }
     AsmJSGlobalAccess asmJSGlobalAccess(size_t i) const { return asmJSGlobalAccesses_[i]; }
 
--- a/js/src/jit/x86-shared/Assembler-x86-shared.h
+++ b/js/src/jit/x86-shared/Assembler-x86-shared.h
@@ -985,41 +985,51 @@ class AssemblerX86Shared : public Assemb
 
     void ret() {
         masm.ret();
     }
     void retn(Imm32 n) {
         // Remove the size of the return address which is included in the frame.
         masm.ret_i(n.value - sizeof(void*));
     }
-    void call(Label* label) {
+    CodeOffsetLabel call(Label* label) {
         if (label->bound()) {
             masm.linkJump(masm.call(), JmpDst(label->offset()));
         } else {
             JmpSrc j = masm.call();
             JmpSrc prev = JmpSrc(label->use(j.offset()));
             masm.setNextJump(j, prev);
         }
-    }
-    void call(Register reg) {
+        return CodeOffsetLabel(masm.currentOffset());
+    }
+    CodeOffsetLabel call(Register reg) {
         masm.call_r(reg.encoding());
+        return CodeOffsetLabel(masm.currentOffset());
     }
     void call(const Operand& op) {
         switch (op.kind()) {
           case Operand::REG:
             masm.call_r(op.reg());
             break;
           case Operand::MEM_REG_DISP:
             masm.call_m(op.disp(), op.base());
             break;
           default:
             MOZ_CRASH("unexpected operand kind");
         }
     }
 
+    CodeOffsetLabel callWithPatch() {
+        return CodeOffsetLabel(masm.call().offset());
+    }
+    void patchCall(uint32_t callerOffset, uint32_t calleeOffset) {
+        unsigned char* code = masm.data();
+        X86Encoding::SetRel32(code + callerOffset, code + calleeOffset);
+    }
+
     void breakpoint() {
         masm.int3();
     }
 
     static bool HasSSE2() { return CPUInfo::IsSSE2Present(); }
     static bool HasSSE3() { return CPUInfo::IsSSE3Present(); }
     static bool HasSSE41() { return CPUInfo::IsSSE41Present(); }
     static bool SupportsFloatingPoint() { return CPUInfo::IsSSE2Present(); }
--- a/js/src/jit/x86-shared/MacroAssembler-x86-shared.cpp
+++ b/js/src/jit/x86-shared/MacroAssembler-x86-shared.cpp
@@ -406,26 +406,26 @@ MacroAssembler::Pop(const ValueOperand& 
 {
     popValue(val);
     framePushed_ -= sizeof(Value);
 }
 
 // ===============================================================
 // Simple call functions.
 
-void
+CodeOffsetLabel
 MacroAssembler::call(Register reg)
 {
-    Assembler::call(reg);
+    return Assembler::call(reg);
 }
 
-void
+CodeOffsetLabel
 MacroAssembler::call(Label* label)
 {
-    Assembler::call(label);
+    return Assembler::call(label);
 }
 
 void
 MacroAssembler::call(const Address& addr)
 {
     Assembler::call(Operand(addr.base, addr.offset));
 }
 
@@ -450,16 +450,27 @@ MacroAssembler::call(ImmPtr target)
 }
 
 void
 MacroAssembler::call(JitCode* target)
 {
     Assembler::call(target);
 }
 
+CodeOffsetLabel
+MacroAssembler::callWithPatch()
+{
+    return Assembler::callWithPatch();
+}
+void
+MacroAssembler::patchCall(uint32_t callerOffset, uint32_t calleeOffset)
+{
+    Assembler::patchCall(callerOffset, calleeOffset);
+}
+
 void
 MacroAssembler::callAndPushReturnAddress(Register reg)
 {
     call(reg);
 }
 
 void
 MacroAssembler::callAndPushReturnAddress(Label* label)
--- a/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -1020,16 +1020,17 @@ xpc::CreateSandboxObject(JSContext* cx, 
                                                      principal, compartmentOptions));
     if (!sandbox)
         return NS_ERROR_FAILURE;
 
     CompartmentPrivate* priv = CompartmentPrivate::Get(sandbox);
     priv->allowWaivers = options.allowWaivers;
     priv->writeToGlobalPrototype = options.writeToGlobalPrototype;
     priv->isWebExtensionContentScript = options.isWebExtensionContentScript;
+    priv->waiveInterposition = options.waiveInterposition;
 
     // Set up the wantXrays flag, which indicates whether xrays are desired even
     // for same-origin access.
     //
     // This flag has historically been ignored for chrome sandboxes due to
     // quirks in the wrapping implementation that have now been removed. Indeed,
     // same-origin Xrays for chrome->chrome access seems a bit superfluous.
     // Arguably we should just flip the default for chrome and still honor the
@@ -1486,16 +1487,17 @@ bool
 SandboxOptions::Parse()
 {
     bool ok = ParseObject("sandboxPrototype", &proto) &&
               ParseBoolean("wantXrays", &wantXrays) &&
               ParseBoolean("allowWaivers", &allowWaivers) &&
               ParseBoolean("wantComponents", &wantComponents) &&
               ParseBoolean("wantExportHelpers", &wantExportHelpers) &&
               ParseBoolean("isWebExtensionContentScript", &isWebExtensionContentScript) &&
+              ParseBoolean("waiveInterposition", &waiveInterposition) &&
               ParseString("sandboxName", sandboxName) &&
               ParseObject("sameZoneAs", &sameZoneAs) &&
               ParseBoolean("freshZone", &freshZone) &&
               ParseBoolean("invisibleToDebugger", &invisibleToDebugger) &&
               ParseBoolean("discardSource", &discardSource) &&
               ParseJSString("addonId", &addonId) &&
               ParseBoolean("writeToGlobalPrototype", &writeToGlobalPrototype) &&
               ParseGlobalProperties() &&
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -189,16 +189,17 @@ public:
 namespace xpc {
 
 CompartmentPrivate::CompartmentPrivate(JSCompartment* c)
     : wantXrays(false)
     , allowWaivers(true)
     , writeToGlobalPrototype(false)
     , skipWriteToGlobalPrototype(false)
     , isWebExtensionContentScript(false)
+    , waiveInterposition(false)
     , universalXPConnectEnabled(false)
     , forcePermissiveCOWs(false)
     , scriptability(c)
     , scope(nullptr)
     , mWrappedJSMap(JSObject2WrappedJSMap::newMap(XPC_JS_MAP_LENGTH))
 {
     MOZ_COUNT_CTOR(xpc::CompartmentPrivate);
     mozilla::PodArrayZero(wrapperDenialWarnings);
--- a/js/xpconnect/src/XPCWrappedNativeScope.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeScope.cpp
@@ -134,19 +134,21 @@ XPCWrappedNativeScope::XPCWrappedNativeS
     }
     if (mUseContentXBLScope) {
       mUseContentXBLScope = principal && !nsContentUtils::IsSystemPrincipal(principal);
     }
 
     JSAddonId* addonId = JS::AddonIdOfObject(aGlobal);
     if (gInterpositionMap) {
         bool isSystem = nsContentUtils::IsSystemPrincipal(principal);
-        if (InterpositionMap::Ptr p = gInterpositionMap->lookup(addonId)) {
+        bool waiveInterposition = priv->waiveInterposition;
+        InterpositionMap::Ptr interposition = gInterpositionMap->lookup(addonId);
+        if (!waiveInterposition && interposition) {
             MOZ_RELEASE_ASSERT(isSystem);
-            mInterposition = p->value();
+            mInterposition = interposition->value();
         }
         // We also want multiprocessCompatible add-ons to have a default interposition.
         if (!mInterposition && addonId && isSystem) {
           bool interpositionEnabled = mozilla::Preferences::GetBool(
             "extensions.interposition.enabled", false);
           if (interpositionEnabled) {
             mInterposition = do_GetService("@mozilla.org/addons/default-addon-shims;1");
             MOZ_ASSERT(mInterposition);
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -3470,16 +3470,17 @@ public:
     explicit SandboxOptions(JSContext* cx = xpc_GetSafeJSContext(),
                             JSObject* options = nullptr)
         : OptionsBase(cx, options)
         , wantXrays(true)
         , allowWaivers(true)
         , wantComponents(true)
         , wantExportHelpers(false)
         , isWebExtensionContentScript(false)
+        , waiveInterposition(false)
         , proto(cx)
         , addonId(cx)
         , writeToGlobalPrototype(false)
         , sameZoneAs(cx)
         , freshZone(false)
         , invisibleToDebugger(false)
         , discardSource(false)
         , metadata(cx)
@@ -3487,16 +3488,17 @@ public:
 
     virtual bool Parse();
 
     bool wantXrays;
     bool allowWaivers;
     bool wantComponents;
     bool wantExportHelpers;
     bool isWebExtensionContentScript;
+    bool waiveInterposition;
     JS::RootedObject proto;
     nsCString sandboxName;
     JS::RootedString addonId;
     bool writeToGlobalPrototype;
     JS::RootedObject sameZoneAs;
     bool freshZone;
     bool invisibleToDebugger;
     bool discardSource;
@@ -3727,16 +3729,21 @@ public:
     // disable the writeToGlobalPrototype behavior (when resolving standard
     // classes, for example).
     bool skipWriteToGlobalPrototype;
 
     // This scope corresponds to a WebExtension content script, and receives
     // various bits of special compatibility behavior.
     bool isWebExtensionContentScript;
 
+    // Even if an add-on needs interposition, it does not necessary need it
+    // for every scope. If this flag is set we waive interposition for this
+    // scope.
+    bool waiveInterposition;
+
     // This is only ever set during mochitest runs when enablePrivilege is called.
     // It's intended as a temporary stopgap measure until we can finish ripping out
     // enablePrivilege. Once set, this value is never unset (i.e., it doesn't follow
     // the old scoping rules of enablePrivilege).
     //
     // Using it in production is inherently unsafe.
     bool universalXPConnectEnabled;
 
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -4572,34 +4572,34 @@ PresShell::StyleSheetApplicableStateChan
   if (aStyleSheet->HasRules()) {
     RecordStyleSheetChange(aStyleSheet);
   }
 }
 
 void
 PresShell::StyleRuleChanged(nsIDocument *aDocument,
                             nsIStyleSheet* aStyleSheet,
-                            nsIStyleRule* aOldStyleRule,
-                            nsIStyleRule* aNewStyleRule)
+                            mozilla::css::Rule* aOldStyleRule,
+                            mozilla::css::Rule* aNewStyleRule)
 {
   RecordStyleSheetChange(aStyleSheet);
 }
 
 void
 PresShell::StyleRuleAdded(nsIDocument *aDocument,
                           nsIStyleSheet* aStyleSheet,
-                          nsIStyleRule* aStyleRule)
+                          mozilla::css::Rule* aStyleRule)
 {
   RecordStyleSheetChange(aStyleSheet);
 }
 
 void
 PresShell::StyleRuleRemoved(nsIDocument *aDocument,
                             nsIStyleSheet* aStyleSheet,
-                            nsIStyleRule* aStyleRule)
+                            mozilla::css::Rule* aStyleRule)
 {
   RecordStyleSheetChange(aStyleSheet);
 }
 
 nsIFrame*
 PresShell::GetRealPrimaryFrameFor(nsIContent* aContent) const
 {
   if (aContent->GetComposedDoc() != GetDocument()) {
--- a/layout/generic/WritingModes.h
+++ b/layout/generic/WritingModes.h
@@ -19,19 +19,22 @@
 // support using debug builds.
 
 // Methods in logical-coordinate classes that take another logical-coordinate
 // object as a parameter should call CHECK_WRITING_MODE on it to verify that
 // the writing modes match.
 // (In some cases, there are internal (private) methods that don't do this;
 // such methods should only be used by other methods that have already checked
 // the writing modes.)
+// The check ignores the eSidewaysMask bit of writing mode, because this does
+// not affect the interpretation of logical coordinates.
 
 #define CHECK_WRITING_MODE(param) \
-   NS_ASSERTION(param == GetWritingMode(), "writing-mode mismatch")
+   NS_ASSERTION(param.IgnoreSideways() == GetWritingMode().IgnoreSideways(), \
+                "writing-mode mismatch")
 
 namespace mozilla {
 
 namespace widget {
 struct IMENotification;
 } // namespace widget
 
 // Physical axis constants.
@@ -254,16 +257,23 @@ public:
    * in vertical lines, in which case we should prefer an alphabetic baseline;
    * otherwise, the default is centered.
    * Note that some glyph runs may be rendered sideways even if this is false,
    * due to text-orientation:mixed resolution, but in that case the dominant
    * baseline remains centered.
    */
   bool IsSideways() const { return !!(mWritingMode & eSidewaysMask); }
 
+#ifdef DEBUG // Used by CHECK_WRITING_MODE to compare modes without regard
+             // for the eSidewaysMask flag.
+  WritingMode IgnoreSideways() const {
+    return WritingMode(mWritingMode & ~eSidewaysMask);
+  }
+#endif
+
   static mozilla::PhysicalAxis PhysicalAxisForLogicalAxis(
                                               uint8_t aWritingModeValue,
                                               LogicalAxis aAxis)
   {
     // This relies on bit 0 of a writing-value mode indicating vertical
     // orientation and bit 0 of a LogicalAxis value indicating the inline axis,
     // so that it can correctly form mozilla::PhysicalAxis values using bit
     // manipulation.
new file mode 100644
--- /dev/null
+++ b/layout/generic/crashtests/1221874-1.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<script>
+
+function boom()
+{
+    document.documentElement.offsetHeight;
+    document.body.style.textOrientation = "sideways";
+}
+
+</script>
+</head>
+<body onload="boom();" style="writing-mode: tb-rl; display: table;">Hello</body>
+</html>
--- a/layout/generic/crashtests/crashtests.list
+++ b/layout/generic/crashtests/crashtests.list
@@ -582,16 +582,17 @@ load 1146114.html
 load 1156222.html
 pref(layout.css.grid.enabled,true) load 1156257.html
 load 1157011.html
 load 1169420-1.html
 load 1169420-2.html
 load 1183431.html
 load 1221112-1.html
 load 1221112-2.html
+load 1221874-1.html
 load first-letter-638937-1.html
 load first-letter-638937-2.html
 pref(dom.meta-viewport.enabled,true) test-pref(font.size.inflation.emPerLine,15) asserts(1-100) load font-inflation-762332.html # bug 762332
 load outline-on-frameset.xhtml
 load text-overflow-bug666751-1.html
 load text-overflow-bug666751-2.html
 load text-overflow-bug670564.xhtml
 load text-overflow-bug671796.xhtml
--- a/layout/inspector/inDOMUtils.cpp
+++ b/layout/inspector/inDOMUtils.cpp
@@ -233,23 +233,27 @@ inDOMUtils::GetCSSStyleRules(nsIDOMEleme
     // if the document they're in doesn't have a presshell.  Bail out.
     return NS_OK;
   }
 
   nsCOMPtr<nsISupportsArray> rules;
   NS_NewISupportsArray(getter_AddRefs(rules));
   if (!rules) return NS_ERROR_OUT_OF_MEMORY;
 
-  RefPtr<mozilla::css::StyleRule> cssRule;
   for ( ; !ruleNode->IsRoot(); ruleNode = ruleNode->GetParent()) {
-    cssRule = do_QueryObject(ruleNode->GetRule());
-    if (cssRule) {
-      nsCOMPtr<nsIDOMCSSRule> domRule = cssRule->GetDOMRule();
-      if (domRule)
-        rules->InsertElementAt(domRule, 0);
+    RefPtr<Declaration> decl = do_QueryObject(ruleNode->GetRule());
+    if (decl) {
+      RefPtr<mozilla::css::StyleRule> styleRule =
+        do_QueryObject(decl->GetOwningRule());
+      if (styleRule) {
+        nsCOMPtr<nsIDOMCSSRule> domRule = styleRule->GetDOMRule();
+        if (domRule) {
+          rules->InsertElementAt(domRule, 0);
+        }
+      }
     }
   }
 
   rules.forget(_retval);
 
   return NS_OK;
 }
 
new file mode 100644
--- /dev/null
+++ b/layout/reftests/box-shadow/boxshadow-rotated-ref.html
@@ -0,0 +1,15 @@
+<html>
+<head>
+<style>
+div.second {
+  margin: 35px auto 95px auto;
+  box-shadow: 0px 0px 15px 0px #808080;
+  width: 100;
+  height: 116.36px;
+}
+</style>
+</head>
+<body>
+<div class="second"></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/box-shadow/boxshadow-rotated.html
@@ -0,0 +1,16 @@
+<html>
+<head>
+<style>
+div.first {
+  margin: 35px auto 95px auto;
+  box-shadow: 0px 0px 15px 0px #808080;
+  transform: rotate(-180deg);
+  width: 100;
+  height: 116.36px;
+}
+</style>
+</head>
+<body>
+<div class="first"></div>
+</body>
+</html>
--- a/layout/reftests/box-shadow/reftest.list
+++ b/layout/reftests/box-shadow/reftest.list
@@ -20,16 +20,17 @@ random-if(d2d) == boxshadow-twocorners.h
 random-if(d2d) == boxshadow-threecorners.html boxshadow-threecorners-ref.html
 == boxshadow-skiprect.html boxshadow-skiprect-ref.html
 == boxshadow-opacity.html boxshadow-opacity-ref.html
 == boxshadow-color-rounding.html boxshadow-color-rounding-ref.html
 == boxshadow-color-rounding-middle.html boxshadow-color-rounding-middle-ref.html
 fuzzy-if(OSX==1010,1,24) == boxshadow-large-border-radius.html boxshadow-large-border-radius-ref.html # Bug 1209649
 == boxshadow-border-radius-int.html boxshadow-border-radius-int-ref.html
 == boxshadow-inset-neg-spread.html about:blank
+fuzzy(26,3610) == boxshadow-rotated.html boxshadow-rotated-ref.html # Bug 1211264
 
 == overflow-not-scrollable-1.html overflow-not-scrollable-1-ref.html
 == overflow-not-scrollable-1.html overflow-not-scrollable-1-ref2.html
 == overflow-not-scrollable-2.html overflow-not-scrollable-2-ref.html
 fails-if(B2G||Mulet) == 611574-1.html 611574-1-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 fails-if(B2G||Mulet) == 611574-2.html 611574-2-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 fuzzy-if(winWidget,5,30) == fieldset.html fieldset-ref.html # minor anti-aliasing problem on Windows
 fuzzy-if(winWidget,5,30) == fieldset-inset.html fieldset-inset-ref.html # minor anti-aliasing problem on Windows
--- a/layout/style/CSSStyleSheet.cpp
+++ b/layout/style/CSSStyleSheet.cpp
@@ -1528,53 +1528,16 @@ CSSStyleSheet::AppendStyleSheet(CSSStyle
   // This is not reference counted. Our parent tells us when
   // it's going away.
   aSheet->mParent = this;
   aSheet->mDocument = mDocument;
   DidDirty();
 }
 
 void
-CSSStyleSheet::InsertStyleSheetAt(CSSStyleSheet* aSheet, int32_t aIndex)
-{
-  NS_PRECONDITION(nullptr != aSheet, "null arg");
-
-  WillDirty();
-  RefPtr<CSSStyleSheet>* tail = &mInner->mFirstChild;
-  while (*tail && aIndex) {
-    --aIndex;
-    tail = &(*tail)->mNext;
-  }
-  aSheet->mNext = *tail;
-  *tail = aSheet;
-
-  // This is not reference counted. Our parent tells us when
-  // it's going away.
-  aSheet->mParent = this;
-  aSheet->mDocument = mDocument;
-  DidDirty();
-}
-
-void
-CSSStyleSheet::PrependStyleRule(css::Rule* aRule)
-{
-  NS_PRECONDITION(nullptr != aRule, "null arg");
-
-  WillDirty();
-  mInner->mOrderedRules.InsertObjectAt(aRule, 0);
-  aRule->SetStyleSheet(this);
-  DidDirty();
-
-  if (css::Rule::NAMESPACE_RULE == aRule->GetType()) {
-    // no api to prepend a namespace (ugh), release old ones and re-create them all
-    mInner->RebuildNameSpaces();
-  }
-}
-
-void
 CSSStyleSheet::AppendStyleRule(css::Rule* aRule)
 {
   NS_PRECONDITION(nullptr != aRule, "null arg");
 
   WillDirty();
   mInner->mOrderedRules.AppendObject(aRule);
   aRule->SetStyleSheet(this);
   DidDirty();
@@ -1619,33 +1582,16 @@ CSSStyleSheet::StyleRuleCount() const
 css::Rule*
 CSSStyleSheet::GetStyleRuleAt(int32_t aIndex) const
 {
   // Important: If this function is ever made scriptable, we must add
   // a security check here. See GetCssRules below for an example.
   return mInner->mOrderedRules.SafeObjectAt(aIndex);
 }
 
-int32_t
-CSSStyleSheet::StyleSheetCount() const
-{
-  // XXX Far from an ideal way to do this, but the hope is that
-  // it won't be done too often. If it is, we might want to 
-  // consider storing the children in an array.
-  int32_t count = 0;
-
-  const CSSStyleSheet* child = mInner->mFirstChild;
-  while (child) {
-    count++;
-    child = child->mNext;
-  }
-
-  return count;
-}
-
 void
 CSSStyleSheet::EnsureUniqueInner()
 {
   mDirty = true;
 
   MOZ_ASSERT(mInner->mSheets.Length() != 0,
              "unexpected number of outers");
   if (mInner->mSheets.Length() == 1) {
--- a/layout/style/CSSStyleSheet.h
+++ b/layout/style/CSSStyleSheet.h
@@ -151,32 +151,28 @@ public:
 
   // Find the ID of the owner inner window.
   uint64_t FindOwningWindowInnerID() const;
 #ifdef DEBUG
   virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
 #endif
 
   void AppendStyleSheet(CSSStyleSheet* aSheet);
-  void InsertStyleSheetAt(CSSStyleSheet* aSheet, int32_t aIndex);
 
   // XXX do these belong here or are they generic?
-  void PrependStyleRule(css::Rule* aRule);
   void AppendStyleRule(css::Rule* aRule);
   void ReplaceStyleRule(css::Rule* aOld, css::Rule* aNew);
 
   int32_t StyleRuleCount() const;
   css::Rule* GetStyleRuleAt(int32_t aIndex) const;
 
   nsresult DeleteRuleFromGroup(css::GroupRule* aGroup, uint32_t aIndex);
   nsresult InsertRuleIntoGroup(const nsAString& aRule, css::GroupRule* aGroup, uint32_t aIndex, uint32_t* _retval);
   nsresult ReplaceRuleInGroup(css::GroupRule* aGroup, css::Rule* aOld, css::Rule* aNew);
 
-  int32_t StyleSheetCount() const;
-
   /**
    * SetURIs must be called on all sheets before parsing into them.
    * SetURIs may only be called while the sheet is 1) incomplete and 2)
    * has no rules in it
    */
   void SetURIs(nsIURI* aSheetURI, nsIURI* aOriginalSheetURI, nsIURI* aBaseURI);
 
   /**
--- a/layout/style/Declaration.cpp
+++ b/layout/style/Declaration.cpp
@@ -14,42 +14,90 @@
 #include "mozilla/css/Declaration.h"
 #include "nsPrintfCString.h"
 #include "gfxFontConstants.h"
 #include "nsStyleUtil.h"
 
 namespace mozilla {
 namespace css {
 
+NS_IMPL_QUERY_INTERFACE(ImportantStyleData, nsIStyleRule)
+NS_IMPL_ADDREF_USING_AGGREGATOR(ImportantStyleData, Declaration())
+NS_IMPL_RELEASE_USING_AGGREGATOR(ImportantStyleData, Declaration())
+
+/* virtual */ void
+ImportantStyleData::MapRuleInfoInto(nsRuleData* aRuleData)
+{
+  Declaration()->MapImportantRuleInfoInto(aRuleData);
+}
+
+#ifdef DEBUG
+/* virtual */ void
+ImportantStyleData::List(FILE* out, int32_t aIndent) const
+{
+  // Indent
+  nsAutoCString str;
+  for (int32_t index = aIndent; --index >= 0; ) {
+    str.AppendLiteral("  ");
+  }
+
+  str.AppendLiteral("! important rule\n");
+  fprintf_stderr(out, "%s", str.get());
+}
+#endif
+
 Declaration::Declaration()
-  : mImmutable(false)
+  : mOwningRule(nullptr)
+  , mImmutable(false)
 {
-  MOZ_COUNT_CTOR(mozilla::css::Declaration);
 }
 
 Declaration::Declaration(const Declaration& aCopy)
   : mOrder(aCopy.mOrder),
     mVariableOrder(aCopy.mVariableOrder),
     mData(aCopy.mData ? aCopy.mData->Clone() : nullptr),
     mImportantData(aCopy.mImportantData ?
                      aCopy.mImportantData->Clone() : nullptr),
     mVariables(aCopy.mVariables ?
         new CSSVariableDeclarations(*aCopy.mVariables) :
         nullptr),
     mImportantVariables(aCopy.mImportantVariables ?
         new CSSVariableDeclarations(*aCopy.mImportantVariables) :
         nullptr),
+    mOwningRule(nullptr),
     mImmutable(false)
 {
-  MOZ_COUNT_CTOR(mozilla::css::Declaration);
 }
 
 Declaration::~Declaration()
 {
-  MOZ_COUNT_DTOR(mozilla::css::Declaration);
+}
+
+NS_INTERFACE_MAP_BEGIN(Declaration)
+  if (aIID.Equals(NS_GET_IID(mozilla::css::Declaration))) {
+    *aInstancePtr = this;
+    NS_ADDREF_THIS();
+    return NS_OK;
+  }
+  else
+  NS_INTERFACE_MAP_ENTRY(nsIStyleRule)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRule)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_ADDREF(Declaration)
+NS_IMPL_RELEASE(Declaration)
+
+/* virtual */ void
+Declaration::MapRuleInfoInto(nsRuleData* aRuleData)
+{
+  MOZ_ASSERT(mData, "called while expanded");
+  mData->MapRuleInfoInto(aRuleData);
+  if (mVariables) {
+    mVariables->MapRuleInfoInto(aRuleData);
+  }
 }
 
 void
 Declaration::ValueAppended(nsCSSProperty aProperty)
 {
   MOZ_ASSERT(!mData && !mImportantData,
              "should only be called while expanded");
   MOZ_ASSERT(!nsCSSProps::IsShorthand(aProperty),
@@ -1334,17 +1382,17 @@ Declaration::ToString(nsAString& aString
   if (! aString.IsEmpty()) {
     // if the string is not empty, we have trailing whitespace we
     // should remove
     aString.Truncate(aString.Length() - 1);
   }
 }
 
 #ifdef DEBUG
-void
+/* virtual */ void
 Declaration::List(FILE* out, int32_t aIndent) const
 {
   nsAutoCString str;
   for (int32_t index = aIndent; --index >= 0; ) {
     str.AppendLiteral("  ");
   }
 
   str.AppendLiteral("{ ");
@@ -1376,25 +1424,27 @@ Declaration::GetNthProperty(uint32_t aIn
 
 void
 Declaration::InitializeEmpty()
 {
   MOZ_ASSERT(!mData && !mImportantData, "already initialized");
   mData = nsCSSCompressedDataBlock::CreateEmptyBlock();
 }
 
-Declaration*
+already_AddRefed<Declaration>
 Declaration::EnsureMutable()
 {
   MOZ_ASSERT(mData, "should only be called when not expanded");
+  RefPtr<Declaration> result;
   if (!IsMutable()) {
-    return new Declaration(*this);
+    result = new Declaration(*this);
   } else {
-    return this;
+    result = this;
   }
+  return result.forget();
 }
 
 size_t
 Declaration::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
 {
   size_t n = aMallocSizeOf(this);
   n += mOrder.ShallowSizeOfExcludingThis(aMallocSizeOf);
   n += mData          ? mData         ->SizeOfIncludingThis(aMallocSizeOf) : 0;
--- a/layout/style/Declaration.h
+++ b/layout/style/Declaration.h
@@ -18,44 +18,96 @@
 #endif
 
 #include "mozilla/Attributes.h"
 #include "mozilla/MemoryReporting.h"
 #include "CSSVariableDeclarations.h"
 #include "nsCSSDataBlock.h"
 #include "nsCSSProperty.h"
 #include "nsCSSProps.h"
+#include "nsIStyleRule.h"
 #include "nsStringFwd.h"
 #include "nsTArray.h"
 #include <stdio.h>
 
+// feec07b8-3fe6-491e-90d5-cc93f853e048
+#define NS_CSS_DECLARATION_IMPL_CID \
+{ 0xfeec07b8, 0x3fe6, 0x491e, \
+  { 0x90, 0xd5, 0xcc, 0x93, 0xf8, 0x53, 0xe0, 0x48 } }
+
 namespace mozilla {
 namespace css {
 
+class Rule;
+class Declaration;
+
+/**
+ * ImportantStyleData is the implementation of nsIStyleRule (a source of
+ * style data) representing the style data coming from !important rules;
+ * the !important declarations need a separate nsIStyleRule object since
+ * they fit at a different point in the cascade.
+ *
+ * ImportantStyleData is allocated only as part of a Declaration object.
+ */
+class ImportantStyleData final : public nsIStyleRule
+{
+public:
+
+  NS_DECL_ISUPPORTS
+
+  inline ::mozilla::css::Declaration* Declaration();
+
+  // nsIStyleRule interface
+  virtual void MapRuleInfoInto(nsRuleData* aRuleData) override;
+#ifdef DEBUG
+  virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
+#endif
+
+private:
+  ImportantStyleData() {}
+  ~ImportantStyleData() {}
+
+  friend class ::mozilla::css::Declaration;
+};
+
 // Declaration objects have unusual lifetime rules.  Every declaration
 // begins life in an invalid state which ends when InitializeEmpty or
 // CompressFrom is called upon it.  After that, it can be attached to
 // exactly one style rule, and will be destroyed when that style rule
-// is destroyed.  A declaration becomes immutable when its style rule's
-// |RuleMatched| method is called; after that, it must be copied before
-// it can be modified, which is taken care of by |EnsureMutable|.
+// is destroyed.  A declaration becomes immutable (via a SetImmutable
+// call) when it is matched (put in the rule tree); after that, it must
+// be copied before it can be modified, which is taken care of by
+// |EnsureMutable|.
 
-class Declaration {
+class Declaration final : public nsIStyleRule {
 public:
   /**
    * Construct an |Declaration| that is in an invalid state (null
    * |mData|) and cannot be used until its |CompressFrom| method or
    * |InitializeEmpty| method is called.
    */
   Declaration();
 
   Declaration(const Declaration& aCopy);
 
+  NS_DECLARE_STATIC_IID_ACCESSOR(NS_CSS_DECLARATION_IMPL_CID)
+
+  NS_DECL_ISUPPORTS
+
+private:
   ~Declaration();
 
+public:
+
+  // nsIStyleRule implementation
+  virtual void MapRuleInfoInto(nsRuleData *aRuleData) override;
+#ifdef DEBUG
+  virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
+#endif
+
   /**
    * |ValueAppended| must be called to maintain this declaration's
    * |mOrder| whenever a property is parsed into an expanded data block
    * for this declaration.  aProperty must not be a shorthand.
    */
   void ValueAppended(nsCSSProperty aProperty);
 
   void RemoveProperty(nsCSSProperty aProperty);
@@ -161,27 +213,16 @@ public:
   void ExpandTo(nsCSSExpandedDataBlock *aExpandedData) {
     AssertMutable();
     aExpandedData->AssertInitialState();
 
     MOZ_ASSERT(mData, "oops");
     aExpandedData->Expand(mData.forget(), mImportantData.forget());
   }
 
-  /**
-   * Do what |nsIStyleRule::MapRuleInfoInto| needs to do for a style
-   * rule using this declaration for storage.
-   */
-  void MapNormalRuleInfoInto(nsRuleData *aRuleData) const {
-    MOZ_ASSERT(mData, "called while expanded");
-    mData->MapRuleInfoInto(aRuleData);
-    if (mVariables) {
-      mVariables->MapRuleInfoInto(aRuleData);
-    }
-  }
   void MapImportantRuleInfoInto(nsRuleData *aRuleData) const {
     MOZ_ASSERT(mData, "called while expanded");
     MOZ_ASSERT(mImportantData || mImportantVariables,
                "must have important data or variables");
     if (mImportantData) {
       mImportantData->MapRuleInfoInto(aRuleData);
     }
     if (mImportantVariables) {
@@ -238,17 +279,17 @@ public:
    */
   bool IsMutable() const {
     return !mImmutable;
   }
 
   /**
    * Copy |this|, if necessary to ensure that it can be modified.
    */
-  Declaration* EnsureMutable();
+  already_AddRefed<Declaration> EnsureMutable();
 
   /**
    * Crash if |this| cannot be modified.
    */
   void AssertMutable() const {
     MOZ_ASSERT(IsMutable(), "someone forgot to call EnsureMutable");
   }
 
@@ -267,19 +308,30 @@ public:
     mData = nullptr;
     mImportantData = nullptr;
     mVariables = nullptr;
     mImportantVariables = nullptr;
     mOrder.Clear();
     mVariableOrder.Clear();
   }
 
-#ifdef DEBUG
-  void List(FILE* out = stdout, int32_t aIndent = 0) const;
-#endif
+  void SetOwningRule(Rule* aRule) {
+    MOZ_ASSERT(!mOwningRule || !aRule,
+               "should never overwrite one rule with another");
+    mOwningRule = aRule;
+  }
+
+  Rule* GetOwningRule() { return mOwningRule; }
+
+  ImportantStyleData* GetImportantStyleData() {
+    if (HasImportantData()) {
+      return &mImportantStyleData;
+    }
+    return nullptr;
+  }
 
 private:
   Declaration& operator=(const Declaration& aCopy) = delete;
   bool operator==(const Declaration& aCopy) const = delete;
 
   void GetValue(nsCSSProperty aProperty, nsAString& aValue,
                 nsCSSValue::Serialization aValueSerialization) const;
 
@@ -346,17 +398,38 @@ private:
   nsAutoPtr<nsCSSCompressedDataBlock> mImportantData;
 
   // may be null
   nsAutoPtr<CSSVariableDeclarations> mVariables;
 
   // may be null
   nsAutoPtr<CSSVariableDeclarations> mImportantVariables;
 
-  // set by style rules when |RuleMatched| is called;
+  // The style rule that owns this declaration.  May be null.
+  Rule* mOwningRule;
+
+  friend class ImportantStyleData;
+  ImportantStyleData mImportantStyleData;
+
+  // set when declaration put in the rule tree;
   // also by ToString (hence the 'mutable').
   mutable bool mImmutable;
 };
 
+inline ::mozilla::css::Declaration*
+ImportantStyleData::Declaration()
+{
+  union {
+    char* ch; /* for pointer arithmetic */
+    ::mozilla::css::Declaration* declaration;
+    ImportantStyleData* importantData;
+  } u;
+  u.importantData = this;
+  u.ch -= offsetof(::mozilla::css::Declaration, mImportantStyleData);
+  return u.declaration;
+}
+
+NS_DEFINE_STATIC_IID_ACCESSOR(Declaration, NS_CSS_DECLARATION_IMPL_CID)
+
 } // namespace css
 } // namespace mozilla
 
 #endif /* mozilla_css_Declaration_h */
--- a/layout/style/FontFaceSet.cpp
+++ b/layout/style/FontFaceSet.cpp
@@ -168,43 +168,43 @@ FontFaceSet::ParseFontShorthandForMatchi
                             const nsAString& aFont,
                             RefPtr<FontFamilyListRefCnt>& aFamilyList,
                             uint32_t& aWeight,
                             int32_t& aStretch,
                             uint8_t& aStyle,
                             ErrorResult& aRv)
 {
   // Parse aFont as a 'font' property value.
-  Declaration declaration;
-  declaration.InitializeEmpty();
+  RefPtr<Declaration> declaration = new Declaration;
+  declaration->InitializeEmpty();
 
   bool changed = false;
   nsCSSParser parser;
   parser.ParseProperty(eCSSProperty_font,
                        aFont,
                        mDocument->GetDocumentURI(),
                        mDocument->GetDocumentURI(),
                        mDocument->NodePrincipal(),
-                       &declaration,
+                       declaration,
                        &changed,
                        /* aIsImportant */ false);
 
   // All of the properties we are interested in should have been set at once.
-  MOZ_ASSERT(changed == (declaration.HasProperty(eCSSProperty_font_family) &&
-                         declaration.HasProperty(eCSSProperty_font_style) &&
-                         declaration.HasProperty(eCSSProperty_font_weight) &&
-                         declaration.HasProperty(eCSSProperty_font_stretch)));
+  MOZ_ASSERT(changed == (declaration->HasProperty(eCSSProperty_font_family) &&
+                         declaration->HasProperty(eCSSProperty_font_style) &&
+                         declaration->HasProperty(eCSSProperty_font_weight) &&
+                         declaration->HasProperty(eCSSProperty_font_stretch)));
 
   if (!changed) {
     aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
     return;
   }
 
-  nsCSSCompressedDataBlock* data = declaration.GetNormalBlock();
-  MOZ_ASSERT(!declaration.GetImportantBlock());
+  nsCSSCompressedDataBlock* data = declaration->GetNormalBlock();
+  MOZ_ASSERT(!declaration->GetImportantBlock());
 
   const nsCSSValue* family = data->ValueFor(eCSSProperty_font_family);
   if (family->GetUnit() != eCSSUnit_FontFamilyList) {
     // We got inherit, initial, unset, a system font, or a token stream.
     aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
     return;
   }
 
--- a/layout/style/GroupRule.h
+++ b/layout/style/GroupRule.h
@@ -37,24 +37,22 @@ protected:
   GroupRule(uint32_t aLineNumber, uint32_t aColumnNumber);
   GroupRule(const GroupRule& aCopy);
   virtual ~GroupRule();
 public:
 
   NS_DECL_CYCLE_COLLECTION_CLASS(GroupRule)
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
 
-  // implement part of nsIStyleRule and Rule
+  // implement part of Rule
   DECL_STYLE_RULE_INHERIT_NO_DOMRULE
-  virtual void SetStyleSheet(CSSStyleSheet* aSheet) override;
-
-  // to help implement nsIStyleRule
 #ifdef DEBUG
   virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
 #endif
+  virtual void SetStyleSheet(CSSStyleSheet* aSheet) override;
 
 public:
   void AppendStyleRule(Rule* aRule);
 
   int32_t StyleRuleCount() const { return mRules.Count(); }
   Rule* GetStyleRuleAt(int32_t aIndex) const;
 
   typedef IncrementalClearCOMRuleArray::nsCOMArrayEnumFunc RuleEnumFunc;
--- a/layout/style/ImportRule.h
+++ b/layout/style/ImportRule.h
@@ -29,31 +29,29 @@ class ImportRule final : public Rule,
 public:
   ImportRule(nsMediaList* aMedia, const nsString& aURLSpec,
              uint32_t aLineNumber, uint32_t aColumnNumber);
 private:
   // for |Clone|
   ImportRule(const ImportRule& aCopy);
   ~ImportRule();
 public:
-  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(ImportRule, nsIStyleRule)
+  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(ImportRule, mozilla::css::Rule)
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
 
   DECL_STYLE_RULE_INHERIT
 
 #ifdef HAVE_CPP_AMBIGUITY_RESOLVING_USING
   using Rule::GetStyleSheet; // unhide since nsIDOMCSSImportRule has its own GetStyleSheet
 #endif
 
-  // nsIStyleRule methods
+  // Rule methods
 #ifdef DEBUG
   virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
 #endif
-
-  // Rule methods
   virtual int32_t GetType() const override;
   virtual already_AddRefed<Rule> Clone() const override;
 
   void SetSheet(CSSStyleSheet*);
 
   virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override;
 
   // nsIDOMCSSRule interface
--- a/layout/style/Loader.cpp
+++ b/layout/style/Loader.cpp
@@ -1413,17 +1413,20 @@ Loader::InsertSheetInDoc(CSSStyleSheet* 
 
 /**
  * InsertChildSheet handles ordering of @import-ed sheet in their
  * parent sheets.  Here we want to just insert based on order of the
  * @import rules that imported the sheets.  In theory we can't just
  * append to the end because the CSSOM can insert @import rules.  In
  * practice, we get the call to load the child sheet before the CSSOM
  * has finished inserting the @import rule, so we have no idea where
- * to put it anyway.  So just append for now.
+ * to put it anyway.  So just append for now.  (In the future if we
+ * want to insert the sheet at the correct position, we'll need to
+ * restore CSSStyleSheet::InsertStyleSheetAt, which was removed in
+ * bug 1220506.)
  */
 nsresult
 Loader::InsertChildSheet(CSSStyleSheet* aSheet,
                          CSSStyleSheet* aParentSheet,
                          ImportRule* aParentRule)
 {
   LOG(("css::Loader::InsertChildSheet"));
   NS_PRECONDITION(aSheet, "Nothing to insert");
--- a/layout/style/NameSpaceRule.h
+++ b/layout/style/NameSpaceRule.h
@@ -34,24 +34,21 @@ private:
   // for |Clone|
   NameSpaceRule(const NameSpaceRule& aCopy);
   ~NameSpaceRule();
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_CSS_NAMESPACE_RULE_IMPL_CID)
 
   NS_DECL_ISUPPORTS
 
+  // Rule methods
   DECL_STYLE_RULE_INHERIT
-
-  // nsIStyleRule methods
 #ifdef DEBUG
   virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
 #endif
-
-  // Rule methods
   virtual int32_t GetType() const override;
   virtual already_AddRefed<Rule> Clone() const override;
 
   nsIAtom* GetPrefix() const { return mPrefix; }
 
   void GetURLSpec(nsString& aURLSpec) const { aURLSpec = mURLSpec; }
 
   virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
--- a/layout/style/Rule.h
+++ b/layout/style/Rule.h
@@ -5,60 +5,62 @@
 
 /* base class for all rule types in a CSS style sheet */
 
 #ifndef mozilla_css_Rule_h___
 #define mozilla_css_Rule_h___
 
 #include "mozilla/CSSStyleSheet.h"
 #include "mozilla/MemoryReporting.h"
-#include "nsIStyleRule.h"
+#include "nsISupports.h"
 #include "nsIDOMCSSRule.h"
 
 class nsIDocument;
 struct nsRuleData;
 template<class T> struct already_AddRefed;
 class nsHTMLCSSStyleSheet;
 
 namespace mozilla {
 namespace css {
 class GroupRule;
 
 #define DECL_STYLE_RULE_INHERIT_NO_DOMRULE  \
-virtual void MapRuleInfoInto(nsRuleData* aRuleData) override;
+ /* nothing */
 
 #define DECL_STYLE_RULE_INHERIT                            \
   DECL_STYLE_RULE_INHERIT_NO_DOMRULE                       \
   virtual nsIDOMCSSRule* GetDOMRule() override;        \
   virtual nsIDOMCSSRule* GetExistingDOMRule() override;
 
-class Rule : public nsIStyleRule {
+class Rule : public nsISupports {
 protected:
   Rule(uint32_t aLineNumber, uint32_t aColumnNumber)
     : mSheet(0),
       mParentRule(nullptr),
       mLineNumber(aLineNumber),
-      mColumnNumber(aColumnNumber),
-      mWasMatched(false)
+      mColumnNumber(aColumnNumber)
   {
   }
 
   Rule(const Rule& aCopy)
     : mSheet(aCopy.mSheet),
       mParentRule(aCopy.mParentRule),
       mLineNumber(aCopy.mLineNumber),
-      mColumnNumber(aCopy.mColumnNumber),
-      mWasMatched(false)
+      mColumnNumber(aCopy.mColumnNumber)
   {
   }
 
   virtual ~Rule() {}
 
 public:
 
+#ifdef DEBUG
+  virtual void List(FILE* out = stdout, int32_t aIndent = 0) const = 0;
+#endif
+
   // The constants in this list must maintain the following invariants:
   //   If a rule of type N must appear before a rule of type M in stylesheets
   //   then N < M
   // Note that CSSStyleSheet::RebuildChildList assumes that no other kinds of
   // rules can come between two rules of type IMPORT_RULE.
   enum {
     UNKNOWN_RULE = 0,
     CHARSET_RULE,
@@ -135,16 +137,15 @@ protected:
   // if the low bit is 0, the latter if the low bit is 1.
   uintptr_t         mSheet;
   // When the parent GroupRule is destroyed, it will call SetParentRule(nullptr)
   // on this object. (Through SetParentRuleReference);
   GroupRule* MOZ_NON_OWNING_REF mParentRule;
 
   // Keep the same type so that MSVC packs them.
   uint32_t          mLineNumber;
-  uint32_t          mColumnNumber : 31;
-  uint32_t          mWasMatched : 1;
+  uint32_t          mColumnNumber;
 };
 
 } // namespace css
 } // namespace mozilla
 
 #endif /* mozilla_css_Rule_h___ */
--- a/layout/style/StyleAnimationValue.cpp
+++ b/layout/style/StyleAnimationValue.cpp
@@ -2486,17 +2486,17 @@ StyleAnimationValue::AddWeighted(nsCSSPr
 
 already_AddRefed<css::StyleRule>
 BuildStyleRule(nsCSSProperty aProperty,
                dom::Element* aTargetElement,
                const nsAString& aSpecifiedValue,
                bool aUseSVGMode)
 {
   // Set up an empty CSS Declaration
-  nsAutoPtr<css::Declaration> declaration(new css::Declaration());
+  RefPtr<css::Declaration> declaration(new css::Declaration());
   declaration->InitializeEmpty();
 
   bool changed; // ignored, but needed as outparam for ParseProperty
   nsIDocument* doc = aTargetElement->OwnerDoc();
   nsCOMPtr<nsIURI> baseURI = aTargetElement->GetBaseURI();
   nsCSSParser parser(doc->CSSLoader());
 
   nsCSSProperty propertyToCheck = nsCSSProps::IsShorthand(aProperty) ?
@@ -2509,17 +2509,17 @@ BuildStyleRule(nsCSSProperty aProperty,
                        &changed, false, aUseSVGMode);
 
   // check whether property parsed without CSS parsing errors
   if (!declaration->HasNonImportantValueFor(propertyToCheck)) {
     return nullptr;
   }
 
   RefPtr<css::StyleRule> rule = new css::StyleRule(nullptr,
-                                                     declaration.forget(),
+                                                     declaration,
                                                      0, 0);
   return rule.forget();
 }
 
 inline
 already_AddRefed<nsStyleContext>
 LookupStyleContext(dom::Element* aElement)
 {
@@ -2626,18 +2626,19 @@ StyleAnimationValue::ComputeValues(
   RefPtr<nsStyleContext> tmpStyleContext;
   if (aIsContextSensitive) {
     MOZ_ASSERT(!nsCSSProps::IsShorthand(aProperty),
                "to correctly set aIsContextSensitive for shorthand properties, "
                "this code must be adjusted");
 
     nsCOMArray<nsIStyleRule> ruleArray;
     ruleArray.AppendObject(styleSet->InitialStyleRule());
-    ruleArray.AppendObject(aStyleRule);
-    aStyleRule->RuleMatched();
+    css::Declaration* declaration = aStyleRule->GetDeclaration();
+    ruleArray.AppendObject(declaration);
+    declaration->SetImmutable();
     tmpStyleContext =
       styleSet->ResolveStyleByAddingRules(styleContext, ruleArray);
     if (!tmpStyleContext) {
       return false;
     }
 
     // Force walk of rule tree
     nsStyleStructID sid = nsCSSProps::kSIDTable[aProperty];
@@ -2653,18 +2654,19 @@ StyleAnimationValue::ComputeValues(
   // If we're not concerned whether the property is context sensitive then just
   // add the rule to a new temporary style context alongside the target
   // element's style context.
   // Also, if we previously discovered that this property IS context-sensitive
   // then we need to throw the temporary style context out since the property's
   // value may have been biased by the 'initial' values supplied.
   if (!aIsContextSensitive || *aIsContextSensitive) {
     nsCOMArray<nsIStyleRule> ruleArray;
-    ruleArray.AppendObject(aStyleRule);
-    aStyleRule->RuleMatched();
+    css::Declaration* declaration = aStyleRule->GetDeclaration();
+    ruleArray.AppendObject(declaration);
+    declaration->SetImmutable();
     tmpStyleContext =
       styleSet->ResolveStyleByAddingRules(styleContext, ruleArray);
     if (!tmpStyleContext) {
       return false;
     }
   }
 
   // Extract computed value of our property (or all longhand components, if
--- a/layout/style/StyleRule.cpp
+++ b/layout/style/StyleRule.cpp
@@ -1033,56 +1033,16 @@ nsCSSSelectorList::SizeOfIncludingThis(m
   while (s) {
     n += aMallocSizeOf(s);
     n += s->mSelectors ? s->mSelectors->SizeOfIncludingThis(aMallocSizeOf) : 0;
     s = s->mNext;
   }
   return n;
 }
 
-// -- ImportantRule ----------------------------------
-
-namespace mozilla {
-namespace css {
-
-ImportantRule::ImportantRule(Declaration* aDeclaration)
-  : mDeclaration(aDeclaration)
-{
-}
-
-ImportantRule::~ImportantRule()
-{
-}
-
-NS_IMPL_ISUPPORTS(ImportantRule, nsIStyleRule)
-
-/* virtual */ void
-ImportantRule::MapRuleInfoInto(nsRuleData* aRuleData)
-{
-  mDeclaration->MapImportantRuleInfoInto(aRuleData);
-}
-
-#ifdef DEBUG
-/* virtual */ void
-ImportantRule::List(FILE* out, int32_t aIndent) const
-{
-  // Indent
-  nsAutoCString str;
-  for (int32_t index = aIndent; --index >= 0; ) {
-    str.AppendLiteral("  ");
-  }
-
-  str.AppendLiteral("! important rule\n");
-  fprintf_stderr(out, "%s", str.get());
-}
-#endif
-
-} // namespace css
-} // namespace mozilla
-
 // --------------------------------------------------------
 
 namespace mozilla {
 namespace css {
 class DOMCSSStyleRule;
 } // namespace css
 } // namespace mozilla
 
@@ -1415,24 +1375,27 @@ StyleRule::StyleRule(nsCSSSelectorList* 
                      Declaration* aDeclaration,
                      uint32_t aLineNumber,
                      uint32_t aColumnNumber)
   : Rule(aLineNumber, aColumnNumber),
     mSelector(aSelector),
     mDeclaration(aDeclaration)
 {
   NS_PRECONDITION(aDeclaration, "must have a declaration");
+
+  mDeclaration->SetOwningRule(this);
 }
 
 // for |Clone|
 StyleRule::StyleRule(const StyleRule& aCopy)
   : Rule(aCopy),
     mSelector(aCopy.mSelector ? aCopy.mSelector->Clone() : nullptr),
     mDeclaration(new Declaration(*aCopy.mDeclaration))
 {
+  mDeclaration->SetOwningRule(this);
   // rest is constructed lazily on existing data
 }
 
 // for |SetCSSDeclaration|
 StyleRule::StyleRule(StyleRule& aCopy,
                      Declaration* aDeclaration)
   : Rule(aCopy),
     mSelector(aCopy.mSelector),
@@ -1447,57 +1410,48 @@ StyleRule::StyleRule(StyleRule& aCopy,
 
   // We are probably replacing the old declaration with |aDeclaration|
   // instead of taking ownership of the old declaration; only null out
   // aCopy.mDeclaration if we are taking ownership.
   if (mDeclaration == aCopy.mDeclaration) {
     // This should only ever happen if the declaration was modifiable.
     mDeclaration->AssertMutable();
     aCopy.mDeclaration = nullptr;
+    mDeclaration->SetOwningRule(nullptr);
   }
+
+  mDeclaration->SetOwningRule(this);
 }
 
 StyleRule::~StyleRule()
 {
   delete mSelector;
-  delete mDeclaration;
   if (mDOMRule) {
     mDOMRule->DOMDeclaration()->DropReference();
   }
+
+  if (mDeclaration) {
+    mDeclaration->SetOwningRule(nullptr);
+  }
 }
 
 // QueryInterface implementation for StyleRule
 NS_INTERFACE_MAP_BEGIN(StyleRule)
   if (aIID.Equals(NS_GET_IID(mozilla::css::StyleRule))) {
     *aInstancePtr = this;
     NS_ADDREF_THIS();
     return NS_OK;
   }
   else
-  NS_INTERFACE_MAP_ENTRY(nsIStyleRule)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRule)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozilla::css::Rule)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_ADDREF(StyleRule)
 NS_IMPL_RELEASE(StyleRule)
 
-void
-StyleRule::RuleMatched()
-{
-  if (!mWasMatched) {
-    MOZ_ASSERT(!mImportantRule, "should not have important rule yet");
-
-    mWasMatched = true;
-    mDeclaration->SetImmutable();
-    if (mDeclaration->HasImportantData()) {
-      mImportantRule = new ImportantRule(mDeclaration);
-    }
-  }
-}
-
 /* virtual */ int32_t
 StyleRule::GetType() const
 {
   return Rule::STYLE_RULE;
 }
 
 /* virtual */ already_AddRefed<Rule>
 StyleRule::Clone() const
@@ -1544,24 +1498,16 @@ StyleRule::DeclarationChanged(Declaratio
     } else if (sheet) {
       sheet->ReplaceStyleRule(this, clone);
     }
   }
 
   return clone.forget();
 }
 
-/* virtual */ void
-StyleRule::MapRuleInfoInto(nsRuleData* aRuleData)
-{
-  MOZ_ASSERT(mWasMatched,
-             "somebody forgot to call css::StyleRule::RuleMatched");
-  mDeclaration->MapNormalRuleInfoInto(aRuleData);
-}
-
 #ifdef DEBUG
 /* virtual */ void
 StyleRule::List(FILE* out, int32_t aIndent) const
 {
   nsAutoCString str;
   // Indent
   for (int32_t index = aIndent; --index >= 0; ) {
     str.AppendLiteral("  ");
@@ -1648,17 +1594,16 @@ StyleRule::SetSelectorText(const nsAStri
 StyleRule::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
 {
   size_t n = aMallocSizeOf(this);
   n += mSelector ? mSelector->SizeOfIncludingThis(aMallocSizeOf) : 0;
   n += mDeclaration ? mDeclaration->SizeOfIncludingThis(aMallocSizeOf) : 0;
 
   // Measurement of the following members may be added later if DMD finds it is
   // worthwhile:
-  // - mImportantRule;
   // - mDOMRule;
 
   return n;
 }
 
 
 } // namespace css
 } // namespace mozilla
--- a/layout/style/StyleRule.h
+++ b/layout/style/StyleRule.h
@@ -14,16 +14,17 @@
 #include "mozilla/Attributes.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/css/Rule.h"
 
 #include "nsString.h"
 #include "nsCOMPtr.h"
 #include "nsCSSPseudoElements.h"
 #include "nsCSSPseudoClasses.h"
+#include "nsIStyleRule.h"
 
 class nsIAtom;
 struct nsCSSSelectorList;
 
 namespace mozilla {
 class CSSStyleSheet;
 } // namespace mozilla
 
@@ -287,41 +288,16 @@ private:
   { 0xab, 0x44, 0xb7, 0xa5, 0xf3, 0xaa, 0xe5, 0x7d } }
 
 namespace mozilla {
 namespace css {
 
 class Declaration;
 class DOMCSSStyleRule;
 
-class StyleRule;
-
-class ImportantRule final : public nsIStyleRule {
-public:
-  explicit ImportantRule(Declaration *aDeclaration);
-
-  NS_DECL_ISUPPORTS
-
-  // nsIStyleRule interface
-  virtual void MapRuleInfoInto(nsRuleData* aRuleData) override;
-#ifdef DEBUG
-  virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
-#endif
-
-protected:
-  virtual ~ImportantRule();
-
-  // Not an owning reference; the StyleRule that owns this
-  // ImportantRule also owns the mDeclaration, and any rule node
-  // pointing to this rule keeps that StyleRule alive as well.
-  Declaration* mDeclaration;
-
-  friend class StyleRule;
-};
-
 class StyleRule final : public Rule
 {
  public:
   StyleRule(nsCSSSelectorList* aSelector,
             Declaration *aDeclaration,
             uint32_t aLineNumber, uint32_t aColumnNumber);
 private:
   // for |Clone|
@@ -346,54 +322,42 @@ public:
    * the declaration is modified.
    *
    * |DeclarationChanged| handles replacing the object in the container
    * sheet or group rule if |aHandleContainer| is true.
    */
   already_AddRefed<StyleRule>
   DeclarationChanged(Declaration* aDecl, bool aHandleContainer);
 
-  nsIStyleRule* GetImportantRule() const { return mImportantRule; }
-
-  /**
-   * The rule processor must call this method before calling
-   * nsRuleWalker::Forward on this rule during rule matching.
-   */
-  void RuleMatched();
-
   // hooks for DOM rule
   void GetCssText(nsAString& aCssText);
   void SetCssText(const nsAString& aCssText);
   void GetSelectorText(nsAString& aSelectorText);
   void SetSelectorText(const nsAString& aSelectorText);
 
   virtual int32_t GetType() const override;
 
   virtual already_AddRefed<Rule> Clone() const override;
 
   virtual nsIDOMCSSRule* GetDOMRule() override;
 
   virtual nsIDOMCSSRule* GetExistingDOMRule() override;
 
-  // The new mapping function.
-  virtual void MapRuleInfoInto(nsRuleData* aRuleData) override;
-
 #ifdef DEBUG
   virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
 #endif
 
   virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override;
 
 private:
   ~StyleRule();
 
 private:
   nsCSSSelectorList*      mSelector; // null for style attribute
-  Declaration*            mDeclaration;
-  RefPtr<ImportantRule> mImportantRule; // initialized by RuleMatched
+  RefPtr<Declaration>     mDeclaration;
   RefPtr<DOMCSSStyleRule> mDOMRule;
 
 private:
   StyleRule& operator=(const StyleRule& aCopy) = delete;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(StyleRule, NS_CSS_STYLE_RULE_IMPL_CID)
 
--- a/layout/style/nsAnimationManager.cpp
+++ b/layout/style/nsAnimationManager.cpp
@@ -599,42 +599,47 @@ struct KeyframeDataComparator {
   }
 };
 
 class ResolvedStyleCache {
 public:
   ResolvedStyleCache() : mCache() {}
   nsStyleContext* Get(nsPresContext *aPresContext,
                       nsStyleContext *aParentStyleContext,
-                      nsCSSKeyframeRule *aKeyframe);
+                      Declaration* aKeyframeDeclaration);
 
 private:
-  nsRefPtrHashtable<nsPtrHashKey<nsCSSKeyframeRule>, nsStyleContext> mCache;
+  nsRefPtrHashtable<nsPtrHashKey<Declaration>, nsStyleContext> mCache;
 };
 
 nsStyleContext*
 ResolvedStyleCache::Get(nsPresContext *aPresContext,
                         nsStyleContext *aParentStyleContext,
-                        nsCSSKeyframeRule *aKeyframe)
+                        Declaration* aKeyframeDeclaration)
 {
   // FIXME (spec):  The css3-animations spec isn't very clear about how
   // properties are resolved when they have values that depend on other
   // properties (e.g., values in 'em').  I presume that they're resolved
   // relative to the other styles of the element.  The question is
   // whether they are resolved relative to other animations:  I assume
   // that they're not, since that would prevent us from caching a lot of
   // data that we'd really like to cache (in particular, the
   // StyleAnimationValue values in AnimationPropertySegment).
-  nsStyleContext *result = mCache.GetWeak(aKeyframe);
+  nsStyleContext *result = mCache.GetWeak(aKeyframeDeclaration);
   if (!result) {
+    aKeyframeDeclaration->SetImmutable();
+    // The spec says that !important declarations should just be ignored
+    MOZ_ASSERT(!aKeyframeDeclaration->HasImportantData(),
+               "Keyframe rule has !important data");
+
     nsCOMArray<nsIStyleRule> rules;
-    rules.AppendObject(aKeyframe);
+    rules.AppendObject(aKeyframeDeclaration);
     RefPtr<nsStyleContext> resultStrong = aPresContext->StyleSet()->
       ResolveStyleByAddingRules(aParentStyleContext, rules);
-    mCache.Put(aKeyframe, resultStrong);
+    mCache.Put(aKeyframeDeclaration, resultStrong);
     result = resultStrong;
   }
   return result;
 }
 
 void
 nsAnimationManager::BuildAnimations(nsStyleContext* aStyleContext,
                                     dom::Element* aTarget,
@@ -787,17 +792,18 @@ nsAnimationManager::BuildAnimations(nsSt
       RefPtr<nsStyleContext> fromContext;
       bool interpolated = true;
       for (uint32_t wpIdx = 0, wpEnd = keyframesWithProperty.Length();
            wpIdx != wpEnd; ++wpIdx) {
         uint32_t kfIdx = keyframesWithProperty[wpIdx];
         KeyframeData &toKeyframe = sortedKeyframes[kfIdx];
 
         RefPtr<nsStyleContext> toContext =
-          resolvedStyles.Get(mPresContext, aStyleContext, toKeyframe.mRule);
+          resolvedStyles.Get(mPresContext, aStyleContext,
+                             toKeyframe.mRule->Declaration());
 
         if (fromKeyframe) {
           interpolated = interpolated &&
             BuildSegment(propData.mSegments, prop, src,
                          fromKeyframe->mKey, fromContext,
                          fromKeyframe->mRule->Declaration(),
                          toKeyframe.mKey, toContext);
         } else {
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -747,18 +747,19 @@ protected:
     // The declaration we're parsing was generated by the CSSUnprefixingService:
     eParseDeclaration_FromUnprefixingSvc = 1 << 2
   };
   enum nsCSSContextType {
     eCSSContext_General,
     eCSSContext_Page
   };
 
-  css::Declaration* ParseDeclarationBlock(uint32_t aFlags,
-                                          nsCSSContextType aContext = eCSSContext_General);
+  already_AddRefed<css::Declaration>
+    ParseDeclarationBlock(uint32_t aFlags,
+                          nsCSSContextType aContext = eCSSContext_General);
   bool ParseDeclaration(css::Declaration* aDeclaration,
                         uint32_t aFlags,
                         bool aMustCallValueAppended,
                         bool* aChanged,
                         nsCSSContextType aContext = eCSSContext_General);
 
   // A "prefix-aware" wrapper for nsCSSKeywords::LookupKeyword().
   // Use this instead of LookupKeyword() if you might be parsing an unprefixed
@@ -1657,17 +1658,17 @@ CSSParserImpl::ParseStyleAttribute(const
   nsCSSScanner scanner(aAttributeValue, 0);
   css::ErrorReporter reporter(scanner, mSheet, mChildLoader, aDocURI);
   InitScanner(scanner, reporter, aDocURI, aBaseURI, aNodePrincipal);
 
   mSection = eCSSSection_General;
 
   uint32_t parseFlags = eParseDeclaration_AllowImportant;
 
-  css::Declaration* declaration = ParseDeclarationBlock(parseFlags);
+  RefPtr<css::Declaration> declaration = ParseDeclarationBlock(parseFlags);
   if (declaration) {
     // Create a style rule for the declaration
     NS_ADDREF(*aResult = new css::StyleRule(nullptr, declaration, 0, 0));
   } else {
     *aResult = nullptr;
   }
 
   ReleaseScanner();
@@ -1776,27 +1777,27 @@ CSSParserImpl::ParseLonghandProperty(con
                                      nsIURI* aSheetURL,
                                      nsIURI* aBaseURL,
                                      nsIPrincipal* aSheetPrincipal,
                                      nsCSSValue& aValue)
 {
   MOZ_ASSERT(aPropID < eCSSProperty_COUNT_no_shorthands,
              "ParseLonghandProperty must only take a longhand property");
 
-  Declaration declaration;
-  declaration.InitializeEmpty();
+  RefPtr<Declaration> declaration = new Declaration;
+  declaration->InitializeEmpty();
 
   bool changed;
   ParseProperty(aPropID, aPropValue, aSheetURL, aBaseURL, aSheetPrincipal,
-                &declaration, &changed,
+                declaration, &changed,
                 /* aIsImportant */ false,
                 /* aIsSVGMode */ false);
 
   if (changed) {
-    aValue = *declaration.GetNormalBlock()->ValueFor(aPropID);
+    aValue = *declaration->GetNormalBlock()->ValueFor(aPropID);
   } else {
     aValue.Reset();
   }
 }
 
 void
 CSSParserImpl::ParseProperty(const nsCSSProperty aPropID,
                              const nsAString& aPropValue,
@@ -4191,28 +4192,26 @@ CSSParserImpl::ParsePageRule(RuleAppendF
   // TODO: There can be page selectors after @page such as ":first", ":left".
   uint32_t parseFlags = eParseDeclaration_InBraces |
                         eParseDeclaration_AllowImportant;
 
   // Forbid viewport units in @page rules. See bug 811391.
   MOZ_ASSERT(mViewportUnitsEnabled,
              "Viewport units should be enabled outside of @page rules.");
   mViewportUnitsEnabled = false;
-  nsAutoPtr<css::Declaration> declaration(
-                                ParseDeclarationBlock(parseFlags,
-                                                      eCSSContext_Page));
+  RefPtr<css::Declaration> declaration =
+    ParseDeclarationBlock(parseFlags, eCSSContext_Page);
   mViewportUnitsEnabled = true;
 
   if (!declaration) {
     return false;
   }
 
-  // Takes ownership of declaration.
-  RefPtr<nsCSSPageRule> rule = new nsCSSPageRule(Move(declaration),
-                                                   linenum, colnum);
+  RefPtr<nsCSSPageRule> rule =
+    new nsCSSPageRule(declaration, linenum, colnum);
 
   (*aAppendFunc)(rule, aData);
   return true;
 }
 
 already_AddRefed<nsCSSKeyframeRule>
 CSSParserImpl::ParseKeyframeRule()
 {
@@ -4221,24 +4220,24 @@ CSSParserImpl::ParseKeyframeRule()
   if (!GetNextTokenLocation(true, &linenum, &colnum) ||
       !ParseKeyframeSelectorList(selectorList)) {
     REPORT_UNEXPECTED(PEBadSelectorKeyframeRuleIgnored);
     return nullptr;
   }
 
   // Ignore !important in keyframe rules
   uint32_t parseFlags = eParseDeclaration_InBraces;
-  nsAutoPtr<css::Declaration> declaration(ParseDeclarationBlock(parseFlags));
+  RefPtr<css::Declaration> declaration(ParseDeclarationBlock(parseFlags));
   if (!declaration) {
     return nullptr;
   }
 
   // Takes ownership of declaration, and steals contents of selectorList.
   RefPtr<nsCSSKeyframeRule> rule =
-    new nsCSSKeyframeRule(selectorList, Move(declaration), linenum, colnum);
+    new nsCSSKeyframeRule(selectorList, declaration, linenum, colnum);
   return rule.forget();
 }
 
 bool
 CSSParserImpl::ParseKeyframeSelectorList(InfallibleTArray<float>& aSelectorList)
 {
   for (;;) {
     if (!GetToken(true)) {
@@ -5146,17 +5145,17 @@ CSSParserImpl::ParseRuleSet(RuleAppendFu
     return false;
   }
   NS_ASSERTION(nullptr != slist, "null selector list");
   CLEAR_ERROR();
 
   // Next parse the declaration block
   uint32_t parseFlags = eParseDeclaration_InBraces |
                         eParseDeclaration_AllowImportant;
-  css::Declaration* declaration = ParseDeclarationBlock(parseFlags);
+  RefPtr<css::Declaration> declaration = ParseDeclarationBlock(parseFlags);
   if (nullptr == declaration) {
     delete slist;
     return false;
   }
 
 #if 0
   slist->Dump();
   fputs("{\n", stdout);
@@ -6357,34 +6356,34 @@ CSSParserImpl::ParseSelector(nsCSSSelect
     return true;
   }
 
   aList->mWeight += selector->CalcWeight();
 
   return true;
 }
 
-css::Declaration*
+already_AddRefed<css::Declaration>
 CSSParserImpl::ParseDeclarationBlock(uint32_t aFlags, nsCSSContextType aContext)
 {
   bool checkForBraces = (aFlags & eParseDeclaration_InBraces) != 0;
 
   MOZ_ASSERT(mWebkitBoxUnprefixState == eNotParsingDecls,
              "Someone forgot to clear mWebkitBoxUnprefixState!");
   AutoRestore<WebkitBoxUnprefixState> autoRestore(mWebkitBoxUnprefixState);
   mWebkitBoxUnprefixState = eHaveNotUnprefixed;
 
   if (checkForBraces) {
     if (!ExpectSymbol('{', true)) {
       REPORT_UNEXPECTED_TOKEN(PEBadDeclBlockStart);
       OUTPUT_ERROR();
       return nullptr;
     }
   }
-  css::Declaration* declaration = new css::Declaration();
+  RefPtr<css::Declaration> declaration = new css::Declaration();
   mData.AssertInitialState();
   for (;;) {
     bool changed;
     if (!ParseDeclaration(declaration, aFlags, true, &changed, aContext)) {
       if (!SkipDeclaration(checkForBraces)) {
         break;
       }
       if (checkForBraces) {
@@ -6392,17 +6391,17 @@ CSSParserImpl::ParseDeclarationBlock(uin
           break;
         }
       }
       // Since the skipped declaration didn't end the block we parse
       // the next declaration.
     }
   }
   declaration->CompressFrom(&mData);
-  return declaration;
+  return declaration.forget();
 }
 
 CSSParseResult
 CSSParserImpl::ParseColor(nsCSSValue& aValue)
 {
   if (!GetToken(true)) {
     REPORT_UNEXPECTED_EOF(PEColorEOF);
     return CSSParseResult::NotFound;
--- a/layout/style/nsCSSRuleProcessor.cpp
+++ b/layout/style/nsCSSRuleProcessor.cpp
@@ -2613,19 +2613,19 @@ void ContentEnumFunc(const RuleValue& va
                       data->mTreeMatchContext, selectorFlags)) {
     nsCSSSelector *next = selector->mNext;
     if (!next ||
         SelectorMatchesTree(data->mElement, next,
                             data->mTreeMatchContext,
                             nodeContext.mIsRelevantLink ?
                               SelectorMatchesTreeFlags(0) :
                               eLookForRelevantLink)) {
-      css::StyleRule *rule = value.mRule;
-      rule->RuleMatched();
-      data->mRuleWalker->Forward(rule);
+      css::Declaration* declaration = value.mRule->GetDeclaration();
+      declaration->SetImmutable();
+      data->mRuleWalker->Forward(declaration);
       // nsStyleSet will deal with the !important rule
     }
   }
 }
 
 /* virtual */ void
 nsCSSRuleProcessor::RulesMatching(ElementRuleProcessorData *aData)
 {
@@ -2660,18 +2660,19 @@ nsCSSRuleProcessor::RulesMatching(AnonBo
 
   if (cascade && cascade->mAnonBoxRules.EntryCount()) {
     auto entry = static_cast<RuleHashTagTableEntry*>
                             (cascade->mAnonBoxRules.Search(aData->mPseudoTag));
     if (entry) {
       nsTArray<RuleValue>& rules = entry->mRules;
       for (RuleValue *value = rules.Elements(), *end = value + rules.Length();
            value != end; ++value) {
-        value->mRule->RuleMatched();
-        aData->mRuleWalker->Forward(value->mRule);
+        css::Declaration* declaration = value->mRule->GetDeclaration();
+        declaration->SetImmutable();
+        aData->mRuleWalker->Forward(declaration);
       }
     }
   }
 }
 
 #ifdef MOZ_XUL
 /* virtual */ void
 nsCSSRuleProcessor::RulesMatching(XULTreeRuleProcessorData* aData)
--- a/layout/style/nsCSSRuleProcessor.h
+++ b/layout/style/nsCSSRuleProcessor.h
@@ -14,16 +14,17 @@
 
 #include "mozilla/Attributes.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/RefCountType.h"
 #include "mozilla/SheetType.h"
 #include "mozilla/UniquePtr.h"
 #include "nsAutoPtr.h"
+#include "nsCSSPseudoElements.h"
 #include "nsExpirationTracker.h"
 #include "nsIMediaList.h"
 #include "nsIStyleRuleProcessor.h"
 #include "nsRuleWalker.h"
 #include "nsTArray.h"
 
 struct CascadeEnumData;
 struct ElementDependentRuleProcessorData;
--- a/layout/style/nsCSSRules.cpp
+++ b/layout/style/nsCSSRules.cpp
@@ -41,23 +41,19 @@
 using namespace mozilla;
 using namespace mozilla::dom;
 
 #define IMPL_STYLE_RULE_INHERIT_GET_DOM_RULE_WEAK(class_, super_) \
   /* virtual */ nsIDOMCSSRule* class_::GetDOMRule()               \
   { return this; }                                                \
   /* virtual */ nsIDOMCSSRule* class_::GetExistingDOMRule()       \
   { return this; }
-#define IMPL_STYLE_RULE_INHERIT_MAP_RULE_INFO_INTO(class_, super_) \
-/* virtual */ void class_::MapRuleInfoInto(nsRuleData* aRuleData) \
-  { MOZ_ASSERT(false, "should not be called"); }
 
 #define IMPL_STYLE_RULE_INHERIT(class_, super_) \
-IMPL_STYLE_RULE_INHERIT_GET_DOM_RULE_WEAK(class_, super_) \
-IMPL_STYLE_RULE_INHERIT_MAP_RULE_INFO_INTO(class_, super_)
+IMPL_STYLE_RULE_INHERIT_GET_DOM_RULE_WEAK(class_, super_)
 
 // base class for all rule types in a CSS style sheet
 
 namespace mozilla {
 namespace css {
 
 CSSStyleSheet*
 Rule::GetStyleSheet() const
@@ -243,20 +239,19 @@ ImportRule::~ImportRule()
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(ImportRule)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(ImportRule)
 
 NS_IMPL_CYCLE_COLLECTION(ImportRule, mMedia, mChildSheet)
 
 // QueryInterface implementation for ImportRule
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ImportRule)
-  NS_INTERFACE_MAP_ENTRY(nsIStyleRule)
   NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRule)
   NS_INTERFACE_MAP_ENTRY(nsIDOMCSSImportRule)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRule)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozilla::css::Rule)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CSSImportRule)
 NS_INTERFACE_MAP_END
 
 IMPL_STYLE_RULE_INHERIT(ImportRule, Rule)
 
 #ifdef DEBUG
 /* virtual */ void
 ImportRule::List(FILE* out, int32_t aIndent) const
@@ -424,18 +419,16 @@ GroupRule::~GroupRule()
 }
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(GroupRule)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(GroupRule)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(GroupRule)
 NS_INTERFACE_MAP_END
 
-IMPL_STYLE_RULE_INHERIT_MAP_RULE_INFO_INTO(GroupRule, Rule)
-
 static bool
 SetStyleSheetReference(Rule* aRule, void* aSheet)
 {
   CSSStyleSheet* sheet = (CSSStyleSheet*)aSheet;
   aRule->SetStyleSheet(sheet);
   return true;
 }
 
@@ -657,22 +650,21 @@ MediaRule::~MediaRule()
   }
 }
 
 NS_IMPL_ADDREF_INHERITED(MediaRule, GroupRule)
 NS_IMPL_RELEASE_INHERITED(MediaRule, GroupRule)
 
 // QueryInterface implementation for MediaRule
 NS_INTERFACE_MAP_BEGIN(MediaRule)
-  NS_INTERFACE_MAP_ENTRY(nsIStyleRule)
   NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRule)
   NS_INTERFACE_MAP_ENTRY(nsIDOMCSSGroupingRule)
   NS_INTERFACE_MAP_ENTRY(nsIDOMCSSConditionRule)
   NS_INTERFACE_MAP_ENTRY(nsIDOMCSSMediaRule)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRule)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozilla::css::Rule)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CSSMediaRule)
 NS_INTERFACE_MAP_END_INHERITING(GroupRule)
 
 /* virtual */ void
 MediaRule::SetStyleSheet(CSSStyleSheet* aSheet)
 {
   if (mMedia) {
     // Set to null so it knows it's leaving one sheet and joining another.
@@ -875,22 +867,21 @@ DocumentRule::~DocumentRule()
 {
 }
 
 NS_IMPL_ADDREF_INHERITED(DocumentRule, GroupRule)
 NS_IMPL_RELEASE_INHERITED(DocumentRule, GroupRule)
 
 // QueryInterface implementation for DocumentRule
 NS_INTERFACE_MAP_BEGIN(DocumentRule)
-  NS_INTERFACE_MAP_ENTRY(nsIStyleRule)
   NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRule)
   NS_INTERFACE_MAP_ENTRY(nsIDOMCSSGroupingRule)
   NS_INTERFACE_MAP_ENTRY(nsIDOMCSSConditionRule)
   NS_INTERFACE_MAP_ENTRY(nsIDOMCSSMozDocumentRule)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRule)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozilla::css::Rule)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CSSMozDocumentRule)
 NS_INTERFACE_MAP_END_INHERITING(GroupRule)
 
 #ifdef DEBUG
 /* virtual */ void
 DocumentRule::List(FILE* out, int32_t aIndent) const
 {
   nsAutoCString indentStr;
@@ -1144,19 +1135,18 @@ NS_IMPL_RELEASE(NameSpaceRule)
 // QueryInterface implementation for NameSpaceRule
 NS_INTERFACE_MAP_BEGIN(NameSpaceRule)
   if (aIID.Equals(NS_GET_IID(css::NameSpaceRule))) {
     *aInstancePtr = this;
     NS_ADDREF_THIS();
     return NS_OK;
   }
   else
-  NS_INTERFACE_MAP_ENTRY(nsIStyleRule)
   NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRule)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRule)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozilla::css::Rule)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CSSNameSpaceRule)
 NS_INTERFACE_MAP_END
 
 IMPL_STYLE_RULE_INHERIT(NameSpaceRule, Rule)
 
 #ifdef DEBUG
 /* virtual */ void
 NameSpaceRule::List(FILE* out, int32_t aIndent) const
@@ -1576,20 +1566,19 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsCSSFontFaceRule)
   // NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS will call into our
   // Trace hook, where we do the right thing with declarations already.
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 // QueryInterface implementation for nsCSSFontFaceRule
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsCSSFontFaceRule)
-  NS_INTERFACE_MAP_ENTRY(nsIStyleRule)
   NS_INTERFACE_MAP_ENTRY(nsIDOMCSSFontFaceRule)
   NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRule)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRule)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozilla::css::Rule)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CSSFontFaceRule)
 NS_INTERFACE_MAP_END
 
 IMPL_STYLE_RULE_INHERIT(nsCSSFontFaceRule, Rule)
 
 #ifdef DEBUG
 void
 nsCSSFontFaceRule::List(FILE* out, int32_t aIndent) const
@@ -1721,20 +1710,19 @@ nsCSSFontFeatureValuesRule::Clone() cons
   return clone.forget();
 }
 
 NS_IMPL_ADDREF(nsCSSFontFeatureValuesRule)
 NS_IMPL_RELEASE(nsCSSFontFeatureValuesRule)
 
 // QueryInterface implementation for nsCSSFontFeatureValuesRule
 NS_INTERFACE_MAP_BEGIN(nsCSSFontFeatureValuesRule)
-  NS_INTERFACE_MAP_ENTRY(nsIStyleRule)
   NS_INTERFACE_MAP_ENTRY(nsIDOMCSSFontFeatureValuesRule)
   NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRule)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRule)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozilla::css::Rule)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CSSFontFeatureValuesRule)
 NS_INTERFACE_MAP_END
 
 IMPL_STYLE_RULE_INHERIT(nsCSSFontFeatureValuesRule, Rule)
 
 static void
 FeatureValuesToString(
   const nsTArray<gfxFontFeatureValueSet::FeatureValues>& aFeatureValues,
@@ -2023,20 +2011,22 @@ nsCSSKeyframeStyleDeclaration::GetParent
 //
 
 nsCSSKeyframeRule::nsCSSKeyframeRule(const nsCSSKeyframeRule& aCopy)
   // copy everything except our reference count and mDOMDeclaration
   : Rule(aCopy)
   , mKeys(aCopy.mKeys)
   , mDeclaration(new css::Declaration(*aCopy.mDeclaration))
 {
+  mDeclaration->SetOwningRule(this);
 }
 
 nsCSSKeyframeRule::~nsCSSKeyframeRule()
 {
+  mDeclaration->SetOwningRule(nullptr);
   if (mDOMDeclaration) {
     mDOMDeclaration->DropReference();
   }
 }
 
 /* virtual */ already_AddRefed<css::Rule>
 nsCSSKeyframeRule::Clone() const
 {
@@ -2056,39 +2046,24 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
   }
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsCSSKeyframeRule)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMDeclaration)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 // QueryInterface implementation for nsCSSKeyframeRule
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsCSSKeyframeRule)
-  NS_INTERFACE_MAP_ENTRY(nsIStyleRule)
   NS_INTERFACE_MAP_ENTRY(nsIDOMMozCSSKeyframeRule)
   NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRule)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRule)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozilla::css::Rule)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(MozCSSKeyframeRule)
 NS_INTERFACE_MAP_END
 
 IMPL_STYLE_RULE_INHERIT_GET_DOM_RULE_WEAK(nsCSSKeyframeRule, Rule)
 
-/* virtual */ void
-nsCSSKeyframeRule::MapRuleInfoInto(nsRuleData* aRuleData)
-{
-  // We need to implement MapRuleInfoInto because the animation manager
-  // constructs a rule node pointing to us in order to compute the
-  // styles it needs to animate.
-
-  // The spec says that !important declarations should just be ignored
-  NS_ASSERTION(!mDeclaration->HasImportantData(),
-               "Keyframe rules has !important data");
-
-  mDeclaration->MapNormalRuleInfoInto(aRuleData);
-}
-
 #ifdef DEBUG
 void
 nsCSSKeyframeRule::List(FILE* out, int32_t aIndent) const
 {
   nsAutoCString str;
   for (int32_t index = aIndent; --index >= 0; ) {
     str.AppendLiteral("  ");
   }
@@ -2220,20 +2195,20 @@ void
 nsCSSKeyframeRule::ChangeDeclaration(css::Declaration* aDeclaration)
 {
   // Our caller already did a BeginUpdate/EndUpdate, but with
   // UPDATE_CONTENT, and we need UPDATE_STYLE to trigger work in
   // PresShell::EndUpdate.
   nsIDocument* doc = GetDocument();
   MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true);
 
-  // Be careful to not assign to an nsAutoPtr if we would be assigning
-  // the thing it already holds.
   if (aDeclaration != mDeclaration) {
+    mDeclaration->SetOwningRule(nullptr);
     mDeclaration = aDeclaration;
+    mDeclaration->SetOwningRule(this);
   }
 
   CSSStyleSheet* sheet = GetStyleSheet();
   if (sheet) {
     sheet->SetModifiedByChildRule();
 
     if (doc) {
       doc->StyleRuleChanged(sheet, this, this);
@@ -2278,20 +2253,19 @@ nsCSSKeyframesRule::Clone() const
   return clone.forget();
 }
 
 NS_IMPL_ADDREF_INHERITED(nsCSSKeyframesRule, css::GroupRule)
 NS_IMPL_RELEASE_INHERITED(nsCSSKeyframesRule, css::GroupRule)
 
 // QueryInterface implementation for nsCSSKeyframesRule
 NS_INTERFACE_MAP_BEGIN(nsCSSKeyframesRule)
-  NS_INTERFACE_MAP_ENTRY(nsIStyleRule)
   NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRule)
   NS_INTERFACE_MAP_ENTRY(nsIDOMMozCSSKeyframesRule)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRule)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozilla::css::Rule)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(MozCSSKeyframesRule)
 NS_INTERFACE_MAP_END_INHERITING(GroupRule)
 
 #ifdef DEBUG
 void
 nsCSSKeyframesRule::List(FILE* out, int32_t aIndent) const
 {
   nsAutoCString indentStr;
@@ -2583,20 +2557,22 @@ nsCSSPageStyleDeclaration::GetParentObje
 // nsCSSPageRule
 //
 
 nsCSSPageRule::nsCSSPageRule(const nsCSSPageRule& aCopy)
   // copy everything except our reference count and mDOMDeclaration
   : Rule(aCopy)
   , mDeclaration(new css::Declaration(*aCopy.mDeclaration))
 {
+  mDeclaration->SetOwningRule(this);
 }
 
 nsCSSPageRule::~nsCSSPageRule()
 {
+  mDeclaration->SetOwningRule(nullptr);
   if (mDOMDeclaration) {
     mDOMDeclaration->DropReference();
   }
 }
 
 /* virtual */ already_AddRefed<css::Rule>
 nsCSSPageRule::Clone() const
 {
@@ -2616,20 +2592,19 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
   }
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsCSSPageRule)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMDeclaration)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 // QueryInterface implementation for nsCSSPageRule
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsCSSPageRule)
-  NS_INTERFACE_MAP_ENTRY(nsIStyleRule)
   NS_INTERFACE_MAP_ENTRY(nsIDOMCSSPageRule)
   NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRule)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRule)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozilla::css::Rule)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CSSPageRule)
 NS_INTERFACE_MAP_END
 
 IMPL_STYLE_RULE_INHERIT_GET_DOM_RULE_WEAK(nsCSSPageRule, Rule)
 
 #ifdef DEBUG
 void
 nsCSSPageRule::List(FILE* out, int32_t aIndent) const
@@ -2692,52 +2667,33 @@ nsCSSPageRule::GetParentRule(nsIDOMCSSRu
 }
 
 css::Rule*
 nsCSSPageRule::GetCSSRule()
 {
   return Rule::GetCSSRule();
 }
 
-css::ImportantRule*
-nsCSSPageRule::GetImportantRule()
-{
-  if (!mDeclaration->HasImportantData()) {
-    return nullptr;
-  }
-  if (!mImportantRule) {
-    mImportantRule = new css::ImportantRule(mDeclaration);
-  }
-  return mImportantRule;
-}
-
-/* virtual */ void
-nsCSSPageRule::MapRuleInfoInto(nsRuleData* aRuleData)
-{
-  mDeclaration->MapNormalRuleInfoInto(aRuleData);
-}
-
 NS_IMETHODIMP
 nsCSSPageRule::GetStyle(nsIDOMCSSStyleDeclaration** aStyle)
 {
   if (!mDOMDeclaration) {
     mDOMDeclaration = new nsCSSPageStyleDeclaration(this);
   }
   NS_ADDREF(*aStyle = mDOMDeclaration);
   return NS_OK;
 }
 
 void
 nsCSSPageRule::ChangeDeclaration(css::Declaration* aDeclaration)
 {
-  mImportantRule = nullptr;
-  // Be careful to not assign to an nsAutoPtr if we would be assigning
-  // the thing it already holds.
   if (aDeclaration != mDeclaration) {
+    mDeclaration->SetOwningRule(nullptr);
     mDeclaration = aDeclaration;
+    mDeclaration->SetOwningRule(this);
   }
 
   CSSStyleSheet* sheet = GetStyleSheet();
   if (sheet) {
     sheet->SetModifiedByChildRule();
   }
 }
 
@@ -2807,22 +2763,21 @@ CSSSupportsRule::UseForPresentation(nsPr
   return mUseGroup;
 }
 
 NS_IMPL_ADDREF_INHERITED(CSSSupportsRule, css::GroupRule)
 NS_IMPL_RELEASE_INHERITED(CSSSupportsRule, css::GroupRule)
 
 // QueryInterface implementation for CSSSupportsRule
 NS_INTERFACE_MAP_BEGIN(CSSSupportsRule)
-  NS_INTERFACE_MAP_ENTRY(nsIStyleRule)
   NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRule)
   NS_INTERFACE_MAP_ENTRY(nsIDOMCSSGroupingRule)
   NS_INTERFACE_MAP_ENTRY(nsIDOMCSSConditionRule)
   NS_INTERFACE_MAP_ENTRY(nsIDOMCSSSupportsRule)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRule)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozilla::css::Rule)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CSSSupportsRule)
 NS_INTERFACE_MAP_END_INHERITING(GroupRule)
 
 // nsIDOMCSSRule methods
 NS_IMETHODIMP
 CSSSupportsRule::GetType(uint16_t* aType)
 {
   *aType = nsIDOMCSSRule::SUPPORTS_RULE;
@@ -2938,20 +2893,19 @@ nsCSSCounterStyleRule::kGetters[] = {
 #undef CSS_COUNTER_DESC
 };
 
 NS_IMPL_ADDREF(nsCSSCounterStyleRule)
 NS_IMPL_RELEASE(nsCSSCounterStyleRule)
 
 // QueryInterface implementation for nsCSSCounterStyleRule
 NS_INTERFACE_MAP_BEGIN(nsCSSCounterStyleRule)
-  NS_INTERFACE_MAP_ENTRY(nsIStyleRule)
   NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRule)
   NS_INTERFACE_MAP_ENTRY(nsIDOMCSSCounterStyleRule)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRule)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozilla::css::Rule)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CSSCounterStyleRule)
 NS_INTERFACE_MAP_END
 
 IMPL_STYLE_RULE_INHERIT(nsCSSCounterStyleRule, css::Rule)
 
 #ifdef DEBUG
 void
 nsCSSCounterStyleRule::List(FILE* out, int32_t aIndent) const
--- a/layout/style/nsCSSRules.h
+++ b/layout/style/nsCSSRules.h
@@ -50,22 +50,20 @@ public:
   MediaRule(uint32_t aLineNumber, uint32_t aColumnNumber);
 private:
   MediaRule(const MediaRule& aCopy);
   ~MediaRule();
 public:
 
   NS_DECL_ISUPPORTS_INHERITED
 
-  // nsIStyleRule methods
+  // Rule methods
 #ifdef DEBUG
   virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
 #endif
-
-  // Rule methods
   virtual void SetStyleSheet(mozilla::CSSStyleSheet* aSheet) override; //override GroupRule
   virtual int32_t GetType() const override;
   virtual already_AddRefed<Rule> Clone() const override;
   virtual nsIDOMCSSRule* GetDOMRule() override
   {
     return this;
   }
   virtual nsIDOMCSSRule* GetExistingDOMRule() override
@@ -108,22 +106,20 @@ public:
   DocumentRule(uint32_t aLineNumber, uint32_t aColumnNumber);
 private:
   DocumentRule(const DocumentRule& aCopy);
   ~DocumentRule();
 public:
 
   NS_DECL_ISUPPORTS_INHERITED
 
-  // nsIStyleRule methods
+  // Rule methods
 #ifdef DEBUG
   virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
 #endif
-
-  // Rule methods
   virtual int32_t GetType() const override;
   virtual already_AddRefed<Rule> Clone() const override;
   virtual nsIDOMCSSRule* GetDOMRule() override
   {
     return this;
   }
   virtual nsIDOMCSSRule* GetExistingDOMRule() override
   {
@@ -246,24 +242,21 @@ public:
   nsCSSFontFaceRule(const nsCSSFontFaceRule& aCopy)
     // copy everything except our reference count
     : mozilla::css::Rule(aCopy), mDecl(aCopy.mDecl) {}
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsCSSFontFaceRule,
                                                          mozilla::css::Rule)
 
-  // nsIStyleRule methods
+  // Rule methods
+  DECL_STYLE_RULE_INHERIT
 #ifdef DEBUG
   virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
 #endif
-
-  // Rule methods
-  DECL_STYLE_RULE_INHERIT
-
   virtual int32_t GetType() const override;
   virtual already_AddRefed<mozilla::css::Rule> Clone() const override;
 
   // nsIDOMCSSRule interface
   NS_DECL_NSIDOMCSSRULE
 
   // nsIDOMCSSFontFaceRule interface
   NS_DECL_NSIDOMCSSFONTFACERULE
@@ -314,24 +307,21 @@ public:
   nsCSSFontFeatureValuesRule(const nsCSSFontFeatureValuesRule& aCopy)
     // copy everything except our reference count
     : mozilla::css::Rule(aCopy),
       mFamilyList(aCopy.mFamilyList),
       mFeatureValues(aCopy.mFeatureValues) {}
 
   NS_DECL_ISUPPORTS
 
-  // nsIStyleRule methods
+  // Rule methods
+  DECL_STYLE_RULE_INHERIT
 #ifdef DEBUG
   virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
 #endif
-
-  // Rule methods
-  DECL_STYLE_RULE_INHERIT
-
   virtual int32_t GetType() const override;
   virtual already_AddRefed<mozilla::css::Rule> Clone() const override;
 
   // nsIDOMCSSRule interface
   NS_DECL_NSIDOMCSSRULE
 
   // nsIDOMCSSFontFaceRule interface
   NS_DECL_NSIDOMCSSFONTFEATUREVALUESRULE
@@ -383,39 +373,38 @@ protected:
   // when it's about to go away.
   nsCSSKeyframeRule* MOZ_NON_OWNING_REF mRule;
 };
 
 class nsCSSKeyframeRule final : public mozilla::css::Rule,
                                 public nsIDOMMozCSSKeyframeRule
 {
 public:
-  // WARNING: Steals the contents of aKeys *and* aDeclaration
+  // WARNING: Steals the contents of aKeys
   nsCSSKeyframeRule(InfallibleTArray<float>& aKeys,
-                    nsAutoPtr<mozilla::css::Declaration>&& aDeclaration,
+                    mozilla::css::Declaration* aDeclaration,
                     uint32_t aLineNumber, uint32_t aColumnNumber)
     : mozilla::css::Rule(aLineNumber, aColumnNumber)
-    , mDeclaration(mozilla::Move(aDeclaration))
+    , mDeclaration(aDeclaration)
   {
     mKeys.SwapElements(aKeys);
+    mDeclaration->SetOwningRule(this);
   }
 private:
   nsCSSKeyframeRule(const nsCSSKeyframeRule& aCopy);
   ~nsCSSKeyframeRule();
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsCSSKeyframeRule, nsIStyleRule)
+  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsCSSKeyframeRule, mozilla::css::Rule)
 
-  // nsIStyleRule methods
+  // Rule methods
+  DECL_STYLE_RULE_INHERIT
 #ifdef DEBUG
   virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
 #endif
-
-  // Rule methods
-  DECL_STYLE_RULE_INHERIT
   virtual int32_t GetType() const override;
   virtual already_AddRefed<mozilla::css::Rule> Clone() const override;
 
   // nsIDOMCSSRule interface
   NS_DECL_NSIDOMCSSRULE
 
   // nsIDOMMozCSSKeyframeRule interface
   NS_DECL_NSIDOMMOZCSSKEYFRAMERULE
@@ -426,17 +415,17 @@ public:
   void ChangeDeclaration(mozilla::css::Declaration* aDeclaration);
 
   virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override;
 
   void DoGetKeyText(nsAString &aKeyText) const;
 
 private:
   nsTArray<float>                            mKeys;
-  nsAutoPtr<mozilla::css::Declaration>       mDeclaration;
+  RefPtr<mozilla::css::Declaration>          mDeclaration;
   // lazily created when needed:
   RefPtr<nsCSSKeyframeStyleDeclaration>    mDOMDeclaration;
 };
 
 class nsCSSKeyframesRule final : public mozilla::css::GroupRule,
                                  public nsIDOMMozCSSKeyframesRule
 {
 public:
@@ -447,22 +436,20 @@ public:
   {
   }
 private:
   nsCSSKeyframesRule(const nsCSSKeyframesRule& aCopy);
   ~nsCSSKeyframesRule();
 public:
   NS_DECL_ISUPPORTS_INHERITED
 
-  // nsIStyleRule methods
+  // Rule methods
 #ifdef DEBUG
   virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
 #endif
-
-  // Rule methods
   virtual int32_t GetType() const override;
   virtual already_AddRefed<mozilla::css::Rule> Clone() const override;
   virtual nsIDOMCSSRule* GetDOMRule() override
   {
     return this;
   }
   virtual nsIDOMCSSRule* GetExistingDOMRule() override
   {
@@ -516,77 +503,69 @@ protected:
   // when it's about to go away.
   nsCSSPageRule* MOZ_NON_OWNING_REF mRule;
 };
 
 class nsCSSPageRule final : public mozilla::css::Rule,
                             public nsIDOMCSSPageRule
 {
 public:
-  // WARNING: Steals the contents of aDeclaration
-  nsCSSPageRule(nsAutoPtr<mozilla::css::Declaration>&& aDeclaration,
+  nsCSSPageRule(mozilla::css::Declaration* aDeclaration,
                 uint32_t aLineNumber, uint32_t aColumnNumber)
     : mozilla::css::Rule(aLineNumber, aColumnNumber)
-    , mDeclaration(mozilla::Move(aDeclaration))
-    , mImportantRule(nullptr)
+    , mDeclaration(aDeclaration)
   {
+    mDeclaration->SetOwningRule(this);
   }
 private:
   nsCSSPageRule(const nsCSSPageRule& aCopy);
   ~nsCSSPageRule();
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsCSSPageRule, nsIDOMCSSPageRule)
 
-  // nsIStyleRule methods
+  // Rule methods
+  DECL_STYLE_RULE_INHERIT
 #ifdef DEBUG
   virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
 #endif
-
-  // Rule methods
-  DECL_STYLE_RULE_INHERIT
   virtual int32_t GetType() const override;
   virtual already_AddRefed<mozilla::css::Rule> Clone() const override;
 
   // nsIDOMCSSRule interface
   NS_DECL_NSIDOMCSSRULE
 
   // nsIDOMCSSPageRule interface
   NS_DECL_NSIDOMCSSPAGERULE
 
   mozilla::css::Declaration* Declaration()   { return mDeclaration; }
 
   void ChangeDeclaration(mozilla::css::Declaration* aDeclaration);
 
-  mozilla::css::ImportantRule* GetImportantRule();
-
   virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override;
 private:
-  nsAutoPtr<mozilla::css::Declaration>    mDeclaration;
+  RefPtr<mozilla::css::Declaration>     mDeclaration;
   // lazily created when needed:
   RefPtr<nsCSSPageStyleDeclaration>     mDOMDeclaration;
-  RefPtr<mozilla::css::ImportantRule>   mImportantRule;
 };
 
 namespace mozilla {
 
 class CSSSupportsRule : public css::GroupRule,
                         public nsIDOMCSSSupportsRule
 {
 public:
   CSSSupportsRule(bool aConditionMet, const nsString& aCondition,
                   uint32_t aLineNumber, uint32_t aColumnNumber);
   CSSSupportsRule(const CSSSupportsRule& aCopy);
 
-  // nsIStyleRule methods
+  // Rule methods
 #ifdef DEBUG
   virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
 #endif
-
-  // Rule methods
   virtual int32_t GetType() const override;
   virtual already_AddRefed<mozilla::css::Rule> Clone() const override;
   virtual bool UseForPresentation(nsPresContext* aPresContext,
                                   nsMediaQueryResultCacheKey& aKey) override;
   virtual nsIDOMCSSRule* GetDOMRule() override
   {
     return this;
   }
@@ -634,23 +613,21 @@ public:
 
 private:
   nsCSSCounterStyleRule(const nsCSSCounterStyleRule& aCopy);
   ~nsCSSCounterStyleRule();
 
 public:
   NS_DECL_ISUPPORTS
 
-  // nsIStyleRule methods
+  // Rule methods
+  DECL_STYLE_RULE_INHERIT
 #ifdef DEBUG
   virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
 #endif
-
-  // Rule methods
-  DECL_STYLE_RULE_INHERIT
   virtual int32_t GetType() const override;
   virtual already_AddRefed<mozilla::css::Rule> Clone() const override;
 
   // nsIDOMCSSRule interface
   NS_DECL_NSIDOMCSSRULE
 
   // nsIDOMCSSCounterStyleRule
   NS_DECL_NSIDOMCSSCOUNTERSTYLERULE
--- a/layout/style/nsDOMCSSAttrDeclaration.cpp
+++ b/layout/style/nsDOMCSSAttrDeclaration.cpp
@@ -133,17 +133,17 @@ nsDOMCSSAttributeDeclaration::GetCSSDecl
     return cssRule->GetDeclaration();
   }
 
   if (aOperation != eOperation_Modify) {
     return nullptr;
   }
 
   // cannot fail
-  css::Declaration *decl = new css::Declaration();
+  RefPtr<css::Declaration> decl = new css::Declaration();
   decl->InitializeEmpty();
   RefPtr<css::StyleRule> newRule = new css::StyleRule(nullptr, decl, 0, 0);
 
   // this *can* fail (inside SetAttrAndNotify, at least).
   nsresult rv;
   if (mIsSMILOverride)
     rv = mElement->SetSMILOverrideStyleRule(newRule, false);
   else
--- a/layout/style/nsDOMCSSDeclaration.cpp
+++ b/layout/style/nsDOMCSSDeclaration.cpp
@@ -102,44 +102,44 @@ nsDOMCSSDeclaration::GetCssText(nsAStrin
 
 NS_IMETHODIMP
 nsDOMCSSDeclaration::SetCssText(const nsAString& aCssText)
 {
   // We don't need to *do* anything with the old declaration, but we need
   // to ensure that it exists, or else SetCSSDeclaration may crash.
   css::Declaration* olddecl = GetCSSDeclaration(eOperation_Modify);
   if (!olddecl) {
-    return NS_ERROR_FAILURE;
+    return NS_ERROR_NOT_AVAILABLE;
   }
 
   CSSParsingEnvironment env;
   GetCSSParsingEnvironment(env);
   if (!env.mPrincipal) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   // For nsDOMCSSAttributeDeclaration, SetCSSDeclaration will lead to
   // Attribute setting code, which leads in turn to BeginUpdate.  We
   // need to start the update now so that the old rule doesn't get used
   // between when we mutate the declaration and when we set the new
   // rule (see stack in bug 209575).
   mozAutoDocConditionalContentUpdateBatch autoUpdate(DocToUpdate(), true);
 
-  nsAutoPtr<css::Declaration> decl(new css::Declaration());
+  RefPtr<css::Declaration> decl(new css::Declaration());
   decl->InitializeEmpty();
   nsCSSParser cssParser(env.mCSSLoader);
   bool changed;
   nsresult result = cssParser.ParseDeclarations(aCssText, env.mSheetURI,
                                                 env.mBaseURI,
                                                 env.mPrincipal, decl, &changed);
   if (NS_FAILED(result) || !changed) {
     return result;
   }
 
-  return SetCSSDeclaration(decl.forget());
+  return SetCSSDeclaration(decl);
 }
 
 NS_IMETHODIMP
 nsDOMCSSDeclaration::GetLength(uint32_t* aLength)
 {
   css::Declaration* decl = GetCSSDeclaration(eOperation_Read);
 
   if (decl) {
@@ -308,127 +308,121 @@ nsDOMCSSDeclaration::GetCSSParsingEnviro
 
 nsresult
 nsDOMCSSDeclaration::ParsePropertyValue(const nsCSSProperty aPropID,
                                         const nsAString& aPropValue,
                                         bool aIsImportant)
 {
   css::Declaration* olddecl = GetCSSDeclaration(eOperation_Modify);
   if (!olddecl) {
-    return NS_ERROR_FAILURE;
+    return NS_ERROR_NOT_AVAILABLE;
   }
 
   CSSParsingEnvironment env;
   GetCSSParsingEnvironment(env);
   if (!env.mPrincipal) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   // For nsDOMCSSAttributeDeclaration, SetCSSDeclaration will lead to
   // Attribute setting code, which leads in turn to BeginUpdate.  We
   // need to start the update now so that the old rule doesn't get used
   // between when we mutate the declaration and when we set the new
   // rule (see stack in bug 209575).
   mozAutoDocConditionalContentUpdateBatch autoUpdate(DocToUpdate(), true);
-  css::Declaration* decl = olddecl->EnsureMutable();
+  RefPtr<css::Declaration> decl = olddecl->EnsureMutable();
 
   nsCSSParser cssParser(env.mCSSLoader);
   bool changed;
   cssParser.ParseProperty(aPropID, aPropValue, env.mSheetURI, env.mBaseURI,
                           env.mPrincipal, decl, &changed, aIsImportant);
   if (!changed) {
-    if (decl != olddecl) {
-      delete decl;
-    }
     // Parsing failed -- but we don't throw an exception for that.
     return NS_OK;
   }
 
   return SetCSSDeclaration(decl);
 }
 
 nsresult
 nsDOMCSSDeclaration::ParseCustomPropertyValue(const nsAString& aPropertyName,
                                               const nsAString& aPropValue,
                                               bool aIsImportant)
 {
   MOZ_ASSERT(nsCSSProps::IsCustomPropertyName(aPropertyName));
 
   css::Declaration* olddecl = GetCSSDeclaration(eOperation_Modify);
   if (!olddecl) {
-    return NS_ERROR_FAILURE;
+    return NS_ERROR_NOT_AVAILABLE;
   }
 
   CSSParsingEnvironment env;
   GetCSSParsingEnvironment(env);
   if (!env.mPrincipal) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   // For nsDOMCSSAttributeDeclaration, SetCSSDeclaration will lead to
   // Attribute setting code, which leads in turn to BeginUpdate.  We
   // need to start the update now so that the old rule doesn't get used
   // between when we mutate the declaration and when we set the new
   // rule (see stack in bug 209575).
   mozAutoDocConditionalContentUpdateBatch autoUpdate(DocToUpdate(), true);
-  css::Declaration* decl = olddecl->EnsureMutable();
+  RefPtr<css::Declaration> decl = olddecl->EnsureMutable();
 
   nsCSSParser cssParser(env.mCSSLoader);
   bool changed;
   cssParser.ParseVariable(Substring(aPropertyName,
                                     CSS_CUSTOM_NAME_PREFIX_LENGTH),
                           aPropValue, env.mSheetURI,
                           env.mBaseURI, env.mPrincipal, decl,
                           &changed, aIsImportant);
   if (!changed) {
-    if (decl != olddecl) {
-      delete decl;
-    }
     // Parsing failed -- but we don't throw an exception for that.
     return NS_OK;
   }
 
   return SetCSSDeclaration(decl);
 }
 
 nsresult
 nsDOMCSSDeclaration::RemoveProperty(const nsCSSProperty aPropID)
 {
-  css::Declaration* decl = GetCSSDeclaration(eOperation_RemoveProperty);
-  if (!decl) {
+  css::Declaration* olddecl = GetCSSDeclaration(eOperation_RemoveProperty);
+  if (!olddecl) {
     return NS_OK; // no decl, so nothing to remove
   }
 
   // For nsDOMCSSAttributeDeclaration, SetCSSDeclaration will lead to
   // Attribute setting code, which leads in turn to BeginUpdate.  We
   // need to start the update now so that the old rule doesn't get used
   // between when we mutate the declaration and when we set the new
   // rule (see stack in bug 209575).
   mozAutoDocConditionalContentUpdateBatch autoUpdate(DocToUpdate(), true);
 
-  decl = decl->EnsureMutable();
+  RefPtr<css::Declaration> decl = olddecl->EnsureMutable();
   decl->RemoveProperty(aPropID);
   return SetCSSDeclaration(decl);
 }
 
 nsresult
 nsDOMCSSDeclaration::RemoveCustomProperty(const nsAString& aPropertyName)
 {
   MOZ_ASSERT(Substring(aPropertyName, 0,
                        CSS_CUSTOM_NAME_PREFIX_LENGTH).EqualsLiteral("--"));
 
-  css::Declaration* decl = GetCSSDeclaration(eOperation_RemoveProperty);
-  if (!decl) {
+  css::Declaration* olddecl = GetCSSDeclaration(eOperation_RemoveProperty);
+  if (!olddecl) {
     return NS_OK; // no decl, so nothing to remove
   }
 
   // For nsDOMCSSAttributeDeclaration, SetCSSDeclaration will lead to
   // Attribute setting code, which leads in turn to BeginUpdate.  We
   // need to start the update now so that the old rule doesn't get used
   // between when we mutate the declaration and when we set the new
   // rule (see stack in bug 209575).
   mozAutoDocConditionalContentUpdateBatch autoUpdate(DocToUpdate(), true);
 
-  decl = decl->EnsureMutable();
+  RefPtr<css::Declaration> decl = olddecl->EnsureMutable();
   decl->RemoveVariableDeclaration(Substring(aPropertyName,
                                             CSS_CUSTOM_NAME_PREFIX_LENGTH));
   return SetCSSDeclaration(decl);
 }
--- a/layout/style/nsHTMLCSSStyleSheet.cpp
+++ b/layout/style/nsHTMLCSSStyleSheet.cpp
@@ -62,28 +62,30 @@ nsHTMLCSSStyleSheet::RulesMatching(Eleme
 void
 nsHTMLCSSStyleSheet::ElementRulesMatching(nsPresContext* aPresContext,
                                           Element* aElement,
                                           nsRuleWalker* aRuleWalker)
 {
   // just get the one and only style rule from the content's STYLE attribute
   css::StyleRule* rule = aElement->GetInlineStyleRule();
   if (rule) {
-    rule->RuleMatched();
-    aRuleWalker->Forward(rule);
+    css::Declaration* declaration = rule->GetDeclaration();
+    declaration->SetImmutable();
+    aRuleWalker->Forward(declaration);
   }
 
   rule = aElement->GetSMILOverrideStyleRule();
   if (rule) {
     RestyleManager* restyleManager = aPresContext->RestyleManager();
     if (!restyleManager->SkipAnimationRules()) {
       // Animation restyle (or non-restyle traversal of rules)
       // Now we can walk SMIL overrride style, without triggering transitions.
-      rule->RuleMatched();
-      aRuleWalker->Forward(rule);
+      css::Declaration* declaration = rule->GetDeclaration();
+      declaration->SetImmutable();
+      aRuleWalker->Forward(declaration);
     }
   }
 }
 
 void
 nsHTMLCSSStyleSheet::PseudoElementRulesMatching(Element* aPseudoElement,
                                                 nsCSSPseudoElements::Type
                                                   aPseudoType,
@@ -91,18 +93,19 @@ nsHTMLCSSStyleSheet::PseudoElementRulesM
 {
   MOZ_ASSERT(nsCSSPseudoElements::
                PseudoElementSupportsStyleAttribute(aPseudoType));
   MOZ_ASSERT(aPseudoElement);
 
   // just get the one and only style rule from the content's STYLE attribute
   css::StyleRule* rule = aPseudoElement->GetInlineStyleRule();
   if (rule) {
-    rule->RuleMatched();
-    aRuleWalker->Forward(rule);
+    css::Declaration* declaration = rule->GetDeclaration();
+    declaration->SetImmutable();
+    aRuleWalker->Forward(declaration);
   }
 }
 
 /* virtual */ void
 nsHTMLCSSStyleSheet::RulesMatching(PseudoElementRuleProcessorData* aData)
 {
   if (nsCSSPseudoElements::PseudoElementSupportsStyleAttribute(aData->mPseudoType) &&
       aData->mPseudoElement) {
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -12,16 +12,18 @@
 #include <algorithm>
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/Likely.h"
 #include "mozilla/LookAndFeel.h"
 
+#include "mozilla/css/Declaration.h"
+
 #include "nsAlgorithm.h" // for clamped()
 #include "nsRuleNode.h"
 #include "nscore.h"
 #include "nsIWidget.h"
 #include "nsIPresShell.h"
 #include "nsFontMetrics.h"
 #include "gfxFont.h"
 #include "nsCSSPseudoElements.h"
@@ -45,16 +47,17 @@
 #include "nsIDocument.h"
 #include "prtime.h"
 #include "CSSVariableResolver.h"
 #include "nsCSSParser.h"
 #include "CounterStyleManager.h"
 #include "nsCSSPropertySet.h"
 #include "mozilla/RuleNodeCacheConditions.h"
 #include "nsDeviceContext.h"
+#include "nsQueryObject.h"
 
 #if defined(_MSC_VER) || defined(__MINGW32__)
 #include <malloc.h>
 #ifdef _MSC_VER
 #define alloca _alloca
 #endif
 #endif
 #ifdef SOLARIS
@@ -1548,16 +1551,24 @@ nsRuleNode::~nsRuleNode()
     mRule->Release();
   }
 }
 
 nsRuleNode*
 nsRuleNode::Transition(nsIStyleRule* aRule, SheetType aLevel,
                        bool aIsImportantRule)
 {
+#ifdef DEBUG
+  {
+    RefPtr<css::Declaration> declaration(do_QueryObject(aRule));
+    MOZ_ASSERT(!declaration || !declaration->IsMutable(),
+               "caller must call Declaration::SetImmutable first");
+  }
+#endif
+
   nsRuleNode* next = nullptr;
   nsRuleNode::Key key(aRule, aLevel, aIsImportantRule);
 
   if (HaveChildren() && !ChildrenAreHashed()) {
     int32_t numKids = 0;
     nsRuleNode* curr = ChildrenList();
     while (curr && curr->GetKey() != key) {
       curr = curr->mNextSibling;
--- a/layout/style/nsRuleProcessorData.h
+++ b/layout/style/nsRuleProcessorData.h
@@ -17,16 +17,17 @@
 #include "nsRuleWalker.h"
 #include "nsNthIndexCache.h"
 #include "nsILoadContext.h"
 #include "nsIDocument.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/BloomFilter.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/GuardObjects.h"
+#include "mozilla/dom/Element.h"
 
 class nsIAtom;
 class nsIContent;
 class nsICSSPseudoComparator;
 struct TreeMatchContext;
 
 /**
  * An AncestorFilter is used to keep track of ancestors so that we can
--- a/layout/style/nsRuleWalker.h
+++ b/layout/style/nsRuleWalker.h
@@ -8,17 +8,17 @@
  * rules are matched
  */
 
 #ifndef nsRuleWalker_h_
 #define nsRuleWalker_h_
 
 #include "nsRuleNode.h"
 #include "nsIStyleRule.h"
-#include "StyleRule.h"
+#include "Declaration.h"
 #include "nsQueryObject.h"
 
 class nsRuleWalker {
 public:
   nsRuleNode* CurrentNode() { return mCurrent; }
   void SetCurrentNode(nsRuleNode* aNode) {
     NS_ASSERTION(aNode, "Must have node here!");
     mCurrent = aNode;
@@ -29,24 +29,24 @@ public:
 protected:
   void DoForward(nsIStyleRule* aRule) {
     mCurrent = mCurrent->Transition(aRule, mLevel, mImportance);
     NS_POSTCONDITION(mCurrent, "Transition messed up");
   }
 
 public:
   void Forward(nsIStyleRule* aRule) {
-    NS_PRECONDITION(!RefPtr<mozilla::css::StyleRule>(do_QueryObject(aRule)),
+    NS_PRECONDITION(!RefPtr<mozilla::css::Declaration>(do_QueryObject(aRule)),
                     "Calling the wrong Forward() overload");
     DoForward(aRule);
   }
-  void Forward(mozilla::css::StyleRule* aRule) {
+  void Forward(mozilla::css::Declaration* aRule) {
     DoForward(aRule);
     mCheckForImportantRules =
-      mCheckForImportantRules && !aRule->GetImportantRule();
+      mCheckForImportantRules && !aRule->HasImportantData();
   }
   // ForwardOnPossiblyCSSRule should only be used by callers that have
   // an explicit list of rules they need to walk, with the list
   // already containing any important rules they care about.
   void ForwardOnPossiblyCSSRule(nsIStyleRule* aRule) {
     DoForward(aRule);
   }
 
--- a/layout/style/nsStyleSet.cpp
+++ b/layout/style/nsStyleSet.cpp
@@ -242,22 +242,22 @@ nsStyleSet::Init(nsPresContext *aPresCon
   mFirstLetterRule = new nsEmptyStyleRule;
   mPlaceholderRule = new nsEmptyStyleRule;
   mDisableTextZoomStyleRule = new nsDisableTextZoomStyleRule;
 
   mRuleTree = nsRuleNode::CreateRootNode(aPresContext);
 
   // Make an explicit GatherRuleProcessors call for the levels that
   // don't have style sheets.  The other levels will have their calls
-  // triggered by DirtyRuleProcessors.  (We should probably convert the
-  // SheetType::PresHint and SheetType::StyleAttr levels to work like
-  // this as well, and not implement nsIStyleSheet.)
+  // triggered by DirtyRuleProcessors.
+  GatherRuleProcessors(SheetType::PresHint);
+  GatherRuleProcessors(SheetType::SVGAttrAnimation);
+  GatherRuleProcessors(SheetType::StyleAttr);
   GatherRuleProcessors(SheetType::Animation);
   GatherRuleProcessors(SheetType::Transition);
-  GatherRuleProcessors(SheetType::SVGAttrAnimation);
 }
 
 nsresult
 nsStyleSet::BeginReconstruct()
 {
   NS_ASSERTION(!mInReconstruct, "Unmatched begin/end?");
   NS_ASSERTION(mRuleTree, "Reconstructing before first construction?");
 
@@ -1034,21 +1034,21 @@ nsStyleSet::AddImportantRules(nsRuleNode
   NS_ASSERTION(aCurrLevelNode &&
                aCurrLevelNode != aLastPrevLevelNode, "How did we get here?");
 
   nsAutoTArray<nsIStyleRule*, 16> importantRules;
   for (nsRuleNode *node = aCurrLevelNode; node != aLastPrevLevelNode;
        node = node->GetParent()) {
     // We guarantee that we never walk the root node here, so no need
     // to null-check GetRule().  Furthermore, it must be a CSS rule.
-    NS_ASSERTION(RefPtr<css::StyleRule>(do_QueryObject(node->GetRule())),
+    NS_ASSERTION(RefPtr<css::Declaration>(do_QueryObject(node->GetRule())),
                  "Unexpected non-CSS rule");
 
     nsIStyleRule* impRule =
-      static_cast<css::StyleRule*>(node->GetRule())->GetImportantRule();
+      static_cast<css::Declaration*>(node->GetRule())->GetImportantStyleData();
     if (impRule)
       importantRules.AppendElement(impRule);
   }
 
   NS_ASSERTION(importantRules.Length() != 0,
                "Why did we think there were important rules?");
 
   for (uint32_t i = importantRules.Length(); i-- != 0; ) {
@@ -1061,35 +1061,41 @@ void
 nsStyleSet::AssertNoImportantRules(nsRuleNode* aCurrLevelNode,
                                    nsRuleNode* aLastPrevLevelNode)
 {
   if (!aCurrLevelNode)
     return;
 
   for (nsRuleNode *node = aCurrLevelNode; node != aLastPrevLevelNode;
        node = node->GetParent()) {
-    RefPtr<css::StyleRule> rule(do_QueryObject(node->GetRule()));
-    NS_ASSERTION(rule, "Unexpected non-CSS rule");
+    RefPtr<css::Declaration> declaration(do_QueryObject(node->GetRule()));
+    NS_ASSERTION(declaration, "Unexpected non-CSS rule");
 
-    NS_ASSERTION(!rule->GetImportantRule(), "Unexpected important rule");
+    NS_ASSERTION(!declaration->GetImportantStyleData(),
+                 "Unexpected important style source");
   }
 }
 
 void
 nsStyleSet::AssertNoCSSRules(nsRuleNode* aCurrLevelNode,
                              nsRuleNode* aLastPrevLevelNode)
 {
   if (!aCurrLevelNode)
     return;
 
   for (nsRuleNode *node = aCurrLevelNode; node != aLastPrevLevelNode;
        node = node->GetParent()) {
     nsIStyleRule *rule = node->GetRule();
-    RefPtr<css::StyleRule> cssRule(do_QueryObject(rule));
-    NS_ASSERTION(!cssRule || !cssRule->Selector(), "Unexpected CSS rule");
+    RefPtr<css::Declaration> declaration(do_QueryObject(rule));
+    if (declaration) {
+      RefPtr<css::StyleRule> cssRule =
+        do_QueryObject(declaration->GetOwningRule());
+      NS_ASSERTION(!cssRule || !cssRule->Selector(),
+                   "Unexpected CSS rule");
+    }
   }
 }
 #endif
 
 // Enumerate the rules in a way that cares about the order of the rules.
 void
 nsStyleSet::FileRules(nsIStyleRuleProcessor::EnumFunc aCollectorFunc, 
                       RuleProcessorData* aData, Element* aElement,
@@ -1955,21 +1961,24 @@ nsStyleSet::ResolveAnonymousBoxStyle(nsI
   nsRuleWalker ruleWalker(mRuleTree, mAuthorStyleDisabled);
   AnonBoxRuleProcessorData data(PresContext(), aPseudoTag, &ruleWalker);
   FileRules(EnumRulesMatching<AnonBoxRuleProcessorData>, &data, nullptr,
             &ruleWalker);
 
   if (aPseudoTag == nsCSSAnonBoxes::pageContent) {
     // Add any @page rules that are specified.
     nsTArray<nsCSSPageRule*> rules;
-    nsTArray<css::ImportantRule*> importantRules;
+    nsTArray<css::ImportantStyleData*> importantRules;
     PresContext()->StyleSet()->AppendPageRules(rules);
     for (uint32_t i = 0, i_end = rules.Length(); i != i_end; ++i) {
-      ruleWalker.Forward(rules[i]);
-      css::ImportantRule* importantRule = rules[i]->GetImportantRule();
+      css::Declaration* declaration = rules[i]->Declaration();
+      declaration->SetImmutable();
+      ruleWalker.Forward(declaration);
+      css::ImportantStyleData* importantRule =
+        declaration->GetImportantStyleData();
       if (importantRule) {
         importantRules.AppendElement(importantRule);
       }
     }
     for (uint32_t i = 0, i_end = importantRules.Length(); i != i_end; ++i) {
       ruleWalker.Forward(importantRules[i]);
     }
   }
--- a/layout/style/nsStyleSet.h
+++ b/layout/style/nsStyleSet.h
@@ -312,18 +312,16 @@ class nsStyleSet final
   nsresult PrependStyleSheet(mozilla::SheetType aType, nsIStyleSheet *aSheet);
   nsresult RemoveStyleSheet(mozilla::SheetType aType, nsIStyleSheet *aSheet);
   nsresult ReplaceSheets(mozilla::SheetType aType,
                          const nsCOMArray<nsIStyleSheet> &aNewSheets);
   nsresult InsertStyleSheetBefore(mozilla::SheetType aType,
                                   nsIStyleSheet *aNewSheet,
                                   nsIStyleSheet *aReferenceSheet);
 
-  nsresult DirtyRuleProcessors(mozilla::SheetType aType);
-
   // Enable/Disable entire author style level (Doc, ScopedDoc & PresHint levels)
   bool GetAuthorStyleDisabled();
   nsresult SetAuthorStyleDisabled(bool aStyleDisabled);
 
   int32_t SheetCount(mozilla::SheetType aType) const {
     return mSheets[aType].Count();
   }
 
@@ -388,23 +386,25 @@ class nsStyleSet final
   nsIStyleRule* InitialStyleRule();
 
   bool HasRuleProcessorUsedByMultipleStyleSets(mozilla::SheetType aSheetType);
 
   // Tells the RestyleManager for the document using this style set
   // to drop any nsCSSSelector pointers it has.
   void ClearSelectors();
 
- private:
+private:
   nsStyleSet(const nsStyleSet& aCopy) = delete;
   nsStyleSet& operator=(const nsStyleSet& aCopy) = delete;
 
   // Run mark-and-sweep GC on mRuleTree and mOldRuleTrees, based on mRoots.
   void GCRuleTrees();
 
+  nsresult DirtyRuleProcessors(mozilla::SheetType aType);
+
   // Update the rule processor list after a change to the style sheet list.
   nsresult GatherRuleProcessors(mozilla::SheetType aType);
 
   void AddImportantRules(nsRuleNode* aCurrLevelNode,
                          nsRuleNode* aLastPrevLevelNode,
                          nsRuleWalker* aRuleWalker);
 
   // Move aRuleWalker forward by the appropriate rule if we need to add
--- a/layout/style/test/mochitest.ini
+++ b/layout/style/test/mochitest.ini
@@ -36,16 +36,17 @@ support-files =
 generated-files = css_properties.js
 
 [test_acid3_test46.html]
 [test_all_shorthand.html]
 [test_animations.html]
 skip-if = toolkit == 'android'
 [test_animations_async_tests.html]
 support-files = ../../reftests/fonts/Ahem.ttf file_animations_async_tests.html
+[test_animations_dynamic_changes.html]
 [test_animations_event_order.html]
 [test_animations_omta.html]
 [test_animations_omta_start.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # bug 1041017
 [test_animations_pausing.html]
 support-files = file_animations_pausing.html
 [test_animations_playbackrate.html]
 support-files = file_animations_playbackrate.html
new file mode 100644
--- /dev/null
+++ b/layout/style/test/test_animations_dynamic_changes.html
@@ -0,0 +1,65 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=978833
+-->
+<head>
+  <title>Test for Bug 978833</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <style id="style">
+  @keyframes a {
+    from, to {
+      /* a non-inherited property, so it's cached in the rule tree */
+      margin-left: 50px;
+    }
+  }
+  .alwaysa {
+    animation: a linear 1s infinite;
+  }
+  </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=978833">Mozilla Bug 978833</a>
+<p id="display"></p>
+<pre id="test">
+<script type="application/javascript">
+
+var p = document.getElementById("display");
+var cs = getComputedStyle(p, "");
+var style = document.getElementById("style").sheet;
+
+/** Test for Bug 978833 **/
+function test_bug978833() {
+  var kfs = style.cssRules[0];
+  var kf = kfs.cssRules[0];
+  is(kf.style.marginLeft, "50px", "we found the right keyframe rule");
+
+  p.classList.add("alwaysa");
+  is(cs.marginLeft, "50px", "p margin-left should be 50px");
+
+  // Temporarily remove the animation style, since we resolve keyframes
+  // on top of current animation styles (although maybe we shouldn't),
+  // so we need to remove those styles to hit the rule tree cache.
+  p.classList.remove("alwaysa");
+  is(cs.marginLeft, "0px", "p margin-left should be 0px without animation");
+
+  p.classList.add("alwaysa");
+  kf.style.marginLeft = "100px";
+  is(cs.marginLeft, "100px", "p margin-left should be 100px after change");
+
+  // Try the same thing a second time, just to make sure it works again.
+  p.classList.remove("alwaysa");
+  is(cs.marginLeft, "0px", "p margin-left should be 0px without animation");
+  p.classList.add("alwaysa");
+  kf.style.marginLeft = "150px";
+  is(cs.marginLeft, "150px", "p margin-left should be 150px after second change");
+
+  p.style.animation = "";
+}
+test_bug978833();
+
+</script>
+</pre>
+</body>
+</html>
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -930,16 +930,21 @@ pref("caret.manages-android-actionbar", 
 pref("consoleservice.logcat", false);
 #else
 pref("consoleservice.logcat", true);
 #endif
 
 // Enable Cardboard VR on mobile, assuming VR at all is enabled
 pref("dom.vr.cardboard.enabled", true);
 
+#ifndef RELEASE_BUILD
+// Enable VR on mobile, making it enable by default.
+pref("dom.vr.enabled", true);
+#endif
+
 pref("browser.tabs.showAudioPlayingIcon", true);
 
 // Enable service workers and fetch interception on non-release Fennec
 #ifndef RELEASE_BUILD
 pref("dom.serviceWorkers.enabled", true);
 pref("dom.serviceWorkers.interception.enabled", true);
 #endif
 
--- a/mobile/android/base/db/BrowserContract.java
+++ b/mobile/android/base/db/BrowserContract.java
@@ -484,9 +484,11 @@ public class BrowserContract {
 
         public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "suggestedsites");
     }
 
     // We refer to the service by name to decouple services from the rest of the code base.
     public static final String TAB_RECEIVED_SERVICE_CLASS_NAME = "org.mozilla.gecko.tabqueue.TabReceivedService";
 
     public static final String SKIP_TAB_QUEUE_FLAG = "skip_tab_queue";
+
+    public static final String EXTRA_CLIENT_GUID = "org.mozilla.gecko.extra.CLIENT_ID";
 }
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/resources/values-v21/integers.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<resources>
+
+    <integer name="search_assist_launch_res">@drawable/search_launcher</integer>
+
+</resources>
--- a/mobile/android/base/resources/values-v21/styles.xml
+++ b/mobile/android/base/resources/values-v21/styles.xml
@@ -4,16 +4,14 @@
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 
 <resources>
 
    <style name="ActionBarTitleTextStyle" parent="@android:style/TextAppearance.Material.Widget.ActionBar.Title">
       <item name="android:textColor">#fff</item>
    </style>
 
-   <style name="ActionBarTheme" parent="@style/ThemeOverlay.AppCompat.ActionBar">
-      <item name="android:colorControlNormal">#fff</item>
-
-      <!-- This color is the system default. -->
-      <item name="android:colorControlHighlight">#65696D</item>
+   <style name="ActionBarThemeGeckoPreferences">
+       <!-- Icon color -->
+       <item name="colorControlNormal">#fff</item>
    </style>
 
 </resources>
--- a/mobile/android/base/resources/values-v21/themes.xml
+++ b/mobile/android/base/resources/values-v21/themes.xml
@@ -10,23 +10,24 @@
     -->
     <style name="GeckoBase" parent="Theme.AppCompat.Light.DarkActionBar">
         <item name="colorPrimary">@color/text_and_tabs_tray_grey</item>
         <item name="colorPrimaryDark">@color/text_and_tabs_tray_grey</item>
         <item name="windowActionBar">false</item>
         <item name="windowNoTitle">true</item>
         <item name="android:windowContentOverlay">@null</item>
         <item name="android:actionBarStyle">@style/GeckoActionBar</item>
-        <item name="android:actionBarTheme">@style/ActionBarTheme</item>
+        <!-- TODO (bug 1220863): Once we extend AppCompatActivity, set actionBarStyle. -->
     </style>
 
     <style name="GeckoPreferencesBase" parent="Gecko">
         <item name="android:windowActionBar">true</item>
         <item name="android:windowNoTitle">false</item>
         <item name="android:actionBarStyle">@style/ActionBar.GeckoPreferences</item>
+        <item name="android:actionBarTheme">@style/ActionBarThemeGeckoPreferences</item>
     </style>
 
     <style name="ActionBar.FxAccountStatusActivity" parent="@android:style/Widget.Material.ActionBar.Solid">
         <item name="android:displayOptions">homeAsUp|showTitle</item>
         <item name="android:titleTextStyle">@style/ActionBarTitleTextStyle</item>
     </style>
 
     <style name="ActionBar.GeckoPreferences" parent="@android:style/Widget.Material.ActionBar.Solid">
--- a/mobile/android/base/resources/values/integers.xml
+++ b/mobile/android/base/resources/values/integers.xml
@@ -7,10 +7,11 @@
 
     <integer name="number_of_top_sites">6</integer>
     <integer name="number_of_top_sites_cols">2</integer>
     <integer name="max_icon_grid_columns">4</integer>
     <integer name="panel_icon_grid_view_columns">3</integer>
     <integer name="number_of_inline_share_devices">2</integer>
     <integer name="max_search_suggestions">2</integer>
     <integer name="max_saved_suggestions">2</integer>
+    <integer name="search_assist_launch_res">0</integer>
 
 </resources>
--- a/mobile/android/base/sync/CommandProcessor.java
+++ b/mobile/android/base/sync/CommandProcessor.java
@@ -251,11 +251,12 @@ public class CommandProcessor {
     if (args.size() == 3) {
       title = args.get(2);
     }
 
     final Intent sendTabNotificationIntent = new Intent();
     sendTabNotificationIntent.setClassName(context, BrowserContract.TAB_RECEIVED_SERVICE_CLASS_NAME);
     sendTabNotificationIntent.setData(Uri.parse(uri));
     sendTabNotificationIntent.putExtra(Intent.EXTRA_TITLE, title);
+    sendTabNotificationIntent.putExtra(BrowserContract.EXTRA_CLIENT_GUID, clientId);
     final ComponentName componentName = context.startService(sendTabNotificationIntent);
   }
 }
--- a/mobile/android/base/tabqueue/TabReceivedService.java
+++ b/mobile/android/base/tabqueue/TabReceivedService.java
@@ -1,26 +1,31 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.tabqueue;
 
+import org.mozilla.gecko.AppConstants;
+import org.mozilla.gecko.BrowserLocaleManager;
+import org.mozilla.gecko.GeckoSharedPrefs;
+import org.mozilla.gecko.R;
+import org.mozilla.gecko.db.BrowserContract;
+
 import android.app.IntentService;
 import android.app.PendingIntent;
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.content.res.Resources;
+import android.database.Cursor;
+import android.support.annotation.Nullable;
+import android.support.annotation.WorkerThread;
 import android.support.v4.app.NotificationCompat;
 import android.support.v4.app.NotificationManagerCompat;
 import android.util.Log;
-import org.mozilla.gecko.BrowserLocaleManager;
-import org.mozilla.gecko.GeckoSharedPrefs;
-import org.mozilla.gecko.R;
-import org.mozilla.gecko.db.BrowserContract;
 
 /**
  * An IntentService that displays a notification for a tab sent to this device.
  *
  * The expected Intent should contain:
  *   * Data: URI to open in the notification
  *   * EXTRA_TITLE: Page title of the URI to open
  */
@@ -44,26 +49,21 @@ public class TabReceivedService extends 
         BrowserLocaleManager.getInstance().correctLocale(this, res, res.getConfiguration());
 
         final String uri = intent.getDataString();
         if (uri == null) {
             Log.d(LOGTAG, "Received null uri – ignoring");
             return;
         }
 
-        final String title = intent.getStringExtra(Intent.EXTRA_TITLE);
-        String notificationTitle = this.getString(R.string.sync_new_tab);
-        if (title != null) {
-            notificationTitle = notificationTitle.concat(": " + title);
-        }
-
         final Intent notificationIntent = new Intent(Intent.ACTION_VIEW, intent.getData());
         notificationIntent.putExtra(BrowserContract.SKIP_TAB_QUEUE_FLAG, true);
         final PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
 
+        final String notificationTitle = getNotificationTitle(intent.getStringExtra(BrowserContract.EXTRA_CLIENT_GUID));
         final NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
         builder.setSmallIcon(R.drawable.flat_icon);
         builder.setContentTitle(notificationTitle);
         builder.setWhen(System.currentTimeMillis());
         builder.setAutoCancel(true);
         builder.setContentText(uri);
         builder.setContentIntent(contentIntent);
 
@@ -75,16 +75,44 @@ public class TabReceivedService extends 
         // Save the ID last so if the Service is killed and the Intent is redeli