Merge m-c to b2g-inbound.
authorRyan VanderMeulen <ryanvm@gmail.com>
Thu, 17 Apr 2014 22:37:10 -0400
changeset 179635 c75f7df6645b9f55613786e789f344f29307ea86
parent 179634 c58b051bff0d88c77f4d4995241211618c96fefa (current diff)
parent 179608 7fe3ee0cf8be3f598d23d610618b1fee976a8fa7 (diff)
child 179636 7e19e4965f99f921480a79eaaf5303dad5674d82
push id272
push userpvanderbeken@mozilla.com
push dateMon, 05 May 2014 16:31:18 +0000
milestone31.0a1
Merge m-c to b2g-inbound.
b2g/config/tooltool-manifests/ics.manifest
b2g/config/tooltool-manifests/releng.manifest
content/canvas/public/nsICanvasElementExternal.h
gfx/layers/composite/APZCTreeManager.cpp
gfx/layers/composite/APZCTreeManager.h
gfx/layers/ipc/AsyncPanZoomController.cpp
gfx/layers/ipc/AsyncPanZoomController.h
gfx/layers/ipc/Axis.cpp
gfx/layers/ipc/Axis.h
gfx/layers/ipc/GeckoContentController.h
gfx/layers/ipc/GestureEventListener.cpp
gfx/layers/ipc/GestureEventListener.h
gfx/layers/ipc/TaskThrottler.cpp
gfx/layers/ipc/TaskThrottler.h
layout/generic/crashtests/455407.html
mobile/android/base/fxa/activities/FxAccountCreateAccountFragment.java
mobile/android/base/resources/drawable/url_bar_right_edge.xml
mobile/android/base/sync/PrefsSource.java
widget/xpwidgets/APZCCallbackHelper.cpp
widget/xpwidgets/APZCCallbackHelper.h
widget/xpwidgets/ActiveElementManager.cpp
widget/xpwidgets/ActiveElementManager.h
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Bug 916012 moves definition from one WEBIDL_FILE to another (Bug 979886)
+Bug 995411 moves some files around in gfx/layers and widget/xpwidget
--- a/addon-sdk/source/lib/sdk/addon/installer.js
+++ b/addon-sdk/source/lib/sdk/addon/installer.js
@@ -60,18 +60,22 @@ exports.install = function install(xpiPa
     },
     onDownloadFailed: function(aInstall) {
       this.onInstallFailed(aInstall);
     }
   };
 
   // Order AddonManager to install the addon
   AddonManager.getInstallForFile(file, function(install) {
-    install.addListener(listener);
-    install.install();
+    if (install.error != null) {
+      install.addListener(listener);
+      install.install();
+    } else {
+      reject(install.error);
+    }
   });
 
   return promise;
 };
 
 exports.uninstall = function uninstall(addonId) {
   let { promise, resolve, reject } = defer();
 
--- a/addon-sdk/source/lib/sdk/content/content-worker.js
+++ b/addon-sdk/source/lib/sdk/content/content-worker.js
@@ -281,27 +281,16 @@ const ContentWorker = Object.freeze({
       on: pipe.on.bind(null),
       once: pipe.once.bind(null),
       removeListener: pipe.removeListener.bind(null),
     };
     Object.defineProperty(exports, "self", {
       value: self
     });
 
-    // Deprecated use of on/postMessage from globals
-    exports.postMessage = function deprecatedPostMessage() {
-      console.error("DEPRECATED: The global `postMessage()` function in " +
-                    "content scripts is deprecated in favor of the " +
-                    "`self.postMessage()` function, which works the same. " +
-                    "Replace calls to `postMessage()` with calls to " +
-                    "`self.postMessage()`." +
-                    "For more info on `self.on`, see " +
-                    "<https://addons.mozilla.org/en-US/developers/docs/sdk/latest/dev-guide/addon-development/web-content.html>.");
-      return self.postMessage.apply(null, arguments);
-    };
     exports.on = function deprecatedOn() {
       console.error("DEPRECATED: The global `on()` function in content " +
                     "scripts is deprecated in favor of the `self.on()` " +
                     "function, which works the same. Replace calls to `on()` " +
                     "with calls to `self.on()`" +
                     "For more info on `self.on`, see " +
                     "<https://addons.mozilla.org/en-US/developers/docs/sdk/latest/dev-guide/addon-development/web-content.html>.");
       return self.on.apply(null, arguments);
--- a/addon-sdk/source/lib/sdk/l10n/html.js
+++ b/addon-sdk/source/lib/sdk/l10n/html.js
@@ -41,19 +41,21 @@ function onDocumentReady2Translate(event
   let document = event.target;
   document.removeEventListener("DOMContentLoaded", onDocumentReady2Translate,
                                false);
 
   translateElement(document);
 
   try {
     // Finally display document when we finished replacing all text content
-    let winUtils = document.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
-                                       .getInterface(Ci.nsIDOMWindowUtils);
-    winUtils.removeSheet(hideSheetUri, winUtils.USER_SHEET);
+    if (document.defaultView) {
+      let winUtils = document.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
+                                         .getInterface(Ci.nsIDOMWindowUtils);
+      winUtils.removeSheet(hideSheetUri, winUtils.USER_SHEET);
+    }
   }
   catch(e) {
     console.exception(e);
   }
 }
 
 function onContentWindow(event) {
   let document = event.subject;
--- a/addon-sdk/source/lib/sdk/panel/utils.js
+++ b/addon-sdk/source/lib/sdk/panel/utils.js
@@ -218,17 +218,16 @@ function show(panel, options, anchor) {
 
   open(panel, options, anchor);
 }
 exports.show = show
 
 function setupPanelFrame(frame) {
   frame.setAttribute("flex", 1);
   frame.setAttribute("transparent", "transparent");
-  frame.setAttribute("showcaret", true);
   frame.setAttribute("autocompleteenabled", true);
   if (platform === "darwin") {
     frame.style.borderRadius = "6px";
     frame.style.padding = "1px";
   }
 }
 
 function make(document) {
--- a/addon-sdk/source/test/test-content-script.js
+++ b/addon-sdk/source/test/test-content-script.js
@@ -186,19 +186,16 @@ exports["test postMessage"] = createProx
     assert.equal(event.data, "{\"foo\":\"bar\\n \\\"escaped\\\".\"}",
                      "message data is correct");
 
     helper.done();
   }, false);
 
   helper.createWorker(
     'new ' + function ContentScriptScope() {
-      assert(postMessage === postMessage,
-          "verify that we doesn't generate multiple functions for the same method");
-
       var json = JSON.stringify({foo : "bar\n \"escaped\"."});
 
       document.getElementById("iframe").contentWindow.postMessage(json, "*");
     }
   );
 });
 
 let html = '<input id="input2" type="checkbox" />';
--- a/addon-sdk/source/test/test-content-worker.js
+++ b/addon-sdk/source/test/test-content-worker.js
@@ -21,17 +21,23 @@ const { set: setPref } = require("sdk/pr
 const { isArray } = require("sdk/lang/type");
 const { URL } = require('sdk/url');
 const fixtures = require("./fixtures");
 
 const DEPRECATE_PREF = "devtools.errorconsole.deprecation_warnings";
 
 const DEFAULT_CONTENT_URL = "data:text/html;charset=utf-8,foo";
 
-function makeWindow(contentURL) {
+const WINDOW_SCRIPT_URL = "data:text/html;charset=utf-8," +
+                          "<script>window.addEventListener('message', function (e) {" +
+                          "  if (e.data === 'from -> content-script')" +
+                          "    window.postMessage('from -> window', '*');" +
+                          "});</script>";
+
+function makeWindow() {
   let content =
     "<?xml version=\"1.0\"?>" +
     "<window " +
     "xmlns=\"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul\">" +
     "<script>var documentValue=true;</script>" +
     "</window>";
   var url = "data:application/vnd.mozilla.xul+xml;charset=utf-8," +
             encodeURIComponent(content);
@@ -777,60 +783,16 @@ exports["test:check worker API with page
         }, 500);
 
       }, false);
     });
 
   }
 );
 
-exports["test:global postMessage"] = WorkerTest(
-  DEFAULT_CONTENT_URL,
-  function(assert, browser, done) {
-    let { loader } = LoaderWithHookedConsole(module, onMessage);
-    setPref(DEPRECATE_PREF, true);
-
-    // Intercept all console method calls
-    let seenMessages = 0;
-    function onMessage(type, message) {
-      seenMessages++;
-      assert.equal(type, "error", "Should be an error");
-      assert.equal(message, "DEPRECATED: The global `postMessage()` function in " +
-                            "content scripts is deprecated in favor of the " +
-                            "`self.postMessage()` function, which works the same. " +
-                            "Replace calls to `postMessage()` with calls to " +
-                            "`self.postMessage()`." +
-                            "For more info on `self.on`, see " +
-                            "<https://addons.mozilla.org/en-US/developers/docs/sdk/latest/dev-guide/addon-development/web-content.html>.",
-                            "Should have seen the deprecation message")
-    }
-
-    assert.notEqual(browser.contentWindow.location.href, "about:blank",
-                        "window is now on the right document");
-
-    let window = browser.contentWindow
-    let worker = loader.require("sdk/content/worker").Worker({
-      window: window,
-      contentScript: "new " + function WorkerScope() {
-        postMessage("success");
-      },
-      contentScriptWhen: "ready",
-      onMessage: function(msg) {
-        assert.equal("success", msg, "Should have seen the right postMessage call");
-        assert.equal(1, seenMessages, "Should have seen the deprecation message");
-        done();
-      }
-    });
-
-    assert.equal(worker.url, window.location.href,
-                     "worker.url works");
-    worker.postMessage("hi!");
-  }
-);
-
 exports['test:conentScriptFile as URL instance'] = WorkerTest(
   DEFAULT_CONTENT_URL,
   function(assert, browser, done) {
 
     let url = new URL(fixtures.url("test-contentScriptFile.js"));
     let worker =  Worker({
       window: browser.contentWindow,
       contentScriptFile: url,
@@ -884,18 +846,18 @@ exports["test:onDetach in contentScript 
     let worker = Worker({
       window: browser.contentWindow,
       contentScript: 'new ' + function WorkerScope() {
         self.port.on('detach', function(reason) {
           window.location.hash += '!' + reason;
         })
       },
     });
-    browser.contentWindow.addEventListener('hashchange', _ => { 
-      assert.equal(browser.contentWindow.location.hash, '#detach!', 
+    browser.contentWindow.addEventListener('hashchange', _ => {
+      assert.equal(browser.contentWindow.location.hash, '#detach!',
                    "location.href is as expected");
       done();
     })
     worker.destroy();
   }
 );
 
 exports["test:onDetach in contentScript on unload"] = WorkerTest(
@@ -905,18 +867,18 @@ exports["test:onDetach in contentScript 
     let worker = loader.require("sdk/content/worker").Worker({
       window: browser.contentWindow,
       contentScript: 'new ' + function WorkerScope() {
         self.port.on('detach', function(reason) {
           window.location.hash += '!' + reason;
         })
       },
     });
-    browser.contentWindow.addEventListener('hashchange', _ => { 
-      assert.equal(browser.contentWindow.location.hash, '#detach!shutdown', 
+    browser.contentWindow.addEventListener('hashchange', _ => {
+      assert.equal(browser.contentWindow.location.hash, '#detach!shutdown',
                    "location.href is as expected");
       done();
     })
     loader.unload('shutdown');
   }
 );
 
 exports["test:console method log functions properly"] = WorkerTest(
@@ -949,9 +911,30 @@ exports["test:console method log functio
         ]);
 
         done();
       }
     });
   }
 );
 
+exports["test:global postMessage"] = WorkerTest(
+  WINDOW_SCRIPT_URL,
+  function(assert, browser, done) {
+    let contentScript = "window.addEventListener('message', function (e) {" +
+                        "  if (e.data === 'from -> window')" +
+                        "    self.port.emit('response', e.data, e.origin);" +
+                        "});" +
+                        "postMessage('from -> content-script', '*');";
+    let { loader } = LoaderWithHookedConsole(module);
+    let worker =  loader.require("sdk/content/worker").Worker({
+      window: browser.contentWindow,
+      contentScriptWhen: "ready",
+      contentScript: contentScript
+    });
+
+    worker.port.on("response", (data, origin) => {
+      assert.equal(data, "from -> window", "Communication from content-script to window completed");
+      done();
+    });
+});
+
 require("test").run(exports);
--- a/addon-sdk/source/test/test-page-worker.js
+++ b/addon-sdk/source/test/test-page-worker.js
@@ -1,17 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
 "use strict";
 
 const { Loader } = require('sdk/test/loader');
-const Pages = require("sdk/page-worker");
-const Page = Pages.Page;
+const { Page } = require("sdk/page-worker");
 const { URL } = require("sdk/url");
 const fixtures = require("./fixtures");
 const testURI = fixtures.url("test.html");
 
 const ERR_DESTROYED =
   "Couldn't find the worker to receive this message. " +
   "The script may not be initialized yet, or may already have been unloaded.";
 
@@ -88,27 +86,27 @@ exports.testPageProperties = function(as
   }
 
   assert.ok(function () page.postMessage("foo") || true,
               "postMessage doesn't throw exception on page.");
 }
 
 exports.testConstructorAndDestructor = function(assert, done) {
   let loader = Loader(module);
-  let Pages = loader.require("sdk/page-worker");
+  let { Page } = loader.require("sdk/page-worker");
   let global = loader.sandbox("sdk/page-worker");
 
   let pagesReady = 0;
 
-  let page1 = Pages.Page({
+  let page1 = Page({
     contentScript:      "self.postMessage('')",
     contentScriptWhen:  "end",
     onMessage:          pageReady
   });
-  let page2 = Pages.Page({
+  let page2 = Page({
     contentScript:      "self.postMessage('')",
     contentScriptWhen:  "end",
     onMessage:          pageReady
   });
 
   assert.notEqual(page1, page2,
                       "Page 1 and page 2 should be different objects.");
 
@@ -123,19 +121,19 @@ exports.testConstructorAndDestructor = f
       loader.unload();
       done();
     }
   }
 }
 
 exports.testAutoDestructor = function(assert, done) {
   let loader = Loader(module);
-  let Pages = loader.require("sdk/page-worker");
+  let { Page } = loader.require("sdk/page-worker");
 
-  let page = Pages.Page({
+  let page = Page({
     contentScript: "self.postMessage('')",
     contentScriptWhen: "end",
     onMessage: function() {
       loader.unload();
       assert.ok(isDestroyed(page), "Page correctly unloaded.");
       done();
     }
   });
@@ -311,22 +309,22 @@ exports.testPingPong = function(assert, 
       }
     }
   });
 };
 
 exports.testRedirect = function (assert, done) {
   let page = Page({
     contentURL: 'data:text/html;charset=utf-8,first-page',
-    contentScript: '(function () {' +
+    contentScriptWhen: "end",
+    contentScript: '' +
       'if (/first-page/.test(document.location.href)) ' +
       '  document.location.href = "data:text/html;charset=utf-8,redirect";' +
       'else ' +
-      '  self.port.emit("redirect", document.location.href);' +
-      '})();'
+      '  self.port.emit("redirect", document.location.href);'
   });
 
   page.port.on('redirect', function (url) {
     assert.equal(url, 'data:text/html;charset=utf-8,redirect', 'Reinjects contentScript on reload');
     done();
   });
 };
 
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -1162,19 +1162,19 @@ let RemoteDebugger = {
           } : DebuggerServer.globalActorFactories
         };
         let root = new DebuggerServer.RootActor(connection, parameters);
         root.applicationType = "operating-system";
         return root;
       };
 
 #ifdef MOZ_WIDGET_GONK
-      DebuggerServer.onConnectionChange = function(what) {
+      DebuggerServer.on("connectionchange", function() {
         AdbController.updateState();
-      }
+      });
 #endif
     }
 
     let path = Services.prefs.getCharPref("devtools.debugger.unix-domain-socket") ||
                "/data/local/debugger-socket";
     try {
       DebuggerServer.openListener(path);
       // Temporary event, until bug 942756 lands and offers a way to know
--- a/b2g/config/mozconfigs/common
+++ b/b2g/config/mozconfigs/common
@@ -1,11 +1,12 @@
 # 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/.
 
 no_tooltool=1
+no_sccache=1
 
 # This file is included at the top of all b2g mozconfigs
 
 . "$topsrcdir/build/mozconfig.common"
 
 ac_add_options --disable-unified-compilation
--- a/b2g/config/mozconfigs/linux32_gecko/nightly
+++ b/b2g/config/mozconfigs/linux32_gecko/nightly
@@ -16,17 +16,18 @@ STRIP_FLAGS="--strip-debug"
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 export MOZ_TELEMETRY_REPORTING=1
 
 # Treat warnings as errors in directories with FAIL_ON_WARNINGS.
 # DISABLED WHILE NOT ON TRY ac_add_options --enable-warnings-as-errors
 
-# Use ccache
+# Use sccache
+no_sccache=
 . "$topsrcdir/build/mozconfig.cache"
 
 #B2G options
 ac_add_options --enable-application=b2g
 ac_add_options --disable-elf-hack
 export CXXFLAGS=-DMOZ_ENABLE_JS_DUMP
 
 GAIADIR=$topsrcdir/gaia
--- a/b2g/config/mozconfigs/linux64_gecko/debug
+++ b/b2g/config/mozconfigs/linux64_gecko/debug
@@ -17,17 +17,18 @@ STRIP_FLAGS="--strip-debug"
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 export MOZ_TELEMETRY_REPORTING=1
 
 # Treat warnings as errors in directories with FAIL_ON_WARNINGS.
 # DISABLED WHILE NOT ON TRY ac_add_options --enable-warnings-as-errors
 
-# Use ccache
+# Use sccache
+no_sccache=
 . "$topsrcdir/build/mozconfig.cache"
 
 #B2G options
 ac_add_options --enable-application=b2g
 ENABLE_MARIONETTE=1
 ac_add_options --disable-elf-hack
 export CXXFLAGS=-DMOZ_ENABLE_JS_DUMP
 
--- a/b2g/config/mozconfigs/linux64_gecko/nightly
+++ b/b2g/config/mozconfigs/linux64_gecko/nightly
@@ -16,17 +16,18 @@ STRIP_FLAGS="--strip-debug"
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 export MOZ_TELEMETRY_REPORTING=1
 
 # Treat warnings as errors in directories with FAIL_ON_WARNINGS.
 # DISABLED WHILE NOT ON TRY ac_add_options --enable-warnings-as-errors
 
-# Use ccache
+# Use sccache
+no_sccache=
 . "$topsrcdir/build/mozconfig.cache"
 
 #B2G options
 ac_add_options --enable-application=b2g
 ac_add_options --disable-elf-hack
 export CXXFLAGS=-DMOZ_ENABLE_JS_DUMP
 
 GAIADIR=$topsrcdir/gaia
deleted file mode 100644
--- a/b2g/config/tooltool-manifests/ics.manifest
+++ /dev/null
@@ -1,14 +0,0 @@
-[
-{
-"size": 195, 
-"digest": "236362c71c433971c36b46d34e8560342435718364bc390df8de6a33249fb1fbf4fc3d0143f1e22bca262a7af7dc1b277a920bfde3ee8197eb07db2e7cef3e1f", 
-"algorithm": "sha512", 
-"filename": "setup.sh"
-}, 
-{
-"size": 63159127, 
-"digest": "fcf629c815b5cbed7858d7697815f355275dcc6b060ae5455b4b31fde0d78ebc176927564a5353ceacdb9f9c9bfc1357f1341bf6ba844c25153a89664e661510", 
-"algorithm": "sha512", 
-"filename": "gonk-toolchain-7.tar.bz2"
-}
-]
new file mode 100644
--- /dev/null
+++ b/b2g/config/tooltool-manifests/linux32/releng.manifest
@@ -0,0 +1,14 @@
+[
+{
+"size": 50,
+"digest": "48f405d8c2712838b9dd3be118951c8b41c63c891576f5287d2e05afa8fd051a08807511259581aa3170a3c4f7d4e77e6c0539b00b8f6845f01562127f6a27fa",
+"algorithm": "sha512",
+"filename": "setup.sh"
+},
+{
+"size": 160232,
+"digest": "8656c3fc2daa66839ec81a0edbd9759040a83c7a41c3e472d7f90508b80eefd008b87305dc8549b4ff6098dc33fe17fedc9b4eb76cf5307d5f22dae925c033db",
+"algorithm": "sha512",
+"filename": "sccache.tar.xz"
+}
+]
new file mode 100644
--- /dev/null
+++ b/b2g/config/tooltool-manifests/linux64/releng.manifest
@@ -0,0 +1,14 @@
+[
+{
+"size": 50,
+"digest": "48f405d8c2712838b9dd3be118951c8b41c63c891576f5287d2e05afa8fd051a08807511259581aa3170a3c4f7d4e77e6c0539b00b8f6845f01562127f6a27fa",
+"algorithm": "sha512",
+"filename": "setup.sh"
+},
+{
+"size": 160232,
+"digest": "8656c3fc2daa66839ec81a0edbd9759040a83c7a41c3e472d7f90508b80eefd008b87305dc8549b4ff6098dc33fe17fedc9b4eb76cf5307d5f22dae925c033db",
+"algorithm": "sha512",
+"filename": "sccache.tar.xz"
+}
+]
deleted file mode 100644
--- a/b2g/config/tooltool-manifests/releng.manifest
+++ /dev/null
@@ -1,14 +0,0 @@
-[
-{
-"size": 195,
-"digest": "da2edcb1ec9b169f6c685d02ebd0bd4ad53ace2df58598f15e1bde43dd74bcb816620601587c97e636bda3327045b43c8f5e973672ebd904e06036f70466908f",
-"algorithm": "sha512",
-"filename": "setup.sh"
-},
-{
-"size": 121166734,
-"digest": "10da1d28d49ff1aa9ad3d84e52235dc8ed7df6721530b896a53424480ec23a2e9f28cadd631f562342325611ecb72152be51f9b62616356f33005f6cc0fb82fe",
-"algorithm": "sha512",
-"filename": "gonk-toolchain-3.tar.bz2"
-}
-]
--- a/browser/base/content/browser-fxaccounts.js
+++ b/browser/base/content/browser-fxaccounts.js
@@ -2,16 +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/.
 
 XPCOMUtils.defineLazyGetter(this, "FxAccountsCommon", function () {
   return Cu.import("resource://gre/modules/FxAccountsCommon.js", {});
 });
 
 const PREF_SYNC_START_DOORHANGER = "services.sync.ui.showSyncStartDoorhanger";
+const DOORHANGER_ACTIVATE_DELAY_MS = 5000;
 
 let gFxAccounts = {
 
   _initialized: false,
   _inCustomizationMode: false,
 
   get weave() {
     delete this.weave;
@@ -28,26 +29,16 @@ let gFxAccounts = {
       "weave:service:sync:start",
       "weave:service:login:error",
       "weave:service:setup-complete",
       FxAccountsCommon.ONVERIFIED_NOTIFICATION,
       FxAccountsCommon.ONLOGOUT_NOTIFICATION
     ];
   },
 
-  // The set of topics that only the active window should handle.
-  get activeWindowTopics() {
-    // Do all this dance to lazy-load FxAccountsCommon.
-    delete this.activeWindowTopics;
-    return this.activeWindowTopics = new Set([
-      "weave:service:sync:start",
-      FxAccountsCommon.ONVERIFIED_NOTIFICATION
-    ]);
-  },
-
   get button() {
     delete this.button;
     return this.button = document.getElementById("PanelUI-fxa-status");
   },
 
   get loginFailed() {
     // Referencing Weave.Service will implicitly initialize sync, and we don't
     // want to force that - so first check if it is ready.
@@ -59,31 +50,31 @@ let gFxAccounts = {
     }
     // LOGIN_FAILED_LOGIN_REJECTED explicitly means "you must log back in".
     // All other login failures are assumed to be transient and should go
     // away by themselves, so aren't reflected here.
     return Weave.Status.login == Weave.LOGIN_FAILED_LOGIN_REJECTED;
   },
 
   get isActiveWindow() {
-    let mostRecentNonPopupWindow =
-      RecentWindow.getMostRecentBrowserWindow({allowPopups: false});
-    return window == mostRecentNonPopupWindow;
+    let fm = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
+    return fm.activeWindow == window;
   },
 
   init: function () {
     // Bail out if we're already initialized and for pop-up windows.
     if (this._initialized || !window.toolbar.visible) {
       return;
     }
 
     for (let topic of this.topics) {
       Services.obs.addObserver(this, topic, false);
     }
 
+    addEventListener("activate", this);
     gNavToolbox.addEventListener("customizationstarting", this);
     gNavToolbox.addEventListener("customizationending", this);
 
     this._initialized = true;
 
     this.updateUI();
   },
 
@@ -95,50 +86,58 @@ let gFxAccounts = {
     for (let topic of this.topics) {
       Services.obs.removeObserver(this, topic);
     }
 
     this._initialized = false;
   },
 
   observe: function (subject, topic) {
-    // Ignore certain topics if we're not the active window.
-    if (this.activeWindowTopics.has(topic) && !this.isActiveWindow) {
-      return;
-    }
-
     switch (topic) {
       case FxAccountsCommon.ONVERIFIED_NOTIFICATION:
         Services.prefs.setBoolPref(PREF_SYNC_START_DOORHANGER, true);
         break;
       case "weave:service:sync:start":
         this.onSyncStart();
         break;
       default:
         this.updateUI();
         break;
     }
   },
 
   onSyncStart: function () {
+    if (!this.isActiveWindow) {
+      return;
+    }
+
     let showDoorhanger = false;
 
     try {
       showDoorhanger = Services.prefs.getBoolPref(PREF_SYNC_START_DOORHANGER);
     } catch (e) { /* The pref might not exist. */ }
 
     if (showDoorhanger) {
       Services.prefs.clearUserPref(PREF_SYNC_START_DOORHANGER);
       this.showSyncStartedDoorhanger();
     }
   },
 
   handleEvent: function (event) {
-    this._inCustomizationMode = event.type == "customizationstarting";
-    this.updateUI();
+    if (event.type == "activate") {
+      // Our window might have been in the background while we received the
+      // sync:start notification. If still needed, show the doorhanger after
+      // a short delay. Without this delay the doorhanger would not show up
+      // or with a too small delay show up while we're still animating the
+      // window.
+      setTimeout(() => this.onSyncStart(), DOORHANGER_ACTIVATE_DELAY_MS);
+    } else {
+      this._inCustomizationMode = event.type == "customizationstarting";
+      this.updateUI();
+    }
   },
 
   showDoorhanger: function (id) {
     let panel = document.getElementById(id);
     let anchor = document.getElementById("PanelUI-menu-button");
 
     let iconAnchor =
       document.getAnonymousElementByAttribute(anchor, "class",
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -6994,20 +6994,16 @@ function restoreLastSession() {
 
 var TabContextMenu = {
   contextTab: null,
   updateContextMenu: function updateContextMenu(aPopupMenu) {
     this.contextTab = aPopupMenu.triggerNode.localName == "tab" ?
                       aPopupMenu.triggerNode : gBrowser.selectedTab;
     let disabled = gBrowser.tabs.length == 1;
 
-    // Enable the "Close Tab" menuitem when the window doesn't close with the last tab.
-    document.getElementById("context_closeTab").disabled =
-      disabled && gBrowser.tabContainer._closeWindowWithLastTab;
-
     var menuItems = aPopupMenu.getElementsByAttribute("tbattr", "tabbrowser-multiple");
     for (let menuItem of menuItems)
       menuItem.disabled = disabled;
 
     disabled = gBrowser.visibleTabs.length == 1;
     menuItems = aPopupMenu.getElementsByAttribute("tbattr", "tabbrowser-multiple-visible");
     for (let menuItem of menuItems)
       menuItem.disabled = disabled;
--- a/browser/base/content/test/general/browser_bug435325.js
+++ b/browser/base/content/test/general/browser_bug435325.js
@@ -46,23 +46,27 @@ function test() {
 function checkPage() {
   ok(Services.io.offline, "Setting Services.io.offline to true.");
   is(gBrowser.contentDocument.documentURI.substring(0,27),
     "about:neterror?e=netOffline", "Loading the Offline mode neterror page.");
 
   // Now press the "Try Again" button
   ok(gBrowser.contentDocument.getElementById("errorTryAgain"),
     "The error page has got a #errorTryAgain element");
+
+  // Re-enable the proxy so example.com is resolved to localhost, rather than
+  // the actual example.com.
+  Services.prefs.setIntPref("network.proxy.type", proxyPrefValue);
+
   gBrowser.contentDocument.getElementById("errorTryAgain").click();
 
   ok(!Services.io.offline, "After clicking the Try Again button, we're back " +
                            "online.");
 
   finish();
 }
 
 registerCleanupFunction(function() {
-  Services.prefs.setIntPref("network.proxy.type", proxyPrefValue);
   Services.prefs.setBoolPref("browser.cache.disk.enable", true);
   Services.prefs.setBoolPref("browser.cache.memory.enable", true);
   Services.io.offline = false;
   gBrowser.removeCurrentTab();
 });
--- a/browser/base/content/test/general/browser_devices_get_user_media.js
+++ b/browser/base/content/test/general/browser_devices_get_user_media.js
@@ -1,8 +1,12 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
 const kObservedTopics = [
   "getUserMedia:response:allow",
   "getUserMedia:revoke",
   "getUserMedia:response:deny",
   "getUserMedia:request",
   "recording-device-events",
   "recording-window-ended"
 ];
@@ -731,16 +735,60 @@ let gTests = [
 
     info("request audio+video, stop sharing resets both");
     yield stopAndCheckPerm(true, true);
     info("request audio, stop sharing resets audio only");
     yield stopAndCheckPerm(true, false);
     info("request video, stop sharing resets video only");
     yield stopAndCheckPerm(false, true);
   }
+},
+
+{
+  desc: "'Always Allow' ignored and not shown on http pages",
+  run: function checkNoAlwaysOnHttp() {
+    // Load an http page instead of the https version.
+    let deferred = Promise.defer();
+    let browser = gBrowser.selectedTab.linkedBrowser;
+    browser.addEventListener("load", function onload() {
+      browser.removeEventListener("load", onload, true);
+      deferred.resolve();
+    }, true);
+    content.location = content.location.href.replace("https://", "http://");
+    yield deferred.promise;
+
+    // Initially set both permissions to 'allow'.
+    let Perms = Services.perms;
+    let uri = content.document.documentURIObject;
+    Perms.add(uri, "microphone", Perms.ALLOW_ACTION);
+    Perms.add(uri, "camera", Perms.ALLOW_ACTION);
+
+    // Request devices and expect a prompt despite the saved 'Allow' permission,
+    // because the connection isn't secure.
+    yield promisePopupNotificationShown("webRTC-shareDevices", () => {
+      content.wrappedJSObject.requestDevice(true, true);
+    });
+    expectObserverCalled("getUserMedia:request");
+
+    // Ensure that the 'Always Allow' action isn't shown.
+    let alwaysLabel = gNavigatorBundle.getString("getUserMedia.always.label");
+    ok(!!alwaysLabel, "found the 'Always Allow' localized label");
+    let labels = [];
+    let notification = PopupNotifications.panel.firstChild;
+    for (let node of notification.childNodes) {
+      if (node.localName == "menuitem")
+        labels.push(node.getAttribute("label"));
+    }
+    is(labels.indexOf(alwaysLabel), -1, "The 'Always Allow' item isn't shown");
+
+    // Cleanup.
+    yield closeStream(true);
+    Perms.remove(uri.host, "camera");
+    Perms.remove(uri.host, "microphone");
+  }
 }
 
 ];
 
 function test() {
   waitForExplicitFinish();
 
   let tab = gBrowser.addTab();
--- a/browser/components/preferences/in-content/sync.js
+++ b/browser/components/preferences/in-content/sync.js
@@ -158,18 +158,18 @@ let gSyncPane = {
         let checkbox = document.getElementById("fxa-pweng-chk");
         let help = document.getElementById("fxa-pweng-help");
         let allowPasswordsEngine = service.allowPasswordsEngine;
 
         if (!allowPasswordsEngine) {
           checkbox.checked = false;
         }
 
-        checkbox.disabled = !allowPasswordsEngine;
-        help.hidden = allowPasswordsEngine;
+        checkbox.disabled = !allowPasswordsEngine || enginesListDisabled;
+        help.hidden = allowPasswordsEngine || enginesListDisabled;
       });
     // If fxAccountEnabled is false and we are in a "not configured" state,
     // then fxAccounts is probably fully disabled rather than just unconfigured,
     // so handle this case.  This block can be removed once we remove support
     // for fxAccounts being disabled.
     } else if (Weave.Status.service == Weave.CLIENT_NOT_CONFIGURED ||
                Weave.Svc.Prefs.get("firstSync", "") == "notReady") {
       this.page = PAGE_NO_ACCOUNT;
--- a/browser/components/preferences/sync.js
+++ b/browser/components/preferences/sync.js
@@ -158,18 +158,18 @@ let gSyncPane = {
         let checkbox = document.getElementById("fxa-pweng-chk");
         let help = document.getElementById("fxa-pweng-help");
         let allowPasswordsEngine = service.allowPasswordsEngine;
 
         if (!allowPasswordsEngine) {
           checkbox.checked = false;
         }
 
-        checkbox.disabled = !allowPasswordsEngine;
-        help.hidden = allowPasswordsEngine;
+        checkbox.disabled = !allowPasswordsEngine || enginesListDisabled;
+        help.hidden = allowPasswordsEngine || enginesListDisabled;
       });
     // If fxAccountEnabled is false and we are in a "not configured" state,
     // then fxAccounts is probably fully disabled rather than just unconfigured,
     // so handle this case.  This block can be removed once we remove support
     // for fxAccounts being disabled.
     } else if (Weave.Status.service == Weave.CLIENT_NOT_CONFIGURED ||
                Weave.Svc.Prefs.get("firstSync", "") == "notReady") {
       this.page = PAGE_NO_ACCOUNT;
--- a/browser/components/tabview/test/browser_tabview_bug625195.js
+++ b/browser/components/tabview/test/browser_tabview_bug625195.js
@@ -2,17 +2,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 function test() {
   is(gBrowser.tabs.length, 1, "Only one tab exist");
 
   let originalTab = gBrowser.tabs[0];
 
   popup(originalTab);
-  ok(document.getElementById("context_closeTab").disabled, "The 'Close tab' menu item is disabled");
+  ok(!document.getElementById("context_closeTab").disabled, "The 'Close tab' menu item is enabled");
   ok(document.getElementById("context_openTabInWindow").disabled, "The 'Move to New Window' menu item is disabled");
 
   let newTabOne = gBrowser.addTab("about:blank", {skipAnimation: true});
 
   waitForExplicitFinish();
 
   showTabView(function() {
     registerCleanupFunction(function () {
--- a/browser/config/mozconfigs/linux32/l10n-mozconfig
+++ b/browser/config/mozconfigs/linux32/l10n-mozconfig
@@ -1,9 +1,10 @@
 no_tooltool=1
+no_sccache=1
 
 ac_add_options --with-l10n-base=../../l10n
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 ac_add_options --enable-update-packaging
 
 . $topsrcdir/build/unix/mozconfig.linux32
 
 export MOZILLA_OFFICIAL=1
--- a/browser/config/mozconfigs/linux32/valgrind
+++ b/browser/config/mozconfigs/linux32/valgrind
@@ -1,9 +1,10 @@
 no_tooltool=1
+no_sccache=1
 
 . $topsrcdir/browser/config/mozconfigs/linux32/nightly
 
 ac_add_options --enable-valgrind
 ac_add_options --disable-jemalloc
 ac_add_options --disable-elf-hack
 ac_add_options --enable-optimize="-g -O -freorder-blocks"
 ac_add_options --disable-install-strip
--- a/browser/config/mozconfigs/linux64/l10n-mozconfig
+++ b/browser/config/mozconfigs/linux64/l10n-mozconfig
@@ -1,9 +1,10 @@
 no_tooltool=1
+no_sccache=1
 
 ac_add_options --with-l10n-base=../../l10n
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 ac_add_options --enable-update-packaging
 
 . $topsrcdir/build/unix/mozconfig.linux
 
 export MOZILLA_OFFICIAL=1
--- a/browser/config/mozconfigs/linux64/valgrind
+++ b/browser/config/mozconfigs/linux64/valgrind
@@ -1,9 +1,10 @@
 no_tooltool=1
+no_sccache=1
 
 . $topsrcdir/browser/config/mozconfigs/linux64/nightly
 
 ac_add_options --enable-valgrind
 ac_add_options --disable-jemalloc
 ac_add_options --disable-elf-hack
 ac_add_options --enable-optimize="-g -O -freorder-blocks"
 ac_add_options --disable-install-strip
--- a/browser/devtools/framework/ToolboxProcess.jsm
+++ b/browser/devtools/framework/ToolboxProcess.jsm
@@ -11,59 +11,104 @@ const DBG_XUL = "chrome://browser/conten
 const CHROME_DEBUGGER_PROFILE_NAME = "-chrome-debugger";
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
 
 Cu.import("resource://gre/modules/devtools/Loader.jsm");
 let require = devtools.require;
 let Telemetry = require("devtools/shared/telemetry");
+let EventEmitter = require("devtools/toolkit/event-emitter");
+const { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
 
 this.EXPORTED_SYMBOLS = ["BrowserToolboxProcess"];
 
+let processes = Set();
+
 /**
  * Constructor for creating a process that will hold a chrome toolbox.
  *
  * @param function aOnClose [optional]
  *        A function called when the process stops running.
  * @param function aOnRun [optional]
  *        A function called when the process starts running.
  * @param object aOptions [optional]
  *        An object with properties for configuring BrowserToolboxProcess.
  */
 this.BrowserToolboxProcess = function BrowserToolboxProcess(aOnClose, aOnRun, aOptions) {
+  let emitter = new EventEmitter();
+  this.on = emitter.on.bind(emitter);
+  this.off = emitter.off.bind(emitter);
+  this.once = emitter.once.bind(emitter);
+  // Forward any events to the shared emitter.
+  this.emit = function(...args) {
+    emitter.emit(...args);
+    BrowserToolboxProcess.emit(...args);
+  }
+
   // If first argument is an object, use those properties instead of
   // all three arguments
   if (typeof aOnClose === "object") {
-    this._closeCallback = aOnClose.onClose;
-    this._runCallback = aOnClose.onRun;
+    if (aOnClose.onClose) {
+      this.on("close", aOnClose.onClose);
+    }
+    if (aOnClose.onRun) {
+      this.on("run", aOnClose.onRun);
+    }
     this._options = aOnClose;
   } else {
-    this._closeCallback = aOnClose;
-    this._runCallback = aOnRun;
+    if (aOnClose) {
+      this.on("close", aOnClose);
+    }
+    if (aOnRun) {
+      this.on("run", aOnRun);
+    }
     this._options = aOptions || {};
   }
 
   this._telemetry = new Telemetry();
 
   this.close = this.close.bind(this);
   Services.obs.addObserver(this.close, "quit-application", false);
   this._initServer();
   this._initProfile();
   this._create();
+
+  processes.add(this);
 };
 
+EventEmitter.decorate(BrowserToolboxProcess);
+
 /**
  * Initializes and starts a chrome toolbox process.
  * @return object
  */
 BrowserToolboxProcess.init = function(aOnClose, aOnRun, aOptions) {
   return new BrowserToolboxProcess(aOnClose, aOnRun, aOptions);
 };
 
+/**
+ * Passes a set of options to the BrowserAddonActors for the given ID.
+ *
+ * @param aId string
+ *        The ID of the add-on to pass the options to
+ * @param aOptions object
+ *        The options.
+ * @return a promise that will be resolved when complete.
+ */
+BrowserToolboxProcess.setAddonOptions = function DSC_setAddonOptions(aId, aOptions) {
+  let promises = [];
+
+  for (let process of processes.values()) {
+    promises.push(process.debuggerServer.setAddonOptions(aId, aOptions));
+  }
+
+  return promise.all(promises);
+};
+
 BrowserToolboxProcess.prototype = {
   /**
    * Initializes the debugger server.
    */
   _initServer: function() {
     dumpn("Initializing the chrome toolbox server.");
 
     if (!this.loader) {
@@ -72,16 +117,19 @@ BrowserToolboxProcess.prototype = {
       // This allows us to safely use the tools against even the actors and
       // DebuggingServer itself, especially since we can mark this loader as
       // invisible to the debugger (unlike the usual loader settings).
       this.loader = new DevToolsLoader();
       this.loader.invisibleToDebugger = true;
       this.loader.main("devtools/server/main");
       this.debuggerServer = this.loader.DebuggerServer;
       dumpn("Created a separate loader instance for the DebuggerServer.");
+
+      // Forward interesting events.
+      this.debuggerServer.on("connectionchange", this.emit.bind(this));
     }
 
     if (!this.debuggerServer.initialized) {
       this.debuggerServer.init();
       this.debuggerServer.addBrowserActors();
       dumpn("initialized and added the browser actors for the DebuggerServer.");
     }
 
@@ -164,19 +212,17 @@ BrowserToolboxProcess.prototype = {
     dumpn("Running chrome debugging process.");
     let args = ["-no-remote", "-foreground", "-P", this._dbgProfile.name, "-chrome", xulURI];
 
     process.runwAsync(args, args.length, { observe: () => this.close() });
 
     this._telemetry.toolOpened("jsbrowserdebugger");
 
     dumpn("Chrome toolbox is now running...");
-    if (typeof this._runCallback == "function") {
-      this._runCallback.call({}, this);
-    }
+    this.emit("run", this);
   },
 
   /**
    * Closes the remote debugging server and kills the toolbox process.
    */
   close: function() {
     if (this.closed) {
       return;
@@ -191,19 +237,18 @@ BrowserToolboxProcess.prototype = {
 
     this._telemetry.toolClosed("jsbrowserdebugger");
     if (this.debuggerServer) {
       this.debuggerServer.destroy();
     }
 
     dumpn("Chrome toolbox is now closed...");
     this.closed = true;
-    if (typeof this._closeCallback == "function") {
-      this._closeCallback.call({}, this);
-    }
+    this.emit("close", this);
+    processes.delete(this);
   }
 };
 
 /**
  * Shortcuts for accessing various debugger preferences.
  */
 let Prefs = new ViewHelpers.Prefs("devtools.debugger", {
   chromeDebuggingHost: ["Char", "chrome-debugging-host"],
--- a/browser/devtools/framework/connect/connect.js
+++ b/browser/devtools/framework/connect/connect.js
@@ -4,19 +4,21 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const Cu = Components.utils;
 Cu.import('resource://gre/modules/XPCOMUtils.jsm');
 Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource://gre/modules/devtools/dbg-client.jsm");
 let {gDevTools} = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
 let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+let {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
 
 let gClient;
 let gConnectionTimeout;
 
 XPCOMUtils.defineLazyGetter(window, 'l10n', function () {
   return Services.strings.createBundle('chrome://browser/locale/devtools/connection-screen.properties');
 });
 
@@ -70,69 +72,107 @@ function submit() {
   let delay = Services.prefs.getIntPref("devtools.debugger.remote-timeout");
   gConnectionTimeout = setTimeout(handleConnectionTimeout, delay);
   gClient.connect(onConnectionReady);
 }
 
 /**
  * Connection is ready. List actors and build buttons.
  */
-function onConnectionReady(aType, aTraits) {
+let onConnectionReady = Task.async(function*(aType, aTraits) {
   clearTimeout(gConnectionTimeout);
-  gClient.listTabs(function(aResponse) {
-    document.body.classList.remove("connecting");
-    document.body.classList.add("actors-mode");
 
-    let parent = document.getElementById("tabActors");
+  let deferred = promise.defer();
+  gClient.listAddons(deferred.resolve);
+  let response = yield deferred.promise;
 
-    // Add Global Process debugging...
-    let globals = JSON.parse(JSON.stringify(aResponse));
-    delete globals.tabs;
-    delete globals.selected;
-    // ...only if there are appropriate actors (a 'from' property will always
-    // be there).
+  let parent = document.getElementById("addonActors")
+  if (!response.error && response.addons.length > 0) {
+    // Add one entry for each add-on.
+    for (let addon of response.addons) {
+      if (!addon.debuggable) {
+        continue;
+      }
+      buildAddonLink(addon, parent);
+    }
+  }
+  else {
+    // Hide the section when there are no add-ons
+    parent.previousElementSibling.remove();
+    parent.remove();
+  }
 
-    // Add one entry for each open tab.
-    for (let i = 0; i < aResponse.tabs.length; i++) {
-      buildLink(aResponse.tabs[i], parent, i == aResponse.selected);
-    }
+  deferred = promise.defer();
+  gClient.listTabs(deferred.resolve);
+  response = yield deferred.promise;
+
+  parent = document.getElementById("tabActors");
 
-    let gParent = document.getElementById("globalActors");
+  // Add Global Process debugging...
+  let globals = JSON.parse(JSON.stringify(response));
+  delete globals.tabs;
+  delete globals.selected;
+  // ...only if there are appropriate actors (a 'from' property will always
+  // be there).
+
+  // Add one entry for each open tab.
+  for (let i = 0; i < response.tabs.length; i++) {
+    buildTabLink(response.tabs[i], parent, i == response.selected);
+  }
+
+  let gParent = document.getElementById("globalActors");
 
-    // Build the Remote Process button
-    if (Object.keys(globals).length > 1) {
-      let a = document.createElement("a");
-      a.onclick = function() {
-        openToolbox(globals, true);
+  // Build the Remote Process button
+  if (Object.keys(globals).length > 1) {
+    let a = document.createElement("a");
+    a.onclick = function() {
+      openToolbox(globals, true);
 
-      }
-      a.title = a.textContent = window.l10n.GetStringFromName("mainProcess");
-      a.className = "remote-process";
-      a.href = "#";
-      gParent.appendChild(a);
     }
-    // Move the selected tab on top
-    let selectedLink = parent.querySelector("a.selected");
-    if (selectedLink) {
-      parent.insertBefore(selectedLink, parent.firstChild);
-    }
+    a.title = a.textContent = window.l10n.GetStringFromName("mainProcess");
+    a.className = "remote-process";
+    a.href = "#";
+    gParent.appendChild(a);
+  }
+  // Move the selected tab on top
+  let selectedLink = parent.querySelector("a.selected");
+  if (selectedLink) {
+    parent.insertBefore(selectedLink, parent.firstChild);
+  }
+
+  document.body.classList.remove("connecting");
+  document.body.classList.add("actors-mode");
 
-    // Ensure the first link is focused
-    let firstLink = parent.querySelector("a:first-of-type");
-    if (firstLink) {
-      firstLink.focus();
-    }
+  // Ensure the first link is focused
+  let firstLink = parent.querySelector("a:first-of-type");
+  if (firstLink) {
+    firstLink.focus();
+  }
+});
 
-  });
+/**
+ * Build one button for an add-on actor.
+ */
+function buildAddonLink(addon, parent) {
+  let a = document.createElement("a");
+  a.onclick = function() {
+    openToolbox({ addonActor: addon.actor, title: addon.name }, true, "jsdebugger");
+  }
+
+  a.textContent = addon.name;
+  a.title = addon.id;
+  a.href = "#";
+
+  parent.appendChild(a);
 }
 
 /**
- * Build one button for an actor.
+ * Build one button for a tab actor.
  */
-function buildLink(tab, parent, selected) {
+function buildTabLink(tab, parent, selected) {
   let a = document.createElement("a");
   a.onclick = function() {
     openToolbox(tab);
   }
 
   a.textContent = tab.title;
   a.title = tab.url;
   if (!a.textContent) {
@@ -168,24 +208,24 @@ function showError(type) {
 function handleConnectionTimeout() {
   showError("timeout");
 }
 
 /**
  * The user clicked on one of the buttons.
  * Opens the toolbox.
  */
-function openToolbox(form, chrome=false) {
+function openToolbox(form, chrome=false, tool="webconsole") {
   let options = {
     form: form,
     client: gClient,
     chrome: chrome
   };
   devtools.TargetFactory.forRemoteTab(options).then((target) => {
     let hostType = devtools.Toolbox.HostType.WINDOW;
-    gDevTools.showToolbox(target, "webconsole", hostType).then((toolbox) => {
+    gDevTools.showToolbox(target, tool, hostType).then((toolbox) => {
       toolbox.once("destroyed", function() {
         gClient.close();
       });
     });
     window.close();
   });
 }
--- a/browser/devtools/framework/connect/connect.xhtml
+++ b/browser/devtools/framework/connect/connect.xhtml
@@ -34,16 +34,18 @@
       </form>
       <p class="error-message error-timeout">&errorTimeout;</p>
       <p class="error-message error-refused">&errorRefused;</p>
       <p class="error-message error-unexpected">&errorUnexpected;</p>
     </section>
     <section id="actors-list">
       <p>&availableTabs;</p>
       <ul class="actors" id="tabActors"></ul>
+      <p>&availableAddons;</p>
+      <ul class="actors" id="addonActors"></ul>
       <p>&availableProcesses;</p>
       <ul class="actors" id="globalActors"></ul>
     </section>
     <section id="connecting">
       <p><img src="chrome://browser/skin/tabbrowser/loading.png"></img> &connecting;</p>
     </section>
     <footer>&remoteHelp;<a target='_' href='https://developer.mozilla.org/docs/Tools/Remote_Debugging'>&remoteDocumentation;</a>&remoteHelpSuffix;</footer>
   </body>
--- a/browser/devtools/markupview/markup-view.js
+++ b/browser/devtools/markupview/markup-view.js
@@ -817,21 +817,27 @@ MarkupView.prototype = {
           this.updateNodeOuterHTML(aNode, aValue, oldValue);
         }
       });
     });
   },
 
   /**
    * Mark the given node expanded.
-   * @param aNode The NodeFront to mark as expanded.
+   * @param {NodeFront} aNode The NodeFront to mark as expanded.
+   * @param {Boolean} aExpanded Whether the expand or collapse.
+   * @param {Boolean} aExpandDescendants Whether to expand all descendants too
    */
-  setNodeExpanded: function(aNode, aExpanded) {
+  setNodeExpanded: function(aNode, aExpanded, aExpandDescendants) {
     if (aExpanded) {
-      this.expandNode(aNode);
+      if (aExpandDescendants) {
+        this.expandAll(aNode);
+      } else {
+        this.expandNode(aNode);
+      }
     } else {
       this.collapseNode(aNode);
     }
   },
 
   /**
    * Mark the given node selected, and update the inspector.selection
    * object's NodeFront to keep consistent state between UI and selection.
@@ -1408,17 +1414,17 @@ MarkupContainer.prototype = {
       this.elt.classList.add("collapsed");
       this.expander.removeAttribute("open");
     }
   },
 
   _onToggle: function(event) {
     this.markup.navigate(this);
     if(this.hasChildren) {
-      this.markup.setNodeExpanded(this.node, !this.expanded);
+      this.markup.setNodeExpanded(this.node, !this.expanded, event.altKey);
     }
     event.stopPropagation();
   },
 
   _onMouseDown: function(event) {
     let target = event.target;
 
     // Target may be a resource link (generated by the output-parser)
--- a/browser/devtools/markupview/test/browser.ini
+++ b/browser/devtools/markupview/test/browser.ini
@@ -4,16 +4,17 @@ subsuite = devtools
 support-files =
   doc_markup_edit.html
   doc_markup_flashing.html
   doc_markup_mutation.html
   doc_markup_navigation.html
   doc_markup_pagesize_01.html
   doc_markup_pagesize_02.html
   doc_markup_search.html
+  doc_markup_toggle.html
   doc_markup_tooltip.png
   head.js
   helper_attributes_test_runner.js
   helper_outerhtml_test_runner.js
 
 [browser_markupview_copy_image_data.js]
 [browser_markupview_css_completion_style_attribute.js]
 [browser_markupview_highlight_hover_01.js]
@@ -32,8 +33,11 @@ support-files =
 [browser_markupview_tag_edit_02.js]
 [browser_markupview_tag_edit_03.js]
 [browser_markupview_tag_edit_04.js]
 [browser_markupview_tag_edit_05.js]
 [browser_markupview_tag_edit_06.js]
 [browser_markupview_tag_edit_07.js]
 [browser_markupview_tag_edit_08.js]
 [browser_markupview_textcontent_edit_01.js]
+[browser_markupview_toggle_01.js]
+[browser_markupview_toggle_02.js]
+[browser_markupview_toggle_03.js]
--- a/browser/devtools/markupview/test/browser_markupview_navigation.js
+++ b/browser/devtools/markupview/test/browser_markupview_navigation.js
@@ -108,24 +108,16 @@ function pressKey(key) {
       EventUtils.synthesizeKey("VK_PAGE_DOWN", {});
       break;
     case "home":
       EventUtils.synthesizeKey("VK_HOME", {});
       break;
   }
 }
 
-function waitForChildrenUpdated(inspector) {
-  let def = promise.defer();
-  inspector.markup._waitForChildren().then(() => {
-    executeSoon(def.resolve);
-  });
-  return def.promise;
-}
-
 function checkSelectedNode(key, className, inspector) {
   let node = inspector.selection.node;
 
   if (className == "*comment*") {
     is(node.nodeType, Node.COMMENT_NODE, "Found a comment after pressing " + key);
   } else if (className == "*text*") {
     is(node.nodeType, Node.TEXT_NODE, "Found text after pressing " + key);
   } else if (className == "*doctype*") {
new file mode 100644
--- /dev/null
+++ b/browser/devtools/markupview/test/browser_markupview_toggle_01.js
@@ -0,0 +1,45 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test toggling (expand/collapse) elements by clicking on twisties
+
+const TEST_URL = TEST_URL_ROOT + "doc_markup_toggle.html";
+
+let test = asyncTest(function*() {
+  let {inspector} = yield addTab(TEST_URL).then(openInspector);
+
+  info("Getting the container for the UL parent element");
+  let container = getContainerForRawNode("ul", inspector);
+
+  info("Clicking on the UL parent expander, and waiting for children");
+  let onChildren = waitForChildrenUpdated(inspector);
+  let onUpdated = inspector.once("inspector-updated");
+  EventUtils.synthesizeMouseAtCenter(container.expander, {},
+    inspector.markup.doc.defaultView);
+  yield onChildren;
+  yield onUpdated;
+
+  info("Checking that child LI elements have been created");
+  for (let li of content.document.querySelectorAll("li")) {
+    ok(getContainerForRawNode(li, inspector),
+      "A container for the child LI element was created");
+  }
+  ok(container.expanded, "Parent UL container is expanded");
+
+  info("Clicking again on the UL expander");
+  // No need to wait, this is a local, synchronous operation where nodes are
+  // only hidden from the view, not destroyed
+  EventUtils.synthesizeMouseAtCenter(container.expander, {},
+    inspector.markup.doc.defaultView);
+
+  info("Checking that child LI elements have been hidden");
+  for (let li of content.document.querySelectorAll("li")) {
+    let liContainer = getContainerForRawNode(li, inspector);
+    is(liContainer.elt.getClientRects().length, 0,
+      "The container for the child LI element was hidden");
+  }
+  ok(!container.expanded, "Parent UL container is collapsed");
+});
new file mode 100644
--- /dev/null
+++ b/browser/devtools/markupview/test/browser_markupview_toggle_02.js
@@ -0,0 +1,45 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test toggling (expand/collapse) elements by dbl-clicking on tag lines
+
+const TEST_URL = TEST_URL_ROOT + "doc_markup_toggle.html";
+
+let test = asyncTest(function*() {
+  let {inspector} = yield addTab(TEST_URL).then(openInspector);
+
+  info("Getting the container for the UL parent element");
+  let container = getContainerForRawNode("ul", inspector);
+
+  info("Dbl-clicking on the UL parent expander, and waiting for children");
+  let onChildren = waitForChildrenUpdated(inspector);
+  let onUpdated = inspector.once("inspector-updated");
+  EventUtils.synthesizeMouseAtCenter(container.tagLine, {clickCount: 2},
+    inspector.markup.doc.defaultView);
+  yield onChildren;
+  yield onUpdated;
+
+  info("Checking that child LI elements have been created");
+  for (let li of content.document.querySelectorAll("li")) {
+    ok(getContainerForRawNode(li, inspector),
+      "A container for the child LI element was created");
+  }
+  ok(container.expanded, "Parent UL container is expanded");
+
+  info("Dbl-clicking again on the UL expander");
+  // No need to wait, this is a local, synchronous operation where nodes are
+  // only hidden from the view, not destroyed
+  EventUtils.synthesizeMouseAtCenter(container.tagLine, {clickCount: 2},
+    inspector.markup.doc.defaultView);
+
+  info("Checking that child LI elements have been hidden");
+  for (let li of content.document.querySelectorAll("li")) {
+    let liContainer = getContainerForRawNode(li, inspector);
+    is(liContainer.elt.getClientRects().length, 0,
+      "The container for the child LI element was hidden");
+  }
+  ok(!container.expanded, "Parent UL container is collapsed");
+});
new file mode 100644
--- /dev/null
+++ b/browser/devtools/markupview/test/browser_markupview_toggle_03.js
@@ -0,0 +1,44 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test toggling (expand/collapse) elements by alt-clicking on twisties, which
+// should expand all the descendants
+
+const TEST_URL = TEST_URL_ROOT + "doc_markup_toggle.html";
+
+let test = asyncTest(function*() {
+  let {inspector} = yield addTab(TEST_URL).then(openInspector);
+
+  info("Getting the container for the UL parent element");
+  let container = getContainerForRawNode("ul", inspector);
+
+  info("Alt-clicking on the UL parent expander, and waiting for children");
+  let onUpdated = inspector.once("inspector-updated");
+  EventUtils.synthesizeMouseAtCenter(container.expander, {altKey: true},
+    inspector.markup.doc.defaultView);
+  yield onUpdated;
+  yield waitForMultipleChildrenUpdates(inspector);
+
+  info("Checking that all nodes exist and are expanded");
+  for (let node of content.document.querySelectorAll("ul, li, span, em")) {
+    let nodeContainer = getContainerForRawNode(node, inspector);
+    ok(nodeContainer, "Container for node " + node.tagName + " exists");
+    ok(nodeContainer.expanded,
+      "Container for node " + node.tagName + " is expanded");
+  }
+});
+
+// The expand all operation of the markup-view calls itself recursively and
+// there's not one event we can wait for to know when it's done
+function* waitForMultipleChildrenUpdates(inspector) {
+  // As long as child updates are queued up while we wait for an update already
+  // wait again
+  if (inspector.markup._queuedChildUpdates &&
+      inspector.markup._queuedChildUpdates.size) {
+    yield waitForChildrenUpdated(inspector);
+    return yield waitForMultipleChildrenUpdates(inspector);
+  }
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/devtools/markupview/test/doc_markup_toggle.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <title>Expanding and collapsing markup-view containers</title>
+</head>
+<body>
+  <ul>
+    <li>
+      <span>list <em>item</em></span>
+    </li>
+    <li>
+      <span>list <em>item</em></span>
+    </li>
+    <li>
+      <span>list <em>item</em></span>
+    </li>
+    <li>
+      <span>list <em>item</em></span>
+    </li>
+    <li>
+      <span>list <em>item</em></span>
+    </li>
+    <li>
+      <span>list <em>item</em></span>
+    </li>
+  </ul>
+</body>
+</html>
\ No newline at end of file
--- a/browser/devtools/markupview/test/head.js
+++ b/browser/devtools/markupview/test/head.js
@@ -162,16 +162,33 @@ function selectNode(nodeOrSelector, insp
 function getContainerForRawNode(nodeOrSelector, {markup}) {
   let front = markup.walker.frontForRawNode(getNode(nodeOrSelector));
   let container = markup.getContainer(front);
   info("Markup-container object for " + nodeOrSelector + " " + container);
   return container;
 }
 
 /**
+ * Using the markupview's _waitForChildren function, wait for all queued
+ * children updates to be handled.
+ * @param {InspectorPanel} inspector The instance of InspectorPanel currently
+ * loaded in the toolbox
+ * @return a promise that resolves when all queued children updates have been
+ * handled
+ */
+function waitForChildrenUpdated({markup}) {
+  info("Waiting for queued children updates to be handled");
+  let def = promise.defer();
+  markup._waitForChildren().then(() => {
+    executeSoon(def.resolve);
+  });
+  return def.promise;
+}
+
+/**
  * Simulate a mouse-over on the markup-container (a line in the markup-view)
  * that corresponds to the node or selector passed.
  * @param {String|DOMNode} nodeOrSelector
  * @param {InspectorPanel} inspector The instance of InspectorPanel currently loaded in the toolbox
  * @return a promise that resolves when the container is hovered and the higlighter
  * is shown on the corresponding node
  */
 function hoverContainer(nodeOrSelector, inspector) {
@@ -347,8 +364,20 @@ function getSelectorSearchBox(inspector)
  */
 function searchUsingSelectorSearch(selector, inspector) {
   info("Entering \"" + selector + "\" into the selector-search input field");
   let field = getSelectorSearchBox(inspector);
   field.focus();
   field.value = selector;
   EventUtils.sendKey("return", inspector.panelWin);
 }
+
+/**
+ * This shouldn't be used in the tests, but is useful when writing new tests or
+ * debugging existing tests in order to introduce delays in the test steps
+ * @param {Number} ms The time to wait
+ * @return A promise that resolves when the time is passed
+ */
+function wait(ms) {
+  let def = promise.defer();
+  content.setTimeout(def.resolve, ms);
+  return def.promise;
+}
--- a/browser/experiments/Experiments.jsm
+++ b/browser/experiments/Experiments.jsm
@@ -37,21 +37,19 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 // would.
 XPCOMUtils.defineLazyGetter(this, "CertUtils",
   function() {
     var mod = {};
     Cu.import("resource://gre/modules/CertUtils.jsm", mod);
     return mod;
   });
 
-#ifdef MOZ_CRASHREPORTER
 XPCOMUtils.defineLazyServiceGetter(this, "gCrashReporter",
                                    "@mozilla.org/xre/app-info;1",
                                    "nsICrashReporter");
-#endif
 
 const FILE_CACHE                = "experiments.json";
 const OBSERVER_TOPIC            = "experiments-changed";
 const MANIFEST_VERSION          = 1;
 const CACHE_VERSION             = 1;
 
 const KEEP_HISTORY_N_DAYS       = 180;
 const MIN_EXPERIMENT_ACTIVE_SECONDS = 60;
@@ -71,46 +69,59 @@ const PREF_HEALTHREPORT_ENABLED = "datar
 
 const PREF_BRANCH_TELEMETRY     = "toolkit.telemetry.";
 const PREF_TELEMETRY_ENABLED    = "enabled";
 
 const TELEMETRY_LOG = {
   // log(key, [kind, experimentId, details])
   ACTIVATION_KEY: "EXPERIMENT_ACTIVATION",
   ACTIVATION: {
-    ACTIVATED: "ACTIVATED",             // successfully activated
-    INSTALL_FAILURE: "INSTALL_FAILURE", // failed to install the extension
-    REJECTED: "REJECTED",               // experiment was rejected because of it's conditions,
-                                        // provides details on which
+    // Successfully activated.
+    ACTIVATED: "ACTIVATED",
+    // Failed to install the add-on.
+    INSTALL_FAILURE: "INSTALL_FAILURE",
+    // Experiment does not meet activation requirements. Details will
+    // be provided.
+    REJECTED: "REJECTED",
   },
 
   // log(key, [kind, experimentId, optionalDetails...])
   TERMINATION_KEY: "EXPERIMENT_TERMINATION",
   TERMINATION: {
-    USERDISABLED: "USERDISABLED", // the user disabled this experiment
-    FROM_API: "FROM_API",         // the experiment disabled itself
-    EXPIRED: "EXPIRED",           // experiment expired e.g. by exceeding the end-date
-    RECHECK: "RECHECK",           // disabled after re-evaluating conditions,
-                                  // provides details on which
+    // The Experiments service was disabled.
+    SERVICE_DISABLED: "SERVICE_DISABLED",
+    // Add-on uninstalled.
+    ADDON_UNINSTALLED: "ADDON_UNINSTALLED",
+    // The experiment disabled itself.
+    FROM_API: "FROM_API",
+    // The experiment expired (e.g. by exceeding the end date).
+    EXPIRED: "EXPIRED",
+    // Disabled after re-evaluating conditions. If this is specified,
+    // details will be provided.
+    RECHECK: "RECHECK",
   },
 };
 
 const gPrefs = new Preferences(PREF_BRANCH);
 const gPrefsTelemetry = new Preferences(PREF_BRANCH_TELEMETRY);
 let gExperimentsEnabled = false;
 let gExperiments = null;
 let gLogAppenderDump = null;
 let gPolicyCounter = 0;
 let gExperimentsCounter = 0;
 let gExperimentEntryCounter = 0;
 
 // Tracks active AddonInstall we know about so we can deny external
 // installs.
 let gActiveInstallURLs = new Set();
 
+// Tracks add-on IDs that are being uninstalled by us. This allows us
+// to differentiate between expected uninstalled and user-driven uninstalls.
+let gActiveUninstallAddonIDs = new Set();
+
 let gLogger;
 let gLogDumping = false;
 
 function configureLogging() {
   if (!gLogger) {
     gLogger = Log.repository.getLogger("Browser.Experiments");
     gLogger.addAppender(new Log.ConsoleAppender(new Log.BasicFormatter()));
   }
@@ -311,16 +322,24 @@ Experiments.Policy.prototype = {
     });
   },
 
   telemetryPayload: function () {
     return TelemetryPing.getPayload();
   },
 };
 
+function AlreadyShutdownError(message="already shut down") {
+  this.name = "AlreadyShutdownError";
+  this.message = message;
+}
+
+AlreadyShutdownError.prototype = new Error();
+AlreadyShutdownError.prototype.constructor = AlreadyShutdownError;
+
 /**
  * Manages the experiments and provides an interface to control them.
  */
 
 Experiments.Experiments = function (policy=new Experiments.Policy()) {
   this._log = Log.repository.getLoggerWithMessagePrefix(
     "Browser.Experiments.Experiments",
     "Experiments #" + gExperimentsCounter++ + "::");
@@ -335,19 +354,16 @@ Experiments.Experiments = function (poli
   this._experiments = null;
   this._refresh = false;
   this._terminateReason = null; // or TELEMETRY_LOG.TERMINATION....
   this._dirty = false;
 
   // Loading the cache happens once asynchronously on startup
   this._loadTask = null;
 
-  // Ignore addon-manager notifications for addons that we are uninstalling ourself
-  this._pendingUninstall = null;
-
   // The _main task handles all other actions:
   // * refreshing the manifest off the network (if _refresh)
   // * disabling/enabling experiments
   // * saving the cache (if _dirty)
   this._mainTask = null;
 
   // Timer for re-evaluating experiment status.
   this._timer = null;
@@ -421,17 +437,21 @@ Experiments.Experiments.prototype = {
 
       if (this._timer) {
         this._timer.clear();
       }
     }
 
     this._shutdown = true;
     if (this._mainTask) {
-      yield this._mainTask;
+      try {
+        yield this._mainTask;
+      } catch (e if e instanceof AlreadyShutdownError) {
+        // We error out of tasks after shutdown via that exception.
+      }
     }
 
     this._log.info("Completed uninitialization.");
   }),
 
   _registerWithAddonManager: function () {
     this._log.trace("Registering instance with Addon Manager.");
 
@@ -444,17 +464,17 @@ Experiments.Experiments.prototype = {
     AddonManager.removeAddonListener(this);
   },
 
   /**
    * Throws an exception if we've already shut down.
    */
   _checkForShutdown: function() {
     if (this._shutdown) {
-      throw Error("uninit() already called");
+      throw new AlreadyShutdownError("uninit() already called");
     }
   },
 
   /**
    * Whether the experiments feature is enabled.
    */
   get enabled() {
     return gExperimentsEnabled;
@@ -463,34 +483,34 @@ Experiments.Experiments.prototype = {
   /**
    * Toggle whether the experiments feature is enabled or not.
    */
   set enabled(enabled) {
     this._log.trace("set enabled(" + enabled + ")");
     gPrefs.set(PREF_ENABLED, enabled);
   },
 
-  _toggleExperimentsEnabled: function (enabled) {
+  _toggleExperimentsEnabled: Task.async(function* (enabled) {
     this._log.trace("_toggleExperimentsEnabled(" + enabled + ")");
     let wasEnabled = gExperimentsEnabled;
     gExperimentsEnabled = enabled && telemetryEnabled();
 
     if (wasEnabled == gExperimentsEnabled) {
       return;
     }
 
     if (gExperimentsEnabled) {
-      this.updateManifest();
+      yield this.updateManifest();
     } else {
-      this.disableExperiment();
+      yield this.disableExperiment(TELEMETRY_LOG.TERMINATION.SERVICE_DISABLED);
       if (this._timer) {
         this._timer.clear();
       }
     }
-  },
+  }),
 
   _telemetryStatusChanged: function () {
     this._toggleExperimentsEnabled(gExperimentsEnabled);
   },
 
   /**
    * Returns a promise that is resolved with an array of `ExperimentInfo` objects,
    * which provide info on the currently and recently active experiments.
@@ -662,39 +682,28 @@ Experiments.Experiments.prototype = {
   notify: function (timer) {
     this._log.trace("notify()");
     this._checkForShutdown();
     return this._run();
   },
 
   // START OF ADD-ON LISTENERS
 
-  onDisabled: function (addon) {
-    this._log.trace("onDisabled() - addon id: " + addon.id);
-    if (addon.id == this._pendingUninstall) {
-      return;
-    }
-    let activeExperiment = this._getActiveExperiment();
-    if (!activeExperiment || activeExperiment._addonId != addon.id) {
-      return;
-    }
-    this.disableExperiment();
-  },
-
   onUninstalled: function (addon) {
     this._log.trace("onUninstalled() - addon id: " + addon.id);
-    if (addon.id == this._pendingUninstall) {
+    if (gActiveUninstallAddonIDs.has(addon.id)) {
       this._log.trace("matches pending uninstall");
       return;
     }
     let activeExperiment = this._getActiveExperiment();
     if (!activeExperiment || activeExperiment._addonId != addon.id) {
       return;
     }
-    this.disableExperiment();
+
+    this.disableExperiment(TELEMETRY_LOG.TERMINATION.ADDON_UNINSTALLED);
   },
 
   onInstallStarted: function (install) {
     if (install.addon.type != "experiment") {
       return;
     }
 
     // We want to be in control of all experiment add-ons: reject installs
@@ -922,25 +931,27 @@ Experiments.Experiments.prototype = {
       this._log.error("getActiveExperimentId() - should not have more than 1 active experiment");
       throw new Error("have more than 1 active experiment");
     }
 
     return null;
   },
 
   /**
-   * Disable an experiment by id.
-   * @param experimentId The id of the experiment.
-   * @param userDisabled (optional) Whether this is disabled as a result of a user action.
+   * Disables all active experiments.
+   *
    * @return Promise<> Promise that will get resolved once the task is done or failed.
    */
-  disableExperiment: function (userDisabled=true) {
+  disableExperiment: function (reason) {
+    if (!reason) {
+      throw new Error("Must specify a termination reason.");
+    }
+
     this._log.trace("disableExperiment()");
-
-    this._terminateReason = userDisabled ? TELEMETRY_LOG.TERMINATION.USERDISABLED : TELEMETRY_LOG.TERMINATION.FROM_API;
+    this._terminateReason = reason;
     return this._run();
   },
 
   /**
    * The Set of add-on IDs that we know about from manifests.
    */
   get _trackedAddonIds() {
     if (!this._experiments) {
@@ -987,54 +998,53 @@ Experiments.Experiments.prototype = {
     let activeChanged = false;
     let now = this._policy.now();
 
     if (!activeExperiment) {
       // Avoid this pref staying out of sync if there were e.g. crashes.
       gPrefs.set(PREF_ACTIVE_EXPERIMENT, false);
     }
 
+    // Ensure the active experiment is in the proper state. This may install,
+    // uninstall, upgrade, or enable the experiment add-on. What exactly is
+    // abstracted away from us by design.
     if (activeExperiment) {
-      this._pendingUninstall = activeExperiment._addonId;
-      try {
-        let wasStopped;
-        if (this._terminateReason) {
-          yield activeExperiment.stop(this._terminateReason);
-          wasStopped = true;
+      let changes;
+      let shouldStopResult = yield activeExperiment.shouldStop();
+      if (shouldStopResult.shouldStop) {
+        let expireReasons = ["endTime", "maxActiveSeconds"];
+        let kind, reason;
+
+        if (expireReasons.indexOf(shouldStopResult.reason[0]) != -1) {
+          kind = TELEMETRY_LOG.TERMINATION.EXPIRED;
+          reason = null;
         } else {
-          wasStopped = yield activeExperiment.maybeStop();
+          kind = TELEMETRY_LOG.TERMINATION.RECHECK;
+          reason = shouldStopResult.reason;
         }
-        if (wasStopped) {
-          this._dirty = true;
-          this._log.debug("evaluateExperiments() - stopped experiment "
-                        + activeExperiment.id);
-          activeExperiment = null;
-          activeChanged = true;
-        } else if (!gExperimentsEnabled) {
-          // No further actions if the feature is disabled.
-        } else if (activeExperiment.needsUpdate) {
-          this._log.debug("evaluateExperiments() - updating experiment "
-                        + activeExperiment.id);
-          try {
-            yield activeExperiment.stop();
-            yield activeExperiment.start();
-          } catch (e) {
-            this._log.error(e);
-            // On failure try the next experiment.
-            activeExperiment = null;
-          }
-          this._dirty = true;
-          activeChanged = true;
-        } else {
-          yield activeExperiment.ensureActive();
-        }
-      } finally {
-        this._pendingUninstall = null;
+        changes = yield activeExperiment.stop(kind, reason);
+      }
+      else if (this._terminateReason) {
+        changes = yield activeExperiment.stop(this._terminateReason);
+      }
+      else {
+        changes = yield activeExperiment.reconcileAddonState();
+      }
+
+      if (changes) {
+        this._dirty = true;
+        activeChanged = true;
+      }
+
+      if (!activeExperiment._enabled) {
+        activeExperiment = null;
+        activeChanged = true;
       }
     }
+
     this._terminateReason = null;
 
     if (!activeExperiment && gExperimentsEnabled) {
       for (let [id, experiment] of this._experiments) {
         let applicable;
         let reason = null;
         try {
           applicable = yield experiment.isApplicable();
@@ -1047,40 +1057,45 @@ Experiments.Experiments.prototype = {
         if (!applicable && reason && reason[0] != "was-active") {
           // Report this from here to avoid over-reporting.
           let desc = TELEMETRY_LOG.ACTIVATION;
           let data = [TELEMETRY_LOG.ACTIVATION.REJECTED, id];
           data = data.concat(reason);
           TelemetryLog.log(TELEMETRY_LOG.ACTIVATION_KEY, data);
         }
 
-        if (applicable) {
-          this._log.debug("evaluateExperiments() - activating experiment " + id);
-          try {
-            yield experiment.start();
-            activeChanged = true;
-            activeExperiment = experiment;
-            this._dirty = true;
-            break;
-          } catch (e) {
-            // On failure try the next experiment.
-          }
+        if (!applicable) {
+          continue;
+        }
+
+        this._log.debug("evaluateExperiments() - activating experiment " + id);
+        try {
+          yield experiment.start();
+          activeChanged = true;
+          activeExperiment = experiment;
+          this._dirty = true;
+          break;
+        } catch (e) {
+          // On failure, clean up the best we can and try the next experiment.
+          this._log.error("evaluateExperiments() - Unable to start experiment: " + e.message);
+          experiment._enabled = false;
+          yield experiment.reconcileAddonState();
         }
       }
     }
 
+    gPrefs.set(PREF_ACTIVE_EXPERIMENT, activeExperiment != null);
+
     if (activeChanged) {
       Services.obs.notifyObservers(null, OBSERVER_TOPIC, null);
     }
 
-#ifdef MOZ_CRASHREPORTER
-    if (activeExperiment) {
+    if ("@mozilla.org/toolkit/crash-reporter;1" in Cc && activeExperiment) {
       gCrashReporter.annotateCrashReport("ActiveExperiment", activeExperiment.id);
     }
-#endif
   },
 
   /*
    * Schedule the soonest re-check of experiment applicability that is needed.
    */
   _scheduleNextRun: function () {
     this._checkForShutdown();
 
@@ -1122,17 +1137,17 @@ Experiments.Experiments.prototype = {
  */
 
 Experiments.ExperimentEntry = function (policy) {
   this._policy = policy || new Experiments.Policy();
   this._log = Log.repository.getLoggerWithMessagePrefix(
     "Browser.Experiments.Experiments",
     "ExperimentEntry #" + gExperimentEntryCounter++ + "::");
 
-  // Is this experiment running?
+  // Is the experiment supposed to be running.
   this._enabled = false;
   // When this experiment was started, if ever.
   this._startDate = null;
   // When this experiment was ended, if ever.
   this._endDate = null;
   // The condition data from the manifest.
   this._manifestData = null;
   // For an active experiment, signifies whether we need to update the xpi.
@@ -1193,16 +1208,21 @@ Experiments.ExperimentEntry.prototype = 
     "_endDate",
   ]),
 
   DATE_KEYS: new Set([
     "_startDate",
     "_endDate",
   ]),
 
+  ADDON_CHANGE_NONE: 0,
+  ADDON_CHANGE_INSTALL: 1,
+  ADDON_CHANGE_UNINSTALL: 2,
+  ADDON_CHANGE_ENABLE: 4,
+
   /*
    * Initialize entry from the manifest.
    * @param data The experiment data from the manifest.
    * @return boolean Whether initialization succeeded.
    */
   initFromManifestData: function (data) {
     if (!this._isManifestDataValid(data)) {
       return false;
@@ -1482,36 +1502,28 @@ Experiments.ExperimentEntry.prototype = 
       }
 
       throw new Task.Result(true);
     }.bind(this));
   },
 
   /*
    * Start running the experiment.
+   *
    * @return Promise<> Resolved when the operation is complete.
    */
-  start: function () {
+  start: Task.async(function* () {
     this._log.trace("start() for " + this.id);
 
-    return Task.spawn(function* ExperimentEntry_start_task() {
-      let addons = yield installedExperimentAddons();
-      if (addons.length > 0) {
-        this._log.error("start() - there are already "
-                        + addons.length + " experiment addons installed");
-        yield uninstallAddons(addons);
-      }
-
-      yield this._installAddon();
-      gPrefs.set(PREF_ACTIVE_EXPERIMENT, true);
-    }.bind(this));
-  },
+    this._enabled = true;
+    return yield this.reconcileAddonState();
+  }),
 
   // Async install of the addon for this experiment, part of the start task above.
-  _installAddon: function* () {
+  _installAddon: Task.async(function* () {
     let deferred = Promise.defer();
 
     let hash = this._policy.ignoreHashes ? null : this._manifestData.xpiHash;
 
     let install = yield addonInstallForURL(this._manifestData.xpiURL, hash);
     gActiveInstallURLs.add(install.sourceURI.spec);
 
     let failureHandler = (install, handler) => {
@@ -1600,108 +1612,146 @@ Experiments.ExperimentEntry.prototype = 
     ["onDownloadCancelled", "onDownloadFailed", "onInstallCancelled", "onInstallFailed"]
       .forEach(what => {
         listener[what] = install => failureHandler(install, what)
       });
 
     install.addListener(listener);
     install.install();
 
-    return deferred.promise;
-  },
+    return yield deferred.promise;
+  }),
 
-  /*
+  /**
    * Stop running the experiment if it is active.
-   * @param terminationKind (optional) The termination kind, e.g. USERDISABLED or EXPIRED.
-   * @param terminationReason (optional) The termination reason details for
-   *                          termination kind RECHECK.
+   *
+   * @param terminationKind (optional)
+   *        The termination kind, e.g. ADDON_UNINSTALLED or EXPIRED.
+   * @param terminationReason (optional)
+   *        The termination reason details for termination kind RECHECK.
    * @return Promise<> Resolved when the operation is complete.
    */
-  stop: function (terminationKind, terminationReason) {
+  stop: Task.async(function* (terminationKind, terminationReason) {
     this._log.trace("stop() - id=" + this.id + ", terminationKind=" + terminationKind);
     if (!this._enabled) {
-      this._log.warning("stop() - experiment not enabled: " + id);
-      return Promise.reject();
+      throw new Error("Must not call stop() on an inactive experiment.");
     }
 
     this._enabled = false;
-    gPrefs.set(PREF_ACTIVE_EXPERIMENT, false);
+
+    let changes = yield this.reconcileAddonState();
+    let now = this._policy.now();
+    this._lastChangedDate = now;
+    this._endDate = now;
+    this._logTermination(terminationKind, terminationReason);
+
+    return changes;
+  }),
 
-    let deferred = Promise.defer();
-    let updateDates = () => {
-      let now = this._policy.now();
-      this._lastChangedDate = now;
-      this._endDate = now;
-    };
+  /**
+   * Reconcile the state of the add-on against what it's supposed to be.
+   *
+   * If we are active, ensure the add-on is enabled and up to date.
+   *
+   * If we are inactive, ensure the add-on is not installed.
+   */
+  reconcileAddonState: Task.async(function* () {
+    this._log.trace("reconcileAddonState()");
 
-    this._getAddon().then((addon) => {
-      if (!addon) {
-        let message = "could not get Addon for " + this.id;
-        this._log.warn("stop() - " + message);
-        updateDates();
-        deferred.resolve();
-        return;
+    if (!this._enabled) {
+      if (!this._addonId) {
+        this._log.trace("reconcileAddonState() - Experiment is not enabled and " +
+                        "has no add-on. Doing nothing.");
+        return this.ADDON_CHANGE_NONE;
       }
 
-      updateDates();
-      this._logTermination(terminationKind, terminationReason);
-      deferred.resolve(uninstallAddons([addon]));
-    });
+      let addon = yield this._getAddon();
+      if (!addon) {
+        this._log.trace("reconcileAddonState() - Inactive experiment has no " +
+                        "add-on. Doing nothing.");
+        return this.ADDON_CHANGE_NONE;
+      }
 
-    return deferred.promise;
-  },
+      this._log.info("reconcileAddonState() - Uninstalling add-on for inactive " +
+                     "experiment: " + addon.id);
+      gActiveUninstallAddonIDs.add(addon.id);
+      yield uninstallAddons([addon]);
+      gActiveUninstallAddonIDs.delete(addon.id);
+      return this.ADDON_CHANGE_UNINSTALL;
+    }
+
+    // If we get here, we're supposed to be active.
+
+    let changes = 0;
 
-  /**
-   * Try to ensure this experiment is active.
-   *
-   * The returned promise will be resolved if the experiment is active
-   * in the Addon Manager or rejected if it isn't.
-   *
-   * @return Promise<>
-   */
-  ensureActive: Task.async(function* () {
-    this._log.trace("ensureActive() for " + this.id);
+    // That requires an add-on.
+    let currentAddon = yield this._getAddon();
+
+    // If we have an add-on but it isn't up to date, uninstall it
+    // (to prepare for reinstall).
+    if (currentAddon && this._needsUpdate) {
+      this._log.info("reconcileAddonState() - Uninstalling add-on because update " +
+                     "needed: " + currentAddon.id);
+      gActiveUninstallAddonIDs.add(currentAddon.id);
+      yield uninstallAddons([currentAddon]);
+      gActiveUninstallAddonIDs.delete(currentAddon.id);
+      changes |= this.ADDON_CHANGE_UNINSTALL;
+    }
+
+    if (!currentAddon || this._needsUpdate) {
+      this._log.info("reconcileAddonState() - Installing add-on.");
+      yield this._installAddon();
+      changes |= this.ADDON_CHANGE_INSTALL;
+    }
 
     let addon = yield this._getAddon();
     if (!addon) {
-      this._log.warn("Experiment is not installed: " + this._addonId);
-      throw new Error("Experiment is not installed: " + this._addonId);
+      throw new Error("Could not obtain add-on for experiment that should be " +
+                      "enabled.");
     }
 
-    // User disabled likely means the experiment is disabled at startup,
-    // since the permissions don't allow it to be disabled by the user.
+    // If we have the add-on and it is enabled, we are done.
     if (!addon.userDisabled) {
-      return;
+      return changes;
     }
 
     let deferred = Promise.defer();
 
+    // Else we need to enable it.
     let listener = {
       onEnabled: enabledAddon => {
         if (enabledAddon.id != addon.id) {
           return;
         }
 
         AddonManager.removeAddonListener(listener);
         deferred.resolve();
       },
     };
 
     this._log.info("Activating add-on: " + addon.id);
     AddonManager.addAddonListener(listener);
     addon.userDisabled = false;
     yield deferred.promise;
-  }),
+    changes |= this.ADDON_CHANGE_ENABLE;
+
+    this._log.info("Add-on has been enabled: " + addon.id);
+    return changes;
+   }),
 
   /**
    * Obtain the underlying Addon from the Addon Manager.
    *
    * @return Promise<Addon|null>
    */
   _getAddon: function () {
+    if (!this._addonId) {
+      return Promise.resolve(null);
+    }
+
     let deferred = Promise.defer();
 
     AddonManager.getAddonByID(this._addonId, deferred.resolve);
 
     return deferred.promise;
   },
 
   _logTermination: function (terminationKind, terminationReason) {
@@ -1717,53 +1767,28 @@ Experiments.ExperimentEntry.prototype = 
     let data = [terminationKind, this.id];
     if (terminationReason) {
       data = data.concat(terminationReason);
     }
 
     TelemetryLog.log(TELEMETRY_LOG.TERMINATION_KEY, data);
   },
 
-  /*
-   * Stop if experiment stop criteria are met.
-   * @return Promise<boolean> Resolved when done stopping or checking,
-   *                          the value indicates whether it was stopped.
+  /**
+   * Determine whether an active experiment should be stopped.
    */
-  maybeStop: function () {
-    this._log.trace("maybeStop()");
-    return Task.spawn(function* ExperimentEntry_maybeStop_task() {
-      if (!gExperimentsEnabled) {
-        this._log.warn("maybeStop() - should not get here");
-        yield this.stop(TELEMETRY_LOG.TERMINATION.FROM_API);
-        return true;
-      }
+  shouldStop: function () {
+    if (!this._enabled) {
+      throw new Error("shouldStop must not be called on disabled experiments.");
+    }
 
-      let result = yield this._shouldStop();
-      if (result.shouldStop) {
-        let expireReasons = ["endTime", "maxActiveSeconds"];
-        if (expireReasons.indexOf(result.reason[0]) != -1) {
-          yield this.stop(TELEMETRY_LOG.TERMINATION.EXPIRED);
-        } else {
-          yield this.stop(TELEMETRY_LOG.TERMINATION.RECHECK, result.reason);
-        }
-      }
-
-      return result.shouldStop;
-    }.bind(this));
-  },
-
-  _shouldStop: function () {
     let data = this._manifestData;
     let now = this._policy.now() / 1000; // The manifest times are in seconds.
     let maxActiveSec = data.maxActiveSeconds || 0;
 
-    if (!this._enabled) {
-      return Promise.resolve({shouldStop: false});
-    }
-
     let deferred = Promise.defer();
     this.isApplicable().then(
       () => deferred.resolve({shouldStop: false}),
       reason => deferred.resolve({shouldStop: true, reason: reason})
     );
 
     return deferred.promise;
   },
--- a/browser/experiments/moz.build
+++ b/browser/experiments/moz.build
@@ -4,15 +4,15 @@
 
 EXTRA_COMPONENTS += [
     'Experiments.manifest',
     'ExperimentsService.js',
 ]
 
 JS_MODULES_PATH = 'modules/experiments'
 
-EXTRA_PP_JS_MODULES += [
+EXTRA_JS_MODULES += [
   'Experiments.jsm',
 ]
 
 XPCSHELL_TESTS_MANIFESTS += ['test/xpcshell/xpcshell.ini']
 
 SPHINX_TREES['experiments'] = 'docs'
--- a/browser/experiments/test/xpcshell/test_activate.js
+++ b/browser/experiments/test/xpcshell/test_activate.js
@@ -97,51 +97,52 @@ add_task(function* test_startStop() {
   let addons = yield getExperimentAddons();
   Assert.equal(addons.length, 0, "No experiment add-ons are installed.");
 
   defineNow(gPolicy, futureDate(startDate, 5 * MS_IN_ONE_DAY));
   result = yield isApplicable(experiment);
   Assert.equal(result.applicable, true, "Experiment should now be applicable.");
   Assert.equal(experiment.enabled, false, "Experiment should not be enabled.");
 
-  yield experiment.start();
+  let changes = yield experiment.start();
+  Assert.equal(changes, experiment.ADDON_CHANGE_INSTALL, "Add-on was installed.");
   addons = yield getExperimentAddons();
   Assert.equal(experiment.enabled, true, "Experiment should now be enabled.");
   Assert.equal(addons.length, 1, "1 experiment add-on is installed.");
   Assert.equal(addons[0].id, experiment._addonId, "The add-on is the one we expect.");
   Assert.equal(addons[0].userDisabled, false, "The add-on is not userDisabled.");
   Assert.ok(addons[0].isActive, "The add-on is active.");
 
-  yield experiment.stop();
+  changes = yield experiment.stop();
+  Assert.equal(changes, experiment.ADDON_CHANGE_UNINSTALL, "Add-on was uninstalled.");
   addons = yield getExperimentAddons();
   Assert.equal(experiment.enabled, false, "Experiment should not be enabled.");
   Assert.equal(addons.length, 0, "Experiment should be uninstalled from the Addon Manager.");
 
-  yield experiment.start();
+  changes = yield experiment.start();
+  Assert.equal(changes, experiment.ADDON_CHANGE_INSTALL, "Add-on was installed.");
   addons = yield getExperimentAddons();
   Assert.equal(experiment.enabled, true, "Experiment should now be enabled.");
   Assert.equal(addons.length, 1, "1 experiment add-on is installed.");
   Assert.equal(addons[0].id, experiment._addonId, "The add-on is the one we expect.");
   Assert.equal(addons[0].userDisabled, false, "The add-on is not userDisabled.");
   Assert.ok(addons[0].isActive, "The add-on is active.");
 
-  let result = yield experiment._shouldStop();
+  let result = yield experiment.shouldStop();
   Assert.equal(result.shouldStop, false, "shouldStop should be false.");
-  let maybeStop = yield experiment.maybeStop();
-  Assert.equal(maybeStop, false, "Experiment should not have been stopped.");
   Assert.equal(experiment.enabled, true, "Experiment should be enabled.");
   addons = yield getExperimentAddons();
   Assert.equal(addons.length, 1, "Experiment still in add-ons manager.");
   Assert.ok(addons[0].isActive, "The add-on is still active.");
 
   defineNow(gPolicy, futureDate(endDate, MS_IN_ONE_DAY));
-  result = yield experiment._shouldStop();
+  result = yield experiment.shouldStop();
   Assert.equal(result.shouldStop, true, "shouldStop should now be true.");
-  maybeStop = yield experiment.maybeStop();
-  Assert.equal(maybeStop, true, "Experiment should have been stopped.");
+  changes = yield experiment.stop();
+  Assert.equal(changes, experiment.ADDON_CHANGE_UNINSTALL, "Add-on should be uninstalled.");
   Assert.equal(experiment.enabled, false, "Experiment should be disabled.");
   addons = yield getExperimentAddons();
   Assert.equal(addons.length, 0, "Experiment add-on is uninstalled.");
 
   // Ensure hash validation works.
   // We set an incorrect hash and expect the install to fail.
   experiment._manifestData.xpiHash = "sha1:41014dcc66b4dcedcd973491a1530a32f0517d8a";
   let errored = false;
--- a/browser/experiments/test/xpcshell/test_api.js
+++ b/browser/experiments/test/xpcshell/test_api.js
@@ -419,17 +419,17 @@ add_task(function* test_disableExperimen
     Assert.equal(experimentInfo[k], list[0][k],
                  "Property " + k + " should match reference data.");
   }
 
   // Test disabling the experiment.
 
   now = futureDate(now, 1 * MS_IN_ONE_DAY);
   defineNow(gPolicy, now);
-  yield experiments.disableExperiment();
+  yield experiments.disableExperiment("foo");
 
   list = yield experiments.getExperiments();
   Assert.equal(list.length, 1, "Experiment list should have 1 entry.");
 
   experimentInfo.active = false;
   experimentInfo.endDate = now.getTime();
   for (let k of Object.keys(experimentInfo)) {
     Assert.equal(experimentInfo[k], list[0][k],
@@ -744,17 +744,17 @@ add_task(function* test_userDisabledAndU
   let todayActive = yield experiments.lastActiveToday();
   Assert.ok(todayActive, "Last active for today reports a value.");
   Assert.equal(todayActive.id, list[0].id, "The entry is what we expect.");
 
   // Explicitly disable an experiment.
 
   now = futureDate(now, 20 * MS_IN_ONE_DAY);
   defineNow(gPolicy, now);
-  yield experiments.disableExperiment();
+  yield experiments.disableExperiment("foo");
   Assert.equal(observerFireCount, ++expectedObserverFireCount,
                "Experiments observer should have been called.");
 
   list = yield experiments.getExperiments();
   Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
   Assert.equal(list[0].id, EXPERIMENT1_ID, "Experiment 1 should be the sole entry.");
   Assert.equal(list[0].active, false, "Experiment should not be active anymore.");
   todayActive = yield experiments.lastActiveToday();
@@ -1400,17 +1400,17 @@ add_task(function* testForeignExperiment
     experiments: [],
   };
 
   yield experiments.init();
 
   let addons = yield getExperimentAddons();
   Assert.equal(addons.length, 0, "Precondition: No experiment add-ons present.");
 
-  let failed;
+  let failed = false;
   try {
     yield AddonTestUtils.installXPIFromURL(gDataRoot + EXPERIMENT1_XPI_NAME, EXPERIMENT1_XPI_SHA1);
   } catch (ex) {
     failed = true;
   }
   Assert.ok(failed, "Add-on install should not have completed successfully");
   addons = yield getExperimentAddons();
   Assert.equal(addons.length, 0, "Add-on install should have been cancelled.");
--- a/browser/experiments/test/xpcshell/test_cache.js
+++ b/browser/experiments/test/xpcshell/test_cache.js
@@ -238,12 +238,12 @@ add_task(function* test_cache() {
 
   experimentListData[0].active = false;
   experimentListData[0].endDate = now.getTime();
   checkExperimentListsEqual(experimentListData, list);
   checkExperimentSerializations(experiments._experiments.values());
 
   // Cleanup.
 
-  yield experiments.disableExperiment();
+  yield experiments._toggleExperimentsEnabled(false);
   yield experiments.uninit();
   yield removeCacheFile();
 });
--- a/browser/experiments/test/xpcshell/test_telemetry.js
+++ b/browser/experiments/test/xpcshell/test_telemetry.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 Cu.import("resource://testing-common/httpd.js");
 Cu.import("resource://gre/modules/TelemetryLog.jsm");
 Cu.import("resource://gre/modules/TelemetryPing.jsm");
-Cu.import("resource:///modules/experiments/Experiments.jsm");
+let bsp = Cu.import("resource:///modules/experiments/Experiments.jsm");
 
 
 const FILE_MANIFEST            = "experiments.manifest";
 const MANIFEST_HANDLER         = "manifests/handler";
 
 const SEC_IN_ONE_DAY  = 24 * 60 * 60;
 const MS_IN_ONE_DAY   = SEC_IN_ONE_DAY * 1000;
 
@@ -20,35 +20,17 @@ let gProfileDir          = null;
 let gHttpServer          = null;
 let gHttpRoot            = null;
 let gDataRoot            = null;
 let gReporter            = null;
 let gPolicy              = null;
 let gManifestObject      = null;
 let gManifestHandlerURI  = null;
 
-const TLOG = {
-  // log(key, [kind, experimentId, details])
-  ACTIVATION_KEY: "EXPERIMENT_ACTIVATION",
-  ACTIVATION: {
-    ACTIVATED: "ACTIVATED",
-    INSTALL_FAILURE: "INSTALL_FAILURE",
-    REJECTED: "REJECTED",
-  },
-
-  // log(key, [kind, experimentId, optionalDetails...])
-  TERMINATION_KEY: "EXPERIMENT_TERMINATION",
-  TERMINATION: {
-    USERDISABLED: "USERDISABLED",
-    FROM_API: "FROM_API",
-    EXPIRED: "EXPIRED",
-    RECHECK: "RECHECK",
-  },
-};
-
+const TLOG = bsp.TELEMETRY_LOG;
 
 let gGlobalScope = this;
 function loadAddonManager() {
   let ns = {};
   Cu.import("resource://gre/modules/Services.jsm", ns);
   let head = "../../../../toolkit/mozapps/extensions/test/xpcshell/head_addons.js";
   let file = do_get_file(head);
   let uri = ns.Services.io.newFileURI(file);
@@ -258,30 +240,30 @@ add_task(function* test_telemetryBasics(
   Assert.equal(list.length, 2, "Experiment list should have 2 entries.");
 
   expectedLogLength += 1;
   log = TelemetryPing.getPayload().log;
   Assert.equal(log.length, expectedLogLength, "Telemetry log should have " + expectedLogLength + " entries.");
   checkEvent(log[log.length-1], TLOG.ACTIVATION_KEY,
              [TLOG.ACTIVATION.ACTIVATED, EXPERIMENT2_ID]);
 
-  // Fake user-disable of an experiment.
+  // Fake user uninstall of experiment via add-on manager.
 
   now = futureDate(now, MS_IN_ONE_DAY);
   defineNow(gPolicy, now);
 
-  yield experiments.disableExperiment();
+  yield experiments.disableExperiment(TLOG.TERMINATION.ADDON_UNINSTALLED);
   list = yield experiments.getExperiments();
   Assert.equal(list.length, 2, "Experiment list should have 2 entries.");
 
   expectedLogLength += 1;
   log = TelemetryPing.getPayload().log;
   Assert.equal(log.length, expectedLogLength, "Telemetry log should have " + expectedLogLength + " entries.");
   checkEvent(log[log.length-1], TLOG.TERMINATION_KEY,
-             [TLOG.TERMINATION.USERDISABLED, EXPERIMENT2_ID]);
+             [TLOG.TERMINATION.ADDON_UNINSTALLED, EXPERIMENT2_ID]);
 
   // Trigger update with experiment 1a ready to start.
 
   now = futureDate(now, MS_IN_ONE_DAY);
   defineNow(gPolicy, now);
   gManifestObject.experiments[0].id      = EXPERIMENT3_ID;
   gManifestObject.experiments[0].endTime = dateToSeconds(futureDate(now, 50 * MS_IN_ONE_DAY));
 
@@ -290,22 +272,22 @@ add_task(function* test_telemetryBasics(
   Assert.equal(list.length, 3, "Experiment list should have 3 entries.");
 
   expectedLogLength += 1;
   log = TelemetryPing.getPayload().log;
   Assert.equal(log.length, expectedLogLength, "Telemetry log should have " + expectedLogLength + " entries.");
   checkEvent(log[log.length-1], TLOG.ACTIVATION_KEY,
              [TLOG.ACTIVATION.ACTIVATED, EXPERIMENT3_ID]);
 
-  // Trigger non-user-disable of an experiment via the API
+  // Trigger disable of an experiment via the API.
 
   now = futureDate(now, MS_IN_ONE_DAY);
   defineNow(gPolicy, now);
 
-  yield experiments.disableExperiment(false);
+  yield experiments.disableExperiment(TLOG.TERMINATION.FROM_API);
   list = yield experiments.getExperiments();
   Assert.equal(list.length, 3, "Experiment list should have 3 entries.");
 
   expectedLogLength += 1;
   log = TelemetryPing.getPayload().log;
   Assert.equal(log.length, expectedLogLength, "Telemetry log should have " + expectedLogLength + " entries.");
   checkEvent(log[log.length-1], TLOG.TERMINATION_KEY,
              [TLOG.TERMINATION.FROM_API, EXPERIMENT3_ID]);
--- a/browser/installer/windows/nsis/stub.nsi
+++ b/browser/installer/windows/nsis/stub.nsi
@@ -225,16 +225,19 @@ Var ControlRightPX
 !insertmacro GetOptions
 !insertmacro StrFilter
 
 !include "locales.nsi"
 !include "branding.nsi"
 
 !include "defines.nsi"
 
+; Must be included after defines.nsi
+!include "locale-fonts.nsh"
+
 ; The OFFICIAL define is a workaround to support different urls for Release and
 ; Beta since they share the same branding when building with other branches that
 ; set the update channel to beta.
 !ifdef OFFICIAL
 !ifdef BETA_UPDATE_CHANNEL
 !undef URLStubDownload
 !define URLStubDownload "http://download.mozilla.org/?os=win&lang=${AB_CD}&product=firefox-beta-latest"
 !undef URLManualDownload
@@ -411,19 +414,37 @@ Function .onInit
   StrCpy $CheckboxSendPing "1"
 !ifdef MOZ_MAINTENANCE_SERVICE
   StrCpy $CheckboxInstallMaintSvc "1"
 !else
   StrCpy $CheckboxInstallMaintSvc "0"
 !endif
   StrCpy $WasOptionsButtonClicked "0"
 
-  CreateFont $FontBlurb "$(^Font)" "12" "500"
-  CreateFont $FontNormal "$(^Font)" "11" "500"
-  CreateFont $FontItalic "$(^Font)" "11" "500" /ITALIC
+  StrCpy $0 ""
+!ifdef FONT_FILE1
+  ${If} ${FileExists} "$FONTS\${FONT_FILE1}"
+    StrCpy $0 "${FONT_NAME1}"
+  ${EndIf}
+!endif
+
+!ifdef FONT_FILE2
+  ${If} $0 == ""
+  ${AndIf} ${FileExists} "$FONTS\${FONT_FILE2}"
+    StrCpy $0 "${FONT_NAME2}"
+  ${EndIf}
+!endif
+
+  ${If} $0 == ""
+    StrCpy $0 "$(^Font)"
+  ${EndIf}
+
+  CreateFont $FontBlurb "$0" "12" "500"
+  CreateFont $FontNormal "$0" "11" "500"
+  CreateFont $FontItalic "$0" "11" "500" /ITALIC
 
   InitPluginsDir
   File /oname=$PLUGINSDIR\bgintro.bmp "bgintro.bmp"
   File /oname=$PLUGINSDIR\appname.bmp "appname.bmp"
   File /oname=$PLUGINSDIR\clock.bmp "clock.bmp"
   File /oname=$PLUGINSDIR\particles.bmp "particles.bmp"
 !ifdef ${AB_CD}_rtl
   ; The horizontally flipped pencil looks better in RTL
--- a/browser/locales/en-US/chrome/browser/devtools/connection-screen.dtd
+++ b/browser/locales/en-US/chrome/browser/devtools/connection-screen.dtd
@@ -8,16 +8,17 @@
   - -->
 
 <!ENTITY title      "Connect">
 <!ENTITY header     "Connect to remote device">
 <!ENTITY host       "Host:">
 <!ENTITY port       "Port:">
 <!ENTITY connect    "Connect">
 <!ENTITY connecting "Connecting…">
+<!ENTITY availableAddons "Available remote add-ons:">
 <!ENTITY availableTabs "Available remote tabs:">
 <!ENTITY availableProcesses "Available remote processes:">
 <!ENTITY connectionError "Error:">
 <!ENTITY errorTimeout "Error: connection timeout.">
 <!ENTITY errorRefused "Error: connection refused.">
 <!ENTITY errorUnexpected "Unexpected error.">
 
 <!-- LOCALIZATION NOTE (remoteHelp, remoteDocumentation, remoteHelpSuffix):
--- a/browser/modules/webrtcUI.jsm
+++ b/browser/modules/webrtcUI.jsm
@@ -136,46 +136,49 @@ function prompt(aContentWindow, aCallID,
     // The real callback will be set during the "showing" event. The
     // empty function here is so that PopupNotifications.show doesn't
     // reject the action.
     callback: function() {}
   };
 
   let secondaryActions = [
     {
-      label: stringBundle.getString("getUserMedia.always.label"),
-      accessKey: stringBundle.getString("getUserMedia.always.accesskey"),
-      callback: function () {
-        // don't save unless secure load!
-        mainAction.callback(aSecure);
-      }
-    },
-    {
       label: stringBundle.getString("getUserMedia.denyRequest.label"),
       accessKey: stringBundle.getString("getUserMedia.denyRequest.accesskey"),
       callback: function () {
         denyRequest(aCallID);
       }
     },
     {
       label: stringBundle.getString("getUserMedia.never.label"),
       accessKey: stringBundle.getString("getUserMedia.never.accesskey"),
       callback: function () {
         denyRequest(aCallID);
-	// Let someone save "Never" for http sites so that they can be stopped from
-	// bothering you with doorhangers
+        // Let someone save "Never" for http sites so that they can be stopped from
+        // bothering you with doorhangers.
         let perms = Services.perms;
         if (audioDevices.length)
           perms.add(uri, "microphone", perms.DENY_ACTION);
         if (videoDevices.length)
           perms.add(uri, "camera", perms.DENY_ACTION);
       }
     }
   ];
 
+  if (aSecure) {
+    // Don't show the 'Always' action if the connection isn't secure.
+    secondaryActions.unshift({
+      label: stringBundle.getString("getUserMedia.always.label"),
+      accessKey: stringBundle.getString("getUserMedia.always.accesskey"),
+      callback: function () {
+        mainAction.callback(true);
+      }
+    });
+  }
+
   let options = {
     eventCallback: function(aTopic, aNewBrowser) {
       if (aTopic == "swapping")
         return true;
 
       let chromeDoc = this.browser.ownerDocument;
 
       if (aTopic == "shown") {
--- a/browser/themes/shared/customizableui/panelUIOverlay.inc.css
+++ b/browser/themes/shared/customizableui/panelUIOverlay.inc.css
@@ -30,16 +30,21 @@
   display: block;
   width: 64px;
   height: 64px;
   position: absolute;
   animation: moveX 3.05s linear 0s infinite alternate,
              moveY 3.4s linear 0s infinite alternate;
 }
 
+#PanelUI-popup #PanelUI-contents:-moz-locale-dir(rtl):empty::before {
+  animation: moveXRTL 3.05s linear 0s infinite alternate,
+             moveY 3.4s linear 0s infinite alternate;
+}
+
 #PanelUI-popup #PanelUI-contents:empty:hover::before {
   background-image: url(chrome://browser/skin/customizableui/whimsy.png);
 }
 
 @media (min-resolution: 2dppx) {
   #PanelUI-popup #PanelUI-contents:empty::before {
     background-image: url(chrome://browser/skin/customizableui/whimsy-bw@2x.png);
     background-size: 64px 64px;
@@ -48,16 +53,22 @@
     background-image: url(chrome://browser/skin/customizableui/whimsy@2x.png);
   }
 }
 
 @keyframes moveX {
   /* These values are adjusted for the padding on the panel. */
   from { margin-left: -15px; } to { margin-left: calc(100% - 49px); }
 }
+
+@keyframes moveXRTL {
+  /* These values are adjusted for the padding on the panel. */
+  from { margin-right: -15px; } to { margin-right: calc(100% - 49px); }
+}
+
 @keyframes moveY {
   /* These values are adjusted for the padding and height of the panel. */
   from { margin-top: -.5em; } to { margin-top: calc(64px - .5em); }
 }
 
 #PanelUI-button {
   background-image: linear-gradient(to bottom, hsla(0,0%,100%,0), hsla(0,0%,100%,.3) 30%, hsla(0,0%,100%,.3) 70%, hsla(0,0%,100%,0)),
                     linear-gradient(to bottom, hsla(210,54%,20%,0), hsla(210,54%,20%,.3) 30%, hsla(210,54%,20%,.3) 70%, hsla(210,54%,20%,0)),
--- a/build/autoconf/android.m4
+++ b/build/autoconf/android.m4
@@ -288,29 +288,45 @@ case "$target" in
         fi
     fi
 
     android_tools="$android_sdk_root"/tools
     android_platform_tools="$android_sdk_root"/platform-tools
     if test ! -d "$android_platform_tools" ; then
         android_platform_tools="$android_sdk"/tools # SDK Tools < r8
     fi
-    # The build tools got moved around to different directories in
-    # SDK Tools r22.  Try to locate them.
+
+    dnl The build tools got moved around to different directories in SDK
+    dnl Tools r22. Try to locate them. This is awful, but, from
+    dnl http://stackoverflow.com/a/4495368, the following sorts versions
+    dnl of the form x.y.z.a.b from newest to oldest:
+    dnl sort -t. -k 1,1nr -k 2,2nr -k 3,3nr -k 4,4nr -k 5,5nr
+    dnl We want to favour the newer versions that start with 'android-';
+    dnl that's what the sed is about.
+    dnl We might iterate over directories that aren't build-tools at all;
+    dnl we use the presence of aapt as a marker.
+    AC_MSG_CHECKING([for android build-tools directory])
     android_build_tools=""
-    for suffix in android-4.4 android-4.3 android-4.2.2 19.0.3 19.0.2 19.0.0 18.1.0 18.0.1 18.0.0 17.0.0; do
-        tools_directory="$android_sdk_root/build-tools/$suffix"
-        if test -d "$tools_directory" ; then
+    for suffix in `ls "$android_sdk_root/build-tools" | sed -e "s,android-,999.," | sort -t. -k 1,1nr -k 2,2nr -k 3,3nr -k 4,4nr -k 5,5nr`; do
+        tools_directory=`echo "$android_sdk_root/build-tools/$suffix" | sed -e "s,999.,android-,"`
+        if test -d "$tools_directory" -a -f "$tools_directory/aapt"; then
             android_build_tools="$tools_directory"
             break
         fi
     done
     if test -z "$android_build_tools" ; then
         android_build_tools="$android_platform_tools" # SDK Tools < r22
     fi
+
+    if test -d "$android_build_tools" -a -f "$android_build_tools/aapt"; then
+        AC_MSG_RESULT([$android_build_tools])
+    else
+        AC_MSG_ERROR([not found. Please check your SDK for the subdirectory of build-tools. With the current configuration, it should be in $android_sdk_root/build_tools])
+    fi
+
     ANDROID_SDK="${android_sdk}"
     ANDROID_SDK_ROOT="${android_sdk_root}"
     if test -e "${ANDROID_SDK_ROOT}/extras/android/compatibility/v4/android-support-v4.jar" ; then
         ANDROID_COMPAT_LIB="${ANDROID_SDK_ROOT}/extras/android/compatibility/v4/android-support-v4.jar"
     else
         ANDROID_COMPAT_LIB="${ANDROID_SDK_ROOT}/extras/android/support/v4/android-support-v4.jar";
     fi
     ANDROID_TOOLS="${android_tools}"
--- a/build/mozconfig.cache
+++ b/build/mozconfig.cache
@@ -4,17 +4,17 @@
 
 # Setup for build cache
 
 read branch platform master <<EOF
 $(python2.7 -c 'import json; p = json.loads(open("'"$topsrcdir"'/../buildprops.json").read())["properties"]; print p["branch"], p["platform"], p["master"]' 2> /dev/null)
 EOF
 
 bucket=
-if test -z "$SCCACHE_DISABLE" -a -z "$no_tooltool"; then
+if test -z "$SCCACHE_DISABLE" -a -z "$no_sccache"; then
     case "${branch}_${master}" in
     try_*scl1.mozilla.com*|try_*.scl3.mozilla.com*)
         bucket=mozilla-releng-ceph-cache-scl3-try
         mk_add_options "export SCCACHE_NO_HTTPS=1"
         ;;
     try_*use1.mozilla.com*)
         bucket=mozilla-releng-s3-cache-us-east-1-try
         ;;
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -993,16 +993,17 @@ GK_ATOM(setcookie, "set-cookie")
 GK_ATOM(setter, "setter")
 GK_ATOM(shape, "shape")
 GK_ATOM(show, "show")
 GK_ATOM(showcaret, "showcaret")
 GK_ATOM(showresizer, "showresizer")
 GK_ATOM(simple, "simple")
 GK_ATOM(single, "single")
 GK_ATOM(size, "size")
+GK_ATOM(sizes, "sizes")
 GK_ATOM(sizemode, "sizemode")
 GK_ATOM(sizetopopup, "sizetopopup")
 GK_ATOM(slider, "slider")
 GK_ATOM(small, "small")
 GK_ATOM(smooth, "smooth")
 GK_ATOM(snap, "snap")
 GK_ATOM(sort, "sort")
 GK_ATOM(sortActive, "sortActive")
--- a/content/canvas/public/moz.build
+++ b/content/canvas/public/moz.build
@@ -6,17 +6,16 @@
 
 XPIDL_SOURCES += [
     'nsICanvasGLPrivate.idl',
 ]
 
 XPIDL_MODULE = 'content_canvas'
 
 EXPORTS += [
-    'nsICanvasElementExternal.h',
     'nsICanvasRenderingContextInternal.h',
 ]
 
 EXPORTS.mozilla.ipc += [
     'DocumentRendererChild.h',
     'DocumentRendererParent.h',
 ]
 
deleted file mode 100644
--- a/content/canvas/public/nsICanvasElementExternal.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/* -*- Mode: C++; tab-width: 40; 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/. */
-
-#ifndef nsICanvasElementExternal_h___
-#define nsICanvasElementExternal_h___
-
-#include "nsISupports.h"
-#include "GraphicsFilter.h"
-
-class gfxContext;
-class nsIFrame;
-struct gfxRect;
-
-#define NS_ICANVASELEMENTEXTERNAL_IID \
-  { 0x51870f54, 0x6c4c, 0x469a, {0xad, 0x46, 0xf0, 0xa9, 0x8e, 0x32, 0xa7, 0xe2 } }
-
-class nsRenderingContext;
-class nsICanvasRenderingContextInternal;
-
-struct _cairo_surface;
-
-/*
- * This interface contains methods that are needed outside of the content/layout
- * modules, specifically widget.  It should eventually go away when we support
- * libxul builds, and HTMLCanvasElement be used directly.
- *
- * Code internal to content/layout should /never/ use this interface; if the
- * same functionality is needed in both places, two separate methods should be
- * used.
- */
-
-class nsICanvasElementExternal : public nsISupports {
-public:
-  NS_DECLARE_STATIC_IID_ACCESSOR(NS_ICANVASELEMENTEXTERNAL_IID)
-
-  enum {
-    RenderFlagPremultAlpha = 0x1
-  };
-
-  /**
-   * Get the size in pixels of this canvas element
-   */
-  NS_IMETHOD_(nsIntSize) GetSizeExternal() = 0;
-
-  /*
-   * Ask the canvas element to tell the contexts to render themselves
-   * to the given gfxContext at the origin of its coordinate space.
-   */
-  NS_IMETHOD RenderContextsExternal(gfxContext *ctx,
-                                    GraphicsFilter aFilter,
-                                    uint32_t aFlags = RenderFlagPremultAlpha) = 0;
-};
-
-NS_DEFINE_STATIC_IID_ACCESSOR(nsICanvasElementExternal, NS_ICANVASELEMENTEXTERNAL_IID)
-
-#endif /* nsICanvasElementExternal_h___ */
--- a/content/canvas/public/nsICanvasRenderingContextInternal.h
+++ b/content/canvas/public/nsICanvasRenderingContextInternal.h
@@ -1,26 +1,27 @@
 /* -*- Mode: C++; tab-width: 40; 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/. */
 
 #ifndef nsICanvasRenderingContextInternal_h___
 #define nsICanvasRenderingContextInternal_h___
 
+#include "mozilla/gfx/2D.h"
 #include "nsISupports.h"
 #include "nsIInputStream.h"
 #include "nsIDocShell.h"
 #include "mozilla/dom/HTMLCanvasElement.h"
 #include "GraphicsFilter.h"
 #include "mozilla/RefPtr.h"
 
 #define NS_ICANVASRENDERINGCONTEXTINTERNAL_IID \
-{ 0x9a6a5bdf, 0x1261, 0x4057, \
-  { 0x85, 0xcc, 0xaf, 0x97, 0x6c, 0x36, 0x99, 0xa9 } }
+{ 0x3cc9e801, 0x1806, 0x4ff6, \
+  { 0x86, 0x14, 0xf9, 0xd0, 0xf4, 0xfb, 0x3b, 0x08 } }
 
 class gfxContext;
 class gfxASurface;
 class nsDisplayListBuilder;
 
 namespace mozilla {
 namespace layers {
 class CanvasLayer;
@@ -36,20 +37,16 @@ class SourceSurface;
 
 class nsICanvasRenderingContextInternal : public nsISupports {
 public:
   typedef mozilla::layers::CanvasLayer CanvasLayer;
   typedef mozilla::layers::LayerManager LayerManager;
 
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_ICANVASRENDERINGCONTEXTINTERNAL_IID)
 
-  enum {
-    RenderFlagPremultAlpha = 0x1
-  };
-
   void SetCanvasElement(mozilla::dom::HTMLCanvasElement* aParentCanvas)
   {
     mCanvasElement = aParentCanvas;
   }
   mozilla::dom::HTMLCanvasElement* GetParentObject() const
   {
     return mCanvasElement;
   }
@@ -61,41 +58,35 @@ public:
 #endif
 
   // Sets the dimensions of the canvas, in pixels.  Called
   // whenever the size of the element changes.
   NS_IMETHOD SetDimensions(int32_t width, int32_t height) = 0;
 
   NS_IMETHOD InitializeWithSurface(nsIDocShell *docShell, gfxASurface *surface, int32_t width, int32_t height) = 0;
 
-  // Render the canvas at the origin of the given gfxContext
-  NS_IMETHOD Render(gfxContext *ctx,
-                    GraphicsFilter aFilter,
-                    uint32_t aFlags = RenderFlagPremultAlpha) = 0;
-
   // Creates an image buffer. Returns null on failure.
   virtual void GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat) = 0;
 
   // Gives you a stream containing the image represented by this context.
   // The format is given in aMimeTime, for example "image/png".
   //
   // If the image format does not support transparency or aIncludeTransparency
   // is false, alpha will be discarded and the result will be the image
   // composited on black.
   NS_IMETHOD GetInputStream(const char *aMimeType,
                             const char16_t *aEncoderOptions,
                             nsIInputStream **aStream) = 0;
-
-  // If this canvas context can be represented with a simple Thebes surface,
-  // return the surface.  Otherwise returns an error.
-  NS_IMETHOD GetThebesSurface(gfxASurface **surface) = 0;
   
   // This gets an Azure SourceSurface for the canvas, this will be a snapshot
   // of the canvas at the time it was called.
-  virtual mozilla::TemporaryRef<mozilla::gfx::SourceSurface> GetSurfaceSnapshot() = 0;
+  // If aPremultAlpha is provided, then it assumed the callee can handle
+  // un-premultiplied surfaces, and *aPremultAlpha will be set to false
+  // if one is returned.
+  virtual mozilla::TemporaryRef<mozilla::gfx::SourceSurface> GetSurfaceSnapshot(bool* aPremultAlpha = nullptr) = 0;
 
   // If this context is opaque, the backing store of the canvas should
   // be created as opaque; all compositing operators should assume the
   // dst alpha is always 1.0.  If this is never called, the context
   // defaults to false (not opaque).
   NS_IMETHOD SetIsOpaque(bool isOpaque) = 0;
   virtual bool GetIsOpaque() = 0;
 
--- a/content/canvas/src/CanvasRenderingContext2D.cpp
+++ b/content/canvas/src/CanvasRenderingContext2D.cpp
@@ -1049,61 +1049,16 @@ CanvasRenderingContext2D::SetIsIPC(bool 
     mIPC = isIPC;
     ClearTarget();
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-CanvasRenderingContext2D::Render(gfxContext *ctx, GraphicsFilter aFilter, uint32_t aFlags)
-{
-  nsresult rv = NS_OK;
-
-  EnsureTarget();
-  if (!IsTargetValid()) {
-    return NS_ERROR_FAILURE;
-  }
-
-  nsRefPtr<gfxASurface> surface;
-
-  if (NS_FAILED(GetThebesSurface(getter_AddRefs(surface)))) {
-    return NS_ERROR_FAILURE;
-  }
-
-  nsRefPtr<gfxPattern> pat = new gfxPattern(surface);
-
-  pat->SetFilter(aFilter);
-  pat->SetExtend(gfxPattern::EXTEND_PAD);
-
-  gfxContext::GraphicsOperator op = ctx->CurrentOperator();
-  if (mOpaque)
-      ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
-
-  // XXX I don't want to use PixelSnapped here, but layout doesn't guarantee
-  // pixel alignment for this stuff!
-  ctx->NewPath();
-  ctx->PixelSnappedRectangleAndSetPattern(gfxRect(0, 0, mWidth, mHeight), pat);
-  ctx->Fill();
-
-  if (mOpaque)
-      ctx->SetOperator(op);
-
-  if (!(aFlags & RenderFlagPremultAlpha)) {
-      nsRefPtr<gfxASurface> curSurface = ctx->CurrentSurface();
-      nsRefPtr<gfxImageSurface> gis = curSurface->GetAsImageSurface();
-      MOZ_ASSERT(gis, "If non-premult alpha, must be able to get image surface!");
-
-      gfxUtils::UnpremultiplyImageSurface(gis);
-  }
-
-  return rv;
-}
-
-NS_IMETHODIMP
 CanvasRenderingContext2D::SetContextOptions(JSContext* aCx, JS::Handle<JS::Value> aOptions)
 {
   if (aOptions.isNullOrUndefined()) {
     return NS_OK;
   }
 
   ContextAttributes2D attributes;
   NS_ENSURE_TRUE(attributes.Init(aCx, aOptions), NS_ERROR_UNEXPECTED);
@@ -3261,38 +3216,16 @@ CanvasRenderingContext2D::DrawImage(cons
   if (image.IsHTMLCanvasElement()) {
     HTMLCanvasElement* canvas = &image.GetAsHTMLCanvasElement();
     element = canvas;
     nsIntSize size = canvas->GetSize();
     if (size.width == 0 || size.height == 0) {
       error.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
       return;
     }
-
-    // Special case for Canvas, which could be an Azure canvas!
-    nsICanvasRenderingContextInternal *srcCanvas = canvas->GetContextAtIndex(0);
-    if (srcCanvas == this) {
-      // Self-copy.
-      srcSurf = mTarget->Snapshot();
-      imgSize = gfxIntSize(mWidth, mHeight);
-    } else if (srcCanvas) {
-      // This might not be an Azure canvas!
-      srcSurf = srcCanvas->GetSurfaceSnapshot();
-
-      if (srcSurf) {
-        if (mCanvasElement) {
-          // Do security check here.
-          CanvasUtils::DoDrawImageSecurityCheck(mCanvasElement,
-                                                element->NodePrincipal(),
-                                                canvas->IsWriteOnly(),
-                                                false);
-        }
-        imgSize = gfxIntSize(srcSurf->GetSize().width, srcSurf->GetSize().height);
-      }
-    }
   } else {
     if (image.IsHTMLImageElement()) {
       HTMLImageElement* img = &image.GetAsHTMLImageElement();
       element = img;
     } else {
       HTMLVideoElement* video = &image.GetAsHTMLVideoElement();
       element = video;
     }
@@ -4142,37 +4075,16 @@ CanvasRenderingContext2D::PutImageData_e
                                dirtyRect.width, dirtyRect.height),
                        IntPoint(dirtyRect.x, dirtyRect.y));
 
   Redraw(mgfx::Rect(dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height));
 
   return NS_OK;
 }
 
-NS_IMETHODIMP
-CanvasRenderingContext2D::GetThebesSurface(gfxASurface **surface)
-{
-  EnsureTarget();
-  if (!IsTargetValid()) {
-    return NS_ERROR_FAILURE;
-  }
-
-  nsRefPtr<gfxASurface> thebesSurface =
-      gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(mTarget);
-
-  if (!thebesSurface) {
-    return NS_ERROR_FAILURE;
-  }
-
-  *surface = thebesSurface;
-  NS_ADDREF(*surface);
-
-  return NS_OK;
-}
-
 static already_AddRefed<ImageData>
 CreateImageData(JSContext* cx, CanvasRenderingContext2D* context,
                 uint32_t w, uint32_t h, ErrorResult& error)
 {
   if (w == 0)
       w = 1;
   if (h == 0)
       h = 1;
--- a/content/canvas/src/CanvasRenderingContext2D.h
+++ b/content/canvas/src/CanvasRenderingContext2D.h
@@ -452,26 +452,28 @@ public:
 #ifdef DEBUG
     virtual int32_t GetWidth() const MOZ_OVERRIDE;
     virtual int32_t GetHeight() const MOZ_OVERRIDE;
 #endif
   // nsICanvasRenderingContextInternal
   NS_IMETHOD SetDimensions(int32_t width, int32_t height) MOZ_OVERRIDE;
   NS_IMETHOD InitializeWithSurface(nsIDocShell *shell, gfxASurface *surface, int32_t width, int32_t height) MOZ_OVERRIDE;
 
-  NS_IMETHOD Render(gfxContext *ctx,
-                    GraphicsFilter aFilter,
-                    uint32_t aFlags = RenderFlagPremultAlpha) MOZ_OVERRIDE;
   NS_IMETHOD GetInputStream(const char* aMimeType,
                             const char16_t* aEncoderOptions,
                             nsIInputStream **aStream) MOZ_OVERRIDE;
-  NS_IMETHOD GetThebesSurface(gfxASurface **surface) MOZ_OVERRIDE;
 
-  mozilla::TemporaryRef<mozilla::gfx::SourceSurface> GetSurfaceSnapshot() MOZ_OVERRIDE
-  { EnsureTarget(); return mTarget->Snapshot(); }
+  mozilla::TemporaryRef<mozilla::gfx::SourceSurface> GetSurfaceSnapshot(bool* aPremultAlpha = nullptr) MOZ_OVERRIDE
+  {
+    EnsureTarget();
+    if (aPremultAlpha) {
+      *aPremultAlpha = true;
+    }
+    return mTarget->Snapshot();
+  }
 
   NS_IMETHOD SetIsOpaque(bool isOpaque) MOZ_OVERRIDE;
   bool GetIsOpaque() MOZ_OVERRIDE { return mOpaque; }
   NS_IMETHOD Reset() MOZ_OVERRIDE;
   already_AddRefed<CanvasLayer> GetCanvasLayer(nsDisplayListBuilder* aBuilder,
                                                CanvasLayer *aOldLayer,
                                                LayerManager *aManager) MOZ_OVERRIDE;
   virtual bool ShouldForceInactiveLayer(LayerManager *aManager) MOZ_OVERRIDE;
--- a/content/canvas/src/WebGLContext.cpp
+++ b/content/canvas/src/WebGLContext.cpp
@@ -131,35 +131,16 @@ WebGLContext::WebGLContext()
     mFakeVertexAttrib0BufferObjectVector[0] = 0;
     mFakeVertexAttrib0BufferObjectVector[1] = 0;
     mFakeVertexAttrib0BufferObjectVector[2] = 0;
     mFakeVertexAttrib0BufferObjectVector[3] = 1;
     mFakeVertexAttrib0BufferObjectSize = 0;
     mFakeVertexAttrib0BufferObject = 0;
     mFakeVertexAttrib0BufferStatus = WebGLVertexAttrib0Status::Default;
 
-    // these are de default values, see 6.2 State tables in the OpenGL ES 2.0.25 spec
-    mColorWriteMask[0] = 1;
-    mColorWriteMask[1] = 1;
-    mColorWriteMask[2] = 1;
-    mColorWriteMask[3] = 1;
-    mDepthWriteMask = 1;
-    mColorClearValue[0] = 0.f;
-    mColorClearValue[1] = 0.f;
-    mColorClearValue[2] = 0.f;
-    mColorClearValue[3] = 0.f;
-    mDepthClearValue = 1.f;
-    mStencilClearValue = 0;
-    mStencilRefFront = 0;
-    mStencilRefBack = 0;
-    mStencilValueMaskFront = 0xffffffff;
-    mStencilValueMaskBack  = 0xffffffff;
-    mStencilWriteMaskFront = 0xffffffff;
-    mStencilWriteMaskBack  = 0xffffffff;
-
     mViewportX = 0;
     mViewportY = 0;
     mViewportWidth = 0;
     mViewportHeight = 0;
 
     mScissorTestEnabled = 0;
     mDitherEnabled = 1;
     mRasterizerDiscardEnabled = 0; // OpenGL ES 3.0 spec p244
@@ -204,17 +185,17 @@ WebGLContext::WebGLContext()
         GenerateWarning("webgl.max-warnings-per-context size is too large (seems like a negative value wrapped)");
         mMaxWarnings = 0;
     }
 
     mLastUseIndex = 0;
 
     InvalidateBufferFetching();
 
-    mIsScreenCleared = false;
+    mBackbufferNeedsClear = true;
 
     mDisableFragHighP = false;
 
     mDrawCallsSinceLastFlush = 0;
 }
 
 WebGLContext::~WebGLContext()
 {
@@ -418,17 +399,17 @@ WebGLContext::SetDimensions(int32_t widt
         gl->ResizeOffscreen(gfx::IntSize(width, height)); // Doesn't matter if it succeeds (soft-fail)
         // It's unlikely that we'll get a proper-sized context if we recreate if we didn't on resize
 
         // everything's good, we're done here
         mWidth = gl->OffscreenSize().width;
         mHeight = gl->OffscreenSize().height;
         mResetLayer = true;
 
-        ClearScreen();
+        mBackbufferNeedsClear = true;
 
         return NS_OK;
     }
 
     // End of early return cases.
     // At this point we know that we're not just resizing an existing context,
     // we are initializing a new context.
 
@@ -605,71 +586,52 @@ WebGLContext::SetDimensions(int32_t widt
     // Make sure that we clear this out, otherwise
     // we'll end up displaying random memory
     gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
 
     gl->fClearColor(0.0f, 0.0f, 0.0f, 0.0f);
     gl->fClearDepth(1.0f);
     gl->fClearStencil(0);
 
-    gl->ClearSafely();
+    mBackbufferNeedsClear = true;
+
+    // Clear immediately, because we need to present the cleared initial
+    // buffer.
+    ClearBackbufferIfNeeded();
 
     mShouldPresent = true;
 
     MOZ_ASSERT(gl->Caps().color == caps.color);
     MOZ_ASSERT(gl->Caps().alpha == caps.alpha);
     MOZ_ASSERT(gl->Caps().depth == caps.depth || !gl->Caps().depth);
     MOZ_ASSERT(gl->Caps().stencil == caps.stencil || !gl->Caps().stencil);
     MOZ_ASSERT(gl->Caps().antialias == caps.antialias || !gl->Caps().antialias);
     MOZ_ASSERT(gl->Caps().preserve == caps.preserve);
 
     reporter.SetSuccessful();
     return NS_OK;
 }
 
-NS_IMETHODIMP
-WebGLContext::Render(gfxContext *ctx, GraphicsFilter f, uint32_t aFlags)
+void
+WebGLContext::ClearBackbufferIfNeeded()
 {
-    if (!gl)
-        return NS_OK;
+    if (!mBackbufferNeedsClear)
+        return;
 
-    nsRefPtr<gfxImageSurface> surf = new gfxImageSurface(gfxIntSize(mWidth, mHeight),
-                                                         gfxImageFormat::ARGB32);
-    if (surf->CairoStatus() != 0)
-        return NS_ERROR_FAILURE;
-
+#ifdef DEBUG
     gl->MakeCurrent();
-    ReadScreenIntoImageSurface(gl, surf);
-
-    bool srcPremultAlpha = mOptions.premultipliedAlpha;
-    bool dstPremultAlpha = aFlags & RenderFlagPremultAlpha;
 
-    if (!srcPremultAlpha && dstPremultAlpha) {
-        gfxUtils::PremultiplyImageSurface(surf);
-    } else if (srcPremultAlpha && !dstPremultAlpha) {
-        gfxUtils::UnpremultiplyImageSurface(surf);
-    }
-    surf->MarkDirty();
-
-    nsRefPtr<gfxPattern> pat = new gfxPattern(surf);
-    pat->SetFilter(f);
+    GLuint fb = 0;
+    gl->GetUIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING, &fb);
+    MOZ_ASSERT(fb == 0);
+#endif
 
-    // Pixels from ReadPixels will be "upside down" compared to
-    // what cairo wants, so draw with a y-flip and a translte to
-    // flip them.
-    gfxMatrix m;
-    m.Translate(gfxPoint(0.0, mHeight));
-    m.Scale(1.0, -1.0);
-    pat->SetMatrix(m);
+    ClearScreen();
 
-    ctx->NewPath();
-    ctx->PixelSnappedRectangleAndSetPattern(gfxRect(0, 0, mWidth, mHeight), pat);
-    ctx->Fill();
-
-    return NS_OK;
+    mBackbufferNeedsClear = false;
 }
 
 void WebGLContext::LoseOldestWebGLContextIfLimitExceeded()
 {
 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
     // some mobile devices can't have more than 8 GL contexts overall
     const size_t kMaxWebGLContextsPerPrincipal = 2;
     const size_t kMaxWebGLContexts             = 4;
@@ -753,54 +715,53 @@ void WebGLContext::LoseOldestWebGLContex
 }
 
 void
 WebGLContext::GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat)
 {
     *aImageBuffer = nullptr;
     *aFormat = 0;
 
-    nsRefPtr<gfxImageSurface> imgsurf =
-        new gfxImageSurface(gfxIntSize(mWidth, mHeight),
-                            gfxImageFormat::ARGB32);
+    // Use GetSurfaceSnapshot() to make sure that appropriate y-flip gets applied
+    bool premult;
+    RefPtr<SourceSurface> snapshot =
+      GetSurfaceSnapshot(mOptions.premultipliedAlpha ? nullptr : &premult);
+    if (!snapshot) {
+        return;
+    }
+    MOZ_ASSERT(mOptions.premultipliedAlpha || !premult, "We must get unpremult when we ask for it!");
 
-    if (!imgsurf || imgsurf->CairoStatus()) {
+    RefPtr<DataSourceSurface> dataSurface = snapshot->GetDataSurface();
+
+    DataSourceSurface::MappedSurface map;
+    if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map)) {
         return;
     }
 
-    nsRefPtr<gfxContext> ctx = new gfxContext(imgsurf);
-    if (!ctx || ctx->HasError()) {
+    static const fallible_t fallible = fallible_t();
+    uint8_t* imageBuffer = new (fallible) uint8_t[mWidth * mHeight * 4];
+    if (!imageBuffer) {
+        dataSurface->Unmap();
         return;
     }
+    memcpy(imageBuffer, map.mData, mWidth * mHeight * 4);
 
-    // Use Render() to make sure that appropriate y-flip gets applied
-    uint32_t flags = mOptions.premultipliedAlpha ? RenderFlagPremultAlpha : 0;
-    nsresult rv = Render(ctx, GraphicsFilter::FILTER_NEAREST, flags);
-    if (NS_FAILED(rv)) {
-        return;
-    }
+    dataSurface->Unmap();
 
     int32_t format = imgIEncoder::INPUT_FORMAT_HOSTARGB;
     if (!mOptions.premultipliedAlpha) {
         // We need to convert to INPUT_FORMAT_RGBA, otherwise
         // we are automatically considered premult, and unpremult'd.
         // Yes, it is THAT silly.
         // Except for different lossy conversions by color,
         // we could probably just change the label, and not change the data.
-        gfxUtils::ConvertBGRAtoRGBA(imgsurf);
+        gfxUtils::ConvertBGRAtoRGBA(imageBuffer, mWidth * mHeight * 4);
         format = imgIEncoder::INPUT_FORMAT_RGBA;
     }
 
-    static const fallible_t fallible = fallible_t();
-    uint8_t* imageBuffer = new (fallible) uint8_t[mWidth * mHeight * 4];
-    if (!imageBuffer) {
-        return;
-    }
-    memcpy(imageBuffer, imgsurf->Data(), mWidth * mHeight * 4);
-
     *aImageBuffer = imageBuffer;
     *aFormat = format;
 }
 
 NS_IMETHODIMP
 WebGLContext::GetInputStream(const char* aMimeType,
                              const char16_t* aEncoderOptions,
                              nsIInputStream **aStream)
@@ -822,22 +783,16 @@ WebGLContext::GetInputStream(const char*
     if (!imageBuffer) {
         return NS_ERROR_FAILURE;
     }
 
     return ImageEncoder::GetInputStream(mWidth, mHeight, imageBuffer, format,
                                         encoder, aEncoderOptions, aStream);
 }
 
-NS_IMETHODIMP
-WebGLContext::GetThebesSurface(gfxASurface **surface)
-{
-    return NS_ERROR_NOT_AVAILABLE;
-}
-
 void WebGLContext::UpdateLastUseIndex()
 {
     static CheckedInt<uint64_t> sIndex = 0;
 
     sIndex++;
 
     // should never happen with 64-bit; trying to handle this would be riskier than
     // not handling it as the handler code would never get exercised.
@@ -1009,17 +964,16 @@ WebGLContext::ClearScreen()
     if (mOptions.depth)
         clearMask |= LOCAL_GL_DEPTH_BUFFER_BIT;
     if (mOptions.stencil)
         clearMask |= LOCAL_GL_STENCIL_BUFFER_BIT;
 
     colorAttachmentsMask[0] = true;
 
     ForceClearFramebufferWithDefaultValues(clearMask, colorAttachmentsMask);
-    mIsScreenCleared = true;
 }
 
 #ifdef DEBUG
 // For NaNs, etc.
 static bool IsShadowCorrect(float shadow, float actual) {
     if (IsNaN(shadow)) {
         // GL is allowed to do anything it wants for NaNs, so if we're shadowing
         // a NaN, then whatever `actual` is might be correct.
@@ -1197,23 +1151,24 @@ WebGLContext::PresentScreenBuffer()
         return false;
     }
 
     if (!mShouldPresent) {
         return false;
     }
 
     gl->MakeCurrent();
+    MOZ_ASSERT(!mBackbufferNeedsClear);
     if (!gl->PublishFrame()) {
         this->ForceLoseContext();
         return false;
     }
 
     if (!mOptions.preserveDrawingBuffer) {
-        ClearScreen();
+        mBackbufferNeedsClear = true;
     }
 
     mShouldPresent = false;
 
     return true;
 }
 
 void
@@ -1371,19 +1326,71 @@ WebGLContext::ForceRestoreContext()
 {
     mContextStatus = ContextLostAwaitingRestore;
 }
 
 void
 WebGLContext::MakeContextCurrent() const { gl->MakeCurrent(); }
 
 mozilla::TemporaryRef<mozilla::gfx::SourceSurface>
-WebGLContext::GetSurfaceSnapshot()
+WebGLContext::GetSurfaceSnapshot(bool* aPremultAlpha)
 {
-  return nullptr;
+    if (!gl)
+        return nullptr;
+
+    nsRefPtr<gfxImageSurface> surf = new gfxImageSurface(gfxIntSize(mWidth, mHeight),
+                                                         gfxImageFormat::ARGB32,
+                                                         mWidth * 4, 0, false);
+    if (surf->CairoStatus() != 0) {
+        return nullptr;
+    }
+
+    gl->MakeCurrent();
+    {
+        ScopedBindFramebuffer autoFB(gl, 0);
+        ClearBackbufferIfNeeded();
+        ReadPixelsIntoImageSurface(gl, surf);
+    }
+
+    if (aPremultAlpha) {
+        *aPremultAlpha = true;
+    }
+    bool srcPremultAlpha = mOptions.premultipliedAlpha;
+    if (!srcPremultAlpha) {
+        if (aPremultAlpha) {
+            *aPremultAlpha = false;
+        } else {
+            gfxUtils::PremultiplyImageSurface(surf);
+            surf->MarkDirty();
+        }
+    }
+
+    RefPtr<DrawTarget> dt =
+        Factory::CreateDrawTarget(BackendType::CAIRO,
+                                  IntSize(mWidth, mHeight),
+                                  SurfaceFormat::B8G8R8A8);
+
+    if (!dt) {
+        return nullptr;
+    }
+
+    RefPtr<SourceSurface> source = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(dt, surf);
+
+    Matrix m;
+    m.Translate(0.0, mHeight);
+    m.Scale(1.0, -1.0);
+    dt->SetTransform(m);
+
+    dt->DrawSurface(source,
+                    Rect(0, 0, mWidth, mHeight),
+                    Rect(0, 0, mWidth, mHeight),
+                    DrawSurfaceOptions(),
+                    DrawOptions(1.0f, CompositionOp::OP_SOURCE));
+
+    return dt->Snapshot();
 }
 
 //
 // XPCOM goop
 //
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(WebGLContext)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(WebGLContext)
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -160,25 +160,21 @@ public:
     virtual int32_t GetWidth() const MOZ_OVERRIDE;
     virtual int32_t GetHeight() const MOZ_OVERRIDE;
 #endif
     NS_IMETHOD SetDimensions(int32_t width, int32_t height) MOZ_OVERRIDE;
     NS_IMETHOD InitializeWithSurface(nsIDocShell *docShell, gfxASurface *surface, int32_t width, int32_t height) MOZ_OVERRIDE
         { return NS_ERROR_NOT_IMPLEMENTED; }
     NS_IMETHOD Reset() MOZ_OVERRIDE
         { /* (InitializeWithSurface) */ return NS_ERROR_NOT_IMPLEMENTED; }
-    NS_IMETHOD Render(gfxContext *ctx,
-                      GraphicsFilter f,
-                      uint32_t aFlags = RenderFlagPremultAlpha) MOZ_OVERRIDE;
     virtual void GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat);
     NS_IMETHOD GetInputStream(const char* aMimeType,
                               const char16_t* aEncoderOptions,
                               nsIInputStream **aStream) MOZ_OVERRIDE;
-    NS_IMETHOD GetThebesSurface(gfxASurface **surface) MOZ_OVERRIDE;
-    mozilla::TemporaryRef<mozilla::gfx::SourceSurface> GetSurfaceSnapshot() MOZ_OVERRIDE;
+    mozilla::TemporaryRef<mozilla::gfx::SourceSurface> GetSurfaceSnapshot(bool* aPremultAlpha) MOZ_OVERRIDE;
 
     NS_IMETHOD SetIsOpaque(bool b) MOZ_OVERRIDE { return NS_OK; };
     bool GetIsOpaque() MOZ_OVERRIDE { return false; }
     NS_IMETHOD SetContextOptions(JSContext* aCx,
                                  JS::Handle<JS::Value> aOptions) MOZ_OVERRIDE;
 
     NS_IMETHOD SetIsIPC(bool b) MOZ_OVERRIDE { return NS_ERROR_NOT_IMPLEMENTED; }
     NS_IMETHOD Redraw(const gfxRect&) { return NS_ERROR_NOT_IMPLEMENTED; }
@@ -238,16 +234,17 @@ public:
     // This is similar to GLContext::ClearSafely, but tries to minimize the
     // amount of work it does.
     // It only clears the buffers we specify, and can reset its state without
     // first having to query anything, as WebGL knows its state at all times.
     void ForceClearFramebufferWithDefaultValues(GLbitfield mask, const bool colorAttachmentsMask[sMaxColorAttachments]);
 
     // Calls ForceClearFramebufferWithDefaultValues() for the Context's 'screen'.
     void ClearScreen();
+    void ClearBackbufferIfNeeded();
 
     bool MinCapabilityMode() const { return mMinCapability; }
 
     void RobustnessTimerCallback(nsITimer* timer);
     static void RobustnessTimerCallbackStatic(nsITimer* timer, void *thisPointer);
     void SetupContextLossTimer();
     void TerminateContextLossTimer();
 
@@ -836,17 +833,17 @@ protected:
     bool mOptionsFrozen;
     bool mMinCapability;
     bool mDisableExtensions;
     bool mHasRobustness;
     bool mIsMesa;
     bool mLoseContextOnHeapMinimize;
     bool mCanLoseContextInForeground;
     bool mShouldPresent;
-    bool mIsScreenCleared;
+    bool mBackbufferNeedsClear;
     bool mDisableFragHighP;
 
     template<typename WebGLObjectType>
     void DeleteWebGLObjectsArray(nsTArray<WebGLObjectType>& array);
 
     GLuint mActiveTexture;
 
     // glGetError sources:
@@ -1029,17 +1026,17 @@ protected:
     nsLayoutUtils::SurfaceFromElementResult SurfaceFromElement(ElementType* aElement) {
         MOZ_ASSERT(aElement);
         uint32_t flags =
              nsLayoutUtils::SFE_WANT_IMAGE_SURFACE;
 
         if (mPixelStoreColorspaceConversion == LOCAL_GL_NONE)
             flags |= nsLayoutUtils::SFE_NO_COLORSPACE_CONVERSION;
         if (!mPixelStorePremultiplyAlpha)
-            flags |= nsLayoutUtils::SFE_NO_PREMULTIPLY_ALPHA;
+            flags |= nsLayoutUtils::SFE_PREFER_NO_PREMULTIPLY_ALPHA;
         return nsLayoutUtils::SurfaceFromElement(aElement, flags);
     }
     template<class ElementType>
     nsLayoutUtils::SurfaceFromElementResult SurfaceFromElement(ElementType& aElement)
     {
       return SurfaceFromElement(&aElement);
     }
 
--- a/content/canvas/src/WebGLContextDraw.cpp
+++ b/content/canvas/src/WebGLContextDraw.cpp
@@ -98,16 +98,18 @@ bool WebGLContext::DrawArrays_check(GLin
 
     MakeContextCurrent();
 
     if (mBoundFramebuffer) {
         if (!mBoundFramebuffer->CheckAndInitializeAttachments()) {
             ErrorInvalidFramebufferOperation("%s: incomplete framebuffer", info);
             return false;
         }
+    } else {
+        ClearBackbufferIfNeeded();
     }
 
     if (!DoFakeVertexAttrib0(checked_firstPlusCount.value())) {
         return false;
     }
     BindFakeBlackTextures();
 
     return true;
@@ -259,16 +261,18 @@ WebGLContext::DrawElements_check(GLsizei
 
     MakeContextCurrent();
 
     if (mBoundFramebuffer) {
         if (!mBoundFramebuffer->CheckAndInitializeAttachments()) {
             ErrorInvalidFramebufferOperation("%s: incomplete framebuffer", info);
             return false;
         }
+    } else {
+        ClearBackbufferIfNeeded();
     }
 
     if (!DoFakeVertexAttrib0(mMaxFetchedVertices)) {
         return false;
     }
     BindFakeBlackTextures();
 
     return true;
@@ -328,17 +332,17 @@ WebGLContext::DrawElementsInstanced(GLen
 void WebGLContext::Draw_cleanup()
 {
     UndoFakeVertexAttrib0();
     UnbindFakeBlackTextures();
 
     if (!mBoundFramebuffer) {
         Invalidate();
         mShouldPresent = true;
-        mIsScreenCleared = false;
+        MOZ_ASSERT(!mBackbufferNeedsClear);
     }
 
     if (gl->WorkAroundDriverBugs()) {
         if (gl->Renderer() == gl::GLRenderer::Tegra) {
             mDrawCallsSinceLastFlush++;
 
             if (mDrawCallsSinceLastFlush >= MAX_DRAW_CALLS_SINCE_FLUSH) {
                 gl->fFlush();
--- a/content/canvas/src/WebGLContextFramebufferOperations.cpp
+++ b/content/canvas/src/WebGLContextFramebufferOperations.cpp
@@ -30,53 +30,23 @@ WebGLContext::Clear(GLbitfield mask)
     }
 
     if (mBoundFramebuffer) {
         if (!mBoundFramebuffer->CheckAndInitializeAttachments())
             return ErrorInvalidFramebufferOperation("clear: incomplete framebuffer");
 
         gl->fClear(mask);
         return;
+    } else {
+        ClearBackbufferIfNeeded();
     }
 
     // Ok, we're clearing the default framebuffer/screen.
 
-    bool needsClear = true;
-    if (mIsScreenCleared) {
-        bool isClearRedundant = true;
-        if (mask & LOCAL_GL_COLOR_BUFFER_BIT) {
-            if (mColorClearValue[0] != 0.0f ||
-                mColorClearValue[1] != 0.0f ||
-                mColorClearValue[2] != 0.0f ||
-                mColorClearValue[3] != 0.0f)
-            {
-                isClearRedundant = false;
-            }
-        }
-
-        if (mask & LOCAL_GL_DEPTH_BUFFER_BIT) {
-            if (mDepthClearValue != 1.0f) {
-                isClearRedundant = false;
-            }
-        }
-
-        if (mask & LOCAL_GL_DEPTH_BUFFER_BIT) {
-            if (mStencilClearValue != 0) {
-                isClearRedundant = false;
-            }
-        }
-
-        if (isClearRedundant)
-            needsClear = false;
-    }
-
-    if (needsClear) {
-        gl->fClear(mask);
-        mIsScreenCleared = false;
-    }
+    gl->fClear(mask);
 
     Invalidate();
     mShouldPresent = true;
 }
 
 static GLclampf
 GLClampFloat(GLclampf val)
 {
--- a/content/canvas/src/WebGLContextGL.cpp
+++ b/content/canvas/src/WebGLContextGL.cpp
@@ -24,16 +24,17 @@
 #include "gfxPlatform.h"
 #include "GLContext.h"
 
 #include "nsContentUtils.h"
 #include "nsError.h"
 #include "nsLayoutUtils.h"
 
 #include "CanvasUtils.h"
+#include "gfxUtils.h"
 
 #include "jsfriendapi.h"
 
 #include "WebGLTexelConversions.h"
 #include "WebGLValidateStrings.h"
 #include <algorithm>
 
 // needed to check if current OS is lower than 10.7
@@ -467,16 +468,18 @@ WebGLContext::CopyTexImage2D(GLenum targ
         if (!mBoundFramebuffer->CheckAndInitializeAttachments())
             return ErrorInvalidFramebufferOperation("copyTexImage2D: incomplete framebuffer");
 
         GLenum readPlaneBits = LOCAL_GL_COLOR_BUFFER_BIT;
         if (!mBoundFramebuffer->HasCompletePlanes(readPlaneBits)) {
             return ErrorInvalidOperation("copyTexImage2D: Read source attachment doesn't have the"
                                          " correct color/depth/stencil type.");
         }
+    } else {
+      ClearBackbufferIfNeeded();
     }
 
     bool texFormatRequiresAlpha = internalformat == LOCAL_GL_RGBA ||
                                   internalformat == LOCAL_GL_ALPHA ||
                                   internalformat == LOCAL_GL_LUMINANCE_ALPHA;
     bool fboFormatHasAlpha = mBoundFramebuffer ? mBoundFramebuffer->ColorAttachment(0).HasAlpha()
                                                : bool(gl->GetPixelFormat().alpha > 0);
     if (texFormatRequiresAlpha && !fboFormatHasAlpha)
@@ -579,16 +582,18 @@ WebGLContext::CopyTexSubImage2D(GLenum t
         if (!mBoundFramebuffer->CheckAndInitializeAttachments())
             return ErrorInvalidFramebufferOperation("copyTexSubImage2D: incomplete framebuffer");
 
         GLenum readPlaneBits = LOCAL_GL_COLOR_BUFFER_BIT;
         if (!mBoundFramebuffer->HasCompletePlanes(readPlaneBits)) {
             return ErrorInvalidOperation("copyTexSubImage2D: Read source attachment doesn't have the"
                                          " correct color/depth/stencil type.");
         }
+    } else {
+        ClearBackbufferIfNeeded();
     }
 
     bool texFormatRequiresAlpha = (internalFormat == LOCAL_GL_RGBA ||
                                    internalFormat == LOCAL_GL_ALPHA ||
                                    internalFormat == LOCAL_GL_LUMINANCE_ALPHA);
     bool fboFormatHasAlpha = mBoundFramebuffer ? mBoundFramebuffer->ColorAttachment(0).HasAlpha()
                                                : bool(gl->GetPixelFormat().alpha > 0);
 
@@ -2189,16 +2194,18 @@ WebGLContext::ReadPixels(GLint x, GLint 
         if (!mBoundFramebuffer->CheckAndInitializeAttachments())
             return ErrorInvalidFramebufferOperation("readPixels: incomplete framebuffer");
 
         GLenum readPlaneBits = LOCAL_GL_COLOR_BUFFER_BIT;
         if (!mBoundFramebuffer->HasCompletePlanes(readPlaneBits)) {
             return ErrorInvalidOperation("readPixels: Read source attachment doesn't have the"
                                          " correct color/depth/stencil type.");
         }
+    } else {
+      ClearBackbufferIfNeeded();
     }
     // Now that the errors are out of the way, on to actually reading
 
     // If we won't be reading any pixels anyways, just skip the actual reading
     if (width == 0 || height == 0)
         return DummyFramebufferOperation("readPixels");
 
     if (CanvasUtils::CheckSaneSubrectSize(x, y, width, height, framebufferWidth, framebufferHeight)) {
@@ -2516,16 +2523,20 @@ WebGLContext::SurfaceFromElementResultTo
     if (!res.mSourceSurface)
         return NS_OK;
     RefPtr<DataSourceSurface> data = res.mSourceSurface->GetDataSurface();
     if (!data) {
         // SurfaceFromElement lied!
         return NS_OK;
     }
 
+    if (!mPixelStorePremultiplyAlpha && res.mIsPremultiplied) {
+      data = gfxUtils::UnpremultiplyDataSurface(data);
+    }
+
     // We disallow loading cross-domain images and videos that have not been validated
     // with CORS as WebGL textures. The reason for doing that is that timing
     // attacks on WebGL shaders are able to retrieve approximations of the
     // pixel values in WebGL textures; see bug 655987.
     //
     // To prevent a loophole where a Canvas2D would be used as a proxy to load
     // cross-domain textures, we also disallow loading textures from write-only
     // Canvas2D's.
--- a/content/canvas/src/WebGLContextState.cpp
+++ b/content/canvas/src/WebGLContextState.cpp
@@ -75,19 +75,18 @@ WebGLContext::GetParameter(JSContext* cx
 {
     if (IsContextLost())
         return JS::NullValue();
 
     MakeContextCurrent();
 
     if (MinCapabilityMode()) {
         switch(pname) {
-            //
+            ////////////////////////////
             // Single-value params
-            //
 
             // int
             case LOCAL_GL_MAX_VERTEX_ATTRIBS:
                 return JS::Int32Value(MINVALUE_GL_MAX_VERTEX_ATTRIBS);
 
             case LOCAL_GL_MAX_FRAGMENT_UNIFORM_VECTORS:
                 return JS::Int32Value(MINVALUE_GL_MAX_FRAGMENT_UNIFORM_VECTORS);
 
@@ -113,28 +112,25 @@ WebGLContext::GetParameter(JSContext* cx
                 return JS::Int32Value(MINVALUE_GL_MAX_RENDERBUFFER_SIZE);
 
             default:
                 // Return the real value; we're not overriding this one
                 break;
         }
     }
 
-    if (IsExtensionEnabled(WEBGL_draw_buffers))
-    {
-        if (pname == LOCAL_GL_MAX_COLOR_ATTACHMENTS)
-        {
+    if (IsExtensionEnabled(WEBGL_draw_buffers)) {
+        if (pname == LOCAL_GL_MAX_COLOR_ATTACHMENTS) {
             return JS::Int32Value(mGLMaxColorAttachments);
-        }
-        else if (pname == LOCAL_GL_MAX_DRAW_BUFFERS)
-        {
+
+        } else if (pname == LOCAL_GL_MAX_DRAW_BUFFERS) {
             return JS::Int32Value(mGLMaxDrawBuffers);
-        }
-        else if (pname >= LOCAL_GL_DRAW_BUFFER0 &&
-                 pname < GLenum(LOCAL_GL_DRAW_BUFFER0 + mGLMaxDrawBuffers))
+
+        } else if (pname >= LOCAL_GL_DRAW_BUFFER0 &&
+                   pname < GLenum(LOCAL_GL_DRAW_BUFFER0 + mGLMaxDrawBuffers))
         {
             if (mBoundFramebuffer) {
                 GLint iv = 0;
                 gl->fGetIntegerv(pname, &iv);
                 return JS::Int32Value(iv);
             }
 
             GLint iv = 0;
@@ -144,77 +140,68 @@ WebGLContext::GetParameter(JSContext* cx
                 return JS::Int32Value(LOCAL_GL_BACK);
             }
 
             return JS::Int32Value(LOCAL_GL_NONE);
         }
     }
 
     if (IsExtensionEnabled(OES_vertex_array_object)) {
-        switch (pname) {
-
-            case LOCAL_GL_VERTEX_ARRAY_BINDING:
-            {
-                if (mBoundVertexArray == mDefaultVertexArray){
-                    return WebGLObjectAsJSValue(cx, (WebGLVertexArray *) nullptr, rv);
-                }
-
-                return WebGLObjectAsJSValue(cx, mBoundVertexArray.get(), rv);
+        if (pname == LOCAL_GL_VERTEX_ARRAY_BINDING) {
+            if (mBoundVertexArray == mDefaultVertexArray){
+                return WebGLObjectAsJSValue(cx, (WebGLVertexArray *) nullptr, rv);
             }
 
+            return WebGLObjectAsJSValue(cx, mBoundVertexArray.get(), rv);
         }
     }
 
     switch (pname) {
         //
         // String params
         //
         case LOCAL_GL_VENDOR:
             return StringValue(cx, "Mozilla", rv);
         case LOCAL_GL_RENDERER:
             return StringValue(cx, "Mozilla", rv);
-        case LOCAL_GL_VERSION:
-        {
+        case LOCAL_GL_VERSION: {
             const char* version = 0;
 
             if (IsWebGL2()) {
                 version = "WebGL 2.0";
             } else {
                 version = "WebGL 1.0";
             }
 
             MOZ_ASSERT(version != 0);
             return StringValue(cx, version, rv);
         }
         case LOCAL_GL_SHADING_LANGUAGE_VERSION:
             return StringValue(cx, "WebGL GLSL ES 1.0", rv);
 
             // Privileged string params exposed by WEBGL_debug_renderer_info:
         case UNMASKED_VENDOR_WEBGL:
-        case UNMASKED_RENDERER_WEBGL:
-        {
+        case UNMASKED_RENDERER_WEBGL: {
             // The privilege check is done in WebGLContext::IsExtensionSupported.
             // So here we just have to check that the extension is enabled.
             if (!IsExtensionEnabled(WEBGL_debug_renderer_info)) {
-                ErrorInvalidEnumInfo("getParameter: parameter", pname);
-                return JS::NullValue();
+                break;
             }
             GLenum glstringname = LOCAL_GL_NONE;
             if (pname == UNMASKED_VENDOR_WEBGL) {
                 glstringname = LOCAL_GL_VENDOR;
             } else if (pname == UNMASKED_RENDERER_WEBGL) {
                 glstringname = LOCAL_GL_RENDERER;
             }
             const char* string = reinterpret_cast<const char*>(gl->fGetString(glstringname));
             return StringValue(cx, string, rv);
         }
 
-        //
+        ////////////////////////////////
         // Single-value params
-        //
 
         // unsigned int
         case LOCAL_GL_CULL_FACE_MODE:
         case LOCAL_GL_FRONT_FACE:
         case LOCAL_GL_ACTIVE_TEXTURE:
         case LOCAL_GL_STENCIL_FUNC:
         case LOCAL_GL_STENCIL_FAIL:
         case LOCAL_GL_STENCIL_PASS_DEPTH_FAIL:
@@ -225,18 +212,17 @@ WebGLContext::GetParameter(JSContext* cx
         case LOCAL_GL_STENCIL_BACK_PASS_DEPTH_PASS:
         case LOCAL_GL_DEPTH_FUNC:
         case LOCAL_GL_BLEND_SRC_RGB:
         case LOCAL_GL_BLEND_SRC_ALPHA:
         case LOCAL_GL_BLEND_DST_RGB:
         case LOCAL_GL_BLEND_DST_ALPHA:
         case LOCAL_GL_BLEND_EQUATION_RGB:
         case LOCAL_GL_BLEND_EQUATION_ALPHA:
-        case LOCAL_GL_GENERATE_MIPMAP_HINT:
-        {
+        case LOCAL_GL_GENERATE_MIPMAP_HINT: {
             GLint i = 0;
             gl->fGetIntegerv(pname, &i);
             return JS::NumberValue(uint32_t(i));
         }
         // int
         case LOCAL_GL_STENCIL_CLEAR_VALUE:
         case LOCAL_GL_STENCIL_REF:
         case LOCAL_GL_STENCIL_BACK_REF:
@@ -249,33 +235,30 @@ WebGLContext::GetParameter(JSContext* cx
         case LOCAL_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS:
         case LOCAL_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS:
         case LOCAL_GL_MAX_TEXTURE_IMAGE_UNITS:
         case LOCAL_GL_RED_BITS:
         case LOCAL_GL_GREEN_BITS:
         case LOCAL_GL_BLUE_BITS:
         case LOCAL_GL_ALPHA_BITS:
         case LOCAL_GL_DEPTH_BITS:
-        case LOCAL_GL_STENCIL_BITS:
-        {
+        case LOCAL_GL_STENCIL_BITS: {
             GLint i = 0;
             gl->fGetIntegerv(pname, &i);
             return JS::Int32Value(i);
         }
-        case LOCAL_GL_FRAGMENT_SHADER_DERIVATIVE_HINT:
+        case LOCAL_GL_FRAGMENT_SHADER_DERIVATIVE_HINT: {
             if (IsExtensionEnabled(OES_standard_derivatives)) {
                 GLint i = 0;
                 gl->fGetIntegerv(pname, &i);
                 return JS::Int32Value(i);
+            } else {
+                break;
             }
-            else {
-                ErrorInvalidEnum("getParameter: parameter", pname);
-                return JS::NullValue();
-            }
-
+        }
         case LOCAL_GL_MAX_TEXTURE_SIZE:
             return JS::Int32Value(mGLMaxTextureSize);
 
         case LOCAL_GL_MAX_CUBE_MAP_TEXTURE_SIZE:
             return JS::Int32Value(mGLMaxCubeMapTextureSize);
 
         case LOCAL_GL_MAX_RENDERBUFFER_SIZE:
             return JS::Int32Value(mGLMaxRenderbufferSize);
@@ -286,205 +269,193 @@ WebGLContext::GetParameter(JSContext* cx
         case LOCAL_GL_MAX_FRAGMENT_UNIFORM_VECTORS:
             return JS::Int32Value(mGLMaxFragmentUniformVectors);
 
         case LOCAL_GL_MAX_VARYING_VECTORS:
             return JS::Int32Value(mGLMaxVaryingVectors);
 
         case LOCAL_GL_NUM_COMPRESSED_TEXTURE_FORMATS:
             return JS::Int32Value(0);
-        case LOCAL_GL_COMPRESSED_TEXTURE_FORMATS:
-        {
+        case LOCAL_GL_COMPRESSED_TEXTURE_FORMATS: {
             uint32_t length = mCompressedTextureFormats.Length();
             JSObject* obj = Uint32Array::Create(cx, this, length, mCompressedTextureFormats.Elements());
             if (!obj) {
                 rv = NS_ERROR_OUT_OF_MEMORY;
             }
             return JS::ObjectOrNullValue(obj);
         }
-        case LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS:
-        {
+        case LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS: {
             if (!IsWebGL2()) {
                 break;
             }
             return JS::Int32Value(mGLMaxTransformFeedbackSeparateAttribs);
         }
 
         // unsigned int. here we may have to return very large values like 2^32-1 that can't be represented as
         // javascript integer values. We just return them as doubles and javascript doesn't care.
         case LOCAL_GL_STENCIL_BACK_VALUE_MASK:
         case LOCAL_GL_STENCIL_BACK_WRITEMASK:
         case LOCAL_GL_STENCIL_VALUE_MASK:
-        case LOCAL_GL_STENCIL_WRITEMASK:
-        {
+        case LOCAL_GL_STENCIL_WRITEMASK: {
             GLint i = 0; // the GL api (glGetIntegerv) only does signed ints
             gl->fGetIntegerv(pname, &i);
             GLuint i_unsigned(i); // this is where -1 becomes 2^32-1
             double i_double(i_unsigned); // pass as FP value to allow large values such as 2^32-1.
             return JS::DoubleValue(i_double);
         }
 
         // float
-        case LOCAL_GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT:
+        case LOCAL_GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT: {
             if (IsExtensionEnabled(EXT_texture_filter_anisotropic)) {
                 GLfloat f = 0.f;
                 gl->fGetFloatv(pname, &f);
                 return JS::DoubleValue(f);
             } else {
-                ErrorInvalidEnumInfo("getParameter: parameter", pname);
-                return JS::NullValue();
+                break;
             }
+        }
         case LOCAL_GL_DEPTH_CLEAR_VALUE:
         case LOCAL_GL_LINE_WIDTH:
         case LOCAL_GL_POLYGON_OFFSET_FACTOR:
         case LOCAL_GL_POLYGON_OFFSET_UNITS:
-        case LOCAL_GL_SAMPLE_COVERAGE_VALUE:
-        {
+        case LOCAL_GL_SAMPLE_COVERAGE_VALUE: {
             GLfloat f = 0.f;
             gl->fGetFloatv(pname, &f);
             return JS::DoubleValue(f);
         }
 
         // bool
         case LOCAL_GL_BLEND:
         case LOCAL_GL_DEPTH_TEST:
         case LOCAL_GL_STENCIL_TEST:
         case LOCAL_GL_CULL_FACE:
         case LOCAL_GL_DITHER:
         case LOCAL_GL_POLYGON_OFFSET_FILL:
         case LOCAL_GL_SCISSOR_TEST:
         case LOCAL_GL_SAMPLE_COVERAGE_INVERT:
-        case LOCAL_GL_DEPTH_WRITEMASK:
-        {
+        case LOCAL_GL_DEPTH_WRITEMASK: {
             realGLboolean b = 0;
             gl->fGetBooleanv(pname, &b);
             return JS::BooleanValue(bool(b));
         }
 
         // bool, WebGL-specific
         case UNPACK_FLIP_Y_WEBGL:
             return JS::BooleanValue(mPixelStoreFlipY);
         case UNPACK_PREMULTIPLY_ALPHA_WEBGL:
             return JS::BooleanValue(mPixelStorePremultiplyAlpha);
 
         // uint, WebGL-specific
         case UNPACK_COLORSPACE_CONVERSION_WEBGL:
             return JS::NumberValue(uint32_t(mPixelStoreColorspaceConversion));
 
-        //
+        ////////////////////////////////
         // Complex values
-        //
-        case LOCAL_GL_DEPTH_RANGE: // 2 floats
-        case LOCAL_GL_ALIASED_POINT_SIZE_RANGE: // 2 floats
-        case LOCAL_GL_ALIASED_LINE_WIDTH_RANGE: // 2 floats
-        {
+
+        // 2 floats
+        case LOCAL_GL_DEPTH_RANGE:
+        case LOCAL_GL_ALIASED_POINT_SIZE_RANGE:
+        case LOCAL_GL_ALIASED_LINE_WIDTH_RANGE: {
             GLfloat fv[2] = { 0 };
             gl->fGetFloatv(pname, fv);
             JSObject* obj = Float32Array::Create(cx, this, 2, fv);
             if (!obj) {
                 rv = NS_ERROR_OUT_OF_MEMORY;
             }
             return JS::ObjectOrNullValue(obj);
         }
 
-        case LOCAL_GL_COLOR_CLEAR_VALUE: // 4 floats
-        case LOCAL_GL_BLEND_COLOR: // 4 floats
-        {
+        // 4 floats
+        case LOCAL_GL_COLOR_CLEAR_VALUE:
+        case LOCAL_GL_BLEND_COLOR: {
             GLfloat fv[4] = { 0 };
             gl->fGetFloatv(pname, fv);
             JSObject* obj = Float32Array::Create(cx, this, 4, fv);
             if (!obj) {
                 rv = NS_ERROR_OUT_OF_MEMORY;
             }
             return JS::ObjectOrNullValue(obj);
         }
 
-        case LOCAL_GL_MAX_VIEWPORT_DIMS: // 2 ints
-        {
+        // 2 ints
+        case LOCAL_GL_MAX_VIEWPORT_DIMS: {
             GLint iv[2] = { 0 };
             gl->fGetIntegerv(pname, iv);
             JSObject* obj = Int32Array::Create(cx, this, 2, iv);
             if (!obj) {
                 rv = NS_ERROR_OUT_OF_MEMORY;
             }
             return JS::ObjectOrNullValue(obj);
         }
 
-        case LOCAL_GL_SCISSOR_BOX: // 4 ints
-        case LOCAL_GL_VIEWPORT: // 4 ints
-        {
+        // 4 ints
+        case LOCAL_GL_SCISSOR_BOX:
+        case LOCAL_GL_VIEWPORT: {
             GLint iv[4] = { 0 };
             gl->fGetIntegerv(pname, iv);
             JSObject* obj = Int32Array::Create(cx, this, 4, iv);
             if (!obj) {
                 rv = NS_ERROR_OUT_OF_MEMORY;
             }
             return JS::ObjectOrNullValue(obj);
         }
 
-        case LOCAL_GL_COLOR_WRITEMASK: // 4 bools
-        {
+        // 4 bools
+        case LOCAL_GL_COLOR_WRITEMASK: {
             realGLboolean gl_bv[4] = { 0 };
             gl->fGetBooleanv(pname, gl_bv);
             bool vals[4] = { bool(gl_bv[0]), bool(gl_bv[1]),
                              bool(gl_bv[2]), bool(gl_bv[3]) };
             JS::Rooted<JS::Value> arr(cx);
             if (!ToJSValue(cx, vals, &arr)) {
                 rv = NS_ERROR_OUT_OF_MEMORY;
             }
             return arr;
         }
 
-        case LOCAL_GL_ARRAY_BUFFER_BINDING:
-        {
+        case LOCAL_GL_ARRAY_BUFFER_BINDING: {
             return WebGLObjectAsJSValue(cx, mBoundArrayBuffer.get(), rv);
         }
 
-        case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_BINDING:
-        {
+        case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_BINDING: {
             if (!IsWebGL2()) {
                 break;
             }
             return WebGLObjectAsJSValue(cx, mBoundTransformFeedbackBuffer.get(), rv);
         }
 
-        case LOCAL_GL_ELEMENT_ARRAY_BUFFER_BINDING:
-        {
+        case LOCAL_GL_ELEMENT_ARRAY_BUFFER_BINDING: {
             return WebGLObjectAsJSValue(cx, mBoundVertexArray->mBoundElementArrayBuffer.get(), rv);
         }
 
-        case LOCAL_GL_RENDERBUFFER_BINDING:
-        {
+        case LOCAL_GL_RENDERBUFFER_BINDING: {
             return WebGLObjectAsJSValue(cx, mBoundRenderbuffer.get(), rv);
         }
 
-        case LOCAL_GL_FRAMEBUFFER_BINDING:
-        {
+        case LOCAL_GL_FRAMEBUFFER_BINDING: {
             return WebGLObjectAsJSValue(cx, mBoundFramebuffer.get(), rv);
         }
 
-        case LOCAL_GL_CURRENT_PROGRAM:
-        {
+        case LOCAL_GL_CURRENT_PROGRAM: {
             return WebGLObjectAsJSValue(cx, mCurrentProgram.get(), rv);
         }
 
-        case LOCAL_GL_TEXTURE_BINDING_2D:
-        {
+        case LOCAL_GL_TEXTURE_BINDING_2D: {
             return WebGLObjectAsJSValue(cx, mBound2DTextures[mActiveTexture].get(), rv);
         }
 
-        case LOCAL_GL_TEXTURE_BINDING_CUBE_MAP:
-        {
+        case LOCAL_GL_TEXTURE_BINDING_CUBE_MAP: {
             return WebGLObjectAsJSValue(cx, mBoundCubeMapTextures[mActiveTexture].get(), rv);
         }
 
         default:
-            ErrorInvalidEnumInfo("getParameter: parameter", pname);
+            break;
     }
 
+    ErrorInvalidEnumInfo("getParameter: parameter", pname);
     return JS::NullValue();
 }
 
 JS::Value
 WebGLContext::GetParameterIndexed(JSContext* cx, GLenum pname, GLuint index)
 {
     if (IsContextLost())
         return JS::NullValue();
--- a/content/canvas/src/WebGLContextValidate.cpp
+++ b/content/canvas/src/WebGLContextValidate.cpp
@@ -1621,16 +1621,37 @@ WebGLContext::InitAndValidateGL()
     mDisableExtensions = Preferences::GetBool("webgl.disable-extensions", false);
     mLoseContextOnHeapMinimize = Preferences::GetBool("webgl.lose-context-on-heap-minimize", false);
     mCanLoseContextInForeground = Preferences::GetBool("webgl.can-lose-context-in-foreground", true);
 
     if (MinCapabilityMode()) {
       mDisableFragHighP = true;
     }
 
+    // These are the default values, see 6.2 State tables in the
+    // OpenGL ES 2.0.25 spec.
+    mColorWriteMask[0] = 1;
+    mColorWriteMask[1] = 1;
+    mColorWriteMask[2] = 1;
+    mColorWriteMask[3] = 1;
+    mDepthWriteMask = 1;
+    mColorClearValue[0] = 0.f;
+    mColorClearValue[1] = 0.f;
+    mColorClearValue[2] = 0.f;
+    mColorClearValue[3] = 0.f;
+    mDepthClearValue = 1.f;
+    mStencilClearValue = 0;
+    mStencilRefFront = 0;
+    mStencilRefBack = 0;
+    mStencilValueMaskFront = 0xffffffff;
+    mStencilValueMaskBack  = 0xffffffff;
+    mStencilWriteMaskFront = 0xffffffff;
+    mStencilWriteMaskBack  = 0xffffffff;
+
+    // Bindings, etc.
     mActiveTexture = 0;
     mEmitContextLostErrorOnce = true;
     mWebGLError = LOCAL_GL_NO_ERROR;
     mUnderlyingGLError = LOCAL_GL_NO_ERROR;
 
     mBound2DTextures.Clear();
     mBoundCubeMapTextures.Clear();
 
--- a/content/html/content/public/HTMLCanvasElement.h
+++ b/content/html/content/public/HTMLCanvasElement.h
@@ -8,38 +8,39 @@
 
 #include "mozilla/Attributes.h"
 #include "nsIDOMHTMLCanvasElement.h"
 #include "nsGenericHTMLElement.h"
 #include "nsGkAtoms.h"
 #include "nsSize.h"
 #include "nsError.h"
 
-#include "nsICanvasElementExternal.h"
 #include "mozilla/gfx/Rect.h"
 
 class nsICanvasRenderingContextInternal;
 class nsIDOMFile;
 class nsITimerCallback;
 
 namespace mozilla {
 
 namespace layers {
 class CanvasLayer;
 class LayerManager;
 }
+namespace gfx {
+class SourceSurface;
+}
 
 namespace dom {
 
 class FileCallback;
 class HTMLCanvasPrintState;
 class PrintCallback;
 
 class HTMLCanvasElement MOZ_FINAL : public nsGenericHTMLElement,
-                                    public nsICanvasElementExternal,
                                     public nsIDOMHTMLCanvasElement
 {
   enum {
     DEFAULT_CANVAS_WIDTH = 300,
     DEFAULT_CANVAS_HEIGHT = 150
   };
 
   typedef layers::CanvasLayer CanvasLayer;
@@ -154,23 +155,17 @@ public:
   nsICanvasRenderingContextInternal *GetContextAtIndex (int32_t index);
 
   /*
    * Returns true if the canvas context content is guaranteed to be opaque
    * across its entire area.
    */
   bool GetIsOpaque();
 
-  /*
-   * nsICanvasElementExternal -- for use outside of content/layout
-   */
-  NS_IMETHOD_(nsIntSize) GetSizeExternal() MOZ_OVERRIDE;
-  NS_IMETHOD RenderContextsExternal(gfxContext *aContext,
-                                    GraphicsFilter aFilter,
-                                    uint32_t aFlags = RenderFlagPremultAlpha) MOZ_OVERRIDE;
+  virtual TemporaryRef<gfx::SourceSurface> GetSurfaceSnapshot(bool* aPremultAlpha = nullptr);
 
   virtual bool ParseAttribute(int32_t aNamespaceID,
                                 nsIAtom* aAttribute,
                                 const nsAString& aValue,
                                 nsAttrValue& aResult) MOZ_OVERRIDE;
   nsChangeHint GetAttributeChangeHint(const nsIAtom* aAttribute, int32_t aModType) const MOZ_OVERRIDE;
 
   // SetAttr override.  C++ is stupid, so have to override both
--- a/content/html/content/src/HTMLCanvasElement.cpp
+++ b/content/html/content/src/HTMLCanvasElement.cpp
@@ -33,16 +33,17 @@
 #include "nsStreamUtils.h"
 #include "ActiveLayerTracker.h"
 
 #ifdef MOZ_WEBGL
 #include "../canvas/src/WebGL2Context.h"
 #endif
 
 using namespace mozilla::layers;
+using namespace mozilla::gfx;
 
 NS_IMPL_NS_NEW_HTML_ELEMENT(Canvas)
 
 namespace {
 
 typedef mozilla::dom::HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement
 HTMLImageOrCanvasOrVideoElement;
 
@@ -125,19 +126,17 @@ HTMLCanvasElement::~HTMLCanvasElement()
 NS_IMPL_CYCLE_COLLECTION_INHERITED_4(HTMLCanvasElement, nsGenericHTMLElement,
                                      mCurrentContext, mPrintCallback,
                                      mPrintState, mOriginalCanvas)
 
 NS_IMPL_ADDREF_INHERITED(HTMLCanvasElement, Element)
 NS_IMPL_RELEASE_INHERITED(HTMLCanvasElement, Element)
 
 NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(HTMLCanvasElement)
-  NS_INTERFACE_TABLE_INHERITED2(HTMLCanvasElement,
-                                nsIDOMHTMLCanvasElement,
-                                nsICanvasElementExternal)
+  NS_INTERFACE_TABLE_INHERITED1(HTMLCanvasElement, nsIDOMHTMLCanvasElement)
 NS_INTERFACE_TABLE_TAIL_INHERITING(nsGenericHTMLElement)
 
 NS_IMPL_ELEMENT_CLONE(HTMLCanvasElement)
 
 /* virtual */ JSObject*
 HTMLCanvasElement::WrapNode(JSContext* aCx)
 {
   return HTMLCanvasElementBinding::Wrap(aCx, this);
@@ -929,25 +928,19 @@ void
 HTMLCanvasElement::MarkContextClean()
 {
   if (!mCurrentContext)
     return;
 
   mCurrentContext->MarkContextClean();
 }
 
-NS_IMETHODIMP_(nsIntSize)
-HTMLCanvasElement::GetSizeExternal()
-{
-  return GetWidthHeight();
-}
-
-NS_IMETHODIMP
-HTMLCanvasElement::RenderContextsExternal(gfxContext *aContext, GraphicsFilter aFilter, uint32_t aFlags)
+TemporaryRef<SourceSurface>
+HTMLCanvasElement::GetSurfaceSnapshot(bool* aPremultAlpha)
 {
   if (!mCurrentContext)
-    return NS_OK;
+    return nullptr;
 
-  return mCurrentContext->Render(aContext, aFilter, aFlags);
+  return mCurrentContext->GetSurfaceSnapshot(aPremultAlpha);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/content/html/content/src/HTMLLinkElement.cpp
+++ b/content/html/content/src/HTMLLinkElement.cpp
@@ -191,20 +191,26 @@ HTMLLinkElement::UnbindFromTree(bool aDe
 }
 
 bool
 HTMLLinkElement::ParseAttribute(int32_t aNamespaceID,
                                 nsIAtom* aAttribute,
                                 const nsAString& aValue,
                                 nsAttrValue& aResult)
 {
-  if (aNamespaceID == kNameSpaceID_None &&
-      aAttribute == nsGkAtoms::crossorigin) {
-    ParseCORSValue(aValue, aResult);
-    return true;
+  if (aNamespaceID == kNameSpaceID_None) {
+    if (aAttribute == nsGkAtoms::crossorigin) {
+      ParseCORSValue(aValue, aResult);
+      return true;
+    }
+
+    if (aAttribute == nsGkAtoms::sizes) {
+      aResult.ParseAtomArray(aValue);
+      return true;
+    }
   }
 
   return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
                                               aResult);
 }
 
 void
 HTMLLinkElement::CreateAndDispatchEvent(nsIDocument* aDoc,
--- a/content/html/content/src/HTMLLinkElement.h
+++ b/content/html/content/src/HTMLLinkElement.h
@@ -104,16 +104,20 @@ public:
   {
     SetHTMLAttr(nsGkAtoms::media, aMedia, aRv);
   }
   // XPCOM GetHreflang is fine.
   void SetHreflang(const nsAString& aHreflang, ErrorResult& aRv)
   {
     SetHTMLAttr(nsGkAtoms::hreflang, aHreflang, aRv);
   }
+  nsDOMSettableTokenList* Sizes()
+  {
+    return GetTokenList(nsGkAtoms::sizes);
+  }
   // XPCOM GetType is fine.
   void SetType(const nsAString& aType, ErrorResult& aRv)
   {
     SetHTMLAttr(nsGkAtoms::type, aType, aRv);
   }
   // XPCOM GetCharset is fine.
   void SetCharset(const nsAString& aCharset, ErrorResult& aRv)
   {
--- a/content/html/content/src/nsGenericHTMLElement.cpp
+++ b/content/html/content/src/nsGenericHTMLElement.cpp
@@ -3149,16 +3149,17 @@ nsDOMSettableTokenListPropertyDestructor
 
 static nsIAtom** sPropertiesToTraverseAndUnlink[] =
   {
     &nsGkAtoms::microdataProperties,
     &nsGkAtoms::itemtype,
     &nsGkAtoms::itemref,
     &nsGkAtoms::itemprop,
     &nsGkAtoms::sandbox,
+    &nsGkAtoms::sizes,
     nullptr
   };
 
 // static
 nsIAtom***
 nsGenericHTMLElement::PropertiesToTraverseAndUnlink()
 {
   return sPropertiesToTraverseAndUnlink;
--- a/content/html/content/test/mochitest.ini
+++ b/content/html/content/test/mochitest.ini
@@ -443,16 +443,17 @@ skip-if = buildapp == 'b2g' # b2g(multip
 [test_iframe_sandbox_popups_inheritance.html]
 skip-if = buildapp == 'b2g' || e10s # b2g(multiple concurrent window.open()s fail on B2G) b2g-debug(multiple concurrent window.open()s fail on B2G) b2g-desktop(Bug 931116, b2g desktop specific, initial triage)
 [test_iframe_sandbox_same_origin.html]
 [test_iframe_sandbox_workers.html]
 [test_img_attributes_reflection.html]
 [test_imageSrcSet.html]
 [test_li_attributes_reflection.html]
 [test_link_attributes_reflection.html]
+[test_link_sizes.html]
 [test_map_attributes_reflection.html]
 [test_meta_attributes_reflection.html]
 [test_mod_attributes_reflection.html]
 [test_mozaudiochannel.html]
 skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug)) # b2g-debug(Perma-orange on debug emulator) b2g-desktop(Bug 931116, b2g desktop specific, initial triage)
 [test_named_options.html]
 [test_nested_invalid_fieldsets.html]
 [test_object_attributes_reflection.html]
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/test_link_sizes.html
@@ -0,0 +1,35 @@
+<!doctype html>
+<html>
+<head>
+<title>Test link.sizes attribute</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" sizes="16x16 24x24 32x32 48x48">
+</head>
+<body>
+
+<pre id="test">
+<script>
+
+  var links = document.getElementsByTagName('link');
+  for (var i = 0; i < links.length; ++i) {
+    var link = links[i];
+    ok("sizes" in link, "link.sizes exists");
+
+    if (link.rel == 'shortcut icon') {
+      is(link.sizes.value, "16x16 24x24 32x32 48x48", 'link.sizes.value correct value');
+      is(link.sizes.length, 4, 'link.sizes.length correct value');
+      ok(link.sizes.contains('32x32'), 'link.sizes.contains() works');
+      link.sizes.add('64x64');
+      is(link.sizes.length, 5, 'link.sizes.length correct value');
+      link.sizes.remove('64x64');
+      is(link.sizes.length, 4, 'link.sizes.length correct value');
+      is(link.sizes + "", "16x16 24x24 32x32 48x48", 'link.sizes stringify correct value');
+    } else {
+      is(link.sizes.value, "", 'link.sizes correct value');
+    }
+  }
+</script>
+</pre>
+</body>
+</html>
--- a/content/media/AudioStream.cpp
+++ b/content/media/AudioStream.cpp
@@ -497,16 +497,17 @@ AudioStream::CheckForStart()
               mLatencyRequest == LowLatency ? "low" : "high"));
     }
   }
 }
 
 NS_IMETHODIMP
 AudioInitTask::Run()
 {
+  MOZ_ASSERT(mThread);
   if (NS_IsMainThread()) {
     mThread->Shutdown(); // can't Shutdown from the thread itself, darn
     mThread = nullptr;
     return NS_OK;
   }
 
   nsresult rv = mAudioStream->OpenCubeb(mParams, mLatencyRequest);
 
--- a/content/media/AudioStream.h
+++ b/content/media/AudioStream.h
@@ -428,17 +428,22 @@ public:
                 const cubeb_stream_params &aParams)
     : mAudioStream(aStream)
     , mLatencyRequest(aLatencyRequest)
     , mParams(aParams)
   {}
 
   nsresult Dispatch()
   {
-    return NS_NewNamedThread("CubebInit", getter_AddRefs(mThread), this);
+    // Can't add 'this' as the event to run, since mThread may not be set yet
+    nsresult rv = NS_NewNamedThread("CubebInit", getter_AddRefs(mThread));
+    if (NS_SUCCEEDED(rv)) {
+      rv = mThread->Dispatch(this, NS_DISPATCH_NORMAL);
+    }
+    return rv;
   }
 
 protected:
   virtual ~AudioInitTask() {};
 
 private:
   NS_IMETHOD Run() MOZ_OVERRIDE MOZ_FINAL;
 
--- a/content/media/MediaStreamGraph.cpp
+++ b/content/media/MediaStreamGraph.cpp
@@ -1254,27 +1254,29 @@ MediaStreamGraphImpl::RunThread()
       }
     }
     messageQueue.Clear();
 
     if (mStreamOrderDirty) {
       UpdateStreamOrder();
     }
 
+    TrackRate sampleRate;
     // Find the sampling rate that we need to use for non-realtime graphs.
-    TrackRate sampleRate = IdealAudioRate();
     if (!mRealtime) {
       for (uint32_t i = 0; i < mStreams.Length(); ++i) {
         AudioNodeStream* n = mStreams[i]->AsAudioNodeStream();
         if (n) {
           // We know that the rest of the streams will run at the same rate.
           sampleRate = n->SampleRate();
           break;
         }
       }
+    } else {
+      sampleRate = IdealAudioRate();
     }
 
     GraphTime endBlockingDecisions =
       RoundUpToNextAudioBlock(sampleRate, mCurrentTime + MillisecondsToMediaTime(AUDIO_TARGET_MS));
     bool ensureNextIteration = false;
 
     // Grab pending stream input.
     for (uint32_t i = 0; i < mStreams.Length(); ++i) {
--- a/content/media/gstreamer/GStreamerFormatHelper.cpp
+++ b/content/media/gstreamer/GStreamerFormatHelper.cpp
@@ -25,20 +25,18 @@ GStreamerFormatHelper* GStreamerFormatHe
 
     gInstance = new GStreamerFormatHelper();
   }
 
   return gInstance;
 }
 
 void GStreamerFormatHelper::Shutdown() {
-  if (gInstance) {
-    delete gInstance;
-    gInstance = nullptr;
-  }
+  delete gInstance;
+  gInstance = nullptr;
 }
 
 static char const *const sContainers[6][2] = {
   {"video/mp4", "video/quicktime"},
   {"video/quicktime", "video/quicktime"},
   {"audio/mp4", "audio/x-m4a"},
   {"audio/x-m4a", "audio/x-m4a"},
   {"audio/mpeg", "audio/mpeg, mpegversion=(int)1"},
--- a/content/media/omx/AudioOutput.cpp
+++ b/content/media/omx/AudioOutput.cpp
@@ -175,20 +175,18 @@ void AudioOutput::Pause()
   }
 }
 
 void AudioOutput::Close()
 {
   AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("%s", __PRETTY_FUNCTION__));
   mTrack.clear();
 
-  if (mCallbackData) {
-    delete mCallbackData;
-    mCallbackData = NULL;
-  }
+  delete mCallbackData;
+  mCallbackData = nullptr;
 }
 
 // static
 void AudioOutput::CallbackWrapper(int aEvent, void* aCookie, void* aInfo)
 {
   CallbackData* data = (CallbackData*) aCookie;
   data->Lock();
   AudioOutput* me = data->GetOutput();
--- a/content/media/plugins/MediaPluginHost.cpp
+++ b/content/media/plugins/MediaPluginHost.cpp
@@ -318,15 +318,13 @@ MediaPluginHost *GetMediaPluginHost()
   if (!sMediaPluginHost) {
     sMediaPluginHost = new MediaPluginHost();
   }
   return sMediaPluginHost;
 }
 
 void MediaPluginHost::Shutdown()
 {
-  if (sMediaPluginHost) {
-    delete sMediaPluginHost;
-    sMediaPluginHost = nullptr;
-  }
+  delete sMediaPluginHost;
+  sMediaPluginHost = nullptr;
 }
 
 } // namespace mozilla
--- a/content/media/webaudio/OscillatorNode.cpp
+++ b/content/media/webaudio/OscillatorNode.cpp
@@ -381,19 +381,17 @@ public:
     for (uint32_t i = aStart; i < aEnd; ++i) {
       UpdateParametersIfNeeded(ticks, i);
       mPeriodicWave->waveDataForFundamentalFrequency(mFinalFrequency,
                                                      lowerWaveData,
                                                      higherWaveData,
                                                      tableInterpolationFactor);
       // mPhase runs 0..periodicWaveSize here instead of 0..2*M_PI.
       mPhase += periodicWaveSize * mFinalFrequency * rate;
-      if (mPhase >= periodicWaveSize) {
-        mPhase -= periodicWaveSize;
-      }
+      mPhase = fmod(mPhase, periodicWaveSize);
       // Bilinear interpolation between adjacent samples in each table.
       uint32_t j1 = floor(mPhase);
       uint32_t j2 = j1 + 1;
       if (j2 >= periodicWaveSize) {
         j2 -= periodicWaveSize;
       }
       float sampleInterpolationFactor = mPhase - j1;
       float lower = sampleInterpolationFactor * lowerWaveData[j1] +
--- a/content/media/webrtc/MediaEngineWebRTCAudio.cpp
+++ b/content/media/webrtc/MediaEngineWebRTCAudio.cpp
@@ -463,20 +463,17 @@ void
 MediaEngineWebRTCAudioSource::Shutdown()
 {
   if (!mInitDone) {
     // duplicate these here in case we failed during Init()
     if (mChannel != -1) {
       mVoENetwork->DeRegisterExternalTransport(mChannel);
     }
 
-    if (mNullTransport) {
-      delete mNullTransport;
-    }
-
+    delete mNullTransport;
     return;
   }
 
   if (mState == kStarted) {
     while (!mSources.IsEmpty()) {
       Stop(mSources[0], kAudioTrack); // XXX change to support multiple tracks
     }
     MOZ_ASSERT(mState == kStopped);
@@ -486,19 +483,17 @@ MediaEngineWebRTCAudioSource::Shutdown()
     Deallocate();
   }
 
   mVoEBase->Terminate();
   if (mChannel != -1) {
     mVoENetwork->DeRegisterExternalTransport(mChannel);
   }
 
-  if (mNullTransport) {
-    delete mNullTransport;
-  }
+  delete mNullTransport;
 
   mVoEProcessing = nullptr;
   mVoENetwork = nullptr;
   mVoERender = nullptr;
   mVoEBase = nullptr;
 
   mState = kReleased;
   mInitDone = false;
--- a/dom/base/Console.cpp
+++ b/dom/base/Console.cpp
@@ -2,25 +2,28 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/Console.h"
 #include "mozilla/dom/ConsoleBinding.h"
 
 #include "mozilla/dom/Exceptions.h"
+#include "mozilla/dom/ToJSValue.h"
+#include "mozilla/Maybe.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsDocument.h"
 #include "nsDOMNavigationTiming.h"
 #include "nsGlobalWindow.h"
 #include "nsJSUtils.h"
 #include "nsPerformance.h"
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
 #include "xpcprivate.h"
+#include "nsContentUtils.h"
 
 #include "nsIConsoleAPIStorage.h"
 #include "nsIDOMWindowUtils.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsILoadContext.h"
 #include "nsIServiceManager.h"
 #include "nsISupportsPrimitives.h"
 #include "nsIWebNavigation.h"
@@ -166,17 +169,26 @@ public:
 
   Console::MethodName mMethodName;
   bool mPrivate;
   int64_t mTimeStamp;
   DOMHighResTimeStamp mMonotonicTimer;
 
   nsString mMethodString;
   nsTArray<JS::Heap<JS::Value>> mArguments;
-  Sequence<ConsoleStackEntry> mStack;
+
+  // Stack management is complicated, because we want to do it as
+  // lazily as possible.  Therefore, we have the following behavior:
+  // 1)  mTopStackFrame is initialized whenever we have any JS on the stack
+  // 2)  mReifiedStack is initialized if we're created in a worker.
+  // 3)  mStack is set (possibly to null if there is no JS on the stack) if
+  //     we're created on main thread.
+  Maybe<ConsoleStackEntry> mTopStackFrame;
+  Maybe<nsTArray<ConsoleStackEntry>> mReifiedStack;
+  nsCOMPtr<nsIStackFrame> mStack;
 };
 
 // This class is used to clear any exception at the end of this method.
 class ClearException
 {
 public:
   ClearException(JSContext* aCx)
     : mCx(aCx)
@@ -728,16 +740,68 @@ Console::Assert(JSContext* aCx, bool aCo
 METHOD(Count, "count")
 
 void
 Console::__noSuchMethod__()
 {
   // Nothing to do.
 }
 
+static
+nsresult
+StackFrameToStackEntry(nsIStackFrame* aStackFrame,
+                       ConsoleStackEntry& aStackEntry,
+                       uint32_t aLanguage)
+{
+  MOZ_ASSERT(aStackFrame);
+
+  nsresult rv = aStackFrame->GetFilename(aStackEntry.mFilename);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  int32_t lineNumber;
+  rv = aStackFrame->GetLineNumber(&lineNumber);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  aStackEntry.mLineNumber = lineNumber;
+
+  rv = aStackFrame->GetName(aStackEntry.mFunctionName);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  aStackEntry.mLanguage = aLanguage;
+  return NS_OK;
+}
+
+static
+nsresult
+ReifyStack(nsIStackFrame* aStack, nsTArray<ConsoleStackEntry>& aRefiedStack)
+{
+  nsCOMPtr<nsIStackFrame> stack(aStack);
+
+  while (stack) {
+    uint32_t language;
+    nsresult rv = stack->GetLanguage(&language);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (language == nsIProgrammingLanguage::JAVASCRIPT ||
+        language == nsIProgrammingLanguage::JAVASCRIPT2) {
+      ConsoleStackEntry& data = *aRefiedStack.AppendElement();
+      rv = StackFrameToStackEntry(stack, data, language);
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+
+    nsCOMPtr<nsIStackFrame> caller;
+    rv = stack->GetCaller(getter_AddRefs(caller));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    stack.swap(caller);
+  }
+
+  return NS_OK;
+}
+
 // Queue a call to a console method. See the CALL_DELAY constant.
 void
 Console::Method(JSContext* aCx, MethodName aMethodName,
                 const nsAString& aMethodString,
                 const Sequence<JS::Value>& aData)
 {
   // This RAII class removes the last element of the mQueuedCalls if something
   // goes wrong.
@@ -792,64 +856,62 @@ Console::Method(JSContext* aCx, MethodNa
                       DEFAULT_MAX_STACKTRACE_DEPTH : 1;
   nsCOMPtr<nsIStackFrame> stack = CreateStack(aCx, maxDepth);
 
   if (!stack) {
     Throw(aCx, NS_ERROR_FAILURE);
     return;
   }
 
-  // nsIStackFrame is not thread-safe so we take what we need and we store in
-  // an array of ConsoleStackEntry objects.
+  // Walk up to the first JS stack frame and save it if we find it.
   do {
     uint32_t language;
     nsresult rv = stack->GetLanguage(&language);
     if (NS_FAILED(rv)) {
       Throw(aCx, rv);
       return;
     }
 
     if (language == nsIProgrammingLanguage::JAVASCRIPT ||
         language == nsIProgrammingLanguage::JAVASCRIPT2) {
-      ConsoleStackEntry& data = *callData->mStack.AppendElement();
-
-      rv = stack->GetFilename(data.mFilename);
+      callData->mTopStackFrame.construct();
+      nsresult rv = StackFrameToStackEntry(stack,
+                                           callData->mTopStackFrame.ref(),
+                                           language);
       if (NS_FAILED(rv)) {
         Throw(aCx, rv);
         return;
       }
 
-      int32_t lineNumber;
-      rv = stack->GetLineNumber(&lineNumber);
-      if (NS_FAILED(rv)) {
-        Throw(aCx, rv);
-        return;
-      }
-
-      data.mLineNumber = lineNumber;
-
-      rv = stack->GetName(data.mFunctionName);
-      if (NS_FAILED(rv)) {
-        Throw(aCx, rv);
-        return;
-      }
-
-      data.mLanguage = language;
+      break;
     }
 
     nsCOMPtr<nsIStackFrame> caller;
     rv = stack->GetCaller(getter_AddRefs(caller));
     if (NS_FAILED(rv)) {
       Throw(aCx, rv);
       return;
     }
 
     stack.swap(caller);
   } while (stack);
 
+  if (NS_IsMainThread()) {
+    callData->mStack = stack;
+  } else {
+    // nsIStackFrame is not threadsafe, so we need to snapshot it now,
+    // before we post our runnable to the main thread.
+    callData->mReifiedStack.construct();
+    nsresult rv = ReifyStack(stack, callData->mReifiedStack.ref());
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      Throw(aCx, rv);
+      return;
+    }
+  }
+
   // Monotonic timer for 'time' and 'timeEnd'
   if ((aMethodName == MethodTime || aMethodName == MethodTimeEnd) && mWindow) {
     nsGlobalWindow *win = static_cast<nsGlobalWindow*>(mWindow.get());
     MOZ_ASSERT(win);
 
     ErrorResult rv;
     nsRefPtr<nsPerformance> performance = win->GetPerformance(rv);
     if (rv.Failed()) {
@@ -913,24 +975,70 @@ Console::Notify(nsITimer *timer)
   if (mQueuedCalls.isEmpty() && mTimer) {
     mTimer->Cancel();
     mTimer = nullptr;
   }
 
   return NS_OK;
 }
 
+// We store information to lazily compute the stack in the reserved slots of
+// LazyStackGetter.  The first slot always stores a JS object: it's either the
+// JS wrapper of the nsIStackFrame or the actual reified stack representation.
+// The second slot is a PrivateValue() holding an nsIStackFrame* when we haven't
+// reified the stack yet, or an UndefinedValue() otherwise.
+enum {
+  SLOT_STACKOBJ,
+  SLOT_RAW_STACK
+};
+
+bool
+LazyStackGetter(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
+{
+  JS::CallArgs args = CallArgsFromVp(aArgc, aVp);
+  JS::Rooted<JSObject*> callee(aCx, &args.callee());
+
+  JS::Value v = js::GetFunctionNativeReserved(&args.callee(), SLOT_RAW_STACK);
+  if (v.isUndefined()) {
+    // Already reified.
+    args.rval().set(js::GetFunctionNativeReserved(callee, SLOT_STACKOBJ));
+    return true;
+  }
+
+  nsIStackFrame* stack = reinterpret_cast<nsIStackFrame*>(v.toPrivate());
+  nsTArray<ConsoleStackEntry> reifiedStack;
+  nsresult rv = ReifyStack(stack, reifiedStack);
+  if (NS_FAILED(rv)) {
+    Throw(aCx, rv);
+    return false;
+  }
+
+  JS::Rooted<JS::Value> stackVal(aCx);
+  if (!ToJSValue(aCx, reifiedStack, &stackVal)) {
+    return false;
+  }
+
+  MOZ_ASSERT(stackVal.isObject());
+
+  js::SetFunctionNativeReserved(callee, SLOT_STACKOBJ, stackVal);
+  js::SetFunctionNativeReserved(callee, SLOT_RAW_STACK, JS::UndefinedValue());
+
+  args.rval().set(stackVal);
+  return true;
+}
+
 void
 Console::ProcessCallData(ConsoleCallData* aData)
 {
   MOZ_ASSERT(aData);
+  MOZ_ASSERT(NS_IsMainThread());
 
   ConsoleStackEntry frame;
-  if (!aData->mStack.IsEmpty()) {
-    frame = aData->mStack[0];
+  if (!aData->mTopStackFrame.empty()) {
+    frame = aData->mTopStackFrame.ref();
   }
 
   AutoSafeJSContext cx;
   ClearException ce(cx);
   RootedDictionary<ConsoleEvent> event(cx);
 
   JSAutoCompartment ac(cx, aData->mGlobal);
 
@@ -966,52 +1074,104 @@ Console::ProcessCallData(ConsoleCallData
                        event.mStyles.Value());
       break;
 
     default:
       event.mArguments.Construct();
       ArgumentsToValueList(aData->mArguments, event.mArguments.Value());
   }
 
-  if (ShouldIncludeStackrace(aData->mMethodName)) {
-    event.mStacktrace.Construct();
-    event.mStacktrace.Value().SwapElements(aData->mStack);
-  }
-
-  else if (aData->mMethodName == MethodGroup ||
-           aData->mMethodName == MethodGroupCollapsed ||
-           aData->mMethodName == MethodGroupEnd) {
+  if (aData->mMethodName == MethodGroup ||
+      aData->mMethodName == MethodGroupCollapsed ||
+      aData->mMethodName == MethodGroupEnd) {
     ComposeGroupName(cx, aData->mArguments, event.mGroupName);
   }
 
   else if (aData->mMethodName == MethodTime && !aData->mArguments.IsEmpty()) {
     event.mTimer = StartTimer(cx, aData->mArguments[0], aData->mMonotonicTimer);
   }
 
   else if (aData->mMethodName == MethodTimeEnd && !aData->mArguments.IsEmpty()) {
     event.mTimer = StopTimer(cx, aData->mArguments[0], aData->mMonotonicTimer);
   }
 
   else if (aData->mMethodName == MethodCount) {
     event.mCounter = IncreaseCounter(cx, frame, aData->mArguments);
   }
 
+  // We want to create a console event object and pass it to our
+  // nsIConsoleAPIStorage implementation.  We want to define some accessor
+  // properties on this object, and those will need to keep an nsIStackFrame
+  // alive.  But nsIStackFrame cannot be wrapped in an untrusted scope.  And
+  // further, passing untrusted objects to system code is likely to run afoul of
+  // Object Xrays.  So we want to wrap in a system-principal scope here.  But
+  // which one?  We could cheat and try to get the underlying JSObject* of
+  // mStorage, but that's a bit fragile.  Instead, we just use the junk scope,
+  // with explicit permission from the XPConnect module owner.  If you're
+  // tempted to do that anywhere else, talk to said module owner first.
+  JSAutoCompartment ac2(cx, xpc::GetJunkScope());
+
   JS::Rooted<JS::Value> eventValue(cx);
   if (!event.ToObject(cx, &eventValue)) {
     Throw(cx, NS_ERROR_FAILURE);
     return;
   }
 
   JS::Rooted<JSObject*> eventObj(cx, &eventValue.toObject());
   MOZ_ASSERT(eventObj);
 
   if (!JS_DefineProperty(cx, eventObj, "wrappedJSObject", eventValue, JSPROP_ENUMERATE)) {
     return;
   }
 
+  if (ShouldIncludeStackrace(aData->mMethodName)) {
+    // Now define the "stacktrace" property on eventObj.  There are two cases
+    // here.  Either we came from a worker and have a reified stack, or we want
+    // to define a getter that will lazily reify the stack.
+    if (!aData->mReifiedStack.empty()) {
+      JS::Rooted<JS::Value> stacktrace(cx);
+      if (!ToJSValue(cx, aData->mReifiedStack.ref(), &stacktrace) ||
+          !JS_DefineProperty(cx, eventObj, "stacktrace", stacktrace,
+                             JSPROP_ENUMERATE)) {
+        return;
+      }
+    } else {
+      JSFunction* fun = js::NewFunctionWithReserved(cx, LazyStackGetter, 0, 0,
+                                                    eventObj, "stacktrace");
+      if (!fun) {
+        return;
+      }
+
+      JS::Rooted<JSObject*> funObj(cx, JS_GetFunctionObject(fun));
+
+      // We want to store our stack in the function and have it stay alive.  But
+      // we also need sane access to the C++ nsIStackFrame.  So store both a JS
+      // wrapper and the raw pointer: the former will keep the latter alive.
+      JS::Rooted<JS::Value> stackVal(cx);
+      nsresult rv = nsContentUtils::WrapNative(cx, aData->mStack,
+                                               &stackVal);
+      if (NS_FAILED(rv)) {
+        return;
+      }
+
+      js::SetFunctionNativeReserved(funObj, SLOT_STACKOBJ, stackVal);
+      js::SetFunctionNativeReserved(funObj, SLOT_RAW_STACK,
+                                    JS::PrivateValue(aData->mStack.get()));
+
+      if (!JS_DefineProperty(cx, eventObj, "stacktrace",
+                             JS::UndefinedHandleValue,
+                             JSPROP_ENUMERATE | JSPROP_SHARED | JSPROP_GETTER |
+                             JSPROP_SETTER,
+                             JS_DATA_TO_FUNC_PTR(JSPropertyOp, funObj.get()),
+                             nullptr)) {
+        return;
+      }
+    }
+  }
+
   if (!mStorage) {
     mStorage = do_GetService("@mozilla.org/consoleAPI-storage;1");
   }
 
   if (!mStorage) {
     NS_WARNING("Failed to get the ConsoleAPIStorage service.");
     return;
   }
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -12454,91 +12454,47 @@ nsGlobalWindow::GetScrollFrame()
   nsCOMPtr<nsIPresShell> presShell = mDocShell->GetPresShell();
   if (presShell) {
     return presShell->GetRootScrollFrameAsScrollable();
   }
   return nullptr;
 }
 
 nsresult
-nsGlobalWindow::BuildURIfromBase(const char *aURL, nsIURI **aBuiltURI,
-                                 JSContext **aCXused)
-{
-  nsIScriptContext *scx = GetContextInternal();
-  JSContext *cx = nullptr;
-
-  *aBuiltURI = nullptr;
-  if (aCXused)
-    *aCXused = nullptr;
-
-  // get JSContext
-  NS_ASSERTION(scx, "opening window missing its context");
-  NS_ASSERTION(mDoc, "opening window missing its document");
-  if (!scx || !mDoc)
-    return NS_ERROR_FAILURE;
-
-  nsCOMPtr<nsIDOMChromeWindow> chrome_win = do_QueryObject(this);
-
-  if (nsContentUtils::IsCallerChrome() && !chrome_win) {
-    // If open() is called from chrome on a non-chrome window, we'll
-    // use the context from the window on which open() is being called
-    // to prevent giving chrome priveleges to new windows opened in
-    // such a way. This also makes us get the appropriate base URI for
-    // the below URI resolution code.
-
-    cx = scx->GetNativeContext();
-  } else {
-    // get the JSContext from the call stack
-    cx = nsContentUtils::GetCurrentJSContext();
-  }
-
-  /* resolve the URI, which could be relative to the calling window
-     (note the algorithm to get the base URI should match the one
-     used to actually kick off the load in nsWindowWatcher.cpp). */
-  nsAutoCString charset(NS_LITERAL_CSTRING("UTF-8")); // default to utf-8
-  nsIURI* baseURI = nullptr;
-  nsCOMPtr<nsIURI> uriToLoad;
+nsGlobalWindow::SecurityCheckURL(const char *aURL)
+{
   nsCOMPtr<nsPIDOMWindow> sourceWindow;
-
-  if (cx) {
-    nsIScriptContext *scriptcx = nsJSUtils::GetDynamicScriptContext(cx);
-    if (scriptcx)
-      sourceWindow = do_QueryInterface(scriptcx->GetGlobalObject());
-  }
-
+  JSContext* topCx = nsContentUtils::GetCurrentJSContext();
+  if (topCx) {
+    sourceWindow = do_QueryInterface(nsJSUtils::GetDynamicScriptGlobal(topCx));
+  }
   if (!sourceWindow) {
     sourceWindow = this;
   }
-
+  AutoJSContext cx;
+  nsGlobalWindow* sourceWin = static_cast<nsGlobalWindow*>(sourceWindow.get());
+  JSAutoCompartment ac(cx, sourceWin->GetGlobalJSObject());
+
+  // Resolve the baseURI, which could be relative to the calling window.
+  //
+  // Note the algorithm to get the base URI should match the one
+  // used to actually kick off the load in nsWindowWatcher.cpp.
   nsCOMPtr<nsIDocument> doc = sourceWindow->GetDoc();
+  nsIURI* baseURI = nullptr;
+  nsAutoCString charset(NS_LITERAL_CSTRING("UTF-8")); // default to utf-8
   if (doc) {
     baseURI = doc->GetDocBaseURI();
     charset = doc->GetDocumentCharacterSet();
   }
-
-  if (aCXused)
-    *aCXused = cx;
-  return NS_NewURI(aBuiltURI, nsDependentCString(aURL), charset.get(), baseURI);
-}
-
-nsresult
-nsGlobalWindow::SecurityCheckURL(const char *aURL)
-{
-  JSContext       *cxUsed;
   nsCOMPtr<nsIURI> uri;
-
-  if (NS_FAILED(BuildURIfromBase(aURL, getter_AddRefs(uri), &cxUsed))) {
-    return NS_ERROR_FAILURE;
-  }
-
-  if (!cxUsed) {
-    return NS_OK;
-  }
-
-  AutoPushJSContext cx(cxUsed);
+  nsresult rv = NS_NewURI(getter_AddRefs(uri), nsDependentCString(aURL),
+                          charset.get(), baseURI);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
 
   if (NS_FAILED(nsContentUtils::GetSecurityManager()->
         CheckLoadURIFromScript(cx, uri))) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -1193,18 +1193,17 @@ protected:
   void InsertTimeoutIntoList(nsTimeout *aTimeout);
   static void TimerCallback(nsITimer *aTimer, void *aClosure);
 
   // Helper Functions
   already_AddRefed<nsIDocShellTreeOwner> GetTreeOwner();
   already_AddRefed<nsIBaseWindow> GetTreeOwnerWindow();
   already_AddRefed<nsIWebBrowserChrome> GetWebBrowserChrome();
   nsresult SecurityCheckURL(const char *aURL);
-  nsresult BuildURIfromBase(const char *aURL, nsIURI **aBuiltURI,
-                            JSContext **aCXused);
+
   bool PopupWhitelisted();
   PopupControlState RevisePopupAbuseLevel(PopupControlState);
   void     FireAbuseEvents(bool aBlocked, bool aWindow,
                            const nsAString &aPopupURL,
                            const nsAString &aPopupWindowName,
                            const nsAString &aPopupWindowFeatures);
   void FireOfflineStatusEvent();
 
--- a/dom/bindings/Exceptions.cpp
+++ b/dom/bindings/Exceptions.cpp
@@ -126,49 +126,51 @@ Throw(JSContext* aCx, nsresult aRv, cons
         // If we weren't able to throw an exception we're
         // most likely out of memory
         JS_ReportOutOfMemory(aCx);
       }
       return false;
     }
   }
 
-  nsRefPtr<Exception> finalException;
-
-  // Do we use DOM exceptions for this error code?
-  switch (NS_ERROR_GET_MODULE(aRv)) {
-  case NS_ERROR_MODULE_DOM:
-  case NS_ERROR_MODULE_SVG:
-  case NS_ERROR_MODULE_DOM_XPATH:
-  case NS_ERROR_MODULE_DOM_INDEXEDDB:
-  case NS_ERROR_MODULE_DOM_FILEHANDLE:
-    finalException = DOMException::Create(aRv);
-    break;
-
-  default:
-      break;
-  }
-
-  // If not, use the default.
-  if (!finalException) {
-    // aMessage can be null.
-    finalException = new Exception(nsCString(aMessage), aRv,
-                                   EmptyCString(), nullptr, nullptr);
-  }
+  nsRefPtr<Exception> finalException = CreateException(aCx, aRv, aMessage);
 
   MOZ_ASSERT(finalException);
   if (!ThrowExceptionObject(aCx, finalException)) {
     // If we weren't able to throw an exception we're
     // most likely out of memory
     JS_ReportOutOfMemory(aCx);
   }
 
   return false;
 }
 
+already_AddRefed<Exception>
+CreateException(JSContext* aCx, nsresult aRv, const char* aMessage)
+{
+  // Do we use DOM exceptions for this error code?
+  switch (NS_ERROR_GET_MODULE(aRv)) {
+  case NS_ERROR_MODULE_DOM:
+  case NS_ERROR_MODULE_SVG:
+  case NS_ERROR_MODULE_DOM_XPATH:
+  case NS_ERROR_MODULE_DOM_INDEXEDDB:
+  case NS_ERROR_MODULE_DOM_FILEHANDLE:
+    return DOMException::Create(aRv);
+  default:
+    break;
+  }
+
+  // If not, use the default.
+  // aMessage can be null, so we can't use nsDependentCString on it.
+  nsRefPtr<Exception> exception =
+    new Exception(nsCString(aMessage), aRv,
+                  EmptyCString(), nullptr, nullptr);
+  return exception.forget();
+}
+
 already_AddRefed<nsIStackFrame>
 GetCurrentJSStack()
 {
   // is there a current context available?
   JSContext* cx = nullptr;
 
   if (NS_IsMainThread()) {
     // Note, in xpcshell nsContentUtils is never initialized, but we still need
--- a/dom/bindings/Exceptions.h
+++ b/dom/bindings/Exceptions.h
@@ -25,16 +25,21 @@ bool
 Throw(JSContext* cx, nsresult rv, const char* sz = nullptr);
 
 bool
 ThrowExceptionObject(JSContext* aCx, Exception* aException);
 
 bool
 ThrowExceptionObject(JSContext* aCx, nsIException* aException);
 
+// Create an exception object for the given nsresult and message but
+// don't set it pending on aCx.  This never returns null.
+already_AddRefed<Exception>
+CreateException(JSContext* aCx, nsresult aRv, const char* aMessage = nullptr);
+
 already_AddRefed<nsIStackFrame>
 GetCurrentJSStack();
 
 // Internal stuff not intended to be widely used.
 namespace exceptions {
 
 // aMaxDepth can be used to define a maximal depth for the stack trace. If the
 // value is -1, a default maximal depth will be selected.
--- a/dom/bindings/ToJSValue.cpp
+++ b/dom/bindings/ToJSValue.cpp
@@ -1,15 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
 /* vim: set ts=2 sw=2 et tw=79: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/ToJSValue.h"
+#include "mozilla/dom/DOMException.h"
+#include "mozilla/dom/Exceptions.h"
 #include "nsAString.h"
 #include "nsContentUtils.h"
 #include "nsStringBuffer.h"
 #include "xpcpublic.h"
 
 namespace mozilla {
 namespace dom {
 
@@ -44,10 +46,19 @@ ISupportsToJSValue(JSContext* aCx,
                    JS::MutableHandle<JS::Value> aValue)
 {
   nsresult rv = nsContentUtils::WrapNative(aCx, aArgument, aValue);
   return NS_SUCCEEDED(rv);
 }
 
 } // namespace tojsvalue_detail
 
+bool
+ToJSValue(JSContext* aCx,
+          nsresult aArgument,
+          JS::MutableHandle<JS::Value> aValue)
+{
+  nsRefPtr<Exception> exception = CreateException(aCx, aArgument);
+  return ToJSValue(aCx, exception, aValue);
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/bindings/ToJSValue.h
+++ b/dom/bindings/ToJSValue.h
@@ -49,27 +49,36 @@ ToJSValue(JSContext* aCx,
 {
   // Make sure we're called in a compartment
   MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
 
   aValue.setInt32(aArgument);
   return true;
 }
 
+// The uint32_t version is disabled for now because on the super-old b2g
+// compiler nsresult and uint32_t are the same type.  If someone needs this at
+// some point we'll need to figure out how to make it work (e.g. by switching to
+// traits structs and using the trick IPC's ParamTraits uses, where a traits
+// struct templated on the type inherits from a base traits struct of some sort,
+// templated on the same type, or something).  Maybe b2g will update to a modern
+// compiler before that happens....
+#if 0
 inline bool
 ToJSValue(JSContext* aCx,
           uint32_t aArgument,
           JS::MutableHandle<JS::Value> aValue)
 {
   // Make sure we're called in a compartment
   MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
 
   aValue.setNumber(aArgument);
   return true;
 }
+#endif
 
 inline bool
 ToJSValue(JSContext* aCx,
           int64_t aArgument,
           JS::MutableHandle<JS::Value> aValue)
 {
   // Make sure we're called in a compartment
   MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
@@ -198,16 +207,32 @@ template <class T>
 typename EnableIf<IsBaseOf<DictionaryBase, T>::value, bool>::Type
 ToJSValue(JSContext* aCx,
           const T& aArgument,
           JS::MutableHandle<JS::Value> aValue)
 {
   return aArgument.ToObject(aCx, aValue);
 }
 
+// Accept existing JS values (which may not be same-compartment with us
+inline bool
+ToJSValue(JSContext* aCx, JS::Handle<JS::Value> aArgument,
+          JS::MutableHandle<JS::Value> aValue)
+{
+  aValue.set(aArgument);
+  return MaybeWrapValue(aCx, aValue);
+}
+
+// Accept nsresult, for use in rejections, and create an XPCOM
+// exception object representing that nsresult.
+bool
+ToJSValue(JSContext* aCx,
+          nsresult aArgument,
+          JS::MutableHandle<JS::Value> aValue);
+
 // Accept arrays of other things we accept
 template <typename T>
 bool
 ToJSValue(JSContext* aCx,
           T* aArguments,
           size_t aLength,
           JS::MutableHandle<JS::Value> aValue)
 {
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -13,16 +13,18 @@
 #include "IndexedDBChild.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/IntentionalCrash.h"
 #include "mozilla/docshell/OfflineCacheUpdateChild.h"
 #include "mozilla/ipc/DocumentRendererChild.h"
 #include "mozilla/ipc/FileDescriptorUtils.h"
+#include "mozilla/layers/ActiveElementManager.h"
+#include "mozilla/layers/APZCCallbackHelper.h"
 #include "mozilla/layers/AsyncPanZoomController.h"
 #include "mozilla/layers/CompositorChild.h"
 #include "mozilla/layers/ImageBridgeChild.h"
 #include "mozilla/layers/ShadowLayers.h"
 #include "mozilla/layout/RenderFrameChild.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
@@ -63,23 +65,21 @@
 #include "nsThreadUtils.h"
 #include "nsWeakReference.h"
 #include "PermissionMessageUtils.h"
 #include "PCOMContentPermissionRequestChild.h"
 #include "PuppetWidget.h"
 #include "StructuredCloneUtils.h"
 #include "nsViewportInfo.h"
 #include "JavaScriptChild.h"
-#include "APZCCallbackHelper.h"
 #include "nsILoadContext.h"
 #include "ipc/nsGUIEventIPC.h"
 #include "mozilla/gfx/Matrix.h"
 #include "UnitTransforms.h"
 #include "ClientLayerManager.h"
-#include "ActiveElementManager.h"
 
 #include "nsColorPickerProxy.h"
 
 #ifdef DEBUG
 #include "PCOMContentPermissionRequestChild.h"
 #endif /* DEBUG */
 
 #define BROWSER_ELEMENT_CHILD_SCRIPT \
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -37,17 +37,17 @@
 class nsICachedFileDescriptorListener;
 class nsIDOMWindowUtils;
 
 namespace mozilla {
 namespace layout {
 class RenderFrameChild;
 }
 
-namespace widget {
+namespace layers {
 class ActiveElementManager;
 }
 
 namespace dom {
 
 class TabChild;
 class ClonedMessageData;
 class TabChildBase;
@@ -227,17 +227,17 @@ class TabChild : public PBrowserChild,
                  public nsIObserver,
                  public TabContext,
                  public nsITooltipListener,
                  public TabChildBase
 {
     typedef mozilla::dom::ClonedMessageData ClonedMessageData;
     typedef mozilla::layout::RenderFrameChild RenderFrameChild;
     typedef mozilla::layout::ScrollingBehavior ScrollingBehavior;
-    typedef mozilla::widget::ActiveElementManager ActiveElementManager;
+    typedef mozilla::layers::ActiveElementManager ActiveElementManager;
 
 public:
     /** 
      * This is expected to be called off the critical path to content
      * startup.  This is an opportunity to load things that are slow
      * on the critical path.
      */
     static void PreloadSlowThings();
--- a/dom/media/tests/mochitest/pc.js
+++ b/dom/media/tests/mochitest/pc.js
@@ -488,22 +488,31 @@ function PeerConnectionTest(options) {
  * Closes the peer connection if it is active
  *
  * @param {Function} onSuccess
  *        Callback to execute when the peer connection has been closed successfully
  */
 PeerConnectionTest.prototype.close = function PCT_close(onSuccess) {
   info("Closing peer connections. Connection state=" + this.connected);
 
+  function signalingstatechangeClose(state) {
+    info("'onsignalingstatechange' event '" + state + "' received");
+    is(state, "closed", "onsignalingstatechange event is closed");
+  }
+
   // There is no onclose event for the remote peer existent yet. So close it
   // side-by-side with the local peer.
-  if (this.pcLocal)
+  if (this.pcLocal) {
+    this.pcLocal.onsignalingstatechange = signalingstatechangeClose;
     this.pcLocal.close();
-  if (this.pcRemote)
+  }
+  if (this.pcRemote) {
+    this.pcRemote.onsignalingstatechange = signalingstatechangeClose;
     this.pcRemote.close();
+  }
   this.connected = false;
 
   onSuccess();
 };
 
 /**
  * Executes the next command.
  */
@@ -580,18 +589,19 @@ function PCT_setLocalDescription(peer, d
   var stateChanged = false;
 
   function check_next_test() {
     if (eventFired && stateChanged) {
       onSuccess();
     }
   }
 
-  peer.onsignalingstatechange = function () {
-    info(peer + ": 'onsignalingstatechange' event registered, signalingState: " + peer.signalingState);
+  peer.onsignalingstatechange = function (state) {
+    //info(peer + ": 'onsignalingstatechange' event registered, signalingState: " + peer.signalingState);
+    info(peer + ": 'onsignalingstatechange' event '" + state + "' received");
 
     eventFired = true;
     check_next_test();
   };
 
   peer.setLocalDescription(desc, function () {
     stateChanged = true;
     check_next_test();
@@ -641,18 +651,18 @@ function PCT_setRemoteDescription(peer, 
   var stateChanged = false;
 
   function check_next_test() {
     if (eventFired && stateChanged) {
       onSuccess();
     }
   }
 
-  peer.onsignalingstatechange = function () {
-    info(peer + ": 'onsignalingstatechange' event registered, signalingState: " + peer.signalingState);
+  peer.onsignalingstatechange = function (state) {
+    info(peer + ": 'onsignalingstatechange' event '" + state + "' received");
 
     eventFired = true;
     check_next_test();
   };
 
   peer.setRemoteDescription(desc, function () {
     stateChanged = true;
     check_next_test();
@@ -1163,17 +1173,19 @@ function PeerConnectionWrapper(label, co
    * failure will be raised if an event of this type is caught.
    *
    * @param {Object} aEvent
    *        Event data which includes the newly created data channel
    */
   this._pc.onsignalingstatechange = function (aEvent) {
     info(self + ": 'onsignalingstatechange' event fired");
 
-    self.onsignalingstatechange();
+    // this calls the eventhandler only once and then overwrites it with the
+    // default unexpectedEvent handler
+    self.onsignalingstatechange(aEvent);
     self.onsignalingstatechange = unexpectedEventAndFinish(self, 'onsignalingstatechange');
   };
 }
 
 PeerConnectionWrapper.prototype = {
 
   /**
    * Returns the local description.
--- a/dom/media/tests/mochitest/test_peerConnection_bug827843.html
+++ b/dom/media/tests/mochitest/test_peerConnection_bug827843.html
@@ -51,26 +51,36 @@
         test.next();
       }
     ], [
       "CHECK_SDP_ON_CLOSED_PC",
       function (test) {
         var description;
         var exception = null;
 
+        // handle the event which the close() triggers
+        test.pcLocal.onsignalingstatechange = function (state) {
+          is(state, "closed", "Received expected onsignalingstatechange event 'closed'");
+        }
+
         test.pcLocal.close();
 
         try { description = test.pcLocal.localDescription; } catch (e) { exception = e; }
         ok(exception, "Attempt to access localDescription of pcLocal after close throws exception");
         exception = null;
 
         try { description = test.pcLocal.remoteDescription; } catch (e) { exception = e; }
         ok(exception, "Attempt to access remoteDescription of pcLocal after close throws exception");
         exception = null;
 
+        // handle the event which the close() triggers
+        test.pcRemote.onsignalingstatechange = function (state) {
+          is(state, "closed", "Received expected onsignalingstatechange event 'closed'");
+        }
+
         test.pcRemote.close();
 
         try  { description = test.pcRemote.localDescription; } catch (e) { exception = e; }
         ok(exception, "Attempt to access localDescription of pcRemote after close throws exception");
         exception = null;
 
         try  { description = test.pcRemote.remoteDescription; } catch (e) { exception = e; }
         ok(exception, "Attempt to access remoteDescription of pcRemote after close throws exception");
--- a/dom/media/tests/mochitest/test_peerConnection_bug835370.html
+++ b/dom/media/tests/mochitest/test_peerConnection_bug835370.html
@@ -44,16 +44,18 @@
     try { pconnect.createOffer(step1, failed, { optional: [1] }); } catch (e) { exception = e; }
     ok(exception, "createOffer(step1, failed, { optional: [1] }) throws");
     exception = null;
     try { pconnect.createOffer(step1, failed, { optional: [{ OfferToReceiveVideo: false, OfferToReceiveAudio: true, }] }); } catch (e) { exception = e; }
     ok(exception, "createOffer(step1, failed, { optional: [{ OfferToReceiveVideo: false, OfferToReceiveAudio: true, }] }) throws");
     exception = null;
     try { pconnects.createOffer(step1, failed, { mandatory: { OfferToReceiveVideo: false, OfferToReceiveAudio: true, MozDontOfferDataChannel: true}, optional: [{ VoiceActivityDetection: true }, { FooBar: "42"  }] }); } catch (e) { exception = e; }
     ok(!exception, "createOffer(step1, failed, { mandatory: { OfferToReceiveVideo: false, OfferToReceiveAudio: true, MozDontOfferDataChannel: true}, optional: [{ VoiceActivityDetection: true }, { FooBar: \"42\"  }] }) succeeds");
+    pconnect.close();
+    pconnects.close();
     pconnect = null;
     pconnects = null;
     SimpleTest.finish();
   });
 </script>
 </pre>
 </body>
 </html>
--- a/dom/promise/Promise.h
+++ b/dom/promise/Promise.h
@@ -73,25 +73,27 @@ public:
                                      JS::Handle<JS::Value> aValue);
 
   void MaybeResolve(JSContext* aCx,
                     JS::Handle<JS::Value> aValue);
   void MaybeReject(JSContext* aCx,
                    JS::Handle<JS::Value> aValue);
 
   // Helpers for using Promise from C++.
-  // Most DOM objects are handled already.  To add a new type T, such as ints,
-  // or dictionaries, add a ToJSValue overload in ToJSValue.h.
+  // Most DOM objects are handled already.  To add a new type T, add a
+  // ToJSValue overload in ToJSValue.h.
+  // aArg is a const reference so we can pass rvalues like integer constants
   template <typename T>
-  void MaybeResolve(T& aArg) {
+  void MaybeResolve(const T& aArg) {
     MaybeSomething(aArg, &Promise::MaybeResolve);
   }
 
+  // aArg is a const reference so we can pass rvalues like NS_ERROR_*
   template <typename T>
-  void MaybeReject(T& aArg) {
+  void MaybeReject(const T& aArg) {
     MaybeSomething(aArg, &Promise::MaybeReject);
   }
 
   // WebIDL
 
   nsIGlobalObject* GetParentObject() const
   {
     return mGlobal;
--- a/dom/webidl/Console.webidl
+++ b/dom/webidl/Console.webidl
@@ -42,17 +42,22 @@ dictionary ConsoleEvent {
   DOMString functionName = "";
   double timeStamp = 0;
   sequence<any> arguments;
 
   // This array will only hold strings or null elements.
   sequence<any> styles;
 
   boolean private = false;
-  sequence<ConsoleStackEntry> stacktrace;
+  // stacktrace is handled via a getter in some cases so we can construct it
+  // lazily.  Note that we're not making this whole thing an interface because
+  // consumers expect to see own properties on it, which would mean making the
+  // props unforgeable, which means lots of JSFunction allocations.  Maybe we
+  // should fix those consumers, of course....
+  // sequence<ConsoleStackEntry> stacktrace;
   DOMString groupName = "";
   any timer = null;
   any counter = null;
 };
 
 // Event for profile operations
 dictionary ConsoleProfileEvent {
   DOMString action = "";
--- a/dom/webidl/HTMLLinkElement.webidl
+++ b/dom/webidl/HTMLLinkElement.webidl
@@ -23,18 +23,17 @@ interface HTMLLinkElement : HTMLElement 
            attribute DOMString rel;
   readonly attribute DOMTokenList relList;
   [SetterThrows, Pure]
            attribute DOMString media;
   [SetterThrows, Pure]
            attribute DOMString hreflang;
   [SetterThrows, Pure]
            attribute DOMString type;
-// Not supported yet:
-//  [PutForwards=value] readonly attribute DOMSettableTokenList sizes;
+  [PutForwards=value] readonly attribute DOMSettableTokenList sizes;
 };
 HTMLLinkElement implements LinkStyle;
 
 // http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
 partial interface HTMLLinkElement {
   [SetterThrows, Pure]
            attribute DOMString charset;
   [SetterThrows, Pure]
rename from gfx/layers/ipc/GeckoContentController.h
rename to gfx/layers/apz/public/GeckoContentController.h
rename from gfx/layers/composite/APZCTreeManager.cpp
rename to gfx/layers/apz/src/APZCTreeManager.cpp
--- a/gfx/layers/composite/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -1,22 +1,22 @@
 /* -*- Mode: C++; tab-width: 8; 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 "APZCTreeManager.h"
-#include "AsyncCompositionManager.h"    // for ViewTransform
 #include "Compositor.h"                 // for Compositor
 #include "CompositorParent.h"           // for CompositorParent, etc
 #include "InputData.h"                  // for InputData, etc
 #include "Layers.h"                     // for ContainerLayer, Layer, etc
 #include "gfx3DMatrix.h"                // for gfx3DMatrix
 #include "mozilla/dom/Touch.h"          // for Touch
 #include "mozilla/gfx/Point.h"          // for Point
+#include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform
 #include "mozilla/layers/AsyncPanZoomController.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/mozalloc.h"           // for operator new
 #include "mozilla/TouchEvents.h"
 #include "mozilla/Preferences.h"        // for Preferences
 #include "nsDebug.h"                    // for NS_WARNING
 #include "nsPoint.h"                    // for nsIntPoint
 #include "nsThreadUtils.h"              // for NS_IsMainThread
rename from gfx/layers/composite/APZCTreeManager.h
rename to gfx/layers/apz/src/APZCTreeManager.h
rename from gfx/layers/ipc/AsyncPanZoomController.cpp
rename to gfx/layers/apz/src/AsyncPanZoomController.cpp
--- a/gfx/layers/ipc/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -9,17 +9,16 @@
 #include <sys/types.h>                  // for int32_t
 #include <algorithm>                    // for max, min
 #include "AnimationCommon.h"            // for ComputedTimingFunction
 #include "AsyncPanZoomController.h"     // for AsyncPanZoomController, etc
 #include "CompositorParent.h"           // for CompositorParent
 #include "FrameMetrics.h"               // for FrameMetrics, etc
 #include "GestureEventListener.h"       // for GestureEventListener
 #include "InputData.h"                  // for MultiTouchInput, etc
-#include "LayerTransactionParent.h"     // for LayerTransactionParent
 #include "Units.h"                      // for CSSRect, CSSPoint, etc
 #include "UnitTransforms.h"             // for TransformTo
 #include "base/message_loop.h"          // for MessageLoop
 #include "base/task.h"                  // for NewRunnableMethod, etc
 #include "base/tracked.h"               // for FROM_HERE
 #include "gfxPrefs.h"                   // for gfxPrefs
 #include "gfxTypes.h"                   // for gfxFloat
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
@@ -35,17 +34,17 @@
 #include "mozilla/gfx/BasePoint.h"      // for BasePoint
 #include "mozilla/gfx/BaseRect.h"       // for BaseRect
 #include "mozilla/gfx/Point.h"          // for Point, RoundedToInt, etc
 #include "mozilla/gfx/Rect.h"           // for RoundedIn
 #include "mozilla/gfx/ScaleFactor.h"    // for ScaleFactor
 #include "mozilla/layers/APZCTreeManager.h"  // for ScrollableLayerGuid
 #include "mozilla/layers/AsyncCompositionManager.h"  // for ViewTransform
 #include "mozilla/layers/Axis.h"        // for AxisX, AxisY, Axis, etc
-#include "mozilla/layers/GeckoContentController.h"
+#include "mozilla/layers/LayerTransactionParent.h" // for LayerTransactionParent
 #include "mozilla/layers/PCompositorParent.h" // for PCompositorParent
 #include "mozilla/layers/TaskThrottler.h"  // for TaskThrottler
 #include "mozilla/mozalloc.h"           // for operator new, etc
 #include "mozilla/unused.h"             // for unused
 #include "mozilla/FloatingPoint.h"      // for FuzzyEqualsMultiplicative
 #include "nsAlgorithm.h"                // for clamped
 #include "nsAutoPtr.h"                  // for nsRefPtr
 #include "nsCOMPtr.h"                   // for already_AddRefed
@@ -614,19 +613,20 @@ nsEventStatus AsyncPanZoomController::On
       // Fall through.
     case FLING:
       CancelAnimation();
       // Fall through.
     case NOTHING: {
       mX.StartTouch(point.x);
       mY.StartTouch(point.y);
       APZCTreeManager* treeManagerLocal = mTreeManager;
-      if (treeManagerLocal) {
+      nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
+      if (treeManagerLocal && controller) {
         bool touchCanBePan = treeManagerLocal->CanBePanned(this);
-        mGeckoContentController->NotifyAPZStateChange(
+        controller->NotifyAPZStateChange(
             GetGuid(), APZStateChange::StartTouch, touchCanBePan);
       }
       SetState(TOUCHING);
       break;
     }
     case TOUCHING:
     case PANNING:
     case PANNING_LOCKED_X:
@@ -957,18 +957,20 @@ nsEventStatus AsyncPanZoomController::Ge
       mTouchBlockState.mSingleTapOccurred = true;
       return nsEventStatus_eConsumeNoDefault;
     }
   }
   return nsEventStatus_eIgnore;
 }
 
 void AsyncPanZoomController::OnTouchEndOrCancel() {
-  mGeckoContentController->NotifyAPZStateChange(
-      GetGuid(), APZStateChange::EndTouch, mTouchBlockState.mSingleTapOccurred);
+  if (nsRefPtr<GeckoContentController> controller = GetGeckoContentController()) {
+    controller->NotifyAPZStateChange(
+        GetGuid(), APZStateChange::EndTouch, mTouchBlockState.mSingleTapOccurred);
+  }
 }
 
 nsEventStatus AsyncPanZoomController::OnSingleTapUp(const TapGestureInput& aEvent) {
   APZC_LOG("%p got a single-tap-up in state %d\n", this, mState);
   // If mZoomConstraints.mAllowDoubleTapZoom is true we wait for a call to OnSingleTapConfirmed before
   // sending event to content
   if (!mZoomConstraints.mAllowDoubleTapZoom) {
     return GenerateSingleTap(aEvent.mPoint, aEvent.modifiers);
@@ -1103,17 +1105,19 @@ nsEventStatus AsyncPanZoomController::St
     if (GetAxisLockMode() == FREE) {
       SetState(PANNING);
     } else {
       HandlePanning(angle);
     }
   }
 
   if (IsPanningState(mState)) {
-    mGeckoContentController->NotifyAPZStateChange(GetGuid(), APZStateChange::StartPanning);
+    if (nsRefPtr<GeckoContentController> controller = GetGeckoContentController()) {
+      controller->NotifyAPZStateChange(GetGuid(), APZStateChange::StartPanning);
+    }
     return nsEventStatus_eConsumeNoDefault;
   }
   // Don't consume an event that didn't trigger a panning.
   return nsEventStatus_eIgnore;
 }
 
 void AsyncPanZoomController::UpdateWithTouchAtDevicePoint(const MultiTouchInput& aEvent) {
   ScreenIntPoint point = GetFirstTouchScreenPoint(aEvent);
@@ -2002,22 +2006,22 @@ void AsyncPanZoomController::SetState(Pa
 
   // Intentional scoping for mutex
   {
     ReentrantMonitorAutoEnter lock(mMonitor);
     oldState = mState;
     mState = aNewState;
   }
 
-  if (mGeckoContentController) {
+  if (nsRefPtr<GeckoContentController> controller = GetGeckoContentController()) {
     if (!IsTransformingState(oldState) && IsTransformingState(aNewState)) {
-      mGeckoContentController->NotifyAPZStateChange(
+      controller->NotifyAPZStateChange(
           GetGuid(), APZStateChange::TransformBegin);
     } else if (IsTransformingState(oldState) && !IsTransformingState(aNewState)) {
-      mGeckoContentController->NotifyAPZStateChange(
+      controller->NotifyAPZStateChange(
           GetGuid(), APZStateChange::TransformEnd);
     }
   }
 }
 
 bool AsyncPanZoomController::IsTransformingState(PanZoomState aState) {
   return !(aState == NOTHING || aState == TOUCHING || aState == WAITING_CONTENT_RESPONSE);
 }
rename from gfx/layers/ipc/AsyncPanZoomController.h
rename to gfx/layers/apz/src/AsyncPanZoomController.h
--- a/gfx/layers/ipc/AsyncPanZoomController.h
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -3,17 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_layers_AsyncPanZoomController_h
 #define mozilla_layers_AsyncPanZoomController_h
 
 #include "CrossProcessMutex.h"
-#include "GeckoContentController.h"
+#include "mozilla/layers/GeckoContentController.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/EventForwards.h"
 #include "mozilla/Monitor.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/Atomics.h"
 #include "InputData.h"
 #include "Axis.h"
rename from gfx/layers/ipc/Axis.cpp
rename to gfx/layers/apz/src/Axis.cpp
rename from gfx/layers/ipc/Axis.h
rename to gfx/layers/apz/src/Axis.h
rename from gfx/layers/ipc/GestureEventListener.cpp
rename to gfx/layers/apz/src/GestureEventListener.cpp
rename from gfx/layers/ipc/GestureEventListener.h
rename to gfx/layers/apz/src/GestureEventListener.h
rename from gfx/layers/ipc/TaskThrottler.cpp
rename to gfx/layers/apz/src/TaskThrottler.cpp
rename from gfx/layers/ipc/TaskThrottler.h
rename to gfx/layers/apz/src/TaskThrottler.h
rename from widget/xpwidgets/APZCCallbackHelper.cpp
rename to gfx/layers/apz/util/APZCCallbackHelper.cpp
--- a/widget/xpwidgets/APZCCallbackHelper.cpp
+++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp
@@ -7,17 +7,17 @@
 #include "gfxPrefs.h" // For gfxPrefs::LayersTilesEnabled, LayersTileWidth/Height
 #include "mozilla/Preferences.h"
 #include "nsIScrollableFrame.h"
 #include "nsLayoutUtils.h"
 #include "nsIDOMElement.h"
 #include "nsIInterfaceRequestorUtils.h"
 
 namespace mozilla {
-namespace widget {
+namespace layers {
 
 bool
 APZCCallbackHelper::HasValidPresShellId(nsIDOMWindowUtils* aUtils,
                                         const FrameMetrics& aMetrics)
 {
     MOZ_ASSERT(aUtils);
 
     uint32_t presShellId;
rename from widget/xpwidgets/APZCCallbackHelper.h
rename to gfx/layers/apz/util/APZCCallbackHelper.h
--- a/widget/xpwidgets/APZCCallbackHelper.h
+++ b/gfx/layers/apz/util/APZCCallbackHelper.h
@@ -1,23 +1,23 @@
 /* -*- 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/. */
 
-#ifndef __mozilla_widget_APZCCallbackHelper_h__
-#define __mozilla_widget_APZCCallbackHelper_h__
+#ifndef mozilla_layers_APZCCallbackHelper_h
+#define mozilla_layers_APZCCallbackHelper_h
 
 #include "FrameMetrics.h"
 #include "nsIContent.h"
 #include "nsIDocument.h"
 #include "nsIDOMWindowUtils.h"
 
 namespace mozilla {
-namespace widget {
+namespace layers {
 
 /* This class contains some helper methods that facilitate implementing the
    GeckoContentController callback interface required by the AsyncPanZoomController.
    Since different platforms need to implement this interface in similar-but-
    not-quite-the-same ways, this utility class provides some helpful methods
    to hold code that can be shared across the different platform implementations.
  */
 class APZCCallbackHelper
@@ -95,9 +95,9 @@ public:
     static nsIntPoint ApplyCallbackTransform(const nsIntPoint& aPoint,
                                              const ScrollableLayerGuid& aGuid,
                                              const CSSToLayoutDeviceScale& aScale);
 };
 
 }
 }
 
-#endif /*__mozilla_widget_APZCCallbackHelper_h__ */
+#endif /* mozilla_layers_APZCCallbackHelper_h */
rename from widget/xpwidgets/ActiveElementManager.cpp
rename to gfx/layers/apz/util/ActiveElementManager.cpp
--- a/widget/xpwidgets/ActiveElementManager.cpp
+++ b/gfx/layers/apz/util/ActiveElementManager.cpp
@@ -10,17 +10,17 @@
 #include "inIDOMUtils.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMEventTarget.h"
 #include "base/message_loop.h"
 #include "base/task.h"
 
 namespace mozilla {
-namespace widget {
+namespace layers {
 
 static int32_t sActivationDelayMs = 100;
 static bool sActivationDelayMsSet = false;
 
 ActiveElementManager::ActiveElementManager()
   : mDomUtils(services::GetInDOMUtils()),
     mCanBePan(false),
     mCanBePanSet(false),
@@ -143,9 +143,9 @@ ActiveElementManager::CancelTask()
 {
   if (mSetActiveTask) {
     mSetActiveTask->Cancel();
     mSetActiveTask = nullptr;
   }
 }
 
 }
-}
\ No newline at end of file
+}
rename from widget/xpwidgets/ActiveElementManager.h
rename to gfx/layers/apz/util/ActiveElementManager.h
--- a/widget/xpwidgets/ActiveElementManager.h
+++ b/gfx/layers/apz/util/ActiveElementManager.h
@@ -1,26 +1,26 @@
 /* -*- 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/. */
 
-#ifndef __mozilla_widget_ActiveElementManager_h__
-#define __mozilla_widget_ActiveElementManager_h__
+#ifndef mozilla_layers_ActiveElementManager_h
+#define mozilla_layers_ActiveElementManager_h
 
 #include "nsCOMPtr.h"
 #include "nsISupportsImpl.h"
 
 class inIDOMUtils;
 class nsIDOMEventTarget;
 class nsIDOMElement;
 class CancelableTask;
 
 namespace mozilla {
-namespace widget {
+namespace layers {
 
 /**
  * Manages setting and clearing the ':active' CSS pseudostate in the presence
  * of touch input.
  */
 class ActiveElementManager {
 public:
   NS_INLINE_DECL_REFCOUNTING(ActiveElementManager)
@@ -77,9 +77,9 @@ private:
   void ResetActive();
   void SetActiveTask(nsIDOMElement* aTarget);
   void CancelTask();
 };
 
 }
 }
 
-#endif /*__mozilla_widget_ActiveElementManager_h__ */
+#endif /* mozilla_layers_ActiveElementManager_h */
--- a/gfx/layers/ipc/ImageBridgeParent.cpp
+++ b/gfx/layers/ipc/ImageBridgeParent.cpp
@@ -29,17 +29,16 @@
 #include "nsDebug.h"                    // for NS_RUNTIMEABORT, etc
 #include "nsISupportsImpl.h"            // for ImageBridgeParent::Release, etc
 #include "nsTArray.h"                   // for nsTArray, nsTArray_Impl
 #include "nsTArrayForwardDeclare.h"     // for InfallibleTArray
 #include "nsXULAppAPI.h"                // for XRE_GetIOMessageLoop
 #include "mozilla/layers/TextureHost.h"
 #include "nsThreadUtils.h"
 
-using namespace base;
 using namespace mozilla::ipc;
 using namespace mozilla::gfx;
 
 namespace mozilla {
 namespace layers {
 
 class PGrallocBufferParent;
 
@@ -113,25 +112,25 @@ ImageBridgeParent::RecvUpdateNoSwap(cons
   bool success = RecvUpdate(aEdits, &noReplies);
   NS_ABORT_IF_FALSE(noReplies.Length() == 0, "RecvUpdateNoSwap requires a sync Update to carry Edits");
   return success;
 }
 
 static void
 ConnectImageBridgeInParentProcess(ImageBridgeParent* aBridge,
                                   Transport* aTransport,
-                                  ProcessHandle aOtherProcess)
+                                  base::ProcessHandle aOtherProcess)
 {
   aBridge->Open(aTransport, aOtherProcess, XRE_GetIOMessageLoop(), ipc::ParentSide);
 }
 
 /*static*/ PImageBridgeParent*
 ImageBridgeParent::Create(Transport* aTransport, ProcessId aOtherProcess)
 {
-  ProcessHandle processHandle;
+  base::ProcessHandle processHandle;
   if (!base::OpenProcessHandle(aOtherProcess, &processHandle)) {
     return nullptr;
   }
 
   MessageLoop* loop = CompositorParent::CompositorLoop();
   nsRefPtr<ImageBridgeParent> bridge = new ImageBridgeParent(loop, aTransport);
   bridge->mSelfRef = bridge;
   loop->PostTask(FROM_HERE,
--- a/gfx/layers/ipc/LayerTransactionParent.cpp
+++ b/gfx/layers/ipc/LayerTransactionParent.cpp
@@ -36,17 +36,17 @@
 #include "nsISupportsImpl.h"            // for Layer::Release, etc
 #include "nsLayoutUtils.h"              // for nsLayoutUtils
 #include "nsMathUtils.h"                // for NS_round
 #include "nsPoint.h"                    // for nsPoint
 #include "nsTArray.h"                   // for nsTArray, nsTArray_Impl, etc
 #include "GeckoProfiler.h"
 #include "mozilla/layers/TextureHost.h"
 #include "mozilla/layers/AsyncCompositionManager.h"
-#include "AsyncPanZoomController.h"
+#include "mozilla/layers/AsyncPanZoomController.h"
 
 typedef std::vector<mozilla::layers::EditReply> EditReplyVector;
 
 using mozilla::layout::RenderFrameParent;
 
 namespace mozilla {
 namespace layers {
 
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -92,64 +92,68 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'wind
             'd3d11/CompositorD3D11.cpp',
         ]
 
 EXPORTS.gfxipc += [
     'ipc/ShadowLayerUtils.h',
 ]
 
 EXPORTS.mozilla.layers += [
+    'apz/public/GeckoContentController.h',
+    # exporting things from apz/src is temporary until we extract a
+    # proper interface for the code there
+    'apz/src/APZCTreeManager.h',
+    'apz/src/AsyncPanZoomController.h',
+    'apz/src/Axis.h',
+    'apz/src/GestureEventListener.h',
+    'apz/src/TaskThrottler.h',
+    'apz/util/ActiveElementManager.h',
+    'apz/util/APZCCallbackHelper.h',
     'AtomicRefCountedWithFinalize.h',
     'basic/BasicCompositor.h',
     'basic/MacIOSurfaceTextureHostBasic.h',
     'basic/TextureHostBasic.h',
     'client/CanvasClient.h',
     'client/CompositableClient.h',
     'client/ContentClient.h',
     'client/ImageClient.h',
     'client/SimpleTextureClientPool.h',
     'client/SimpleTiledContentClient.h',
     'client/TextureClient.h',
     'client/TextureClientPool.h',
     'client/TiledContentClient.h',
-    'composite/APZCTreeManager.h',
     'composite/AsyncCompositionManager.h',
     'composite/CanvasLayerComposite.h',
     'composite/ColorLayerComposite.h',
     'composite/ContainerLayerComposite.h',
     'composite/ContentHost.h',
     'composite/ImageHost.h',
     'composite/ImageLayerComposite.h',
     'composite/LayerManagerComposite.h',
     'composite/TextureHost.h',
     'composite/ThebesLayerComposite.h',
     'Compositor.h',
     'CompositorTypes.h',
     'D3D9SurfaceImage.h',
     'Effects.h',
     'ImageDataSerializer.h',
-    'ipc/AsyncPanZoomController.h',
-    'ipc/Axis.h',
     'ipc/CompositableForwarder.h',
     'ipc/CompositableTransactionParent.h',
     'ipc/CompositorChild.h',
     'ipc/CompositorParent.h',
     'ipc/FenceUtils.h',
-    'ipc/GeckoContentController.h',
-    'ipc/GestureEventListener.h',
     'ipc/ImageBridgeChild.h',
     'ipc/ImageBridgeParent.h',
     'ipc/ISurfaceAllocator.h',
     'ipc/LayerTransactionChild.h',
     'ipc/LayerTransactionParent.h',
     'ipc/ShadowLayers.h',
     'ipc/ShadowLayersManager.h',
     'ipc/SharedPlanarYCbCrImage.h',
     'ipc/SharedRGBImage.h',
-    'ipc/TaskThrottler.h',
     'LayersTypes.h',
     'opengl/CompositingRenderTargetOGL.h',
     'opengl/CompositorOGL.h',
     'opengl/GrallocTextureClient.h',
     'opengl/GrallocTextureHost.h',
     'opengl/MacIOSurfaceTextureClientOGL.h',
     'opengl/MacIOSurfaceTextureHostOGL.h',
     'opengl/TextureClientOGL.h',
@@ -211,16 +215,23 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk
         EXPORTS.mozilla.layers += [
             'ipc/FenceUtilsGonk.h',
         ]
         SOURCES += [
             'ipc/FenceUtilsGonk.cpp',
         ]
 
 UNIFIED_SOURCES += [
+    'apz/src/APZCTreeManager.cpp',
+    'apz/src/AsyncPanZoomController.cpp',
+    'apz/src/Axis.cpp',
+    'apz/src/GestureEventListener.cpp',
+    'apz/src/TaskThrottler.cpp',
+    'apz/util/ActiveElementManager.cpp',
+    'apz/util/APZCCallbackHelper.cpp',
     'basic/BasicCanvasLayer.cpp',
     'basic/BasicColorLayer.cpp',
     'basic/BasicCompositor.cpp',
     'basic/BasicContainerLayer.cpp',
     'basic/BasicImages.cpp',
     'basic/BasicLayerManager.cpp',
     'basic/BasicLayersImpl.cpp',
     'basic/BasicThebesLayer.cpp',
@@ -237,17 +248,16 @@ UNIFIED_SOURCES += [
     'client/CompositableClient.cpp',
     'client/ContentClient.cpp',
     'client/ImageClient.cpp',
     'client/SimpleTextureClientPool.cpp',
     'client/SimpleTiledContentClient.cpp',
     'client/TextureClient.cpp',
     'client/TextureClientPool.cpp',
     'client/TiledContentClient.cpp',
-    'composite/APZCTreeManager.cpp',
     'composite/AsyncCompositionManager.cpp',
     'composite/CanvasLayerComposite.cpp',
     'composite/ColorLayerComposite.cpp',
     'composite/CompositableHost.cpp',
     'composite/ContainerLayerComposite.cpp',
     'composite/ContentHost.cpp',
     'composite/ImageHost.cpp',
     'composite/ImageLayerComposite.cpp',
@@ -256,33 +266,29 @@ UNIFIED_SOURCES += [
     'composite/TextureHost.cpp',
     'composite/ThebesLayerComposite.cpp',
     'composite/TiledContentHost.cpp',
     'Compositor.cpp',
     'CopyableCanvasLayer.cpp',
     'Effects.cpp',
     'ImageDataSerializer.cpp',
     'ImageLayers.cpp',
-    'ipc/AsyncPanZoomController.cpp',
-    'ipc/Axis.cpp',
     'ipc/CompositableTransactionParent.cpp',
     'ipc/CompositorChild.cpp',
     'ipc/CompositorParent.cpp',
-    'ipc/GestureEventListener.cpp',
     'ipc/ImageBridgeChild.cpp',
     'ipc/ImageBridgeParent.cpp',
     'ipc/ISurfaceAllocator.cpp',
     'ipc/LayerTransactionChild.cpp',
     'ipc/LayerTransactionParent.cpp',
     'ipc/ShadowLayerChild.cpp',
     'ipc/ShadowLayerParent.cpp',
     'ipc/ShadowLayers.cpp',
     'ipc/SharedPlanarYCbCrImage.cpp',
     'ipc/SharedRGBImage.cpp',
-    'ipc/TaskThrottler.cpp',
     'LayerScope.cpp',
     'LayersLogging.cpp',
     'LayerSorter.cpp',
     'LayerUtils.cpp',
     'opengl/CompositingRenderTargetOGL.cpp',
     'opengl/CompositorOGL.cpp',
     'opengl/OGLShaderProgram.cpp',
     'opengl/TextureClientOGL.cpp',
--- a/gfx/thebes/gfxMacPlatformFontList.mm
+++ b/gfx/thebes/gfxMacPlatformFontList.mm
@@ -825,23 +825,23 @@ gfxMacPlatformFontList::RegisteredFontsC
                                                                    CFStringRef name,
                                                                    const void *object,
                                                                    CFDictionaryRef userInfo)
 {
     if (!::CFEqual(name, kCTFontManagerRegisteredFontsChangedNotification)) {
         return;
     }
 
+    gfxMacPlatformFontList* fl = static_cast<gfxMacPlatformFontList*>(observer);
+
     // xxx - should be carefully pruning the list of fonts, not rebuilding it from scratch
-    static_cast<gfxMacPlatformFontList*>(observer)->UpdateFontList();
+    fl->UpdateFontList();
 
     // modify a preference that will trigger reflow everywhere
-    static const char kPrefName[] = "font.internaluseonly.changed";
-    bool fontInternalChange = Preferences::GetBool(kPrefName, false);
-    Preferences::SetBool(kPrefName, !fontInternalChange);
+    fl->ForceGlobalReflow();
 }
 
 gfxFontEntry*
 gfxMacPlatformFontList::GlobalFontFallback(const uint32_t aCh,
                                            int32_t aRunScript,
                                            const gfxFontStyle* aMatchStyle,
                                            uint32_t& aCmapCount,
                                            gfxFontFamily** aMatchedFamily)
--- a/gfx/thebes/gfxPlatformFontList.cpp
+++ b/gfx/thebes/gfxPlatformFontList.cpp
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifdef MOZ_LOGGING
 #define FORCE_PR_LOG /* Allow logging in the release build */
 #endif
 #include "prlog.h"
 
 #include "gfxPlatformFontList.h"
+#include "gfxUserFontSet.h"
 
 #include "nsUnicharUtils.h"
 #include "nsUnicodeRange.h"
 #include "nsUnicodeProperties.h"
 
 #include "mozilla/Attributes.h"
 #include "mozilla/Likely.h"
 #include "mozilla/MemoryReporting.h"
@@ -298,16 +299,31 @@ gfxPlatformFontList::ResolveFontName(con
     gfxFontFamily *family = FindFamily(aFontName);
     if (family) {
         aResolvedFontName = family->Name();
         return true;
     }
     return false;
 }
 
+static PLDHashOperator
+RebuildLocalFonts(nsPtrHashKey<gfxUserFontSet>* aKey,
+                  void* aClosure)
+{
+    aKey->GetKey()->RebuildLocalRules();
+    return PL_DHASH_NEXT;
+}
+
+void
+gfxPlatformFontList::UpdateFontList()
+{
+    InitFontList();
+    mUserFontSetList.EnumerateEntries(RebuildLocalFonts, nullptr);
+}
+
 struct FontListData {
     FontListData(nsIAtom *aLangGroup,
                  const nsACString& aGenericFamily,
                  nsTArray<nsString>& aListOfFonts) :
         mLangGroup(aLangGroup), mGenericFamily(aGenericFamily),
         mListOfFonts(aListOfFonts) {}
     nsIAtom *mLangGroup;
     const nsACString& mGenericFamily;
@@ -792,16 +808,21 @@ gfxPlatformFontList::LoadFontInfo()
 #ifdef PR_LOGGING
     if (LOG_FONTINIT_ENABLED()) {
         TimeDuration elapsed = TimeStamp::Now() - start;
         LOG_FONTINIT(("(fontinit) fontloader load pass %8.2f ms done %s\n",
                       elapsed.ToMilliseconds(), (done ? "true" : "false")));
     }
 #endif
 
+    if (done) {
+        mOtherFamilyNamesInitialized = true;
+        mFaceNamesInitialized = true;
+    }
+
     return done;
 }
 
 void 
 gfxPlatformFontList::CleanupLoader()
 {
     mFontFamiliesToLoad.Clear();
     mNumFamilies = 0;
@@ -815,18 +836,16 @@ gfxPlatformFontList::CleanupLoader()
                       mFontInfo->mLoadStats.families,
                       mFontInfo->mLoadStats.fonts,
                       mFontInfo->mLoadStats.cmaps,
                       mFontInfo->mLoadStats.facenames,
                       mFontInfo->mLoadStats.othernames));
     }
 #endif
 
-    mOtherFamilyNamesInitialized = true;
-    mFaceNamesInitialized = true;
     gfxFontInfoLoader::CleanupLoader();
 }
 
 void
 gfxPlatformFontList::GetPrefsAndStartLoader()
 {
     mIncrement =
         std::max(1u, Preferences::GetUint(FONT_LOADER_FAMILIES_PER_SLICE_PREF));
@@ -834,16 +853,25 @@ gfxPlatformFontList::GetPrefsAndStartLoa
     uint32_t delay =
         std::max(1u, Preferences::GetUint(FONT_LOADER_DELAY_PREF));
     uint32_t interval =
         std::max(1u, Preferences::GetUint(FONT_LOADER_INTERVAL_PREF));
 
     StartLoader(delay, interval);
 }
 
+void
+gfxPlatformFontList::ForceGlobalReflow()
+{
+    // modify a preference that will trigger reflow everywhere
+    static const char kPrefName[] = "font.internaluseonly.changed";
+    bool fontInternalChange = Preferences::GetBool(kPrefName, false);
+    Preferences::SetBool(kPrefName, !fontInternalChange);
+}
+
 // Support for memory reporting
 
 static size_t
 SizeOfFamilyEntryExcludingThis(const nsAString&               aKey,
                                const nsRefPtr<gfxFontFamily>& aFamily,
                                MallocSizeOf                   aMallocSizeOf,
                                void*                          aUserArg)
 {
--- a/gfx/thebes/gfxPlatformFontList.h
+++ b/gfx/thebes/gfxPlatformFontList.h
@@ -77,16 +77,18 @@ protected:
 struct FontListSizes {
     uint32_t mFontListSize; // size of the font list and dependent objects
                             // (font family and face names, etc), but NOT
                             // including the font table cache and the cmaps
     uint32_t mFontTableCacheSize; // memory used for the gfxFontEntry table caches
     uint32_t mCharMapsSize; // memory used for cmap coverage info
 };
 
+class gfxUserFontSet;
+
 class gfxPlatformFontList : public gfxFontInfoLoader
 {
 public:
     static gfxPlatformFontList* PlatformFontList() {
         return sPlatformFontList;
     }
 
     static nsresult Init() {
@@ -110,17 +112,17 @@ public:
 
     void GetFontList (nsIAtom *aLangGroup,
                       const nsACString& aGenericFamily,
                       nsTArray<nsString>& aListOfFonts);
 
     virtual bool ResolveFontName(const nsAString& aFontName,
                                    nsAString& aResolvedFontName);
 
-    void UpdateFontList() { InitFontList(); }
+    void UpdateFontList();
 
     void ClearPrefFonts() { mPrefFonts.Clear(); }
 
     virtual void GetFontFamilyList(nsTArray<nsRefPtr<gfxFontFamily> >& aFamilyArray);
 
     virtual gfxFontEntry*
     SystemFindFontForChar(const uint32_t aCh,
                           int32_t aRunScript,
@@ -173,16 +175,25 @@ public:
     gfxCharacterMap* FindCharMap(gfxCharacterMap *aCmap);
 
     // add a cmap to the shared cmap set
     gfxCharacterMap* AddCmap(const gfxCharacterMap *aCharMap);
 
     // remove the cmap from the shared cmap set
     void RemoveCmap(const gfxCharacterMap *aCharMap);
 
+    // keep track of userfont sets to notify when global fontlist changes occur
+    void AddUserFontSet(gfxUserFontSet *aUserFontSet) {
+        mUserFontSetList.PutEntry(aUserFontSet);
+    }
+
+    void RemoveUserFontSet(gfxUserFontSet *aUserFontSet) {
+        mUserFontSetList.RemoveEntry(aUserFontSet);
+    }
+
 protected:
     class MemoryReporter MOZ_FINAL : public nsIMemoryReporter
     {
     public:
         NS_DECL_ISUPPORTS
         NS_DECL_NSIMEMORYREPORTER
     };
 
@@ -249,16 +260,19 @@ protected:
     // gfxFontInfoLoader overrides, used to load in font cmaps
     virtual void InitLoader();
     virtual bool LoadFontInfo();
     virtual void CleanupLoader();
 
     // read the loader initialization prefs, and start it
     void GetPrefsAndStartLoader();
 
+    // for font list changes that affect all documents
+    void ForceGlobalReflow();
+
     // used by memory reporter to accumulate sizes of family names in the hash
     static size_t
     SizeOfFamilyNameEntryExcludingThis(const nsAString&               aKey,
                                        const nsRefPtr<gfxFontFamily>& aFamily,
                                        mozilla::MallocSizeOf          aMallocSizeOf,
                                        void*                          aUserArg);
 
     // canonical family name ==> family entry (unique, one name per family entry)
@@ -300,11 +314,13 @@ protected:
     // contains weak ptrs to cmaps shared by font entry objects
     nsTHashtable<CharMapHashKey> mSharedCmaps;
 
     // data used as part of the font cmap loading process
     nsTArray<nsRefPtr<gfxFontFamily> > mFontFamiliesToLoad;
     uint32_t mStartIndex;
     uint32_t mIncrement;
     uint32_t mNumFamilies;
+
+    nsTHashtable<nsPtrHashKey<gfxUserFontSet> > mUserFontSetList;
 };
 
 #endif /* GFXPLATFORMFONTLIST_H_ */
--- a/gfx/thebes/gfxUserFontSet.cpp
+++ b/gfx/thebes/gfxUserFontSet.cpp
@@ -13,16 +13,17 @@
 #include "nsUnicharUtils.h"
 #include "nsNetUtil.h"
 #include "nsICacheService.h"
 #include "nsIProtocolHandler.h"
 #include "nsIPrincipal.h"
 #include "gfxFontConstants.h"
 #include "mozilla/Services.h"
 #include "mozilla/gfx/2D.h"
+#include "gfxPlatformFontList.h"
 
 #include "opentype-sanitiser.h"
 #include "ots-memory-stream.h"
 
 using namespace mozilla;
 
 #ifdef PR_LOGGING
 PRLogModuleInfo *
@@ -97,23 +98,31 @@ gfxProxyFontEntry::Matches(const nsTArra
 gfxFont*
 gfxProxyFontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold)
 {
     // cannot create an actual font for a proxy entry
     return nullptr;
 }
 
 gfxUserFontSet::gfxUserFontSet()
-    : mFontFamilies(5)
+    : mFontFamilies(5), mLocalRulesUsed(false)
 {
     IncrementGeneration();
+    gfxPlatformFontList *fp = gfxPlatformFontList::PlatformFontList();
+    if (fp) {
+        fp->AddUserFontSet(this);
+    }
 }
 
 gfxUserFontSet::~gfxUserFontSet()
 {
+    gfxPlatformFontList *fp = gfxPlatformFontList::PlatformFontList();
+    if (fp) {
+        fp->RemoveUserFontSet(this);
+    }
 }
 
 gfxFontEntry*
 gfxUserFontSet::AddFontFace(const nsAString& aFamilyName,
                             const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
                             uint32_t aWeight,
                             int32_t aStretch,
                             uint32_t aItalicStyle,
@@ -534,16 +543,17 @@ gfxUserFontSet::LoadNext(gfxMixedFontFam
         const gfxFontFaceSrc& currSrc = aProxyEntry->mSrcList[aProxyEntry->mSrcIndex];
 
         // src local ==> lookup and load immediately
 
         if (currSrc.mIsLocal) {
             gfxFontEntry *fe =
                 gfxPlatform::GetPlatform()->LookupLocalFont(aProxyEntry,
                                                             currSrc.mLocalName);
+            mLocalRulesUsed = true;
             if (fe) {
                 LOG(("userfonts (%p) [src %d] loaded local: (%s) for (%s) gen: %8.8x\n",
                      this, aProxyEntry->mSrcIndex,
                      NS_ConvertUTF16toUTF8(currSrc.mLocalName).get(),
                      NS_ConvertUTF16toUTF8(aFamily->Name()).get(),
                      uint32_t(mGeneration)));
                 fe->mFeatureSettings.AppendElements(aProxyEntry->mFeatureSettings);
                 fe->mLanguageOverride = aProxyEntry->mLanguageOverride;
@@ -659,16 +669,23 @@ gfxUserFontSet::IncrementGeneration()
 {
     // add one, increment again if zero
     ++sFontSetGeneration;
     if (sFontSetGeneration == 0)
        ++sFontSetGeneration;
     mGeneration = sFontSetGeneration;
 }
 
+void
+gfxUserFontSet::RebuildLocalRules()
+{
+    if (mLocalRulesUsed) {
+        DoRebuildUserFontSet();
+    }
+}
 
 gfxFontEntry*
 gfxUserFontSet::LoadFont(gfxMixedFontFamily *aFamily,
                          gfxProxyFontEntry *aProxy,
                          const uint8_t *aFontData, uint32_t &aLength)
 {
     gfxFontEntry *fe = nullptr;
 
--- a/gfx/thebes/gfxUserFontSet.h
+++ b/gfx/thebes/gfxUserFontSet.h
@@ -237,16 +237,19 @@ public:
 
     // generation - each time a face is loaded, generation is
     // incremented so that the change can be recognized 
     uint64_t GetGeneration() { return mGeneration; }
 
     // increment the generation on font load
     void IncrementGeneration();
 
+    // rebuild if local rules have been used
+    void RebuildLocalRules();
+
     class UserFontCache {
     public:
         // Record a loaded user-font in the cache. This requires that the
         // font-entry's userFontData has been set up already, as it relies
         // on the URI and Principal recorded there.
         static void CacheFont(gfxFontEntry *aFontEntry);
 
         // The given gfxFontEntry is being destroyed, so remove any record that
@@ -413,21 +416,27 @@ protected:
                                         gfxProxyFontEntry *aProxy,
                                         const uint8_t* aData,
                                         uint32_t aLength,
                                         uint32_t& aSaneLength,
                                         bool aIsCompressed);
 
     static bool OTSMessage(void *aUserData, const char *format, ...);
 
+    // helper method for performing the actual userfont set rebuild
+    virtual void DoRebuildUserFontSet() = 0;
+
     // font families defined by @font-face rules
     nsRefPtrHashtable<nsStringHashKey, gfxMixedFontFamily> mFontFamilies;
 
     uint64_t        mGeneration;
 
+    // true when local names have been looked up, false otherwise
+    bool mLocalRulesUsed;
+
     static PRLogModuleInfo* GetUserFontsLog();
 
 private:
     static void CopyWOFFMetadata(const uint8_t* aFontData,
                                  uint32_t aLength,
                                  FallibleTArray<uint8_t>* aMetadata,
                                  uint32_t* aMetaOrigLen);
 };
--- a/gfx/thebes/gfxUtils.cpp
+++ b/gfx/thebes/gfxUtils.cpp
@@ -136,16 +136,76 @@ gfxUtils::UnpremultiplyImageSurface(gfxI
           *dstRow++ = UnpremultiplyValue(a, r);
           *dstRow++ = UnpremultiplyValue(a, g);
           *dstRow++ = UnpremultiplyValue(a, b);
 #endif
         }
     }
 }
 
+TemporaryRef<DataSourceSurface>
+gfxUtils::UnpremultiplyDataSurface(DataSourceSurface* aSurface)
+{
+    // Only premultiply ARGB32
+    if (aSurface->GetFormat() != SurfaceFormat::B8G8R8A8) {
+        return aSurface;
+    }
+
+    DataSourceSurface::MappedSurface map;
+    if (!aSurface->Map(DataSourceSurface::MapType::READ, &map)) {
+        return nullptr;
+    }
+
+    RefPtr<DataSourceSurface> dest = Factory::CreateDataSourceSurfaceWithStride(aSurface->GetSize(),
+                                                                                aSurface->GetFormat(),
+                                                                                map.mStride);
+
+    DataSourceSurface::MappedSurface destMap;
+    if (!dest->Map(DataSourceSurface::MapType::WRITE, &destMap)) {
+        aSurface->Unmap();
+        return nullptr;
+    }
+
+    uint8_t *src = map.mData;
+    uint8_t *dst = destMap.mData;
+
+    for (int32_t i = 0; i < aSurface->GetSize().height; ++i) {
+        uint8_t *srcRow = src + (i * map.mStride);
+        uint8_t *dstRow = dst + (i * destMap.mStride);
+
+        for (int32_t j = 0; j < aSurface->GetSize().width; ++j) {
+#ifdef IS_LITTLE_ENDIAN
+          uint8_t b = *srcRow++;
+          uint8_t g = *srcRow++;
+          uint8_t r = *srcRow++;
+          uint8_t a = *srcRow++;
+
+          *dstRow++ = UnpremultiplyValue(a, b);
+          *dstRow++ = UnpremultiplyValue(a, g);
+          *dstRow++ = UnpremultiplyValue(a, r);
+          *dstRow++ = a;
+#else
+          uint8_t a = *srcRow++;
+          uint8_t r = *srcRow++;
+          uint8_t g = *srcRow++;
+          uint8_t b = *srcRow++;
+
+          *dstRow++ = a;
+          *dstRow++ = UnpremultiplyValue(a, r);
+          *dstRow++ = UnpremultiplyValue(a, g);
+          *dstRow++ = UnpremultiplyValue(a, b);
+#endif
+        }
+    }
+
+    aSurface->Unmap();
+    dest->Unmap();
+    return dest;
+}
+
 void
 gfxUtils::ConvertBGRAtoRGBA(gfxImageSurface *aSourceSurface,
                             gfxImageSurface *aDestSurface) {
     if (!aDestSurface)
         aDestSurface = aSourceSurface;
 
     MOZ_ASSERT(aSourceSurface->Format() == aDestSurface->Format() &&
                aSourceSurface->Width()  == aDestSurface->Width() &&
@@ -181,16 +241,34 @@ gfxUtils::ConvertBGRAtoRGBA(gfxImageSurf
             dst[0] = src[2];
             dst[1] = src[1];
             dst[2] = src[0];
             dst[3] = src[3];
         }
     }
 }
 
+void
+gfxUtils::ConvertBGRAtoRGBA(uint8_t* aData, uint32_t aLength)
+{
+    uint8_t *src = aData;
+    uint8_t *srcEnd = src + aLength;
+
+    uint8_t buffer[4];
+    for (; src != srcEnd; src += 4) {
+        buffer[0] = src[2];
+        buffer[1] = src[1];
+        buffer[2] = src[0];
+
+        src[0] = buffer[0];
+        src[1] = buffer[1];
+        src[2] = buffer[2];
+    }
+}
+
 static bool
 IsSafeImageTransformComponent(gfxFloat aValue)
 {
   return aValue >= -32768 && aValue <= 32767;
 }
 
 #ifndef MOZ_GFX_OPTIMIZE_MOBILE
 /**
--- a/gfx/thebes/gfxUtils.h
+++ b/gfx/thebes/gfxUtils.h
@@ -39,19 +39,21 @@ public:
      *
      * If the source is not gfxImageFormat::ARGB32, no operation is performed.  If
      * aDestSurface is given, the data is copied over.
      */
     static void PremultiplyImageSurface(gfxImageSurface *aSourceSurface,
                                         gfxImageSurface *aDestSurface = nullptr);
     static void UnpremultiplyImageSurface(gfxImageSurface *aSurface,
                                           gfxImageSurface *aDestSurface = nullptr);
+    static mozilla::TemporaryRef<DataSourceSurface> UnpremultiplyDataSurface(DataSourceSurface* aSurface);
 
     static void ConvertBGRAtoRGBA(gfxImageSurface *aSourceSurface,
                                   gfxImageSurface *aDestSurface = nullptr);
+    static void ConvertBGRAtoRGBA(uint8_t* aData, uint32_t aLength);
 
     /**
      * Draw something drawable while working around limitations like bad support
      * for EXTEND_PAD, lack of source-clipping, or cairo / pixman bugs with
      * extreme user-space-to-image-space transforms.
      *
      * The input parameters here usually come from the output of our image
      * snapping algorithm in nsLayoutUtils.cpp.
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -18,16 +18,17 @@
 
 #include "jit/AsmJS.h"
 #include "jit/AsmJSLink.h"
 #include "js/StructuredClone.h"
 #include "vm/ForkJoin.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
 #include "vm/ProxyObject.h"
+#include "vm/TraceLogging.h"
 
 #include "jscntxtinlines.h"
 #include "jsobjinlines.h"
 
 using namespace js;
 using namespace JS;
 
 using mozilla::ArrayLength;
@@ -739,19 +740,19 @@ static const struct TraceKindPair {
 static bool
 CountHeap(JSContext *cx, unsigned argc, jsval *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     RootedValue startValue(cx, UndefinedValue());
     if (args.length() > 0) {
         jsval v = args[0];
-        if (JSVAL_IS_TRACEABLE(v)) {
+        if (v.isMarkable()) {
             startValue = v;
-        } else if (!JSVAL_IS_NULL(v)) {
+        } else if (!v.isNull()) {
             JS_ReportError(cx,
                            "the first argument is not null or a heap-allocated "
                            "thing");
             return false;
         }
     }
 
     RootedValue traceValue(cx);
@@ -766,21 +767,21 @@ CountHeap(JSContext *cx, unsigned argc, 
             return false;
         if (JS_FlatStringEqualsAscii(flatStr, "specific")) {
             if (args.length() < 3) {
                 JS_ReportError(cx, "tracing of specific value requested "
                                "but no value provided");
                 return false;
             }
             traceValue = args[2];
-            if (!JSVAL_IS_TRACEABLE(traceValue)){
+            if (!traceValue.isMarkable()){
                 JS_ReportError(cx, "cannot trace this kind of value");
                 return false;
             }
-            traceThing = JSVAL_TO_TRACEABLE(traceValue);
+            traceThing = traceValue.toGCThing();
         } else {
             for (size_t i = 0; ;) {
                 if (JS_FlatStringEqualsAscii(flatStr, traceKindNames[i].name)) {
                     traceKind = traceKindNames[i].kind;
                     break;
                 }
                 if (++i == ArrayLength(traceKindNames)) {
                     JSAutoByteString bytes(cx, str);
@@ -1483,16 +1484,36 @@ static bool
 TimesAccessed(JSContext *cx, unsigned argc, jsval *vp)
 {
     static int32_t accessed = 0;
     CallArgs args = CallArgsFromVp(argc, vp);
     args.rval().setInt32(++accessed);
     return true;
 }
 
+static bool
+EnableTraceLogger(JSContext *cx, unsigned argc, jsval *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
+    args.rval().setBoolean(TraceLoggerEnable(logger));
+
+    return true;
+}
+
+static bool
+DisableTraceLogger(JSContext *cx, unsigned argc, jsval *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
+    args.rval().setBoolean(TraceLoggerDisable(logger));
+
+    return true;
+}
+
 static const JSFunctionSpecWithHelp TestingFunctions[] = {
     JS_FN_HELP("gc", ::GC, 0, 0,
 "gc([obj] | 'compartment')",
 "  Run the garbage collector. When obj is given, GC only its compartment.\n"
 "  If 'compartment' is given, GC any compartments that were scheduled for\n"
 "  GC via schedulegc."),
 
     JS_FN_HELP("minorgc", ::MinorGC, 0, 0,
@@ -1718,16 +1739,25 @@ static const JSFunctionSpecWithHelp Test
     JS_FN_HELP("neuter", Neuter, 1, 0,
 "neuter(buffer)",
 "  Neuter the given ArrayBuffer object as if it had been transferred to a WebWorker."),
 
     JS_FN_HELP("workerThreadCount", WorkerThreadCount, 0, 0,
 "workerThreadCount()",
 "  Returns the number of worker threads available for off-main-thread tasks."),
 
+    JS_FN_HELP("startTraceLogger", EnableTraceLogger, 0, 0,
+"startTraceLogger()",
+"  Start logging the mainThread.\n"
+"  Note: tracelogging starts automatically. Disable it by setting environment variable\n"
+"  TLOPTIONS=disableMainThread"),
+
+    JS_FN_HELP("stopTraceLogger", DisableTraceLogger, 0, 0,
+"startTraceLogger()",
+"  Stop logging the mainThread."),
     JS_FS_HELP_END
 };
 
 static const JSPropertySpec TestingProperties[] = {
     JS_PSG("timesAccessed", TimesAccessed, 0),
     JS_PS_END
 };
 
--- a/js/src/ds/LifoAlloc.h
+++ b/js/src/ds/LifoAlloc.h
@@ -136,22 +136,16 @@ class BumpChunk
         if (MOZ_UNLIKELY(newBump < bump))
             return nullptr;
 
         JS_ASSERT(canAlloc(n)); // Ensure consistency between "can" and "try".
         setBump(newBump);
         return aligned;
     }
 
-    void *peek(size_t n) {
-        if (bump - bumpBase() < ptrdiff_t(n))
-            return nullptr;
-        return bump - n;
-    }
-
     static BumpChunk *new_(size_t chunkSize);
     static void delete_(BumpChunk *chunk);
 };
 
 } // namespace detail
 
 // LIFO bump allocator: used for phase-oriented and fast LIFO allocations.
 //
@@ -456,26 +450,16 @@ class LifoAlloc
         }
 
         // Return a Mark at the current position of the Enum.
         Mark mark() {
             alloc_->markCount++;
             return Mark(chunk_, position_);
         }
     };
-
-    // Return a modifiable pointer to the most recently allocated bytes. The
-    // type of the thing must be known, so is only applicable to some special-
-    // purpose allocators. Will return a nullptr if nothing has been allocated.
-    template <typename T>
-    T *peek() {
-        if (!latest)
-            return nullptr;
-        return static_cast<T *>(latest->peek(sizeof(T)));
-    }
 };
 
 class LifoAllocScope
 {
     LifoAlloc       *lifoAlloc;
     LifoAlloc::Mark mark;
     bool            shouldRelease;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
--- a/js/src/gc/StoreBuffer.cpp
+++ b/js/src/gc/StoreBuffer.cpp
@@ -347,28 +347,28 @@ JS::HeapCellRelocate(js::gc::Cell **cell
     JS_ASSERT(*cellp);
     JSRuntime *runtime = (*cellp)->runtimeFromMainThread();
     runtime->gcStoreBuffer.removeRelocatableCell(cellp);
 }
 
 JS_PUBLIC_API(void)
 JS::HeapValuePostBarrier(JS::Value *valuep)
 {
-    JS_ASSERT(JSVAL_IS_TRACEABLE(*valuep));
+    JS_ASSERT(valuep->isMarkable());
     if (valuep->isString() && StringIsPermanentAtom(valuep->toString()))
         return;
     JSRuntime *runtime = static_cast<js::gc::Cell *>(valuep->toGCThing())->runtimeFromMainThread();
     runtime->gcStoreBuffer.putRelocatableValue(valuep);
 }
 
 JS_PUBLIC_API(void)
 JS::HeapValueRelocate(JS::Value *valuep)
 {
     /* Called with old contents of *valuep before overwriting. */
-    JS_ASSERT(JSVAL_IS_TRACEABLE(*valuep));
+    JS_ASSERT(valuep->isMarkable());
     if (valuep->isString() && StringIsPermanentAtom(valuep->toString()))
         return;
     JSRuntime *runtime = static_cast<js::gc::Cell *>(valuep->toGCThing())->runtimeFromMainThread();
     runtime->gcStoreBuffer.removeRelocatableValue(valuep);
 }
 
 template class StoreBuffer::MonoTypeBuffer<StoreBuffer::ValueEdge>;
 template class StoreBuffer::MonoTypeBuffer<StoreBuffer::CellPtrEdge>;
--- a/js/src/gc/StoreBuffer.h
+++ b/js/src/gc/StoreBuffer.h
@@ -129,20 +129,16 @@ class StoreBuffer
 
         /* Compacts if any entries have been added since the last compaction. */
         void maybeCompact(StoreBuffer *owner);
 
         /* Add one item to the buffer. */
         void put(StoreBuffer *owner, const T &t) {
             JS_ASSERT(storage_);
 
-            T *tip = storage_->peek<T>();
-            if (tip && tip->canMergeWith(t))
-                return tip->mergeInplace(t);
-
             T *tp = storage_->new_<T>(t);
             if (!tp)
                 CrashAtUnhandlableOOM("Failed to allocate for MonoTypeBuffer::put.");
 
             if (isAboutToOverflow())
                 handleOverflow(owner);
         }
 
@@ -246,19 +242,16 @@ class StoreBuffer
         explicit CellPtrEdge(Cell **v) : edge(v) {}
         bool operator==(const CellPtrEdge &other) const { return edge == other.edge; }
         bool operator!=(const CellPtrEdge &other) const { return edge != other.edge; }
 
         bool maybeInRememberedSet(const Nursery &nursery) const {
             return !nursery.isInside(edge) && nursery.isInside(*edge);
         }
 
-        bool canMergeWith(const CellPtrEdge &other) const { return edge == other.edge; }
-        void mergeInplace(const CellPtrEdge &) {}
-
         void mark(JSTracer *trc);
 
         CellPtrEdge tagged() const { return CellPtrEdge((Cell **)(uintptr_t(edge) | 1)); }
         CellPtrEdge untagged() const { return CellPtrEdge((Cell **)(uintptr_t(edge) & ~1)); }
         bool isTagged() const { return bool(uintptr_t(edge) & 1); }
 
         typedef PointerEdgeHasher<CellPtrEdge> Hasher;
     };
@@ -272,19 +265,16 @@ class StoreBuffer
         bool operator!=(const ValueEdge &other) const { return edge != other.edge; }
 
         void *deref() const { return edge->isGCThing() ? edge->toGCThing() : nullptr; }
 
         bool maybeInRememberedSet(const Nursery &nursery) const {
             return !nursery.isInside(edge) && nursery.isInside(deref());
         }
 
-        bool canMergeWith(const ValueEdge &other) const { return edge == other.edge; }
-        void mergeInplace(const ValueEdge &) {}
-
         void mark(JSTracer *trc);
 
         ValueEdge tagged() const { return ValueEdge((JS::Value *)(uintptr_t(edge) | 1)); }
         ValueEdge untagged() const { return ValueEdge((JS::Value *)(uintptr_t(edge) & ~1)); }
         bool isTagged() const { return bool(uintptr_t(edge) & 1); }
 
         typedef PointerEdgeHasher<ValueEdge> Hasher;
     };
@@ -316,26 +306,16 @@ class StoreBuffer
                    start_ == other.start_ &&
                    count_ == other.count_;
         }
 
         bool operator!=(const SlotsEdge &other) const {
             return !(*this == other);
         }
 
-        bool canMergeWith(const SlotsEdge &other) const {
-            return objectAndKind_ == other.objectAndKind_;
-        }
-
-        void mergeInplace(const SlotsEdge &other) {
-            int32_t end = Max(start_ + count_, other.start_ + other.count_);
-            start_ = Min(start_, other.start_);
-            count_ = end - start_;
-        }
-
         bool maybeInRememberedSet(const Nursery &nursery) const {
             return !nursery.isInside(object());
         }
 
         void mark(JSTracer *trc);
 
         typedef struct {
             typedef SlotsEdge Lookup;
@@ -355,19 +335,16 @@ class StoreBuffer
         bool operator==(const WholeCellEdges &other) const { return edge == other.edge; }
         bool operator!=(const WholeCellEdges &other) const { return edge != other.edge; }
 
         bool maybeInRememberedSet(const Nursery &nursery) const { return true; }
 
         static bool supportsDeduplication() { return true; }
         void *deduplicationKey() const { return (void *)edge; }
 
-        bool canMergeWith(const WholeCellEdges &other) const { return edge == other.edge; }
-        void mergeInplace(const WholeCellEdges &) {}
-
         void mark(JSTracer *trc);
 
         typedef PointerEdgeHasher<WholeCellEdges> Hasher;
     };
 
     template <typename Key>
     struct CallbackRef : public BufferableRef
     {
--- a/js/src/jit-test/tests/asm.js/testSource.js
+++ b/js/src/jit-test/tests/asm.js/testSource.js
@@ -232,16 +232,43 @@ if (isAsmJSCompilationAvailable() && isC
     var m = new Function('glob', 'ffi', 'heap', bodyOnly);
     assertEq(isAsmJSModuleLoadedFromCache(m), true);
     assertEq(m.toString(), "function anonymous(glob, ffi, heap) {\n" + bodyOnly + "\n}");
     assertEq(m.toSource(), "(function anonymous(glob, ffi, heap) {\n" + bodyOnly + "\n})");
 }
 
 })();
 
+/* Implicit "use strict" context */
+(function() {
+
+var funcHeader =  'function (glob, ffi, heap) {',
+    funcBody = '\n"use asm";\n\
+    function g() {}\n\
+    return g;\n\n'
+    funcFooter = '}',
+    funcSource = funcHeader + funcBody + funcFooter
+    useStrict = '\n"use strict";\n';
+
+var f4 = eval("\"use strict\";\n(" + funcSource + ")");
+
+var expectedToString = funcHeader + useStrict + funcBody + funcFooter
+var expectedToSource = '(' + expectedToString + ')'
+
+assertEq(f4.toString(), expectedToString);
+assertEq(f4.toSource(), expectedToSource);
+
+if (isAsmJSCompilationAvailable() && isCachingEnabled()) {
+    var f5 = eval("\"use strict\";\n(" + funcSource + ")");
+    assertEq(isAsmJSModuleLoadedFromCache(f5), true);
+    assertEq(f5.toString(), expectedToString);
+    assertEq(f5.toSource(), expectedToSource);
+}
+})();
+
 /* Functions */
 (function() {
 
 var noSrc = "function noArgument() {\n\
     return 42;\n\
 }"
 var oneSrc = "function oneArgument(x) {\n\
     x = x | 0;\n\
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug995675.js
@@ -0,0 +1,5 @@
+function f(x) {
+        return Math.cos(~(~Math.pow(Number.MAX_VALUE, x)))
+}
+f(-0)
+assertEq(f(undefined - undefined), 1)
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug995826.js
@@ -0,0 +1,5 @@
+function f(x) {
+        return Math.round(-Math.tan(x > 0))
+}
+f(2)
+assertEq(f(-1), -0);
--- a/js/src/jit/AsmJS.cpp
+++ b/js/src/jit/AsmJS.cpp
@@ -1138,17 +1138,23 @@ class MOZ_STACK_CLASS ModuleCompiler
             !addStandardLibraryMathName("SQRT1_2", M_SQRT1_2) ||
             !addStandardLibraryMathName("SQRT2", M_SQRT2))
         {
             return false;
         }
 
         uint32_t funcStart = parser_.pc->maybeFunction->pn_body->pn_pos.begin;
         uint32_t offsetToEndOfUseAsm = parser_.tokenStream.currentToken().pos.end;
-        module_ = cx_->new_<AsmJSModule>(parser_.ss, funcStart, offsetToEndOfUseAsm);
+
+        // "use strict" should be added to the source if we are in an implicit
+        // strict context, see also comment above addUseStrict in
+        // js::FunctionToString.
+        bool strict = parser_.pc->sc->strict && !parser_.pc->sc->hasExplicitUseStrict();
+
+        module_ = cx_->new_<AsmJSModule>(parser_.ss, funcStart, offsetToEndOfUseAsm, strict);
         if (!module_)
             return false;
 
         return true;
     }
 
     bool failOffset(uint32_t offset, const char *str) {
         JS_ASSERT(!errorString_);
@@ -1509,17 +1515,16 @@ class MOZ_STACK_CLASS ModuleCompiler
                                slowFuns ? slowFuns.get() : ""));
 #endif
     }
 
     bool finish(ScopedJSDeletePtr<AsmJSModule> *module)
     {
         module_->initFuncEnd(parser_.tokenStream.currentToken().pos.end,
                              parser_.tokenStream.peekTokenPos().end);
-
         masm_.finish();
         if (masm_.oom())
             return false;
 
 #if defined(JS_CODEGEN_ARM)
         // Now that compilation has finished, we need to update offsets to
         // reflect actual offsets (an ARM distinction).
         for (unsigned i = 0; i < module_->numHeapAccesses(); i++) {
--- a/js/src/jit/AsmJSLink.cpp
+++ b/js/src/jit/AsmJSLink.cpp
@@ -795,18 +795,41 @@ js::AsmJSModuleToString(JSContext *cx, H
         if (!out.append(") {\n"))
             return nullptr;
     }
 
     Rooted<JSFlatString*> src(cx, source->substring(cx, begin, end));
     if (!src)
         return nullptr;
 
-    if (!out.append(src->chars(), src->length()))
-        return nullptr;
+    if (module.strict()) {
+        // We need to add "use strict" in the body right after the opening
+        // brace.
+        size_t bodyStart = 0, bodyEnd;
+
+        // No need to test for functions created with the Function ctor as
+        // these doesn't implicitly inherit the "use strict" context. Strict mode is
+        // enabled for functions created with the Function ctor only if they begin with
+        // the "use strict" directive, but these functions won't validate as asm.js
+        // modules.
+
+        ConstTwoByteChars chars(src->chars(), src->length());
+        if (!FindBody(cx, fun, chars, src->length(), &bodyStart, &bodyEnd))
+            return nullptr;
+
+        if (!out.append(chars, bodyStart) ||
+            !out.append("\n\"use strict\";\n") ||
+            !out.append(chars + bodyStart, src->length() - bodyStart))
+        {
+            return nullptr;
+        }
+    } else {
+        if (!out.append(src->chars(), src->length()))
+            return nullptr;
+    }
 
     if (funCtor && !out.append("\n}"))
         return nullptr;
 
     if (addParenToLambda && fun->isLambda() && !out.append(")"))
         return nullptr;
 
     return out.finishString();
--- a/js/src/jit/AsmJSModule.cpp
+++ b/js/src/jit/AsmJSModule.cpp
@@ -323,32 +323,34 @@ AsmJSModule::staticallyLink(ExclusiveCon
     // Initialize global data segment
 
     for (size_t i = 0; i < exits_.length(); i++) {
         exitIndexToGlobalDatum(i).exit = interpExitTrampoline(exits_[i]);
         exitIndexToGlobalDatum(i).fun = nullptr;
     }
 }
 
-AsmJSModule::AsmJSModule(ScriptSource *scriptSource, uint32_t funcStart, uint32_t offsetToEndOfUseAsm)
+AsmJSModule::AsmJSModule(ScriptSource *scriptSource, uint32_t funcStart,
+                         uint32_t offsetToEndOfUseAsm, bool strict)
   : globalArgumentName_(nullptr),
     importArgumentName_(nullptr),
     bufferArgumentName_(nullptr),
     code_(nullptr),
     interruptExit_(nullptr),
     dynamicallyLinked_(false),
     loadedFromCache_(false),
     funcStart_(funcStart),
     offsetToEndOfUseAsm_(offsetToEndOfUseAsm),
     scriptSource_(scriptSource),
     codeIsProtected_(false)
 {
     mozilla::PodZero(&pod);
     scriptSource_->incref();
     pod.minHeapLength_ = AsmJSAllocationGranularity;
+    pod.strict_ = strict;
 }
 
 AsmJSModule::~AsmJSModule()
 {
     scriptSource_->decref();
 
     if (code_) {
         for (unsigned i = 0; i < numExits(); i++) {
@@ -871,17 +873,17 @@ class AutoUnprotectCodeForClone
     }
 };
 
 bool
 AsmJSModule::clone(JSContext *cx, ScopedJSDeletePtr<AsmJSModule> *moduleOut) const
 {
     AutoUnprotectCodeForClone cloneGuard(cx, *this);
 
-    *moduleOut = cx->new_<AsmJSModule>(scriptSource_, funcStart_, offsetToEndOfUseAsm_);
+    *moduleOut = cx->new_<AsmJSModule>(scriptSource_, funcStart_, offsetToEndOfUseAsm_, pod.strict_);
     if (!*moduleOut)
         return false;
 
     AsmJSModule &out = **moduleOut;
 
     // Mirror the order of serialize/deserialize in cloning:
 
     out.pod = pod;
@@ -1308,18 +1310,19 @@ js::LookupAsmJSModuleInCache(ExclusiveCo
 
     ModuleCharsForLookup moduleChars;
     cursor = moduleChars.deserialize(cx, cursor);
     if (!moduleChars.match(parser))
         return true;
 
     uint32_t funcStart = parser.pc->maybeFunction->pn_body->pn_pos.begin;
     uint32_t offsetToEndOfUseAsm = parser.tokenStream.currentToken().pos.end;
+    bool strict = parser.pc->sc->strict && !parser.pc->sc->hasExplicitUseStrict();
     ScopedJSDeletePtr<AsmJSModule> module(
-        cx->new_<AsmJSModule>(parser.ss, funcStart, offsetToEndOfUseAsm));
+        cx->new_<AsmJSModule>(parser.ss, funcStart, offsetToEndOfUseAsm, strict));
     if (!module)
         return false;
     cursor = module->deserialize(cx, cursor);
     if (!cursor)
         return false;
 
     bool atEnd = cursor == entry.memory + entry.serializedSize;
     MOZ_ASSERT(atEnd, "Corrupt cache file");
--- a/js/src/jit/AsmJSModule.h
+++ b/js/src/jit/AsmJSModule.h
@@ -426,16 +426,17 @@ class AsmJSModule
 #endif
 #if defined(JS_ION_PERF)
     ProfiledBlocksFunctionVector          perfProfiledBlocksFunctions_;
 #endif
 
     struct Pod {
         uint32_t                          funcLength_;
         uint32_t                          funcLengthWithRightBrace_;
+        bool                              strict_;
         uint32_t                          numGlobalVars_;
         uint32_t                          numFFIs_;
         size_t                            funcPtrTableAndExitBytes_;
         bool                              hasArrayView_;
         size_t                            functionBytes_; // just the function bodies, no stubs
         size_t                            codeBytes_;     // function bodies and stubs
         size_t                            totalBytes_;    // function bodies, stubs, and global data
         uint32_t                          minHeapLength_;
@@ -459,17 +460,18 @@ class AsmJSModule
 
     FunctionCountsVector                  functionCounts_;
 
     // This field is accessed concurrently when requesting an interrupt.
     // Access must be synchronized via the runtime's interrupt lock.
     mutable bool                          codeIsProtected_;
 
   public:
-    explicit AsmJSModule(ScriptSource *scriptSource, uint32_t functStart, uint32_t offsetToEndOfUseAsm);
+    explicit AsmJSModule(ScriptSource *scriptSource, uint32_t functStart,
+                         uint32_t offsetToEndOfUseAsm, bool strict);
     ~AsmJSModule();
 
     void trace(JSTracer *trc) {
         for (unsigned i = 0; i < globals_.length(); i++)
             globals_[i].trace(trc);
         for (unsigned i = 0; i < exports_.length(); i++)
             exports_[i].trace(trc);
         for (unsigned i = 0; i < exits_.length(); i++) {
@@ -519,16 +521,19 @@ class AsmJSModule
         pod.funcLengthWithRightBrace_ = endAfterCurly - funcStart_;
     }
     uint32_t funcEndBeforeCurly() const {
         return funcStart_ + pod.funcLength_;
     }
     uint32_t funcEndAfterCurly() const {
         return funcStart_ + pod.funcLengthWithRightBrace_;
     }
+    bool strict() const {
+        return pod.strict_;
+    }
 
     bool addGlobalVarInit(const Value &v, AsmJSCoercion coercion, uint32_t *globalIndex) {
         JS_ASSERT(pod.funcPtrTableAndExitBytes_ == 0);
         if (pod.numGlobalVars_ == UINT32_MAX)
             return false;
         Global g(Global::Variable, nullptr);
         g.pod.u.var.initKind_ = Global::InitConstant;
         g.pod.u.var.init.constant_ = v;
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -581,23 +581,16 @@ IonBuilder::inlineMathAbs(CallInfo &call
     callInfo.setImplicitlyUsedUnchecked();
 
     // If the arg is a Float32, we specialize the op as double, it will be specialized
     // as float32 if necessary later.
     MIRType absType = (argType == MIRType_Float32) ? MIRType_Double : argType;
     MInstruction *ins = MAbs::New(alloc(), callInfo.getArg(0), absType);
     current->add(ins);
 
-    if (IsFloatingPointType(argType) && returnType == MIRType_Int32) {
-        MToInt32 *toInt = MToInt32::New(alloc(), ins);
-        toInt->setCanBeNegativeZero(false);
-        current->add(toInt);
-        ins = toInt;
-    }
-
     current->push(ins);
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
 IonBuilder::inlineMathFloor(CallInfo &callInfo)
 {
     if (callInfo.constructing())
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -2323,22 +2323,16 @@ MToDouble::foldsTo(TempAllocator &alloc,
     if (in->isConstant()) {
         const Value &v = in->toConstant()->value();
         if (v.isNumber()) {
             double out = v.toNumber();
             return MConstant::New(alloc, DoubleValue(out));
         }
     }
 
-    // Fold unnecessary numeric conversions.
-    if (input()->isToInt32()) {
-        replaceOperand(0, input()->getOperand(0));
-        conversion_ = NonStringPrimitives;
-    }
-
     return this;
 }
 
 MDefinition *
 MToFloat32::foldsTo(TempAllocator &alloc, bool useValueNumbers)
 {
     if (input()->type() == MIRType_Float32)
         return input();
--- a/js/src/jit/TypePolicy.cpp
+++ b/js/src/jit/TypePolicy.cpp
@@ -290,16 +290,17 @@ TypeBarrierPolicy::adjustInputs(TempAllo
     }
 
     // Input is a value. Unbox the input to the requested type.
     if (inputType == MIRType_Value) {
         JS_ASSERT(outputType != MIRType_Value);
 
         // We can't unbox a value to null/undefined. So keep output also a value.
         if (IsNullOrUndefined(outputType) || outputType == MIRType_Magic) {
+            JS_ASSERT(ins->defUseCount() == 0);
             ins->setResultType(MIRType_Value);
             return true;
         }
 
         MUnbox *unbox = MUnbox::New(alloc, ins->getOperand(0), outputType, MUnbox::TypeBarrier);
         ins->block()->insertBefore(ins, unbox);
         ins->replaceOperand(0, unbox);
         return true;
--- a/js/src/jit/shared/CodeGenerator-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-shared.cpp
@@ -9,16 +9,17 @@
 #include "mozilla/DebugOnly.h"
 
 #include "jit/IonCaches.h"
 #include "jit/IonMacroAssembler.h"
 #include "jit/IonSpewer.h"
 #include "jit/MIR.h"
 #include "jit/MIRGenerator.h"
 #include "jit/ParallelFunctions.h"
+#include "vm/TraceLogging.h"
 
 #include "jit/IonFrames-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::DebugOnly;
 
@@ -623,16 +624,21 @@ CodeGeneratorShared::callVM(const VMFunc
 #ifdef DEBUG
     if (ins->mirRaw()) {
         JS_ASSERT(ins->mirRaw()->isInstruction());
         MInstruction *mir = ins->mirRaw()->toInstruction();
         JS_ASSERT_IF(mir->isEffectful(), mir->resumePoint());
     }
 #endif
 
+#ifdef JS_TRACE_LOGGING
+    if (!emitTracelogStartEvent(TraceLogger::VM))
+        return false;
+#endif
+
     // Stack is:
     //    ... frame ...
     //    [args]
 #ifdef DEBUG
     JS_ASSERT(pushedArgs_ == fun.explicitArgs);
     pushedArgs_ = 0;
 #endif
 
@@ -662,16 +668,22 @@ CodeGeneratorShared::callVM(const VMFunc
     // Remove rest of the frame left on the stack. We remove the return address
     // which is implicitly poped when returning.
     int framePop = sizeof(IonExitFrameLayout) - sizeof(void*);
 
     // Pop arguments from framePushed.
     masm.implicitPop(fun.explicitStackSlots() * sizeof(void *) + framePop);
     // Stack is:
     //    ... frame ...
+
+#ifdef JS_TRACE_LOGGING
+    if (!emitTracelogStopEvent(TraceLogger::VM))
+        return false;
+#endif
+
     return true;
 }
 
 class OutOfLineTruncateSlow : public OutOfLineCodeBase<CodeGeneratorShared>
 {
     FloatRegister src_;
     Register dest_;
     bool needFloat32Conversion_;
@@ -1023,47 +1035,37 @@ CodeGeneratorShared::emitTracelogScript(
     masm.Pop(script);
     masm.Pop(logger);
     return true;
 }
 
 bool
 CodeGeneratorShared::emitTracelogTree(bool isStart, uint32_t textId)
 {
+    if (!TraceLogTextIdEnabled(textId))
+        return true;
+
     RegisterSet regs = RegisterSet::Volatile();
     Register logger = regs.takeGeneral();
 
     masm.Push(logger);
 
     CodeOffsetLabel patchLocation = masm.movWithPatch(ImmPtr(nullptr), logger);
     if (!patchableTraceLoggers_.append(patchLocation))
         return false;
 
-    if (isStart)
+    if (isStart) {
         masm.tracelogStart(logger, textId);
-    else
+    } else {
+#ifdef DEBUG
         masm.tracelogStop(logger, textId);
-
-    masm.Pop(logger);
-    return true;
-}
-
-bool
-CodeGeneratorShared::emitTracelogStopEvent()
-{
-    RegisterSet regs = RegisterSet::Volatile();
-    Register logger = regs.takeGeneral();
-
-    masm.Push(logger);
-
-    CodeOffsetLabel patchLocation = masm.movWithPatch(ImmPtr(nullptr), logger);
-    if (!patchableTraceLoggers_.append(patchLocation))
-        return false;
-
-    masm.tracelogStop(logger);
+#else
+        masm.tracelogStop(logger);
+#endif
+    }
 
     masm.Pop(logger);
     return true;
 }
 #endif
 
 } // namespace jit
 } // namespace js
--- a/js/src/jit/shared/CodeGenerator-shared.h
+++ b/js/src/jit/shared/CodeGenerator-shared.h
@@ -462,23 +462,18 @@ class CodeGeneratorShared : public LInst
     }
     bool emitTracelogScriptStop() {
         return emitTracelogScript(/* isStart =*/ false);
     }
     bool emitTracelogStartEvent(uint32_t textId) {
         return emitTracelogTree(/* isStart =*/ true, textId);
     }
     bool emitTracelogStopEvent(uint32_t textId) {
-#ifdef DEBUG
         return emitTracelogTree(/* isStart =*/ false, textId);
-#else
-        return emitTracelogScript(/* isStart =*/ false);
-#endif
     }
-    bool emitTracelogStopEvent();
 #endif
 };
 
 // An out-of-line path is generated at the end of the function.
 class OutOfLineCode : public TempObject
 {
     Label entry_;
     Label rejoin_;
--- a/js/src/jit/shared/IonAssemblerBufferWithConstantPools.h
+++ b/js/src/jit/shared/IonAssemblerBufferWithConstantPools.h
@@ -755,16 +755,20 @@ struct AssemblerBufferWithConstantPool :
                     // block, chances are you will have a bad time.
                     // ADDENDUM: this CANNOT happen on ARM, because the only elements that
                     // fall into this case are doubles loaded via vfp, but they will also be
                     // the last pool, which means it cannot affect the alignment of any other
                     // Sub Pools.
                     IonSpew(IonSpew_Pools, "[%d]***Offset was still out of range!***", id, codeOffset - magicAlign);
                     IonSpew(IonSpew_Pools, "[%d] Too complicated; bailingp", id);
                     this->fail_bail();
+                    // only free up to the current offset
+                    for (int pi = poolIdx; pi < numPoolKinds; pi++)
+                        delete[] outcastEntries[pi];
+                    delete[] preservedEntries;
                     return;
                 } else {
                     preservedEntries[idx] = true;
                 }
             }
             // remove the elements of the pool that should not be there (YAY, MEMCPY)
             unsigned int idxDest = 0;
             // If no elements were skipped, no expensive copy is necessary.
@@ -778,37 +782,42 @@ struct AssemblerBufferWithConstantPool :
                         }
                         idxDest++;
                     }
                 }
                 p->numEntries -= numSkips;
             }
             poolOffset += p->numEntries * p->immSize;
             delete[] preservedEntries;
+            preservedEntries = nullptr;
         }
         // bind the current pool to the perforation point.
         Pool **tmp = &perforatedNode->data;
         *tmp = static_cast<Pool*>(this->LifoAlloc_.alloc(sizeof(Pool) * numPoolKinds));
         if (tmp == nullptr) {
             this->fail_oom();
+            for (int pi = 0; pi < numPoolKinds; pi++)
+                delete[] outcastEntries[pi];
             return;
         }
         // The above operations may have changed the size of pools!
         // recalibrate the size of the pool.
         newPoolInfo = getPoolData();
         poolInfo[numDumps] = newPoolInfo;
         poolSize += poolInfo[numDumps].size;
         numDumps++;
 
         memcpy(*tmp, pools, sizeof(Pool) * numPoolKinds);
 
         // reset everything to the state that it was in when we started
         for (int poolIdx = 0; poolIdx < numPoolKinds; poolIdx++) {
             if (!pools[poolIdx].reset(this->LifoAlloc_)) {
                 this->fail_oom();
+                for (int pi = 0; pi < numPoolKinds; pi++)
+                    delete[] outcastEntries[pi];
                 return;
             }
         }
         new (&perforation) BufferOffset();
         perforatedNode = nullptr;
         inBackref = false;
 
         // Now that the backwards pool has been emptied, and a new forward pool
--- a/js/src/jit/x86/MacroAssembler-x86.cpp
+++ b/js/src/jit/x86/MacroAssembler-x86.cpp
@@ -391,26 +391,37 @@ MacroAssemblerX86::testNegativeZero(cons
 {
     // Determines whether the single double contained in the XMM register reg
     // is equal to double-precision -0.
 
     Label nonZero;
 
     // Compare to zero. Lets through {0, -0}.
     xorpd(ScratchFloatReg, ScratchFloatReg);
-    // If reg is non-zero, then a test of Zero is false.
+
+    // If reg is non-zero, jump to nonZero.
+    // Sets ZF=0 and PF=0.
     branchDouble(DoubleNotEqual, reg, ScratchFloatReg, &nonZero);
 
-    // Input register is either zero or negative zero. Test sign bit.
+    // Input register is either zero or negative zero. Retrieve sign of input.
     movmskpd(reg, scratch);
-    // If reg is -0, then a test of Zero is true.
-    cmpl(scratch, Imm32(1));
+
+    // If reg is 1 or 3, input is negative zero.
+    // If reg is 0 or 2, input is a normal zero.
+    // So the following test will set PF=1 for negative zero.
+    orl(Imm32(2), scratch);
 
     bind(&nonZero);
-    return Zero;
+
+    // Here we need to be able to test if the input is a negative zero.
+    // - branchDouble joins here for non-zero values in which case it sets
+    //   ZF=0 and PF=0. In that case the test should fail.
+    // - orl sets PF=1 on negative zero and PF=0 otherwise
+    // => So testing PF=1 will return if input is negative zero or not.
+    return Parity;
 }
 
 Assembler::Condition
 MacroAssemblerX86::testNegativeZeroFloat32(const FloatRegister &reg, const Register &scratch)
 {
     movd(reg, scratch);
     cmpl(scratch, Imm32(1));
     return Overflow;
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1975,48 +1975,16 @@ RemoveScriptRootRT(JSRuntime *rt, JS::He
  */
 extern JS_PUBLIC_API(bool)
 JS_AddExtraGCRootsTracer(JSRuntime *rt, JSTraceDataOp traceOp, void *data);
 
 /* Undo a call to JS_AddExtraGCRootsTracer. */
 extern JS_PUBLIC_API(void)
 JS_RemoveExtraGCRootsTracer(JSRuntime *rt, JSTraceDataOp traceOp, void *data);
 
-/*
- * JS_CallTracer API and related macros for implementors of JSTraceOp, to
- * enumerate all references to traceable things reachable via a property or
- * other strong ref identified for debugging purposes by name or index or
- * a naming callback.
- *
- * See the JSTraceOp typedef.
- */
-
-/*
- * Use the following macros to check if a particular jsval is a traceable
- * thing and to extract the thing and its kind to pass to JS_CallTracer.
- */
-static MOZ_ALWAYS_INLINE bool
-JSVAL_IS_TRACEABLE(jsval v)
-{
-    return JSVAL_IS_TRACEABLE_IMPL(JSVAL_TO_IMPL(v));
-}
-
-static MOZ_ALWAYS_INLINE void *
-JSVAL_TO_TRACEABLE(jsval v)
-{
-    return JSVAL_TO_GCTHING(v);
-}
-
-static MOZ_ALWAYS_INLINE JSGCTraceKind
-JSVAL_TRACE_KIND(jsval v)
-{
-    JS_ASSERT(JSVAL_IS_GCTHING(v));
-    return (JSGCTraceKind) JSVAL_TRACE_KIND_IMPL(JSVAL_TO_IMPL(v));
-}
-
 #ifdef JS_DEBUG
 
 /*
  * Debug-only method to dump the object graph of heap-allocated things.
  *
  * fp:              file for the dump output.
  * start:           when non-null, dump only things reachable from start
  *                  thing. Otherwise dump all things reachable from the
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -621,24 +621,28 @@ const Class JSFunction::class_ = {
     fun_hasInstance,
     nullptr,                 /* construct   */
     fun_trace
 };
 
 const Class* const js::FunctionClassPtr = &JSFunction::class_;
 
 /* Find the body of a function (not including braces). */
-static bool
-FindBody(JSContext *cx, HandleFunction fun, ConstTwoByteChars chars, size_t length,
+bool
+js::FindBody(JSContext *cx, HandleFunction fun, ConstTwoByteChars chars, size_t length,
          size_t *bodyStart, size_t *bodyEnd)
 {
     // We don't need principals, since those are only used for error reporting.
     CompileOptions options(cx);
-    options.setFileAndLine("internal-findBody", 0)
-           .setVersion(fun->nonLazyScript()->getVersion());
+    options.setFileAndLine("internal-findBody", 0);
+
+    // For asm.js modules, there's no script.
+    if (fun->hasScript())
+        options.setVersion(fun->nonLazyScript()->getVersion());
+
     AutoKeepAtoms keepAtoms(cx->perThreadData);
     TokenStream ts(cx, options, chars.get(), length, nullptr);
     int nest = 0;
     bool onward = true;
     // Skip arguments list.
     do {
         switch (ts.getToken()) {
           case TOK_NAME:
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -549,16 +549,21 @@ class FunctionExtended : public JSFuncti
     HeapValue extendedSlots[NUM_EXTENDED_SLOTS];
 };
 
 extern JSFunction *
 CloneFunctionObject(JSContext *cx, HandleFunction fun, HandleObject parent,
                     gc::AllocKind kind = JSFunction::FinalizeKind,
                     NewObjectKind newKindArg = GenericObject);
 
+
+extern bool
+FindBody(JSContext *cx, HandleFunction fun, ConstTwoByteChars chars, size_t length,
+         size_t *bodyStart, size_t *bodyEnd);
+
 } // namespace js
 
 inline js::FunctionExtended *
 JSFunction::toExtended()
 {
     JS_ASSERT(isExtended());
     return static_cast<js::FunctionExtended *>(this);
 }
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -92,17 +92,17 @@ enum JSIterateOp {
 
     /* Iterate once. */
     JSENUMERATE_NEXT,
 
     /* Destroy iterator state. */
     JSENUMERATE_DESTROY
 };
 
-/* See JSVAL_TRACE_KIND and JSTraceCallback in jsapi.h. */
+/* See Value::gcKind() and JSTraceCallback in Tracer.h. */
 enum JSGCTraceKind {
     JSTRACE_OBJECT,
     JSTRACE_STRING,
     JSTRACE_SCRIPT,
 
     /*
      * Trace kinds internal to the engine. The embedding can only see them if
      * it implements JSTraceCallback.
--- a/js/src/vm/Opcodes.h
+++ b/js/src/vm/Opcodes.h
@@ -86,26 +86,53 @@ 1234567890123456789012345678901234567890
      * No operation is performed.
      *   Category: Other
      *   Operands:
      *   Stack: =>
      */ \
     macro(JSOP_NOP,       0,  "nop",        NULL,         1,  0,  0, JOF_BYTE) \
     \
     /* Long-standing JavaScript bytecodes. */ \
+    /*
+     * Pushes 'undefined' onto the stack.
+     *   Category: Literals
+     *   Type: Constants
+     *   Operands:
+     *   Stack: => undefined
+     */ \
     macro(JSOP_UNDEFINED, 1,  js_undefined_str, "",       1,  0,  1, JOF_BYTE) \
     macro(JSOP_UNUSED2,   2,  "unused2",    NULL,         1,  1,  0, JOF_BYTE) \
     macro(JSOP_ENTERWITH, 3,  "enterwith",  NULL,         5,  1,  0, JOF_OBJECT) \
     macro(JSOP_LEAVEWITH, 4,  "leavewith",  NULL,         1,  0,  0, JOF_BYTE) \
+    /*
+     * Pops the top of stack value as 'rval', stops interpretation of current
+     * script and returns 'rval'.
+     *   Category: Statements
+     *   Type: Function
+     *   Operands:
+     *   Stack: rval =>
+     */ \
     macro(JSOP_RETURN,    5,  "return",     NULL,         1,  1,  0, JOF_BYTE) \
     macro(JSOP_GOTO,      6,  "goto",       NULL,         5,  0,  0, JOF_JUMP) \
     macro(JSOP_IFEQ,      7,  "ifeq",       NULL,         5,  1,  0, JOF_JUMP|JOF_DETECTING) \
     macro(JSOP_IFNE,      8,  "ifne",       NULL,         5,  1,  0, JOF_JUMP) \
     \
-    /* Get the arguments object for the current, lightweight function activation. */ \
+    /*
+     * Pushes the 'arguments' object for the current function activation.
+     *
+     * If 'JSScript' is not marked 'needsArgsObj', then a
+     * JS_OPTIMIZED_ARGUMENTS magic value is pushed. Otherwise, a proper
+     * arguments object is constructed and pushed.
+     *
+     * This opcode requires that the function does not have rest parameter.
+     *   Category: Variables and Scopes
+     *   Type: Arguments
+     *   Operands:
+     *   Stack: => arguments
+     */ \
     macro(JSOP_ARGUMENTS, 9,  "arguments",  NULL,         1,  0,  1, JOF_BYTE) \
     \
     /*
      * Swaps the top two values on the stack. This is useful for things like
      * post-increment/decrement.
      *   Category: Operator
      *   Type: Stack Operations
      *   Operands:
@@ -134,16 +161,24 @@ 1234567890123456789012345678901234567890
     /*
      * Duplicates the top two values on the stack.
      *   Category: Operator
      *   Type: Stack Operations
      *   Operands:
      *   Stack: v1, v2 => v1, v2, v1, v2
      */ \
     macro(JSOP_DUP2,      13, "dup2",       NULL,         1,  2,  4, JOF_BYTE) \
+    /*
+     * Defines a readonly property on the frame's current variables-object (the
+     * scope object on the scope chain designated to receive new variables).
+     *   Category: Variables and Scopes
+     *   Type: Variables
+     *   Operands: uint32_t nameIndex
+     *   Stack: val => val
+     */ \
     macro(JSOP_SETCONST,  14, "setconst",   NULL,         5,  1,  1, JOF_ATOM|JOF_NAME|JOF_SET) \
     /*
      * Pops the top two values 'lval' and 'rval' from the stack, then pushes
      * the result of the operation applied to the two operands, converting
      * both to 32-bit signed integers if necessary.
      *   Category: Operator
      *   Type: Bitwise Logical Operators
      *   Operands:
@@ -234,18 +269,46 @@ 1234567890123456789012345678901234567890
      * Pops the value 'val' from the stack, then pushes '+val'.
      * ('+val' is the value converted to a number.)
      *   Category: Operator
      *   Type: Arithmetic Operators
      *   Operands:
      *   Stack: val => (+val)
      */ \
     macro(JSOP_POS,       35, "pos",        "+ ",         1,  1,  1, JOF_BYTE|JOF_ARITH) \
+    /*
+     * Looks up name on the scope chain and deletes it, pushes 'true' onto the
+     * stack if succeeded (if the property was present and deleted or if the
+     * property wasn't present in the first place), 'false' if not.
+     *
+     * Strict mode code should never contain this opcode.
+     *   Category: Variables and Scopes
+     *   Type: Variables
+     *   Operands: uint32_t nameIndex
+     *   Stack: => succeeded
+     */ \
     macro(JSOP_DELNAME,   36, "delname",    NULL,         5,  0,  1, JOF_ATOM|JOF_NAME) \
+    /*
+     * Pops the top of stack value, deletes property from it, pushes 'true' onto
+     * the stack if succeeded, 'false' if not.
+     *   Category: Literals
+     *   Type: Object
+     *   Operands: uint32_t nameIndex
+     *   Stack: obj => succeeded
+     */ \
     macro(JSOP_DELPROP,   37, "delprop",    NULL,         5,  1,  1, JOF_ATOM|JOF_PROP) \
+    /*
+     * Pops the top two values on the stack as 'propval' and 'obj',
+     * deletes 'propval' property from 'obj', pushes 'true'  onto the stack if
+     * succeeded, 'false' if not.
+     *   Category: Literals
+     *   Type: Object
+     *   Operands:
+     *   Stack: obj, propval => succeeded
+     */ \
     macro(JSOP_DELELEM,   38, "delelem",    NULL,         1,  2,  1, JOF_BYTE |JOF_ELEM) \
     /*
      * Pops the value 'val' from the stack, then pushes 'typeof val'.
      *   Category: Operator
      *   Type: Special Operators
      *   Operands:
      *   Stack: val => (typeof val)
      */ \
@@ -254,21 +317,52 @@ 1234567890123456789012345678901234567890
      * Pops the top value on the stack and pushes 'undefined'.
      *   Category: Operator
      *   Type: Special Operators
      *   Operands:
      *   Stack: val => undefined
      */ \
     macro(JSOP_VOID,      40, js_void_str,  NULL,         1,  1,  1, JOF_BYTE) \
     \
-    /* spreadcall variant of JSOP_CALL */ \
+    /*
+     * spreadcall variant of JSOP_CALL.
+     *
+     * Invokes 'callee' with 'this' and 'args', pushes the return value onto
+     * the stack.
+     *
+     * 'args' is an Array object which contains actual arguments.
+     *   Category: Statements
+     *   Type: Function
+     *   Operands:
+     *   Stack: callee, this, args => rval
+     */ \
     macro(JSOP_SPREADCALL,41, "spreadcall", NULL,         1,  3,  1, JOF_BYTE|JOF_INVOKE|JOF_TYPESET) \
-    /* spreadcall variant of JSOP_NEW */ \
+    /*
+     * spreadcall variant of JSOP_NEW
+     *
+     * Invokes 'callee' as a constructor with 'this' and 'args', pushes the
+     * return value onto the stack.
+     *   Category: Statements
+     *   Type: Function
+     *   Operands:
+     *   Stack: callee, this, args => rval
+     */ \
     macro(JSOP_SPREADNEW, 42, "spreadnew",  NULL,         1,  3,  1, JOF_BYTE|JOF_INVOKE|JOF_TYPESET) \
-    /* spreadcall variant of JSOP_EVAL */ \
+    /*
+     * spreadcall variant of JSOP_EVAL
+     *
+     * Invokes 'eval' with 'args' and pushes the return value onto the stack.
+     *
+     * If 'eval' in global scope is not original one, invokes the function
+     * with 'this' and 'args', and pushes return value onto the stack.
+     *   Category: Statements
+     *   Type: Function
+     *   Operands:
+     *   Stack: callee, this, args => rval
+     */ \
     macro(JSOP_SPREADEVAL,43, "spreadeval", NULL,         1,  3,  1, JOF_BYTE|JOF_INVOKE|JOF_TYPESET) \
     \
     /*
      * Duplicates the Nth value from the top onto the stack.
      *   Category: Operator
      *   Type: Stack Operations
      *   Operands: uint24_t n
      *   Stack: v[n], v[n-1], ..., v[1], v[0] =>
@@ -280,40 +374,141 @@ 1234567890123456789012345678901234567890
     macro(JSOP_UNUSED46,  46, "unused46",   NULL,         1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED47,  47, "unused47",   NULL,         1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED48,  48, "unused48",   NULL,         1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED49,  49, "unused49",   NULL,         1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED50,  50, "unused50",   NULL,         1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED51,  51, "unused51",   NULL,         1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED52,  52, "unused52",   NULL,         1,  0,  0,  JOF_BYTE) \
     \
+    /*
+     * Pops the top of stack value, pushes property of it onto the stack.
+     *   Category: Literals
+     *   Type: Object
+     *   Operands: uint32_t nameIndex
+     *   Stack: obj => obj[name]
+     */ \
     macro(JSOP_GETPROP,   53, "getprop",    NULL,         5,  1,  1, JOF_ATOM|JOF_PROP|JOF_TYPESET|JOF_TMPSLOT3) \
+    /*
+     * Pops the top two values on the stack as 'val' and 'obj', sets property of
+     * 'obj' as 'val', pushes 'obj' onto the stack.
+     *   Category: Literals
+     *   Type: Object
+     *   Operands: uint32_t nameIndex
+     *   Stack: obj, val => val
+     */ \
     macro(JSOP_SETPROP,   54, "setprop",    NULL,         5,  2,  1, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING) \
+    /*
+     * Pops the top two values on the stack as 'propval' and 'obj', pushes
+     * 'propval' property of 'obj' onto the stack.
+     *   Category: Literals
+     *   Type: Object
+     *   Operands:
+     *   Stack: obj, propval => obj[propval]
+     */ \
     macro(JSOP_GETELEM,   55, "getelem",    NULL,         1,  2,  1, JOF_BYTE |JOF_ELEM|JOF_TYPESET|JOF_LEFTASSOC) \
+    /*
+     * Pops the top three values on the stack as 'val', 'propval' and 'obj',
+     * sets 'propval' property of 'obj' as 'val', pushes 'obj' onto the
+     * stack.
+     *   Category: Literals
+     *   Type: Object
+     *   Operands:
+     *   Stack: obj, propval, val => val
+     */ \
     macro(JSOP_SETELEM,   56, "setelem",    NULL,         1,  3,  1, JOF_BYTE |JOF_ELEM|JOF_SET|JOF_DETECTING) \
     macro(JSOP_UNUSED57,  57, "unused57",   NULL,         1,  0,  0, JOF_BYTE) \
+    /*
+     * Invokes 'callee' with 'this' and 'args', pushes return value onto the
+     * stack.
+     *   Category: Statements
+     *   Type: Function
+     *   Operands: uint16_t argc
+     *   Stack: callee, this, args[0], ..., args[argc-1] => rval
+     *   nuses: (argc+2)
+     */ \
     macro(JSOP_CALL,      58, "call",       NULL,         3, -1,  1, JOF_UINT16|JOF_INVOKE|JOF_TYPESET) \
+    /*
+     * Looks up name on the scope chain and pushes its value onto the stack.
+     *   Category: Variables and Scopes
+     *   Type: Variables
+     *   Operands: uint32_t nameIndex
+     *   Stack: => val
+     */ \
     macro(JSOP_NAME,      59, "name",       NULL,         5,  0,  1, JOF_ATOM|JOF_NAME|JOF_TYPESET) \
+    /*
+     * Pushes numeric constant onto the stack.
+     *   Category: Literals
+     *   Type: Constants
+     *   Operands: uint32_t constIndex
+     *   Stack: => val
+     */ \
     macro(JSOP_DOUBLE,    60, "double",     NULL,         5,  0,  1, JOF_DOUBLE) \
+    /*
+     * Pushes string constant onto the stack.
+     *   Category: Literals
+     *   Type: Constants
+     *   Operands: uint32_t atomIndex
+     *   Stack: => string
+     */ \
     macro(JSOP_STRING,    61, "string",     NULL,         5,  0,  1, JOF_ATOM) \
+    /*
+     * Pushes '0' onto the stack.
+     *   Category: Literals
+     *   Type: Constants
+     *   Operands:
+     *   Stack: => 0
+     */ \
     macro(JSOP_ZERO,      62, "zero",       "0",          1,  0,  1, JOF_BYTE) \
+    /*
+     * Pushes '1' onto the stack.
+     *   Category: Literals
+     *   Type: Constants
+     *   Operands:
+     *   Stack: => 1
+     */ \
     macro(JSOP_ONE,       63, "one",        "1",          1,  0,  1, JOF_BYTE) \
+    /*
+     * Pushes 'null' onto the stack.
+     *   Category: Literals
+     *   Type: Constants
+     *   Operands:
+     *   Stack: => null
+     */ \
     macro(JSOP_NULL,      64, js_null_str,  js_null_str,  1,  0,  1, JOF_BYTE) \
+    /*
+     * Pushes 'this' value for current stack frame onto the stack.
+     *   Category: Variables and Scopes
+     *   Type: This
+     *   Operands:
+     *   Stack: => this
+     */ \
     macro(JSOP_THIS,      65, js_this_str,  js_this_str,  1,  0,  1, JOF_BYTE) \
+    /*
+     * Pushes boolean value onto the stack.
+     *   Category: Literals
+     *   Type: Constants
+     *   Operands:
+     *   Stack: => true/false
+     */ \
     macro(JSOP_FALSE,     66, js_false_str, js_false_str, 1,  0,  1, JOF_BYTE) \
     macro(JSOP_TRUE,      67, js_true_str,  js_true_str,  1,  0,  1, JOF_BYTE) \
     macro(JSOP_OR,        68, "or",         NULL,         5,  1,  1, JOF_JUMP|JOF_DETECTING|JOF_LEFTASSOC) \
     macro(JSOP_AND,       69, "and",        NULL,         5,  1,  1, JOF_JUMP|JOF_DETECTING|JOF_LEFTASSOC) \
     \
     /* The switch bytecodes have variable length. */ \
     macro(JSOP_TABLESWITCH, 70, "tableswitch", NULL,     -1,  1,  0,  JOF_TABLESWITCH|JOF_DETECTING) \
     \
     /*
-     * Prologue emitted in scripts expected to run once, which deoptimizes code if
-     * it executes multiple times.
+     * Prologue emitted in scripts expected to run once, which deoptimizes code
+     * if it executes multiple times.
+     *   Category: Statements
+     *   Type: Function
+     *   Operands:
+     *   Stack: =>
      */ \
     macro(JSOP_RUNONCE,   71, "runonce",    NULL,         1,  0,  0,  JOF_BYTE) \
     \
     /* New, infallible/transitive identity ops. */ \
     /*
      * Pops the top two values from the stack, then pushes the result of
      * applying the operator to the two values.
      *   Category: Operator
@@ -321,18 +516,22 @@ 1234567890123456789012345678901234567890
      *   Operands:
      *   Stack: lval, rval => (lval OP rval)
      */ \
     macro(JSOP_STRICTEQ,  72, "stricteq",   "===",        1,  2,  1, JOF_BYTE|JOF_DETECTING|JOF_LEFTASSOC|JOF_ARITH) \
     macro(JSOP_STRICTNE,  73, "strictne",   "!==",        1,  2,  1, JOF_BYTE|JOF_DETECTING|JOF_LEFTASSOC|JOF_ARITH) \
     \
     /*
      * Sometimes web pages do 'o.Item(i) = j'. This is not an early SyntaxError,
-     * for web compatibility. Instead we emit JSOP_SETCALL after the function call,
-     * an opcode that always throws.
+     * for web compatibility. Instead we emit JSOP_SETCALL after the function
+     * call, an opcode that always throws.
+     *   Category: Statements
+     *   Type: Function
+     *   Operands:
+     *   Stack: =>
      */ \
     macro(JSOP_SETCALL,   74, "setcall",    NULL,         1,  0,  0, JOF_BYTE) \
     \
     /*
      * JSOP_ITER sets up a for-in or for-each-in loop using the JSITER_* flag bits
      * in this op's uint8_t immediate operand. It replaces the top of stack value
      * with an iterator for that value.
      *
@@ -343,95 +542,313 @@ 1234567890123456789012345678901234567890
      * JSOP_ENDITER cleans up after the loop. It uses the slot above the iterator
      * for temporary GC rooting.
      */ \
     macro(JSOP_ITER,      75, "iter",       NULL,         2,  1,  1,  JOF_UINT8) \
     macro(JSOP_MOREITER,  76, "moreiter",   NULL,         1,  1,  2,  JOF_BYTE) \
     macro(JSOP_ITERNEXT,  77, "iternext",   "<next>",     1,  0,  1,  JOF_BYTE) \
     macro(JSOP_ENDITER,   78, "enditer",    NULL,         1,  1,  0,  JOF_BYTE) \
     \
+    /*
+     * Invokes 'callee' with 'this' and 'args', pushes return value onto the
+     * stack.
+     *
+     * This is for 'f.apply'.
+     *   Category: Statements
+     *   Type: Function
+     *   Operands: uint16_t argc
+     *   Stack: callee, this, args[0], ..., args[argc-1] => rval
+     *   nuses: (argc+2)
+     */ \
     macro(JSOP_FUNAPPLY,  79, "funapply",   NULL,         3, -1,  1,  JOF_UINT16|JOF_INVOKE|JOF_TYPESET) \
     \
-    /* Push object initializer literal. */ \
+    /*
+     * Pushes deep-cloned object literal or singleton onto the stack.
+     *   Category: Literals
+     *   Type: Object
+     *   Operands: uint32_t objectIndex
+     *   Stack: => obj
+     */ \
     macro(JSOP_OBJECT,    80, "object",     NULL,         5,  0,  1,  JOF_OBJECT) \
     \
     /*
      * Pops the top value off the stack.
      *   Category: Operator
      *   Type: Stack Operations
      *   Operands:
      *   Stack: v =>
      */ \
     macro(JSOP_POP,       81, "pop",        NULL,         1,  1,  0,  JOF_BYTE) \
     \
-    /* Call a function as a constructor; operand is argc. */ \
+    /*
+     * Invokes 'callee' as a constructor with 'this' and 'args', pushes return
+     * value onto the stack.
+     *   Category: Statements
+     *   Type: Function
+     *   Operands: uint16_t argc
+     *   Stack: callee, this, args[0], ..., args[argc-1] => rval
+     *   nuses: (argc+2)
+     */ \
     macro(JSOP_NEW,       82, js_new_str,   NULL,         3, -1,  1,  JOF_UINT16|JOF_INVOKE|JOF_TYPESET) \
-    \
+    /*
+     * Pops the top three values on the stack as 'iterable', 'index' and 'obj',
+     * iterates over 'iterable' and stores the iteration values as 'index + i'
+     * elements of 'obj', pushes 'obj' and 'index + iteration count' onto the
+     * stack.
+     *
+     * This opcode is used in Array literals with spread and spreadcall
+     * arguments.
+     *   Category: Literals
+     *   Type: Array
+     *   Operands:
+     *   Stack: obj, index, iterable => obj, (index + iteration count)
+     */ \
     macro(JSOP_SPREAD,    83, "spread",     NULL,         1,  3,  2,  JOF_BYTE|JOF_ELEM|JOF_SET) \
     \
-    /* Fast get/set ops for function arguments and local variables. */ \
+    /*
+     * Fast get op for function arguments and local variables.
+     *
+     * Pushes 'arguments[argno]' onto the stack.
+     *   Category: Variables and Scopes
+     *   Type: Arguments
+     *   Operands: uint16_t argno
+     *   Stack: => arguments[argno]
+     */ \
     macro(JSOP_GETARG,    84, "getarg",     NULL,         3,  0,  1,  JOF_QARG |JOF_NAME) \
+    /*
+     * Fast set op for function arguments and local variables.
+     *
+     * Sets 'arguments[argno]' as the top of stack value.
+     *   Category: Variables and Scopes
+     *   Type: Arguments
+     *   Operands: uint16_t argno
+     *   Stack: v => v
+     */ \
     macro(JSOP_SETARG,    85, "setarg",     NULL,         3,  1,  1,  JOF_QARG |JOF_NAME|JOF_SET) \
+    /*
+     * Pushes the value of local variable onto the stack.
+     *   Category: Variables and Scopes
+     *   Type: Local Variables
+     *   Operands: uint32_t localno
+     *   Stack: => val
+     */ \
     macro(JSOP_GETLOCAL,  86,"getlocal",    NULL,         4,  0,  1,  JOF_LOCAL|JOF_NAME) \
+    /*
+     * Stores the top stack value to the given local.
+     *   Category: Variables and Scopes
+     *   Type: Local Variables
+     *   Operands: uint32_t localno
+     *   Stack: v => v
+     */ \
     macro(JSOP_SETLOCAL,  87,"setlocal",    NULL,         4,  1,  1,  JOF_LOCAL|JOF_NAME|JOF_SET|JOF_DETECTING) \
     \
-    /* Push unsigned 16-bit int constant. */ \
-    macro(JSOP_UINT16,    88, "uint16",     NULL,         3,  0,  1,  JOF_UINT16) \
-    \
     /*
-     * Object and array literal support.  NEWINIT takes the kind of initializer
-     * (JSProto_Array or JSProto_Object).  NEWARRAY is an array initializer
-     * taking the final length, which can be filled in at the start and initialized
-     * directly.  NEWOBJECT is an object initializer taking an object with the final
-     * shape, which can be set at the start and slots then filled in directly.
-     * NEWINIT has an extra byte so it can be exchanged with NEWOBJECT during emit.
+     * Pushes unsigned 16-bit int immediate integer operand onto the stack.
+     *   Category: Literals
+     *   Type: Constants
+     *   Operands: uint16_t val
+     *   Stack: => val
+     */ \
+    macro(JSOP_UINT16,    88, "uint16",     NULL,         3,  0,  1,  JOF_UINT16) \
+    \
+    /* Object and array literal support. */ \
+    /*
+     * Pushes newly created object onto the stack.
+     *
+     * This opcode takes the kind of initializer (JSProto_Array or
+     * JSProto_Object).
+     *
+     * This opcode has an extra byte so it can be exchanged with JSOP_NEWOBJECT
+     * during emit.
+     *   Category: Literals
+     *   Type: Object
+     *   Operands: uint8_t kind (, uint24_t extra)
+     *   Stack: => obj
      */ \
     macro(JSOP_NEWINIT,   89, "newinit",    NULL,         5,  0,  1, JOF_UINT8) \
+    /*
+     * Pushes newly created array onto the stack.
+     *
+     * This opcode takes the final length, which is preallocated.
+     *   Category: Literals
+     *   Type: Array
+     *   Operands: uint24_t length
+     *   Stack: => obj
+     */ \
     macro(JSOP_NEWARRAY,  90, "newarray",   NULL,         4,  0,  1, JOF_UINT24) \
+    /*
+     * Pushes newly created object onto the stack.
+     *
+     * This opcode takes an object with the final shape, which can be set at
+     * the start and slots then filled in directly.
+     *   Category: Literals
+     *   Type: Object
+     *   Operands: uint32_t baseobjIndex
+     *   Stack: => obj
+     */ \
     macro(JSOP_NEWOBJECT, 91, "newobject",  NULL,         5,  0,  1, JOF_OBJECT) \
+    /*
+     * A no-operation bytecode.
+     *
+     * Indicates the end of object/array initialization, and used for
+     * Type-Inference, decompile, etc.
+     *   Category: Literals
+     *   Type: Object
+     *   Operands:
+     *   Stack: =>
+     */ \
     macro(JSOP_ENDINIT,   92, "endinit",    NULL,         1,  0,  0, JOF_BYTE) \
+    /*
+     * Initialize a named property in an object literal, like '{a: x}'.
+     *
+     * Pops the top two values on the stack as 'val' and 'obj', defines
+     * 'nameIndex' property of 'obj' as 'val', pushes 'obj' onto the stack.
+     *   Category: Literals
+     *   Type: Object
+     *   Operands: uint32_t nameIndex
+     *   Stack: obj, val => obj
+     */ \
     macro(JSOP_INITPROP,  93, "initprop",   NULL,         5,  2,  1, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING) \
     \
-    /* Initialize a numeric property in an object literal, like {1: x}. */ \
+    /*
+     * Initialize a numeric property in an object literal, like '{1: x}'.
+     *
+     * Pops the top three values on the stack as 'val', 'id' and 'obj', defines
+     * 'id' property of 'obj' as 'val', pushes 'obj' onto the stack.
+     *   Category: Literals
+     *   Type: Object
+     *   Operands:
+     *   Stack: obj, id, val => obj
+     */ \
     macro(JSOP_INITELEM,  94, "initelem",   NULL,         1,  3,  1, JOF_BYTE|JOF_ELEM|JOF_SET|JOF_DETECTING) \
     \
-    /* Used in array literals with spread. */ \
+    /*
+     * Pops the top three values on the stack as 'val', 'index' and 'obj', sets
+     * 'index' property of 'obj' as 'val', pushes 'obj' and 'index + 1' onto
+     * the stack.
+     *
+     * This opcode is used in Array literals with spread and spreadcall
+     * arguments.
+     *   Category: Literals
+     *   Type: Array
+     *   Operands:
+     *   Stack: obj, index, val => obj, (index + 1)
+     */ \
     macro(JSOP_INITELEM_INC,95, "initelem_inc", NULL,     1,  3,  2, JOF_BYTE|JOF_ELEM|JOF_SET) \
     \
-    /* Initialize an array element. */ \
+    /*
+     * Initialize an array element.
+     *
+     * Pops the top two values on the stack as 'val' and 'obj', sets 'index'
+     * property of 'obj' as 'val', pushes 'obj' onto the stack.
+     *   Category: Literals
+     *   Type: Array
+     *   Operands: uint24_t index
+     *   Stack: obj, val => obj
+     */ \
     macro(JSOP_INITELEM_ARRAY,96, "initelem_array", NULL, 4,  2,  1,  JOF_UINT24|JOF_ELEM|JOF_SET|JOF_DETECTING) \
     \
     /*
-     * Initialize a getter/setter in an object literal. The INITELEM* ops are used
-     * for numeric properties like {get 2() {}}.
+     * Initialize a getter in an object literal.
+     *
+     * Pops the top two values on the stack as 'val' and 'obj', defines getter
+     * of 'obj' as 'val', pushes 'obj' onto the stack.
+     *   Category: Literals
+     *   Type: Object
+     *   Operands: uint32_t nameIndex
+     *   Stack: obj, val => obj
      */ \
     macro(JSOP_INITPROP_GETTER,  97, "initprop_getter",   NULL, 5,  2,  1, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING) \
+    /*
+     * Initialize a setter in an object literal.
+     *
+     * Pops the top two values on the stack as 'val' and 'obj', defines setter
+     * of 'obj' as 'val', pushes 'obj' onto the stack.
+     *   Category: Literals
+     *   Type: Object
+     *   Operands: uint32_t nameIndex
+     *   Stack: obj, val => obj
+     */ \
     macro(JSOP_INITPROP_SETTER,  98, "initprop_setter",   NULL, 5,  2,  1, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING) \
+    /*
+     * Initialize a numeric getter in an object literal like
+     * '{get 2() {}}'.
+     *
+     * Pops the top three values on the stack as 'val', 'id' and 'obj', defines
+     * 'id' getter of 'obj' as 'val', pushes 'obj' onto the stack.
+     *   Category: Literals
+     *   Type: Object
+     *   Operands:
+     *   Stack: obj, id, val => obj
+     */ \
     macro(JSOP_INITELEM_GETTER,  99, "initelem_getter",   NULL, 1,  3,  1, JOF_BYTE|JOF_ELEM|JOF_SET|JOF_DETECTING) \
+    /*
+     * Initialize a numeric setter in an object literal like
+     * '{set 2(v) {}}'.
+     *
+     * Pops the top three values on the stack as 'val', 'id' and 'obj', defines
+     * 'id' setter of 'obj' as 'val', pushes 'obj' onto the stack.
+     *   Category: Literals
+     *   Type: Object
+     *   Operands:
+     *   Stack: obj, id, val => obj
+     */ \
     macro(JSOP_INITELEM_SETTER, 100, "initelem_setter",   NULL, 1,  3,  1, JOF_BYTE|JOF_ELEM|JOF_SET|JOF_DETECTING) \
     \
     macro(JSOP_UNUSED101,  101, "unused101",   NULL,         1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED102,  102, "unused102",   NULL,         1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED103,  103, "unused103",   NULL,         1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED104,  104, "unused104",   NULL,         1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED105,  105, "unused105",   NULL,         1,  0,  0,  JOF_BYTE) \
     \
     /* The argument is the offset to the next statement and is used by IonMonkey. */ \
     macro(JSOP_LABEL,     106,"label",     NULL,          5,  0,  0,  JOF_JUMP) \
     \
     macro(JSOP_UNUSED107, 107,"unused107",  NULL,         1,  0,  0,  JOF_BYTE) \
     \
-    /* Like JSOP_FUNAPPLY but for f.call instead of f.apply. */ \
+    /*
+     * Invokes 'callee' with 'this' and 'args', pushes return value onto the
+     * stack.
+     *
+     * If 'callee' is determined to be the canonical 'Function.prototype.call'
+     * function, then this operation is optimized to directly call 'callee'
+     * with 'args[0]' as 'this', and the remaining arguments as formal args
+     * to 'callee'.
+     *
+     * Like JSOP_FUNAPPLY but for 'f.call' instead of 'f.apply'.
+     *   Category: Statements
+     *   Type: Function
+     *   Operands: uint16_t argc
+     *   Stack: callee, this, args[0], ..., args[argc-1] => rval
+     *   nuses: (argc+2)
+     */ \
     macro(JSOP_FUNCALL,   108,"funcall",    NULL,         3, -1,  1, JOF_UINT16|JOF_INVOKE|JOF_TYPESET) \
     \
     /* This opcode is the target of the backwards jump for some loop. */ \
     macro(JSOP_LOOPHEAD,  109,"loophead",   NULL,         1,  0,  0,  JOF_BYTE) \
     \
     /* ECMA-compliant assignment ops. */ \
+    /*
+     * Looks up name on the scope chain and pushes the scope which contains
+     * the name onto the stack. If not found, pushes global scope onto the
+     * stack.
+     *   Category: Variables and Scopes
+     *   Type: Variables
+     *   Operands: uint32_t nameIndex
+     *   Stack: => scope
+     */ \
     macro(JSOP_BINDNAME,  110,"bindname",   NULL,         5,  0,  1,  JOF_ATOM|JOF_NAME|JOF_SET) \
+    /*
+     * Pops a scope and value from the stack, assigns value to the given name,
+     * and pushes the value back on the stack
+     *   Category: Variables and Scopes
+     *   Type: Variables
+     *   Operands: uint32_t nameIndex
+     *   Stack: scope, val => val
+     */ \
     macro(JSOP_SETNAME,   111,"setname",    NULL,         5,  2,  1,  JOF_ATOM|JOF_NAME|JOF_SET|JOF_DETECTING) \
     \
     /* Exception handling ops. */ \
     macro(JSOP_THROW,     112,js_throw_str, NULL,         1,  1,  0,  JOF_BYTE) \
     \
     /*
      * Pops the top two values 'id' and 'obj' from the stack, then pushes
      * 'id in obj'.  This will throw a 'TypeError' if 'obj' is not an object.
@@ -449,57 +866,132 @@ 1234567890123456789012345678901234567890
      * object.
      *   Category: Operator
      *   Type: Special Operators
      *   Operands:
      *   Stack: obj, ctor => (obj instanceof ctor)
      */ \
     macro(JSOP_INSTANCEOF,114,js_instanceof_str,js_instanceof_str,1,2,1,JOF_BYTE|JOF_LEFTASSOC|JOF_TMPSLOT) \
     \
-    /* debugger op */ \
+    /*
+     * Invokes debugger.
+     *   Category: Statements
+     *   Type: Debugger
+     *   Operands:
+     *   Stack: =>
+     */ \
     macro(JSOP_DEBUGGER,  115,"debugger",   NULL,         1,  0,  0, JOF_BYTE) \
     \
     /* gosub/retsub for finally handling */ \
     macro(JSOP_GOSUB,     116,"gosub",      NULL,         5,  0,  0,  JOF_JUMP) \
     macro(JSOP_RETSUB,    117,"retsub",     NULL,         1,  2,  0,  JOF_BYTE) \
     \
     /* More exception handling ops. */ \
     macro(JSOP_EXCEPTION, 118,"exception",  NULL,         1,  0,  1,  JOF_BYTE) \
     \
-    /* Embedded lineno to speedup pc->line mapping. */ \
+    /*
+     * Embedded lineno to speedup 'pc->line' mapping.
+     *   Category: Other
+     *   Operands: uint32_t lineno
+     *   Stack: =>
+     */ \
     macro(JSOP_LINENO,    119,"lineno",     NULL,         3,  0,  0,  JOF_UINT16) \
     \
     /*
      * ECMA-compliant switch statement ops.
      * CONDSWITCH is a decompilable NOP; CASE is ===, POP, jump if true, re-push
      * lval if false; and DEFAULT is POP lval and GOTO.
      */ \
     macro(JSOP_CONDSWITCH,120,"condswitch", NULL,         1,  0,  0,  JOF_BYTE) \
     macro(JSOP_CASE,      121,"case",       NULL,         5,  2,  1,  JOF_JUMP) \
     macro(JSOP_DEFAULT,   122,"default",    NULL,         5,  1,  0,  JOF_JUMP) \
     \
+    /* ECMA-compliant call to eval op. */ \
     /*
-     * ECMA-compliant call to eval op
+     * Invokes 'eval' with 'args' and pushes return value onto the stack.
+     *
+     * If 'eval' in global scope is not original one, invokes the function
+     * with 'this' and 'args', and pushes return value onto the stack.
+     *   Category: Statements
+     *   Type: Function
+     *   Operands: uint16_t argc
+     *   Stack: callee, this, args[0], ..., args[argc-1] => rval
+     *   nuses: (argc+2)
      */ \
     macro(JSOP_EVAL,      123,"eval",       NULL,         3, -1,  1, JOF_UINT16|JOF_INVOKE|JOF_TYPESET) \
     \
     macro(JSOP_UNUSED124,  124, "unused124", NULL,      1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED125,  125, "unused125", NULL,      1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED126,  126, "unused126", NULL,      1,  0,  0,  JOF_BYTE) \
     \
-    /* Prolog bytecodes for defining function, var, and const names. */ \
+    /*
+     * Defines the given function on the current scope.
+     *
+     * This is used for global scripts and also in some cases for function
+     * scripts where use of dynamic scoping inhibits optimization.
+     *   Category: Variables and Scopes
+     *   Type: Variables
+     *   Operands: uint32_t funcIndex
+     *   Stack: =>
+     */ \
     macro(JSOP_DEFFUN,    127,"deffun",     NULL,         5,  0,  0,  JOF_OBJECT) \
+    /*
+     * Defines the new binding on the frame's current variables-object (the
+     * scope object on the scope chain designated to receive new variables)
+     * with 'READONLY' attribute.
+     *
+     * This is used for global scripts and also in some cases for function
+     * scripts where use of dynamic scoping inhibits optimization.
+     *   Category: Variables and Scopes
+     *   Type: Variables
+     *   Operands: uint32_t nameIndex
+     *   Stack: =>
+     */ \
     macro(JSOP_DEFCONST,  128,"defconst",   NULL,         5,  0,  0,  JOF_ATOM) \
+    /*
+     * Defines the new binding on the frame's current variables-object (the
+     * scope object on the scope chain designated to receive new variables).
+     *
+     * This is used for global scripts and also in some cases for function
+     * scripts where use of dynamic scoping inhibits optimization.
+     *   Category: Variables and Scopes
+     *   Type: Variables
+     *   Operands: uint32_t nameIndex
+     *   Stack: =>
+     */ \
     macro(JSOP_DEFVAR,    129,"defvar",     NULL,         5,  0,  0,  JOF_ATOM) \
     \
-    /* Push a closure for a named or anonymous function expression. */ \
+    /*
+     * Pushes a closure for a named or anonymous function expression onto the
+     * stack.
+     *   Category: Statements
+     *   Type: Function
+     *   Operands: uint32_t funcIndex
+     *   Stack: => obj
+     */ \
     macro(JSOP_LAMBDA,    130, "lambda",    NULL,         5,  0,  1, JOF_OBJECT) \
+    /*
+     * Pops the top of stack value as 'this', pushes an arrow function with
+     * 'this' onto the stack.
+     *   Category: Statements
+     *   Type: Function
+     *   Operands: uint32_t funcIndex
+     *   Stack: this => obj
+     */ \
     macro(JSOP_LAMBDA_ARROW, 131, "lambda_arrow", NULL,   5,  1,  1, JOF_OBJECT) \
     \
-    /* Used for named function expression self-naming, if lightweight. */ \
+    /*
+     * Pushes current callee onto the stack.
+     *
+     * Used for named function expression self-naming, if lightweight.
+     *   Category: Variables and Scopes
+     *   Type: Arguments
+     *   Operands:
+     *   Stack: => callee
+     */ \
     macro(JSOP_CALLEE,    132, "callee",    NULL,         1,  0,  1, JOF_BYTE) \
     \
     /*
      * Picks the nth element from the stack and moves it to the top of the
      * stack.
      *   Category: Operator
      *   Type: Stack Operations
      *   Operands: uint8_t n
@@ -510,79 +1002,154 @@ 1234567890123456789012345678901234567890
     /*
      * Exception handling no-op, for more economical byte-coding than SRC_TRYFIN
      * srcnote-annotated JSOP_NOPs and to simply stack balance handling.
      */ \
     macro(JSOP_TRY,         134,"try",        NULL,       1,  0,  0,  JOF_BYTE) \
     macro(JSOP_FINALLY,     135,"finally",    NULL,       1,  0,  2,  JOF_BYTE) \
     \
     /*
-     * An "aliased variable" is a var, let, or formal arg that is aliased. Sources
-     * of aliasing include: nested functions accessing the vars of an enclosing
-     * function, function statements that are conditionally executed, 'eval',
-     * 'with', and 'arguments'. All of these cases require creating a CallObject to
-     * own the aliased variable.
+     * Pushes aliased variable onto the stack.
+     *
+     * An "aliased variable" is a var, let, or formal arg that is aliased.
+     * Sources of aliasing include: nested functions accessing the vars of an
+     * enclosing function, function statements that are conditionally executed,
+     * 'eval', 'with', and 'arguments'. All of these cases require creating a
+     * CallObject to own the aliased variable.
      *
      * An ALIASEDVAR opcode contains the following immediates:
      *  uint8 hops:  the number of scope objects to skip to find the ScopeObject
      *               containing the variable being accessed
      *  uint24 slot: the slot containing the variable in the ScopeObject (this
      *               'slot' does not include RESERVED_SLOTS).
+     *   Category: Variables and Scopes
+     *   Type: Aliased Variables
+     *   Operands: uint8_t hops, uint24_t slot
+     *   Stack: => aliasedVar
      */ \
     macro(JSOP_GETALIASEDVAR, 136,"getaliasedvar",NULL,      5,  0,  1,  JOF_SCOPECOORD|JOF_NAME|JOF_TYPESET) \
+    /*
+     * Sets aliased variable as the top of stack value.
+     *   Category: Variables and Scopes
+     *   Type: Aliased Variables
+     *   Operands: uint8_t hops, uint24_t slot
+     *   Stack: v => v
+     */ \
     macro(JSOP_SETALIASEDVAR, 137,"setaliasedvar",NULL,      5,  1,  1,  JOF_SCOPECOORD|JOF_NAME|JOF_SET|JOF_DETECTING) \
     \
     macro(JSOP_UNUSED138,  138, "unused138",   NULL,         1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED139,  139, "unused139",   NULL,         1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED140,  140, "unused140",   NULL,         1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED141,  141, "unused141",   NULL,         1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED142,  142, "unused142",   NULL,         1,  0,  0,  JOF_BYTE) \
     \
     /*
+     * Pushes the value of the intrinsic onto the stack.
+     *
      * Intrinsic names are emitted instead of JSOP_*NAME ops when the
-     * CompileOptions flag "selfHostingMode" is set.
+     * 'CompileOptions' flag 'selfHostingMode' is set.
      *
      * They are used in self-hosted code to access other self-hosted values and
      * intrinsic functions the runtime doesn't give client JS code access to.
+     *   Category: Variables and Scopes
+     *   Type: Intrinsics
+     *   Operands: uint32_t nameIndex
+     *   Stack: => intrinsic[name]
      */ \
     macro(JSOP_GETINTRINSIC,  143, "getintrinsic",  NULL, 5,  0,  1, JOF_ATOM|JOF_NAME|JOF_TYPESET) \
+    /*
+     * Pops the top two values on the stack as 'val' and 'scope', sets intrinsic
+     * as 'val', and pushes 'val' onto the stack.
+     *
+     * 'scope' is not used.
+     *   Category: Variables and Scopes
+     *   Type: Intrinsics
+     *   Operands: uint32_t nameIndex
+     *   Stack: scope, val => val
+     */ \
     macro(JSOP_SETINTRINSIC,  144, "setintrinsic",  NULL, 5,  2,  1, JOF_ATOM|JOF_NAME|JOF_SET|JOF_DETECTING) \
+    /*
+     * Pushes 'intrinsicHolder' onto the stack.
+     *   Category: Variables and Scopes
+     *   Type: Intrinsics
+     *   Operands: uint32_t nameIndex
+     *   Stack: => intrinsicHolder
+     */ \
     macro(JSOP_BINDINTRINSIC, 145, "bindintrinsic", NULL, 5,  0,  1, JOF_ATOM|JOF_NAME|JOF_SET) \
     \
     /* Unused. */ \
     macro(JSOP_UNUSED146,     146,"unused146", NULL,      1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED147,     147,"unused147", NULL,      1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED148,     148,"unused148", NULL,      1,  0,  0,  JOF_BYTE) \
     \
     /* Placeholders for a real jump opcode set during backpatch chain fixup. */ \
     macro(JSOP_BACKPATCH,     149,"backpatch", NULL,      5,  0,  0,  JOF_JUMP) \
     macro(JSOP_UNUSED150,     150,"unused150", NULL,      1,  0,  0,  JOF_BYTE) \
     \
     /* Set pending exception from the stack, to trigger rethrow. */ \
     macro(JSOP_THROWING,      151,"throwing", NULL,       1,  1,  0,  JOF_BYTE) \
     \
-    /* Set the return value pseudo-register in stack frame. */ \
+    /*
+     * Pops the top of stack value as 'rval', sets the return value in stack
+     * frame as 'rval'.
+     *   Category: Statements
+     *   Type: Function
+     *   Operands:
+     *   Stack: rval =>
+     */ \
     macro(JSOP_SETRVAL,       152,"setrval",    NULL,       1,  1,  0,  JOF_BYTE) \
     /*
-     * Stop interpretation and return value set by JSOP_SETRVAL. When not set,
-     * returns UndefinedValue. Also emitted at end of script so interpreter
-     * don't need to check if opcode is still in script range.
+     * Stops interpretation and returns value set by JSOP_SETRVAL. When not set,
+     * returns 'undefined'.
+     *
+     * Also emitted at end of script so interpreter don't need to check if
+     * opcode is still in script range.
+     *   Category: Statements
+     *   Type: Function
+     *   Operands:
+     *   Stack: =>
      */ \
     macro(JSOP_RETRVAL,       153,"retrval",    NULL,       1,  0,  0,  JOF_BYTE) \
     \
-    /* Free variable references that must either be found on the global or a ReferenceError */ \
+    /*
+     * Looks up name on global scope and pushes its value onto the stack.
+     *
+     * Free variable references that must either be found on the global or a
+     * ReferenceError.
+     *   Category: Variables and Scopes
+     *   Type: Free Variables
+     *   Operands: uint32_t nameIndex
+     *   Stack: => val
+     */ \
     macro(JSOP_GETGNAME,      154,"getgname",  NULL,       5,  0,  1, JOF_ATOM|JOF_NAME|JOF_TYPESET|JOF_GNAME) \
+    /*
+     * Pops the top two values on the stack as 'val' and 'scope', sets property
+     * of 'scope' as 'val' and pushes 'val' back on the stack.
+     *
+     * 'scope' should be the global scope.
+     *   Category: Variables and Scopes
+     *   Type: Free Variables
+     *   Operands: uint32_t nameIndex
+     *   Stack: scope, val => val
+     */ \
     macro(JSOP_SETGNAME,      155,"setgname",  NULL,       5,  2,  1, JOF_ATOM|JOF_NAME|JOF_SET|JOF_DETECTING|JOF_GNAME) \
     \
     macro(JSOP_UNUSED156,  156, "unused156",   NULL,         1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED157,  157, "unused157",   NULL,         1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED158,  158, "unused158",   NULL,         1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED159,  159, "unused159",   NULL,         1,  0,  0,  JOF_BYTE) \
     \
-    /* Regular expression literal requiring special "fork on exec" handling. */ \
+    /*
+     * Pushes a regular expression literal onto the stack.
+     * It requires special "clone on exec" handling.
+     *   Category: Literals
+     *   Type: RegExp
+     *   Operands: uint32_t regexpIndex
+     *   Stack: => regexp
+     */ \
     macro(JSOP_REGEXP,        160,"regexp",   NULL,       5,  0,  1, JOF_REGEXP) \
     \
     macro(JSOP_UNUSED161,     161,"unused161",  NULL,     1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED162,     162,"unused162",  NULL,     1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED163,     163,"unused163",  NULL,     1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED164,     164,"unused164",  NULL,     1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED165,     165,"unused165",  NULL,     1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED166,     166,"unused166",  NULL,     1,  0,  0,  JOF_BYTE) \
@@ -599,38 +1166,77 @@ 1234567890123456789012345678901234567890
     macro(JSOP_UNUSED177,     177,"unused177",  NULL,     1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED178,     178,"unused178",  NULL,     1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED179,     179,"unused179",  NULL,     1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED180,     180,"unused180",  NULL,     1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED181,     181,"unused181",  NULL,     1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED182,     182,"unused182",  NULL,     1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED183,     183,"unused183",  NULL,     1,  0,  0,  JOF_BYTE) \
     \
+    /*
+     * Pops the top of stack value, pushes property of it onto the stack.
+     *
+     * Like JSOP_GETPROP but for call context.
+     *   Category: Literals
+     *   Type: Object
+     *   Operands: uint32_t nameIndex
+     *   Stack: obj => obj[name]
+     */ \
     macro(JSOP_CALLPROP,      184,"callprop",   NULL,     5,  1,  1, JOF_ATOM|JOF_PROP|JOF_TYPESET|JOF_TMPSLOT3) \
     \
     macro(JSOP_UNUSED185,     185,"unused185",  NULL,     1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED186,     186,"unused186",  NULL,     1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED187,     187,"unused187",  NULL,     1,  0,  0,  JOF_BYTE) \
     \
-    /* Opcode to hold 24-bit immediate integer operands. */ \
+    /*
+     * Pushes unsigned 24-bit int immediate integer operand onto the stack.
+     *   Category: Literals
+     *   Type: Constants
+     *   Operands: uint24_t val
+     *   Stack: => val
+     */ \
     macro(JSOP_UINT24,        188,"uint24",     NULL,     4,  0,  1, JOF_UINT24) \
     \
     macro(JSOP_UNUSED189,     189,"unused189",   NULL,    1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED190,     190,"unused190",   NULL,    1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED191,     191,"unused191",   NULL,    1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED192,     192,"unused192",   NULL,    1,  0,  0,  JOF_BYTE) \
     \
+    /*
+     * Pops the top two values on the stack as 'propval' and 'obj', pushes
+     * 'propval' property of 'obj' onto the stack.
+     *
+     * Like JSOP_GETELEM but for call context.
+     *   Category: Literals
+     *   Type: Object
+     *   Operands:
+     *   Stack: obj, propval => obj[propval]
+     */ \
     macro(JSOP_CALLELEM,      193, "callelem",   NULL,    1,  2,  1, JOF_BYTE |JOF_ELEM|JOF_TYPESET|JOF_LEFTASSOC) \
     \
-    /* __proto__: v inside an object initializer. */ \
+    /*
+     * '__proto__: v' inside an object initializer.
+     *
+     * Pops the top two values on the stack as 'newProto' and 'obj', sets
+     * prototype of 'obj' as 'newProto', pushes 'true' onto the stack if
+     * succeeded, 'false' if not.
+     *   Category: Literals
+     *   Type: Object
+     *   Operands:
+     *   Stack: obj, newProto => succeeded
+     */ \
     macro(JSOP_MUTATEPROTO,   194, "mutateproto",NULL,    1,  2,  1, JOF_BYTE) \
     \
     /*
-     * Get an extant property value, throwing ReferenceError if the identified
-     * property does not exist.
+     * Pops the top of stack value, gets an extant property value of it,
+     * throwing ReferenceError if the identified property does not exist.
+     *   Category: Literals
+     *   Type: Object
+     *   Operands: uint32_t nameIndex
+     *   Stack: obj => obj[name]
      */ \
     macro(JSOP_GETXPROP,      195,"getxprop",    NULL,    5,  1,  1, JOF_ATOM|JOF_PROP|JOF_TYPESET) \
     \
     macro(JSOP_UNUSED196,     196,"unused196",   NULL,    1,  0,  0, JOF_BYTE) \
     \
     /*
      * Pops the top stack value as 'val' and pushes 'typeof val'.  Note that
      * this opcode isn't used when, in the original source code, 'val' is a
@@ -639,65 +1245,168 @@ 1234567890123456789012345678901234567890
      *   Category: Operator
      *   Type: Special Operators
      *   Operands:
      *   Stack: val => (typeof val)
      */ \
     macro(JSOP_TYPEOFEXPR,    197,"typeofexpr",  NULL,    1,  1,  1, JOF_BYTE|JOF_DETECTING) \
     \
     /* Block-local scope support. */ \
+    /*
+     * Pushes block onto the scope chain.
+     *   Category: Variables and Scopes
+     *   Type: Block-local Scope
+     *   Operands: uint32_t staticBlockObjectIndex
+     *   Stack: =>
+     */ \
     macro(JSOP_PUSHBLOCKSCOPE,198,"pushblockscope", NULL, 5,  0,  0,  JOF_OBJECT) \
+    /*
+     * Pops block from the scope chain.
+     *   Category: Variables and Scopes
+     *   Type: Block-local Scope
+     *   Operands:
+     *   Stack: =>
+     */ \
     macro(JSOP_POPBLOCKSCOPE, 199,"popblockscope", NULL,  1,  0,  0,  JOF_BYTE) \
+    /*
+     * The opcode to assist the debugger.
+     *   Category: Statements
+     *   Type: Debugger
+     *   Operands:
+     *   Stack: =>
+     */ \
     macro(JSOP_DEBUGLEAVEBLOCK, 200,"debugleaveblock", NULL, 1,  0,  0,  JOF_BYTE) \
     \
     macro(JSOP_UNUSED201,     201,"unused201",  NULL,     1,  0,  0,  JOF_BYTE) \
     \
-    /* Generator and array comprehension support. */ \
+    /*
+     * Initializes generator frame, creates a generator, sets 'YIELDING' flag,
+     * stops interpretation and returns the generator.
+     *   Category: Statements
+     *   Type: Generator
+     *   Operands:
+     *   Stack: =>
+     */ \
     macro(JSOP_GENERATOR,     202,"generator",   NULL,    1,  0,  0,  JOF_BYTE) \
+    /*
+     * Pops the top of stack value as 'rval1', sets 'YIELDING' flag,
+     * stops interpretation and returns 'rval1', pushes sent value from
+     * 'send()' onto the stack.
+     *   Category: Statements
+     *   Type: Generator
+     *   Operands:
+     *   Stack: rval1 => rval2
+     */ \
     macro(JSOP_YIELD,         203,"yield",       NULL,    1,  1,  1,  JOF_BYTE) \
+    /*
+     * Pops the top two values on the stack as 'obj' and 'v', pushes 'v' to
+     * 'obj'.
+     *
+     * This opcode is used for Array Comprehension.
+     *   Category: Literals
+     *   Type: Array
+     *   Operands:
+     *   Stack: v, obj =>
+     */ \
     macro(JSOP_ARRAYPUSH,     204,"arraypush",   NULL,    1,  2,  0,  JOF_BYTE) \
     \
     macro(JSOP_UNUSED205,     205, "unused205",    NULL,  1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED206,     206, "unused206",    NULL,  1,  0,  0,  JOF_BYTE) \
     \
     macro(JSOP_UNUSED207,     207, "unused207",    NULL,  1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED208,     208, "unused208",    NULL,  1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED209,     209, "unused209",    NULL,  1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED210,     210, "unused210",    NULL,  1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED211,     211, "unused211",    NULL,  1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED212,     212, "unused212",    NULL,  1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED213,     213, "unused213",    NULL,  1,  0,  0,  JOF_BYTE) \
+    /*
+     * Pushes the global scope onto the stack.
+     *
+     * 'nameIndex' is not used.
+     *   Category: Variables and Scopes
+     *   Type: Free Variables
+     *   Operands: uint32_t nameIndex
+     *   Stack: => global
+     */ \
     macro(JSOP_BINDGNAME,     214, "bindgname",    NULL,  5,  0,  1,  JOF_ATOM|JOF_NAME|JOF_SET|JOF_GNAME) \
     \
-    /* Opcodes to hold 8-bit and 32-bit immediate integer operands. */ \
+    /*
+     * Pushes 8-bit int immediate integer operand onto the stack.
+     *   Category: Literals
+     *   Type: Constants
+     *   Operands: int8_t val
+     *   Stack: => val
+     */ \
     macro(JSOP_INT8,          215, "int8",         NULL,  2,  0,  1, JOF_INT8) \
+    /*
+     * Pushes 32-bit int immediate integer operand onto the stack.
+     *   Category: Literals
+     *   Type: Constants
+     *   Operands: int32_t val
+     *   Stack: => val
+     */ \
     macro(JSOP_INT32,         216, "int32",        NULL,  5,  0,  1, JOF_INT32) \
     \
-    /* Get the value of the 'length' property from a stacked value. */ \
+    /*
+     * Pops the top of stack value, pushes the 'length' property of it onto the
+     * stack.
+     *   Category: Literals
+     *   Type: Array
+     *   Operands: uint32_t nameIndex
+     *   Stack: obj => obj['length']
+     */ \
     macro(JSOP_LENGTH,        217, "length",       NULL,  5,  1,  1, JOF_ATOM|JOF_PROP|JOF_TYPESET|JOF_TMPSLOT3) \
     \
     /*
-     * Push a JSVAL_HOLE value onto the stack, representing an omitted property in
-     * an array literal (e.g. property 0 in the array [, 1]).  This opcode is used
-     * with the JSOP_NEWARRAY opcode.
+     * Pushes a JS_ELEMENTS_HOLE value onto the stack, representing an omitted
+     * property in an array literal (e.g. property 0 in the array '[, 1]').
+     *
+     * This opcode is used with the JSOP_NEWARRAY opcode.
+     *   Category: Literals
+     *   Type: Array
+     *   Operands:
+     *   Stack: => hole
      */ \
     macro(JSOP_HOLE,          218, "hole",         NULL,  1,  0,  1,  JOF_BYTE) \
     \
     macro(JSOP_UNUSED219,     219,"unused219",     NULL,  1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED220,     220,"unused220",     NULL,  1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED221,     221,"unused221",     NULL,  1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED222,     222,"unused222",     NULL,  1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED223,     223,"unused223",     NULL,  1,  0,  0,  JOF_BYTE) \
     \
+    /*
+     * Creates rest parameter array for current function call, and pushes it
+     * onto the stack.
+     *   Category: Variables and Scopes
+     *   Type: Arguments
+     *   Operands:
+     *   Stack: => rest
+     */ \
     macro(JSOP_REST,          224, "rest",         NULL,  1,  0,  1,  JOF_BYTE|JOF_TYPESET) \
     \
-    /* Pop the stack, convert to a jsid (int or string), and push back. */ \
+    /*
+     * Pops the top of stack value, converts it into a jsid (int or string), and
+     * pushes it onto the stack.
+     *   Category: Literals
+     *   Type: Object
+     *   Operands:
+     *   Stack: obj, id => obj, (jsid of id)
+     */ \
     macro(JSOP_TOID,          225, "toid",         NULL,  1,  1,  1,  JOF_BYTE) \
     \
-    /* Push the implicit 'this' value for calls to the associated name. */ \
+    /*
+     * Pushes the implicit 'this' value for calls to the associated name onto
+     * the stack.
+     *   Category: Variables and Scopes
+     *   Type: This
+     *   Operands: uint32_t nameIndex
+     *   Stack: => this
+     */                                                                 \
     macro(JSOP_IMPLICITTHIS,  226, "implicitthis", "",    5,  0,  1,  JOF_ATOM) \
     \
     /*
      * This opcode is the target of the entry jump for some loop. The uint8
      * argument is a bitfield. The lower 7 bits of the argument indicate the
      * loop depth. This value starts at 1 and is just a hint: deeply nested
      * loops all have the same value.  The upper bit is set if Ion should be
      * able to OSR at this point, which is true unless there is non-loop state
--- a/js/src/vm/TraceLogging.cpp
+++ b/js/src/vm/TraceLogging.cpp
@@ -85,16 +85,17 @@ const char* const text[] = {
     "MinorGC",
     "ParserCompileFunction",
     "ParserCompileLazy",
     "ParserCompileScript",
     "TraceLogger",
     "YarrCompile",
     "YarrInterpret",
     "YarrJIT",
+    "VM",
     "SplitCriticalEdges",
     "RenumberBlocks",
     "DominatorTree",
     "PhiAnalysis",
     "ApplyTypes",
     "ParallelSafetyAnalysis",
     "AliasAnalysis",
     "GVN",
@@ -104,16 +105,17 @@ const char* const text[] = {
     "EffectiveAddressAnalysis",
     "EliminateDeadCode",
     "EdgeCaseAnalysis",
     "EliminateRedundantChecks"
 };
 
 TraceLogger::TraceLogger()
  : enabled(false),
+   enabledTimes(0),
    failed(false),
    nextTextId(0),
    treeOffset(0),
    top(nullptr)
 { }
 
 bool
 TraceLogger::init(uint32_t loggerId)
@@ -122,17 +124,17 @@ TraceLogger::init(uint32_t loggerId)
         return false;
     if (!tree.init())
         return false;
     if (!stack.init())
         return false;
     if (!events.init())
         return false;
 
-    JS_ASSERT(loggerId <= 999);
+    MOZ_ASSERT(loggerId <= 999);
 
     char dictFilename[sizeof TRACE_LOG_DIR "tl-dict.100.json"];
     sprintf(dictFilename, TRACE_LOG_DIR "tl-dict.%d.json", loggerId);
     dictFile = fopen(dictFilename, "w");
     if (!dictFile)
         return false;
 
     char treeFilename[sizeof TRACE_LOG_DIR "tl-tree.100.tl"];
@@ -153,45 +155,155 @@ TraceLogger::init(uint32_t loggerId)
         dictFile = nullptr;
         treeFile = nullptr;
         return false;
     }
 
     uint64_t start = rdtsc() - traceLoggers.startupTime;
 
     TreeEntry &treeEntry = tree.pushUninitialized();
-    treeEntry.start = start;
-    treeEntry.stop = 0;
-    treeEntry.u.s.textId = 0;
-    treeEntry.u.s.hasChildren = false;
-    treeEntry.nextId = 0;
+    treeEntry.setStart(start);
+    treeEntry.setStop(0);
+    treeEntry.setTextId(0);
+    treeEntry.setHasChildren(false);
+    treeEntry.setNextId(0);
 
     StackEntry &stackEntry = stack.pushUninitialized();
-    stackEntry.treeId = 0;
-    stackEntry.lastChildId = 0;
-    stackEntry.active = true;
+    stackEntry.setTreeId(0);
+    stackEntry.setLastChildId(0);
+    stackEntry.setActive(true);
 
     int written = fprintf(dictFile, "[");
     if (written < 0)
         fprintf(stderr, "TraceLogging: Error while writing.\n");
 
     // Eagerly create the default textIds, to match their Tracelogger::TextId.
     for (uint32_t i = 0; i < LAST; i++) {
         mozilla::DebugOnly<uint32_t> textId = createTextId(text[i]);
-        JS_ASSERT(textId == i);
+        MOZ_ASSERT(textId == i);
     }
 
     enabled = true;
+    enabledTimes = 1;
+    return true;
+}
+
+bool
+TraceLogger::enable()
+{
+    if (enabled) {
+        enabledTimes++;
+        return true;
+    }
+
+    if (failed)
+        return false;
+
+    if (!tree.ensureSpaceBeforeAdd(stack.size())) {
+        if (!flush()) {
+            fprintf(stderr, "TraceLogging: Couldn't write the data to disk.\n");
+            failed = true;
+            return false;
+        }
+        if (!tree.ensureSpaceBeforeAdd(stack.size())) {
+            fprintf(stderr, "TraceLogging: Couldn't reserve enough space.\n");
+            failed = true;
+            return false;
+        }
+    }
+
+    uint64_t start = rdtsc() - traceLoggers.startupTime;
+    StackEntry *parent = &stack[0];
+    for (uint32_t i = 1; i < stack.size(); i++) {
+        if (!traceLoggers.isTextIdEnabled(stack[i].textId()))
+            continue;
+#ifdef DEBUG
+        TreeEntry entry;
+        if (!getTreeEntry(parent->treeId(), &entry))
+            return false;
+#endif
+
+        if (parent->lastChildId() == 0) {
+            MOZ_ASSERT(!entry.hasChildren());
+            MOZ_ASSERT(parent->treeId() == tree.currentId() + treeOffset);
+            if (!updateHasChildren(parent->treeId())) {
+                fprintf(stderr, "TraceLogging: Couldn't update an entry.\n");
+                failed = true;
+                return false;
+            }
+        } else {
+            MOZ_ASSERT(entry.hasChildren() == 1);
+            if (!updateNextId(parent->lastChildId(), tree.nextId() + treeOffset)) {
+                fprintf(stderr, "TraceLogging: Couldn't update an entry.\n");
+                failed = true;
+                return false;
+            }
+        }
+
+        TreeEntry &treeEntry = tree.pushUninitialized();
+        treeEntry.setStart(start);
+        treeEntry.setStop(0);
+        treeEntry.setTextId(stack[i].textId());
+        treeEntry.setHasChildren(false);
+        treeEntry.setNextId(0);
+
+        stack[i].setActive(true);
+        stack[i].setTreeId(tree.currentId() + treeOffset);
+
+        parent->setLastChildId(tree.currentId() + treeOffset);
+
+        parent = &stack[i];
+    }
+
+    enabled = true;
+    enabledTimes = 1;
+
+    return true;
+}
+
+bool
+TraceLogger::disable()
+{
+    if (failed)
+        return false;
+
+    if (!enabled)
+        return true;
+
+    if (enabledTimes > 1) {
+        enabledTimes--;
+        return true;
+    }
+
+    uint64_t stop = rdtsc() - traceLoggers.startupTime;
+    for (uint32_t i = 1; i < stack.size(); i++) {
+        if (!stack[i].active())
+            continue;
+
+        if (!updateStop(stack[i].treeId(), stop)) {
+            fprintf(stderr, "TraceLogging: Failed to stop an event.\n");
+            failed = true;
+            enabled = false;
+            return false;
+        }
+
+        stack[i].setActive(false);
+    }
+
+
+    enabled = false;
+    enabledTimes = 0;
+
     return true;
 }
 
 bool
 TraceLogger::flush()
 {
-    JS_ASSERT(!failed);
+    MOZ_ASSERT(!failed);
 
     if (treeFile) {
         // Format data in big endian.
         for (size_t i = 0; i < tree.size(); i++)
             entryToBigEndian(&tree[i]);
 
         int success = fseek(treeFile, 0, SEEK_END);
         if (success != 0)
@@ -358,33 +470,33 @@ TraceLogger::logTimestamp(uint32_t id)
     EventEntry &entry = events.pushUninitialized();
     entry.time = time;
     entry.textId = id;
 }
 
 void
 TraceLogger::entryToBigEndian(TreeEntry *entry)
 {
-    entry->start = htobe64(entry->start);
-    entry->stop = htobe64(entry->stop);
-    entry->u.value = htobe32((entry->u.s.textId << 1) + entry->u.s.hasChildren);
-    entry->nextId = htobe32(entry->nextId);
+    entry->start_ = htobe64(entry->start_);
+    entry->stop_ = htobe64(entry->stop_);
+    entry->u.value_ = htobe32((entry->u.s.textId_ << 1) + entry->u.s.hasChildren_);
+    entry->nextId_ = htobe32(entry->nextId_);
 }
 
 void
 TraceLogger::entryToSystemEndian(TreeEntry *entry)
 {
-    entry->start = be64toh(entry->start);
-    entry->stop = be64toh(entry->stop);
+    entry->start_ = be64toh(entry->start_);
+    entry->stop_ = be64toh(entry->stop_);
 
-    uint32_t data = be32toh(entry->u.value);
-    entry->u.s.textId = data >> 1;
-    entry->u.s.hasChildren = data & 0x1;
+    uint32_t data = be32toh(entry->u.value_);
+    entry->u.s.textId_ = data >> 1;
+    entry->u.s.hasChildren_ = data & 0x1;
 
-    entry->nextId = be32toh(entry->nextId);
+    entry->nextId_ = be32toh(entry->nextId_);
 }
 
 bool
 TraceLogger::getTreeEntry(uint32_t treeId, TreeEntry *entry)
 {
     // Entry is still in memory
     if (treeId >= treeOffset) {
         *entry = tree[treeId];
@@ -421,73 +533,82 @@ TraceLogger::saveTreeEntry(uint32_t tree
 
 bool
 TraceLogger::updateHasChildren(uint32_t treeId, bool hasChildren)
 {
     if (treeId < treeOffset) {
         TreeEntry entry;
         if (!getTreeEntry(treeId, &entry))
             return false;
-        entry.u.s.hasChildren = hasChildren;
+        entry.setHasChildren(hasChildren);
         if (!saveTreeEntry(treeId, &entry))
             return false;
         return true;
     }
 
-    tree[treeId - treeOffset].u.s.hasChildren = hasChildren;
+    tree[treeId - treeOffset].setHasChildren(hasChildren);
     return true;
 }
 
 bool
 TraceLogger::updateNextId(uint32_t treeId, uint32_t nextId)
 {
     if (treeId < treeOffset) {
         TreeEntry entry;
         if (!getTreeEntry(treeId, &entry))
             return false;
-        entry.nextId = nextId;
+        entry.setNextId(nextId);
         if (!saveTreeEntry(treeId, &entry))
             return false;
         return true;
     }
 
-    tree[treeId - treeOffset].nextId = nextId;
+    tree[treeId - treeOffset].setNextId(nextId);
     return true;
 }
 
 bool
 TraceLogger::updateStop(uint32_t treeId, uint64_t timestamp)
 {
     if (treeId < treeOffset) {
         TreeEntry entry;
         if (!getTreeEntry(treeId, &entry))
             return false;
-        entry.stop = timestamp;
+        entry.setStop(timestamp);
         if (!saveTreeEntry(treeId, &entry))
             return false;
         return true;
     }
 
-    tree[treeId - treeOffset].stop = timestamp;
+    tree[treeId - treeOffset].setStop(timestamp);
     return true;
 }
 
 void
 TraceLogger::startEvent(uint32_t id)
 {
-    if (!enabled)
+    if (failed)
         return;
 
     if (!stack.ensureSpaceBeforeAdd()) {
         fprintf(stderr, "TraceLogging: Failed to allocate space to keep track of the stack.\n");
         enabled = false;
         failed = true;
         return;
     }
 
+    if (!enabled) {
+        StackEntry &stackEntry = stack.pushUninitialized();
+        stackEntry.setTreeId(tree.currentId() + treeOffset);
+        stackEntry.setLastChildId(0);
+        stackEntry.setTextId(id);
+        stackEntry.setActive(false);
+        return;
+    }
+
     if (!tree.ensureSpaceBeforeAdd()) {
         uint64_t start = rdtsc() - traceLoggers.startupTime;
         if (!flush()) {
             fprintf(stderr, "TraceLogging: Couldn't write the data to disk.\n");
             enabled = false;
             failed = true;
             return;
         }
@@ -511,110 +632,109 @@ TraceLogger::startEvent(uint32_t id)
         return;
     }
 }
 
 TraceLogger::StackEntry &
 TraceLogger::getActiveAncestor()
 {
     uint32_t parentId = stack.currentId();
-    while (!stack[parentId].active)
+    while (!stack[parentId].active())
         parentId--;
     return stack[parentId];
 }
 
 bool
 TraceLogger::startEvent(uint32_t id, uint64_t timestamp)
 {
     // When a textId is disabled, a stack entry still needs to be pushed,
     // together with an annotation that nothing needs to get done when receiving
     // the stop event.
     if (!traceLoggers.isTextIdEnabled(id)) {
         StackEntry &stackEntry = stack.pushUninitialized();
-        stackEntry.active = false;
+        stackEntry.setActive(false);
         return true;
     }
 
     // Patch up the tree to be correct. There are two scenarios:
     // 1) Parent has no children yet. So update parent to include children.
     // 2) Parent has already children. Update last child to link to the new
     //    child.
     StackEntry &parent = getActiveAncestor();
 #ifdef DEBUG
     TreeEntry entry;
-    if (!getTreeEntry(parent.treeId, &entry))
+    if (!getTreeEntry(parent.treeId(), &entry))
         return false;
 #endif
 
-    if (parent.lastChildId == 0) {
-        JS_ASSERT(entry.u.s.hasChildren == 0);
-        JS_ASSERT(parent.treeId == tree.currentId() + treeOffset);
+    if (parent.lastChildId() == 0) {
+        MOZ_ASSERT(!entry.hasChildren());
+        MOZ_ASSERT(parent.treeId() == tree.currentId() + treeOffset);
 
-        if (!updateHasChildren(parent.treeId))
+        if (!updateHasChildren(parent.treeId()))
             return false;
     } else {
-        JS_ASSERT(entry.u.s.hasChildren == 1);
+        MOZ_ASSERT(entry.hasChildren());
 
-        if (!updateNextId(parent.lastChildId, tree.nextId() + treeOffset))
+        if (!updateNextId(parent.lastChildId(), tree.nextId() + treeOffset))
             return false;
     }
 
     // Add a new tree entry.
     TreeEntry &treeEntry = tree.pushUninitialized();
-    treeEntry.start = timestamp;
-    treeEntry.stop = 0;
-    treeEntry.u.s.textId = id;
-    treeEntry.u.s.hasChildren = false;
-    treeEntry.nextId = 0;
+    treeEntry.setStart(timestamp);
+    treeEntry.setStop(0);
+    treeEntry.setTextId(id);
+    treeEntry.setHasChildren(false);
+    treeEntry.setNextId(0);
 
     // Add a new stack entry.
     StackEntry &stackEntry = stack.pushUninitialized();
-    stackEntry.treeId = tree.currentId() + treeOffset;
-    stackEntry.lastChildId = 0;
-    stackEntry.active = true;
+    stackEntry.setTreeId(tree.currentId() + treeOffset);
+    stackEntry.setLastChildId(0);
+    stackEntry.setActive(true);
 
     // Set the last child of the parent to this newly added entry.
-    parent.lastChildId = tree.currentId() + treeOffset;
+    parent.setLastChildId(tree.currentId() + treeOffset);
 
     return true;
 }
 
 void
 TraceLogger::stopEvent(uint32_t id)
 {
 #ifdef DEBUG
     TreeEntry entry;
-    JS_ASSERT(getTreeEntry(stack.current().treeId, &entry));
-    JS_ASSERT(entry.u.s.textId == id);
+    MOZ_ASSERT_IF(stack.current().active(), getTreeEntry(stack.current().treeId(), &entry));
+    MOZ_ASSERT_IF(stack.current().active(), entry.textId() == id);
 #endif
     stopEvent();
 }
 
 void
 TraceLogger::stopEvent()
 {
-    if (!enabled)
-        return;
-
-    if (stack.current().active) {
+    if (enabled && stack.current().active()) {
         uint64_t stop = rdtsc() - traceLoggers.startupTime;
-        if (!updateStop(stack.current().treeId, stop)) {
+        if (!updateStop(stack.current().treeId(), stop)) {
             fprintf(stderr, "TraceLogging: Failed to stop an event.\n");
             enabled = false;
             failed = true;
             return;
         }
     }
     stack.pop();
 }
 
 TraceLogging::TraceLogging()
 {
     initialized = false;
     enabled = false;
+    mainThreadEnabled = true;
+    offThreadEnabled = true;
     loggerId = 0;
 
 #ifdef JS_THREADSAFE
     lock = PR_NewLock();
     if (!lock)
         MOZ_CRASH();
 #endif // JS_THREADSAFE
 }
@@ -677,17 +797,17 @@ TraceLogging::lazyInit()
 
 #ifdef JS_THREADSAFE
     if (!threadLoggers.init())
         return false;
 #endif // JS_THREADSAFE
 
     const char *env = getenv("TLLOG");
     if (!env)
-        return false;
+        env = "";
 
     if (strstr(env, "help")) {
         fflush(nullptr);
         printf(
             "\n"
             "usage: TLLOG=option,option,option,... where options can be:\n"
             "\n"
             "Collections:\n"
@@ -744,16 +864,38 @@ TraceLogging::lazyInit()
         enabledTextIds[TraceLogger::LICM] = true;
         enabledTextIds[TraceLogger::RangeAnalysis] = true;
         enabledTextIds[TraceLogger::EffectiveAddressAnalysis] = true;
         enabledTextIds[TraceLogger::EliminateDeadCode] = true;
         enabledTextIds[TraceLogger::EdgeCaseAnalysis] = true;
         enabledTextIds[TraceLogger::EliminateRedundantChecks] = true;
     }
 
+    const char *options = getenv("TLOPTIONS");
+    if (options) {
+        if (strstr(options, "help")) {
+            fflush(nullptr);
+            printf(
+                "\n"
+                "usage: TLOPTIONS=option,option,option,... where options can be:\n"
+                "\n"
+                "  DisableMainThread        Don't start logging the mainThread automatically.\n"
+                "  DisableOffThread         Don't start logging the off mainThread automatically.\n"
+            );
+            printf("\n");
+            exit(0);
+            /*NOTREACHED*/
+        }
+
+        if (strstr(options, "DisableMainThread"))
+           mainThreadEnabled = false;
+        if (strstr(options, "DisableOffThread"))
+           offThreadEnabled = false;
+    }
+
     startupTime = rdtsc();
     enabled = true;
     return true;
 }
 
 TraceLogger *
 js::TraceLoggerForMainThread(jit::CompileRuntime *runtime)
 {
@@ -787,16 +929,19 @@ TraceLogging::forMainThread(PerThreadDat
         if (!lazyInit())
             return nullptr;
 
         TraceLogger *logger = create();
         mainThread->traceLogger = logger;
 
         if (!mainThreadLoggers.append(logger))
             return nullptr;
+
+        if (!mainThreadEnabled)
+            logger->disable();
     }
 
     return mainThread->traceLogger;
 }
 
 TraceLogger *
 js::TraceLoggerForCurrentThread()
 {
@@ -825,16 +970,19 @@ TraceLogging::forThread(PRThread *thread
     if (!logger)
         return nullptr;
 
     if (!threadLoggers.add(p, thread, logger)) {
         delete logger;
         return nullptr;
     }
 
+    if (!offThreadEnabled)
+        logger->disable();
+
     return logger;
 }
 #endif // JS_THREADSAFE
 
 TraceLogger *
 TraceLogging::create()
 {
     if (loggerId > 999) {
@@ -862,8 +1010,14 @@ TraceLogging::create()
 
     if (!logger->init(loggerId)) {
         delete logger;
         return nullptr;
     }
 
     return logger;
 }
+
+bool
+js::TraceLogTextIdEnabled(uint32_t textId)
+{
+    return traceLoggers.isTextIdEnabled(textId);
+}
--- a/js/src/vm/TraceLogging.h
+++ b/js/src/vm/TraceLogging.h
@@ -147,29 +147,31 @@ class ContinuousSpace {
         return next_;
     }
 
     T &next() {
         return data()[next_];
     }
 
     uint32_t currentId() {
-        JS_ASSERT(next_ > 0);
+        MOZ_ASSERT(next_ > 0);
         return next_ - 1;
     }
 
     T &current() {
         return data()[currentId()];
     }
 
-    bool ensureSpaceBeforeAdd() {
-        if (next_ < capacity_)
+    bool ensureSpaceBeforeAdd(uint32_t count = 1) {
+        if (next_ + count <= capacity_)
             return true;
 
         uint32_t nCapacity = capacity_ * 2;
+        if (next_ + count > nCapacity)
+            nCapacity = next_ + count;
         T *entries = (T *) js_realloc(data_, nCapacity * sizeof(T));
 
         if (!entries)
             return false;
 
         data_ = entries;
         capacity_ = nCapacity;
 
@@ -182,21 +184,22 @@ class ContinuousSpace {
     }
 
     void push(T &data) {
         MOZ_ASSERT(next_ < capacity_);
         data()[next_++] = data;
     }
 
     T &pushUninitialized() {
+        MOZ_ASSERT(next_ < capacity_);
         return data()[next_++];
     }
 
     void pop() {
-        JS_ASSERT(next_ > 0);
+        MOZ_ASSERT(next_ > 0);
         next_--;
     }
 
     void clear() {
         next_ = 0;
     }
 };
 
@@ -222,16 +225,17 @@ class TraceLogger
       MinorGC,
       ParserCompileFunction,
       ParserCompileLazy,
       ParserCompileScript,
       TL,
       YarrCompile,
       YarrInterpret,
       YarrJIT,
+      VM,
 
       // Specific passes during ion compilation:
       SplitCriticalEdges,
       RenumberBlocks,
       DominatorTree,
       PhiAnalysis,
       ApplyTypes,
       ParallelSafetyAnalysis,
@@ -253,50 +257,112 @@ class TraceLogger
     typedef HashMap<const void *,
                     uint32_t,
                     PointerHasher<const void *, 3>,
                     SystemAllocPolicy> PointerHashMap;
 
     // The layout of the tree in memory and in the log file. Readable by JS
     // using TypedArrays.
     struct TreeEntry {
-        uint64_t start;
-        uint64_t stop;
+        uint64_t start_;
+        uint64_t stop_;
         union {
             struct {
-                uint32_t textId: 31;
-                uint32_t hasChildren: 1;
+                uint32_t textId_: 31;
+                uint32_t hasChildren_: 1;
             } s;
-            uint32_t value;
+            uint32_t value_;
         } u;
-        uint32_t nextId;
+        uint32_t nextId_;
 
         TreeEntry(uint64_t start, uint64_t stop, uint32_t textId, bool hasChildren,
                   uint32_t nextId)
         {
-            this->start = start;
-            this->stop = stop;
-            this->u.s.textId = textId;
-            this->u.s.hasChildren = hasChildren;
-            this->nextId = nextId;
+            start_ = start;
+            stop_ = stop;
+            u.s.textId_ = textId;
+            u.s.hasChildren_ = hasChildren;
+            nextId_ = nextId;
         }
         TreeEntry()
         { }
+        uint64_t start() {
+            return start_;
+        }
+        uint64_t stop() {
+            return stop_;
+        }
+        uint32_t textId() {
+            return u.s.textId_;
+        }
+        bool hasChildren() {
+            return u.s.hasChildren_;
+        }
+        uint32_t nextId() {
+            return nextId_;
+        }
+        void setStart(uint64_t start) {
+            start_ = start;
+        }
+        void setStop(uint64_t stop) {
+            stop_ = stop;
+        }
+        void setTextId(uint32_t textId) {
+            MOZ_ASSERT(textId < uint32_t(1<<31) );
+            u.s.textId_ = textId;
+        }
+        void setHasChildren(bool hasChildren) {
+            u.s.hasChildren_ = hasChildren;
+        }
+        void setNextId(uint32_t nextId) {
+            nextId_ = nextId;
+        }
     };
 
     // Helper structure for keeping track of the current entries in
     // the tree. Pushed by `start(id)`, popped by `stop(id)`. The active flag
     // is used to know if a subtree doesn't need to get logged.
     struct StackEntry {
-        uint32_t treeId;
-        uint32_t lastChildId;
-        bool active;
+        uint32_t treeId_;
+        uint32_t lastChildId_;
+        struct {
+            uint32_t textId_: 31;
+            uint32_t active_: 1;
+        } s;
         StackEntry(uint32_t treeId, uint32_t lastChildId, bool active = true)
-          : treeId(treeId), lastChildId(lastChildId), active(active)
-        { }
+          : treeId_(treeId), lastChildId_(lastChildId)
+        {
+            s.textId_ = 0;
+            s.active_ = active;
+        }
+        uint32_t treeId() {
+            return treeId_;
+        }
+        uint32_t lastChildId() {
+            return lastChildId_;
+        }
+        uint32_t textId() {
+            return s.textId_;
+        }
+        bool active() {
+            return s.active_;
+        }
+        void setTreeId(uint32_t treeId) {
+            treeId_ = treeId;
+        }
+        void setLastChildId(uint32_t lastChildId) {
+            lastChildId_ = lastChildId;
+        }
+        void setTextId(uint32_t textId) {
+            MOZ_ASSERT(textId < uint32_t(1<<31) );
+            s.textId_ = textId;
+        }
+        void setActive(bool active) {
+            s.active_ = active;
+        }
     };
 
     // The layout of the event log in memory and in the log file.
     // Readable by JS using TypedArrays.
     struct EventEntry {
         uint64_t time;
         uint32_t textId;
         EventEntry(uint64_t time, uint32_t textId)
@@ -304,16 +370,17 @@ class TraceLogger
         { }
     };
 
     FILE *dictFile;
     FILE *treeFile;
     FILE *eventFile;
 
     bool enabled;
+    uint32_t enabledTimes;
     bool failed;
     uint32_t nextTextId;
 
     PointerHashMap pointerMap;
 
     ContinuousSpace<TreeEntry> tree;
     ContinuousSpace<StackEntry> stack;
     ContinuousSpace<EventEntry> events;
@@ -350,16 +417,19 @@ class TraceLogger
     bool flush();
 
   public:
     TraceLogger();
     ~TraceLogger();
 
     bool init(uint32_t loggerId);
 
+    bool enable();
+    bool disable();
+
     // The createTextId functions map a unique input to a logger ID.
     // This ID can be used to log something. Calls to these functions should be
     // limited if possible, because of the overhead.
     uint32_t createTextId(const char *text);
     uint32_t createTextId(JSScript *script);
     uint32_t createTextId(const JS::ReadOnlyCompileOptions &script);
 
     // Log an event (no start/stop, only the timestamp is recorded).
@@ -391,16 +461,18 @@ class TraceLogging
                     PointerHasher<PRThread *, 3>,
                     SystemAllocPolicy> ThreadLoggerHashMap;
 #endif // JS_THREADSAFE
     typedef Vector<TraceLogger *, 1, js::SystemAllocPolicy > MainThreadLoggers;
 
     bool initialized;
     bool enabled;
     bool enabledTextIds[TraceLogger::LAST];
+    bool mainThreadEnabled;
+    bool offThreadEnabled;
 #ifdef JS_THREADSAFE
     ThreadLoggerHashMap threadLoggers;
 #endif // JS_THREADSAFE
     MainThreadLoggers mainThreadLoggers;
     uint32_t loggerId;
     FILE *out;
 
   public:
@@ -442,16 +514,31 @@ inline TraceLogger *TraceLoggerForMainTh
 inline TraceLogger *TraceLoggerForMainThread(jit::CompileRuntime *runtime) {
     return nullptr;
 };
 inline TraceLogger *TraceLoggerForCurrentThread() {
     return nullptr;
 };
 #endif
 
+inline bool TraceLoggerEnable(TraceLogger *logger) {
+#ifdef JS_TRACE_LOGGING
+    if (logger)
+        return logger->enable();
+#endif
+    return false;
+}
+inline bool TraceLoggerDisable(TraceLogger *logger) {
+#ifdef JS_TRACE_LOGGING
+    if (logger)
+        return logger->disable();
+#endif
+    return false;
+}
+
 inline uint32_t TraceLogCreateTextId(TraceLogger *logger, JSScript *script) {
 #ifdef JS_TRACE_LOGGING
     if (logger)
         return logger->createTextId(script);
 #endif
     return TraceLogger::TL_Error;
 }
 inline uint32_t TraceLogCreateTextId(TraceLogger *logger,
@@ -465,16 +552,23 @@ inline uint32_t TraceLogCreateTextId(Tra
 }
 inline uint32_t TraceLogCreateTextId(TraceLogger *logger, const char *text) {
 #ifdef JS_TRACE_LOGGING
     if (logger)
         return logger->createTextId(text);
 #endif
     return TraceLogger::TL_Error;
 }
+#ifdef JS_TRACE_LOGGING
+bool TraceLogTextIdEnabled(uint32_t textId);
+#else
+inline bool TraceLogTextIdEnabled(uint32_t textId) {
+    return false;
+}
+#endif
 inline void TraceLogTimestamp(TraceLogger *logger, uint32_t textId) {
 #ifdef JS_TRACE_LOGGING
     if (logger)
         logger->logTimestamp(textId);
 #endif
 }
 inline void TraceLogStartEvent(TraceLogger *logger, uint32_t textId) {
 #ifdef JS_TRACE_LOGGING
--- a/js/src/vm/make_opcode_doc.py
+++ b/js/src/vm/make_opcode_doc.py
@@ -280,17 +280,17 @@ def override(value, override_value):
 
 def format_flags(flags):
     if flags == '':
         return ''
 
     return ' ({flags})'.format(flags=flags)
 
 def print_opcode(opcode):
-    names_template = '{name} [-{nuses}, +{ndefs}] ({flags})'
+    names_template = '{name} [-{nuses}, +{ndefs}]{flags}'
     names = map(lambda code: names_template.format(name=escape(code.name),
                                                    nuses=override(code.nuses,
                                                                   opcode.nuses_override),
                                                    ndefs=override(code.ndefs,
                                                                   opcode.ndefs_override),
                                                    flags=format_flags(code.flags)),
                 sorted([opcode] + opcode.group,
                        key=lambda opcode: opcode.name))
--- a/js/xpconnect/src/XPCVariant.cpp
+++ b/js/xpconnect/src/XPCVariant.cpp
@@ -64,17 +64,17 @@ XPCTraceableVariant::~XPCTraceableVarian
         nsVariant::Cleanup(&mData);
 
     if (!JSVAL_IS_NULL(val))
         RemoveFromRootSet();
 }
 
 void XPCTraceableVariant::TraceJS(JSTracer* trc)
 {
-    MOZ_ASSERT(JSVAL_IS_TRACEABLE(mJSVal));
+    MOZ_ASSERT(mJSVal.isMarkable());
     JS_SET_TRACING_DETAILS(trc, GetTraceName, this, 0);
     JS_CallHeapValueTracer(trc, &mJSVal, "XPCTraceableVariant::mJSVal");
 }
 
 // static
 void
 XPCTraceableVariant::GetTraceName(JSTracer* trc, char *buf, size_t bufsize)
 {
@@ -110,17 +110,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(XP
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 // static
 already_AddRefed<XPCVariant>
 XPCVariant::newVariant(JSContext* cx, jsval aJSVal)
 {
     nsRefPtr<XPCVariant> variant;
 
-    if (!JSVAL_IS_TRACEABLE(aJSVal))
+    if (!aJSVal.isMarkable())
         variant = new XPCVariant(cx, aJSVal);
     else
         variant = new XPCTraceableVariant(cx, aJSVal);
 
     if (!variant->InitializeData(cx))
         return nullptr;
 
     return variant.forget();
--- a/layout/base/nsDisplayListInvalidation.cpp
+++ b/layout/base/nsDisplayListInvalidation.cpp
@@ -1,15 +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 "nsDisplayListInvalidation.h"
 #include "nsDisplayList.h"
+#include "nsIFrame.h"
 
 nsDisplayItemGeometry::nsDisplayItemGeometry(nsDisplayItem* aItem, nsDisplayListBuilder* aBuilder)
 {
   MOZ_COUNT_CTOR(nsDisplayItemGeometry);
   bool snap;
   mBounds = aItem->GetBounds(aBuilder, &snap);
 }
 
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -5382,18 +5382,20 @@ nsLayoutUtils::SurfaceFromElement(nsIIma
   uint32_t noRasterize = aSurfaceFlags & SFE_NO_RASTERIZING_VECTORS;
 
   uint32_t whichFrame = (aSurfaceFlags & SFE_WANT_FIRST_FRAME)
                         ? (uint32_t) imgIContainer::FRAME_FIRST
                         : (uint32_t) imgIContainer::FRAME_CURRENT;
   uint32_t frameFlags = imgIContainer::FLAG_SYNC_DECODE;
   if (aSurfaceFlags & SFE_NO_COLORSPACE_CONVERSION)
     frameFlags |= imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION;
-  if (aSurfaceFlags & SFE_NO_PREMULTIPLY_ALPHA)
+  if (aSurfaceFlags & SFE_PREFER_NO_PREMULTIPLY_ALPHA) {
     frameFlags |= imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA;
+    result.mIsPremultiplied = false;
+  }
 
   int32_t imgWidth, imgHeight;
   rv = imgContainer->GetWidth(&imgWidth);
   nsresult rv2 = imgContainer->GetHeight(&imgHeight);
   if (NS_FAILED(rv) || NS_FAILED(rv2))
     return result;
 
   if (!noRasterize || imgContainer->GetType() == imgIContainer::TYPE_RASTER) {
@@ -5445,62 +5447,38 @@ nsLayoutUtils::SurfaceFromElement(HTMLIm
 }
 
 nsLayoutUtils::SurfaceFromElementResult
 nsLayoutUtils::SurfaceFromElement(HTMLCanvasElement* aElement,
                                   uint32_t aSurfaceFlags,
                                   DrawTarget* aTarget)
 {
   SurfaceFromElementResult result;
-  nsresult rv;
-
-  bool premultAlpha = (aSurfaceFlags & SFE_NO_PREMULTIPLY_ALPHA) == 0;
+
+  bool* isPremultiplied = nullptr;
+  if (aSurfaceFlags & SFE_PREFER_NO_PREMULTIPLY_ALPHA) {
+    isPremultiplied = &result.mIsPremultiplied;
+  }
 
   gfxIntSize size = aElement->GetSize();
 
-  if (premultAlpha && aElement->CountContexts() == 1) {
-    nsICanvasRenderingContextInternal *srcCanvas = aElement->GetContextAtIndex(0);
-    result.mSourceSurface = srcCanvas->GetSurfaceSnapshot();
-  }
-
+  result.mSourceSurface = aElement->GetSurfaceSnapshot(isPremultiplied);
   if (!result.mSourceSurface) {
-    nsRefPtr<gfxContext> ctx;
-    RefPtr<DrawTarget> dt;
-    if (premultAlpha) {
-      if (aTarget) {
-        dt = aTarget->CreateSimilarDrawTarget(IntSize(size.width, size.height), SurfaceFormat::B8G8R8A8);
-      } else {
-        dt = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(IntSize(size.width, size.height),
-                                                                          SurfaceFormat::B8G8R8A8);
-      }
-      if (!dt) {
-        return result;
-      }
-      ctx = new gfxContext(dt);
-    } else {
-      // TODO: RenderContextsExternal expects to get a gfxImageFormat
-      // so that it can un-premultiply.
-      RefPtr<DataSourceSurface> data = Factory::CreateDataSourceSurface(IntSize(size.width, size.height),
-                                                                        SurfaceFormat::B8G8R8A8);
-      memset(data->GetData(), 0, data->Stride() * size.height);
-      result.mSourceSurface = data;
-      nsRefPtr<gfxImageSurface> image = new gfxImageSurface(data->GetData(),
-                                                            gfxIntSize(size.width, size.height),
-                                                            data->Stride(),
-                                                            gfxImageFormat::ARGB32);
-      ctx = new gfxContext(image);
-    }
-    // XXX shouldn't use the external interface, but maybe we can layerify this
-    uint32_t flags = premultAlpha ? HTMLCanvasElement::RenderFlagPremultAlpha : 0;
-    rv = aElement->RenderContextsExternal(ctx, GraphicsFilter::FILTER_NEAREST, flags);
-    if (NS_FAILED(rv))
-      return result;
-
-    if (premultAlpha) {
-      result.mSourceSurface = dt->Snapshot();
+     // If the element doesn't have a context then we won't get a snapshot. The canvas spec wants us to not error and just
+     // draw nothing, so return an empty surface.
+     DrawTarget *ref = aTarget ? aTarget : gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
+     RefPtr<DrawTarget> dt = ref->CreateSimilarDrawTarget(IntSize(size.width, size.height),
+                                                          SurfaceFormat::B8G8R8A8);
+     if (dt) {
+       result.mSourceSurface = dt->Snapshot();
+     }
+  } else if (aTarget) {
+    RefPtr<SourceSurface> opt = aTarget->OptimizeSourceSurface(result.mSourceSurface);
+    if (opt) {
+      result.mSourceSurface = opt;
     }
   }
 
   // Ensure that any future changes to the canvas trigger proper invalidation,
   // in case this is being used by -moz-element()
   aElement->MarkContextClean();
 
   result.mSize = size;
@@ -5512,17 +5490,17 @@ nsLayoutUtils::SurfaceFromElement(HTMLCa
 
 nsLayoutUtils::SurfaceFromElementResult
 nsLayoutUtils::SurfaceFromElement(HTMLVideoElement* aElement,
                                   uint32_t aSurfaceFlags,
                                   DrawTarget* aTarget)
 {
   SurfaceFromElementResult result;
 
-  NS_WARN_IF_FALSE((aSurfaceFlags & SFE_NO_PREMULTIPLY_ALPHA) == 0, "We can't support non-premultiplied alpha for video!");
+  NS_WARN_IF_FALSE((aSurfaceFlags & SFE_PREFER_NO_PREMULTIPLY_ALPHA) == 0, "We can't support non-premultiplied alpha for video!");
 
   uint16_t readyState;
   if (NS_SUCCEEDED(aElement->GetReadyState(&readyState)) &&
       (readyState == nsIDOMHTMLMediaElement::HAVE_NOTHING ||
        readyState == nsIDOMHTMLMediaElement::HAVE_METADATA)) {
     result.mIsStillLoading = true;
     return result;
   }
@@ -5536,16 +5514,23 @@ nsLayoutUtils::SurfaceFromElement(HTMLVi
   if (!container)
     return result;
 
   mozilla::gfx::IntSize size;
   result.mSourceSurface = container->GetCurrentAsSourceSurface(&size);
   if (!result.mSourceSurface)
     return result;
 
+  if (aTarget) {
+    RefPtr<SourceSurface> opt = aTarget->OptimizeSourceSurface(result.mSourceSurface);
+    if (opt) {
+      result.mSourceSurface = opt;
+    }
+  }
+
   result.mCORSUsed = aElement->GetCORSMode() != CORS_NONE;
   result.mSize = ThebesIntSize(size);
   result.mPrincipal = principal.forget();
   result.mIsWriteOnly = false;
 
   return result;
 }
 
@@ -6504,17 +6489,20 @@ nsLayoutUtils::WantSubAPZC()
      wantSubAPZC = false;
    }
 #endif
    return wantSubAPZC;
  }
 
 nsLayoutUtils::SurfaceFromElementResult::SurfaceFromElementResult()
   // Use safe default values here
-  : mIsWriteOnly(true), mIsStillLoading(false), mCORSUsed(false)
+  : mIsWriteOnly(true)
+  , mIsStillLoading(false)
+  , mCORSUsed(false)
+  , mIsPremultiplied(true)
 {
 }
 
 bool
 nsLayoutUtils::IsNonWrapperBlock(nsIFrame* aFrame)
 {
   return GetAsBlock(aFrame) && !aFrame->IsBlockWrapper();
 }
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -1690,20 +1690,20 @@ public:
   enum {
     /* When creating a new surface, create an image surface */
     SFE_WANT_IMAGE_SURFACE = 1 << 0,
     /* Whether to extract the first frame (as opposed to the
        current frame) in the case that the element is an image. */
     SFE_WANT_FIRST_FRAME = 1 << 1,
     /* Whether we should skip colorspace/gamma conversion */
     SFE_NO_COLORSPACE_CONVERSION = 1 << 2,
-    /* Whether we should skip premultiplication -- the resulting
-       image will always be an image surface, and must not be given to
-       Thebes for compositing