Merge m-c to b2g-inbound.
authorRyan VanderMeulen <ryanvm@gmail.com>
Thu, 17 Apr 2014 22:37:10 -0400
changeset 197690 c75f7df6645b9f55613786e789f344f29307ea86
parent 197689 c58b051bff0d88c77f4d4995241211618c96fefa (current diff)
parent 197672 7fe3ee0cf8be3f598d23d610618b1fee976a8fa7 (diff)
child 197691 7e19e4965f99f921480a79eaaf5303dad5674d82
push id3624
push userasasaki@mozilla.com
push dateMon, 09 Jun 2014 21:49:01 +0000
treeherdermozilla-beta@b1a5da15899a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone31.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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);