Merge b2ginbound to central, a=merge
authorWes Kocher <wkocher@mozilla.com>
Tue, 17 Nov 2015 13:21:48 -0800
changeset 272980 8c3a0b58fd51c02c8aca138b0da58445becb037f
parent 272974 eed903a7e4e70b0b66b4f016ae6146f0257c69e7 (diff)
parent 272979 4b5bd6832c52ca8e1639781ff21691670391aa10 (current diff)
child 272981 bae1769b53946d864adfd9bd6cfee492f9118d57
child 272986 93d3671347066a184b4325c84dfc2277000bfee8
child 272995 90941c814c3a77af9f41e8f202dcd9e45c8a3b21
child 273042 233f67378440e5495a636e6fffc02ec1f1eaffce
push id29689
push userkwierso@gmail.com
push dateTue, 17 Nov 2015 21:21:51 +0000
treeherdermozilla-central@8c3a0b58fd51 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone45.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 b2ginbound to central, a=merge
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1400,16 +1400,17 @@ pref("loop.feedback.baseUrl", "https://i
 pref("loop.feedback.product", "Loop");
 pref("loop.debug.loglevel", "Error");
 pref("loop.debug.dispatcher", false);
 pref("loop.debug.sdk", false);
 pref("loop.debug.twoWayMediaTelemetry", false);
 pref("loop.feedback.dateLastSeenSec", 0);
 pref("loop.feedback.periodSec", 15770000); // 6 months.
 pref("loop.feedback.formURL", "https://www.mozilla.org/firefox/hello/npssurvey/");
+pref("loop.feedback.manualFormURL", "https://www.mozilla.org/firefox/hello/feedbacksurvey/");
 #ifdef DEBUG
 pref("loop.CSP", "default-src 'self' about: file: chrome: http://localhost:*; img-src * data:; font-src 'none'; connect-src wss://*.tokbox.com https://*.opentok.com https://*.tokbox.com wss://*.mozilla.com https://*.mozilla.org wss://*.mozaws.net http://localhost:* ws://localhost:*; media-src blob:");
 #else
 pref("loop.CSP", "default-src 'self' about: file: chrome:; img-src * data:; font-src 'none'; connect-src wss://*.tokbox.com https://*.opentok.com https://*.tokbox.com wss://*.mozilla.com https://*.mozilla.org wss://*.mozaws.net; media-src blob:");
 #endif
 pref("loop.fxa_oauth.tokendata", "");
 pref("loop.fxa_oauth.profile", "");
 pref("loop.support_url", "https://support.mozilla.org/kb/group-conversations-firefox-hello-webrtc");
--- a/browser/components/loop/content/js/panel.js
+++ b/browser/components/loop/content/js/panel.js
@@ -225,17 +225,17 @@ loop.panel = (function(_, mozL10n) {
       this.hideDropdownMenu();
     },
 
     /**
      * Load on the browser the feedback url from prefs
      */
     handleSubmitFeedback: function(event) {
       event.preventDefault();
-      var helloFeedbackUrl = this.props.mozLoop.getLoopPref("feedback.formURL");
+      var helloFeedbackUrl = this.props.mozLoop.getLoopPref("feedback.manualFormURL");
       this.props.mozLoop.openURL(helloFeedbackUrl);
       this.closeWindow();
     },
 
     _isSignedIn: function() {
       return !!this.props.mozLoop.userProfile;
     },
 
--- a/browser/components/loop/content/js/panel.jsx
+++ b/browser/components/loop/content/js/panel.jsx
@@ -225,17 +225,17 @@ loop.panel = (function(_, mozL10n) {
       this.hideDropdownMenu();
     },
 
     /**
      * Load on the browser the feedback url from prefs
      */
     handleSubmitFeedback: function(event) {
       event.preventDefault();
-      var helloFeedbackUrl = this.props.mozLoop.getLoopPref("feedback.formURL");
+      var helloFeedbackUrl = this.props.mozLoop.getLoopPref("feedback.manualFormURL");
       this.props.mozLoop.openURL(helloFeedbackUrl);
       this.closeWindow();
     },
 
     _isSignedIn: function() {
       return !!this.props.mozLoop.userProfile;
     },
 
--- a/browser/components/loop/test/desktop-local/panel_test.js
+++ b/browser/components/loop/test/desktop-local/panel_test.js
@@ -488,17 +488,17 @@ describe("loop.panel", function() {
           React.createElement(loop.panel.SettingsDropdown, {
             mozLoop: fakeMozLoop
           }));
       }
 
       beforeEach(function() {
         feedbackUrl = "https://example.com";
         fakeMozLoop.getLoopPref = function(pref) {
-          if (pref === "feedback.formURL") {
+          if (pref === "feedback.manualFormURL") {
             return feedbackUrl;
           }
 
           return "unseen";
         };
       });
 
       it("should open a tab to the feedback page", function() {
--- a/browser/components/preferences/in-content/search.js
+++ b/browser/components/preferences/in-content/search.js
@@ -90,25 +90,18 @@ var gSearchPane = {
     let permanentPBLabel =
       document.getElementById("urlBarSuggestionPermanentPBLabel");
     permanentPBLabel.hidden = urlbarSuggests.hidden || !permanentPB;
   },
 
   buildDefaultEngineDropDown: function() {
     // This is called each time something affects the list of engines.
     let list = document.getElementById("defaultEngine");
-    let currentEngine;
-
-    // First, try to preserve the current selection.
-    if (list.selectedItem)
-      currentEngine = list.selectedItem.label;
-
-    // If there's no current selection, use the current default engine.
-    if (!currentEngine)
-      currentEngine = Services.search.currentEngine.name;
+    // Set selection to the current default engine.
+    let currentEngine = Services.search.currentEngine.name;
 
     // If the current engine isn't in the list any more, select the first item.
     let engines = gEngineView._engineStore._engines;
     if (!engines.some(e => e.name == currentEngine))
       currentEngine = engines[0].name;
 
     // Now clean-up and rebuild the list.
     list.removeAllItems();
@@ -406,16 +399,17 @@ EngineStore.prototype = {
 
         this._engines.splice(i, 0, e);
         let engine = e.originalEngine;
         engine.hidden = false;
         Services.search.moveEngine(engine, i);
         added++;
       }
     }
+    Services.search.resetToOriginalDefaultEngine();
     gSearchPane.showRestoreDefaults(false);
     gSearchPane.buildDefaultEngineDropDown();
     return added;
   },
 
   changeEngine: function ES_changeEngine(aEngine, aProp, aNewValue) {
     var index = this._getIndexForEngine(aEngine);
     if (index == -1)
--- a/chrome/nsChromeRegistry.cpp
+++ b/chrome/nsChromeRegistry.cpp
@@ -396,82 +396,74 @@ nsresult nsChromeRegistry::RefreshWindow
   nsCOMPtr<nsIDocument> document = aWindow->GetDoc();
   if (!document)
     return NS_OK;
 
   // Deal with the agent sheets first.  Have to do all the style sets by hand.
   nsCOMPtr<nsIPresShell> shell = document->GetShell();
   if (shell) {
     // Reload only the chrome URL agent style sheets.
-    nsCOMArray<nsIStyleSheet> agentSheets;
+    nsTArray<RefPtr<CSSStyleSheet>> agentSheets;
     rv = shell->GetAgentStyleSheets(agentSheets);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    nsCOMArray<nsIStyleSheet> newAgentSheets;
-    for (int32_t l = 0; l < agentSheets.Count(); ++l) {
-      nsIStyleSheet *sheet = agentSheets[l];
-
+    nsTArray<RefPtr<CSSStyleSheet>> newAgentSheets;
+    for (CSSStyleSheet* sheet : agentSheets) {
       nsIURI* uri = sheet->GetSheetURI();
 
       if (IsChromeURI(uri)) {
         // Reload the sheet.
         RefPtr<CSSStyleSheet> newSheet;
         rv = document->LoadChromeSheetSync(uri, true,
                                            getter_AddRefs(newSheet));
         if (NS_FAILED(rv)) return rv;
         if (newSheet) {
-          rv = newAgentSheets.AppendObject(newSheet) ? NS_OK : NS_ERROR_FAILURE;
+          rv = newAgentSheets.AppendElement(newSheet) ? NS_OK : NS_ERROR_FAILURE;
           if (NS_FAILED(rv)) return rv;
         }
       }
       else {  // Just use the same sheet.
-        rv = newAgentSheets.AppendObject(sheet) ? NS_OK : NS_ERROR_FAILURE;
+        rv = newAgentSheets.AppendElement(sheet) ? NS_OK : NS_ERROR_FAILURE;
         if (NS_FAILED(rv)) return rv;
       }
     }
 
     rv = shell->SetAgentStyleSheets(newAgentSheets);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
-  // Build an array of nsIURIs of style sheets we need to load.
-  nsCOMArray<nsIStyleSheet> oldSheets;
-  nsCOMArray<nsIStyleSheet> newSheets;
+  int32_t count = document->GetNumberOfStyleSheets();
 
-  int32_t count = document->GetNumberOfStyleSheets();
+  // Build an array of style sheets we need to reload.
+  nsTArray<RefPtr<CSSStyleSheet>> oldSheets(count);
+  nsTArray<RefPtr<CSSStyleSheet>> newSheets(count);
 
   // Iterate over the style sheets.
-  int32_t i;
-  for (i = 0; i < count; i++) {
+  for (int32_t i = 0; i < count; i++) {
     // Get the style sheet
-    nsIStyleSheet *styleSheet = document->GetStyleSheetAt(i);
-
-    if (!oldSheets.AppendObject(styleSheet)) {
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
+    CSSStyleSheet* styleSheet = document->GetStyleSheetAt(i);
+    oldSheets.AppendElement(styleSheet);
   }
 
   // Iterate over our old sheets and kick off a sync load of the new
   // sheet if and only if it's a chrome URL.
-  for (i = 0; i < count; i++) {
-    RefPtr<CSSStyleSheet> sheet = do_QueryObject(oldSheets[i]);
+  for (CSSStyleSheet* sheet : oldSheets) {
     nsIURI* uri = sheet ? sheet->GetOriginalURI() : nullptr;
 
     if (uri && IsChromeURI(uri)) {
       // Reload the sheet.
       RefPtr<CSSStyleSheet> newSheet;
       // XXX what about chrome sheets that have a title or are disabled?  This
       // only works by sheer dumb luck.
       document->LoadChromeSheetSync(uri, false, getter_AddRefs(newSheet));
       // Even if it's null, we put in in there.
-      newSheets.AppendObject(newSheet);
-    }
-    else {
+      newSheets.AppendElement(newSheet);
+    } else {
       // Just use the same sheet.
-      newSheets.AppendObject(sheet);
+      newSheets.AppendElement(sheet);
     }
   }
 
   // Now notify the document that multiple sheets have been added and removed.
   document->UpdateStyleSheets(oldSheets, newSheets);
   return NS_OK;
 }
 
--- a/devtools/client/jar.mn
+++ b/devtools/client/jar.mn
@@ -327,8 +327,9 @@ devtools.jar:
     skin/tooltip/arrow-horizontal-dark.png (themes/tooltip/arrow-horizontal-dark.png)
     skin/tooltip/arrow-horizontal-dark@2x.png (themes/tooltip/arrow-horizontal-dark@2x.png)
     skin/tooltip/arrow-vertical-dark.png (themes/tooltip/arrow-vertical-dark.png)
     skin/tooltip/arrow-vertical-dark@2x.png (themes/tooltip/arrow-vertical-dark@2x.png)
     skin/tooltip/arrow-horizontal-light.png (themes/tooltip/arrow-horizontal-light.png)
     skin/tooltip/arrow-horizontal-light@2x.png (themes/tooltip/arrow-horizontal-light@2x.png)
     skin/tooltip/arrow-vertical-light.png (themes/tooltip/arrow-vertical-light.png)
     skin/tooltip/arrow-vertical-light@2x.png (themes/tooltip/arrow-vertical-light@2x.png)
+    skin/images/reload.svg (themes/images/reload.svg)
--- a/devtools/client/locales/en-US/webide.dtd
+++ b/devtools/client/locales/en-US/webide.dtd
@@ -84,16 +84,17 @@
 <!ENTITY projectPanel_tabs "Tabs">
 <!ENTITY runtimePanel_usb "USB Devices">
 <!ENTITY runtimePanel_wifi "Wi-Fi Devices">
 <!ENTITY runtimePanel_simulator "Simulators">
 <!ENTITY runtimePanel_other "Other">
 <!ENTITY runtimePanel_installsimulator "Install Simulator">
 <!ENTITY runtimePanel_noadbhelper "Install ADB Helper">
 <!ENTITY runtimePanel_nousbdevice "Can't see your device?">
+<!ENTITY runtimePanel_refreshDevices_label "Refresh Devices">
 
 <!-- Lense -->
 <!ENTITY details_valid_header "valid">
 <!ENTITY details_warning_header "warnings">
 <!ENTITY details_error_header "errors">
 <!ENTITY details_description "Description">
 <!ENTITY details_location "Location">
 <!ENTITY details_manifestURL "App ID">
--- a/devtools/client/themes/images/animation-fast-track.svg
+++ b/devtools/client/themes/images/animation-fast-track.svg
@@ -1,9 +1,6 @@
 <!-- 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/. -->
-
-<svg xmlns="http://www.w3.org/2000/svg" width="9" height="12">
-  <g transform="matrix(1.0251088,0,0,0.85613344,-3.1546734,-888.94343)">
-    <path d="m 5.1284819,1038.3667 6.4950901,0 -2.7147491,4.6651 2.9438561,0 -8.1148915,9.3081 1.6126718,-6.8973 -2.2701022,0 z" style="fill:white;"/>
-  </g>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 9 12" width="16" height="16">
+  <path d="M5.75 0l-1 5.5 2 .5-3.5 6 1-5-2-.5z" fill="#fff"/>
 </svg>
new file mode 100644
--- /dev/null
+++ b/devtools/client/themes/images/reload.svg
@@ -0,0 +1,6 @@
+<!-- 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/. -->
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14 14" width="14" height="14">
+  <path d="M12,7H6l2.4-2.4C7.6,4,6.6,3.8,5.5,4.1C4.3,4.5,3.3,5.5,3,6.8 C2.6,9,4.3,11,6.5,11c1,0,2-0.5,2.6-1.2l1.7,1c-1.3,1.6-3.3,2.5-5.6,2c-2-0.5-3.6-2.1-4-4.1C0.4,5.1,3.1,2,6.5,2 c1.3,0,2.4,0.4,3.3,1.2L12,1V7z"/>
+</svg>
--- a/devtools/client/webide/content/project-listing.xhtml
+++ b/devtools/client/webide/content/project-listing.xhtml
@@ -21,15 +21,15 @@
         <button class="panel-item project-panel-item-newapp" id="new-app">&projectMenu_newApp_label;</button>
         <button class="panel-item project-panel-item-openpackaged" id="packaged-app">&projectMenu_importPackagedApp_label;</button>
         <button class="panel-item project-panel-item-openhosted" id="hosted-app">&projectMenu_importHostedApp_label;</button>
         <label class="panel-header">&projectPanel_myProjects;</label>
         <div id="project-panel-projects"></div>
         <label class="panel-header" id="panel-header-runtimeapps" hidden="true">&projectPanel_runtimeApps;</label>
         <div id="project-panel-runtimeapps"/>
         <label class="panel-header" id="panel-header-tabs" hidden="true">&projectPanel_tabs;
-          <button class="panel-item project-panel-item-refreshtabs" id="refresh-tabs">&projectMenu_refreshTabs_label;</button>
+          <button class="project-panel-item-refreshtabs refresh-icon" id="refresh-tabs" title="&projectMenu_refreshTabs_label;"></button>
         </label>
         <div id="project-panel-tabs"/>
       </div>
     </div>
   </body>
 </html>
--- a/devtools/client/webide/content/runtime-listing.js
+++ b/devtools/client/webide/content/runtime-listing.js
@@ -14,16 +14,17 @@ window.addEventListener("load", function
   document.getElementById("runtime-permissions").onclick = ShowPermissionsTable;
   document.getElementById("runtime-details").onclick = ShowRuntimeDetails;
   document.getElementById("runtime-disconnect").onclick = DisconnectRuntime;
   document.getElementById("runtime-preferences").onclick = ShowDevicePreferences;
   document.getElementById("runtime-settings").onclick = ShowSettings;
   document.getElementById("runtime-panel-installsimulator").onclick = ShowAddons;
   document.getElementById("runtime-panel-noadbhelper").onclick = ShowAddons;
   document.getElementById("runtime-panel-nousbdevice").onclick = ShowTroubleShooting;
+  document.getElementById("refresh-devices").onclick = RefreshScanners;
   runtimeList.update();
   runtimeList.updateCommands();
 }, true);
 
 window.addEventListener("unload", function onUnload() {
   window.removeEventListener("unload", onUnload);
   runtimeList.destroy();
 });
@@ -43,16 +44,20 @@ function ShowPermissionsTable() {
 function ShowDevicePreferences() {
   runtimeList.showDevicePreferences();
 }
 
 function ShowSettings() {
   runtimeList.showSettings();
 }
 
+function RefreshScanners() {
+  runtimeList.refreshScanners();
+}
+
 function DisconnectRuntime() {
   window.parent.Cmds.disconnectRuntime();
 }
 
 function ShowAddons() {
   runtimeList.showAddons();
 }
 
--- a/devtools/client/webide/content/runtime-listing.xhtml
+++ b/devtools/client/webide/content/runtime-listing.xhtml
@@ -13,17 +13,19 @@
   <head>
     <meta charset="utf8"/>
     <link rel="stylesheet" href="chrome://webide/skin/panel-listing.css" type="text/css"/>
     <script type="application/javascript;version=1.8" src="chrome://webide/content/runtime-listing.js"></script>
   </head>
   <body>
     <div id="runtime-panel">
       <div id="runtime-panel-box">
-        <label class="panel-header">&runtimePanel_usb;</label>
+        <label class="panel-header">&runtimePanel_usb;
+          <button class="runtime-panel-item-refreshdevices refresh-icon" id="refresh-devices" title="&runtimePanel_refreshDevices_label;"></button>
+        </label>
         <button class="panel-item" id="runtime-panel-nousbdevice">&runtimePanel_nousbdevice;</button>
         <button class="panel-item" id="runtime-panel-noadbhelper">&runtimePanel_noadbhelper;</button>
         <div id="runtime-panel-usb"></div>
         <label class="panel-header" id="runtime-header-wifi">&runtimePanel_wifi;</label>
         <div id="runtime-panel-wifi"></div>
         <label class="panel-header">&runtimePanel_simulator;</label>
         <div id="runtime-panel-simulator"></div>
         <button class="panel-item" id="runtime-panel-installsimulator">&runtimePanel_installsimulator;</button>
--- a/devtools/client/webide/modules/project-list.js
+++ b/devtools/client/webide/modules/project-list.js
@@ -153,21 +153,17 @@ ProjectList.prototype = {
 
     if (!AppManager.connected) {
       tabsHeaderNode.setAttribute("hidden", "true");
       return;
     }
 
     let tabs = AppManager.tabStore.tabs;
 
-    if (tabs.length > 0) {
-      tabsHeaderNode.removeAttribute("hidden");
-    } else {
-      tabsHeaderNode.setAttribute("hidden", "true");
-    }
+    tabsHeaderNode.removeAttribute("hidden");
 
     for (let i = 0; i < tabs.length; i++) {
       let tab = tabs[i];
       let URL = this._parentWindow.URL;
       let url;
       try {
         url = new URL(tab.url);
       } catch (e) {
--- a/devtools/client/webide/modules/runtime-list.js
+++ b/devtools/client/webide/modules/runtime-list.js
@@ -81,16 +81,20 @@ RuntimeList.prototype = {
   showTroubleShooting: function() {
     this._Cmds.showTroubleShooting();
   },
 
   showAddons: function() {
     this._Cmds.showAddons();
   },
 
+  refreshScanners: function() {
+    RuntimeScanners.scan();
+  },
+
   updateCommands: function() {
     let doc = this._doc;
 
     // Runtime commands
     let screenshotCmd = doc.querySelector("#runtime-screenshot");
     let permissionsCmd = doc.querySelector("#runtime-permissions");
     let detailsCmd = doc.querySelector("#runtime-details");
     let disconnectCmd = doc.querySelector("#runtime-disconnect");
--- a/devtools/client/webide/test/test_duplicate_import.html
+++ b/devtools/client/webide/test/test_duplicate_import.html
@@ -49,18 +49,18 @@
           yield winProject.projectList.importHostedApp(hostedAppManifest);
           yield waitForUpdate(win, "project-validated");
           project = win.AppManager.selectedProject;
           is(project.location, hostedAppManifest, "Correctly reselected existing hosted app.");
           yield nextTick();
 
           let panelNode = docProject.querySelector("#project-panel");
           let items = panelNode.querySelectorAll(".panel-item");
-          // 4 controls, + 2 projects
-          is(items.length, 6, "6 projects in panel");
+          // 3 controls, + 2 projects
+          is(items.length, 5, "5 projects in panel");
           is(items[3].querySelector("span").textContent, "A name (in app directory)", "Panel text is correct");
           is(items[4].querySelector("span").textContent, "hosted manifest name property", "Panel text is correct");
 
           yield closeWebIDE(win);
 
           yield removeAllProjects();
 
           SimpleTest.finish();
--- a/devtools/client/webide/test/test_import.html
+++ b/devtools/client/webide/test/test_import.html
@@ -55,17 +55,17 @@
           yield waitForUpdate(win, "project-validated");
 
           project = win.AppManager.selectedProject;
           ok(project.location.endsWith('manifest.webapp'), "The manifest was found and the project was updated");
 
           let panelNode = docProject.querySelector("#project-panel");
           let items = panelNode.querySelectorAll(".panel-item");
           // 4 controls, + 2 projects
-          is(items.length, 7, "7 projects in panel");
+          is(items.length, 6, "6 projects in panel");
           is(items[3].querySelector("span").textContent, "A name (in app directory)", "Panel text is correct");
           is(items[4].querySelector("span").textContent, "hosted manifest name property", "Panel text is correct");
 
           yield closeWebIDE(win);
 
           yield removeAllProjects();
 
           SimpleTest.finish();
--- a/devtools/client/webide/themes/panel-listing.css
+++ b/devtools/client/webide/themes/panel-listing.css
@@ -86,31 +86,29 @@ button.panel-item {
 
 .panel-item:disabled {
   background-color: #FFF;
   color: #5A5A5A;
   opacity: 0.5;
   cursor: default;
 }
 
-#refresh-tabs {
-  background-color: #FFF;
-  border-top: 1px solid #EDEDED;
+.refresh-icon {
+  background-image: url("chrome://devtools/skin/images/reload.svg");
+  height: 14px;
+  width: 14px;
+  border: 0;
+  opacity: 0.6;
   display: inline-block;
+  margin: 3px;
   float: right;
-  padding: 3px;
-  text-transform: none;
-  border-right: 1px solid #CCC;
-  width: auto;
-  margin: 0 4px 5px 5px;
 }
 
 .panel-item:not(:disabled):hover,
-button.panel-item:not(:disabled):hover,
-#refresh-tabs:hover {
+button.panel-item:not(:disabled):hover {
   background-color: #CCF0FD;
   border-top: 1px solid #EDEDED;
 }
 
 .configure-button {
   display: inline-block;
   height: 30px;
   width: 30px;
--- a/devtools/client/webide/themes/webide.css
+++ b/devtools/client/webide/themes/webide.css
@@ -92,17 +92,17 @@ window.busy-determined #action-busy-unde
 
 #action-button-debug[active="true"] { -moz-image-region: rect(100px,300px,200px,200px) }
 
 /* Panels */
 
 .panel-list {
   display: none;
   position: relative;
-  max-width: 180px;
+  max-width: 190px;
   overflow: hidden;
 }
 
 #project-listing-panel {
   max-width: 165px;
 }
 
 .panel-list-wrapper {
--- a/devtools/server/actors/storage.js
+++ b/devtools/server/actors/storage.js
@@ -2,27 +2,27 @@
  * 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 {Cc, Ci} = require("chrome");
 const events = require("sdk/event/core");
 const protocol = require("devtools/server/protocol");
-const {async} = require("devtools/shared/async-utils");
 const {Arg, method, RetVal, types} = protocol;
 const {LongStringActor} = require("devtools/server/actors/string");
 const {DebuggerServer} = require("devtools/server/main");
 const Services = require("Services");
 const promise = require("promise");
 const {isWindowIncluded} = require("devtools/shared/layout/utils");
-const { setTimeout, clearTimeout } = require("sdk/timers");
+const {setTimeout, clearTimeout} = require("sdk/timers");
 
 loader.lazyImporter(this, "OS", "resource://gre/modules/osfile.jsm");
 loader.lazyImporter(this, "Sqlite", "resource://gre/modules/Sqlite.jsm");
+loader.lazyImporter(this, "Task", "resource://gre/modules/Task.jsm", "Task");
 
 var gTrackedMessageManager = new Map();
 
 // Maximum number of cookies/local storage key-value-pairs that can be sent
 // over the wire to the client in one request.
 const MAX_STORE_OBJECT_COUNT = 50;
 // Delay for the batch job that sends the accumulated update packets to the
 // client (ms).
@@ -240,17 +240,17 @@ StorageActors.defaults = function(typeNa
 
     /**
      * When a new window is added to the page. This generally means that a new
      * iframe is created, or the current window is completely reloaded.
      *
      * @param {window} window
      *        The window which was added.
      */
-    onWindowReady: async(function*(window) {
+    onWindowReady: Task.async(function*(window) {
       let host = this.getHostName(window.location);
       if (!this.hostVsStores.has(host)) {
         yield this.populateStoresForHost(host, window);
         let data = {};
         data[host] = this.getNamesForHost(host);
         this.storageActor.update("added", typeName, data);
       }
     }),
@@ -324,17 +324,17 @@ StorageActors.defaults = function(typeNa
      *
      * @return {object} An object containing following properties:
      *          - offset - The actual offset of the returned array. This might
      *                     be different from the requested offset if that was
      *                     invalid
      *          - total - The total number of entries possible.
      *          - data - The requested values.
      */
-    getStoreObjects: method(async(function*(host, names, options = {}) {
+    getStoreObjects: method(Task.async(function*(host, names, options = {}) {
       let offset = options.offset || 0;
       let size = options.size || MAX_STORE_OBJECT_COUNT;
       if (size > MAX_STORE_OBJECT_COUNT) {
         size = MAX_STORE_OBJECT_COUNT;
       }
       let sortOn = options.sortOn || "name";
 
       let toReturn = {
@@ -1138,25 +1138,25 @@ StorageActors.createActor({
   },
 
   /**
    * Purpose of this method is same as populateStoresForHosts but this is async.
    * This exact same operation cannot be performed in populateStoresForHosts
    * method, as that method is called in initialize method of the actor, which
    * cannot be asynchronous.
    */
-  preListStores: async(function*() {
+  preListStores: Task.async(function*() {
     this.hostVsStores = new Map();
 
     for (let host of this.hosts) {
       yield this.populateStoresForHost(host);
     }
   }),
 
-  populateStoresForHost: async(function*(host) {
+  populateStoresForHost: Task.async(function*(host) {
     let storeMap = new Map();
     let {names} = yield this.getDBNamesForHost(host);
 
     for (let name of names) {
       let metadata = yield this.getDBMetaData(host, name);
 
       indexedDBHelpers.patchMetadataMapsAndProtos(metadata);
       storeMap.set(name, metadata);
@@ -1287,17 +1287,17 @@ var indexedDBHelpers = {
     });
   },
 
   /**
    * Fetches and stores all the metadata information for the given database
    * `name` for the given `host`. The stored metadata information is of
    * `DatabaseMetadata` type.
    */
-  getDBMetaData: async(function*(host, name) {
+  getDBMetaData: Task.async(function*(host, name) {
     let request = this.openWithOrigin(host, name);
     let success = promise.defer();
 
     request.onsuccess = event => {
       let db = event.target.result;
 
       let dbData = new DatabaseMetadata(host, db);
       db.close();
@@ -1328,17 +1328,17 @@ var indexedDBHelpers = {
     }
 
     return require("indexedDB").openForPrincipal(principal, name);
   },
 
     /**
    * Fetches all the databases and their metadata for the given `host`.
    */
-  getDBNamesForHost: async(function*(host) {
+  getDBNamesForHost: Task.async(function*(host) {
     let sanitizedHost = this.getSanitizedHost(host);
     let directory = OS.Path.join(OS.Constants.Path.profileDir, "storage",
                                  "default", sanitizedHost, "idb");
 
     let exists = yield OS.File.exists(directory);
     if (!exists && host.startsWith("about:")) {
       // try for moz-safe-about directory
       sanitizedHost = this.getSanitizedHost("moz-safe-" + host);
@@ -1384,17 +1384,17 @@ var indexedDBHelpers = {
   getSanitizedHost: function(host) {
     return host.replace(ILLEGAL_CHAR_REGEX, "+");
   },
 
   /**
    * Retrieves the proper indexed db database name from the provided .sqlite
    * file location.
    */
-  getNameFromDatabaseFile: async(function*(path) {
+  getNameFromDatabaseFile: Task.async(function*(path) {
     let connection = null;
     let retryCount = 0;
 
     // Content pages might be having an open transaction for the same indexed db
     // which this sqlite file belongs to. In that case, sqlite.openConnection
     // will throw. Thus we retey for some time to see if lock is removed.
     while (!connection && retryCount++ < 25) {
       try {
@@ -1417,17 +1417,17 @@ var indexedDBHelpers = {
     let name = rows[0].getResultByName("name");
 
     yield connection.close();
 
     return name;
   }),
 
   getValuesForHost:
-  async(function*(host, name = "null", options, hostVsStores) {
+  Task.async(function*(host, name = "null", options, hostVsStores) {
     name = JSON.parse(name);
     if (!name || !name.length) {
       // This means that details about the db in this particular host are
       // requested.
       let dbs = [];
       if (hostVsStores.has(host)) {
         for (let [, db] of hostVsStores.get(host)) {
           indexedDBHelpers.patchMetadataMapsAndProtos(db);
@@ -1819,17 +1819,17 @@ var StorageActor = exports.StorageActor 
    * Lists the available hosts for all the registered storage types.
    *
    * @returns {object} An object containing with the following structure:
    *  - <storageType> : [{
    *      actor: <actorId>,
    *      host: <hostname>
    *    }]
    */
-  listStores: method(async(function*() {
+  listStores: method(Task.async(function*() {
     let toReturn = {};
 
     for (let [name, value] of this.childActorPool) {
       if (value.preListStores) {
         yield value.preListStores();
       }
       toReturn[name] = value;
     }
--- a/devtools/shared/async-utils.js
+++ b/devtools/shared/async-utils.js
@@ -13,30 +13,16 @@
  * See Task documentation at https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/Task.jsm.
  */
 
 var {Cu} = require("chrome");
 var {Task} = require("resource://gre/modules/Task.jsm");
 var Promise = require("promise");
 
 /**
- * Create an async function from a generator function.
- *
- * @param Function func
- *        The generator function that to wrap as an async function.
- * @return Function
- *         The async function.
- */
-exports.async = function async(func) {
-  return function(...args) {
-    return Task.spawn(func.apply(this, args));
-  };
-};
-
-/**
  * Create an async function that only executes once per instance of an object.
  * Once called on a given object, the same promise will be returned for any
  * future calls for that object.
  *
  * @param Function func
  *        The generator function that to wrap as an async function.
  * @return Function
  *         The async function.
--- a/devtools/shared/heapsnapshot/HeapSnapshot.cpp
+++ b/devtools/shared/heapsnapshot/HeapSnapshot.cpp
@@ -1336,17 +1336,16 @@ getCoreDumpOutputStream(ErrorResult& rv,
 
 namespace dom {
 
 using namespace JS;
 using namespace devtools;
 
 /* static */ void
 ThreadSafeChromeUtils::SaveHeapSnapshot(GlobalObject& global,
-                                        JSContext* cx,
                                         const HeapSnapshotBoundaries& boundaries,
                                         nsAString& outFilePath,
                                         ErrorResult& rv)
 {
   auto start = TimeStamp::Now();
 
   bool wantNames = true;
   ZoneSet zones;
@@ -1355,16 +1354,17 @@ ThreadSafeChromeUtils::SaveHeapSnapshot(
 
   nsCOMPtr<nsIOutputStream> outputStream = getCoreDumpOutputStream(rv, start, outFilePath);
   if (NS_WARN_IF(rv.Failed()))
     return;
 
   ZeroCopyNSIOutputStream zeroCopyStream(outputStream);
   ::google::protobuf::io::GzipOutputStream gzipStream(&zeroCopyStream);
 
+  JSContext* cx = global.Context();
   StreamWriter writer(cx, gzipStream, wantNames);
   if (NS_WARN_IF(!writer.init())) {
     rv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return;
   }
 
   {
     Maybe<AutoCheckCannotGC> maybeNoGC;
@@ -1400,17 +1400,16 @@ ThreadSafeChromeUtils::SaveHeapSnapshot(
   Telemetry::Accumulate(Telemetry::DEVTOOLS_HEAP_SNAPSHOT_NODE_COUNT,
                         nodeCount);
   Telemetry::Accumulate(Telemetry::DEVTOOLS_HEAP_SNAPSHOT_EDGE_COUNT,
                         edgeCount);
 }
 
 /* static */ already_AddRefed<HeapSnapshot>
 ThreadSafeChromeUtils::ReadHeapSnapshot(GlobalObject& global,
-                                        JSContext* cx,
                                         const nsAString& filePath,
                                         ErrorResult& rv)
 {
   auto start = TimeStamp::Now();
 
   UniquePtr<char[]> path(ToNewCString(filePath));
   if (!path) {
     rv.Throw(NS_ERROR_OUT_OF_MEMORY);
@@ -1418,17 +1417,18 @@ ThreadSafeChromeUtils::ReadHeapSnapshot(
   }
 
   AutoMemMap mm;
   rv = mm.init(path.get());
   if (rv.Failed())
     return nullptr;
 
   RefPtr<HeapSnapshot> snapshot = HeapSnapshot::Create(
-      cx, global, reinterpret_cast<const uint8_t*>(mm.address()), mm.size(), rv);
+      global.Context(), global, reinterpret_cast<const uint8_t*>(mm.address()),
+      mm.size(), rv);
 
   if (!rv.Failed())
     Telemetry::AccumulateTimeDelta(Telemetry::DEVTOOLS_READ_HEAP_SNAPSHOT_MS,
                                    start);
 
   return snapshot.forget();
 }
 
--- a/devtools/shared/tests/unit/test_async-utils.js
+++ b/devtools/shared/tests/unit/test_async-utils.js
@@ -7,26 +7,25 @@
 const {Task} = Cu.import("resource://gre/modules/Task.jsm", {});
 // |const| will not work because
 // it will make the Promise object immutable before assigning.
 // Using Object.defineProperty() instead.
 Object.defineProperty(this, "Promise", {
   value: require("promise"),
   writable: false, configurable: false
 });
-const {async, asyncOnce, promiseInvoke, promiseCall} = require("devtools/shared/async-utils");
+const {asyncOnce, promiseInvoke, promiseCall} = require("devtools/shared/async-utils");
 
 function run_test() {
   do_test_pending();
   Task.spawn(function*() {
-    for (let helper of [async, asyncOnce]) {
-      yield test_async_args(helper);
-      yield test_async_return(helper);
-      yield test_async_throw(helper);
-    }
+    yield test_async_args(asyncOnce);
+    yield test_async_return(asyncOnce);
+    yield test_async_throw(asyncOnce);
+
     yield test_async_once();
     yield test_async_invoke();
     do_test_finished();
   }).then(null, error => {
     do_throw(error);
   });
 }
 
--- a/dom/base/ChromeUtils.h
+++ b/dom/base/ChromeUtils.h
@@ -20,24 +20,22 @@ class HeapSnapshot;
 
 namespace dom {
 
 class ThreadSafeChromeUtils
 {
 public:
   // Implemented in devtools/shared/heapsnapshot/HeapSnapshot.cpp
   static void SaveHeapSnapshot(GlobalObject& global,
-                               JSContext* cx,
                                const HeapSnapshotBoundaries& boundaries,
                                nsAString& filePath,
                                ErrorResult& rv);
 
   // Implemented in devtools/shared/heapsnapshot/HeapSnapshot.cpp
   static already_AddRefed<devtools::HeapSnapshot> ReadHeapSnapshot(GlobalObject& global,
-                                                                   JSContext* cx,
                                                                    const nsAString& filePath,
                                                                    ErrorResult& rv);
 
   static void NondeterministicGetWeakMapKeys(GlobalObject& aGlobal,
                                              JS::Handle<JS::Value> aMap,
                                              JS::MutableHandle<JS::Value> aRetval,
                                              ErrorResult& aRv);
 
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -94,17 +94,16 @@
 #include "nsROCSSPrimitiveValue.h"
 #include "nsIBaseWindow.h"
 #include "nsIDocShellTreeOwner.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "GeckoProfiler.h"
 #include "mozilla/Preferences.h"
 #include "nsIContentIterator.h"
 #include "nsIDOMStyleSheet.h"
-#include "nsIStyleSheet.h"
 #include "nsIStyleSheetService.h"
 #include "nsContentPermissionHelper.h"
 #include "nsNetUtil.h"
 #include "nsDocument.h"
 #include "HTMLImageElement.h"
 #include "mozilla/css/ImageLoader.h"
 
 #ifdef XP_WIN
@@ -3239,17 +3238,17 @@ nsDOMWindowUtils::AddSheet(nsIDOMStyleSh
   NS_ENSURE_ARG(aSheetType == AGENT_SHEET ||
                 aSheetType == USER_SHEET ||
                 aSheetType == AUTHOR_SHEET);
 
   nsCOMPtr<nsIDocument> doc = GetDocument();
   NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
 
   nsIDocument::additionalSheetType type = convertSheetType(aSheetType);
-  nsCOMPtr<nsIStyleSheet> sheet = do_QueryInterface(aSheet);
+  RefPtr<CSSStyleSheet> sheet = do_QueryObject(aSheet);
   NS_ENSURE_TRUE(sheet, NS_ERROR_FAILURE);
   if (sheet->GetOwningDocument()) {
     return NS_ERROR_INVALID_ARG;
   }
   return doc->AddAdditionalStyleSheet(type, sheet);
 }
 
 NS_IMETHODIMP
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -720,73 +720,58 @@ nsDOMStyleSheetList::Length()
     return 0;
   }
 
   // XXX Find the number and then cache it. We'll use the
   // observer notification to figure out if new ones have
   // been added or removed.
   if (-1 == mLength) {
     mLength = mDocument->GetNumberOfStyleSheets();
-
-#ifdef DEBUG
-    int32_t i;
-    for (i = 0; i < mLength; i++) {
-      nsIStyleSheet *sheet = mDocument->GetStyleSheetAt(i);
-      nsCOMPtr<nsIDOMStyleSheet> domss(do_QueryInterface(sheet));
-      NS_ASSERTION(domss, "All \"normal\" sheets implement nsIDOMStyleSheet");
-    }
-#endif
   }
   return mLength;
 }
 
 CSSStyleSheet*
 nsDOMStyleSheetList::IndexedGetter(uint32_t aIndex, bool& aFound)
 {
   if (!mDocument || aIndex >= (uint32_t)mDocument->GetNumberOfStyleSheets()) {
     aFound = false;
     return nullptr;
   }
 
   aFound = true;
-  nsIStyleSheet *sheet = mDocument->GetStyleSheetAt(aIndex);
+  CSSStyleSheet* sheet = mDocument->GetStyleSheetAt(aIndex);
   NS_ASSERTION(sheet, "Must have a sheet");
 
   return static_cast<CSSStyleSheet*>(sheet);
 }
 
 void
 nsDOMStyleSheetList::NodeWillBeDestroyed(const nsINode *aNode)
 {
   mDocument = nullptr;
 }
 
 void
 nsDOMStyleSheetList::StyleSheetAdded(nsIDocument *aDocument,
-                                     nsIStyleSheet* aStyleSheet,
+                                     CSSStyleSheet* aStyleSheet,
                                      bool aDocumentSheet)
 {
   if (aDocumentSheet && -1 != mLength) {
-    nsCOMPtr<nsIDOMStyleSheet> domss(do_QueryInterface(aStyleSheet));
-    if (domss) {
-      mLength++;
-    }
+    mLength++;
   }
 }
 
 void
 nsDOMStyleSheetList::StyleSheetRemoved(nsIDocument *aDocument,
-                                       nsIStyleSheet* aStyleSheet,
+                                       CSSStyleSheet* aStyleSheet,
                                        bool aDocumentSheet)
 {
   if (aDocumentSheet && -1 != mLength) {
-    nsCOMPtr<nsIDOMStyleSheet> domss(do_QueryInterface(aStyleSheet));
-    if (domss) {
-      mLength--;
-    }
+    mLength--;
   }
 }
 
 // nsOnloadBlocker implementation
 NS_IMPL_ISUPPORTS(nsOnloadBlocker, nsIRequest)
 
 NS_IMETHODIMP
 nsOnloadBlocker::GetName(nsACString &aResult)
@@ -1342,17 +1327,17 @@ nsDOMStyleSheetSetList::EnsureFresh()
   if (!mDocument) {
     return; // Spec says "no exceptions", and we have no style sets if we have
             // no document, for sure
   }
 
   int32_t count = mDocument->GetNumberOfStyleSheets();
   nsAutoString title;
   for (int32_t index = 0; index < count; index++) {
-    nsIStyleSheet* sheet = mDocument->GetStyleSheetAt(index);
+    CSSStyleSheet* sheet = mDocument->GetStyleSheetAt(index);
     NS_ASSERTION(sheet, "Null sheet in sheet list!");
     sheet->GetTitle(title);
     if (!title.IsEmpty() && !mNames.Contains(title) && !Add(title)) {
       return;
     }
   }
 }
 
@@ -1619,29 +1604,26 @@ nsDocument::~nsDocument()
   mSubDocuments = nullptr;
 
   // Destroy link map now so we don't waste time removing
   // links one by one
   DestroyElementMaps();
 
   nsAutoScriptBlocker scriptBlocker;
 
-  int32_t indx; // must be signed
-  uint32_t count = mChildren.ChildCount();
-  for (indx = int32_t(count) - 1; indx >= 0; --indx) {
+  for (uint32_t indx = mChildren.ChildCount(); indx-- != 0; ) {
     mChildren.ChildAt(indx)->UnbindFromTree();
     mChildren.RemoveChildAt(indx);
   }
   mFirstChild = nullptr;
   mCachedRootElement = nullptr;
 
   // Let the stylesheets know we're going away
-  indx = mStyleSheets.Count();
-  while (--indx >= 0) {
-    mStyleSheets[indx]->SetOwningDocument(nullptr);
+  for (CSSStyleSheet* sheet : mStyleSheets) {
+    sheet->SetOwningDocument(nullptr);
   }
   if (mAttrStyleSheet) {
     mAttrStyleSheet->SetOwningDocument(nullptr);
   }
   // We don't own the mOnDemandBuiltInUASheets, so we don't need to reset them.
 
   if (mListenerManager) {
     mListenerManager->Disconnect();
@@ -2291,50 +2273,46 @@ nsDocument::ResetToURI(nsIURI *aURI, nsI
     win->RefreshCompartmentPrincipal();
   }
 }
 
 void
 nsDocument::RemoveDocStyleSheetsFromStyleSets()
 {
   // The stylesheets should forget us
-  int32_t indx = mStyleSheets.Count();
-  while (--indx >= 0) {
-    nsIStyleSheet* sheet = mStyleSheets[indx];
+  for (CSSStyleSheet* sheet : Reversed(mStyleSheets)) {
     sheet->SetOwningDocument(nullptr);
 
     if (sheet->IsApplicable()) {
       nsCOMPtr<nsIPresShell> shell = GetShell();
       if (shell) {
         shell->StyleSet()->RemoveDocStyleSheet(sheet);
       }
     }
     // XXX Tell observers?
   }
 }
 
 void
-nsDocument::RemoveStyleSheetsFromStyleSets(nsCOMArray<nsIStyleSheet>& aSheets, SheetType aType)
+nsDocument::RemoveStyleSheetsFromStyleSets(
+    nsTArray<RefPtr<CSSStyleSheet>>& aSheets,
+    SheetType aType)
 {
   // The stylesheets should forget us
-  int32_t indx = aSheets.Count();
-  while (--indx >= 0) {
-    nsIStyleSheet* sheet = aSheets[indx];
+  for (CSSStyleSheet* sheet : Reversed(aSheets)) {
     sheet->SetOwningDocument(nullptr);
 
     if (sheet->IsApplicable()) {
       nsCOMPtr<nsIPresShell> shell = GetShell();
       if (shell) {
         shell->StyleSet()->RemoveStyleSheet(aType, sheet);
       }
     }
-
     // XXX Tell observers?
   }
-
 }
 
 void
 nsDocument::ResetStylesheetsToURI(nsIURI* aURI)
 {
   MOZ_ASSERT(aURI);
 
   mozAutoDocUpdate upd(this, UPDATE_STYLE, true);
@@ -2374,59 +2352,49 @@ nsDocument::ResetStylesheetsToURI(nsIURI
 
   // Now set up our style sets
   nsCOMPtr<nsIPresShell> shell = GetShell();
   if (shell) {
     FillStyleSet(shell->StyleSet());
   }
 }
 
-static bool
-AppendAuthorSheet(nsIStyleSheet *aSheet, void *aData)
-{
-  nsStyleSet *styleSet = static_cast<nsStyleSet*>(aData);
-  styleSet->AppendStyleSheet(SheetType::Doc, aSheet);
-  return true;
-}
-
 static void
 AppendSheetsToStyleSet(nsStyleSet* aStyleSet,
-                       const nsCOMArray<nsIStyleSheet>& aSheets,
+                       const nsTArray<RefPtr<CSSStyleSheet>>& aSheets,
                        SheetType aType)
 {
-  for (int32_t i = aSheets.Count() - 1; i >= 0; --i) {
-    aStyleSet->AppendStyleSheet(aType, aSheets[i]);
+  for (CSSStyleSheet* sheet : Reversed(aSheets)) {
+    aStyleSet->AppendStyleSheet(aType, sheet);
   }
 }
 
 
 void
 nsDocument::FillStyleSet(nsStyleSet* aStyleSet)
 {
   NS_PRECONDITION(aStyleSet, "Must have a style set");
   NS_PRECONDITION(aStyleSet->SheetCount(SheetType::Doc) == 0,
                   "Style set already has document sheets?");
 
-  int32_t i;
-  for (i = mStyleSheets.Count() - 1; i >= 0; --i) {
-    nsIStyleSheet* sheet = mStyleSheets[i];
+  for (CSSStyleSheet* sheet : Reversed(mStyleSheets)) {
     if (sheet->IsApplicable()) {
       aStyleSet->AddDocStyleSheet(sheet, this);
     }
   }
 
   nsStyleSheetService *sheetService = nsStyleSheetService::GetInstance();
   if (sheetService) {
-    sheetService->AuthorStyleSheets()->EnumerateForwards(AppendAuthorSheet,
-                                                         aStyleSet);
+    for (CSSStyleSheet* sheet : *sheetService->AuthorStyleSheets()) {
+      aStyleSet->AppendStyleSheet(SheetType::Doc, sheet);
+    }
   }
 
   // Iterate backwards to maintain order
-  for (i = mOnDemandBuiltInUASheets.Count() - 1; i >= 0; --i) {
-    nsIStyleSheet* sheet = mOnDemandBuiltInUASheets[i];
+  for (CSSStyleSheet* sheet : Reversed(mOnDemandBuiltInUASheets)) {
     if (sheet->IsApplicable()) {
       aStyleSet->PrependStyleSheet(SheetType::Agent, sheet);
     }
   }
 
   AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eAgentSheet],
                          SheetType::Agent);
   AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eUserSheet],
@@ -4012,30 +3980,28 @@ nsDocument::RemoveChildAt(uint32_t aInde
 
   doRemoveChildAt(aIndex, aNotify, oldKid, mChildren);
   mCachedRootElement = nullptr;
 }
 
 void
 nsDocument::EnsureOnDemandBuiltInUASheet(CSSStyleSheet* aSheet)
 {
-  // Contains() takes nsISupport*, so annoyingly we have to cast here
-  if (mOnDemandBuiltInUASheets.Contains(static_cast<nsIStyleSheet*>(aSheet))) {
+  if (mOnDemandBuiltInUASheets.Contains(aSheet)) {
     return;
   }
   BeginUpdate(UPDATE_STYLE);
   AddOnDemandBuiltInUASheet(aSheet);
   EndUpdate(UPDATE_STYLE);
 }
 
 void
 nsDocument::AddOnDemandBuiltInUASheet(CSSStyleSheet* aSheet)
 {
-  // Contains() takes nsISupport*, so annoyingly we have to cast here
-  MOZ_ASSERT(!mOnDemandBuiltInUASheets.Contains(static_cast<nsIStyleSheet*>(aSheet)));
+  MOZ_ASSERT(!mOnDemandBuiltInUASheets.Contains(aSheet));
 
   // Prepend here so that we store the sheets in mOnDemandBuiltInUASheets in
   // the same order that they should end up in the style set.
   mOnDemandBuiltInUASheets.InsertElementAt(0, aSheet);
 
   if (aSheet->IsApplicable()) {
     // This is like |AddStyleSheetToStyleSets|, but for an agent sheet.
     nsCOMPtr<nsIPresShell> shell = GetShell();
@@ -4049,196 +4015,191 @@ nsDocument::AddOnDemandBuiltInUASheet(CS
   }
 
   NotifyStyleSheetAdded(aSheet, false);
 }
 
 int32_t
 nsDocument::GetNumberOfStyleSheets() const
 {
-  return mStyleSheets.Count();
-}
-
-nsIStyleSheet*
+  return mStyleSheets.Length();
+}
+
+CSSStyleSheet*
 nsDocument::GetStyleSheetAt(int32_t aIndex) const
 {
-  NS_ENSURE_TRUE(0 <= aIndex && aIndex < mStyleSheets.Count(), nullptr);
-  return mStyleSheets[aIndex];
+  return mStyleSheets.SafeElementAt(aIndex, nullptr);
 }
 
 int32_t
-nsDocument::GetIndexOfStyleSheet(nsIStyleSheet* aSheet) const
+nsDocument::GetIndexOfStyleSheet(CSSStyleSheet* aSheet) const
 {
   return mStyleSheets.IndexOf(aSheet);
 }
 
 void
-nsDocument::AddStyleSheetToStyleSets(nsIStyleSheet* aSheet)
+nsDocument::AddStyleSheetToStyleSets(CSSStyleSheet* aSheet)
 {
   nsCOMPtr<nsIPresShell> shell = GetShell();
   if (shell) {
     shell->StyleSet()->AddDocStyleSheet(aSheet, this);
   }
 }
 
 #define DO_STYLESHEET_NOTIFICATION(className, type, memberName, argName)      \
   do {                                                                        \
-    RefPtr<CSSStyleSheet> cssSheet = do_QueryObject(aSheet);                \
-    if (!cssSheet) {                                                          \
-      return;                                                                 \
-    }                                                                         \
-                                                                              \
     className##Init init;                                                     \
     init.mBubbles = true;                                                     \
     init.mCancelable = true;                                                  \
-    init.mStylesheet = cssSheet;                                              \
+    init.mStylesheet = aSheet;                                                \
     init.memberName = argName;                                                \
                                                                               \
     RefPtr<className> event =                                               \
       className::Constructor(this, NS_LITERAL_STRING(type), init);            \
     event->SetTrusted(true);                                                  \
     event->SetTarget(this);                                                   \
     RefPtr<AsyncEventDispatcher> asyncDispatcher =                          \
       new AsyncEventDispatcher(this, event);                                  \
     asyncDispatcher->mOnlyChromeDispatch = true;                              \
     asyncDispatcher->PostDOMEvent();                                          \
   } while (0);
 
 void
-nsDocument::NotifyStyleSheetAdded(nsIStyleSheet* aSheet, bool aDocumentSheet)
+nsDocument::NotifyStyleSheetAdded(CSSStyleSheet* aSheet, bool aDocumentSheet)
 {
   NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetAdded, (this, aSheet, aDocumentSheet));
 
   if (StyleSheetChangeEventsEnabled()) {
     DO_STYLESHEET_NOTIFICATION(StyleSheetChangeEvent,
                                "StyleSheetAdded",
                                mDocumentSheet,
                                aDocumentSheet);
   }
 }
 
 void
-nsDocument::NotifyStyleSheetRemoved(nsIStyleSheet* aSheet, bool aDocumentSheet)
+nsDocument::NotifyStyleSheetRemoved(CSSStyleSheet* aSheet, bool aDocumentSheet)
 {
   NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetRemoved, (this, aSheet, aDocumentSheet));
 
   if (StyleSheetChangeEventsEnabled()) {
     DO_STYLESHEET_NOTIFICATION(StyleSheetChangeEvent,
                                "StyleSheetRemoved",
                                mDocumentSheet,
                                aDocumentSheet);
   }
 }
 
 void
-nsDocument::AddStyleSheet(nsIStyleSheet* aSheet)
+nsDocument::AddStyleSheet(CSSStyleSheet* aSheet)
 {
   NS_PRECONDITION(aSheet, "null arg");
-  mStyleSheets.AppendObject(aSheet);
+  mStyleSheets.AppendElement(aSheet);
   aSheet->SetOwningDocument(this);
 
   if (aSheet->IsApplicable()) {
     AddStyleSheetToStyleSets(aSheet);
   }
 
   NotifyStyleSheetAdded(aSheet, true);
 }
 
 void
-nsDocument::RemoveStyleSheetFromStyleSets(nsIStyleSheet* aSheet)
+nsDocument::RemoveStyleSheetFromStyleSets(CSSStyleSheet* aSheet)
 {
   nsCOMPtr<nsIPresShell> shell = GetShell();
   if (shell) {
     shell->StyleSet()->RemoveDocStyleSheet(aSheet);
   }
 }
 
 void
-nsDocument::RemoveStyleSheet(nsIStyleSheet* aSheet)
+nsDocument::RemoveStyleSheet(CSSStyleSheet* aSheet)
 {
   NS_PRECONDITION(aSheet, "null arg");
-  nsCOMPtr<nsIStyleSheet> sheet = aSheet; // hold ref so it won't die too soon
-
-  if (!mStyleSheets.RemoveObject(aSheet)) {
+  RefPtr<CSSStyleSheet> sheet = aSheet; // hold ref so it won't die too soon
+
+  if (!mStyleSheets.RemoveElement(aSheet)) {
     NS_ASSERTION(mInUnlinkOrDeletion, "stylesheet not found");
     return;
   }
 
   if (!mIsGoingAway) {
     if (aSheet->IsApplicable()) {
       RemoveStyleSheetFromStyleSets(aSheet);
     }
 
     NotifyStyleSheetRemoved(aSheet, true);
   }
 
   aSheet->SetOwningDocument(nullptr);
 }
 
 void
-nsDocument::UpdateStyleSheets(nsCOMArray<nsIStyleSheet>& aOldSheets,
-                              nsCOMArray<nsIStyleSheet>& aNewSheets)
+nsDocument::UpdateStyleSheets(nsTArray<RefPtr<CSSStyleSheet>>& aOldSheets,
+                              nsTArray<RefPtr<CSSStyleSheet>>& aNewSheets)
 {
   BeginUpdate(UPDATE_STYLE);
 
   // XXX Need to set the sheet on the ownernode, if any
-  NS_PRECONDITION(aOldSheets.Count() == aNewSheets.Count(),
+  NS_PRECONDITION(aOldSheets.Length() == aNewSheets.Length(),
                   "The lists must be the same length!");
-  int32_t count = aOldSheets.Count();
-
-  nsCOMPtr<nsIStyleSheet> oldSheet;
+  int32_t count = aOldSheets.Length();
+
+  RefPtr<CSSStyleSheet> oldSheet;
   int32_t i;
   for (i = 0; i < count; ++i) {
     oldSheet = aOldSheets[i];
 
     // First remove the old sheet.
     NS_ASSERTION(oldSheet, "None of the old sheets should be null");
     int32_t oldIndex = mStyleSheets.IndexOf(oldSheet);
     RemoveStyleSheet(oldSheet);  // This does the right notifications
 
     // Now put the new one in its place.  If it's null, just ignore it.
-    nsIStyleSheet* newSheet = aNewSheets[i];
+    CSSStyleSheet* newSheet = aNewSheets[i];
     if (newSheet) {
-      mStyleSheets.InsertObjectAt(newSheet, oldIndex);
+      mStyleSheets.InsertElementAt(oldIndex, newSheet);
       newSheet->SetOwningDocument(this);
       if (newSheet->IsApplicable()) {
         AddStyleSheetToStyleSets(newSheet);
       }
 
       NotifyStyleSheetAdded(newSheet, true);
     }
   }
 
   EndUpdate(UPDATE_STYLE);
 }
 
 void
-nsDocument::InsertStyleSheetAt(nsIStyleSheet* aSheet, int32_t aIndex)
+nsDocument::InsertStyleSheetAt(CSSStyleSheet* aSheet, int32_t aIndex)
 {
   NS_PRECONDITION(aSheet, "null ptr");
-  mStyleSheets.InsertObjectAt(aSheet, aIndex);
+
+  mStyleSheets.InsertElementAt(aIndex, aSheet);
 
   aSheet->SetOwningDocument(this);
 
   if (aSheet->IsApplicable()) {
     AddStyleSheetToStyleSets(aSheet);
   }
 
   NotifyStyleSheetAdded(aSheet, true);
 }
 
 
 void
-nsDocument::SetStyleSheetApplicableState(nsIStyleSheet* aSheet,
+nsDocument::SetStyleSheetApplicableState(CSSStyleSheet* aSheet,
                                          bool aApplicable)
 {
   NS_PRECONDITION(aSheet, "null arg");
 
   // If we're actually in the document style sheet list
-  if (-1 != mStyleSheets.IndexOf(aSheet)) {
+  if (mStyleSheets.IndexOf(aSheet) != mStyleSheets.NoIndex) {
     if (aApplicable) {
       AddStyleSheetToStyleSets(aSheet);
     } else {
       RemoveStyleSheetFromStyleSets(aSheet);
     }
   }
 
   // We have to always notify, since this will be called for sheets
@@ -4289,19 +4250,19 @@ ConvertAdditionalSheetType(nsIDocument::
     default:
       MOZ_ASSERT(false, "wrong type");
       // we must return something although this should never happen
       return SheetType::Count;
   }
 }
 
 static int32_t
-FindSheet(const nsCOMArray<nsIStyleSheet>& aSheets, nsIURI* aSheetURI)
-{
-  for (int32_t i = aSheets.Count() - 1; i >= 0; i-- ) {
+FindSheet(const nsTArray<RefPtr<CSSStyleSheet>>& aSheets, nsIURI* aSheetURI)
+{
+  for (int32_t i = aSheets.Length() - 1; i >= 0; i-- ) {
     bool bEqual;
     nsIURI* uri = aSheets[i]->GetSheetURI();
 
     if (uri && NS_SUCCEEDED(uri->Equals(aSheetURI, &bEqual)) && bEqual)
       return i;
   }
 
   return -1;
@@ -4345,25 +4306,25 @@ nsDocument::LoadAdditionalStyleSheet(add
 
   sheet->SetOwningDocument(this);
   MOZ_ASSERT(sheet->IsApplicable());
 
   return AddAdditionalStyleSheet(aType, sheet);
 }
 
 nsresult
-nsDocument::AddAdditionalStyleSheet(additionalSheetType aType, nsIStyleSheet* aSheet)
+nsDocument::AddAdditionalStyleSheet(additionalSheetType aType, CSSStyleSheet* aSheet)
 {
   if (mAdditionalSheets[aType].Contains(aSheet))
     return NS_ERROR_INVALID_ARG;
 
   if (!aSheet->IsApplicable())
     return NS_ERROR_INVALID_ARG;
 
-  mAdditionalSheets[aType].AppendObject(aSheet);
+  mAdditionalSheets[aType].AppendElement(aSheet);
 
   BeginUpdate(UPDATE_STYLE);
   nsCOMPtr<nsIPresShell> shell = GetShell();
   if (shell) {
     SheetType type = ConvertAdditionalSheetType(aType);
     shell->StyleSet()->AppendStyleSheet(type, aSheet);
   }
 
@@ -4374,22 +4335,22 @@ nsDocument::AddAdditionalStyleSheet(addi
   return NS_OK;
 }
 
 void
 nsDocument::RemoveAdditionalStyleSheet(additionalSheetType aType, nsIURI* aSheetURI)
 {
   MOZ_ASSERT(aSheetURI);
 
-  nsCOMArray<nsIStyleSheet>& sheets = mAdditionalSheets[aType];
+  nsTArray<RefPtr<CSSStyleSheet>>& sheets = mAdditionalSheets[aType];
 
   int32_t i = FindSheet(mAdditionalSheets[aType], aSheetURI);
   if (i >= 0) {
-    nsCOMPtr<nsIStyleSheet> sheetRef = sheets[i];
-    sheets.RemoveObjectAt(i);
+    RefPtr<CSSStyleSheet> sheetRef = sheets[i];
+    sheets.RemoveElementAt(i);
 
     BeginUpdate(UPDATE_STYLE);
     if (!mIsGoingAway) {
       MOZ_ASSERT(sheetRef->IsApplicable());
       nsCOMPtr<nsIPresShell> shell = GetShell();
       if (shell) {
         SheetType type = ConvertAdditionalSheetType(aType);
         shell->StyleSet()->RemoveStyleSheet(type, sheetRef);
@@ -4400,20 +4361,20 @@ nsDocument::RemoveAdditionalStyleSheet(a
     // these additional sheets.
     NotifyStyleSheetRemoved(sheetRef, false);
     EndUpdate(UPDATE_STYLE);
 
     sheetRef->SetOwningDocument(nullptr);
   }
 }
 
-nsIStyleSheet*
+CSSStyleSheet*
 nsDocument::FirstAdditionalAuthorSheet()
 {
-  return mAdditionalSheets[eAuthorSheet].SafeObjectAt(0);
+  return mAdditionalSheets[eAuthorSheet].SafeElementAt(0, nullptr);
 }
 
 nsIGlobalObject*
 nsDocument::GetScopeObject() const
 {
   nsCOMPtr<nsIGlobalObject> scope(do_QueryReferent(mScopeObject));
   return scope;
 }
@@ -5150,17 +5111,17 @@ nsDocument::DocumentStatesChanged(EventS
   // Invalidate our cached state.
   mGotDocumentState &= ~aStateMask;
   mDocumentState &= ~aStateMask;
 
   NS_DOCUMENT_NOTIFY_OBSERVERS(DocumentStatesChanged, (this, aStateMask));
 }
 
 void
-nsDocument::StyleRuleChanged(nsIStyleSheet* aSheet,
+nsDocument::StyleRuleChanged(CSSStyleSheet* aSheet,
                              css::Rule* aOldStyleRule,
                              css::Rule* aNewStyleRule)
 {
   NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleChanged,
                                (this, aSheet,
                                 aOldStyleRule, aNewStyleRule));
 
   if (StyleSheetChangeEventsEnabled()) {
@@ -5168,33 +5129,33 @@ nsDocument::StyleRuleChanged(nsIStyleShe
                                "StyleRuleChanged",
                                mRule,
                                aNewStyleRule ? aNewStyleRule->GetDOMRule()
                                              : nullptr);
   }
 }
 
 void
-nsDocument::StyleRuleAdded(nsIStyleSheet* aSheet,
+nsDocument::StyleRuleAdded(CSSStyleSheet* aSheet,
                            css::Rule* aStyleRule)
 {
   NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleAdded,
                                (this, aSheet, aStyleRule));
 
   if (StyleSheetChangeEventsEnabled()) {
     DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent,
                                "StyleRuleAdded",
                                mRule,
                                aStyleRule ? aStyleRule->GetDOMRule()
                                           : nullptr);
   }
 }
 
 void
-nsDocument::StyleRuleRemoved(nsIStyleSheet* aSheet,
+nsDocument::StyleRuleRemoved(CSSStyleSheet* aSheet,
                              css::Rule* aStyleRule)
 {
   NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleRemoved,
                                (this, aSheet, aStyleRule));
 
   if (StyleSheetChangeEventsEnabled()) {
     DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent,
                                "StyleRuleRemoved",
@@ -6423,23 +6384,21 @@ void
 nsIDocument::GetSelectedStyleSheetSet(nsAString& aSheetSet)
 {
   aSheetSet.Truncate();
 
   // Look through our sheets, find the selected set title
   int32_t count = GetNumberOfStyleSheets();
   nsAutoString title;
   for (int32_t index = 0; index < count; index++) {
-    nsIStyleSheet* sheet = GetStyleSheetAt(index);
+    CSSStyleSheet* sheet = GetStyleSheetAt(index);
     NS_ASSERTION(sheet, "Null sheet in sheet list!");
 
-    nsCOMPtr<nsIDOMStyleSheet> domSheet = do_QueryInterface(sheet);
-    NS_ASSERTION(domSheet, "Sheet must QI to nsIDOMStyleSheet");
     bool disabled;
-    domSheet->GetDisabled(&disabled);
+    sheet->GetDisabled(&disabled);
     if (disabled) {
       // Disabled sheets don't affect the currently selected set
       continue;
     }
 
     sheet->GetTitle(title);
 
     if (aSheetSet.IsEmpty()) {
@@ -6539,17 +6498,17 @@ nsDocument::EnableStyleSheetsForSet(cons
 void
 nsDocument::EnableStyleSheetsForSetInternal(const nsAString& aSheetSet,
                                             bool aUpdateCSSLoader)
 {
   BeginUpdate(UPDATE_STYLE);
   int32_t count = GetNumberOfStyleSheets();
   nsAutoString title;
   for (int32_t index = 0; index < count; index++) {
-    nsIStyleSheet* sheet = GetStyleSheetAt(index);
+    CSSStyleSheet* sheet = GetStyleSheetAt(index);
     NS_ASSERTION(sheet, "Null sheet in sheet list!");
     sheet->GetTitle(title);
     if (!title.IsEmpty()) {
       sheet->SetEnabled(title.Equals(aSheetSet));
     }
   }
   if (aUpdateCSSLoader) {
     CSSLoader()->SetPreferredSheet(aSheetSet);
@@ -10168,34 +10127,31 @@ nsIDocument::CreateStaticClone(nsIDocShe
       } else {
         clonedDoc->mOriginalDocument = this;
       }
 
       clonedDoc->mOriginalDocument->mStaticCloneCount++;
 
       int32_t sheetsCount = GetNumberOfStyleSheets();
       for (int32_t i = 0; i < sheetsCount; ++i) {
-        RefPtr<CSSStyleSheet> sheet = do_QueryObject(GetStyleSheetAt(i));
+        RefPtr<CSSStyleSheet> sheet = GetStyleSheetAt(i);
         if (sheet) {
           if (sheet->IsApplicable()) {
             RefPtr<CSSStyleSheet> clonedSheet =
               sheet->Clone(nullptr, nullptr, clonedDoc, nullptr);
             NS_WARN_IF_FALSE(clonedSheet, "Cloning a stylesheet didn't work!");
             if (clonedSheet) {
               clonedDoc->AddStyleSheet(clonedSheet);
             }
           }
         }
       }
 
-      sheetsCount = thisAsDoc->mOnDemandBuiltInUASheets.Count();
       // Iterate backwards to maintain order
-      for (int32_t i = sheetsCount - 1; i >= 0; --i) {
-        RefPtr<CSSStyleSheet> sheet =
-          do_QueryObject(thisAsDoc->mOnDemandBuiltInUASheets[i]);
+      for (CSSStyleSheet* sheet : Reversed(thisAsDoc->mOnDemandBuiltInUASheets)) {
         if (sheet) {
           if (sheet->IsApplicable()) {
             RefPtr<CSSStyleSheet> clonedSheet =
               sheet->Clone(nullptr, nullptr, clonedDoc, nullptr);
             NS_WARN_IF_FALSE(clonedSheet, "Cloning a stylesheet didn't work!");
             if (clonedSheet) {
               clonedDoc->AddOnDemandBuiltInUASheet(clonedSheet);
             }
@@ -12273,17 +12229,17 @@ nsDocument::OnAppThemeChanged()
 {
   // Bail out if there is no theme support set up properly.
   auto themeOrigin = Preferences::GetString("b2g.theme.origin");
   if (!themeOrigin || !Preferences::GetBool("dom.mozApps.themable")) {
     return;
   }
 
   for (int32_t i = 0; i < GetNumberOfStyleSheets(); i++) {
-    RefPtr<CSSStyleSheet> sheet =  do_QueryObject(GetStyleSheetAt(i));
+    RefPtr<CSSStyleSheet> sheet = GetStyleSheetAt(i);
     if (!sheet) {
       continue;
     }
 
     nsINode* owningNode = sheet->GetOwnerNode();
     if (!owningNode) {
       continue;
     }
@@ -12640,25 +12596,29 @@ nsIDocument::DocAddSizeOfExcludingThis(n
 void
 nsIDocument::DocAddSizeOfIncludingThis(nsWindowSizes* aWindowSizes) const
 {
   aWindowSizes->mDOMOtherSize += aWindowSizes->mMallocSizeOf(this);
   DocAddSizeOfExcludingThis(aWindowSizes);
 }
 
 static size_t
-SizeOfStyleSheetsElementIncludingThis(nsIStyleSheet* aStyleSheet,
-                                      MallocSizeOf aMallocSizeOf,
-                                      void* aData)
-{
-  if (!aStyleSheet->GetOwningDocument()) {
-    // Avoid over-reporting shared sheets.
-    return 0;
-  }
-  return aStyleSheet->SizeOfIncludingThis(aMallocSizeOf);
+SizeOfOwnedSheetArrayExcludingThis(const nsTArray<RefPtr<CSSStyleSheet>>& aSheets,
+                                   MallocSizeOf aMallocSizeOf)
+{
+  size_t n = 0;
+  n += aSheets.ShallowSizeOfExcludingThis(aMallocSizeOf);
+  for (CSSStyleSheet* sheet : aSheets) {
+    if (!sheet->GetOwningDocument()) {
+      // Avoid over-reporting shared sheets.
+      continue;
+    }
+    n += sheet->SizeOfIncludingThis(aMallocSizeOf);
+  }
+  return n;
 }
 
 size_t
 nsDocument::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
 {
   // This SizeOfExcludingThis() overrides the one from nsINode.  But
   // nsDocuments can only appear at the top of the DOM tree, and we use the
   // specialized DocAddSizeOfExcludingThis() in that case.  So this should never
@@ -12699,36 +12659,28 @@ nsDocument::DocAddSizeOfExcludingThis(ns
     *p += nodeSize;
 
     if (EventListenerManager* elm = node->GetExistingListenerManager()) {
       aWindowSizes->mDOMEventListenersCount += elm->ListenerCount();
     }
   }
 
   aWindowSizes->mStyleSheetsSize +=
-    mStyleSheets.SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis,
-                                     aWindowSizes->mMallocSizeOf);
+    SizeOfOwnedSheetArrayExcludingThis(mStyleSheets,
+                                       aWindowSizes->mMallocSizeOf);
   // Note that we do not own the sheets pointed to by mOnDemandBuiltInUASheets
-  // (the nsLayoutStyleSheetCache singleton does) so pass nullptr as the
-  // aSizeOfElementIncludingThis callback argument.
-  aWindowSizes->mStyleSheetsSize +=
-    mOnDemandBuiltInUASheets.SizeOfExcludingThis(nullptr,
-                                                 aWindowSizes->mMallocSizeOf);
+  // (the nsLayoutStyleSheetCache singleton does).
   aWindowSizes->mStyleSheetsSize +=
-    mAdditionalSheets[eAgentSheet].
-      SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis,
-                          aWindowSizes->mMallocSizeOf);
-  aWindowSizes->mStyleSheetsSize +=
-    mAdditionalSheets[eUserSheet].
-      SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis,
-                          aWindowSizes->mMallocSizeOf);
-  aWindowSizes->mStyleSheetsSize +=
-    mAdditionalSheets[eAuthorSheet].
-      SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis,
-                          aWindowSizes->mMallocSizeOf);
+    mOnDemandBuiltInUASheets.ShallowSizeOfExcludingThis(
+        aWindowSizes->mMallocSizeOf);
+  for (auto& sheetArray : mAdditionalSheets) {
+    aWindowSizes->mStyleSheetsSize +=
+      SizeOfOwnedSheetArrayExcludingThis(sheetArray,
+                                         aWindowSizes->mMallocSizeOf);
+  }
   // Lumping in the loader with the style-sheets size is not ideal,
   // but most of the things in there are in fact stylesheets, so it
   // doesn't seem worthwhile to separate it out.
   aWindowSizes->mStyleSheetsSize +=
     CSSLoader()->SizeOfIncludingThis(aWindowSizes->mMallocSizeOf);
 
   aWindowSizes->mDOMOtherSize +=
     mAttrStyleSheet ?
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -796,34 +796,39 @@ public:
 
   virtual void EnsureOnDemandBuiltInUASheet(mozilla::CSSStyleSheet* aSheet) override;
 
   /**
    * Get the (document) style sheets owned by this document.
    * These are ordered, highest priority last
    */
   virtual int32_t GetNumberOfStyleSheets() const override;
-  virtual nsIStyleSheet* GetStyleSheetAt(int32_t aIndex) const override;
-  virtual int32_t GetIndexOfStyleSheet(nsIStyleSheet* aSheet) const override;
-  virtual void AddStyleSheet(nsIStyleSheet* aSheet) override;
-  virtual void RemoveStyleSheet(nsIStyleSheet* aSheet) override;
+  virtual mozilla::CSSStyleSheet* GetStyleSheetAt(int32_t aIndex) const override;
+  virtual int32_t GetIndexOfStyleSheet(mozilla::CSSStyleSheet* aSheet) const override;
+  virtual void AddStyleSheet(mozilla::CSSStyleSheet* aSheet) override;
+  virtual void RemoveStyleSheet(mozilla::CSSStyleSheet* aSheet) override;
 
-  virtual void UpdateStyleSheets(nsCOMArray<nsIStyleSheet>& aOldSheets,
-                                 nsCOMArray<nsIStyleSheet>& aNewSheets) override;
-  virtual void AddStyleSheetToStyleSets(nsIStyleSheet* aSheet);
-  virtual void RemoveStyleSheetFromStyleSets(nsIStyleSheet* aSheet);
+  virtual void UpdateStyleSheets(
+      nsTArray<RefPtr<mozilla::CSSStyleSheet>>& aOldSheets,
+      nsTArray<RefPtr<mozilla::CSSStyleSheet>>& aNewSheets) override;
+  virtual void AddStyleSheetToStyleSets(mozilla::CSSStyleSheet* aSheet);
+  virtual void RemoveStyleSheetFromStyleSets(mozilla::CSSStyleSheet* aSheet);
 
-  virtual void InsertStyleSheetAt(nsIStyleSheet* aSheet, int32_t aIndex) override;
-  virtual void SetStyleSheetApplicableState(nsIStyleSheet* aSheet,
+  virtual void InsertStyleSheetAt(mozilla::CSSStyleSheet* aSheet,
+                                  int32_t aIndex) override;
+  virtual void SetStyleSheetApplicableState(mozilla::CSSStyleSheet* aSheet,
                                             bool aApplicable) override;
 
-  virtual nsresult LoadAdditionalStyleSheet(additionalSheetType aType, nsIURI* aSheetURI) override;
-  virtual nsresult AddAdditionalStyleSheet(additionalSheetType aType, nsIStyleSheet* aSheet) override;
-  virtual void RemoveAdditionalStyleSheet(additionalSheetType aType, nsIURI* sheetURI) override;
-  virtual nsIStyleSheet* FirstAdditionalAuthorSheet() override;
+  virtual nsresult LoadAdditionalStyleSheet(additionalSheetType aType,
+                                            nsIURI* aSheetURI) override;
+  virtual nsresult AddAdditionalStyleSheet(additionalSheetType aType,
+                                           mozilla::CSSStyleSheet* aSheet) override;
+  virtual void RemoveAdditionalStyleSheet(additionalSheetType aType,
+                                          nsIURI* sheetURI) override;
+  virtual mozilla::CSSStyleSheet* FirstAdditionalAuthorSheet() override;
 
   virtual nsIChannel* GetChannel() const override {
     return mChannel;
   }
 
   virtual nsIChannel* GetFailedChannel() const override {
     return mFailedChannel;
   }
@@ -873,22 +878,22 @@ public:
   virtual void SetReadyStateInternal(ReadyState rs) override;
 
   virtual void ContentStateChanged(nsIContent* aContent,
                                    mozilla::EventStates aStateMask)
                                      override;
   virtual void DocumentStatesChanged(
                  mozilla::EventStates aStateMask) override;
 
-  virtual void StyleRuleChanged(nsIStyleSheet* aStyleSheet,
+  virtual void StyleRuleChanged(mozilla::CSSStyleSheet* aStyleSheet,
                                 mozilla::css::Rule* aOldStyleRule,
                                 mozilla::css::Rule* aNewStyleRule) override;
-  virtual void StyleRuleAdded(nsIStyleSheet* aStyleSheet,
+  virtual void StyleRuleAdded(mozilla::CSSStyleSheet* aStyleSheet,
                               mozilla::css::Rule* aStyleRule) override;
-  virtual void StyleRuleRemoved(nsIStyleSheet* aStyleSheet,
+  virtual void StyleRuleRemoved(mozilla::CSSStyleSheet* aStyleSheet,
                                 mozilla::css::Rule* aStyleRule) override;
 
   virtual void FlushPendingNotifications(mozFlushType aType) override;
   virtual void FlushExternalResources(mozFlushType aType) override;
   virtual void SetXMLDeclaration(const char16_t *aVersion,
                                  const char16_t *aEncoding,
                                  const int32_t aStandalone) override;
   virtual void GetXMLDeclaration(nsAString& aVersion,
@@ -1489,18 +1494,19 @@ public:
 
 protected:
   already_AddRefed<nsIPresShell> doCreateShell(nsPresContext* aContext,
                                                nsViewManager* aViewManager,
                                                nsStyleSet* aStyleSet,
                                                nsCompatibility aCompatMode);
 
   void RemoveDocStyleSheetsFromStyleSets();
-  void RemoveStyleSheetsFromStyleSets(nsCOMArray<nsIStyleSheet>& aSheets, 
-                                      mozilla::SheetType aType);
+  void RemoveStyleSheetsFromStyleSets(
+      nsTArray<RefPtr<mozilla::CSSStyleSheet>>& aSheets,
+      mozilla::SheetType aType);
   void ResetStylesheetsToURI(nsIURI* aURI);
   void FillStyleSet(nsStyleSet* aStyleSet);
 
   // Return whether all the presshells for this document are safe to flush
   bool IsSafeToFlush() const;
 
   void DispatchPageTransition(mozilla::dom::EventTarget* aDispatchTarget,
                               const nsAString& aType,
@@ -1543,19 +1549,19 @@ protected:
   // parsed into.
   nsCOMPtr<nsIParser> mParser;
 
   // Weak reference to our sink for in case we no longer have a parser.  This
   // will allow us to flush out any pending stuff from the sink even if
   // EndLoad() has already happened.
   nsWeakPtr mWeakSink;
 
-  nsCOMArray<nsIStyleSheet> mStyleSheets;
-  nsCOMArray<nsIStyleSheet> mOnDemandBuiltInUASheets;
-  nsCOMArray<nsIStyleSheet> mAdditionalSheets[AdditionalSheetTypeCount];
+  nsTArray<RefPtr<mozilla::CSSStyleSheet>> mStyleSheets;
+  nsTArray<RefPtr<mozilla::CSSStyleSheet>> mOnDemandBuiltInUASheets;
+  nsTArray<RefPtr<mozilla::CSSStyleSheet>> mAdditionalSheets[AdditionalSheetTypeCount];
 
   // Array of observers
   nsTObserverArray<nsIDocumentObserver*> mObservers;
 
   // Tracker for animations that are waiting to start.
   // nullptr until GetOrCreatePendingAnimationTracker is called.
   RefPtr<mozilla::PendingAnimationTracker> mPendingAnimationTracker;
 
@@ -1704,18 +1710,18 @@ public:
   mozilla::EventStates mDocumentState;
   mozilla::EventStates mGotDocumentState;
 
   RefPtr<nsDOMNavigationTiming> mTiming;
 private:
   friend class nsUnblockOnloadEvent;
   // Recomputes the visibility state but doesn't set the new value.
   mozilla::dom::VisibilityState GetVisibilityState() const;
-  void NotifyStyleSheetAdded(nsIStyleSheet* aSheet, bool aDocumentSheet);
-  void NotifyStyleSheetRemoved(nsIStyleSheet* aSheet, bool aDocumentSheet);
+  void NotifyStyleSheetAdded(mozilla::CSSStyleSheet* aSheet, bool aDocumentSheet);
+  void NotifyStyleSheetRemoved(mozilla::CSSStyleSheet* aSheet, bool aDocumentSheet);
 
   void PostUnblockOnloadEvent();
   void DoUnblockOnload();
 
   nsresult CheckFrameOptions();
   bool IsLoopDocument(nsIChannel* aChannel);
   nsresult InitCSP(nsIChannel* aChannel);
 
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -67,17 +67,16 @@ class nsILoadContext;
 class nsIObjectLoadingContent;
 class nsIObserver;
 class nsIPresShell;
 class nsIPrincipal;
 class nsIRequest;
 class nsIRunnable;
 class nsIStreamListener;
 class nsIStructuredCloneContainer;
-class nsIStyleSheet;
 class nsIURI;
 class nsIVariant;
 class nsLocation;
 class nsViewManager;
 class nsPresContext;
 class nsRange;
 class nsScriptLoader;
 class nsSMILAnimationController;
@@ -151,18 +150,18 @@ template<typename> class Sequence;
 
 template<typename, typename> class CallbackObjectHolder;
 typedef CallbackObjectHolder<NodeFilter, nsIDOMNodeFilter> NodeFilterHolder;
 
 } // namespace dom
 } // namespace mozilla
 
 #define NS_IDOCUMENT_IID \
-{ 0x4307abe8, 0x5386, 0x4024, \
-  { 0xa2, 0xfe, 0x4a, 0x80, 0xe8, 0x47, 0x46, 0x90 } }
+{ 0xecc9e376, 0x6c31, 0x4f04, \
+  { 0xbe, 0xde, 0xd6, 0x27, 0x61, 0xd7, 0x00, 0x84 } }
 
 // Enum for requesting a particular type of document when creating a doc
 enum DocumentFlavor {
   DocumentFlavorLegacyGuess, // compat with old code until made HTML5-compliant
   DocumentFlavorHTML, // HTMLDocument with HTMLness bit set to true
   DocumentFlavorSVG, // SVGDocument
   DocumentFlavorPlain, // Just a Document
 };
@@ -920,74 +919,79 @@ public:
   virtual int32_t GetNumberOfStyleSheets() const = 0;
 
   /**
    * Get a particular stylesheet
    * @param aIndex the index the stylesheet lives at.  This is zero-based
    * @return the stylesheet at aIndex.  Null if aIndex is out of range.
    * @throws no exceptions
    */
-  virtual nsIStyleSheet* GetStyleSheetAt(int32_t aIndex) const = 0;
+  virtual mozilla::CSSStyleSheet* GetStyleSheetAt(int32_t aIndex) const = 0;
 
   /**
    * Insert a sheet at a particular spot in the stylesheet list (zero-based)
    * @param aSheet the sheet to insert
    * @param aIndex the index to insert at.  This index will be
    *   adjusted for the "special" sheets.
    * @throws no exceptions
    */
-  virtual void InsertStyleSheetAt(nsIStyleSheet* aSheet, int32_t aIndex) = 0;
+  virtual void InsertStyleSheetAt(mozilla::CSSStyleSheet* aSheet,
+                                  int32_t aIndex) = 0;
 
   /**
    * Get the index of a particular stylesheet.  This will _always_
    * consider the "special" sheets as part of the sheet list.
    * @param aSheet the sheet to get the index of
    * @return aIndex the index of the sheet in the full list
    */
-  virtual int32_t GetIndexOfStyleSheet(nsIStyleSheet* aSheet) const = 0;
+  virtual int32_t GetIndexOfStyleSheet(mozilla::CSSStyleSheet* aSheet) const = 0;
 
   /**
    * Replace the stylesheets in aOldSheets with the stylesheets in
    * aNewSheets. The two lists must have equal length, and the sheet
    * at positon J in the first list will be replaced by the sheet at
    * position J in the second list.  Some sheets in the second list
    * may be null; if so the corresponding sheets in the first list
    * will simply be removed.
    */
-  virtual void UpdateStyleSheets(nsCOMArray<nsIStyleSheet>& aOldSheets,
-                                 nsCOMArray<nsIStyleSheet>& aNewSheets) = 0;
+  virtual void UpdateStyleSheets(
+      nsTArray<RefPtr<mozilla::CSSStyleSheet>>& aOldSheets,
+      nsTArray<RefPtr<mozilla::CSSStyleSheet>>& aNewSheets) = 0;
 
   /**
    * Add a stylesheet to the document
    */
-  virtual void AddStyleSheet(nsIStyleSheet* aSheet) = 0;
+  virtual void AddStyleSheet(mozilla::CSSStyleSheet* aSheet) = 0;
 
   /**
    * Remove a stylesheet from the document
    */
-  virtual void RemoveStyleSheet(nsIStyleSheet* aSheet) = 0;
+  virtual void RemoveStyleSheet(mozilla::CSSStyleSheet* aSheet) = 0;
 
   /**
    * Notify the document that the applicable state of the sheet changed
    * and that observers should be notified and style sets updated
    */
-  virtual void SetStyleSheetApplicableState(nsIStyleSheet* aSheet,
+  virtual void SetStyleSheetApplicableState(mozilla::CSSStyleSheet* aSheet,
                                             bool aApplicable) = 0;
 
   enum additionalSheetType {
     eAgentSheet,
     eUserSheet,
     eAuthorSheet,
     AdditionalSheetTypeCount
   };
 
-  virtual nsresult LoadAdditionalStyleSheet(additionalSheetType aType, nsIURI* aSheetURI) = 0;
-  virtual nsresult AddAdditionalStyleSheet(additionalSheetType aType, nsIStyleSheet* aSheet) = 0;
-  virtual void RemoveAdditionalStyleSheet(additionalSheetType aType, nsIURI* sheetURI) = 0;
-  virtual nsIStyleSheet* FirstAdditionalAuthorSheet() = 0;
+  virtual nsresult LoadAdditionalStyleSheet(additionalSheetType aType,
+                                            nsIURI* aSheetURI) = 0;
+  virtual nsresult AddAdditionalStyleSheet(additionalSheetType aType,
+                                           mozilla::CSSStyleSheet* aSheet) = 0;
+  virtual void RemoveAdditionalStyleSheet(additionalSheetType aType,
+                                          nsIURI* sheetURI) = 0;
+  virtual mozilla::CSSStyleSheet* FirstAdditionalAuthorSheet() = 0;
 
   /**
    * Get this document's CSSLoader.  This is guaranteed to not return null.
    */
   mozilla::css::Loader* CSSLoader() const {
     return mCSSLoader;
   }
 
@@ -1280,22 +1284,22 @@ public:
 
   // Notify that a document state has changed.
   // This should only be called by callers whose state is also reflected in the
   // implementation of nsDocument::GetDocumentState.
   virtual void DocumentStatesChanged(mozilla::EventStates aStateMask) = 0;
 
   // Observation hooks for style data to propagate notifications
   // to document observers
-  virtual void StyleRuleChanged(nsIStyleSheet* aStyleSheet,
+  virtual void StyleRuleChanged(mozilla::CSSStyleSheet* aStyleSheet,
                                 mozilla::css::Rule* aOldStyleRule,
                                 mozilla::css::Rule* aNewStyleRule) = 0;
-  virtual void StyleRuleAdded(nsIStyleSheet* aStyleSheet,
+  virtual void StyleRuleAdded(mozilla::CSSStyleSheet* aStyleSheet,
                               mozilla::css::Rule* aStyleRule) = 0;
-  virtual void StyleRuleRemoved(nsIStyleSheet* aStyleSheet,
+  virtual void StyleRuleRemoved(mozilla::CSSStyleSheet* aStyleSheet,
                                 mozilla::css::Rule* aStyleRule) = 0;
 
   /**
    * Flush notifications for this document and its parent documents
    * (since those may affect the layout of this one).
    */
   virtual void FlushPendingNotifications(mozFlushType aType) = 0;
 
--- a/dom/base/nsIDocumentObserver.h
+++ b/dom/base/nsIDocumentObserver.h
@@ -6,28 +6,28 @@
 #ifndef nsIDocumentObserver_h___
 #define nsIDocumentObserver_h___
 
 #include "mozilla/EventStates.h"
 #include "nsISupports.h"
 #include "nsIMutationObserver.h"
 
 class nsIContent;
-class nsIStyleSheet;
 class nsIDocument;
 
 namespace mozilla {
+class CSSStyleSheet;
 namespace css {
 class Rule;
 } // namespace css
 } // namespace mozilla
 
 #define NS_IDOCUMENT_OBSERVER_IID \
-{ 0xce1d53d0, 0x4739, 0x44e5, \
-  { 0xb4, 0xae, 0x60, 0xe8, 0x82, 0xcb, 0x73, 0x1b } }
+{ 0x21c8ad67, 0x3a7d, 0x4881, \
+  { 0xa5, 0x43, 0xcb, 0xa9, 0xbb, 0xe4, 0x9e, 0x39 } }
 
 typedef uint32_t nsUpdateType;
 
 #define UPDATE_CONTENT_MODEL 0x00000001
 #define UPDATE_STYLE         0x00000002
 #define UPDATE_ALL (UPDATE_CONTENT_MODEL | UPDATE_STYLE)
 
 // Document observer interface
@@ -97,48 +97,48 @@ public:
    * notification is passed on to all of the document observers.   
    *
    * @param aDocument The document being observed
    * @param aStyleSheet the StyleSheet that has been added
    * @param aDocumentSheet True if sheet is in document's style sheet list,
    *                       false if sheet is not (i.e., UA or user sheet)
    */
   virtual void StyleSheetAdded(nsIDocument *aDocument,
-                               nsIStyleSheet* aStyleSheet,
+                               mozilla::CSSStyleSheet* aStyleSheet,
                                bool aDocumentSheet) = 0;
 
   /**
    * A StyleSheet has just been removed from the document.  This
    * method is called automatically when a StyleSheet gets removed
    * from the document, even if the stylesheet is not applicable. The
    * notification is passed on to all of the document observers.
    *
    * @param aDocument The document being observed
    * @param aStyleSheet the StyleSheet that has been removed
    * @param aDocumentSheet True if sheet is in document's style sheet list,
    *                       false if sheet is not (i.e., UA or user sheet)
    */
   virtual void StyleSheetRemoved(nsIDocument *aDocument,
-                                 nsIStyleSheet* aStyleSheet,
+                                 mozilla::CSSStyleSheet* aStyleSheet,
                                  bool aDocumentSheet) = 0;
   
   /**
    * A StyleSheet has just changed its applicable state.
    * This method is called automatically when the applicable state
    * of a StyleSheet gets changed. The style sheet passes this
    * notification to the document. The notification is passed on 
    * to all of the document observers.
    *
    * @param aDocument The document being observed
    * @param aStyleSheet the StyleSheet that has changed state
    * @param aApplicable true if the sheet is applicable, false if
    *        it is not applicable
    */
   virtual void StyleSheetApplicableStateChanged(nsIDocument *aDocument,
-                                                nsIStyleSheet* aStyleSheet,
+                                                mozilla::CSSStyleSheet* aStyleSheet,
                                                 bool aApplicable) = 0;
 
   /**
    * A StyleRule has just been modified within a style sheet.
    * This method is called automatically when the rule gets
    * modified. The style sheet passes this notification to 
    * the document. The notification is passed on to all of 
    * the document observers.
@@ -155,48 +155,48 @@ public:
    * @param aStyleSheet the StyleSheet that contians the rule
    * @param aOldStyleRule The rule being removed.  This rule may not be
    *                      fully valid anymore -- however, it can still
    *                      be used for pointer comparison and
    *                      |QueryInterface|.
    * @param aNewStyleRule The rule being added.
    */
   virtual void StyleRuleChanged(nsIDocument *aDocument,
-                                nsIStyleSheet* aStyleSheet,
+                                mozilla::CSSStyleSheet* aStyleSheet,
                                 mozilla::css::Rule* aOldStyleRule,
                                 mozilla::css::Rule* aNewStyleRule) = 0;
 
   /**
    * A StyleRule has just been added to a style sheet.
    * This method is called automatically when the rule gets
    * added to the sheet. The style sheet passes this
    * notification to the document. The notification is passed on 
    * to all of the document observers.
    *
    * @param aDocument The document being observed
    * @param aStyleSheet the StyleSheet that has been modified
    * @param aStyleRule the rule that was added
    */
   virtual void StyleRuleAdded(nsIDocument *aDocument,
-                              nsIStyleSheet* aStyleSheet,
+                              mozilla::CSSStyleSheet* aStyleSheet,
                               mozilla::css::Rule* aStyleRule) = 0;
 
   /**
    * A StyleRule has just been removed from a style sheet.
    * This method is called automatically when the rule gets
    * removed from the sheet. The style sheet passes this
    * notification to the document. The notification is passed on 
    * to all of the document observers.
    *
    * @param aDocument The document being observed
    * @param aStyleSheet the StyleSheet that has been modified
    * @param aStyleRule the rule that was removed
    */
   virtual void StyleRuleRemoved(nsIDocument *aDocument,
-                                nsIStyleSheet* aStyleSheet,
+                                mozilla::CSSStyleSheet* aStyleSheet,
                                 mozilla::css::Rule* aStyleRule) = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocumentObserver, NS_IDOCUMENT_OBSERVER_IID)
 
 #define NS_DECL_NSIDOCUMENTOBSERVER_BEGINUPDATE                              \
     virtual void BeginUpdate(nsIDocument* aDocument,                         \
                              nsUpdateType aUpdateType) override;
@@ -216,43 +216,44 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocumen
                                      mozilla::EventStates aStateMask) override;
 
 #define NS_DECL_NSIDOCUMENTOBSERVER_DOCUMENTSTATESCHANGED                    \
     virtual void DocumentStatesChanged(nsIDocument* aDocument,               \
                                        mozilla::EventStates aStateMask) override;
 
 #define NS_DECL_NSIDOCUMENTOBSERVER_STYLESHEETADDED                          \
     virtual void StyleSheetAdded(nsIDocument* aDocument,                     \
-                                 nsIStyleSheet* aStyleSheet,                 \
+                                 mozilla::CSSStyleSheet* aStyleSheet,        \
                                  bool aDocumentSheet) override;
 
 #define NS_DECL_NSIDOCUMENTOBSERVER_STYLESHEETREMOVED                        \
     virtual void StyleSheetRemoved(nsIDocument* aDocument,                   \
-                                   nsIStyleSheet* aStyleSheet,               \
+                                   mozilla::CSSStyleSheet* aStyleSheet,      \
                                    bool aDocumentSheet) override;
 
 #define NS_DECL_NSIDOCUMENTOBSERVER_STYLESHEETAPPLICABLESTATECHANGED         \
-    virtual void StyleSheetApplicableStateChanged(nsIDocument* aDocument,    \
-                                                  nsIStyleSheet* aStyleSheet,\
-                                                  bool aApplicable) override;
+    virtual void StyleSheetApplicableStateChanged(                           \
+        nsIDocument* aDocument,                                              \
+        mozilla::CSSStyleSheet* aStyleSheet,                                 \
+        bool aApplicable) override;
 
 #define NS_DECL_NSIDOCUMENTOBSERVER_STYLERULECHANGED                         \
     virtual void StyleRuleChanged(nsIDocument* aDocument,                    \
-                                  nsIStyleSheet* aStyleSheet,                \
+                                  mozilla::CSSStyleSheet* aStyleSheet,       \
                                   mozilla::css::Rule* aOldStyleRule,         \
                                   mozilla::css::Rule* aNewStyleRule) override;
 
 #define NS_DECL_NSIDOCUMENTOBSERVER_STYLERULEADDED                           \
     virtual void StyleRuleAdded(nsIDocument* aDocument,                      \
-                                nsIStyleSheet* aStyleSheet,                  \
+                                mozilla::CSSStyleSheet* aStyleSheet,         \
                                 mozilla::css::Rule* aStyleRule) override;
 
 #define NS_DECL_NSIDOCUMENTOBSERVER_STYLERULEREMOVED                         \
     virtual void StyleRuleRemoved(nsIDocument* aDocument,                    \
-                                  nsIStyleSheet* aStyleSheet,                \
+                                  mozilla::CSSStyleSheet* aStyleSheet,       \
                                   mozilla::css::Rule* aStyleRule) override;
 
 #define NS_DECL_NSIDOCUMENTOBSERVER                                          \
     NS_DECL_NSIDOCUMENTOBSERVER_BEGINUPDATE                                  \
     NS_DECL_NSIDOCUMENTOBSERVER_ENDUPDATE                                    \
     NS_DECL_NSIDOCUMENTOBSERVER_BEGINLOAD                                    \
     NS_DECL_NSIDOCUMENTOBSERVER_ENDLOAD                                      \
     NS_DECL_NSIDOCUMENTOBSERVER_CONTENTSTATECHANGED                          \
@@ -302,45 +303,45 @@ void                                    
 }
 
 #define NS_IMPL_NSIDOCUMENTOBSERVER_CONTENT(_class)                       \
 NS_IMPL_NSIMUTATIONOBSERVER_CONTENT(_class)
 
 #define NS_IMPL_NSIDOCUMENTOBSERVER_STYLE_STUB(_class)                    \
 void                                                                      \
 _class::StyleSheetAdded(nsIDocument* aDocument,                           \
-                        nsIStyleSheet* aStyleSheet,                       \
-                        bool aDocumentSheet)                            \
+                        mozilla::CSSStyleSheet* aStyleSheet,              \
+                        bool aDocumentSheet)                              \
 {                                                                         \
 }                                                                         \
 void                                                                      \
 _class::StyleSheetRemoved(nsIDocument* aDocument,                         \
-                          nsIStyleSheet* aStyleSheet,                     \
-                          bool aDocumentSheet)                          \
+                          mozilla::CSSStyleSheet* aStyleSheet,            \
+                          bool aDocumentSheet)                            \
 {                                                                         \
 }                                                                         \
 void                                                                      \
 _class::StyleSheetApplicableStateChanged(nsIDocument* aDocument,          \
-                                         nsIStyleSheet* aStyleSheet,      \
-                                         bool aApplicable)              \
+                                         mozilla::CSSStyleSheet* aStyleSheet,\
+                                         bool aApplicable)                \
 {                                                                         \
 }                                                                         \
 void                                                                      \
 _class::StyleRuleChanged(nsIDocument* aDocument,                          \
-                         nsIStyleSheet* aStyleSheet,                      \
+                         mozilla::CSSStyleSheet* aStyleSheet,             \
                          mozilla::css::Rule* aOldStyleRule,               \
                          mozilla::css::Rule* aNewStyleRule)               \
 {                                                                         \
 }                                                                         \
 void                                                                      \
 _class::StyleRuleAdded(nsIDocument* aDocument,                            \
-                       nsIStyleSheet* aStyleSheet,                        \
+                       mozilla::CSSStyleSheet* aStyleSheet,               \
                        mozilla::css::Rule* aStyleRule)                    \
 {                                                                         \
 }                                                                         \
 void                                                                      \
 _class::StyleRuleRemoved(nsIDocument* aDocument,                          \
-                         nsIStyleSheet* aStyleSheet,                      \
+                         mozilla::CSSStyleSheet* aStyleSheet,             \
                          mozilla::css::Rule* aStyleRule)                  \
 {                                                                         \
 }
 
 #endif /* nsIDocumentObserver_h___ */
--- a/dom/base/nsMappedAttributes.cpp
+++ b/dom/base/nsMappedAttributes.cpp
@@ -179,16 +179,24 @@ nsMappedAttributes::SetStyleSheet(nsHTML
 /* virtual */ void
 nsMappedAttributes::MapRuleInfoInto(nsRuleData* aRuleData)
 {
   if (mRuleMapper) {
     (*mRuleMapper)(this, aRuleData);
   }
 }
 
+/* virtual */ bool
+nsMappedAttributes::MightMapInheritedStyleData()
+{
+  // Just assume that we do, rather than adding checks to all of the different
+  // kinds of attribute mapping functions we have.
+  return true;
+}
+
 #ifdef DEBUG
 /* virtual */ void
 nsMappedAttributes::List(FILE* out, int32_t aIndent) const
 {
   nsAutoCString str;
   nsAutoString tmp;
   uint32_t i;
 
--- a/dom/base/nsMappedAttributes.h
+++ b/dom/base/nsMappedAttributes.h
@@ -69,16 +69,17 @@ public:
   // aValue; any value that was already in aValue is destroyed.
   void RemoveAttrAt(uint32_t aPos, nsAttrValue& aValue);
   const nsAttrName* GetExistingAttrNameFromQName(const nsAString& aName) const;
   int32_t IndexOfAttr(nsIAtom* aLocalName) const;
   
 
   // nsIStyleRule 
   virtual void MapRuleInfoInto(nsRuleData* aRuleData) override;
+  virtual bool MightMapInheritedStyleData() override;
 #ifdef DEBUG
   virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
 #endif
 
   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
 
 private:
   nsMappedAttributes(const nsMappedAttributes& aCopy);
--- a/dom/base/nsStyleLinkElement.cpp
+++ b/dom/base/nsStyleLinkElement.cpp
@@ -283,27 +283,16 @@ UpdateIsElementInStyleScopeFlagOnSubtree
       if (n->IsElement()) {
         n->ClearIsElementInStyleScope();
       }
       n = n->GetNextNode(aElement);
     }
   }
 }
 
-static Element*
-GetScopeElement(nsIStyleSheet* aSheet)
-{
-  RefPtr<CSSStyleSheet> cssStyleSheet = do_QueryObject(aSheet);
-  if (!cssStyleSheet) {
-    return nullptr;
-  }
-
-  return cssStyleSheet->GetScopeElement();
-}
-
 nsresult
 nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument* aOldDocument,
                                        ShadowRoot* aOldShadowRoot,
                                        nsICSSLoaderObserver* aObserver,
                                        bool* aWillNotify,
                                        bool* aIsAlternate,
                                        bool aForceUpdate)
 {
@@ -325,17 +314,18 @@ nsStyleLinkElement::DoUpdateStyleSheet(n
   // Check for a ShadowRoot because link elements are inert in a
   // ShadowRoot.
   ShadowRoot* containingShadow = thisContent->GetContainingShadow();
   if (thisContent->IsHTMLElement(nsGkAtoms::link) &&
       (aOldShadowRoot || containingShadow)) {
     return NS_OK;
   }
 
-  Element* oldScopeElement = GetScopeElement(mStyleSheet);
+  Element* oldScopeElement =
+    mStyleSheet ? mStyleSheet->GetScopeElement() : nullptr;
 
   if (mStyleSheet && (aOldDocument || aOldShadowRoot)) {
     MOZ_ASSERT(!(aOldDocument && aOldShadowRoot),
                "ShadowRoot content is never in document, thus "
                "there should not be a old document and old "
                "ShadowRoot simultaneously.");
 
     // We're removing the link element from the document or shadow tree,
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -1335,17 +1335,16 @@ DOMInterfaces = {
     'implicitJSContext': ['send']
 },
 
 'ThreadSafeChromeUtils': {
     # The codegen is dumb, and doesn't understand that this interface is only a
     # collection of static methods, so we have this `concrete: False` hack.
     'concrete': False,
     'headerFile': 'mozilla/dom/ChromeUtils.h',
-    'implicitJSContext': ['readHeapSnapshot', 'saveHeapSnapshot']
 },
 
 'TouchList': {
     'headerFile': 'mozilla/dom/TouchEvent.h',
 },
 
 'TreeColumn': {
     'nativeType': 'nsTreeColumn',
--- a/dom/camera/GonkCameraControl.cpp
+++ b/dom/camera/GonkCameraControl.cpp
@@ -2383,25 +2383,21 @@ nsGonkCameraControl::OnPoster(void* aDat
   }
   CameraControlImpl::OnPoster(blobImpl);
 }
 
 void
 nsGonkCameraControl::OnNewPreviewFrame(layers::TextureClient* aBuffer)
 {
 #ifdef MOZ_WIDGET_GONK
-  RefPtr<Image> frame = mImageContainer->CreateImage(ImageFormat::GRALLOC_PLANAR_YCBCR);
-
-  GrallocImage* videoImage = static_cast<GrallocImage*>(frame.get());
+  RefPtr<GrallocImage> frame = new GrallocImage();
 
-  GrallocImage::GrallocData data;
-  data.mGraphicBuffer = aBuffer;
-  data.mPicSize = IntSize(mCurrentConfiguration.mPreviewSize.width,
-                          mCurrentConfiguration.mPreviewSize.height);
-  videoImage->SetData(data);
+  IntSize picSize(mCurrentConfiguration.mPreviewSize.width,
+                  mCurrentConfiguration.mPreviewSize.height);
+  frame->SetData(aBuffer, picSize);
 
   if (mCapturePoster.exchange(false)) {
     CreatePoster(frame,
                  mCurrentConfiguration.mPreviewSize.width,
                  mCurrentConfiguration.mPreviewSize.height,
                  mVideoRotation);
     return;
   }
--- a/dom/canvas/ImageBitmap.cpp
+++ b/dom/canvas/ImageBitmap.cpp
@@ -146,25 +146,17 @@ CropAndCopyDataSourceSurface(DataSourceS
 
 /*
  * Encapsulate the given _aSurface_ into a layers::CairoImage.
  */
 static already_AddRefed<layers::Image>
 CreateImageFromSurface(SourceSurface* aSurface, ErrorResult& aRv)
 {
   MOZ_ASSERT(aSurface);
-
-  layers::CairoImage::Data cairoData;
-  cairoData.mSize = aSurface->GetSize();
-  cairoData.mSourceSurface = aSurface;
-
-  RefPtr<layers::CairoImage> image = new layers::CairoImage();
-
-  image->SetData(cairoData);
-
+  RefPtr<layers::CairoImage> image = new layers::CairoImage(aSurface->GetSize(), aSurface);
   return image.forget();
 }
 
 /*
  * CreateImageFromRawData(), CreateSurfaceFromRawData() and
  * CreateImageFromRawDataInMainThreadSyncTask are helpers for
  * create-from-ImageData case
  */
--- a/dom/html/HTMLBodyElement.cpp
+++ b/dom/html/HTMLBodyElement.cpp
@@ -163,16 +163,22 @@ BodyRule::MapRuleInfoInto(nsRuleData* aD
         nsCSSValue* marginBottom = aData->ValueForMarginBottom();
         if (marginBottom->GetUnit() == eCSSUnit_Null)
           marginBottom->SetFloatValue((float)frameMarginHeight, eCSSUnit_Pixel);
       }
     }
   }
 }
 
+/* virtual */ bool
+BodyRule::MightMapInheritedStyleData()
+{
+  return false;
+}
+
 #ifdef DEBUG
 /* virtual */ void
 BodyRule::List(FILE* out, int32_t aIndent) const
 {
   nsAutoCString indent;
   for (int32_t index = aIndent; --index >= 0; ) {
     indent.AppendLiteral("  ");
   }
--- a/dom/html/HTMLBodyElement.h
+++ b/dom/html/HTMLBodyElement.h
@@ -23,16 +23,17 @@ class BodyRule: public nsIStyleRule
 
 public:
   explicit BodyRule(HTMLBodyElement* aPart);
 
   NS_DECL_ISUPPORTS
 
   // nsIStyleRule interface
   virtual void MapRuleInfoInto(nsRuleData* aRuleData) override;
+  virtual bool MightMapInheritedStyleData() override;
 #ifdef DEBUG
   virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override;
 #endif
 
   HTMLBodyElement*  mPart;  // not ref-counted, cleared by content 
 };
 
 class HTMLBodyElement final : public nsGenericHTMLElement,
--- a/dom/html/HTMLCanvasElement.cpp
+++ b/dom/html/HTMLCanvasElement.cpp
@@ -1168,23 +1168,17 @@ HTMLCanvasElement::IsFrameCaptureRequest
   }
   return false;
 }
 
 void
 HTMLCanvasElement::SetFrameCapture(already_AddRefed<SourceSurface> aSurface)
 {
   RefPtr<SourceSurface> surface = aSurface;
-
-  CairoImage::Data imageData;
-  imageData.mSize = surface->GetSize();
-  imageData.mSourceSurface = surface;
-
-  RefPtr<CairoImage> image = new CairoImage();
-  image->SetData(imageData);
+  RefPtr<CairoImage> image = new CairoImage(surface->GetSize(), surface);
 
   // Loop backwards to allow removing elements in the loop.
   for (int i = mRequestedFrameListeners.Length() - 1; i >= 0; --i) {
     WeakPtr<FrameCaptureListener> listener = mRequestedFrameListeners[i];
     if (!listener) {
       // listener was destroyed. Remove it from the list.
       mRequestedFrameListeners.RemoveElementAt(i);
       continue;
--- a/dom/html/HTMLLinkElement.cpp
+++ b/dom/html/HTMLLinkElement.cpp
@@ -16,17 +16,16 @@
 #include "nsContentUtils.h"
 #include "nsGenericHTMLElement.h"
 #include "nsGkAtoms.h"
 #include "nsDOMTokenList.h"
 #include "nsIDocument.h"
 #include "nsIDOMEvent.h"
 #include "nsIDOMStyleSheet.h"
 #include "nsINode.h"
-#include "nsIStyleSheet.h"
 #include "nsIStyleSheetLinkingElement.h"
 #include "nsIURL.h"
 #include "nsPIDOMWindow.h"
 #include "nsReadableUtils.h"
 #include "nsStyleConsts.h"
 #include "nsUnicharUtils.h"
 
 NS_IMPL_NS_NEW_HTML_ELEMENT(Link)
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -974,18 +974,23 @@ void HTMLMediaElement::NotifyLoadError()
   }
 }
 
 void HTMLMediaElement::NotifyMediaTrackEnabled(MediaTrack* aTrack)
 {
   if (!aTrack) {
     return;
   }
-
-  LOG(LogLevel::Debug, ("MediaElement %p MediaStreamTrack %p enabled", this));
+#ifdef DEBUG
+  nsString id;
+  aTrack->GetId(id);
+
+  LOG(LogLevel::Debug, ("MediaElement %p MediaStreamTrack enabled with id %s",
+                        this, NS_ConvertUTF16toUTF8(id).get()));
+#endif
 
   // TODO: We are dealing with single audio track and video track for now.
   if (AudioTrack* track = aTrack->AsAudioTrack()) {
     if (!track->Enabled()) {
       SetMutedInternal(mMuted | MUTED_BY_AUDIO_TRACK);
     } else {
       SetMutedInternal(mMuted & ~MUTED_BY_AUDIO_TRACK);
     }
--- a/dom/html/HTMLStyleElement.cpp
+++ b/dom/html/HTMLStyleElement.cpp
@@ -3,17 +3,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #include "mozilla/dom/HTMLStyleElement.h"
 #include "mozilla/dom/HTMLStyleElementBinding.h"
 #include "nsGkAtoms.h"
 #include "nsStyleConsts.h"
 #include "nsIDOMStyleSheet.h"
-#include "nsIStyleSheet.h"
 #include "nsIDocument.h"
 #include "nsUnicharUtils.h"
 #include "nsThreadUtils.h"
 #include "nsContentUtils.h"
 #include "nsStubMutationObserver.h"
 
 NS_IMPL_NS_NEW_HTML_ELEMENT(Style)
 
--- a/dom/html/nsHTMLDocument.cpp
+++ b/dom/html/nsHTMLDocument.cpp
@@ -2636,22 +2636,22 @@ nsHTMLDocument::TearingDownEditor(nsIEdi
   if (IsEditingOn()) {
     EditingState oldState = mEditingState;
     mEditingState = eTearingDown;
 
     nsCOMPtr<nsIPresShell> presShell = GetShell();
     if (!presShell)
       return;
 
-    nsCOMArray<nsIStyleSheet> agentSheets;
+    nsTArray<RefPtr<CSSStyleSheet>> agentSheets;
     presShell->GetAgentStyleSheets(agentSheets);
 
-    agentSheets.RemoveObject(nsLayoutStylesheetCache::ContentEditableSheet());
+    agentSheets.RemoveElement(nsLayoutStylesheetCache::ContentEditableSheet());
     if (oldState == eDesignMode)
-      agentSheets.RemoveObject(nsLayoutStylesheetCache::DesignModeSheet());
+      agentSheets.RemoveElement(nsLayoutStylesheetCache::DesignModeSheet());
 
     presShell->SetAgentStyleSheets(agentSheets);
 
     presShell->ReconstructStyleData();
   }
 }
 
 nsresult
@@ -2775,48 +2775,44 @@ nsHTMLDocument::EditingStateChanged()
     nsAutoEditingState push(this, eSettingUp);
 
     nsCOMPtr<nsIPresShell> presShell = GetShell();
     NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
 
     // Before making this window editable, we need to modify UA style sheet
     // because new style may change whether focused element will be focusable
     // or not.
-    nsCOMArray<nsIStyleSheet> agentSheets;
+    nsTArray<RefPtr<CSSStyleSheet>> agentSheets;
     rv = presShell->GetAgentStyleSheets(agentSheets);
     NS_ENSURE_SUCCESS(rv, rv);
 
     CSSStyleSheet* contentEditableSheet =
       nsLayoutStylesheetCache::ContentEditableSheet();
 
-    bool result;
-
     if (!agentSheets.Contains(contentEditableSheet)) {
-      bool result = agentSheets.AppendObject(contentEditableSheet);
-      NS_ENSURE_TRUE(result, NS_ERROR_OUT_OF_MEMORY);
+      agentSheets.AppendElement(contentEditableSheet);
     }
 
     // Should we update the editable state of all the nodes in the document? We
     // need to do this when the designMode value changes, as that overrides
     // specific states on the elements.
     if (designMode) {
       // designMode is being turned on (overrides contentEditable).
       CSSStyleSheet* designModeSheet =
         nsLayoutStylesheetCache::DesignModeSheet();
       if (!agentSheets.Contains(designModeSheet)) {
-        result = agentSheets.AppendObject(designModeSheet);
-        NS_ENSURE_TRUE(result, NS_ERROR_OUT_OF_MEMORY);
+        agentSheets.AppendElement(designModeSheet);
       }
 
       updateState = true;
       spellRecheckAll = oldState == eContentEditable;
     }
     else if (oldState == eDesignMode) {
       // designMode is being turned off (contentEditable is still on).
-      agentSheets.RemoveObject(nsLayoutStylesheetCache::DesignModeSheet());
+      agentSheets.RemoveElement(nsLayoutStylesheetCache::DesignModeSheet());
       updateState = true;
     }
 
     rv = presShell->SetAgentStyleSheets(agentSheets);
     NS_ENSURE_SUCCESS(rv, rv);
 
     presShell->ReconstructStyleData();
 
--- a/dom/interfaces/push/nsIPushNotificationService.idl
+++ b/dom/interfaces/push/nsIPushNotificationService.idl
@@ -52,8 +52,26 @@ interface nsIPushNotificationService : n
    */
   jsval clearAll();
 
   /**
    * Clear subscriptions for a domain.
    */
   jsval clearForDomain(in string domain);
 };
+
+[scriptable, uuid(a2555e70-46f8-4b52-bf02-d978b979d143)]
+interface nsIPushQuotaManager : nsISupports
+{
+  /**
+   * Informs the quota manager that a notification
+   * for the given origin has been shown. Used to
+   * determine if push quota should be relaxed.
+   */
+  void notificationForOriginShown(in string origin);
+
+  /**
+   * Informs the quota manager that a notification
+   * for the given origin has been closed. Used to
+   * determine if push quota should be relaxed.
+   */
+  void notificationForOriginClosed(in string origin);
+};
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -31,16 +31,17 @@
 #include "BlobParent.h"
 #include "CrashReporterParent.h"
 #include "GMPServiceParent.h"
 #include "HandlerServiceParent.h"
 #include "IHistory.h"
 #include "imgIContainer.h"
 #include "mozIApplication.h"
 #include "mozilla/ClearOnShutdown.h"
+#include "mozilla/CSSStyleSheet.h"
 #include "mozilla/DataStorage.h"
 #include "mozilla/devtools/HeapSnapshotTempFileHelperParent.h"
 #include "mozilla/docshell/OfflineCacheUpdateParent.h"
 #include "mozilla/dom/DataStoreService.h"
 #include "mozilla/dom/DataTransfer.h"
 #include "mozilla/dom/DOMStorageIPC.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/File.h"
@@ -137,17 +138,16 @@
 #include "nsIMozBrowserFrame.h"
 #include "nsIMutable.h"
 #include "nsIObserverService.h"
 #include "nsIPresShell.h"
 #include "nsIScriptError.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsISiteSecurityService.h"
 #include "nsISpellChecker.h"
-#include "nsIStyleSheet.h"
 #include "nsISupportsPrimitives.h"
 #include "nsISystemMessagesInternal.h"
 #include "nsITimer.h"
 #include "nsIURIFixup.h"
 #include "nsIWindowMediator.h"
 #include "nsIDocShellTreeOwner.h"
 #include "nsIXULWindow.h"
 #include "nsIDOMChromeWindow.h"
@@ -2571,34 +2571,31 @@ ContentParent::InitInternal(ProcessPrior
         Unused << SendAppInit();
     }
 
     nsStyleSheetService *sheetService = nsStyleSheetService::GetInstance();
     if (sheetService) {
         // This looks like a lot of work, but in a normal browser session we just
         // send two loads.
 
-        nsCOMArray<nsIStyleSheet>& agentSheets = *sheetService->AgentStyleSheets();
-        for (uint32_t i = 0; i < agentSheets.Length(); i++) {
+        for (CSSStyleSheet* sheet : *sheetService->AgentStyleSheets()) {
             URIParams uri;
-            SerializeURI(agentSheets[i]->GetSheetURI(), uri);
+            SerializeURI(sheet->GetSheetURI(), uri);
             Unused << SendLoadAndRegisterSheet(uri, nsIStyleSheetService::AGENT_SHEET);
         }
 
-        nsCOMArray<nsIStyleSheet>& userSheets = *sheetService->UserStyleSheets();
-        for (uint32_t i = 0; i < userSheets.Length(); i++) {
+        for (CSSStyleSheet* sheet : *sheetService->UserStyleSheets()) {
             URIParams uri;
-            SerializeURI(userSheets[i]->GetSheetURI(), uri);
+            SerializeURI(sheet->GetSheetURI(), uri);
             Unused << SendLoadAndRegisterSheet(uri, nsIStyleSheetService::USER_SHEET);
         }
 
-        nsCOMArray<nsIStyleSheet>& authorSheets = *sheetService->AuthorStyleSheets();
-        for (uint32_t i = 0; i < authorSheets.Length(); i++) {
+        for (CSSStyleSheet* sheet : *sheetService->AuthorStyleSheets()) {
             URIParams uri;
-            SerializeURI(authorSheets[i]->GetSheetURI(), uri);
+            SerializeURI(sheet->GetSheetURI(), uri);
             Unused << SendLoadAndRegisterSheet(uri, nsIStyleSheetService::AUTHOR_SHEET);
         }
     }
 
 #ifdef MOZ_CONTENT_SANDBOX
     bool shouldSandbox = true;
 #ifdef MOZ_NUWA_PROCESS
     if (IsNuwaProcess()) {
--- a/dom/media/DOMMediaStream.cpp
+++ b/dom/media/DOMMediaStream.cpp
@@ -1039,20 +1039,19 @@ DOMAudioNodeMediaStream::CreateTrackUnio
   stream->InitTrackUnionStream(aWindow, aGraph);
   return stream.forget();
 }
 
 DOMHwMediaStream::DOMHwMediaStream()
 {
 #ifdef MOZ_WIDGET_GONK
   mImageContainer = LayerManager::CreateImageContainer(ImageContainer::ASYNCHRONOUS_OVERLAY);
-  RefPtr<Image> img = mImageContainer->CreateImage(ImageFormat::OVERLAY_IMAGE);
-  mOverlayImage = static_cast<layers::OverlayImage*>(img.get());
+  mOverlayImage = mImageContainer->CreateOverlayImage();
   nsAutoTArray<ImageContainer::NonOwningImage,1> images;
-  images.AppendElement(ImageContainer::NonOwningImage(img));
+  images.AppendElement(ImageContainer::NonOwningImage(mOverlayImage));
   mImageContainer->SetCurrentImages(images);
 #endif
 }
 
 DOMHwMediaStream::~DOMHwMediaStream()
 {
 }
 
--- a/dom/media/MediaData.cpp
+++ b/dom/media/MediaData.cpp
@@ -307,48 +307,49 @@ VideoData::Create(const VideoInfo& aInfo
   const YCbCrBuffer::Plane &Cr = aBuffer.mPlanes[2];
 #endif
 
   if (!aImage) {
     // Currently our decoder only knows how to output to ImageFormat::PLANAR_YCBCR
     // format.
 #ifdef MOZ_WIDGET_GONK
     if (IsYV12Format(Y, Cb, Cr) && !IsInEmulator()) {
-      v->mImage = aContainer->CreateImage(ImageFormat::GRALLOC_PLANAR_YCBCR);
+      v->mImage = new layers::GrallocImage();
     }
 #endif
     if (!v->mImage) {
-      v->mImage = aContainer->CreateImage(ImageFormat::PLANAR_YCBCR);
+      v->mImage = aContainer->CreatePlanarYCbCrImage();
     }
   } else {
     v->mImage = aImage;
   }
 
   if (!v->mImage) {
     return nullptr;
   }
   NS_ASSERTION(v->mImage->GetFormat() == ImageFormat::PLANAR_YCBCR ||
                v->mImage->GetFormat() == ImageFormat::GRALLOC_PLANAR_YCBCR,
                "Wrong format?");
-  PlanarYCbCrImage* videoImage = static_cast<PlanarYCbCrImage*>(v->mImage.get());
+  PlanarYCbCrImage* videoImage = v->mImage->AsPlanarYCbCrImage();
+  MOZ_ASSERT(videoImage);
 
   bool shouldCopyData = (aImage == nullptr);
   if (!VideoData::SetVideoDataToImage(videoImage, aInfo, aBuffer, aPicture,
                                       shouldCopyData)) {
     return nullptr;
   }
 
 #ifdef MOZ_WIDGET_GONK
   if (!videoImage->IsValid() && !aImage && IsYV12Format(Y, Cb, Cr)) {
     // Failed to allocate gralloc. Try fallback.
-    v->mImage = aContainer->CreateImage(ImageFormat::PLANAR_YCBCR);
+    v->mImage = aContainer->CreatePlanarYCbCrImage();
     if (!v->mImage) {
       return nullptr;
     }
-    videoImage = static_cast<PlanarYCbCrImage*>(v->mImage.get());
+    videoImage = v->mImage->AsPlanarYCbCrImage();
     if(!VideoData::SetVideoDataToImage(videoImage, aInfo, aBuffer, aPicture,
                                        true /* aCopyData */)) {
       return nullptr;
     }
   }
 #endif
   return v.forget();
 }
@@ -455,32 +456,19 @@ VideoData::Create(const VideoInfo& aInfo
   RefPtr<VideoData> v(new VideoData(aOffset,
                                       aTime,
                                       aDuration,
                                       aKeyframe,
                                       aTimecode,
                                       aInfo.mDisplay,
                                       0));
 
-  v->mImage = aContainer->CreateImage(ImageFormat::GRALLOC_PLANAR_YCBCR);
-  if (!v->mImage) {
-    return nullptr;
-  }
-  NS_ASSERTION(v->mImage->GetFormat() == ImageFormat::GRALLOC_PLANAR_YCBCR,
-               "Wrong format?");
-  typedef mozilla::layers::GrallocImage GrallocImage;
-  GrallocImage* videoImage = static_cast<GrallocImage*>(v->mImage.get());
-  GrallocImage::GrallocData data;
-
-  data.mPicSize = aPicture.Size();
-  data.mGraphicBuffer = aBuffer;
-
-  if (!videoImage->SetData(data)) {
-    return nullptr;
-  }
+  RefPtr<layers::GrallocImage> image = new layers::GrallocImage();
+  image->SetData(aBuffer, aPicture.Size());
+  v->mImage = image;
 
   return v.forget();
 }
 #endif  // MOZ_OMX_DECODER
 
 // Alignment value - 1. 0 means that data isn't aligned.
 // For 32-bytes aligned, use 31U.
 #define RAW_DATA_ALIGNMENT 31U
--- a/dom/media/MediaStreamGraph.cpp
+++ b/dom/media/MediaStreamGraph.cpp
@@ -823,23 +823,21 @@ MediaStreamGraphImpl::PlayVideo(MediaStr
     // tracks.
     // frameBufferTime is in the non-blocking interval.
     GraphTime frameTime = aStream->StreamTimeToGraphTime(frameBufferTime);
     TimeStamp targetTime = currentTimeStamp +
       TimeDuration::FromSeconds(MediaTimeToSeconds(frameTime - IterationEnd()));
 
     if (frame->GetForceBlack()) {
       if (!blackImage) {
-        blackImage = aStream->mVideoOutputs[0]->
-          GetImageContainer()->CreateImage(ImageFormat::PLANAR_YCBCR);
+        blackImage = aStream->mVideoOutputs[0]->GetImageContainer()->CreatePlanarYCbCrImage();
         if (blackImage) {
           // Sets the image to a single black pixel, which will be scaled to
           // fill the rendered size.
-          SetImageToBlackPixel(static_cast<PlanarYCbCrImage*>
-                               (blackImage.get()));
+          SetImageToBlackPixel(blackImage->AsPlanarYCbCrImage());
         }
       }
       if (blackImage) {
         image = blackImage;
       }
     }
     newImages.AppendElement(ImageContainer::NonOwningImage(image, targetTime));
 
--- a/dom/media/VideoSegment.cpp
+++ b/dom/media/VideoSegment.cpp
@@ -38,27 +38,24 @@ VideoFrame::TakeFrom(VideoFrame* aFrame)
   mIntrinsicSize = aFrame->mIntrinsicSize;
   mForceBlack = aFrame->GetForceBlack();
 }
 
 #if !defined(MOZILLA_XPCOMRT_API)
 /* static */ already_AddRefed<Image>
 VideoFrame::CreateBlackImage(const gfx::IntSize& aSize)
 {
-  RefPtr<ImageContainer> container;
-  RefPtr<Image> image;
-  container = LayerManager::CreateImageContainer();
-  image = container->CreateImage(ImageFormat::PLANAR_YCBCR);
+  RefPtr<ImageContainer> container = LayerManager::CreateImageContainer();
+  RefPtr<PlanarYCbCrImage> image = container->CreatePlanarYCbCrImage();
   if (!image) {
     MOZ_ASSERT(false);
     return nullptr;
   }
 
   int len = ((aSize.width * aSize.height) * 3 / 2);
-  PlanarYCbCrImage* planar = static_cast<PlanarYCbCrImage*>(image.get());
 
   // Generate a black image.
   ScopedDeletePtr<uint8_t> frame(new uint8_t[len]);
   int y = aSize.width * aSize.height;
   // Fill Y plane.
   memset(frame.rwget(), 0x10, y);
   // Fill Cb/Cr planes.
   memset(frame.rwget() + y, 0x80, (len - y));
@@ -75,17 +72,17 @@ VideoFrame::CreateBlackImage(const gfx::
   data.mCrChannel = data.mCbChannel + aSize.height * data.mCbCrStride / 2;
   data.mCbCrSize = gfx::IntSize(aSize.width / 2, aSize.height / 2);
   data.mPicX = 0;
   data.mPicY = 0;
   data.mPicSize = gfx::IntSize(aSize.width, aSize.height);
   data.mStereoMode = StereoMode::MONO;
 
   // SetData copies data, so we can free data.
-  if (!planar->SetData(data)) {
+  if (!image->SetData(data)) {
     MOZ_ASSERT(false);
     return nullptr;
   }
 
   return image.forget();
 }
 #endif // !defined(MOZILLA_XPCOMRT_API)
 
--- a/dom/media/android/AndroidMediaReader.cpp
+++ b/dom/media/android/AndroidMediaReader.cpp
@@ -382,18 +382,18 @@ AndroidMediaReader::ImageBufferCallback:
       return nullptr;
   }
 }
 
 uint8_t *
 AndroidMediaReader::ImageBufferCallback::CreateI420Image(size_t aWidth,
                                                          size_t aHeight)
 {
-  mImage = mImageContainer->CreateImage(ImageFormat::PLANAR_YCBCR);
-  PlanarYCbCrImage *yuvImage = static_cast<PlanarYCbCrImage *>(mImage.get());
+  RefPtr<PlanarYCbCrImage> yuvImage = mImageContainer->CreatePlanarYCbCrImage();
+  mImage = yuvImage;
 
   if (!yuvImage) {
     NS_WARNING("Could not create I420 image");
     return nullptr;
   }
 
   size_t frameSize = aWidth * aHeight;
 
--- a/dom/media/gstreamer/GStreamerReader-0.10.cpp
+++ b/dom/media/gstreamer/GStreamerReader-0.10.cpp
@@ -40,18 +40,17 @@ GstFlowReturn GStreamerReader::AllocateV
                                                        GstBuffer** aBuf,
                                                        RefPtr<PlanarYCbCrImage>& aImage)
 {
   /* allocate an image using the container */
   ImageContainer* container = mDecoder->GetImageContainer();
   if (container == nullptr) {
     return GST_FLOW_ERROR;
   }
-  RefPtr<PlanarYCbCrImage> image =
-    container->CreateImage(ImageFormat::PLANAR_YCBCR).downcast<PlanarYCbCrImage>();
+  RefPtr<PlanarYCbCrImage> image = container->CreatePlanarYCbCrImage();
 
   /* prepare a GstBuffer pointing to the underlying PlanarYCbCrImage buffer */
   GstBuffer* buf = GST_BUFFER(gst_moz_video_buffer_new());
   GST_BUFFER_SIZE(buf) = aSize;
   /* allocate the actual YUV buffer */
   GST_BUFFER_DATA(buf) = image->AllocateAndGetNewBuffer(aSize);
 
   aImage = image;
--- a/dom/media/platforms/android/AndroidDecoderModule.cpp
+++ b/dom/media/platforms/android/AndroidDecoderModule.cpp
@@ -122,24 +122,18 @@ public:
   }
 
   nsresult PostOutput(BufferInfo::Param aInfo, MediaFormat::Param aFormat,
                       const media::TimeUnit& aDuration) override {
     if (!EnsureGLContext()) {
       return NS_ERROR_FAILURE;
     }
 
-    RefPtr<layers::Image> img = mImageContainer->CreateImage(ImageFormat::SURFACE_TEXTURE);
-    layers::SurfaceTextureImage::Data data;
-    data.mSurfTex = mSurfaceTexture.get();
-    data.mSize = mConfig.mDisplay;
-    data.mOriginPos = gl::OriginPos::BottomLeft;
-
-    layers::SurfaceTextureImage* stImg = static_cast<layers::SurfaceTextureImage*>(img.get());
-    stImg->SetData(data);
+    RefPtr<layers::Image> img =
+      new SurfaceTextureImage(mSurfaceTexture.get(), mConfig.mDisplay, gl::OriginPos::BottomLeft);
 
     if (WantCopy()) {
       EGLImage eglImage = CopySurface(img);
       if (!eglImage) {
         return NS_ERROR_FAILURE;
       }
 
       EGLSync eglSync = nullptr;
@@ -151,26 +145,20 @@ public:
                                           LOCAL_EGL_SYNC_FENCE,
                                           nullptr);
         MOZ_ASSERT(eglSync);
         mGLContext->fFlush();
       } else {
         NS_WARNING("No EGL fence support detected, rendering artifacts may occur!");
       }
 
-      img = mImageContainer->CreateImage(ImageFormat::EGLIMAGE);
-      layers::EGLImageImage::Data data;
-      data.mImage = eglImage;
-      data.mSync = eglSync;
-      data.mOwns = true;
-      data.mSize = mConfig.mDisplay;
-      data.mOriginPos = gl::OriginPos::TopLeft;
-
-      layers::EGLImageImage* typedImg = static_cast<layers::EGLImageImage*>(img.get());
-      typedImg->SetData(data);
+      img = new layers::EGLImageImage(
+        eglImage, eglSync,
+        mConfig.mDisplay, gl::OriginPos::TopLeft,
+        true /* owns */);
     }
 
     nsresult rv;
     int32_t flags;
     NS_ENSURE_SUCCESS(rv = aInfo->Flags(&flags), rv);
 
     bool isSync = !!(flags & MediaCodec::BUFFER_FLAG_SYNC_FRAME);
 
--- a/dom/media/platforms/apple/AppleVDADecoder.cpp
+++ b/dom/media/platforms/apple/AppleVDADecoder.cpp
@@ -392,21 +392,17 @@ AppleVDADecoder::OutputFrame(CVPixelBuff
     CVPixelBufferUnlockBaseAddress(aImage, kCVPixelBufferLock_ReadOnly);
   } else {
 #ifndef MOZ_WIDGET_UIKIT
     IOSurfacePtr surface = MacIOSurfaceLib::CVPixelBufferGetIOSurface(aImage);
     MOZ_ASSERT(surface, "Decoder didn't return an IOSurface backed buffer");
 
     RefPtr<MacIOSurface> macSurface = new MacIOSurface(surface);
 
-    RefPtr<layers::Image> image =
-      mImageContainer->CreateImage(ImageFormat::MAC_IOSURFACE);
-    layers::MacIOSurfaceImage* videoImage =
-      static_cast<layers::MacIOSurfaceImage*>(image.get());
-    videoImage->SetSurface(macSurface);
+    RefPtr<layers::Image> image = new MacIOSurfaceImage(macSurface);
 
     data =
       VideoData::CreateFromImage(info,
                                  mImageContainer,
                                  aFrameRef.byte_offset,
                                  aFrameRef.composition_timestamp.ToMicroseconds(),
                                  aFrameRef.duration.ToMicroseconds(),
                                  image.forget(),
--- a/dom/media/platforms/ffmpeg/FFmpegH264Decoder.cpp
+++ b/dom/media/platforms/ffmpeg/FFmpegH264Decoder.cpp
@@ -326,20 +326,18 @@ FFmpegH264Decoder<LIBAV_VER>::AllocateYU
   int chroma_height = (decodeHeight +1) / 2;
 
   // Get strides for each plane.
   aFrame->linesize[0] = pitch;
   aFrame->linesize[1] = aFrame->linesize[2] = chroma_pitch;
 
   size_t allocSize = pitch * decodeHeight + (chroma_pitch * chroma_height) * 2;
 
-  RefPtr<Image> image =
-    mImageContainer->CreateImage(ImageFormat::PLANAR_YCBCR);
-  PlanarYCbCrImage* ycbcr = static_cast<PlanarYCbCrImage*>(image.get());
-  uint8_t* buffer = ycbcr->AllocateAndGetNewBuffer(allocSize + 64);
+  RefPtr<PlanarYCbCrImage> image = mImageContainer->CreatePlanarYCbCrImage();
+  uint8_t* buffer = image->AllocateAndGetNewBuffer(allocSize + 64);
   // FFmpeg requires a 16/32 bytes-aligned buffer, align it on 64 to be safe
   buffer = reinterpret_cast<uint8_t*>((reinterpret_cast<uintptr_t>(buffer) + 63) & ~63);
 
   if (!buffer) {
     NS_WARNING("Failed to allocate buffer for FFmpeg video decoding");
     return -1;
   }
 
--- a/dom/media/platforms/wmf/DXVA2Manager.cpp
+++ b/dom/media/platforms/wmf/DXVA2Manager.cpp
@@ -415,27 +415,23 @@ D3D9DXVA2Manager::CopyToImage(IMFSample*
 
   RefPtr<IDirect3DSurface9> surface;
   hr = wmf::MFGetService(buffer,
                          MR_BUFFER_SERVICE,
                          IID_IDirect3DSurface9,
                          getter_AddRefs(surface));
   NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
 
-  RefPtr<Image> image = aImageContainer->CreateImage(ImageFormat::D3D9_RGB32_TEXTURE);
-  NS_ENSURE_TRUE(image, E_FAIL);
-  NS_ASSERTION(image->GetFormat() == ImageFormat::D3D9_RGB32_TEXTURE,
-               "Wrong format?");
+  RefPtr<D3D9SurfaceImage> image = new D3D9SurfaceImage(mFirstFrame);
+  hr = image->AllocateAndCopy(mTextureClientAllocator, surface, aRegion);
+  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
 
-  D3D9SurfaceImage* videoImage = static_cast<D3D9SurfaceImage*>(image.get());
-  hr = videoImage->SetData(D3D9SurfaceImage::Data(surface, aRegion, mTextureClientAllocator, mFirstFrame));
   mFirstFrame = false;
 
   image.forget(aOutImage);
-
   return S_OK;
 }
 
 // Count of the number of DXVAManager's we've created. This is also the
 // number of videos we're decoding with DXVA. Use on main thread only.
 static uint32_t sDXVAVideosCount = 0;
 
 /* static */
@@ -709,33 +705,26 @@ D3D11DXVA2Manager::CopyToImage(IMFSample
   NS_ENSURE_TRUE(aVideoSample, E_POINTER);
   NS_ENSURE_TRUE(aContainer, E_POINTER);
   NS_ENSURE_TRUE(aOutImage, E_POINTER);
 
   // Our video frame is stored in a non-sharable ID3D11Texture2D. We need
   // to create a copy of that frame as a sharable resource, save its share
   // handle, and put that handle into the rendering pipeline.
 
-  ImageFormat format = ImageFormat::D3D11_SHARE_HANDLE_TEXTURE;
-  RefPtr<Image> image(aContainer->CreateImage(format));
-  NS_ENSURE_TRUE(image, E_FAIL);
-  NS_ASSERTION(image->GetFormat() == ImageFormat::D3D11_SHARE_HANDLE_TEXTURE,
-               "Wrong format?");
+  RefPtr<D3D11ShareHandleImage> image =
+    new D3D11ShareHandleImage(gfx::IntSize(mWidth, mHeight), aRegion);
+  bool ok = image->AllocateTexture(mTextureClientAllocator);
+  NS_ENSURE_TRUE(ok, E_FAIL);
 
-  D3D11ShareHandleImage* videoImage = static_cast<D3D11ShareHandleImage*>(image.get());
-  HRESULT hr = videoImage->SetData(D3D11ShareHandleImage::Data(mTextureClientAllocator,
-                                                               gfx::IntSize(mWidth, mHeight),
-                                                               aRegion));
-  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
-
-  hr = mTransform->Input(aVideoSample);
+  HRESULT hr = mTransform->Input(aVideoSample);
   NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
 
   RefPtr<IMFSample> sample;
-  RefPtr<ID3D11Texture2D> texture = videoImage->GetTexture();
+  RefPtr<ID3D11Texture2D> texture = image->GetTexture();
   hr = CreateOutputSample(sample, texture);
   NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
 
   RefPtr<IDXGIKeyedMutex> keyedMutex;
   hr = texture->QueryInterface(static_cast<IDXGIKeyedMutex**>(getter_AddRefs(keyedMutex)));
   NS_ENSURE_TRUE(SUCCEEDED(hr) && keyedMutex, hr);
 
   hr = keyedMutex->AcquireSync(0, INFINITE);
--- a/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
+++ b/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
@@ -362,22 +362,18 @@ WMFVideoMFTManager::ConfigureVideoFrameG
   // we use YV12, as that's easier for us to stick into our rendering
   // pipeline than NV12. NV12 has interleaved UV samples, whereas YV12
   // is a planar format.
   GUID videoFormat;
   hr = mediaType->GetGUID(MF_MT_SUBTYPE, &videoFormat);
   NS_ENSURE_TRUE(videoFormat == MFVideoFormat_NV12 || !mUseHwAccel, E_FAIL);
   NS_ENSURE_TRUE(videoFormat == MFVideoFormat_YV12 || mUseHwAccel, E_FAIL);
 
-  UINT32 width = 0, height = 0;
-  hr = MFGetAttributeSize(mediaType, MF_MT_FRAME_SIZE, &width, &height);
-  NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
-
-  mVideoInfo.mImage.width = width;
-  mVideoInfo.mImage.height = height;
+  UINT32 width = mVideoInfo.mImage.width;
+  UINT32 height = mVideoInfo.mImage.height;
   nsIntRect pictureRegion = mVideoInfo.mImage;
   // Calculate and validate the picture region and frame dimensions after
   // scaling by the pixel aspect ratio.
   nsIntSize frameSize = nsIntSize(width, height);
   nsIntSize displaySize = nsIntSize(mVideoInfo.mDisplay.width, mVideoInfo.mDisplay.height);
   if (!IsValidVideoRegion(frameSize, pictureRegion, displaySize)) {
     // Video track's frame sizes will overflow. Ignore the video track.
     return E_FAIL;
--- a/dom/media/tests/mochitest/mochitest.ini
+++ b/dom/media/tests/mochitest/mochitest.ini
@@ -67,16 +67,18 @@ skip-if = (toolkit == 'gonk' || buildapp
 [test_getUserMedia_stopVideoStreamWithFollowupVideo.html]
 [test_getUserMedia_peerIdentity.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 1021776, too --ing slow on b2g)
 [test_peerConnection_addIceCandidate.html]
 [test_peerConnection_basicAudio.html]
 skip-if = toolkit == 'gonk' # B2G emulator is too slow to handle a two-way audio call reliably
 [test_peerConnection_basicAudioRequireEOC.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
+[test_peerConnection_basicAudioPcmaPcmuOnly.html]
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 [test_peerConnection_basicAudioVideo.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' || (android_version == '18' && debug) # b2g(Bug 960442, video support for WebRTC is disabled on b2g), android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_basicAudioVideoCombined.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' || (android_version == '18' && debug) # b2g(Bug 960442, video support for WebRTC is disabled on b2g), android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_basicAudioVideoNoBundle.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' || (android_version == '18' && debug) # b2g(Bug 960442, video support for WebRTC is disabled on b2g), android(Bug 1189784, timeouts on 4.3 emulator)
 [test_peerConnection_basicAudioVideoNoBundleNoRtcpMux.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' || android_version == '18' # b2g(Bug 960442, video support for WebRTC is disabled on b2g), android(Bug 1189784, timeouts on 4.3 emulator)
--- a/dom/media/tests/mochitest/pc.js
+++ b/dom/media/tests/mochitest/pc.js
@@ -57,16 +57,17 @@ function PeerConnectionTest(options) {
   options = options || { };
   options.commands = options.commands || makeDefaultCommands();
   options.is_local = "is_local" in options ? options.is_local : true;
   options.is_remote = "is_remote" in options ? options.is_remote : true;
 
   options.h264 = "h264" in options ? options.h264 : false;
   options.bundle = "bundle" in options ? options.bundle : true;
   options.rtcpmux = "rtcpmux" in options ? options.rtcpmux : true;
+  options.opus = "opus" in options ? options.opus : true;
 
   if (typeof turnServers !== "undefined") {
     if ((!options.turn_disabled_local) && (turnServers.local)) {
       if (!options.hasOwnProperty("config_local")) {
         options.config_local = {};
       }
       if (!options.config_local.hasOwnProperty("iceServers")) {
         options.config_local.iceServers = turnServers.local.iceServers;
--- a/dom/media/tests/mochitest/sdpUtils.js
+++ b/dom/media/tests/mochitest/sdpUtils.js
@@ -40,16 +40,24 @@ removeVP8: function(sdp) {
 removeRtcpMux: function(sdp) {
   return sdp.replace(/a=rtcp-mux\r\n/g,"");
 },
 
 removeBundle: function(sdp) {
   return sdp.replace(/a=group:BUNDLE .*\r\n/g, "");
 },
 
+reduceAudioMLineToPcmuPcma: function(sdp) {
+  return sdp.replace(/m=audio .*\r\n/g, "m=audio 9 UDP/TLS/RTP/SAVPF 0 8\r\n");
+},
+
+removeAllRtpMaps: function(sdp) {
+  return sdp.replace(/a=rtpmap:.*\r\n/g, "");
+},
+
 verifySdp: function(desc, expectedType, offerConstraintsList, offerOptions,
                     testOptions) {
   info("Examining this SessionDescription: " + JSON.stringify(desc));
   info("offerConstraintsList: " + JSON.stringify(offerConstraintsList));
   info("offerOptions: " + JSON.stringify(offerOptions));
   ok(desc, "SessionDescription is not null");
   is(desc.type, expectedType, "SessionDescription type is " + expectedType);
   ok(desc.sdp.length > 10, "SessionDescription body length is plausible");
@@ -71,17 +79,17 @@ verifySdp: function(desc, expectedType, 
       sdputils.countTracksInConstraint('audio', offerConstraintsList) ||
       ((offerOptions && offerOptions.offerToReceiveAudio) ? 1 : 0);
 
   info("expected audio tracks: " + audioTracks);
   if (audioTracks == 0) {
     ok(!desc.sdp.includes("m=audio"), "audio m-line is absent from SDP");
   } else {
     ok(desc.sdp.includes("m=audio"), "audio m-line is present in SDP");
-    ok(desc.sdp.includes("a=rtpmap:109 opus/48000/2"), "OPUS codec is present in SDP");
+    is(testOptions.opus, desc.sdp.includes("a=rtpmap:109 opus/48000/2"), "OPUS codec is present in SDP");
     //TODO: ideally the rtcp-mux should be for the m=audio, and not just
     //      anywhere in the SDP (JS SDP parser bug 1045429)
     is(testOptions.rtcpmux, desc.sdp.includes("a=rtcp-mux"), "RTCP Mux is offered in SDP");
   }
 
   var videoTracks =
       sdputils.countTracksInConstraint('video', offerConstraintsList) ||
       ((offerOptions && offerOptions.offerToReceiveVideo) ? 1 : 0);
copy from dom/media/tests/mochitest/test_peerConnection_basicAudio.html
copy to dom/media/tests/mochitest/test_peerConnection_basicAudioPcmaPcmuOnly.html
--- a/dom/media/tests/mochitest/test_peerConnection_basicAudio.html
+++ b/dom/media/tests/mochitest/test_peerConnection_basicAudioPcmaPcmuOnly.html
@@ -3,21 +3,32 @@
 <head>
   <script type="application/javascript" src="pc.js"></script>
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
   createHTML({
     bug: "796892",
-    title: "Basic audio-only peer connection"
+    title: "Only offer PCMA and PMCU in mline (no rtpmaps)"
   });
 
   var test;
   runNetworkTest(function (options) {
+    options = options || { };
+    options.opus = false;
     test = new PeerConnectionTest(options);
+    test.chain.insertBefore("PC_REMOTE_GET_OFFER", [
+      function PC_LOCAL_REDUCE_MLINE_REMOVE_RTPMAPS(test) {
+        test.originalOffer.sdp =
+          sdputils.reduceAudioMLineToPcmuPcma(test.originalOffer.sdp);
+        test.originalOffer.sdp =
+          sdputils.removeAllRtpMaps(test.originalOffer.sdp);
+        info("SDP without Rtpmaps: " + JSON.stringify(test.originalOffer));
+      }
+    ]);
     test.setMediaConstraints([{audio: true}], [{audio: true}]);
     test.run();
   });
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/webrtc/MediaEngineDefault.cpp
+++ b/dom/media/webrtc/MediaEngineDefault.cpp
@@ -231,19 +231,17 @@ MediaEngineDefaultVideoSource::Notify(ns
     } else {
       mCr--;
     }
   } else {
     mCr--;
   }
 
   // Allocate a single solid color image
-  RefPtr<layers::Image> image = mImageContainer->CreateImage(ImageFormat::PLANAR_YCBCR);
-  RefPtr<layers::PlanarYCbCrImage> ycbcr_image =
-      static_cast<layers::PlanarYCbCrImage*>(image.get());
+  RefPtr<layers::PlanarYCbCrImage> ycbcr_image = mImageContainer->CreatePlanarYCbCrImage();
   layers::PlanarYCbCrData data;
   AllocateSolidColorFrame(data, mOpts.mWidth, mOpts.mHeight, 0x80, mCb, mCr);
 
 #ifdef MOZ_WEBRTC
   uint64_t timestamp = PR_Now();
   YuvStamper::Encode(mOpts.mWidth, mOpts.mHeight, mOpts.mWidth,
 		     data.mYChannel,
 		     reinterpret_cast<unsigned char*>(&timestamp), sizeof(timestamp),
--- a/dom/media/webrtc/MediaEngineGonkVideoSource.cpp
+++ b/dom/media/webrtc/MediaEngineGonkVideoSource.cpp
@@ -733,44 +733,42 @@ MediaEngineGonkVideoSource::RotateImage(
   void *pMem = nullptr;
   // Bug 1109957 size will be wrong if width or height are odd
   uint32_t size = aWidth * aHeight * 3 / 2;
   MOZ_ASSERT(!(aWidth & 1) && !(aHeight & 1));
 
   graphicBuffer->lock(GraphicBuffer::USAGE_SW_READ_MASK, &pMem);
 
   uint8_t* srcPtr = static_cast<uint8_t*>(pMem);
+
   // Create a video frame and append it to the track.
-  ImageFormat format = ImageFormat::GONK_CAMERA_IMAGE;
-  RefPtr<layers::Image> image = mImageContainer->CreateImage(format);
+  RefPtr<layers::PlanarYCbCrImage> image = new GonkCameraImage();
 
   uint32_t dstWidth;
   uint32_t dstHeight;
 
   if (mRotation == 90 || mRotation == 270) {
     dstWidth = aHeight;
     dstHeight = aWidth;
   } else {
     dstWidth = aWidth;
     dstHeight = aHeight;
   }
 
   uint32_t half_width = dstWidth / 2;
 
-  layers::GrallocImage* videoImage = static_cast<layers::GrallocImage*>(image.get());
   MOZ_ASSERT(mTextureClientAllocator);
   RefPtr<layers::TextureClient> textureClient
     = mTextureClientAllocator->CreateOrRecycle(gfx::SurfaceFormat::YUV,
                                                gfx::IntSize(dstWidth, dstHeight),
                                                layers::BackendSelector::Content,
                                                layers::TextureFlags::DEFAULT,
                                                layers::ALLOC_DISALLOW_BUFFERTEXTURECLIENT);
   if (textureClient) {
-    RefPtr<layers::GrallocTextureClientOGL> grallocTextureClient =
-      static_cast<layers::GrallocTextureClientOGL*>(textureClient.get());
+    RefPtr<layers::GrallocTextureClientOGL> grallocTextureClient = textureClient->AsGrallocTextureClientOGL();
 
     android::sp<android::GraphicBuffer> destBuffer = grallocTextureClient->GetGraphicBuffer();
 
     void* destMem = nullptr;
     destBuffer->lock(android::GraphicBuffer::USAGE_SW_WRITE_OFTEN, &destMem);
     uint8_t* dstPtr = static_cast<uint8_t*>(destMem);
 
     int32_t yStride = destBuffer->getStride();
@@ -783,26 +781,21 @@ MediaEngineGonkVideoSource::RotateImage(
                           dstPtr + (yStride * dstHeight), uvStride,
                           0, 0,
                           graphicBuffer->getStride(), aHeight,
                           aWidth, aHeight,
                           static_cast<libyuv::RotationMode>(mRotation),
                           libyuv::FOURCC_NV21);
     destBuffer->unlock();
 
-    layers::GrallocImage::GrallocData data;
-
-    data.mPicSize = gfx::IntSize(dstWidth, dstHeight);
-    data.mGraphicBuffer = textureClient;
-    videoImage->SetData(data);
+    image->AsGrallocImage()->SetData(textureClient, gfx::IntSize(dstWidth, dstHeight));
   } else {
     // Handle out of gralloc case.
-    image = mImageContainer->CreateImage(ImageFormat::PLANAR_YCBCR);
-    layers::PlanarYCbCrImage* videoImage = static_cast<layers::PlanarYCbCrImage*>(image.get());
-    uint8_t* dstPtr = videoImage->AllocateAndGetNewBuffer(size);
+    image = mImageContainer->CreatePlanarYCbCrImage();
+    uint8_t* dstPtr = image->AsPlanarYCbCrImage()->AllocateAndGetNewBuffer(size);
 
     libyuv::ConvertToI420(srcPtr, size,
                           dstPtr, dstWidth,
                           dstPtr + (dstWidth * dstHeight), half_width,
                           dstPtr + (dstWidth * dstHeight * 5 / 4), half_width,
                           0, 0,
                           graphicBuffer->getStride(), aHeight,
                           aWidth, aHeight,
@@ -820,17 +813,17 @@ MediaEngineGonkVideoSource::RotateImage(
     data.mCbChannel = dstPtr + dstHeight * data.mYStride;
     data.mCrChannel = data.mCbChannel + data.mCbCrStride * (dstHeight / 2);
     data.mCbCrSize = IntSize(dstWidth / 2, dstHeight / 2);
     data.mPicX = 0;
     data.mPicY = 0;
     data.mPicSize = IntSize(dstWidth, dstHeight);
     data.mStereoMode = StereoMode::MONO;
 
-    videoImage->SetDataNoCopy(data);
+    image->AsPlanarYCbCrImage()->SetDataNoCopy(data);
   }
   graphicBuffer->unlock();
 
   // Implicitly releases last preview image.
   mImage = image.forget();
 }
 
 bool
--- a/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp
+++ b/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp
@@ -289,18 +289,17 @@ MediaEngineRemoteVideoSource::DeliverFra
   }
 
   if (mWidth*mHeight + 2*(((mWidth+1)/2)*((mHeight+1)/2)) != size) {
     MOZ_ASSERT(false, "Wrong size frame in DeliverFrame!");
     return 0;
   }
 
   // Create a video frame and append it to the track.
-  RefPtr<layers::Image> image = mImageContainer->CreateImage(ImageFormat::PLANAR_YCBCR);
-  layers::PlanarYCbCrImage* videoImage = static_cast<layers::PlanarYCbCrImage*>(image.get());
+  RefPtr<layers::PlanarYCbCrImage> image = mImageContainer->CreatePlanarYCbCrImage();
 
   uint8_t* frame = static_cast<uint8_t*> (buffer);
   const uint8_t lumaBpp = 8;
   const uint8_t chromaBpp = 4;
 
   // Take lots of care to round up!
   layers::PlanarYCbCrData data;
   data.mYChannel = frame;
@@ -310,17 +309,17 @@ MediaEngineRemoteVideoSource::DeliverFra
   data.mCbChannel = frame + mHeight * data.mYStride;
   data.mCrChannel = data.mCbChannel + ((mHeight+1)/2) * data.mCbCrStride;
   data.mCbCrSize = IntSize((mWidth+1)/ 2, (mHeight+1)/ 2);
   data.mPicX = 0;
   data.mPicY = 0;
   data.mPicSize = IntSize(mWidth, mHeight);
   data.mStereoMode = StereoMode::MONO;
 
-  if (!videoImage->SetData(data)) {
+  if (!image->SetData(data)) {
     MOZ_ASSERT(false);
     return 0;
   }
 
 #ifdef DEBUG
   static uint32_t frame_num = 0;
   LOGFRAME(("frame %d (%dx%d); timestamp %u, ntp_time %" PRIu64 ", render_time %" PRIu64,
             frame_num++, mWidth, mHeight, time_stamp, ntp_time, render_time));
--- a/dom/media/webrtc/MediaEngineTabVideoSource.cpp
+++ b/dom/media/webrtc/MediaEngineTabVideoSource.cpp
@@ -293,23 +293,17 @@ MediaEngineTabVideoSource::Draw() {
 
   NS_ENSURE_SUCCESS_VOID(presShell->RenderDocument(r, renderDocFlags, bgColor, context));
 
   RefPtr<SourceSurface> surface = dt->Snapshot();
   if (!surface) {
     return;
   }
 
-  layers::CairoImage::Data cairoData;
-  cairoData.mSize = size;
-  cairoData.mSourceSurface = surface;
-
-  RefPtr<layers::CairoImage> image = new layers::CairoImage();
-
-  image->SetData(cairoData);
+  RefPtr<layers::CairoImage> image = new layers::CairoImage(size, surface);
 
   MonitorAutoLock mon(mMonitor);
   mImage = image;
 }
 
 nsresult
 MediaEngineTabVideoSource::Stop(mozilla::SourceMediaStream*, mozilla::TrackID)
 {
--- a/dom/notification/Notification.cpp
+++ b/dom/notification/Notification.cpp
@@ -46,16 +46,20 @@
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
 #include "WorkerScope.h"
 
 #ifdef MOZ_B2G
 #include "nsIDOMDesktopNotification.h"
 #endif
 
+#ifndef MOZ_SIMPLEPUSH
+#include "nsIPushNotificationService.h"
+#endif
+
 namespace mozilla {
 namespace dom {
 
 using namespace workers;
 
 struct NotificationStrings
 {
   const nsString mID;
@@ -673,16 +677,18 @@ public:
     MOZ_ASSERT(mPrincipal);
   }
 
 protected:
   virtual ~NotificationObserver()
   {
     AssertIsOnMainThread();
   }
+
+  nsresult AdjustPushQuota(const char* aTopic);
 };
 
 NS_IMPL_ISUPPORTS(NotificationObserver, nsIObserver)
 
 class MainThreadNotificationObserver : public nsIObserver
 {
 public:
   UniquePtr<NotificationRef> mNotificationRef;
@@ -1168,21 +1174,49 @@ NotificationObserver::Observe(nsISupport
     if (XRE_IsParentProcess()) {
       return Notification::OpenSettings(mPrincipal);
     }
     // `ContentParent::RecvOpenNotificationSettings` notifies observers in the
     // parent process.
     ContentChild::GetSingleton()->SendOpenNotificationSettings(
       IPC::Principal(mPrincipal));
     return NS_OK;
+  } else if (!strcmp("alertshow", aTopic) ||
+             !strcmp("alertfinished", aTopic)) {
+    Unused << NS_WARN_IF(NS_FAILED(AdjustPushQuota(aTopic)));
   }
 
   return mObserver->Observe(aSubject, aTopic, aData);
 }
 
+nsresult
+NotificationObserver::AdjustPushQuota(const char* aTopic)
+{
+#ifdef MOZ_SIMPLEPUSH
+  return NS_ERROR_NOT_IMPLEMENTED;
+#else
+  nsCOMPtr<nsIPushQuotaManager> pushQuotaManager =
+    do_GetService("@mozilla.org/push/NotificationService;1");
+  if (!pushQuotaManager) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsAutoCString origin;
+  nsresult rv = mPrincipal->GetOrigin(origin);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  if (!strcmp("alertshow", aTopic)) {
+    return pushQuotaManager->NotificationForOriginShown(origin.get());
+  }
+  return pushQuotaManager->NotificationForOriginClosed(origin.get());
+#endif
+}
+
 NS_IMETHODIMP
 MainThreadNotificationObserver::Observe(nsISupports* aSubject, const char* aTopic,
                                         const char16_t* aData)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(mNotificationRef);
   Notification* notification = mNotificationRef->GetNotification();
   MOZ_ASSERT(notification);
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -174,26 +174,20 @@ AttachToContainerAsEGLImage(ImageContain
   MOZ_ASSERT(out_image);
   MOZ_ASSERT(!*out_image);
 
   EGLImage image = instance->AsEGLImage();
   if (!image) {
     return;
   }
 
-  RefPtr<Image> img = container->CreateImage(ImageFormat::EGLIMAGE);
-
-  EGLImageImage::Data data;
-  data.mImage = image;
-  data.mSize = gfx::IntSize(rect.width, rect.height);
-  data.mOriginPos = instance->OriginPos();
-
-  EGLImageImage* typedImg = static_cast<EGLImageImage*>(img.get());
-  typedImg->SetData(data);
-
+  RefPtr<EGLImageImage> img = new EGLImageImage(
+    image, nullptr,
+    gfx::IntSize(rect.width, rect.height), instance->OriginPos(),
+    false /* owns */);
   *out_image = img;
 }
 
 static void
 AttachToContainerAsSurfaceTexture(ImageContainer* container,
                                   nsNPAPIPluginInstance* instance,
                                   const LayoutDeviceRect& rect,
                                   RefPtr<Image>* out_image)
@@ -201,26 +195,20 @@ AttachToContainerAsSurfaceTexture(ImageC
   MOZ_ASSERT(out_image);
   MOZ_ASSERT(!*out_image);
 
   mozilla::gl::AndroidSurfaceTexture* surfTex = instance->AsSurfaceTexture();
   if (!surfTex) {
     return;
   }
 
-  RefPtr<Image> img = container->CreateImage(ImageFormat::SURFACE_TEXTURE);
-
-  SurfaceTextureImage::Data data;
-  data.mSurfTex = surfTex;
-  data.mSize = gfx::IntSize(rect.width, rect.height);
-  data.mOriginPos = instance->OriginPos();
-
-  SurfaceTextureImage* typedImg = static_cast<SurfaceTextureImage*>(img.get());
-  typedImg->SetData(data);
-
+  RefPtr<Image> img = new SurfaceTextureImage(
+    surfTex,
+    gfx::IntSize(rect.width, rect.height),
+    instance->OriginPos());
   *out_image = img;
 }
 #endif
 
 already_AddRefed<ImageContainer>
 nsPluginInstanceOwner::GetImageContainer()
 {
   if (!mInstance)
@@ -1334,26 +1322,20 @@ nsPluginInstanceOwner::GetVideos(nsTArra
   mInstance->GetVideos(aVideos);
 }
 
 already_AddRefed<ImageContainer>
 nsPluginInstanceOwner::GetImageContainerForVideo(nsNPAPIPluginInstance::VideoInfo* aVideoInfo)
 {
   RefPtr<ImageContainer> container = LayerManager::CreateImageContainer();
 
-  RefPtr<Image> img = container->CreateImage(ImageFormat::SURFACE_TEXTURE);
-
-  SurfaceTextureImage::Data data;
-  data.mSurfTex = aVideoInfo->mSurfaceTexture;
-  data.mOriginPos = gl::OriginPos::BottomLeft;
-  data.mSize = gfx::IntSize(aVideoInfo->mDimensions.width, aVideoInfo->mDimensions.height);
-
-  SurfaceTextureImage* typedImg = static_cast<SurfaceTextureImage*>(img.get());
-  typedImg->SetData(data);
-
+  RefPtr<Image> img = new SurfaceTextureImage(
+    aVideoInfo->mSurfaceTexture,
+    gfx::IntSize(aVideoInfo->mDimensions.width, aVideoInfo->mDimensions.height),
+    gl::OriginPos::BottomLeft);
   container->SetCurrentImageInTransaction(img);
 
   return container.forget();
 }
 
 void nsPluginInstanceOwner::Invalidate() {
   NPRect rect;
   rect.left = rect.top = 0;
--- a/dom/plugins/ipc/PluginInstanceParent.cpp
+++ b/dom/plugins/ipc/PluginInstanceParent.cpp
@@ -610,28 +610,25 @@ PluginInstanceParent::RecvShow(const NPR
     if (surface) {
         // Notify the cairo backend that this surface has changed behind
         // its back.
         gfxRect ur(updatedRect.left, updatedRect.top,
                    updatedRect.right - updatedRect.left,
                    updatedRect.bottom - updatedRect.top);
         surface->MarkDirty(ur);
 
-        ImageContainer *container = GetImageContainer();
-        RefPtr<Image> image = container->CreateImage(ImageFormat::CAIRO_SURFACE);
-        NS_ASSERTION(image->GetFormat() == ImageFormat::CAIRO_SURFACE, "Wrong format?");
-        CairoImage* cairoImage = static_cast<CairoImage*>(image.get());
-        CairoImage::Data cairoData;
-        cairoData.mSize = surface->GetSize();
-        cairoData.mSourceSurface = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(nullptr, surface);
-        cairoImage->SetData(cairoData);
+        RefPtr<gfx::SourceSurface> sourceSurface =
+            gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(nullptr, surface);
+        RefPtr<CairoImage> image = new CairoImage(surface->GetSize(), sourceSurface);
 
         nsAutoTArray<ImageContainer::NonOwningImage,1> imageList;
         imageList.AppendElement(
             ImageContainer::NonOwningImage(image));
+
+        ImageContainer *container = GetImageContainer();
         container->SetCurrentImages(imageList);
     }
     else if (mImageContainer) {
         mImageContainer->ClearAllImages();
     }
 
     mFrontSurface = surface;
     RecvNPN_InvalidateRect(updatedRect);
@@ -692,27 +689,18 @@ PluginInstanceParent::GetImageContainer(
     ImageContainer *container = GetImageContainer();
 
     if (!container) {
         return NS_ERROR_FAILURE;
     }
 
 #ifdef XP_MACOSX
     if (ioSurface) {
-        RefPtr<Image> image = container->CreateImage(ImageFormat::MAC_IOSURFACE);
-        if (!image) {
-            return NS_ERROR_FAILURE;
-        }
-
-        NS_ASSERTION(image->GetFormat() == ImageFormat::MAC_IOSURFACE, "Wrong format?");
-
-        MacIOSurfaceImage* pluginImage = static_cast<MacIOSurfaceImage*>(image.get());
-        pluginImage->SetSurface(ioSurface);
-
-        container->SetCurrentImageInTransaction(pluginImage);
+        RefPtr<Image> image = new MacIOSurfaceImage(ioSurface);
+        container->SetCurrentImageInTransaction(image);
 
         NS_IF_ADDREF(container);
         *aContainer = container;
         return NS_OK;
     }
 #endif
 
     NS_IF_ADDREF(container);
--- a/dom/plugins/ipc/PluginProcessParent.cpp
+++ b/dom/plugins/ipc/PluginProcessParent.cpp
@@ -69,17 +69,18 @@ AddSandboxAllowedFile(vector<std::wstrin
     }
     aAllowedFiles.push_back(std::wstring(userDirPath.get()));
     return;
 }
 
 static void
 AddSandboxAllowedFiles(int32_t aSandboxLevel,
                        vector<std::wstring>& aAllowedFilesRead,
-                       vector<std::wstring>& aAllowedFilesReadWrite)
+                       vector<std::wstring>& aAllowedFilesReadWrite,
+                       vector<std::wstring>& aAllowedDirectories)
 {
     if (aSandboxLevel < 2) {
         return;
     }
 
     nsresult rv;
     nsCOMPtr<nsIProperties> dirSvc =
         do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
@@ -90,39 +91,48 @@ AddSandboxAllowedFiles(int32_t aSandboxL
     // Higher than level 2 currently removes the users own rights.
     if (aSandboxLevel > 2) {
         AddSandboxAllowedFile(aAllowedFilesRead, dirSvc, NS_WIN_HOME_DIR);
         AddSandboxAllowedFile(aAllowedFilesRead, dirSvc, NS_WIN_HOME_DIR,
                               NS_LITERAL_STRING("\\*"));
     }
 
     // Level 2 and above is now using low integrity, so we need to give write
-    // access to the Flash directories.
+    // access to the Flash directories. Access also has to be given to create
+    // the parent directories as they may not exist.
     // This should be made Flash specific (Bug 1171396).
     AddSandboxAllowedFile(aAllowedFilesReadWrite, dirSvc, NS_WIN_APPDATA_DIR,
                           NS_LITERAL_STRING("\\Macromedia\\Flash Player\\*"));
+    AddSandboxAllowedFile(aAllowedDirectories, dirSvc, NS_WIN_APPDATA_DIR,
+                          NS_LITERAL_STRING("\\Macromedia\\Flash Player"));
+    AddSandboxAllowedFile(aAllowedDirectories, dirSvc, NS_WIN_APPDATA_DIR,
+                          NS_LITERAL_STRING("\\Macromedia"));
     AddSandboxAllowedFile(aAllowedFilesReadWrite, dirSvc, NS_WIN_APPDATA_DIR,
                           NS_LITERAL_STRING("\\Adobe\\Flash Player\\*"));
+    AddSandboxAllowedFile(aAllowedDirectories, dirSvc, NS_WIN_APPDATA_DIR,
+                          NS_LITERAL_STRING("\\Adobe\\Flash Player"));
+    AddSandboxAllowedFile(aAllowedDirectories, dirSvc, NS_WIN_APPDATA_DIR,
+                          NS_LITERAL_STRING("\\Adobe"));
 
     // Write access to the Temp directory is needed in some mochitest crash
     // tests.
     // Bug 1171393 tracks removing this requirement.
     AddSandboxAllowedFile(aAllowedFilesReadWrite, dirSvc, NS_OS_TEMP_DIR,
                           NS_LITERAL_STRING("\\*"));
 }
 #endif
 
 bool
 PluginProcessParent::Launch(mozilla::UniquePtr<LaunchCompleteTask> aLaunchCompleteTask,
                             int32_t aSandboxLevel)
 {
 #if defined(XP_WIN) && defined(MOZ_SANDBOX)
     mSandboxLevel = aSandboxLevel;
     AddSandboxAllowedFiles(mSandboxLevel, mAllowedFilesRead,
-                           mAllowedFilesReadWrite);
+                           mAllowedFilesReadWrite, mAllowedDirectories);
 #else
     if (aSandboxLevel != 0) {
         MOZ_ASSERT(false,
                    "Can't enable an NPAPI process sandbox for platform/build.");
     }
 #endif
 
     ProcessArchitecture currentArchitecture = base::GetCurrentProcessArchitecture();
--- a/dom/push/PushNotificationService.js
+++ b/dom/push/PushNotificationService.js
@@ -31,17 +31,18 @@ this.PushNotificationService = function 
 PushNotificationService.prototype = {
   classID: Components.ID("{32028e38-903b-4a64-a180-5857eb4cb3dd}"),
 
   contractID: "@mozilla.org/push/NotificationService;1",
 
   _xpcom_factory: XPCOMUtils.generateSingletonFactory(PushNotificationService),
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                          Ci.nsISupportsWeakReference,
-                                         Ci.nsIPushNotificationService]),
+                                         Ci.nsIPushNotificationService,
+                                         Ci.nsIPushQuotaManager,]),
 
   register: function register(scope, originAttributes) {
     return PushService.register({
       scope: scope,
       originAttributes: originAttributes,
       maxQuota: Infinity,
     });
   },
@@ -69,16 +70,36 @@ PushNotificationService.prototype = {
         break;
       case "sessionstore-windows-restored":
         Services.obs.removeObserver(this, "sessionstore-windows-restored");
         if (isParent) {
           PushService.init();
         }
         break;
     }
+  },
+
+  // nsIPushQuotaManager methods
+
+  notificationForOriginShown: function(origin) {
+    if (!isParent) {
+      Services.cpmm.sendAsyncMessage("Push:NotificationForOriginShown", origin);
+      return;
+    }
+
+    PushService._notificationForOriginShown(origin);
+  },
+
+  notificationForOriginClosed: function(origin) {
+    if (!isParent) {
+      Services.cpmm.sendAsyncMessage("Push:NotificationForOriginClosed", origin);
+      return;
+    }
+
+    PushService._notificationForOriginClosed(origin);
   }
 };
 
 this.PushObserverNotification = function PushObserverNotification() {};
 
 PushObserverNotification.prototype = {
   classID: Components.ID("{66a87970-6dc9-46e0-ac61-adb4a13791de}"),
 
--- a/dom/push/PushRecord.jsm
+++ b/dom/push/PushRecord.jsm
@@ -60,44 +60,43 @@ PushRecord.prototype = {
       return;
     }
     if (lastVisit < 0) {
       // If the user cleared their history, but retained the push permission,
       // mark the registration as expired.
       this.quota = 0;
       return;
     }
-    let currentQuota;
     if (lastVisit > this.lastPush) {
       // If the user visited the site since the last time we received a
       // notification, reset the quota.
       let daysElapsed = (Date.now() - lastVisit) / 24 / 60 / 60 / 1000;
-      currentQuota = Math.min(
+      this.quota = Math.min(
         Math.round(8 * Math.pow(daysElapsed, -0.8)),
         prefs.get("maxQuotaPerSubscription")
       );
-      Services.telemetry.getHistogramById("PUSH_API_QUOTA_RESET_TO").add(currentQuota - 1);
-    } else {
-      // The user hasn't visited the site since the last notification.
-      currentQuota = this.quota;
-    }
-    this.quota = Math.max(currentQuota - 1, 0);
-    // We check for ctime > 0 to skip older records that did not have ctime.
-    if (this.isExpired() && this.ctime > 0) {
-      let duration = Date.now() - this.ctime;
-      Services.telemetry.getHistogramById("PUSH_API_QUOTA_EXPIRATION_TIME").add(duration / 1000);
+      Services.telemetry.getHistogramById("PUSH_API_QUOTA_RESET_TO").add(this.quota);
     }
   },
 
   receivedPush(lastVisit) {
     this.updateQuota(lastVisit);
     this.pushCount++;
     this.lastPush = Date.now();
   },
 
+  reduceQuota() {
+    this.quota = Math.max(this.quota - 1, 0);
+    // We check for ctime > 0 to skip older records that did not have ctime.
+    if (this.isExpired() && this.ctime > 0) {
+      let duration = Date.now() - this.ctime;
+      Services.telemetry.getHistogramById("PUSH_API_QUOTA_EXPIRATION_TIME").add(duration / 1000);
+    }
+  },
+
   /**
    * Queries the Places database for the last time a user visited the site
    * associated with a push registration.
    *
    * @returns {Promise} A promise resolved with either the last time the user
    *  visited the site, or `-Infinity` if the site is not in the user's history.
    *  The time is expressed in milliseconds since Epoch.
    */
--- a/dom/push/PushService.jsm
+++ b/dom/push/PushService.jsm
@@ -40,16 +40,18 @@ XPCOMUtils.defineLazyGetter(this, "conso
     prefix: "PushService",
   });
 });
 
 const prefs = new Preferences("dom.push.");
 
 const kCHILD_PROCESS_MESSAGES = ["Push:Register", "Push:Unregister",
                                  "Push:Registration", "Push:RegisterEventNotificationListener",
+                                 "Push:NotificationForOriginShown",
+                                 "Push:NotificationForOriginClosed",
                                  "child-process-shutdown"];
 
 const PUSH_SERVICE_UNINIT = 0;
 const PUSH_SERVICE_INIT = 1; // No serverURI
 const PUSH_SERVICE_ACTIVATING = 2;//activating db
 const PUSH_SERVICE_CONNECTION_DISABLE = 3;
 const PUSH_SERVICE_ACTIVE_OFFLINE = 4;
 const PUSH_SERVICE_RUNNING = 5;
@@ -92,16 +94,21 @@ const UNINIT_EVENT = 3;
  * for persistence.
  */
 this.PushService = {
   _service: null,
   _state: PUSH_SERVICE_UNINIT,
   _db: null,
   _options: null,
   _alarmID: null,
+  _visibleNotifications: new Map(),
+
+  // Callback that is called after attempting to
+  // reduce the quota for a record. Used for testing purposes.
+  _updateQuotaTestCallback: null,
 
   _childListeners: [],
 
   // When serverURI changes (this is used for testing), db is cleaned up and a
   // a new db is started. This events must be sequential.
   _stateChangeProcessQueue: null,
   _stateChangeProcessEnqueue: function(op) {
     if (!this._stateChangeProcessQueue) {
@@ -878,30 +885,87 @@ this.PushService = {
         );
       } else {
         decodedPromise = Promise.resolve(null);
       }
       return decodedPromise.then(message => {
         if (shouldNotify) {
           notified = this._notifyApp(record, message);
         }
-        if (record.isExpired()) {
-          this._recordDidNotNotify(kDROP_NOTIFICATION_REASON_EXPIRED);
-          // Drop the registration in the background. If the user returns to the
-          // site, the service worker will be notified on the next `idle-daily`
-          // event.
-          this._backgroundUnregister(record);
-        }
+        // Update quota after the delay, at which point
+        // we check for visible notifications.
+        setTimeout(() => this._updateQuota(keyID),
+          prefs.get("quotaUpdateDelay"));
         return notified;
       });
     }).catch(error => {
       console.error("receivedPushMessage: Error notifying app", error);
     });
   },
 
+  _updateQuota: function(keyID) {
+    console.debug("updateQuota()");
+
+    this._db.update(keyID, record => {
+      // Record may have expired from an earlier quota update.
+      if (record.isExpired()) {
+        console.debug(
+          "updateQuota: Trying to update quota for expired record", record);
+        return null;
+      }
+      // If there are visible notifications, don't apply the quota penalty
+      // for the message.
+      if (!this._visibleNotifications.has(record.uri.prePath)) {
+        record.reduceQuota();
+      }
+      return record;
+    }).then(record => {
+      if (record && record.isExpired()) {
+        this._recordDidNotNotify(kDROP_NOTIFICATION_REASON_EXPIRED);
+        // Drop the registration in the background. If the user returns to the
+        // site, the service worker will be notified on the next `idle-daily`
+        // event.
+        this._backgroundUnregister(record);
+      }
+      if (this._updateQuotaTestCallback) {
+        // Callback so that test may be notified when the quota update is complete.
+        this._updateQuotaTestCallback();
+      }
+    }).catch(error => {
+      console.debug("updateQuota: Error while trying to update quota", error);
+    });
+  },
+
+  _notificationForOriginShown(origin) {
+    console.debug("notificationForOriginShown()", origin);
+    let count;
+    if (this._visibleNotifications.has(origin)) {
+      count = this._visibleNotifications.get(origin);
+    } else {
+      count = 0;
+    }
+    this._visibleNotifications.set(origin, count + 1);
+  },
+
+  _notificationForOriginClosed(origin) {
+    console.debug("notificationForOriginClosed()", origin);
+    let count;
+    if (this._visibleNotifications.has(origin)) {
+      count = this._visibleNotifications.get(origin);
+    } else {
+      console.debug("notificationForOriginClosed: closing notification that has not been shown?");
+      return;
+    }
+    if (count > 1) {
+      this._visibleNotifications.set(origin, count - 1);
+    } else {
+      this._visibleNotifications.delete(origin);
+    }
+  },
+
   _notifyApp: function(aPushRecord, message) {
     if (!aPushRecord || !aPushRecord.scope ||
         aPushRecord.originAttributes === undefined) {
       console.error("notifyApp: Invalid record", aPushRecord);
       return false;
     }
 
     console.debug("notifyApp()", aPushRecord.scope);
@@ -1050,16 +1114,30 @@ this.PushService = {
     if (aMessage.name === "child-process-shutdown") {
       console.debug("receiveMessage: Possibly removing child listener");
       for (var i = this._childListeners.length - 1; i >= 0; --i) {
         if (this._childListeners[i] == aMessage.target) {
           console.debug("receiveMessage: Removed child listener");
           this._childListeners.splice(i, 1);
         }
       }
+      console.debug("receiveMessage: Clearing notifications from child");
+      this._visibleNotifications.clear();
+      return;
+    }
+
+    if (aMessage.name === "Push:NotificationForOriginShown") {
+      console.debug("receiveMessage: Notification shown from child");
+      this._notificationForOriginShown(aMessage.data);
+      return;
+    }
+
+    if (aMessage.name === "Push:NotificationForOriginClosed") {
+      console.debug("receiveMessage: Notification closed from child");
+      this._notificationForOriginClosed(aMessage.data);
       return;
     }
 
     if (!aMessage.target.assertPermission("push")) {
       console.debug("receiveMessage: Got message from a child process that",
         "does not have 'push' permission");
       return null;
     }
--- a/dom/push/test/xpcshell/head.js
+++ b/dom/push/test/xpcshell/head.js
@@ -217,16 +217,17 @@ function setPrefs(prefs = {}) {
     'adaptive.gap': 60000,
     'adaptive.upperLimit': 29 * 60 * 1000,
     // Misc. defaults.
     'adaptive.mobile': '',
     'http2.maxRetries': 2,
     'http2.retryInterval': 500,
     'http2.reset_retry_count_after_ms': 60000,
     maxQuotaPerSubscription: 16,
+    quotaUpdateDelay: 3000,
   }, prefs);
   for (let pref in defaultPrefs) {
     servicePrefs.set(pref, defaultPrefs[pref]);
   }
 }
 
 function compareAscending(a, b) {
   return a > b ? 1 : a < b ? -1 : 0;
new file mode 100644
--- /dev/null
+++ b/dom/push/test/xpcshell/test_quota_with_notification.js
@@ -0,0 +1,115 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+'use strict';
+
+const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
+
+Cu.import("resource://gre/modules/Task.jsm");
+
+const userAgentID = 'aaabf1f8-2f68-44f1-a920-b88e9e7d7559';
+const nsIPushQuotaManager = Components.interfaces.nsIPushQuotaManager;
+
+function run_test() {
+  do_get_profile();
+  setPrefs({
+    userAgentID,
+  });
+  run_next_test();
+}
+
+add_task(function* test_expiration_origin_threshold() {
+  let db = PushServiceWebSocket.newPushDB();
+  do_register_cleanup(() => {
+    db.drop().then(_ => db.close())
+    PushService._notificationForOriginClosed("https://example.com");
+  });
+
+  // Simulate a notification being shown for the origin,
+  // this should relax the quota and allow as many push messages
+  // as we want.
+  PushService._notificationForOriginShown("https://example.com");
+
+  yield db.put({
+    channelID: 'f56645a9-1f32-4655-92ad-ddc37f6d54fb',
+    pushEndpoint: 'https://example.org/push/1',
+    scope: 'https://example.com/quota',
+    pushCount: 0,
+    lastPush: 0,
+    version: null,
+    originAttributes: '',
+    quota: 16,
+  });
+
+  // A visit one day ago should provide a quota of 8 messages.
+  yield addVisit({
+    uri: 'https://example.com/login',
+    title: 'Sign in to see your auctions',
+    visits: [{
+      visitDate: (Date.now() - 1 * 24 * 60 * 60 * 1000) * 1000,
+      transitionType: Ci.nsINavHistoryService.TRANSITION_LINK,
+    }],
+  });
+
+  let numMessages = 10;
+
+  let updates = 0;
+  let notifyPromise = promiseObserverNotification('push-notification', (subject, data) => {
+    dump(updates++);
+    return updates == numMessages;
+  });
+
+  let updateQuotaPromise = new Promise((resolve, reject) => {
+    let quotaUpdateCount = 0;
+    PushService._updateQuotaTestCallback = function() {
+      quotaUpdateCount++;
+      if (quotaUpdateCount == 10) {
+        resolve();
+      }
+    };
+  });
+
+  PushService.init({
+    serverURI: 'wss://push.example.org/',
+    networkInfo: new MockDesktopNetworkInfo(),
+    db,
+    makeWebSocket(uri) {
+      return new MockWebSocket(uri, {
+        onHello(request) {
+          this.serverSendMsg(JSON.stringify({
+            messageType: 'hello',
+            status: 200,
+            uaid: userAgentID,
+          }));
+
+          // If the origin has visible notifications, the
+          // message should not affect quota.
+          for (let version = 1; version <= 10; version++) {
+            this.serverSendMsg(JSON.stringify({
+              messageType: 'notification',
+              updates: [{
+                channelID: 'f56645a9-1f32-4655-92ad-ddc37f6d54fb',
+                version,
+              }],
+            }));
+          }
+        },
+        onUnregister(request) {
+          ok(false, "Channel should not be unregistered.");
+        },
+        // We expect to receive acks, but don't care about their
+        // contents.
+        onACK(request) {},
+      });
+    },
+  });
+
+  yield waitForPromise(notifyPromise, DEFAULT_TIMEOUT,
+    'Timed out waiting for notifications');
+
+  yield waitForPromise(updateQuotaPromise, DEFAULT_TIMEOUT,
+    'Timed out waiting for quota to be updated');
+
+  let expiredRecord = yield db.getByKeyID('f56645a9-1f32-4655-92ad-ddc37f6d54fb');
+  notStrictEqual(expiredRecord.quota, 0, 'Expired record not updated');
+});
--- a/dom/push/test/xpcshell/xpcshell.ini
+++ b/dom/push/test/xpcshell/xpcshell.ini
@@ -12,16 +12,17 @@ skip-if = toolkit == 'android'
 [test_notification_incomplete.js]
 [test_notification_version_string.js]
 
 [test_permissions.js]
 run-sequentially = This will delete all existing push subscriptions.
 
 [test_quota_exceeded.js]
 [test_quota_observer.js]
+[test_quota_with_notification.js]
 [test_register_case.js]
 [test_register_flush.js]
 [test_register_invalid_channel.js]
 [test_register_invalid_endpoint.js]
 [test_register_invalid_json.js]
 [test_register_no_id.js]
 [test_register_request_queue.js]
 [test_register_rollback.js]
--- a/dom/xul/nsXULElement.cpp
+++ b/dom/xul/nsXULElement.cpp
@@ -48,17 +48,16 @@
 #include "nsIRDFCompositeDataSource.h"
 #include "nsIRDFNode.h"
 #include "nsIRDFService.h"
 #include "nsIScriptContext.h"
 #include "nsIScriptError.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIServiceManager.h"
 #include "mozilla/css/StyleRule.h"
-#include "nsIStyleSheet.h"
 #include "nsIURL.h"
 #include "nsViewManager.h"
 #include "nsIWidget.h"
 #include "nsIXULDocument.h"
 #include "nsIXULTemplateBuilder.h"
 #include "nsLayoutCID.h"
 #include "nsContentCID.h"
 #include "mozilla/dom/Event.h"
--- a/editor/libeditor/nsStyleSheetTxns.cpp
+++ b/editor/libeditor/nsStyleSheetTxns.cpp
@@ -14,33 +14,31 @@
 #include "nsError.h"                    // for NS_OK, etc
 #include "nsIDOMDocument.h"             // for nsIDOMDocument
 #include "nsIDocument.h"                // for nsIDocument
 #include "nsIDocumentObserver.h"        // for UPDATE_STYLE
 #include "nsIEditor.h"                  // for nsIEditor
 
 using namespace mozilla;
 
-class nsIStyleSheet;
-
 static void
-AddStyleSheet(nsIEditor* aEditor, nsIStyleSheet* aSheet)
+AddStyleSheet(nsIEditor* aEditor, CSSStyleSheet* aSheet)
 {
   nsCOMPtr<nsIDOMDocument> domDoc;
   aEditor->GetDocument(getter_AddRefs(domDoc));
   nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
   if (doc) {
     doc->BeginUpdate(UPDATE_STYLE);
     doc->AddStyleSheet(aSheet);
     doc->EndUpdate(UPDATE_STYLE);
   }
 }
 
 static void
-RemoveStyleSheet(nsIEditor *aEditor, nsIStyleSheet *aSheet)
+RemoveStyleSheet(nsIEditor* aEditor, CSSStyleSheet* aSheet)
 {
   nsCOMPtr<nsIDOMDocument> domDoc;
   aEditor->GetDocument(getter_AddRefs(domDoc));
   nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
   if (doc) {
     doc->BeginUpdate(UPDATE_STYLE);
     doc->RemoveStyleSheet(aSheet);
     doc->EndUpdate(UPDATE_STYLE);
--- a/embedding/components/webbrowserpersist/nsWebBrowserPersist.h
+++ b/embedding/components/webbrowserpersist/nsWebBrowserPersist.h
@@ -11,17 +11,16 @@
 #include "nsWeakReference.h"
 
 #include "nsIInterfaceRequestor.h"
 #include "nsIMIMEService.h"
 #include "nsIStreamListener.h"
 #include "nsIOutputStream.h"
 #include "nsIInputStream.h"
 #include "nsIChannel.h"
-#include "nsIStyleSheet.h"
 #include "nsIDocumentEncoder.h"
 #include "nsITransport.h"
 #include "nsIProgressEventSink.h"
 #include "nsIFile.h"
 #include "nsIWebProgressListener2.h"
 #include "nsIWebBrowserPersistDocument.h"
 
 #include "mozilla/UniquePtr.h"
--- a/gfx/gl/GLBlitHelper.cpp
+++ b/gfx/gl/GLBlitHelper.cpp
@@ -679,17 +679,17 @@ GLBlitHelper::BlitGrallocImage(layers::G
 
 #ifdef MOZ_WIDGET_ANDROID
 
 #define ATTACH_WAIT_MS 50
 
 bool
 GLBlitHelper::BlitSurfaceTextureImage(layers::SurfaceTextureImage* stImage)
 {
-    AndroidSurfaceTexture* surfaceTexture = stImage->GetData()->mSurfTex;
+    AndroidSurfaceTexture* surfaceTexture = stImage->GetSurfaceTexture();
 
     ScopedBindTextureUnit boundTU(mGL, LOCAL_GL_TEXTURE0);
 
     if (NS_FAILED(surfaceTexture->Attach(mGL, PR_MillisecondsToInterval(ATTACH_WAIT_MS))))
         return false;
 
     // UpdateTexImage() changes the EXTERNAL binding, so save it here
     // so we can restore it after.
@@ -708,18 +708,18 @@ GLBlitHelper::BlitSurfaceTextureImage(la
 
     mGL->fBindTexture(LOCAL_GL_TEXTURE_EXTERNAL, oldBinding);
     return true;
 }
 
 bool
 GLBlitHelper::BlitEGLImageImage(layers::EGLImageImage* image)
 {
-    EGLImage eglImage = image->GetData()->mImage;
-    EGLSync eglSync = image->GetData()->mSync;
+    EGLImage eglImage = image->GetImage();
+    EGLSync eglSync = image->GetSync();
 
     if (eglSync) {
         EGLint status = sEGLLibrary.fClientWaitSync(EGL_DISPLAY(), eglSync, 0, LOCAL_EGL_FOREVER);
         if (status != LOCAL_EGL_CONDITION_SATISFIED) {
             return false;
         }
     }
 
@@ -836,23 +836,22 @@ GLBlitHelper::BlitImageToFramebuffer(lay
         type = ConvertGralloc;
         srcOrigin = OriginPos::TopLeft;
         break;
 #endif
 
 #ifdef MOZ_WIDGET_ANDROID
     case ImageFormat::SURFACE_TEXTURE:
         type = ConvertSurfaceTexture;
-        srcOrigin = static_cast<layers::SurfaceTextureImage*>(srcImage)->GetData()
-                                                                       ->mOriginPos;
+        srcOrigin = srcImage->AsSurfaceTextureImage()->GetOriginPos();
         break;
 
     case ImageFormat::EGLIMAGE:
         type = ConvertEGLImage;
-        srcOrigin = static_cast<layers::EGLImageImage*>(srcImage)->GetData()->mOriginPos;
+        srcOrigin = srcImage->AsEGLImageImage()->GetOriginPos();
         break;
 #endif
 #ifdef XP_MACOSX
     case ImageFormat::MAC_IOSURFACE:
         type = ConvertMacIOSurfaceImage;
         srcOrigin = OriginPos::TopLeft;
         break;
 #endif
@@ -888,17 +887,17 @@ GLBlitHelper::BlitImageToFramebuffer(lay
         return BlitSurfaceTextureImage(static_cast<layers::SurfaceTextureImage*>(srcImage));
 
     case ConvertEGLImage:
         return BlitEGLImageImage(static_cast<layers::EGLImageImage*>(srcImage));
 #endif
 
 #ifdef XP_MACOSX
     case ConvertMacIOSurfaceImage:
-        return BlitMacIOSurfaceImage(static_cast<layers::MacIOSurfaceImage*>(srcImage));
+        return BlitMacIOSurfaceImage(srcImage->AsMacIOSurfaceImage());
 #endif
 
     default:
         return false;
     }
 }
 
 bool
--- a/gfx/layers/D3D11ShareHandleImage.cpp
+++ b/gfx/layers/D3D11ShareHandleImage.cpp
@@ -11,30 +11,29 @@
 #include "mozilla/layers/TextureD3D11.h"
 #include "mozilla/layers/CompositableClient.h"
 #include "mozilla/layers/CompositableForwarder.h"
 #include "d3d11.h"
 
 namespace mozilla {
 namespace layers {
 
-HRESULT
-D3D11ShareHandleImage::SetData(const Data& aData)
+D3D11ShareHandleImage::D3D11ShareHandleImage(const gfx::IntSize& aSize,
+                                             const gfx::IntRect& aRect)
+ : Image(nullptr, ImageFormat::D3D11_SHARE_HANDLE_TEXTURE),
+   mSize(aSize),
+   mPictureRect(aRect)
 {
-  mPictureRect = aData.mRegion;
-  mSize = aData.mSize;
+}
 
-  mTextureClient =
-    aData.mAllocator->CreateOrRecycleClient(gfx::SurfaceFormat::B8G8R8A8,
-                                            mSize);
-  if (!mTextureClient) {
-    return E_FAIL;
-  }
-
-  return S_OK;
+bool
+D3D11ShareHandleImage::AllocateTexture(D3D11RecycleAllocator* aAllocator)
+{
+  mTextureClient = aAllocator->CreateOrRecycleClient(gfx::SurfaceFormat::B8G8R8A8, mSize);
+  return !!mTextureClient;
 }
 
 gfx::IntSize
 D3D11ShareHandleImage::GetSize()
 {
   return mSize;
 }
 
--- a/gfx/layers/D3D11ShareHandleImage.h
+++ b/gfx/layers/D3D11ShareHandleImage.h
@@ -40,50 +40,32 @@ protected:
 
   RefPtr<ID3D11Device> mDevice;
 };
 
 // Image class that wraps a ID3D11Texture2D. This class copies the image
 // passed into SetData(), so that it can be accessed from other D3D devices.
 // This class also manages the synchronization of the copy, to ensure the
 // resource is ready to use.
-class D3D11ShareHandleImage : public Image {
+class D3D11ShareHandleImage final : public Image {
 public:
+  D3D11ShareHandleImage(const gfx::IntSize& aSize,
+                        const gfx::IntRect& aRect);
+  ~D3D11ShareHandleImage() override {}
 
-  struct Data {
-    Data(D3D11RecycleAllocator* aAllocator,
-         const gfx::IntSize& aSize,
-         const gfx::IntRect& aRegion)
-      : mAllocator(aAllocator)
-      , mSize(aSize)
-      , mRegion(aRegion) {}
-    RefPtr<D3D11RecycleAllocator> mAllocator;
-    gfx::IntSize mSize;
-    gfx::IntRect mRegion;
-  };
-
-  D3D11ShareHandleImage() : Image(NULL, ImageFormat::D3D11_SHARE_HANDLE_TEXTURE), mSize(0, 0) {}
-  virtual ~D3D11ShareHandleImage() {}
-
-  // Copies the surface into a sharable texture's surface, and initializes
-  // the image.
-  HRESULT SetData(const Data& aData);
+  bool AllocateTexture(D3D11RecycleAllocator* aAllocator);
 
   gfx::IntSize GetSize() override;
-
   virtual already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override;
-
   virtual TextureClient* GetTextureClient(CompositableClient* aClient) override;
+  virtual gfx::IntRect GetPictureRect() override { return mPictureRect; }
 
   ID3D11Texture2D* GetTexture() const;
 
-  virtual gfx::IntRect GetPictureRect() override { return mPictureRect; }
-
 private:
-
   gfx::IntSize mSize;
   gfx::IntRect mPictureRect;
   RefPtr<TextureClientD3D11> mTextureClient;
 };
 
 } // namepace layers
 } // namespace mozilla
 
--- a/gfx/layers/D3D9SurfaceImage.cpp
+++ b/gfx/layers/D3D9SurfaceImage.cpp
@@ -10,33 +10,35 @@
 #include "mozilla/layers/CompositableForwarder.h"
 #include "mozilla/layers/ImageBridgeChild.h"
 #include "mozilla/gfx/Types.h"
 
 namespace mozilla {
 namespace layers {
 
 
-D3D9SurfaceImage::D3D9SurfaceImage()
+D3D9SurfaceImage::D3D9SurfaceImage(bool aIsFirstFrame)
   : Image(nullptr, ImageFormat::D3D9_RGB32_TEXTURE)
   , mSize(0, 0)
   , mValid(false)
-  , mIsFirstFrame(false)
+  , mIsFirstFrame(aIsFirstFrame)
 {}
 
 D3D9SurfaceImage::~D3D9SurfaceImage()
 {
 }
 
 HRESULT
-D3D9SurfaceImage::SetData(const Data& aData)
+D3D9SurfaceImage::AllocateAndCopy(D3D9RecycleAllocator* aAllocator,
+                                  IDirect3DSurface9* aSurface,
+                                  const gfx::IntRect& aRegion)
 {
-  NS_ENSURE_TRUE(aData.mSurface, E_POINTER);
+  NS_ENSURE_TRUE(aSurface, E_POINTER);
   HRESULT hr;
-  RefPtr<IDirect3DSurface9> surface = aData.mSurface;
+  RefPtr<IDirect3DSurface9> surface = aSurface;
 
   RefPtr<IDirect3DDevice9> device;
   hr = surface->GetDevice(getter_AddRefs(device));
   NS_ENSURE_TRUE(SUCCEEDED(hr), E_FAIL);
 
   RefPtr<IDirect3D9> d3d9;
   hr = device->GetDirect3D(getter_AddRefs(d3d9));
   NS_ENSURE_TRUE(SUCCEEDED(hr), E_FAIL);
@@ -49,48 +51,44 @@ D3D9SurfaceImage::SetData(const Data& aD
                                          D3DDEVTYPE_HAL,
                                          desc.Format,
                                          D3DFMT_X8R8G8B8);
   NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
 
   // DXVA surfaces aren't created sharable, so we need to copy the surface
   // to a sharable texture to that it's accessible to the layer manager's
   // device.
-  const gfx::IntRect& region = aData.mRegion;
   RefPtr<SharedTextureClientD3D9> textureClient =
-    aData.mAllocator->CreateOrRecycleClient(gfx::SurfaceFormat::B8G8R8X8,
-                                            region.Size());
+    aAllocator->CreateOrRecycleClient(gfx::SurfaceFormat::B8G8R8X8, aRegion.Size());
   if (!textureClient) {
     return E_FAIL;
   }
 
   // Copy the image onto the texture, preforming YUV -> RGB conversion if necessary.
   RefPtr<IDirect3DSurface9> textureSurface = textureClient->GetD3D9Surface();
   if (!textureSurface) {
     return E_FAIL;
   }
 
-  RECT src = { region.x, region.y, region.x+region.width, region.y+region.height };
+  RECT src = { aRegion.x, aRegion.y, aRegion.x+aRegion.width, aRegion.y+aRegion.height };
   hr = device->StretchRect(surface, &src, textureSurface, nullptr, D3DTEXF_NONE);
   NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
 
   // Flush the draw command now, so that by the time we come to draw this
   // image, we're less likely to need to wait for the draw operation to
   // complete.
   RefPtr<IDirect3DQuery9> query;
   hr = device->CreateQuery(D3DQUERYTYPE_EVENT, getter_AddRefs(query));
   NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
   hr = query->Issue(D3DISSUE_END);
   NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
 
   mTextureClient = textureClient;
-  mSize = region.Size();
+  mSize = aRegion.Size();
   mQuery = query;
-  mIsFirstFrame = aData.mIsFirstFrame;
-
   return S_OK;
 }
 
 bool
 D3D9SurfaceImage::IsValid()
 {
   EnsureSynchronized();
   return mValid;
--- a/gfx/layers/D3D9SurfaceImage.h
+++ b/gfx/layers/D3D9SurfaceImage.h
@@ -42,40 +42,22 @@ protected:
 };
 
 // Image class that wraps a IDirect3DSurface9. This class copies the image
 // passed into SetData(), so that it can be accessed from other D3D devices.
 // This class also manages the synchronization of the copy, to ensure the
 // resource is ready to use.
 class D3D9SurfaceImage : public Image {
 public:
-
-  struct Data {
-    Data(IDirect3DSurface9* aSurface,
-         const gfx::IntRect& aRegion,
-         D3D9RecycleAllocator* aAllocator,
-         bool aIsFirstFrame)
-      : mSurface(aSurface)
-      , mRegion(aRegion)
-      , mAllocator(aAllocator)
-      , mIsFirstFrame(aIsFirstFrame)
-    {}
-
-    RefPtr<IDirect3DSurface9> mSurface;
-    gfx::IntRect mRegion;
-    RefPtr<D3D9RecycleAllocator> mAllocator;
-    bool mIsFirstFrame;
-  };
-
-  D3D9SurfaceImage();
+  explicit D3D9SurfaceImage(bool aIsFirstFrame);
   virtual ~D3D9SurfaceImage();
 
-  // Copies the surface into a sharable texture's surface, and initializes
-  // the image.
-  HRESULT SetData(const Data& aData);
+  HRESULT AllocateAndCopy(D3D9RecycleAllocator* aAllocator,
+                          IDirect3DSurface9* aSurface,
+                          const gfx::IntRect& aRegion);
 
   // Returns the description of the shared surface.
   const D3DSURFACE_DESC& GetDesc() const;
 
   gfx::IntSize GetSize() override;
 
   virtual already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override;
 
--- a/gfx/layers/GLImages.cpp
+++ b/gfx/layers/GLImages.cpp
@@ -11,30 +11,42 @@
 using namespace mozilla;
 using namespace mozilla::gl;
 
 namespace mozilla {
 namespace layers {
 
 static RefPtr<GLContext> sSnapshotContext;
 
+EGLImageImage::EGLImageImage(EGLImage aImage, EGLSync aSync,
+                             const gfx::IntSize& aSize, const gl::OriginPos& aOrigin,
+                             bool aOwns)
+ : GLImage(ImageFormat::EGLIMAGE),
+   mImage(aImage),
+   mSync(aSync),
+   mSize(aSize),
+   mPos(aOrigin),
+   mOwns(aOwns)
+{
+}
+
 EGLImageImage::~EGLImageImage()
 {
-  if (!mData.mOwns) {
+  if (!mOwns) {
     return;
   }
 
-  if (mData.mImage) {
-    sEGLLibrary.fDestroyImage(EGL_DISPLAY(), mData.mImage);
-    mData.mImage = nullptr;
+  if (mImage) {
+    sEGLLibrary.fDestroyImage(EGL_DISPLAY(), mImage);
+    mImage = nullptr;
   }
 
-  if (mData.mSync) {
-    sEGLLibrary.fDestroySync(EGL_DISPLAY(), mData.mSync);
-    mData.mSync = nullptr;
+  if (mSync) {
+    sEGLLibrary.fDestroySync(EGL_DISPLAY(), mSync);
+    mSync = nullptr;
   }
 }
 
 already_AddRefed<gfx::SourceSurface>
 GLImage::GetAsSourceSurface()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Should be on the main thread");
 
@@ -77,10 +89,22 @@ GLImage::GetAsSourceSurface()
     return nullptr;
   }
 
   ScopedBindFramebuffer bind(sSnapshotContext, autoFBForTex.FB());
   ReadPixelsIntoDataSurface(sSnapshotContext, source);
   return source.forget();
 }
 
+#ifdef MOZ_WIDGET_ANDROID
+SurfaceTextureImage::SurfaceTextureImage(gl::AndroidSurfaceTexture* aSurfTex,
+                                         const gfx::IntSize& aSize,
+                                         gl::OriginPos aOriginPos)
+ : GLImage(ImageFormat::SURFACE_TEXTURE),
+   mSurfaceTexture(aSurfTex),
+   mSize(aSize),
+   mOriginPos(aOriginPos)
+{
+}
+#endif
+
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/GLImages.h
+++ b/gfx/layers/GLImages.h
@@ -23,62 +23,70 @@ class GLImage : public Image {
 public:
   explicit GLImage(ImageFormat aFormat) : Image(nullptr, aFormat){}
 
   virtual already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override;
 };
 
 class EGLImageImage : public GLImage {
 public:
-  struct Data {
-    EGLImage mImage;
-    EGLSync mSync;
-    gfx::IntSize mSize;
-    gl::OriginPos mOriginPos;
-    bool mOwns;
+  EGLImageImage(EGLImage aImage, EGLSync aSync,
+                const gfx::IntSize& aSize, const gl::OriginPos& aOrigin,
+                bool aOwns);
 
-    Data() : mImage(nullptr), mSync(nullptr), mSize(0, 0),
-             mOriginPos(gl::OriginPos::TopLeft), mOwns(false)
-    {
-    }
-  };
+  gfx::IntSize GetSize() override { return mSize; }
+  gl::OriginPos GetOriginPos() const {
+    return mPos;
+  }
+  EGLImage GetImage() const {
+    return mImage;
+  }
+  EGLSync GetSync() const {
+    return mSync;
+  }
 
-  void SetData(const Data& aData) { mData = aData; }
-  const Data* GetData() { return &mData; }
-
-  gfx::IntSize GetSize() { return mData.mSize; }
-
-  EGLImageImage() : GLImage(ImageFormat::EGLIMAGE) {}
+  EGLImageImage* AsEGLImageImage() override {
+    return this;
+  }
 
 protected:
   virtual ~EGLImageImage();
 
 private:
-  Data mData;
+  EGLImage mImage;
+  EGLSync mSync;
+  gfx::IntSize mSize;
+  gl::OriginPos mPos;
+  bool mOwns;
 };
 
 #ifdef MOZ_WIDGET_ANDROID
 
 class SurfaceTextureImage : public GLImage {
 public:
-  struct Data {
-    mozilla::gl::AndroidSurfaceTexture* mSurfTex;
-    gfx::IntSize mSize;
-    gl::OriginPos mOriginPos;
-  };
+  SurfaceTextureImage(gl::AndroidSurfaceTexture* aSurfTex,
+                      const gfx::IntSize& aSize,
+                      gl::OriginPos aOriginPos);
 
-  void SetData(const Data& aData) { mData = aData; }
-  const Data* GetData() { return &mData; }
+  gfx::IntSize GetSize() override { return mSize; }
+  gl::AndroidSurfaceTexture* GetSurfaceTexture() const {
+    return mSurfaceTexture;
+  }
+  gl::OriginPos GetOriginPos() const {
+    return mOriginPos;
+  }
 
-  gfx::IntSize GetSize() { return mData.mSize; }
-
-  SurfaceTextureImage() : GLImage(ImageFormat::SURFACE_TEXTURE) {}
+  SurfaceTextureImage* AsSurfaceTextureImage() override {
+    return this;
+  }
 
 private:
-  Data mData;
+  gl::AndroidSurfaceTexture* mSurfaceTexture;
+  gfx::IntSize mSize;
+  gl::OriginPos mOriginPos;
 };
 
 #endif // MOZ_WIDGET_ANDROID
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // GFX_GLIMAGES_H
--- a/gfx/layers/GrallocImages.cpp
+++ b/gfx/layers/GrallocImages.cpp
@@ -142,21 +142,22 @@ GrallocImage::SetData(const Data& aData)
   // gralloc hal could map gralloc buffer only when the buffer is locked,
   // though some gralloc hals implementation maps it when it is allocated.
   mData.mYChannel     = nullptr;
   mData.mCrChannel    = nullptr;
   mData.mCbChannel    = nullptr;
   return true;
 }
 
-bool GrallocImage::SetData(const GrallocData& aData)
+void
+GrallocImage::SetData(TextureClient* aGraphicBuffer, const gfx::IntSize& aSize)
 {
-  mTextureClient = static_cast<GrallocTextureClientOGL*>(aData.mGraphicBuffer.get());
-  mSize = aData.mPicSize;
-  return true;
+  MOZ_ASSERT(aGraphicBuffer->AsGrallocTextureClientOGL());
+  mTextureClient = aGraphicBuffer->AsGrallocTextureClientOGL();
+  mSize = aSize;
 }
 
 /**
  * Converts YVU420 semi planar frames to RGB565, possibly taking different
  * stride values.
  * Needed because the Android ColorConverter class assumes that the Y and UV
  * channels have equal stride.
  */
--- a/gfx/layers/GrallocImages.h
+++ b/gfx/layers/GrallocImages.h
@@ -48,36 +48,31 @@ GetDataSourceSurfaceFrom(android::sp<and
  * mPicX, mPicY and mPicSize. The size of the rendered image is
  * mPicSize, not mYSize or mCbCrSize.
  */
 class GrallocImage : public RecyclingPlanarYCbCrImage
 {
   typedef PlanarYCbCrData Data;
   static int32_t sColorIdMap[];
 public:
-  struct GrallocData {
-    RefPtr<TextureClient> mGraphicBuffer;
-    gfx::IntSize mPicSize;
-  };
-
   GrallocImage();
 
   virtual ~GrallocImage();
 
   /**
    * This makes a copy of the data buffers, in order to support functioning
    * in all different layer managers.
    */
   virtual bool SetData(const Data& aData);
 
   /**
    *  Share the SurfaceDescriptor without making the copy, in order
    *  to support functioning in all different layer managers.
    */
-  virtual bool SetData(const GrallocData& aData);
+  void SetData(TextureClient* aGraphicBuffer, const gfx::IntSize& aSize);
 
   // From [android 4.0.4]/hardware/msm7k/libgralloc-qsd8k/gralloc_priv.h
   enum {
     /* OEM specific HAL formats */
     HAL_PIXEL_FORMAT_YCbCr_422_P            = 0x102,
     HAL_PIXEL_FORMAT_YCbCr_420_P            = 0x103,
     HAL_PIXEL_FORMAT_YCbCr_420_SP           = 0x109,
     HAL_PIXEL_FORMAT_YCrCb_420_SP_ADRENO    = 0x10A,
--- a/gfx/layers/ImageContainer.cpp
+++ b/gfx/layers/ImageContainer.cpp
@@ -12,107 +12,53 @@
 #include "gfxUtils.h"                   // for gfxUtils
 #include "mozilla/RefPtr.h"             // for already_AddRefed
 #include "mozilla/ipc/CrossProcessMutex.h"  // for CrossProcessMutex, etc
 #include "mozilla/layers/CompositorTypes.h"
 #include "mozilla/layers/ImageBridgeChild.h"  // for ImageBridgeChild
 #include "mozilla/layers/PImageContainerChild.h"
 #include "mozilla/layers/ImageClient.h"  // for ImageClient
 #include "mozilla/layers/LayersMessages.h"
+#include "mozilla/layers/SharedPlanarYCbCrImage.h"
+#include "mozilla/layers/SharedRGBImage.h"
 #include "nsISupportsUtils.h"           // for NS_IF_ADDREF
 #include "YCbCrUtils.h"                 // for YCbCr conversions
 #ifdef MOZ_WIDGET_GONK
 #include "GrallocImages.h"
 #endif
 #if defined(MOZ_WIDGET_GONK) && defined(MOZ_B2G_CAMERA) && defined(MOZ_WEBRTC)
 #include "GonkCameraImage.h"
 #endif
 #include "gfx2DGlue.h"
 #include "mozilla/gfx/2D.h"
 
 #ifdef XP_MACOSX
 #include "mozilla/gfx/QuartzSupport.h"
-#include "MacIOSurfaceImage.h"
 #endif
 
 #ifdef XP_WIN
 #include "gfxWindowsPlatform.h"
 #include <d3d10_1.h>
-#include "D3D9SurfaceImage.h"
-#include "D3D11ShareHandleImage.h"
 #endif
 
 namespace mozilla {
 namespace layers {
 
 using namespace mozilla::ipc;
 using namespace android;
 using namespace mozilla::gfx;
 
 Atomic<int32_t> Image::sSerialCounter(0);
 
 Atomic<uint32_t> ImageContainer::sGenerationCounter(0);
 
-already_AddRefed<Image>
-ImageFactory::CreateImage(ImageFormat aFormat,
-                          const gfx::IntSize &,
-                          BufferRecycleBin *aRecycleBin)
+RefPtr<PlanarYCbCrImage>
+ImageFactory::CreatePlanarYCbCrImage(const gfx::IntSize& aScaleHint, BufferRecycleBin *aRecycleBin)
 {
-  RefPtr<Image> img;
-#ifdef MOZ_WIDGET_GONK
-  if (aFormat == ImageFormat::GRALLOC_PLANAR_YCBCR) {
-    img = new GrallocImage();
-    return img.forget();
-  }
-  if (aFormat == ImageFormat::OVERLAY_IMAGE) {
-    img = new OverlayImage();
-    return img.forget();
-  }
-#endif
-#if defined(MOZ_WIDGET_GONK) && defined(MOZ_B2G_CAMERA) && defined(MOZ_WEBRTC)
-  if (aFormat == ImageFormat::GONK_CAMERA_IMAGE) {
-    img = new GonkCameraImage();
-    return img.forget();
-  }
-#endif
-  if (aFormat == ImageFormat::PLANAR_YCBCR) {
-    img = new RecyclingPlanarYCbCrImage(aRecycleBin);
-    return img.forget();
-  }
-  if (aFormat == ImageFormat::CAIRO_SURFACE) {
-    img = new CairoImage();
-    return img.forget();
-  }
-#ifdef MOZ_WIDGET_ANDROID
-  if (aFormat == ImageFormat::SURFACE_TEXTURE) {
-    img = new SurfaceTextureImage();
-    return img.forget();
-  }
-#endif
-  if (aFormat == ImageFormat::EGLIMAGE) {
-    img = new EGLImageImage();
-    return img.forget();
-  }
-#ifdef XP_MACOSX
-  if (aFormat == ImageFormat::MAC_IOSURFACE) {
-    img = new MacIOSurfaceImage();
-    return img.forget();
-  }
-#endif
-#ifdef XP_WIN
-  if (aFormat == ImageFormat::D3D11_SHARE_HANDLE_TEXTURE) {
-    img = new D3D11ShareHandleImage();
-    return img.forget();
-  }
-  if (aFormat == ImageFormat::D3D9_RGB32_TEXTURE) {
-    img = new D3D9SurfaceImage();
-    return img.forget();
-  }
-#endif
-  return nullptr;
+  return new RecyclingPlanarYCbCrImage(aRecycleBin);
 }
 
 BufferRecycleBin::BufferRecycleBin()
   : mLock("mozilla.layers.BufferRecycleBin.mLock")
 {
 }
 
 void
@@ -203,41 +149,52 @@ ImageContainer::ImageContainer(Mode flag
 ImageContainer::~ImageContainer()
 {
   if (IsAsync()) {
     mIPDLChild->ForgetImageContainer();
     ImageBridgeChild::DispatchReleaseImageClient(mImageClient, mIPDLChild);
   }
 }
 
-already_AddRefed<Image>
-ImageContainer::CreateImage(ImageFormat aFormat)
+RefPtr<PlanarYCbCrImage>
+ImageContainer::CreatePlanarYCbCrImage()
 {
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+  if (mImageClient && mImageClient->AsImageClientSingle()) {
+    return new SharedPlanarYCbCrImage(mImageClient);
+  }
+  return mImageFactory->CreatePlanarYCbCrImage(mScaleHint, mRecycleBin);
+}
+
+RefPtr<SharedRGBImage>
+ImageContainer::CreateSharedRGBImage()
+{
+  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+  if (!mImageClient || !mImageClient->AsImageClientSingle()) {
+    return nullptr;
+  }
+  return new SharedRGBImage(mImageClient);
+}
 
 #ifdef MOZ_WIDGET_GONK
-  if (aFormat == ImageFormat::OVERLAY_IMAGE) {
-    if (mImageClient && mImageClient->GetTextureInfo().mCompositableType != CompositableType::IMAGE_OVERLAY) {
-      // If this ImageContainer is async but the image type mismatch, fix it here
-      if (ImageBridgeChild::IsCreated()) {
-        ImageBridgeChild::DispatchReleaseImageClient(mImageClient);
-        mImageClient = ImageBridgeChild::GetSingleton()->CreateImageClient(
-            CompositableType::IMAGE_OVERLAY, this).take();
-      }
+RefPtr<OverlayImage>
+ImageContainer::CreateOverlayImage()
+{
+  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+  if (mImageClient && mImageClient->GetTextureInfo().mCompositableType != CompositableType::IMAGE_OVERLAY) {
+    // If this ImageContainer is async but the image type mismatch, fix it here
+    if (ImageBridgeChild::IsCreated()) {
+      ImageBridgeChild::DispatchReleaseImageClient(mImageClient);
+      mImageClient = ImageBridgeChild::GetSingleton()->CreateImageClient(
+          CompositableType::IMAGE_OVERLAY, this).take();
     }
   }
+  return new OverlayImage();
+}
 #endif
-  if (mImageClient) {
-    RefPtr<Image> img = mImageClient->CreateImage(aFormat);
-    if (img) {
-      return img.forget();
-    }
-  }
-  return mImageFactory->CreateImage(aFormat, mScaleHint, mRecycleBin);
-}
 
 void
 ImageContainer::SetCurrentImageInternal(const nsTArray<NonOwningImage>& aImages)
 {
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
 
   mGenerationCounter = ++sGenerationCounter;
 
@@ -587,18 +544,20 @@ PlanarYCbCrImage::GetAsSourceSurface()
 
   gfx::ConvertYCbCrToRGB(mData, format, size, mapping.GetData(), mapping.GetStride());
 
   mSourceSurface = surface;
 
   return surface.forget();
 }
 
-CairoImage::CairoImage()
-  : Image(nullptr, ImageFormat::CAIRO_SURFACE)
+CairoImage::CairoImage(const gfx::IntSize& aSize, gfx::SourceSurface* aSourceSurface)
+  : Image(nullptr, ImageFormat::CAIRO_SURFACE),
+    mSize(aSize),
+    mSourceSurface(aSourceSurface)
 {}
 
 CairoImage::~CairoImage()
 {
 }
 
 TextureClient*
 CairoImage::GetTextureClient(CompositableClient *aClient)
--- a/gfx/layers/ImageContainer.h
+++ b/gfx/layers/ImageContainer.h
@@ -98,28 +98,40 @@ namespace mozilla {
 
 namespace layers {
 
 class ImageClient;
 class ImageCompositeNotification;
 class ImageContainerChild;
 class PImageContainerChild;
 class SharedPlanarYCbCrImage;
+class PlanarYCbCrImage;
 class TextureClient;
 class CompositableClient;
 class GrallocImage;
 
 struct ImageBackendData
 {
   virtual ~ImageBackendData() {}
 
 protected:
   ImageBackendData() {}
 };
 
+/* Forward declarations for Image derivatives. */
+class EGLImageImage;
+class SharedRGBImage;
+#ifdef MOZ_WIDGET_ANDROID
+class SurfaceTextureImage;
+#elif defined(XP_MACOSX)
+class MacIOSurfaceImage;
+#elif defined(MOZ_WIDGET_GONK)
+class OverlayImage;
+#endif
+
 /**
  * A class representing a buffer of pixel data. The data can be in one
  * of various formats including YCbCr.
  * 
  * Create an image using an ImageContainer. Fill the image with data, and
  * then call ImageContainer::SetImage to display it. An image must not be
  * modified after calling SetImage. Image implementations do not need to
  * perform locking; when filling an Image, the Image client is responsible
@@ -156,21 +168,31 @@ public:
     return nullptr;
   }
 
   virtual bool IsValid() { return true; }
 
   virtual uint8_t* GetBuffer() { return nullptr; }
 
   /**
-  * For use with the CompositableClient only (so that the later can
-  * synchronize the TextureClient with the TextureHost).
-  */
+   * For use with the CompositableClient only (so that the later can
+   * synchronize the TextureClient with the TextureHost).
+   */
   virtual TextureClient* GetTextureClient(CompositableClient* aClient) { return nullptr; }
 
+  /* Access to derived classes. */
+  virtual EGLImageImage* AsEGLImageImage() { return nullptr; }
+#ifdef MOZ_WIDGET_ANDROID
+  virtual SurfaceTextureImage* AsSurfaceTextureImage() { return nullptr; }
+#endif
+#ifdef XP_MACOSX
+  virtual MacIOSurfaceImage* AsMacIOSurfaceImage() { return nullptr; }
+#endif
+  virtual PlanarYCbCrImage* AsPlanarYCbCrImage() { return nullptr; }
+
 protected:
   Image(void* aImplData, ImageFormat aFormat) :
     mImplData(aImplData),
     mSerial(++sSerialCounter),
     mFormat(aFormat)
   {}
 
   // Protected destructor, to discourage deletion outside of Release():
@@ -247,20 +269,19 @@ class ImageFactory
 {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ImageFactory)
 protected:
   friend class ImageContainer;
 
   ImageFactory() {}
   virtual ~ImageFactory() {}
 
-  virtual already_AddRefed<Image> CreateImage(ImageFormat aFormat,
-                                              const gfx::IntSize &aScaleHint,
-                                              BufferRecycleBin *aRecycleBin);
-
+  virtual RefPtr<PlanarYCbCrImage> CreatePlanarYCbCrImage(
+    const gfx::IntSize& aScaleHint,
+    BufferRecycleBin *aRecycleBin);
 };
  
 /**
  * A class that manages Images for an ImageLayer. The only reason
  * we need a separate class here is that ImageLayers aren't threadsafe
  * (because layers can only be used on the main thread) and we want to
  * be able to set the current Image from any thread, to facilitate
  * video playback without involving the main thread, for example.
@@ -287,26 +308,24 @@ public:
 
   enum Mode { SYNCHRONOUS = 0x0, ASYNCHRONOUS = 0x01, ASYNCHRONOUS_OVERLAY = 0x02 };
 
   explicit ImageContainer(ImageContainer::Mode flag = SYNCHRONOUS);
 
   typedef uint32_t FrameID;
   typedef uint32_t ProducerID;
 
+  RefPtr<PlanarYCbCrImage> CreatePlanarYCbCrImage();
 
-  /**
-   * Create an Image in one of the given formats.
-   * Picks the "best" format from the list and creates an Image of that
-   * format.
-   * Returns null if this backend does not support any of the formats.
-   * Can be called on any thread. This method takes mReentrantMonitor
-   * when accessing thread-shared state.
-   */
-  B2G_ACL_EXPORT already_AddRefed<Image> CreateImage(ImageFormat aFormat);
+  // Factory methods for shared image types.
+  RefPtr<SharedRGBImage> CreateSharedRGBImage();
+
+#ifdef MOZ_WIDGET_GONK
+  RefPtr<OverlayImage> CreateOverlayImage();
+#endif
 
   struct NonOwningImage {
     explicit NonOwningImage(Image* aImage = nullptr,
                             TimeStamp aTimeStamp = TimeStamp(),
                             FrameID aFrameID = 0,
                             ProducerID aProducerID = 0)
       : mImage(aImage), mTimeStamp(aTimeStamp), mFrameID(aFrameID),
         mProducerID(aProducerID) {}
@@ -699,16 +718,18 @@ public:
   virtual SharedPlanarYCbCrImage *AsSharedPlanarYCbCrImage() { return nullptr; }
 
   virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   }
 
   virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const = 0;
 
+  PlanarYCbCrImage* AsPlanarYCbCrImage() { return this; }
+
 protected:
   already_AddRefed<gfx::SourceSurface> GetAsSourceSurface();
 
   void SetOffscreenFormat(gfxImageFormat aFormat) { mOffscreenFormat = aFormat; }
   gfxImageFormat GetOffscreenFormat();
 
   Data mData;
   gfx::IntSize mSize;
@@ -743,47 +764,31 @@ protected:
 
 /**
  * Currently, the data in a CairoImage surface is treated as being in the
  * device output color space. This class is very simple as all backends
  * have to know about how to deal with drawing a cairo image.
  */
 class CairoImage final : public Image {
 public:
-  struct Data {
-    gfx::IntSize mSize;
-    RefPtr<gfx::SourceSurface> mSourceSurface;
-  };
-
-  /**
-   * This can only be called on the main thread. It may add a reference
-   * to the surface (which will eventually be released on the main thread).
-   * The surface must not be modified after this call!!!
-   */
-  void SetData(const Data& aData)
-  {
-    mSize = aData.mSize;
-    mSourceSurface = aData.mSourceSurface;
-  }
-
   virtual already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override
   {
     RefPtr<gfx::SourceSurface> surface(mSourceSurface);
     return surface.forget();
   }
 
   virtual TextureClient* GetTextureClient(CompositableClient* aClient) override;
 
   virtual gfx::IntSize GetSize() override { return mSize; }
 
-  CairoImage();
+  CairoImage(const gfx::IntSize& aSize, gfx::SourceSurface* aSourceSurface);
   ~CairoImage();
 
+private:
   gfx::IntSize mSize;
-
   nsCountedRef<nsMainThreadSourceSurfaceRef> mSourceSurface;
   nsDataHashtable<nsUint32HashKey, RefPtr<TextureClient> >  mTextureClients;
 };
 
 #ifdef MOZ_WIDGET_GONK
 class OverlayImage : public Image {
   /**
    * OverlayImage is a special Image type that does not hold any buffer.
--- a/gfx/layers/MacIOSurfaceImage.h
+++ b/gfx/layers/MacIOSurfaceImage.h
@@ -12,28 +12,34 @@
 #include "mozilla/layers/TextureClient.h"
 
 namespace mozilla {
 
 namespace layers {
 
 class MacIOSurfaceImage : public Image {
 public:
-  void SetSurface(MacIOSurface* aSurface) { mSurface = aSurface; }
+  explicit MacIOSurfaceImage(MacIOSurface* aSurface)
+   : Image(nullptr, ImageFormat::MAC_IOSURFACE),
+     mSurface(aSurface)
+  {}
+
   MacIOSurface* GetSurface() { return mSurface; }
 
   gfx::IntSize GetSize() override {
     return gfx::IntSize(mSurface->GetDevicePixelWidth(), mSurface->GetDevicePixelHeight());
   }
 
   virtual already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override;
 
   virtual TextureClient* GetTextureClient(CompositableClient* aClient) override;
 
-  MacIOSurfaceImage() : Image(nullptr, ImageFormat::MAC_IOSURFACE) {}
+  virtual MacIOSurfaceImage* AsMacIOSurfaceImage() override {
+    return this;
+  }
 
 private:
   RefPtr<MacIOSurface> mSurface;
   RefPtr<TextureClient> mTextureClient;
 };
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/basic/BasicImages.cpp
+++ b/gfx/layers/basic/BasicImages.cpp
@@ -73,27 +73,20 @@ private:
   bool mDelayedConversion;
 };
 
 class BasicImageFactory : public ImageFactory
 {
 public:
   BasicImageFactory() {}
 
-  virtual already_AddRefed<Image> CreateImage(ImageFormat aFormat,
-                                              const gfx::IntSize &aScaleHint,
-                                              BufferRecycleBin *aRecycleBin)
+  virtual RefPtr<PlanarYCbCrImage>
+  CreatePlanarYCbCrImage(const gfx::IntSize& aScaleHint, BufferRecycleBin* aRecycleBin)
   {
-    RefPtr<Image> image;
-    if (aFormat == ImageFormat::PLANAR_YCBCR) {
-      image = new BasicPlanarYCbCrImage(aScaleHint, gfxPlatform::GetPlatform()->GetOffscreenFormat(), aRecycleBin);
-      return image.forget();
-    }
-
-    return ImageFactory::CreateImage(aFormat, aScaleHint, aRecycleBin);
+    return new BasicPlanarYCbCrImage(aScaleHint, gfxPlatform::GetPlatform()->GetOffscreenFormat(), aRecycleBin);
   }
 };
 
 bool
 BasicPlanarYCbCrImage::SetData(const Data& aData)
 {
   RecyclingPlanarYCbCrImage::SetData(aData);
 
--- a/gfx/layers/basic/BasicLayerManager.cpp
+++ b/gfx/layers/basic/BasicLayerManager.cpp
@@ -106,26 +106,28 @@ BasicLayerManager::PushGroupForLayer(gfx
     gfxMatrix oldMat = aContext->CurrentMatrix();
     aContext->SetMatrix(gfxMatrix());
     gfxRect rect = aContext->GetClipExtents();
     aContext->SetMatrix(oldMat);
     rect.RoundOut();
     IntRect surfRect;
     ToRect(rect).ToIntRect(&surfRect);
 
-    RefPtr<DrawTarget> dt = aContext->GetDrawTarget()->CreateSimilarDrawTarget(surfRect.Size(), SurfaceFormat::B8G8R8A8);
+    if (!surfRect.IsEmpty()) {
+      RefPtr<DrawTarget> dt = aContext->GetDrawTarget()->CreateSimilarDrawTarget(surfRect.Size(), SurfaceFormat::B8G8R8A8);
 
-    RefPtr<gfxContext> ctx = new gfxContext(dt, ToRect(rect).TopLeft());
-    ctx->SetMatrix(oldMat);
+      RefPtr<gfxContext> ctx = new gfxContext(dt, ToRect(rect).TopLeft());
+      ctx->SetMatrix(oldMat);
 
-    group.mGroupOffset = surfRect.TopLeft();
-    group.mGroupTarget = ctx;
+      group.mGroupOffset = surfRect.TopLeft();
+      group.mGroupTarget = ctx;
 
-    group.mMaskSurface = GetMaskForLayer(aLayer, &group.mMaskTransform);
-    return group;
+      group.mMaskSurface = GetMaskForLayer(aLayer, &group.mMaskTransform);
+      return group;
+    }
   }
 
   Matrix maskTransform;
   RefPtr<SourceSurface> maskSurf = GetMaskForLayer(aLayer, &maskTransform);
 
   if (aLayer->CanUseOpaqueSurface() &&
       ((didCompleteClip && aRegion.GetNumRects() == 1) ||
        !aContext->CurrentMatrix().HasNonIntegerTranslation())) {
--- a/gfx/layers/client/ImageClient.cpp
+++ b/gfx/layers/client/ImageClient.cpp
@@ -16,18 +16,16 @@
 #include "mozilla/gfx/Point.h"          // for IntSize
 #include "mozilla/gfx/Types.h"          // for SurfaceFormat, etc
 #include "mozilla/layers/CompositableClient.h"  // for CompositableClient
 #include "mozilla/layers/CompositableForwarder.h"
 #include "mozilla/layers/CompositorTypes.h"  // for CompositableType, etc
 #include "mozilla/layers/ISurfaceAllocator.h"
 #include "mozilla/layers/LayersSurfaces.h"  // for SurfaceDescriptor, etc
 #include "mozilla/layers/ShadowLayers.h"  // for ShadowLayerForwarder
-#include "mozilla/layers/SharedPlanarYCbCrImage.h"
-#include "mozilla/layers/SharedRGBImage.h"
 #include "mozilla/layers/TextureClient.h"  // for TextureClient, etc
 #include "mozilla/layers/TextureClientOGL.h"  // for SurfaceTextureClient
 #include "mozilla/mozalloc.h"           // for operator delete, etc
 #include "nsAutoPtr.h"                  // for nsRefPtr
 #include "nsCOMPtr.h"                   // for already_AddRefed
 #include "nsDebug.h"                    // for NS_WARNING, NS_ASSERTION
 #include "nsISupportsImpl.h"            // for Image::Release, etc
 #include "nsRect.h"                     // for mozilla::gfx::IntRect
@@ -199,28 +197,27 @@ ImageClientSingle::UpdateImage(ImageCont
         if (!status) {
           return false;
         }
       } else if (image->GetFormat() == ImageFormat::SURFACE_TEXTURE ||
                  image->GetFormat() == ImageFormat::EGLIMAGE) {
         gfx::IntSize size = image->GetSize();
 
         if (image->GetFormat() == ImageFormat::EGLIMAGE) {
-          EGLImageImage* typedImage = static_cast<EGLImageImage*>(image);
+          EGLImageImage* typedImage = image->AsEGLImageImage();
           texture = new EGLImageTextureClient(GetForwarder(),
                                               mTextureFlags,
                                               typedImage,
                                               size);
 #ifdef MOZ_WIDGET_ANDROID
         } else if (image->GetFormat() == ImageFormat::SURFACE_TEXTURE) {
-          SurfaceTextureImage* typedImage = static_cast<SurfaceTextureImage*>(image);
-          const SurfaceTextureImage::Data* data = typedImage->GetData();
+          SurfaceTextureImage* typedImage = image->AsSurfaceTextureImage();
           texture = new SurfaceTextureClient(GetForwarder(), mTextureFlags,
-                                             data->mSurfTex, size,
-                                             data->mOriginPos);
+                                             typedImage->GetSurfaceTexture(), size,
+                                             typedImage->GetOriginPos());
 #endif
         } else {
           MOZ_ASSERT(false, "Bad ImageFormat.");
         }
       } else {
         RefPtr<gfx::SourceSurface> surface = image->GetAsSourceSurface();
         MOZ_ASSERT(surface);
         texture = CreateTextureClientForDrawing(surface->GetFormat(), image->GetSize(),
@@ -315,37 +312,16 @@ ImageClientBridge::UpdateImage(ImageCont
   if (mAsyncContainerID == aContainer->GetAsyncContainerID()) {
     return true;
   }
   mAsyncContainerID = aContainer->GetAsyncContainerID();
   static_cast<ShadowLayerForwarder*>(GetForwarder())->AttachAsyncCompositable(mAsyncContainerID, mLayer);
   return true;
 }
 
-already_AddRefed<Image>
-ImageClientSingle::CreateImage(ImageFormat aFormat)
-{
-  RefPtr<Image> img;
-  switch (aFormat) {
-    case ImageFormat::PLANAR_YCBCR:
-      img = new SharedPlanarYCbCrImage(this);
-      return img.forget();
-    case ImageFormat::SHARED_RGB:
-      img = new SharedRGBImage(this);
-      return img.forget();
-#ifdef MOZ_WIDGET_GONK
-    case ImageFormat::GRALLOC_PLANAR_YCBCR:
-      img = new GrallocImage();
-      return img.forget();
-#endif
-    default:
-      return nullptr;
-  }
-}
-
 #ifdef MOZ_WIDGET_GONK
 ImageClientOverlay::ImageClientOverlay(CompositableForwarder* aFwd,
                                        TextureFlags aFlags)
   : ImageClient(aFwd, aFlags, CompositableType::IMAGE_OVERLAY)
 {
 }
 
 bool
@@ -370,25 +346,11 @@ ImageClientOverlay::UpdateImage(ImageCon
 
     OverlaySource source;
     source.handle() = OverlayHandle(overlayId);
     source.size() = size;
     GetForwarder()->UseOverlaySource(this, source, image->GetPictureRect());
   }
   return true;
 }
-
-already_AddRefed<Image>
-ImageClientOverlay::CreateImage(ImageFormat aFormat)
-{
-  RefPtr<Image> img;
-  switch (aFormat) {
-    case ImageFormat::OVERLAY_IMAGE:
-      img = new OverlayImage();
-      return img.forget();
-    default:
-      return nullptr;
-  }
-}
-
 #endif
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/client/ImageClient.h
+++ b/gfx/layers/client/ImageClient.h
@@ -24,16 +24,17 @@ namespace mozilla {
 namespace layers {
 
 class ClientLayer;
 class CompositableForwarder;
 class AsyncTransactionTracker;
 class Image;
 class ImageContainer;
 class ShadowableLayer;
+class ImageClientSingle;
 
 /**
  * Image clients are used by basic image layers on the content thread, they
  * always match with an ImageHost on the compositor thread. See
  * CompositableClient.h for information on connecting clients to hosts.
  */
 class ImageClient : public CompositableClient
 {
@@ -51,32 +52,32 @@ public:
 
   /**
    * Update this ImageClient from aContainer in aLayer
    * returns false if this is the wrong kind of ImageClient for aContainer.
    * Note that returning true does not necessarily imply success
    */
   virtual bool UpdateImage(ImageContainer* aContainer, uint32_t aContentFlags) = 0;
 
-  virtual already_AddRefed<Image> CreateImage(ImageFormat aFormat) = 0;
-
   void SetLayer(ClientLayer* aLayer) { mLayer = aLayer; }
   ClientLayer* GetLayer() const { return mLayer; }
 
   /**
    * asynchronously remove all the textures used by the image client.
    *
    */
   virtual void FlushAllImages(AsyncTransactionWaiter* aAsyncTransactionWaiter) {}
 
   virtual void RemoveTexture(TextureClient* aTexture) override;
 
   void RemoveTextureWithWaiter(TextureClient* aTexture,
                                AsyncTransactionWaiter* aAsyncTransactionWaiter = nullptr);
 
+  virtual ImageClientSingle* AsImageClientSingle() { return nullptr; }
+
 protected:
   ImageClient(CompositableForwarder* aFwd, TextureFlags aFlags,
               CompositableType aType);
 
   ClientLayer* mLayer;
   CompositableType mType;
   uint32_t mLastUpdateGenerationCounter;
 };
@@ -94,19 +95,19 @@ public:
   virtual bool UpdateImage(ImageContainer* aContainer, uint32_t aContentFlags) override;
 
   virtual void OnDetach() override;
 
   virtual bool AddTextureClient(TextureClient* aTexture) override;
 
   virtual TextureInfo GetTextureInfo() const override;
 
-  virtual already_AddRefed<Image> CreateImage(ImageFormat aFormat) override;
+  virtual void FlushAllImages(AsyncTransactionWaiter* aAsyncTransactionWaiter) override;
 
-  virtual void FlushAllImages(AsyncTransactionWaiter* aAsyncTransactionWaiter) override;
+  ImageClientSingle* AsImageClientSingle() override { return this; }
 
 protected:
   struct Buffer {
     RefPtr<TextureClient> mTextureClient;
     int32_t mImageSerial;
   };
   nsTArray<Buffer> mBuffers;
 };
@@ -130,22 +131,16 @@ public:
     return TextureInfo(mType);
   }
 
   virtual void SetIPDLActor(CompositableChild* aChild) override
   {
     MOZ_ASSERT(!aChild, "ImageClientBridge should not have IPDL actor");
   }
 
-  virtual already_AddRefed<Image> CreateImage(ImageFormat aFormat) override
-  {
-    NS_WARNING("Should not create an image through an ImageClientBridge");
-    return nullptr;
-  }
-
 protected:
   uint64_t mAsyncContainerID;
 };
 
 #ifdef MOZ_WIDGET_GONK
 /**
  * And ImageClient to handle opaque video stream.
  * Such video stream does not upload new Image for each frame.
@@ -155,17 +150,16 @@ protected:
  */
 class ImageClientOverlay : public ImageClient
 {
 public:
   ImageClientOverlay(CompositableForwarder* aFwd,
                      TextureFlags aFlags);
 
   virtual bool UpdateImage(ImageContainer* aContainer, uint32_t aContentFlags);
-  virtual already_AddRefed<Image> CreateImage(ImageFormat aFormat);
   TextureInfo GetTextureInfo() const override
   {
     return TextureInfo(CompositableType::IMAGE_OVERLAY);
   }
 };
 #endif
 
 } // namespace layers
--- a/gfx/layers/client/TextureClient.h
+++ b/gfx/layers/client/TextureClient.h
@@ -51,16 +51,17 @@ class PTextureChild;
 class TextureChild;
 class BufferTextureClient;
 class TextureClient;
 class TextureClientRecycleAllocator;
 #ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
 class TextureClientPool;
 #endif
 class KeepAlive;
+class GrallocTextureClientOGL;
 
 /**
  * TextureClient is the abstraction that allows us to share data between the
  * content and the compositor side.
  */
 
 enum TextureAllocationFlags {
   ALLOC_DEFAULT = 0,
@@ -229,16 +230,17 @@ public:
    */
   virtual bool AllocateForSurface(gfx::IntSize aSize,
                                   TextureAllocationFlags flags = ALLOC_DEFAULT)
   {
     return false;
   }
 
   virtual TextureClientYCbCr* AsTextureClientYCbCr() { return nullptr; }
+  virtual GrallocTextureClientOGL* AsGrallocTextureClientOGL() { return nullptr; }
 
   /**
    * Locks the shared data, allowing the caller to get access to it.
    *
    * Please always lock/unlock when accessing the shared data.
    * If Lock() returns false, you should not attempt to access the shared data.
    */
   virtual bool Lock(OpenMode aMode) { return IsValid(); }
--- a/gfx/layers/ipc/SharedRGBImage.cpp
+++ b/gfx/layers/ipc/SharedRGBImage.cpp
@@ -36,29 +36,26 @@ CreateSharedRGBImage(ImageContainer *aIm
                aImageFormat == gfxImageFormat::RGB16_565,
                "RGB formats supported only");
 
   if (!aImageContainer) {
     NS_WARNING("No ImageContainer to allocate SharedRGBImage");
     return nullptr;
   }
 
-  RefPtr<Image> image = aImageContainer->CreateImage(ImageFormat::SHARED_RGB);
-
-  if (!image) {
+  RefPtr<SharedRGBImage> rgbImage = aImageContainer->CreateSharedRGBImage();
+  if (!rgbImage) {
     NS_WARNING("Failed to create SharedRGBImage");
     return nullptr;
   }
-
-  RefPtr<SharedRGBImage> rgbImage = static_cast<SharedRGBImage*>(image.get());
   if (!rgbImage->Allocate(aSize, gfx::ImageFormatToSurfaceFormat(aImageFormat))) {
     NS_WARNING("Failed to allocate a shared image");
     return nullptr;
   }
-  return image.forget();
+  return rgbImage.forget();
 }
 
 SharedRGBImage::SharedRGBImage(ImageClient* aCompositable)
 : Image(nullptr, ImageFormat::SHARED_RGB)
 , mCompositable(aCompositable)
 {
   MOZ_COUNT_CTOR(SharedRGBImage);
 }
--- a/gfx/layers/opengl/GrallocTextureClient.h
+++ b/gfx/layers/opengl/GrallocTextureClient.h
@@ -59,16 +59,20 @@ public:
   virtual bool IsAllocated() const override;
 
   virtual bool ToSurfaceDescriptor(SurfaceDescriptor& aOutDescriptor) override;
 
   virtual void SetRemoveFromCompositableWaiter(AsyncTransactionWaiter* aWaiter) override;
 
   virtual void WaitForBufferOwnership(bool aWaitReleaseFence = true) override;
 
+  GrallocTextureClientOGL* AsGrallocTextureClientOGL() override {
+    return this;
+  }
+
   void SetTextureFlags(TextureFlags aFlags) { AddFlags(aFlags); }
 
   gfx::IntSize GetSize() const override { return mSize; }
 
   android::sp<android::GraphicBuffer> GetGraphicBuffer()
   {
     return mGraphicBuffer;
   }
--- a/gfx/layers/opengl/TextureClientOGL.cpp
+++ b/gfx/layers/opengl/TextureClientOGL.cpp
@@ -29,31 +29,32 @@ EGLImageTextureClient::EGLImageTextureCl
   , mSize(aSize)
   , mIsLocked(false)
 {
   MOZ_ASSERT(XRE_IsParentProcess(),
              "Can't pass an `EGLImage` between processes.");
 
   AddFlags(TextureFlags::DEALLOCATE_CLIENT);
 
-  if (aImage->GetData()->mOriginPos == gl::OriginPos::BottomLeft) {
+  if (aImage->GetOriginPos() == gl::OriginPos::BottomLeft) {
     AddFlags(TextureFlags::ORIGIN_BOTTOM_LEFT);
   }
 }
 
 bool
 EGLImageTextureClient::ToSurfaceDescriptor(SurfaceDescriptor& aOutDescriptor)
 {
   MOZ_ASSERT(IsValid());
   MOZ_ASSERT(IsAllocated());
 
-  const EGLImageImage::Data* data = mImage->GetData();
   const bool hasAlpha = true;
-  aOutDescriptor = EGLImageDescriptor((uintptr_t)data->mImage, (uintptr_t)data->mSync,
-                                      mSize, hasAlpha);
+  aOutDescriptor =
+    EGLImageDescriptor((uintptr_t)mImage->GetImage(),
+                       (uintptr_t)mImage->GetSync(),
+                       mImage->GetSize(), hasAlpha);
   return true;
 }
 
 bool
 EGLImageTextureClient::Lock(OpenMode mode)
   {
     MOZ_ASSERT(!mIsLocked);
     if (!IsValid() || !IsAllocated()) {
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -305,16 +305,17 @@ private:
   // and ignore the preference.
   DECL_GFX_PREF(Skip, "layers.componentalpha.enabled",         ComponentAlphaEnabled, bool, false);
 #else
   // If MOZ_GFX_OPTIMIZE_MOBILE is not defined, we actually take the
   // preference value, defaulting to true.
   DECL_GFX_PREF(Once, "layers.componentalpha.enabled",         ComponentAlphaEnabled, bool, true);
 #endif
   DECL_GFX_PREF(Live, "layers.composer2d.enabled",             Composer2DCompositionEnabled, bool, false);
+  DECL_GFX_PREF(Live, "layers.screen-recording.enabled",       ScreenRecordingEnabled, bool, false);
   DECL_GFX_PREF(Once, "layers.d3d11.disable-warp",             LayersD3D11DisableWARP, bool, false);
   DECL_GFX_PREF(Once, "layers.d3d11.force-warp",               LayersD3D11ForceWARP, bool, false);
   DECL_GFX_PREF(Live, "layers.deaa.enabled",                   LayersDEAAEnabled, bool, false);
   DECL_GFX_PREF(Live, "layers.draw-bigimage-borders",          DrawBigImageBorders, bool, false);
   DECL_GFX_PREF(Live, "layers.draw-borders",                   DrawLayerBorders, bool, false);
   DECL_GFX_PREF(Live, "layers.draw-tile-borders",              DrawTileBorders, bool, false);
   DECL_GFX_PREF(Live, "layers.draw-layer-info",                DrawLayerInfo, bool, false);
   DECL_GFX_PREF(Live, "layers.dump",                           LayersDump, bool, false);
--- a/image/ImageLogging.h
+++ b/image/ImageLogging.h
@@ -6,73 +6,72 @@
 
 #ifndef mozilla_image_ImageLogging_h
 #define mozilla_image_ImageLogging_h
 
 #include "mozilla/Logging.h"
 #include "prinrval.h"
 #include "nsString.h"
 
-// Declared in imgRequest.cpp.
-extern PRLogModuleInfo* GetImgLog();
+static mozilla::LazyLogModule gImgLog("imgRequest");
 
 #define GIVE_ME_MS_NOW() PR_IntervalToMilliseconds(PR_IntervalNow())
 
 using mozilla::LogLevel;
 
 class LogScope {
 public:
 
-  LogScope(PRLogModuleInfo* aLog, void* aFrom, const char* aFunc)
+  LogScope(mozilla::LogModule* aLog, void* aFrom, const char* aFunc)
     : mLog(aLog)
     , mFrom(aFrom)
     , mFunc(aFunc)
   {
     MOZ_LOG(mLog, LogLevel::Debug, ("%d [this=%p] %s {ENTER}\n",
                                 GIVE_ME_MS_NOW(), mFrom, mFunc));
   }
 
   /* const char * constructor */
-  LogScope(PRLogModuleInfo* aLog, void* from, const char* fn,
+  LogScope(mozilla::LogModule* aLog, void* from, const char* fn,
            const char* paramName, const char* paramValue)
     : mLog(aLog)
     , mFrom(from)
     , mFunc(fn)
   {
     MOZ_LOG(mLog, LogLevel::Debug, ("%d [this=%p] %s (%s=\"%s\") {ENTER}\n",
                                  GIVE_ME_MS_NOW(), mFrom, mFunc,
                                  paramName, paramValue));
   }
 
   /* void ptr constructor */
-  LogScope(PRLogModuleInfo* aLog, void* from, const char* fn,
+  LogScope(mozilla::LogModule* aLog, void* from, const char* fn,
            const char* paramName, const void* paramValue)
     : mLog(aLog)
     , mFrom(from)
     , mFunc(fn)
   {
     MOZ_LOG(mLog, LogLevel::Debug, ("%d [this=%p] %s (%s=%p) {ENTER}\n",
                                 GIVE_ME_MS_NOW(), mFrom, mFunc,
                                 paramName, paramValue));
   }
 
   /* int32_t constructor */
-  LogScope(PRLogModuleInfo* aLog, void* from, const char* fn,
+  LogScope(mozilla::LogModule* aLog, void* from, const char* fn,
            const char* paramName, int32_t paramValue)
     : mLog(aLog)
     , mFrom(from)
     , mFunc(fn)
   {
     MOZ_LOG(mLog, LogLevel::Debug, ("%d [this=%p] %s (%s=\"%d\") {ENTER}\n",
                                 GIVE_ME_MS_NOW(), mFrom, mFunc,
                                 paramName, paramValue));
   }
 
   /* uint32_t constructor */
-  LogScope(PRLogModuleInfo* aLog, void* from, const char* fn,
+  LogScope(mozilla::LogModule* aLog, void* from, const char* fn,
            const char* paramName, uint32_t paramValue)
     : mLog(aLog)
     , mFrom(from)
     , mFunc(fn)
   {
     MOZ_LOG(mLog, LogLevel::Debug, ("%d [this=%p] %s (%s=\"%d\") {ENTER}\n",
                                 GIVE_ME_MS_NOW(), mFrom, mFunc,
                                 paramName, paramValue));
@@ -80,60 +79,60 @@ public:
 
   ~LogScope()
   {
     MOZ_LOG(mLog, LogLevel::Debug, ("%d [this=%p] %s {EXIT}\n",
                                 GIVE_ME_MS_NOW(), mFrom, mFunc));
   }
 
 private:
-  PRLogModuleInfo* mLog;
+  mozilla::LogModule* mLog;
   void* mFrom;
   const char* mFunc;
 };
 
 class LogFunc {
 public:
-  LogFunc(PRLogModuleInfo* aLog, void* from, const char* fn)
+  LogFunc(mozilla::LogModule* aLog, void* from, const char* fn)
   {
     MOZ_LOG(aLog, LogLevel::Debug, ("%d [this=%p] %s\n",
                                 GIVE_ME_MS_NOW(), from, fn));
   }
 
-  LogFunc(PRLogModuleInfo* aLog, void* from, const char* fn,
+  LogFunc(mozilla::LogModule* aLog, void* from, const char* fn,
           const char* paramName, const char* paramValue)
   {
     MOZ_LOG(aLog, LogLevel::Debug, ("%d [this=%p] %s (%s=\"%s\")\n",
                                 GIVE_ME_MS_NOW(), from, fn,
                                 paramName, paramValue));
   }
 
-  LogFunc(PRLogModuleInfo* aLog, void* from, const char* fn,
+  LogFunc(mozilla::LogModule* aLog, void* from, const char* fn,
           const char* paramName, const void* paramValue)
   {
     MOZ_LOG(aLog, LogLevel::Debug, ("%d [this=%p] %s (%s=\"%p\")\n",
                                 GIVE_ME_MS_NOW(), from, fn,
                                 paramName, paramValue));
   }
 
 
-  LogFunc(PRLogModuleInfo* aLog, void* from, const char* fn,
+  LogFunc(mozilla::LogModule* aLog, void* from, const char* fn,
           const char* paramName, uint32_t paramValue)
   {
     MOZ_LOG(aLog, LogLevel::Debug, ("%d [this=%p] %s (%s=\"%d\")\n",
                                 GIVE_ME_MS_NOW(), from, fn,
                                 paramName, paramValue));
   }
 
 };
 
 
 class LogMessage {
 public:
-  LogMessage(PRLogModuleInfo* aLog, void* from, const char* fn,
+  LogMessage(mozilla::LogModule* aLog, void* from, const char* fn,
              const char* msg)
   {
     MOZ_LOG(aLog, LogLevel::Debug, ("%d [this=%p] %s -- %s\n",
                                 GIVE_ME_MS_NOW(), from, fn, msg));
   }
 };
 
 #define LOG_SCOPE_APPEND_LINE_NUMBER_PASTE(id, line) id ## line
--- a/image/ProgressTracker.cpp
+++ b/image/ProgressTracker.cpp
@@ -152,26 +152,26 @@ class AsyncNotifyRunnable : public nsRun
     nsTArray<RefPtr<IProgressObserver>> mObservers;
 };
 
 void
 ProgressTracker::Notify(IProgressObserver* aObserver)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  if (MOZ_LOG_TEST(GetImgLog(), LogLevel::Debug)) {
+  if (MOZ_LOG_TEST(gImgLog, LogLevel::Debug)) {
     RefPtr<Image> image = GetImage();
     if (image && image->GetURI()) {
       RefPtr<ImageURL> uri(image->GetURI());
       nsAutoCString spec;
       uri->GetSpec(spec);
-      LOG_FUNC_WITH_PARAM(GetImgLog(),
+      LOG_FUNC_WITH_PARAM(gImgLog,
                           "ProgressTracker::Notify async", "uri", spec.get());
     } else {
-      LOG_FUNC_WITH_PARAM(GetImgLog(),
+      LOG_FUNC_WITH_PARAM(gImgLog,
                           "ProgressTracker::Notify async", "uri", "<unknown>");
     }
   }
 
   aObserver->SetNotificationsDeferred(true);
 
   // If we have an existing runnable that we can use, we just append this
   // observer to its list of observers to be notified. This ensures we don't
@@ -221,23 +221,23 @@ class AsyncNotifyCurrentStateRunnable : 
     RefPtr<Image> mImage;
 };
 
 void
 ProgressTracker::NotifyCurrentState(IProgressObserver* aObserver)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  if (MOZ_LOG_TEST(GetImgLog(), LogLevel::Debug)) {
+  if (MOZ_LOG_TEST(gImgLog, LogLevel::Debug)) {
     RefPtr<Image> image = GetImage();
     nsAutoCString spec;
     if (image && image->GetURI()) {
       image->GetURI()->GetSpec(spec);
     }
-    LOG_FUNC_WITH_PARAM(GetImgLog(),
+    LOG_FUNC_WITH_PARAM(gImgLog,
                         "ProgressTracker::NotifyCurrentState", "uri", spec.get());
   }
 
   aObserver->SetNotificationsDeferred(true);
 
   nsCOMPtr<nsIRunnable> ev = new AsyncNotifyCurrentStateRunnable(this,
                                                                  aObserver);
   NS_DispatchToCurrentThread(ev);
@@ -401,17 +401,17 @@ ProgressTracker::SyncNotify(IProgressObs
   MOZ_ASSERT(NS_IsMainThread());
 
   RefPtr<Image> image = GetImage();
 
   nsAutoCString spec;
   if (image && image->GetURI()) {
     image->GetURI()->GetSpec(spec);
   }
-  LOG_SCOPE_WITH_PARAM(GetImgLog(),
+  LOG_SCOPE_WITH_PARAM(gImgLog,
                        "ProgressTracker::SyncNotify", "uri", spec.get());
 
   nsIntRect rect;
   if (image) {
     if (NS_FAILED(image->GetWidth(&rect.width)) ||
         NS_FAILED(image->GetHeight(&rect.height))) {
       // Either the image has no intrinsic size, or it has an error.
       rect = GetMaxSizedIntRect();
--- a/image/RasterImage.cpp
+++ b/image/RasterImage.cpp
@@ -623,27 +623,21 @@ RasterImage::GetCurrentImage(ImageContai
   Tie(drawResult, surface) =
     GetFrameInternal(mSize, FRAME_CURRENT, aFlags | FLAG_ASYNC_NOTIFY);
   if (!surface) {
     // The OS threw out some or all of our buffer. We'll need to wait for the
     // redecode (which was automatically triggered by GetFrame) to complete.
     return MakePair(drawResult, RefPtr<layers::Image>());
   }
 
-  CairoImage::Data cairoData;
-  GetWidth(&cairoData.mSize.width);
-  GetHeight(&cairoData.mSize.height);
-  cairoData.mSourceSurface = surface;
+  IntSize size;
+  GetWidth(&size.width);
+  GetHeight(&size.height);
 
-  RefPtr<layers::Image> image =
-    aContainer->CreateImage(ImageFormat::CAIRO_SURFACE);
-  MOZ_ASSERT(image);
-
-  static_cast<CairoImage*>(image.get())->SetData(cairoData);
-
+  RefPtr<layers::Image> image = new layers::CairoImage(size, surface);
   return MakePair(drawResult, Move(image));
 }
 
 NS_IMETHODIMP_(bool)
 RasterImage::IsImageContainerAvailable(LayerManager* aManager, uint32_t aFlags)
 {
   int32_t maxTextureSize = aManager->GetMaxTextureSize();
   if (!mHasSize ||
@@ -1635,17 +1629,17 @@ RasterImage::DoError()
   SurfaceCache::UnlockImage(ImageKey(this));
 
   // Release all frames from the surface cache.
   SurfaceCache::RemoveImage(ImageKey(this));
 
   // Invalidate to get rid of any partially-drawn image content.
   NotifyProgress(NoProgress, IntRect(0, 0, mSize.width, mSize.height));
 
-  MOZ_LOG(GetImgLog(), LogLevel::Error,
+  MOZ_LOG(gImgLog, LogLevel::Error,
           ("RasterImage: [this=%p] Error detected for image\n", this));
 }
 
 /* static */ void
 RasterImage::HandleErrorWorker::DispatchIfNeeded(RasterImage* aImage)
 {
   RefPtr<HandleErrorWorker> worker = new HandleErrorWorker(aImage);
   NS_DispatchToMainThread(worker);
--- a/image/StreamingLexer.h
+++ b/image/StreamingLexer.h
@@ -1,92 +1,138 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /**
  * StreamingLexer is a lexing framework designed to make it simple to write
  * image decoders without worrying about the details of how the data is arriving
  * from the network.
  */
 
 #ifndef mozilla_image_StreamingLexer_h
 #define mozilla_image_StreamingLexer_h
 
 #include <algorithm>
 #include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
 #include "mozilla/Maybe.h"
+#include "mozilla/Variant.h"
 #include "mozilla/Vector.h"
 
 namespace mozilla {
 namespace image {
 
 /// Buffering behaviors for StreamingLexer transitions.
 enum class BufferingStrategy
 {
   BUFFERED,   // Data will be buffered and processed in one chunk.
   UNBUFFERED  // Data will be processed as it arrives, in multiple chunks.
 };
 
-/// @return true if @aState is a terminal state.
-template <typename State>
-bool IsTerminalState(State aState)
+/// The result of a call to StreamingLexer::Lex().
+enum class TerminalState
 {
-  return aState == State::SUCCESS ||
-         aState == State::FAILURE;
-}
+  SUCCESS,
+  FAILURE
+};
 
 /**
  * LexerTransition is a type used to give commands to the lexing framework.
  * Code that uses StreamingLexer can create LexerTransition values using the
  * static methods on Transition, and then return them to the lexing framework
  * for execution.
  */
 template <typename State>
 class LexerTransition
 {
 public:
-  State NextState() const { return mNextState; }
-  State UnbufferedState() const { return *mUnbufferedState; }
-  size_t Size() const { return mSize; }
-  BufferingStrategy Buffering() const { return mBufferingStrategy; }
+  // This is implicit so that Terminate{Success,Failure}() can return a
+  // TerminalState and have it implicitly converted to a
+  // LexerTransition<State>, which avoids the need for a "<State>"
+  // qualification to the Terminate{Success,Failure}() callsite.
+  MOZ_IMPLICIT LexerTransition(TerminalState aFinalState)
+    : mNextState(aFinalState)
+  {}
+
+  bool NextStateIsTerminal() const
+  {
+    return mNextState.template is<TerminalState>();
+  }
+
+  TerminalState NextStateAsTerminal() const
+  {
+    return mNextState.template as<TerminalState>();
+  }
+
+  State NextState() const
+  {
+    return mNextState.template as<NonTerminalState>().mState;
+  }
+
+  State UnbufferedState() const
+  {
+    return *mNextState.template as<NonTerminalState>().mUnbufferedState;
+  }
+
+  size_t Size() const
+  {
+    return mNextState.template as<NonTerminalState>().mSize;
+  }
+
+  BufferingStrategy Buffering() const
+  {
+    return mNextState.template as<NonTerminalState>().mBufferingStrategy;
+  }
 
 private:
   friend struct Transition;
 
-  LexerTransition(const State& aNextState,
+  LexerTransition(State aNextState,
                   const Maybe<State>& aUnbufferedState,
                   size_t aSize,
                   BufferingStrategy aBufferingStrategy)
-    : mNextState(aNextState)
-    , mUnbufferedState(aUnbufferedState)
-    , mSize(aSize)
-    , mBufferingStrategy(aBufferingStrategy)
+    : mNextState(NonTerminalState(aNextState, aUnbufferedState, aSize,
+                                  aBufferingStrategy))
+  {}
+
+  struct NonTerminalState
   {
-    MOZ_ASSERT_IF(mBufferingStrategy == BufferingStrategy::UNBUFFERED,
-                  mUnbufferedState);
-    MOZ_ASSERT_IF(mUnbufferedState,
-                  mBufferingStrategy == BufferingStrategy::UNBUFFERED);
-  }
+    State mState;
+    Maybe<State> mUnbufferedState;
+    size_t mSize;
+    BufferingStrategy mBufferingStrategy;
 
-  State mNextState;
-  Maybe<State> mUnbufferedState;
-  size_t mSize;
-  BufferingStrategy mBufferingStrategy;
+    NonTerminalState(State aState,
+                     const Maybe<State>& aUnbufferedState,
+                     size_t aSize,
+                     BufferingStrategy aBufferingStrategy)
+      : mState(aState)
+      , mUnbufferedState(aUnbufferedState)
+      , mSize(aSize)
+      , mBufferingStrategy(aBufferingStrategy)
+    {
+      MOZ_ASSERT_IF(mBufferingStrategy == BufferingStrategy::UNBUFFERED,
+                    mUnbufferedState);
+      MOZ_ASSERT_IF(mUnbufferedState,
+                    mBufferingStrategy == BufferingStrategy::UNBUFFERED);
+    }
+  };
+  Variant<NonTerminalState, TerminalState> mNextState;
 };
 
 struct Transition
 {
   /// Transition to @aNextState, buffering @aSize bytes of data.
   template <typename State>
   static LexerTransition<State>
   To(const State& aNextState, size_t aSize)
   {
-    MOZ_ASSERT(!IsTerminalState(aNextState));
     return LexerTransition<State>(aNextState, Nothing(), aSize,
                                   BufferingStrategy::BUFFERED);
   }
 
   /**
    * Transition to @aNextState via @aUnbufferedState, reading @aSize bytes of
    * data unbuffered.
    *
@@ -97,83 +143,92 @@ struct Transition
    * @aNextState will always be reached unless lexing terminates early.
    */
   template <typename State>
   static LexerTransition<State>
   ToUnbuffered(const State& aNextState,
                const State& aUnbufferedState,
                size_t aSize)
   {
-    MOZ_ASSERT(!IsTerminalState(aNextState));
-    MOZ_ASSERT(!IsTerminalState(aUnbufferedState));
     return LexerTransition<State>(aNextState, Some(aUnbufferedState), aSize,
                                   BufferingStrategy::UNBUFFERED);
   }
 
   /**
    * Continue receiving unbuffered data. @aUnbufferedState should be the same
    * state as the @aUnbufferedState specified in the preceding call to
    * ToUnbuffered().
    *
    * This should be used during an unbuffered read initiated by ToUnbuffered().
    */
   template <typename State>
   static LexerTransition<State>
   ContinueUnbuffered(const State& aUnbufferedState)
   {
-    MOZ_ASSERT(!IsTerminalState(aUnbufferedState));
     return LexerTransition<State>(aUnbufferedState, Nothing(), 0,
                                   BufferingStrategy::BUFFERED);
   }
 
   /**
-   * Terminate lexing, ending up in terminal state @aFinalState.
+   * Terminate lexing, ending up in terminal state SUCCESS. (The implicit
+   * LexerTransition constructor will convert the result to a LexerTransition
+   * as needed.)
    *
-   * No more data will be delivered after Terminate() is used.
+   * No more data will be delivered after this function is used.
    */
-  template <typename State>
-  static LexerTransition<State>
-  Terminate(const State& aFinalState)
+  static TerminalState
+  TerminateSuccess()
   {
-    MOZ_ASSERT(IsTerminalState(aFinalState));
-    return LexerTransition<State>(aFinalState, Nothing(), 0,
-                                  BufferingStrategy::BUFFERED);
+    return TerminalState::SUCCESS;
+  }
+
+  /**
+   * Terminate lexing, ending up in terminal state FAILURE. (The implicit
+   * LexerTransition constructor will convert the result to a LexerTransition
+   * as needed.)
+   *
+   * No more data will be delivered after this function is used.
+   */
+  static TerminalState
+  TerminateFailure()
+  {
+    return TerminalState::FAILURE;
   }
 
 private:
   Transition();
 };
 
 /**
  * StreamingLexer is a lexing framework designed to make it simple to write
  * image decoders without worrying about the details of how the data is arriving
  * from the network.
  *
  * To use StreamingLexer:
  *
  *  - Create a State type. This should be an |enum class| listing all of the
  *    states that you can be in while lexing the image format you're trying to
- *    read. It must contain the two terminal states SUCCESS and FAILURE.
+ *    read.
  *
  *  - Add an instance of StreamingLexer<State> to your decoder class. Initialize
  *    it with a Transition::To() the state that you want to start lexing in.
  *
  *  - In your decoder's WriteInternal method(), call Lex(), passing in the input
  *    data and length that are passed to WriteInternal(). You also need to pass
  *    a lambda which dispatches to lexing code for each state based on the State
  *    value that's passed in. The lambda generally should just continue a
  *    |switch| statement that calls different methods for each State value. Each
  *    method should return a LexerTransition<State>, which the lambda should
  *    return in turn.
  *
  *  - Write the methods that actually implement lexing for your image format.
  *    These methods should return either Transition::To(), to move on to another
- *    state, or Transition::Terminate(), if lexing has terminated in either
- *    success or failure. (There are also additional transitions for unbuffered
- *    reads; see below.)
+ *    state, or Transition::Terminate{Success,Failure}(), if lexing has
+ *    terminated in either success or failure. (There are also additional
+ *    transitions for unbuffered reads; see below.)
  *
  * That's all there is to it. The StreamingLexer will track your position in the
  * input and buffer enough data so that your lexing methods can process
  * everything in one pass. Lex() returns Nothing() if more data is needed, in
  * which case you should just return from WriteInternal(). If lexing reaches a
  * terminal state, Lex() returns Some(State::SUCCESS) or Some(State::FAILURE),
  * and you can check which one to determine if lexing succeeded or failed and do
  * any necessary cleanup.
@@ -203,57 +258,57 @@ class StreamingLexer
 {
 public:
   explicit StreamingLexer(LexerTransition<State> aStartState)
     : mTransition(aStartState)
     , mToReadUnbuffered(0)
   { }
 
   template <typename Func>
-  Maybe<State> Lex(const char* aInput, size_t aLength, Func aFunc)
+  Maybe<TerminalState> Lex(const char* aInput, size_t aLength, Func aFunc)
   {
-    if (IsTerminalState(mTransition.NextState())) {
+    if (mTransition.NextStateIsTerminal()) {
       // We've already reached a terminal state. We never deliver any more data
       // in this case; just return the terminal state again immediately.
-      return Some(mTransition.NextState());
+      return Some(mTransition.NextStateAsTerminal());
     }
 
     if (mToReadUnbuffered > 0) {
       // We're continuing an unbuffered read.
 
       MOZ_ASSERT(mBuffer.empty(),
                  "Shouldn't be continuing an unbuffered read and a buffered "
                  "read at the same time");
 
       size_t toRead = std::min(mToReadUnbuffered, aLength);
 
-      // Call aFunc with the unbuffered state to indicate that we're in the middle
-      // of an unbuffered read. We enforce that any state transition passed back
-      // to us is either a terminal states or takes us back to the unbuffered
-      // state.
+      // Call aFunc with the unbuffered state to indicate that we're in the
+      // middle of an unbuffered read. We enforce that any state transition
+      // passed back to us is either a terminal state or takes us back to the
+      // unbuffered state.
       LexerTransition<State> unbufferedTransition =
         aFunc(mTransition.UnbufferedState(), aInput, toRead);
-      if (IsTerminalState(unbufferedTransition.NextState())) {
+      if (unbufferedTransition.NextStateIsTerminal()) {
         mTransition = unbufferedTransition;
-        return Some(mTransition.NextState());  // Done!
+        return Some(mTransition.NextStateAsTerminal());  // Done!
       }
       MOZ_ASSERT(mTransition.UnbufferedState() ==
                    unbufferedTransition.NextState());
 
       aInput += toRead;
       aLength -= toRead;
       mToReadUnbuffered -= toRead;
       if (mToReadUnbuffered != 0) {
         return Nothing();  // Need more input.
       }
 
       // We're done with the unbuffered read, so transition to the next state.
       mTransition = aFunc(mTransition.NextState(), nullptr, 0);
-      if (IsTerminalState(mTransition.NextState())) {
-        return Some(mTransition.NextState());  // Done!
+      if (mTransition.NextStateIsTerminal()) {
+        return Some(mTransition.NextStateAsTerminal());  // Done!
       }
     } else if (0 < mBuffer.length()) {
       // We're continuing a buffered read.
 
       MOZ_ASSERT(mToReadUnbuffered == 0,
                  "Shouldn't be continuing an unbuffered read and a buffered "
                  "read at the same time");
       MOZ_ASSERT(mBuffer.length() < mTransition.Size(),
@@ -267,18 +322,18 @@ public:
       if (mBuffer.length() != mTransition.Size()) {
         return Nothing();  // Need more input.
       }
 
       // We've buffered everything, so transition to the next state.
       mTransition =
         aFunc(mTransition.NextState(), mBuffer.begin(), mBuffer.length());
       mBuffer.clear();
-      if (IsTerminalState(mTransition.NextState())) {
-        return Some(mTransition.NextState());  // Done!
+      if (mTransition.NextStateIsTerminal()) {
+        return Some(mTransition.NextStateAsTerminal());  // Done!
       }
     }
 
     MOZ_ASSERT(mToReadUnbuffered == 0);
     MOZ_ASSERT(mBuffer.empty());
 
     // Process states as long as we continue to have enough input to do so.
     while (mTransition.Size() <= aLength) {
@@ -286,63 +341,63 @@ public:
 
       if (mTransition.Buffering() == BufferingStrategy::BUFFERED) {
         mTransition = aFunc(mTransition.NextState(), aInput, toRead);
       } else {
         MOZ_ASSERT(mTransition.Buffering() == BufferingStrategy::UNBUFFERED);
 
         // Call aFunc with the unbuffered state to indicate that we're in the
         // middle of an unbuffered read. We enforce that any state transition
-        // passed back to us is either a terminal states or takes us back to the
+        // passed back to us is either a terminal state or takes us back to the
         // unbuffered state.
         LexerTransition<State> unbufferedTransition =
           aFunc(mTransition.UnbufferedState(), aInput, toRead);
-        if (IsTerminalState(unbufferedTransition.NextState())) {
+        if (unbufferedTransition.NextStateIsTerminal()) {
           mTransition = unbufferedTransition;
-          return Some(mTransition.NextState());  // Done!
+          return Some(mTransition.NextStateAsTerminal());  // Done!
         }
         MOZ_ASSERT(mTransition.UnbufferedState() ==
                      unbufferedTransition.NextState());
 
         // We're done with the unbuffered read, so transition to the next state.
         mTransition = aFunc(mTransition.NextState(), nullptr, 0);
       }
 
       aInput += toRead;
       aLength -= toRead;
 
-      if (IsTerminalState(mTransition.NextState())) {
-        return Some(mTransition.NextState());  // Done!
+      if (mTransition.NextStateIsTerminal()) {
+        return Some(mTransition.NextStateAsTerminal());  // Done!
       }
     }
 
     if (aLength == 0) {
       // We finished right at a transition point. Just wait for more data.
       return Nothing();
     }
 
     // If the next state is unbuffered, deliver what we can and then wait.
     if (mTransition.Buffering() == BufferingStrategy::UNBUFFERED) {
       LexerTransition<State> unbufferedTransition =
         aFunc(mTransition.UnbufferedState(), aInput, aLength);
-      if (IsTerminalState(unbufferedTransition.NextState())) {
+      if (unbufferedTransition.NextStateIsTerminal()) {
         mTransition = unbufferedTransition;
-        return Some(mTransition.NextState());  // Done!
+        return Some(mTransition.NextStateAsTerminal());  // Done!
       }
       MOZ_ASSERT(mTransition.UnbufferedState() ==
                    unbufferedTransition.NextState());
 
       mToReadUnbuffered = mTransition.Size() - aLength;
       return Nothing();  // Need more input.
     }
-    
+
     // If the next state is buffered, buffer what we can and then wait.
     MOZ_ASSERT(mTransition.Buffering() == BufferingStrategy::BUFFERED);
     if (!mBuffer.reserve(mTransition.Size())) {
-      return Some(State::FAILURE);  // Done due to allocation failure.
+      return Some(TerminalState::FAILURE);  // Done due to allocation failure.
     }
     mBuffer.append(aInput, aLength);
     return Nothing();  // Need more input.
   }
 
 private:
   Vector<char, InlineBufferSize> mBuffer;
   LexerTransition<State> mTransition;
--- a/image/decoders/nsBMPDecoder.cpp
+++ b/image/decoders/nsBMPDecoder.cpp
@@ -157,25 +157,17 @@ Set4BitPixel(uint32_t*& aDecoded, uint8_
   SetPixel(aDecoded, idx, aColors);
   if (--aCount > 0) {
     idx = aData & 0xF;
     SetPixel(aDecoded, idx, aColors);
     --aCount;
   }
 }
 
-static PRLogModuleInfo*
-GetBMPLog()
-{
-  static PRLogModuleInfo* sBMPLog;
-  if (!sBMPLog) {
-    sBMPLog = PR_NewLogModule("BMPDecoder");
-  }
-  return sBMPLog;
-}
+static mozilla::LazyLogModule sBMPLog("BMPDecoder");
 
 // The length of the mBIHSize field in the info header.
 static const uint32_t BIHSIZE_FIELD_LENGTH = 4;
 
 nsBMPDecoder::nsBMPDecoder(RasterImage* aImage, State aState, size_t aLength)
   : Decoder(aImage)
   , mLexer(Transition::To(aState, aLength))
   , mIsWithinICO(false)
@@ -432,59 +424,49 @@ nsBMPDecoder::FinishRow()
 
 void
 nsBMPDecoder::WriteInternal(const char* aBuffer, uint32_t aCount)
 {
   MOZ_ASSERT(!HasError(), "Shouldn't call WriteInternal after error!");
   MOZ_ASSERT(aBuffer);
   MOZ_ASSERT(aCount > 0);
 
-  Maybe<State> terminalState =
+  Maybe<TerminalState> terminalState =
     mLexer.Lex(aBuffer, aCount, [=](State aState,
                                     const char* aData, size_t aLength) {
       switch (aState) {
         case State::FILE_HEADER:      return ReadFileHeader(aData, aLength);
         case State::INFO_HEADER_SIZE: return ReadInfoHeaderSize(aData, aLength);
         case State::INFO_HEADER_REST: return ReadInfoHeaderRest(aData, aLength);
         case State::BITFIELDS:        return ReadBitfields(aData, aLength);
         case State::COLOR_TABLE:      return ReadColorTable(aData, aLength);
         case State::GAP:              return SkipGap();
         case State::PIXEL_ROW:        return ReadPixelRow(aData);
         case State::RLE_SEGMENT:      return ReadRLESegment(aData);
         case State::RLE_DELTA:        return ReadRLEDelta(aData);
         case State::RLE_ABSOLUTE:     return ReadRLEAbsolute(aData, aLength);
         default:
-          MOZ_ASSERT_UNREACHABLE("Unknown State");
-          return Transition::Terminate(State::FAILURE);
+          MOZ_CRASH("Unknown State");
       }
     });
 
-  if (!terminalState) {
-    return;  // Need more data.
-  }
-
-  if (*terminalState == State::FAILURE) {
+  if (terminalState == Some(TerminalState::FAILURE)) {
     PostDataError();
-    return;
   }
-
-  MOZ_ASSERT(*terminalState == State::SUCCESS);
-
-  return;
 }
 
 LexerTransition<nsBMPDecoder::State>
 nsBMPDecoder::ReadFileHeader(const char* aData, size_t aLength)
 {
   mPreGapLength += aLength;
 
   bool signatureOk = aData[0] == 'B' && aData[1] == 'M';
   if (!signatureOk) {
     PostDataError();
-    return Transition::Terminate(State::FAILURE);
+    return Transition::TerminateFailure();
   }
 
   // We ignore the filesize (aData + 2) and reserved (aData + 6) fields.
 
   mH.mDataOffset = LittleEndian::readUint32(aData + 10);
 
   return Transition::To(State::INFO_HEADER_SIZE, BIHSIZE_FIELD_LENGTH);
 }
@@ -501,17 +483,17 @@ nsBMPDecoder::ReadInfoHeaderSize(const c
   bool bihSizeOk = mH.mBIHSize == InfoHeaderLength::WIN_V2 ||
                    mH.mBIHSize == InfoHeaderLength::WIN_V3 ||
                    mH.mBIHSize == InfoHeaderLength::WIN_V4 ||
                    mH.mBIHSize == InfoHeaderLength::WIN_V5 ||
                    (mH.mBIHSize >= InfoHeaderLength::OS2_V2_MIN &&
                     mH.mBIHSize <= InfoHeaderLength::OS2_V2_MAX);
   if (!bihSizeOk) {
     PostDataError();
-    return Transition::Terminate(State::FAILURE);
+    return Transition::TerminateFailure();
   }
   // ICO BMPs must have a WinVMPv3 header. nsICODecoder should have already
   // terminated decoding if this isn't the case.
   MOZ_ASSERT_IF(mIsWithinICO, mH.mBIHSize == InfoHeaderLength::WIN_V3);
 
   return Transition::To(State::INFO_HEADER_REST,
                         mH.mBIHSize - BIHSIZE_FIELD_LENGTH);
 }
@@ -543,44 +525,44 @@ nsBMPDecoder::ReadInfoHeaderRest(const c
     // We ignore the important_colors (aData + 36) field.
 
     // For WinBMPv4, WinBMPv5 and (possibly) OS2-BMPv2 there are additional
     // fields in the info header which we ignore, with the possible exception
     // of the color bitfields (see below).
   }
 
   // Run with NSPR_LOG_MODULES=BMPDecoder:4 set to see this output.
-  MOZ_LOG(GetBMPLog(), LogLevel::Debug,
+  MOZ_LOG(sBMPLog, LogLevel::Debug,
           ("BMP: bihsize=%u, %d x %d, bpp=%u, compression=%u, colors=%u\n",
           mH.mBIHSize, mH.mWidth, mH.mHeight, uint32_t(mH.mBpp),
           mH.mCompression, mH.mNumColors));
 
   // BMPs with negative width are invalid. Also, reject extremely wide images
   // to keep the math sane. And reject INT_MIN as a height because you can't
   // get its absolute value (because -INT_MIN is one more than INT_MAX).
   const int32_t k64KWidth = 0x0000FFFF;
   bool sizeOk = 0 <= mH.mWidth && mH.mWidth <= k64KWidth &&
                 mH.mHeight != INT_MIN;
   if (!sizeOk) {
     PostDataError();
-    return Transition::Terminate(State::FAILURE);
+    return Transition::TerminateFailure();
   }
 
   // Check mBpp and mCompression.
   bool bppCompressionOk =
     (mH.mCompression == Compression::RGB &&
       (mH.mBpp ==  1 || mH.mBpp ==  4 || mH.mBpp ==  8 ||
        mH.mBpp == 16 || mH.mBpp == 24 || mH.mBpp == 32)) ||
     (mH.mCompression == Compression::RLE8 && mH.mBpp == 8) ||
     (mH.mCompression == Compression::RLE4 && mH.mBpp == 4) ||
     (mH.mCompression == Compression::BITFIELDS &&
       (mH.mBpp == 16 || mH.mBpp == 32));
   if (!bppCompressionOk) {
     PostDataError();
-    return Transition::Terminate(State::FAILURE);
+    return Transition::TerminateFailure();
   }
 
   // Post our size to the superclass.
   uint32_t absHeight = AbsoluteHeight();
   PostSize(mH.mWidth, absHeight);
   mCurrentRow = absHeight;
 
   // Round it up to the nearest byte count, then pad to 4-byte boundary.
@@ -647,17 +629,17 @@ nsBMPDecoder::ReadBitfields(const char* 
      mBitFields.mAlpha.IsPresent());
   if (mMayHaveTransparency) {
     PostHasTransparency();
   }
 
   // We've now read all the headers. If we're doing a metadata decode, we're
   // done.
   if (IsMetadataDecode()) {
-    return Transition::Terminate(State::SUCCESS);
+    return Transition::TerminateSuccess();
   }
 
   // Set up the color table, if present; it'll be filled in by ReadColorTable().
   if (mH.mBpp <= 8) {
     mNumColors = 1 << mH.mBpp;
     if (0 < mH.mNumColors && mH.mNumColors < mNumColors) {
       mNumColors = mH.mNumColors;
     }
@@ -672,28 +654,28 @@ nsBMPDecoder::ReadBitfields(const char* 
   }
 
   MOZ_ASSERT(!mImageData, "Already have a buffer allocated?");
   IntSize targetSize = mDownscaler ? mDownscaler->TargetSize() : GetSize();
   nsresult rv = AllocateFrame(/* aFrameNum = */ 0, targetSize,
                               IntRect(IntPoint(), targetSize),
                               SurfaceFormat::B8G8R8A8);
   if (NS_FAILED(rv)) {
-    return Transition::Terminate(State::FAILURE);
+    return Transition::TerminateFailure();
   }
   MOZ_ASSERT(mImageData, "Should have a buffer now");
 
   if (mDownscaler) {
     // BMPs store their rows in reverse order, so the downscaler needs to
     // reverse them again when writing its output.
     rv = mDownscaler->BeginFrame(GetSize(), Nothing(),
                                  mImageData, mMayHaveTransparency,
                                  /* aFlipVertically = */ true);
     if (NS_FAILED(rv)) {
-      return Transition::Terminate(State::FAILURE);
+      return Transition::TerminateFailure();
     }
   }
 
   return Transition::To(State::COLOR_TABLE, mNumColors * mBytesPerColor);
 }
 
 LexerTransition<nsBMPDecoder::State>
 nsBMPDecoder::ReadColorTable(const char* aData, size_t aLength)
@@ -714,17 +696,17 @@ nsBMPDecoder::ReadColorTable(const char*
   // offset of the pixel data (mH.mDataOffset), so we can determine the length
   // of the gap (possibly zero) between the color table and the pixel data.
   //
   // If the gap is negative the file must be malformed (e.g. mH.mDataOffset
   // points into the middle of the color palette instead of past the end) and
   // we give up.
   if (mPreGapLength > mH.mDataOffset) {
     PostDataError();
-    return Transition::Terminate(State::FAILURE);
+    return Transition::TerminateFailure();
   }
   uint32_t gapLength = mH.mDataOffset - mPreGapLength;
   return Transition::To(State::GAP, gapLength);
 }
 
 LexerTransition<nsBMPDecoder::State>
 nsBMPDecoder::SkipGap()
 {
@@ -861,25 +843,25 @@ nsBMPDecoder::ReadPixelRow(const char* a
       break;
 
     default:
       MOZ_CRASH("Unsupported color depth; earlier check didn't catch it?");
   }
 
   FinishRow();
   return mCurrentRow == 0
-       ? Transition::Terminate(State::SUCCESS)
+       ? Transition::TerminateSuccess()
        : Transition::To(State::PIXEL_ROW, mPixelRowSize);
 }
 
 LexerTransition<nsBMPDecoder::State>
 nsBMPDecoder::ReadRLESegment(const char* aData)
 {
   if (mCurrentRow == 0) {
-    return Transition::Terminate(State::SUCCESS);
+    return Transition::TerminateSuccess();
   }
 
   uint8_t byte1 = uint8_t(aData[0]);
   uint8_t byte2 = uint8_t(aData[1]);
 
   if (byte1 != RLE::ESCAPE) {
     // Encoded mode consists of two bytes: byte1 specifies the number of
     // consecutive pixels to be drawn using the color index contained in
@@ -904,22 +886,22 @@ nsBMPDecoder::ReadRLESegment(const char*
     }
     return Transition::To(State::RLE_SEGMENT, RLE::SEGMENT_LENGTH);
   }
 
   if (byte2 == RLE::ESCAPE_EOL) {
     mCurrentPos = 0;
     FinishRow();
     return mCurrentRow == 0
-         ? Transition::Terminate(State::SUCCESS)
+         ? Transition::TerminateSuccess()
          : Transition::To(State::RLE_SEGMENT, RLE::SEGMENT_LENGTH);
   }
 
   if (byte2 == RLE::ESCAPE_EOF) {
-    return Transition::Terminate(State::SUCCESS);
+    return Transition::TerminateSuccess();
   }
 
   if (byte2 == RLE::ESCAPE_DELTA) {
     return Transition::To(State::RLE_DELTA, RLE::DELTA_LENGTH);
   }
 
   // Absolute mode. |byte2| gives the number of pixels. The length depends on
   // whether it's 4-bit or 8-bit RLE. Also, the length must be even (and zero
@@ -967,30 +949,30 @@ nsBMPDecoder::ReadRLEDelta(const char* a
     // Clear and commit the remaining skipped rows.
     for (int32_t line = 1; line < yDelta; line++) {
       mDownscaler->ClearRow();
       mDownscaler->CommitRow();
     }
   }
 
   return mCurrentRow == 0
-       ? Transition::Terminate(State::SUCCESS)
+       ? Transition::TerminateSuccess()
        : Transition::To(State::RLE_SEGMENT, RLE::SEGMENT_LENGTH);
 }
 
 LexerTransition<nsBMPDecoder::State>
 nsBMPDecoder::ReadRLEAbsolute(const char* aData, size_t aLength)
 {
   uint32_t n = mAbsoluteModeNumPixels;
   mAbsoluteModeNumPixels = 0;
 
   if (mCurrentPos + n > uint32_t(mH.mWidth)) {
     // Bad data. Stop decoding; at least part of the image may have been
     // decoded.
-    return Transition::Terminate(State::SUCCESS);
+    return Transition::TerminateSuccess();
   }
 
   // In absolute mode, n represents the number of pixels that follow, each of
   // which contains the color index of a single pixel.
   uint32_t* dst = RowBuffer();
   uint32_t iSrc = 0;
   uint32_t* oldPos = dst;
   if (mH.mCompression == Compression::RLE8) {
--- a/image/decoders/nsBMPDecoder.h
+++ b/image/decoders/nsBMPDecoder.h
@@ -162,19 +162,17 @@ private:
     INFO_HEADER_SIZE,
     INFO_HEADER_REST,
     BITFIELDS,
     COLOR_TABLE,
     GAP,
     PIXEL_ROW,
     RLE_SEGMENT,
     RLE_DELTA,
-    RLE_ABSOLUTE,
-    SUCCESS,
-    FAILURE
+    RLE_ABSOLUTE
   };
 
   // This is the constructor used by DecoderFactory.
   explicit nsBMPDecoder(RasterImage* aImage);
 
   // This is the constructor used by nsICODecoder.
   // XXX(seth): nsICODecoder is temporarily an exception to the rule that
   //            decoders should only be instantiated via DecoderFactory.
--- a/image/decoders/nsICODecoder.cpp
+++ b/image/decoders/nsICODecoder.cpp
@@ -165,24 +165,24 @@ nsICODecoder::FixBitmapWidth(int8_t* bih
   return true;
 }
 
 LexerTransition<ICOState>
 nsICODecoder::ReadHeader(const char* aData)
 {
   // If the third byte is 1, this is an icon. If 2, a cursor.
   if ((aData[2] != 1) && (aData[2] != 2)) {
-    return Transition::Terminate(ICOState::FAILURE);
+    return Transition::TerminateFailure();
   }
   mIsCursor = (aData[2] == 2);
 
   // The fifth and sixth bytes specify the number of resources in the file.
   mNumIcons = LittleEndian::readUint16(aData + 4);
   if (mNumIcons == 0) {
-    return Transition::Terminate(ICOState::SUCCESS); // Nothing to do.
+    return Transition::TerminateSuccess(); // Nothing to do.
   }
 
   // Downscale-during-decode can end up decoding different resources in the ICO
   // file depending on the target size. Since the resources are not necessarily
   // scaled versions of the same image, some may be transparent and some may not
   // be. We could be precise about transparency if we decoded the metadata of
   // every resource, but for now we don't and it's safest to assume that
   // transparency could be present.
@@ -252,32 +252,32 @@ nsICODecoder::ReadDirEntry(const char* a
       mBestResourceColorDepth = e.mBitCount;
       mDirEntry = e;
     }
   }
 
   if (mCurrIcon == mNumIcons) {
     // Ensure the resource we selected has an offset past the ICO headers.
     if (mDirEntry.mImageOffset < FirstResourceOffset()) {
-      return Transition::Terminate(ICOState::FAILURE);
+      return Transition::TerminateFailure();
     }
 
     // If this is a cursor, set the hotspot. We use the hotspot from the biggest
     // resource since we also use that resource for the intrinsic size.
     if (mIsCursor) {
       mImageMetadata.SetHotspot(mBiggestResourceHotSpot.width,
                                 mBiggestResourceHotSpot.height);
     }
 
     // We always report the biggest resource's size as the intrinsic size; this
     // is necessary for downscale-during-decode to work since we won't even
     // attempt to *upscale* while decoding.
     PostSize(mBiggestResourceSize.width, mBiggestResourceSize.height);
     if (IsMetadataDecode()) {
-      return Transition::Terminate(ICOState::SUCCESS);
+      return Transition::TerminateSuccess();
     }
 
     // If the resource we selected matches the downscaler's target size
     // perfectly, we don't need to do any downscaling.
     if (mDownscaler && GetRealSize() == mDownscaler->TargetSize()) {
       mDownscaler.reset();
     }
 
@@ -304,55 +304,55 @@ nsICODecoder::SniffResource(const char* 
     mContainedDecoder->SetDecoderFlags(GetDecoderFlags());
     mContainedDecoder->SetSurfaceFlags(GetSurfaceFlags());
     if (mDownscaler) {
       mContainedDecoder->SetTargetSize(mDownscaler->TargetSize());
     }
     mContainedDecoder->Init();
 
     if (!WriteToContainedDecoder(aData, PNGSIGNATURESIZE)) {
-      return Transition::Terminate(ICOState::FAILURE);
+      return Transition::TerminateFailure();
     }
 
     if (mDirEntry.mBytesInRes <= PNGSIGNATURESIZE) {
-      return Transition::Terminate(ICOState::FAILURE);
+      return Transition::TerminateFailure();
     }
 
     // Read in the rest of the PNG unbuffered.
     size_t toRead = mDirEntry.mBytesInRes - PNGSIGNATURESIZE;
     return Transition::ToUnbuffered(ICOState::FINISHED_RESOURCE,
                                     ICOState::READ_PNG,
                                     toRead);
   } else {
     // Make sure we have a sane size for the bitmap information header.
     int32_t bihSize = LittleEndian::readUint32(aData);
     if (bihSize != static_cast<int32_t>(BITMAPINFOSIZE)) {
-      return Transition::Terminate(ICOState::FAILURE);
+      return Transition::TerminateFailure();
     }
 
     // Buffer the first part of the bitmap information header.
     memcpy(mBIHraw, aData, PNGSIGNATURESIZE);
 
     // Read in the rest of the bitmap information header.
     return Transition::To(ICOState::READ_BIH,
                           BITMAPINFOSIZE - PNGSIGNATURESIZE);
   }
 }
 
 LexerTransition<ICOState>
 nsICODecoder::ReadPNG(const char* aData, uint32_t aLen)
 {
   if (!WriteToContainedDecoder(aData, aLen)) {
-    return Transition::Terminate(ICOState::FAILURE);
+    return Transition::TerminateFailure();
   }
 
   // Raymond Chen says that 32bpp only are valid PNG ICOs
   // http://blogs.msdn.com/b/oldnewthing/archive/2010/10/22/10079192.aspx
   if (!static_cast<nsPNGDecoder*>(mContainedDecoder.get())->IsValidICO()) {
-    return Transition::Terminate(ICOState::FAILURE);
+    return Transition::TerminateFailure();
   }
 
   return Transition::ContinueUnbuffered(ICOState::READ_PNG);
 }
 
 LexerTransition<ICOState>
 nsICODecoder::ReadBIH(const char* aData)
 {
@@ -367,17 +367,17 @@ nsICODecoder::ReadBIH(const char* aData)
   // bitmap file header. So we create the BMP decoder via the constructor that
   // tells it to skip this, and pass in the required data (dataOffset) that
   // would have been present in the header.
   uint32_t dataOffset = bmp::FILE_HEADER_LENGTH + BITMAPINFOSIZE;
   if (mDirEntry.mBitCount <= 8) {
     // The color table is present only if BPP is <= 8.
     uint16_t numColors = GetNumColors();
     if (numColors == (uint16_t)-1) {
-      return Transition::Terminate(ICOState::FAILURE);
+      return Transition::TerminateFailure();
     }
     dataOffset += 4 * numColors;
   }
 
   // Create a BMP decoder which will do most of the work for us; the exception
   // is the AND mask, which isn't present in standalone BMPs.
   RefPtr<nsBMPDecoder> bmpDecoder = new nsBMPDecoder(mImage, dataOffset);
   mContainedDecoder = bmpDecoder;
@@ -388,33 +388,33 @@ nsICODecoder::ReadBIH(const char* aData)
     mContainedDecoder->SetTargetSize(mDownscaler->TargetSize());
   }
   mContainedDecoder->Init();
 
   // Fix the ICO height from the BIH. It needs to be halved so our BMP decoder
   // will understand, because the BMP decoder doesn't expect the alpha mask that
   // follows the BMP data in an ICO.
   if (!FixBitmapHeight(reinterpret_cast<int8_t*>(mBIHraw))) {
-    return Transition::Terminate(ICOState::FAILURE);
+    return Transition::TerminateFailure();
   }
 
   // Fix the ICO width from the BIH.
   if (!FixBitmapWidth(reinterpret_cast<int8_t*>(mBIHraw))) {
-    return Transition::Terminate(ICOState::FAILURE);
+    return Transition::TerminateFailure();
   }
 
   // Write out the BMP's bitmap info header.
   if (!WriteToContainedDecoder(mBIHraw, sizeof(mBIHraw))) {
-    return Transition::Terminate(ICOState::FAILURE);
+    return Transition::TerminateFailure();
   }
 
   // Check to make sure we have valid color settings.
   uint16_t numColors = GetNumColors();
   if (numColors == uint16_t(-1)) {
-    return Transition::Terminate(ICOState::FAILURE);
+    return Transition::TerminateFailure();
   }
 
   // Do we have an AND mask on this BMP? If so, we need to read it after we read
   // the BMP data itself.
   uint32_t bmpDataLength = bmpDecoder->GetCompressedImageSize() + 4 * numColors;
   bool hasANDMask = (BITMAPINFOSIZE + bmpDataLength) < mDirEntry.mBytesInRes;
   ICOState afterBMPState = hasANDMask ? ICOState::PREPARE_FOR_MASK
                                       : ICOState::FINISHED_RESOURCE;
@@ -424,17 +424,17 @@ nsICODecoder::ReadBIH(const char* aData)
                                   ICOState::READ_BMP,
                                   bmpDataLength);
 }
 
 LexerTransition<ICOState>
 nsICODecoder::ReadBMP(const char* aData, uint32_t aLen)
 {
   if (!WriteToContainedDecoder(aData, aLen)) {
-    return Transition::Terminate(ICOState::FAILURE);
+    return Transition::TerminateFailure();
   }
 
   return Transition::ContinueUnbuffered(ICOState::READ_BMP);
 }
 
 LexerTransition<ICOState>
 nsICODecoder::PrepareForMask()
 {
@@ -461,34 +461,34 @@ nsICODecoder::PrepareForMask()
 
   // Compute the row size for the mask.
   mMaskRowSize = ((GetRealWidth() + 31) / 32) * 4; // + 31 to round up
 
   // If the expected size of the AND mask is larger than its actual size, then
   // we must have a truncated (and therefore corrupt) AND mask.
   uint32_t expectedLength = mMaskRowSize * GetRealHeight();
   if (maskLength < expectedLength) {
-    return Transition::Terminate(ICOState::FAILURE);
+    return Transition::TerminateFailure();
   }
 
   // If we're downscaling, the mask is the wrong size for the surface we've
   // produced, so we need to downscale the mask into a temporary buffer and then
   // combine the mask's alpha values with the color values from the image.
   if (mDownscaler) {
     MOZ_ASSERT(bmpDecoder->GetImageDataLength() ==
                  mDownscaler->TargetSize().width *
                  mDownscaler->TargetSize().height *
                  sizeof(uint32_t));
     mMaskBuffer = MakeUnique<uint8_t[]>(bmpDecoder->GetImageDataLength());
     nsresult rv = mDownscaler->BeginFrame(GetRealSize(), Nothing(),
                                           mMaskBuffer.get(),
                                           /* aHasAlpha = */ true,
                                           /* aFlipVertically = */ true);
     if (NS_FAILED(rv)) {
-      return Transition::Terminate(ICOState::FAILURE);
+      return Transition::TerminateFailure();
     }
   }
 
   mCurrMaskLine = GetRealHeight();
   return Transition::To(ICOState::READ_MASK_ROW, mMaskRowSize);
 }
 
 
@@ -511,17 +511,17 @@ nsICODecoder::ReadMaskRow(const char* aD
     memset(mDownscaler->RowBuffer(), 0xFF, GetRealWidth() * sizeof(uint32_t));
 
     decoded = reinterpret_cast<uint32_t*>(mDownscaler->RowBuffer());
   } else {
     RefPtr<nsBMPDecoder> bmpDecoder =
       static_cast<nsBMPDecoder*>(mContainedDecoder.get());
     uint32_t* imageData = bmpDecoder->GetImageData();
     if (!imageData) {
-      return Transition::Terminate(ICOState::FAILURE);
+      return Transition::TerminateFailure();
     }
 
     decoded = imageData + mCurrMaskLine * GetRealWidth();
   }
 
   MOZ_ASSERT(decoded);
   uint32_t* decodedRowEnd = decoded + GetRealWidth();
 
@@ -561,17 +561,17 @@ nsICODecoder::FinishMask()
   // If we're downscaling, we now have the appropriate alpha values in
   // mMaskBuffer. We just need to transfer them to the image.
   if (mDownscaler) {
     // Retrieve the image data.
     RefPtr<nsBMPDecoder> bmpDecoder =
       static_cast<nsBMPDecoder*>(mContainedDecoder.get());
     uint8_t* imageData = reinterpret_cast<uint8_t*>(bmpDecoder->GetImageData());
     if (!imageData) {
-      return Transition::Terminate(ICOState::FAILURE);
+      return Transition::TerminateFailure();
     }
 
     // Iterate through the alpha values, copying from mask to image.
     MOZ_ASSERT(mMaskBuffer);
     MOZ_ASSERT(bmpDecoder->GetImageDataLength() > 0);
     for (size_t i = 3 ; i < bmpDecoder->GetImageDataLength() ; i += 4) {
       imageData[i] = mMaskBuffer[i];
     }
@@ -591,30 +591,30 @@ nsICODecoder::FinishMask()
 
 LexerTransition<ICOState>
 nsICODecoder::FinishResource()
 {
   // Make sure the actual size of the resource matches the size in the directory
   // entry. If not, we consider the image corrupt.
   if (mContainedDecoder->HasSize() &&
       mContainedDecoder->GetSize() != GetRealSize()) {
-    return Transition::Terminate(ICOState::FAILURE);
+    return Transition::TerminateFailure();
   }
 
-  return Transition::Terminate(ICOState::SUCCESS);
+  return Transition::TerminateSuccess();
 }
 
 void
 nsICODecoder::WriteInternal(const char* aBuffer, uint32_t aCount)
 {
   MOZ_ASSERT(!HasError(), "Shouldn't call WriteInternal after error!");
   MOZ_ASSERT(aBuffer);
   MOZ_ASSERT(aCount > 0);
 
-  Maybe<ICOState> terminalState =
+  Maybe<TerminalState> terminalState =
     mLexer.Lex(aBuffer, aCount,
                [=](ICOState aState, const char* aData, size_t aLength) {
       switch (aState) {
         case ICOState::HEADER:
           return ReadHeader(aData);
         case ICOState::DIR_ENTRY:
           return ReadDirEntry(aData);
         case ICOState::SKIP_TO_RESOURCE:
@@ -635,31 +635,23 @@ nsICODecoder::WriteInternal(const char* 
           return ReadMaskRow(aData);
         case ICOState::FINISH_MASK:
           return FinishMask();
         case ICOState::SKIP_MASK:
           return Transition::ContinueUnbuffered(ICOState::SKIP_MASK);
         case ICOState::FINISHED_RESOURCE:
           return FinishResource();
         default:
-          MOZ_ASSERT_UNREACHABLE("Unknown ICOState");
-          return Transition::Terminate(ICOState::FAILURE);
+          MOZ_CRASH("Unknown ICOState");
       }
     });
 
-  if (!terminalState) {
-    return;  // Need more data.
+  if (terminalState == Some(TerminalState::FAILURE)) {
+    PostDataError();
   }
-
-  if (*terminalState == ICOState::FAILURE) {
-    PostDataError();
-    return;
-  }
-
-  MOZ_ASSERT(*terminalState == ICOState::SUCCESS);
 }
 
 bool
 nsICODecoder::WriteToContainedDecoder(const char* aBuffer, uint32_t aCount)
 {
   mContainedDecoder->Write(aBuffer, aCount);
   mProgress |= mContainedDecoder->TakeProgress();
   mInvalidRect.UnionRect(mInvalidRect, mContainedDecoder->TakeInvalidRect());
--- a/image/decoders/nsICODecoder.h
+++ b/image/decoders/nsICODecoder.h
@@ -16,18 +16,16 @@
 
 namespace mozilla {
 namespace image {
 
 class RasterImage;
 
 enum class ICOState
 {
-  SUCCESS,
-  FAILURE,
   HEADER,
   DIR_ENTRY,
   SKIP_TO_RESOURCE,
   FOUND_RESOURCE,
   SNIFF_RESOURCE,
   READ_PNG,
   READ_BIH,
   READ_BMP,
--- a/image/decoders/nsIconDecoder.cpp
+++ b/image/decoders/nsIconDecoder.cpp
@@ -37,42 +37,34 @@ nsIconDecoder::~nsIconDecoder()
 
 void
 nsIconDecoder::WriteInternal(const char* aBuffer, uint32_t aCount)
 {
   MOZ_ASSERT(!HasError(), "Shouldn't call WriteInternal after error!");
   MOZ_ASSERT(aBuffer);
   MOZ_ASSERT(aCount > 0);
 
-  Maybe<State> terminalState =
+  Maybe<TerminalState> terminalState =
     mLexer.Lex(aBuffer, aCount, [=](State aState,
                                     const char* aData, size_t aLength) {
       switch (aState) {
         case State::HEADER:
           return ReadHeader(aData);
         case State::ROW_OF_PIXELS:
           return ReadRowOfPixels(aData, aLength);
         case State::FINISH:
           return Finish();
         default:
-          MOZ_ASSERT_UNREACHABLE("Unknown State");
-          return Transition::Terminate(State::FAILURE);
+          MOZ_CRASH("Unknown State");
       }
     });
 
-  if (!terminalState) {
-    return;  // Need more data.
+  if (terminalState == Some(TerminalState::FAILURE)) {
+    PostDataError();
   }
-
-  if (*terminalState == State::FAILURE) {
-    PostDataError();
-    return;
-  }
-
-  MOZ_ASSERT(*terminalState == State::SUCCESS);
 }
 
 LexerTransition<nsIconDecoder::State>
 nsIconDecoder::ReadHeader(const char* aData)
 {
   // Grab the width and height.
   mWidth  = uint8_t(aData[0]);
   mHeight = uint8_t(aData[1]);
@@ -83,34 +75,34 @@ nsIconDecoder::ReadHeader(const char* aD
   // Post our size to the superclass.
   PostSize(mWidth, mHeight);
 
   // Icons have alpha.
   PostHasTransparency();
 
   // If we're doing a metadata decode, we're done.
   if (IsMetadataDecode()) {
-    return Transition::Terminate(State::SUCCESS);
+    return Transition::TerminateSuccess();
   }
 
   MOZ_ASSERT(!mImageData, "Already have a buffer allocated?");
   IntSize targetSize = mDownscaler ? mDownscaler->TargetSize() : GetSize();
   nsresult rv = AllocateFrame(0, targetSize,
                               IntRect(IntPoint(), targetSize),
                               gfx::SurfaceFormat::B8G8R8A8);
   if (NS_FAILED(rv)) {
-    return Transition::Terminate(State::FAILURE);
+    return Transition::TerminateFailure();
   }
   MOZ_ASSERT(mImageData, "Should have a buffer now");
 
   if (mDownscaler) {
     nsresult rv = mDownscaler->BeginFrame(GetSize(), Nothing(),
                                           mImageData, /* aHasAlpha = */ true);
     if (NS_FAILED(rv)) {
-      return Transition::Terminate(State::FAILURE);
+      return Transition::TerminateFailure();
     }
   }
 
   return Transition::To(State::ROW_OF_PIXELS, mBytesPerRow);
 }
 
 LexerTransition<nsIconDecoder::State>
 nsIconDecoder::ReadRowOfPixels(const char* aData, size_t aLength)
@@ -137,13 +129,13 @@ nsIconDecoder::ReadRowOfPixels(const cha
 }
 
 LexerTransition<nsIconDecoder::State>
 nsIconDecoder::Finish()
 {
   PostFrameStop();
   PostDecodeDone();
 
-  return Transition::Terminate(State::SUCCESS);
+  return Transition::TerminateSuccess();
 }
 
 } // namespace image
 } // namespace mozilla
--- a/image/decoders/nsIconDecoder.h
+++ b/image/decoders/nsIconDecoder.h
@@ -45,19 +45,17 @@ private:
   friend class DecoderFactory;
 
   // Decoders should only be instantiated via DecoderFactory.
   explicit nsIconDecoder(RasterImage* aImage);
 
   enum class State {
     HEADER,
     ROW_OF_PIXELS,
-    FINISH,
-    SUCCESS,
-    FAILURE
+    FINISH
   };
 
   LexerTransition<State> ReadHeader(const char* aData);
   LexerTransition<State> ReadRowOfPixels(const char* aData, size_t aLength);
   LexerTransition<State> Finish();
 
   StreamingLexer<State> mLexer;
   uint8_t mWidth;
--- a/image/decoders/nsJPEGDecoder.cpp
+++ b/image/decoders/nsJPEGDecoder.cpp
@@ -31,35 +31,19 @@ extern "C" {
 #define MOZ_JCS_EXT_NATIVE_ENDIAN_XRGB JCS_EXT_BGRX
 #endif
 
 static void cmyk_convert_rgb(JSAMPROW row, JDIMENSION width);
 
 namespace mozilla {
 namespace image {
 
-static PRLogModuleInfo*
-GetJPEGLog()
-{
-  static PRLogModuleInfo* sJPEGLog;
-  if (!sJPEGLog) {
-    sJPEGLog = PR_NewLogModule("JPEGDecoder");
-  }
-  return sJPEGLog;
-}
+static mozilla::LazyLogModule sJPEGLog("JPEGDecoder");
 
-static PRLogModuleInfo*
-GetJPEGDecoderAccountingLog()
-{
-  static PRLogModuleInfo* sJPEGDecoderAccountingLog;
-  if (!sJPEGDecoderAccountingLog) {
-    sJPEGDecoderAccountingLog = PR_NewLogModule("JPEGDecoderAccounting");
-  }
-  return sJPEGDecoderAccountingLog;
-}
+static mozilla::LazyLogModule sJPEGDecoderAccountingLog("JPEGDecoderAccounting");
 
 static qcms_profile*
 GetICCProfile(struct jpeg_decompress_struct& info)
 {
   JOCTET* profilebuf;
   uint32_t profileLength;
   qcms_profile* profile = nullptr;
 
@@ -101,17 +85,17 @@ nsJPEGDecoder::nsJPEGDecoder(RasterImage
   mBackBuffer = nullptr;
   mBackBufferLen = mBackBufferSize = mBackBufferUnreadLen = 0;
 
   mInProfile = nullptr;
   mTransform = nullptr;
 
   mCMSMode = 0;
 
-  MOZ_LOG(GetJPEGDecoderAccountingLog(), LogLevel::Debug,
+  MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
          ("nsJPEGDecoder::nsJPEGDecoder: Creating JPEG decoder %p",
           this));
 }
 
 nsJPEGDecoder::~nsJPEGDecoder()
 {
   // Step 8: Release JPEG decompression object
   mInfo.src = nullptr;
@@ -120,17 +104,17 @@ nsJPEGDecoder::~nsJPEGDecoder()
   PR_FREEIF(mBackBuffer);
   if (mTransform) {
     qcms_transform_release(mTransform);
   }
   if (mInProfile) {
     qcms_profile_release(mInProfile);
   }
 
-  MOZ_LOG(GetJPEGDecoderAccountingLog(), LogLevel::Debug,
+  MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
          ("nsJPEGDecoder::~nsJPEGDecoder: Destroying JPEG decoder %p",
           this));
 }
 
 Telemetry::ID
 nsJPEGDecoder::SpeedHistogram()
 {
   return Telemetry::IMAGE_DECODE_SPEED_JPEG;
@@ -200,42 +184,42 @@ nsJPEGDecoder::WriteInternal(const char*
   // This cast to nsresult makes sense because setjmp() returns whatever we
   // passed to longjmp(), which was actually an nsresult.
   if ((error_code = (nsresult)setjmp(mErr.setjmp_buffer)) != NS_OK) {
     if (error_code == NS_ERROR_FAILURE) {
       PostDataError();
       // Error due to corrupt stream - return NS_OK and consume silently
       // so that ImageLib doesn't throw away a partial image load
       mState = JPEG_SINK_NON_JPEG_TRAILER;
-      MOZ_LOG(GetJPEGDecoderAccountingLog(), LogLevel::Debug,
+      MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
              ("} (setjmp returned NS_ERROR_FAILURE)"));
       return;
     } else {
       // Error due to reasons external to the stream (probably out of
       // memory) - let ImageLib attempt to clean up, even though
       // mozilla is seconds away from falling flat on its face.
       PostDecoderError(error_code);
       mState = JPEG_ERROR;
-      MOZ_LOG(GetJPEGDecoderAccountingLog(), LogLevel::Debug,
+      MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
              ("} (setjmp returned an error)"));
       return;
     }
   }
 
-  MOZ_LOG(GetJPEGLog(), LogLevel::Debug,
+  MOZ_LOG(sJPEGLog, LogLevel::Debug,
          ("[this=%p] nsJPEGDecoder::Write -- processing JPEG data\n", this));
 
   switch (mState) {
     case JPEG_HEADER: {
-      LOG_SCOPE(GetJPEGLog(), "nsJPEGDecoder::Write -- entering JPEG_HEADER"
+      LOG_SCOPE((mozilla::LogModule*)sJPEGLog, "nsJPEGDecoder::Write -- entering JPEG_HEADER"
                 " case");
 
       // Step 3: read file parameters with jpeg_read_header()
       if (jpeg_read_header(&mInfo, TRUE) == JPEG_SUSPENDED) {
-        MOZ_LOG(GetJPEGDecoderAccountingLog(), LogLevel::Debug,
+        MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
                ("} (JPEG_SUSPENDED)"));
         return; // I/O suspension
       }
 
       // If we have a sample size specified for -moz-sample-size, use it.
       if (mSampleSize > 0) {
         mInfo.scale_num = 1;
         mInfo.scale_denom = mSampleSize;
@@ -291,34 +275,34 @@ nsJPEGDecoder::WriteInternal(const char*
         case JCS_CMYK:
         case JCS_YCCK:
             // qcms doesn't support cmyk
             mismatch = true;
           break;
         default:
           mState = JPEG_ERROR;
           PostDataError();
-          MOZ_LOG(GetJPEGDecoderAccountingLog(), LogLevel::Debug,
+          MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
                  ("} (unknown colorpsace (1))"));
           return;
       }
 
       if (!mismatch) {
         qcms_data_type type;
         switch (mInfo.out_color_space) {
           case JCS_GRAYSCALE:
             type = QCMS_DATA_GRAY_8;
             break;
           case JCS_RGB:
             type = QCMS_DATA_RGB_8;
             break;
           default:
             mState = JPEG_ERROR;
             PostDataError();
-            MOZ_LOG(GetJPEGDecoderAccountingLog(), LogLevel::Debug,
+            MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
                    ("} (unknown colorpsace (2))"));
             return;
         }
 #if 0
         // We don't currently support CMYK profiles. The following
         // code dealt with lcms types. Add something like this
         // back when we gain support for CMYK.
 
@@ -367,17 +351,17 @@ nsJPEGDecoder::WriteInternal(const char*
         case JCS_CMYK:
         case JCS_YCCK:
           // libjpeg can convert from YCCK to CMYK, but not to RGB
           mInfo.out_color_space = JCS_CMYK;
           break;
         default:
           mState = JPEG_ERROR;
           PostDataError();
-          MOZ_LOG(GetJPEGDecoderAccountingLog(), LogLevel::Debug,
+          MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
                  ("} (unknown colorpsace (3))"));
           return;
       }
     }
 
     // Don't allocate a giant and superfluous memory buffer
     // when not doing a progressive decode.
     mInfo.buffered_image = mDecodeStyle == PROGRESSIVE &&
@@ -385,91 +369,91 @@ nsJPEGDecoder::WriteInternal(const char*
 
     MOZ_ASSERT(!mImageData, "Already have a buffer allocated?");
     nsIntSize targetSize = mDownscaler ? mDownscaler->TargetSize() : GetSize();
     nsresult rv = AllocateFrame(0, targetSize,
                                 nsIntRect(nsIntPoint(), targetSize),
                                 gfx::SurfaceFormat::B8G8R8A8);
     if (NS_FAILED(rv)) {
       mState = JPEG_ERROR;
-      MOZ_LOG(GetJPEGDecoderAccountingLog(), LogLevel::Debug,
+      MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
              ("} (could not initialize image frame)"));
       return;
     }
 
     MOZ_ASSERT(mImageData, "Should have a buffer now");
 
     if (mDownscaler) {
       nsresult rv = mDownscaler->BeginFrame(GetSize(), Nothing(),
                                             mImageData,
                                             /* aHasAlpha = */ false);
       if (NS_FAILED(rv)) {
         mState = JPEG_ERROR;
         return;
       }
     }
 
-    MOZ_LOG(GetJPEGDecoderAccountingLog(), LogLevel::Debug,
+    MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
            ("        JPEGDecoderAccounting: nsJPEGDecoder::"
             "Write -- created image frame with %ux%u pixels",
             mInfo.output_width, mInfo.output_height));
 
     mState = JPEG_START_DECOMPRESS;
   }
 
   case JPEG_START_DECOMPRESS: {
-    LOG_SCOPE(GetJPEGLog(), "nsJPEGDecoder::Write -- entering"
+    LOG_SCOPE((mozilla::LogModule*)sJPEGLog, "nsJPEGDecoder::Write -- entering"
                             " JPEG_START_DECOMPRESS case");
     // Step 4: set parameters for decompression
 
     // FIXME -- Should reset dct_method and dither mode
     // for final pass of progressive JPEG
 
     mInfo.dct_method =  JDCT_ISLOW;
     mInfo.dither_mode = JDITHER_FS;
     mInfo.do_fancy_upsampling = TRUE;
     mInfo.enable_2pass_quant = FALSE;
     mInfo.do_block_smoothing = TRUE;
 
     // Step 5: Start decompressor
     if (jpeg_start_decompress(&mInfo) == FALSE) {
-      MOZ_LOG(GetJPEGDecoderAccountingLog(), LogLevel::Debug,
+      MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
              ("} (I/O suspension after jpeg_start_decompress())"));
       return; // I/O suspension
     }
 
     // If this is a progressive JPEG ...
     mState = mInfo.buffered_image ?
              JPEG_DECOMPRESS_PROGRESSIVE : JPEG_DECOMPRESS_SEQUENTIAL;
   }
 
   case JPEG_DECOMPRESS_SEQUENTIAL: {
     if (mState == JPEG_DECOMPRESS_SEQUENTIAL) {
-      LOG_SCOPE(GetJPEGLog(), "nsJPEGDecoder::Write -- "
+      LOG_SCOPE((mozilla::LogModule*)sJPEGLog, "nsJPEGDecoder::Write -- "
                               "JPEG_DECOMPRESS_SEQUENTIAL case");
 
       bool suspend;
       OutputScanlines(&suspend);
 
       if (suspend) {
-        MOZ_LOG(GetJPEGDecoderAccountingLog(), LogLevel::Debug,
+        MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
                ("} (I/O suspension after OutputScanlines() - SEQUENTIAL)"));
         return; // I/O suspension
       }
 
       // If we've completed image output ...
       NS_ASSERTION(mInfo.output_scanline == mInfo.output_height,
                    "We didn't process all of the data!");
       mState = JPEG_DONE;
     }
   }
 
   case JPEG_DECOMPRESS_PROGRESSIVE: {
     if (mState == JPEG_DECOMPRESS_PROGRESSIVE) {
-      LOG_SCOPE(GetJPEGLog(),
+      LOG_SCOPE((mozilla::LogModule*)sJPEGLog,
                 "nsJPEGDecoder::Write -- JPEG_DECOMPRESS_PROGRESSIVE case");
 
       int status;
       do {
         status = jpeg_consume_input(&mInfo);
       } while ((status != JPEG_SUSPENDED) &&
                (status != JPEG_REACHED_EOI));
 
@@ -481,17 +465,17 @@ nsJPEGDecoder::WriteInternal(const char*
           // and we have enough data for a complete scan, force output
           // of the last full scan
           if ((mInfo.output_scan_number == 0) &&
               (scan > 1) &&
               (status != JPEG_REACHED_EOI))
             scan--;
 
           if (!jpeg_start_output(&mInfo, scan)) {
-            MOZ_LOG(GetJPEGDecoderAccountingLog(), LogLevel::Debug,
+            MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
                    ("} (I/O suspension after jpeg_start_output() -"
                     " PROGRESSIVE)"));
             return; // I/O suspension
           }
         }
 
         if (mInfo.output_scanline == 0xffffff) {
           mInfo.output_scanline = 0;
@@ -501,24 +485,24 @@ nsJPEGDecoder::WriteInternal(const char*
         OutputScanlines(&suspend);
 
         if (suspend) {
           if (mInfo.output_scanline == 0) {
             // didn't manage to read any lines - flag so we don't call
             // jpeg_start_output() multiple times for the same scan
             mInfo.output_scanline = 0xffffff;
           }
-          MOZ_LOG(GetJPEGDecoderAccountingLog(), LogLevel::Debug,
+          MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
                  ("} (I/O suspension after OutputScanlines() - PROGRESSIVE)"));
           return; // I/O suspension
         }
 
         if (mInfo.output_scanline == mInfo.output_height) {
           if (!jpeg_finish_output(&mInfo)) {
-            MOZ_LOG(GetJPEGDecoderAccountingLog(), LogLevel::Debug,
+            MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
                    ("} (I/O suspension after jpeg_finish_output() -"
                     " PROGRESSIVE)"));
             return; // I/O suspension
           }
 
           if (jpeg_input_complete(&mInfo) &&
               (mInfo.input_scan_number == mInfo.output_scan_number))
             break;
@@ -530,46 +514,46 @@ nsJPEGDecoder::WriteInternal(const char*
         }
       }
 
       mState = JPEG_DONE;
     }
   }
 
   case JPEG_DONE: {
-    LOG_SCOPE(GetJPEGLog(), "nsJPEGDecoder::ProcessData -- entering"
+    LOG_SCOPE((mozilla::LogModule*)sJPEGLog, "nsJPEGDecoder::ProcessData -- entering"
                             " JPEG_DONE case");
 
     // Step 7: Finish decompression
 
     if (jpeg_finish_decompress(&mInfo) == FALSE) {
-      MOZ_LOG(GetJPEGDecoderAccountingLog(), LogLevel::Debug,
+      MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
              ("} (I/O suspension after jpeg_finish_decompress() - DONE)"));
       return; // I/O suspension
     }
 
     mState = JPEG_SINK_NON_JPEG_TRAILER;
 
     // we're done dude
     break;
   }
   case JPEG_SINK_NON_JPEG_TRAILER:
-    MOZ_LOG(GetJPEGLog(), LogLevel::Debug,
+    MOZ_LOG(sJPEGLog, LogLevel::Debug,
            ("[this=%p] nsJPEGDecoder::ProcessData -- entering"
             " JPEG_SINK_NON_JPEG_TRAILER case\n", this));
 
     break;
 
   case JPEG_ERROR:
     MOZ_ASSERT(false,
                "Should always return immediately after error and not re-enter "
                "decoder");
   }
 
-  MOZ_LOG(GetJPEGDecoderAccountingLog(), LogLevel::Debug,
+  MOZ_LOG(sJPEGDecoderAccountingLog, LogLevel::Debug,
          ("} (end of function)"));
   return;
 }
 
 Orientation
 nsJPEGDecoder::ReadOrientationFromEXIF()
 {
   jpeg_saved_marker_ptr marker;
--- a/image/encoders/png/nsPNGEncoder.cpp
+++ b/image/encoders/png/nsPNGEncoder.cpp
@@ -7,25 +7,17 @@
 #include "nsCRT.h"
 #include "nsPNGEncoder.h"
 #include "nsStreamUtils.h"
 #include "nsString.h"
 #include "prprf.h"
 
 using namespace mozilla;
 
-static PRLogModuleInfo*
-GetPNGEncoderLog()
-{
-  static PRLogModuleInfo* sPNGEncoderLog;
-  if (!sPNGEncoderLog) {
-    sPNGEncoderLog = PR_NewLogModule("PNGEncoder");
-  }
-  return sPNGEncoderLog;
-}
+static LazyLogModule sPNGEncoderLog("PNGEncoder");
 
 NS_IMPL_ISUPPORTS(nsPNGEncoder, imgIEncoder, nsIInputStream,
                   nsIAsyncInputStream)
 
 nsPNGEncoder::nsPNGEncoder() : mPNG(nullptr), mPNGinfo(nullptr),
                                mIsAnimation(false),
                                mFinished(false),
                                mImageBuffer(nullptr), mImageBufferSize(0),
@@ -680,28 +672,28 @@ nsPNGEncoder::StripAlpha(const uint8_t* 
 
 
 // nsPNGEncoder::WarningCallback
 
 void
 nsPNGEncoder::WarningCallback(png_structp png_ptr,
                             png_const_charp warning_msg)
 {
-  MOZ_LOG(GetPNGEncoderLog(), LogLevel::Warning,
+  MOZ_LOG(sPNGEncoderLog, LogLevel::Warning,
          ("libpng warning: %s\n", warning_msg));
 }
 
 
 // nsPNGEncoder::ErrorCallback
 
 void
 nsPNGEncoder::ErrorCallback(png_structp png_ptr,
                             png_const_charp error_msg)
 {
-  MOZ_LOG(GetPNGEncoderLog(), LogLevel::Error, ("libpng error: %s\n", error_msg));
+  MOZ_LOG(sPNGEncoderLog, LogLevel::Error, ("libpng error: %s\n", error_msg));
   png_longjmp(png_ptr, 1);
 }
 
 // nsPNGEncoder::WriteCallback
 
 void // static
 nsPNGEncoder::WriteCallback(png_structp png, png_bytep data,
                             png_size_t size)
--- a/image/imgLoader.cpp
+++ b/image/imgLoader.cpp
@@ -861,23 +861,23 @@ imgCacheEntry::imgCacheEntry(imgLoader* 
    // will set this to false.
    mEvicted(true),
    mHasNoProxies(true),
    mForcePrincipalCheck(forcePrincipalCheck)
 { }
 
 imgCacheEntry::~imgCacheEntry()
 {
-  LOG_FUNC(GetImgLog(), "imgCacheEntry::~imgCacheEntry()");
+  LOG_FUNC(gImgLog, "imgCacheEntry::~imgCacheEntry()");
 }
 
 void
 imgCacheEntry::Touch(bool updateTime /* = true */)
 {
-  LOG_SCOPE(GetImgLog(), "imgCacheEntry::Touch");
+  LOG_SCOPE(gImgLog, "imgCacheEntry::Touch");
 
   if (updateTime) {
     mTouchedTime = SecondsFromPRTime(PR_Now());
   }
 
   UpdateCache();
 }
 
@@ -894,22 +894,22 @@ imgCacheEntry::UpdateCache(int32_t diff 
 void imgCacheEntry::UpdateLoadTime()
 {
   mLoadTime = SecondsFromPRTime(PR_Now());
 }
 
 void
 imgCacheEntry::SetHasNoProxies(bool hasNoProxies)
 {
-  if (MOZ_LOG_TEST(GetImgLog(), LogLevel::Debug)) {
+  if (MOZ_LOG_TEST(gImgLog, LogLevel::Debug)) {
     if (hasNoProxies) {
-      LOG_FUNC_WITH_PARAM(GetImgLog(), "imgCacheEntry::SetHasNoProxies true",
+      LOG_FUNC_WITH_PARAM(gImgLog, "imgCacheEntry::SetHasNoProxies true",
                           "uri", mRequest->CacheKey().Spec());
     } else {
-      LOG_FUNC_WITH_PARAM(GetImgLog(), "imgCacheEntry::SetHasNoProxies false",
+      LOG_FUNC_WITH_PARAM(gImgLog, "imgCacheEntry::SetHasNoProxies false",
                           "uri", mRequest->CacheKey().Spec());
     }
   }
 
   mHasNoProxies = hasNoProxies;
 }
 
 imgCacheQueue::imgCacheQueue()
@@ -1022,17 +1022,17 @@ imgCacheQueue::end() const
 
 nsresult
 imgLoader::CreateNewProxyForRequest(imgRequest* aRequest,
                                     nsILoadGroup* aLoadGroup,
                                     imgINotificationObserver* aObserver,
                                     nsLoadFlags aLoadFlags,
                                     imgRequestProxy** _retval)
 {
-  LOG_SCOPE_WITH_PARAM(GetImgLog(), "imgLoader::CreateNewProxyForRequest",
+  LOG_SCOPE_WITH_PARAM(gImgLog, "imgLoader::CreateNewProxyForRequest",
                        "imgRequest", aRequest);
 
   /* XXX If we move decoding onto separate threads, we should save off the
      calling thread here and pass it off to |proxyRequest| so that it call
      proxy calls to |aObserver|.
    */
 
   RefPtr<imgRequestProxy> proxyRequest = new imgRequestProxy();
@@ -1073,20 +1073,20 @@ imgCacheExpirationTracker::imgCacheExpir
 
 void
 imgCacheExpirationTracker::NotifyExpired(imgCacheEntry* entry)
 {
   // Hold on to a reference to this entry, because the expiration tracker
   // mechanism doesn't.
   RefPtr<imgCacheEntry> kungFuDeathGrip(entry);
 
-  if (MOZ_LOG_TEST(GetImgLog(), LogLevel::Debug)) {
+  if (MOZ_LOG_TEST(gImgLog, LogLevel::Debug)) {
     RefPtr<imgRequest> req = entry->GetRequest();
     if (req) {
-      LOG_FUNC_WITH_PARAM(GetImgLog(),
+      LOG_FUNC_WITH_PARAM(gImgLog,
                          "imgCacheExpirationTracker::NotifyExpired",
                          "entry", req->CacheKey().Spec());
     }
   }
 
   // We can be called multiple times on the same entry. Don't do work multiple
   // times.
   if (!entry->Evicted()) {
@@ -1397,37 +1397,37 @@ imgLoader::MinimizeCaches()
   EvictEntries(mChromeCacheQueue);
 }
 
 bool
 imgLoader::PutIntoCache(const ImageCacheKey& aKey, imgCacheEntry* entry)
 {
   imgCacheTable& cache = GetCache(aKey);
 
-  LOG_STATIC_FUNC_WITH_PARAM(GetImgLog(),
+  LOG_STATIC_FUNC_WITH_PARAM(gImgLog,
                              "imgLoader::PutIntoCache", "uri", aKey.Spec());
 
   // Check to see if this request already exists in the cache. If so, we'll
   // replace the old version.
   RefPtr<imgCacheEntry> tmpCacheEntry;
   if (cache.Get(aKey, getter_AddRefs(tmpCacheEntry)) && tmpCacheEntry) {
-    MOZ_LOG(GetImgLog(), LogLevel::Debug,
+    MOZ_LOG(gImgLog, LogLevel::Debug,
            ("[this=%p] imgLoader::PutIntoCache -- Element already in the cache",
             nullptr));
     RefPtr<imgRequest> tmpRequest = tmpCacheEntry->GetRequest();
 
     // If it already exists, and we're putting the same key into the cache, we
     // should remove the old version.
-    MOZ_LOG(GetImgLog(), LogLevel::Debug,
+    MOZ_LOG(gImgLog, LogLevel::Debug,
            ("[this=%p] imgLoader::PutIntoCache -- Replacing cached element",
             nullptr));
 
     RemoveFromCache(aKey);
   } else {
-    MOZ_LOG(GetImgLog(), LogLevel::Debug,
+    MOZ_LOG(gImgLog, LogLevel::Debug,
            ("[this=%p] imgLoader::PutIntoCache --"
             " Element NOT already in the cache", nullptr));
   }
 
   cache.Put(aKey, entry);
 
   // We can be called to resurrect an evicted entry.
   if (entry->Evicted()) {
@@ -1454,17 +1454,17 @@ imgLoader::PutIntoCache(const ImageCache
   RemoveFromUncachedImages(request);
 
   return true;
 }
 
 bool
 imgLoader::SetHasNoProxies(imgRequest* aRequest, imgCacheEntry* aEntry)
 {
-  LOG_STATIC_FUNC_WITH_PARAM(GetImgLog(),
+  LOG_STATIC_FUNC_WITH_PARAM(gImgLog,
                              "imgLoader::SetHasNoProxies", "uri",
                              aRequest->CacheKey().Spec());
 
   aEntry->SetHasNoProxies(true);
 
   if (aEntry->Evicted()) {
     return false;
   }
@@ -1490,17 +1490,17 @@ imgLoader::SetHasNoProxies(imgRequest* a
 bool
 imgLoader::SetHasProxies(imgRequest* aRequest)
 {
   VerifyCacheSizes();
 
   const ImageCacheKey& key = aRequest->CacheKey();
   imgCacheTable& cache = GetCache(key);
 
-  LOG_STATIC_FUNC_WITH_PARAM(GetImgLog(),
+  LOG_STATIC_FUNC_WITH_PARAM(gImgLog,
                              "imgLoader::SetHasProxies", "uri", key.Spec());
 
   RefPtr<imgCacheEntry> entry;
   if (cache.Get(key, getter_AddRefs(entry)) && entry) {
     // Make sure the cache entry is for the right request
     RefPtr<imgRequest> entryRequest = entry->GetRequest();
     if (entryRequest == aRequest && entry->HasNoProxies()) {
       imgCacheQueue& queue = GetCacheQueue(key);
@@ -1537,20 +1537,20 @@ imgLoader::CheckCacheLimits(imgCacheTabl
 
   // Remove entries from the cache until we're back at our desired max size.
   while (queue.GetSize() > sCacheMaxSize) {
     // Remove the first entry in the queue.
     RefPtr<imgCacheEntry> entry(queue.Pop());
 
     NS_ASSERTION(entry, "imgLoader::CheckCacheLimits -- NULL entry pointer");
 
-    if (MOZ_LOG_TEST(GetImgLog(), LogLevel::Debug)) {
+    if (MOZ_LOG_TEST(gImgLog, LogLevel::Debug)) {
       RefPtr<imgRequest> req = entry->GetRequest();
       if (req) {
-        LOG_STATIC_FUNC_WITH_PARAM(GetImgLog(),
+        LOG_STATIC_FUNC_WITH_PARAM(gImgLog,
                                    "imgLoader::CheckCacheLimits",
                                    "entry", req->CacheKey().Spec());
       }
     }
 
     if (entry) {
       RemoveFromCache(entry);
     }
@@ -1699,17 +1699,17 @@ imgLoader::ValidateEntry(imgCacheEntry* 
                          nsISupports* aCX,
                          nsLoadFlags aLoadFlags,
                          nsContentPolicyType aLoadPolicyType,
                          bool aCanMakeNewChannel,
                          imgRequestProxy** aProxyRequest,
                          nsIPrincipal* aLoadingPrincipal,
                          int32_t aCORSMode)
 {
-  LOG_SCOPE(GetImgLog(), "imgLoader::ValidateEntry");
+  LOG_SCOPE(gImgLog, "imgLoader::ValidateEntry");
 
   bool hasExpired;
   uint32_t expirationTime = aEntry->GetExpiryTime();
   if (expirationTime <= SecondsFromPRTime(PR_Now())) {
     hasExpired = true;
   } else {
     hasExpired = false;
   }
@@ -1770,24 +1770,24 @@ imgLoader::ValidateEntry(imgCacheEntry* 
     // bypass the cache, we don't allow this entry to be used.
     if (aLoadFlags & nsIRequest::LOAD_BYPASS_CACHE) {
       return false;
     }
 
     // Determine whether the cache aEntry must be revalidated...
     validateRequest = ShouldRevalidateEntry(aEntry, aLoadFlags, hasExpired);
 
-    MOZ_LOG(GetImgLog(), LogLevel::Debug,
+    MOZ_LOG(gImgLog, LogLevel::Debug,
            ("imgLoader::ValidateEntry validating cache entry. "
             "validateRequest = %d", validateRequest));
-  } else if (!key && MOZ_LOG_TEST(GetImgLog(), LogLevel::Debug)) {
+  } else if (!key && MOZ_LOG_TEST(gImgLog, LogLevel::Debug)) {
     nsAutoCString spec;
     aURI->GetSpec(spec);
 
-    MOZ_LOG(GetImgLog(), LogLevel::Debug,
+    MOZ_LOG(gImgLog, LogLevel::Debug,
            ("imgLoader::ValidateEntry BYPASSING cache validation for %s "
             "because of NULL LoadID", spec.get()));
   }
 
   // We can't use a cached request if it comes from a different
   // application cache than this load is expecting.
   nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer;
   nsCOMPtr<nsIApplicationCache> requestAppCache;
@@ -1795,42 +1795,42 @@ imgLoader::ValidateEntry(imgCacheEntry* 
   if ((appCacheContainer = do_GetInterface(request->GetRequest()))) {
     appCacheContainer->GetApplicationCache(getter_AddRefs(requestAppCache));
   }
   if ((appCacheContainer = do_QueryInterface(aLoadGroup))) {
     appCacheContainer->GetApplicationCache(getter_AddRefs(groupAppCache));
   }
 
   if (requestAppCache != groupAppCache) {
-    MOZ_LOG(GetImgLog(), LogLevel::Debug,
+    MOZ_LOG(gImgLog, LogLevel::Debug,
            ("imgLoader::ValidateEntry - Unable to use cached imgRequest "
             "[request=%p] because of mismatched application caches\n",
             address_of(request)));
     return false;
   }
 
   if (validateRequest && aCanMakeNewChannel) {
-    LOG_SCOPE(GetImgLog(),
+    LOG_SCOPE(gImgLog,
               "imgLoader::ValidateRequest |cache hit| must validate");
 
     return ValidateRequestWithNewChannel(request, aURI, aInitialDocumentURI,
                                          aReferrerURI, aReferrerPolicy,
                                          aLoadGroup, aObserver,
                                          aCX, aLoadFlags, aLoadPolicyType,
                                          aProxyRequest, aLoadingPrincipal,
                                          aCORSMode);
   }
 
   return !validateRequest;
 }
 
 bool
 imgLoader::RemoveFromCache(const ImageCacheKey& aKey)
 {
-  LOG_STATIC_FUNC_WITH_PARAM(GetImgLog(),
+  LOG_STATIC_FUNC_WITH_PARAM(gImgLog,
                              "imgLoader::RemoveFromCache", "uri", aKey.Spec());
 
   imgCacheTable& cache = GetCache(aKey);
   imgCacheQueue& queue = GetCacheQueue(aKey);
 
   RefPtr<imgCacheEntry> entry;
   if (cache.Get(aKey, getter_AddRefs(entry)) && entry) {
     cache.Remove(aKey);
@@ -1856,32 +1856,32 @@ imgLoader::RemoveFromCache(const ImageCa
   } else {
     return false;
   }
 }
 
 bool
 imgLoader::RemoveFromCache(imgCacheEntry* entry)
 {
-  LOG_STATIC_FUNC(GetImgLog(), "imgLoader::RemoveFromCache entry");
+  LOG_STATIC_FUNC(gImgLog, "imgLoader::RemoveFromCache entry");
 
   RefPtr<imgRequest> request = entry->GetRequest();
   if (request) {
     const ImageCacheKey& key = request->CacheKey();
     imgCacheTable& cache = GetCache(key);
     imgCacheQueue& queue = GetCacheQueue(key);
 
-    LOG_STATIC_FUNC_WITH_PARAM(GetImgLog(),
+    LOG_STATIC_FUNC_WITH_PARAM(gImgLog,
                                "imgLoader::RemoveFromCache", "entry's uri",
                                key.Spec());
 
     cache.Remove(key);
 
     if (entry->HasNoProxies()) {
-      LOG_STATIC_FUNC(GetImgLog(),
+      LOG_STATIC_FUNC(gImgLog,
                       "imgLoader::RemoveFromCache removing from tracker");
       if (mCacheTracker) {
         mCacheTracker->RemoveObject(entry);
       }
       queue.Remove(entry);
     }
 
     entry->SetEvicted(true);
@@ -1892,17 +1892,17 @@ imgLoader::RemoveFromCache(imgCacheEntry
   }
 
   return false;
 }
 
 nsresult
 imgLoader::EvictEntries(imgCacheTable& aCacheToClear)
 {
-  LOG_STATIC_FUNC(GetImgLog(), "imgLoader::EvictEntries table");
+  LOG_STATIC_FUNC(gImgLog, "imgLoader::EvictEntries table");
 
   // We have to make a temporary, since RemoveFromCache removes the element
   // from the queue, invalidating iterators.
   nsTArray<RefPtr<imgCacheEntry> > entries;
   for (auto iter = aCacheToClear.Iter(); !iter.Done(); iter.Next()) {
     RefPtr<imgCacheEntry>& data = iter.Data();
     entries.AppendElement(data);
   }
@@ -1916,17 +1916,17 @@ imgLoader::EvictEntries(imgCacheTable& a
   MOZ_ASSERT(aCacheToClear.Count() == 0);
 
   return NS_OK;
 }
 
 nsresult
 imgLoader::EvictEntries(imgCacheQueue& aQueueToClear)
 {
-  LOG_STATIC_FUNC(GetImgLog(), "imgLoader::EvictEntries queue");
+  LOG_STATIC_FUNC(gImgLog, "imgLoader::EvictEntries queue");
 
   // We have to make a temporary, since RemoveFromCache removes the element
   // from the queue, invalidating iterators.
   nsTArray<RefPtr<imgCacheEntry> > entries(aQueueToClear.GetNumElements());
   for (imgCacheQueue::const_iterator i = aQueueToClear.begin();
        i != aQueueToClear.end(); ++i) {
     entries.AppendElement(*i);
   }
@@ -2021,17 +2021,17 @@ imgLoader::LoadImage(nsIURI* aURI,
   NS_ASSERTION(aURI, "imgLoader::LoadImage -- NULL URI pointer");
 
   if (!aURI) {
     return NS_ERROR_NULL_POINTER;
   }
 
   nsAutoCString spec;
   aURI->GetSpec(spec);
-  LOG_SCOPE_WITH_PARAM(GetImgLog(), "imgLoader::LoadImage", "aURI", spec.get());
+  LOG_SCOPE_WITH_PARAM(gImgLog, "imgLoader::LoadImage", "aURI", spec.get());
 
   *_retval = nullptr;
 
   RefPtr<imgRequest> request;
 
   nsresult rv;
   nsLoadFlags requestFlags = nsIRequest::LOAD_NORMAL;
 
@@ -2097,17 +2097,17 @@ imgLoader::LoadImage(nsIURI* aURI,
                       aReferrerPolicy, aLoadGroup, aObserver, aCX,
                       requestFlags, aContentPolicyType, true, _retval,
                       aLoadingPrincipal, corsmode)) {
       request = entry->GetRequest();
 
       // If this entry has no proxies, its request has no reference to the
       // entry.
       if (entry->HasNoProxies()) {
-        LOG_FUNC_WITH_PARAM(GetImgLog(),
+        LOG_FUNC_WITH_PARAM(gImgLog,
           "imgLoader::LoadImage() adding proxyless entry", "uri", key.Spec());
         MOZ_ASSERT(!request->HasCacheEntry(),
           "Proxyless entry's request has cache entry!");
         request->SetCacheEntry(entry);
 
         if (mCacheTracker) {
           mCacheTracker->MarkUsed(entry);
         }
@@ -2126,17 +2126,17 @@ imgLoader::LoadImage(nsIURI* aURI,
     }
   }
 
   // Keep the channel in this scope, so we can adjust its notificationCallbacks
   // later when we create the proxy.
   nsCOMPtr<nsIChannel> newChannel;
   // If we didn't get a cache hit, we need to load from the network.
   if (!request) {
-    LOG_SCOPE(GetImgLog(), "imgLoader::LoadImage |cache miss|");
+    LOG_SCOPE(gImgLog, "imgLoader::LoadImage |cache miss|");
 
     bool forcePrincipalCheck;
     rv = NewImageChannel(getter_AddRefs(newChannel),
                          &forcePrincipalCheck,
                          aURI,
                          aInitialDocumentURI,
                          aReferrerURI,
                          aReferrerPolicy,
@@ -2151,17 +2151,17 @@ imgLoader::LoadImage(nsIURI* aURI,
     }
 
     MOZ_ASSERT(NS_UsePrivateBrowsing(newChannel) == mRespectPrivacy);
 
     NewRequestAndEntry(forcePrincipalCheck, this, key,
                        getter_AddRefs(request),
                        getter_AddRefs(entry));
 
-    MOZ_LOG(GetImgLog(), LogLevel::Debug,
+    MOZ_LOG(gImgLog, LogLevel::Debug,
            ("[this=%p] imgLoader::LoadImage -- Created new imgRequest"
             " [request=%p]\n", this, request.get()));
 
     nsCOMPtr<nsILoadGroup> channelLoadGroup;
     newChannel->GetLoadGroup(getter_AddRefs(channelLoadGroup));
     request->Init(aURI, aURI, /* aHadInsecureRedirect = */ false,
                   channelLoadGroup, newChannel, entry, aCX,
                   aLoadingPrincipal, corsmode, aReferrerPolicy);
@@ -2174,56 +2174,56 @@ imgLoader::LoadImage(nsIURI* aURI,
 
     // create the proxy listener
     nsCOMPtr<nsIStreamListener> pl = new ProxyListener(request.get());
 
     // See if we need to insert a CORS proxy between the proxy listener and the
     // request.
     nsCOMPtr<nsIStreamListener> listener = pl;
     if (corsmode != imgIRequest::CORS_NONE) {
-      MOZ_LOG(GetImgLog(), LogLevel::Debug,
+      MOZ_LOG(gImgLog, LogLevel::Debug,
              ("[this=%p] imgLoader::LoadImage -- Setting up a CORS load",
               this));
       bool withCredentials = corsmode == imgIRequest::CORS_USE_CREDENTIALS;
 
       RefPtr<nsCORSListenerProxy> corsproxy =
         new nsCORSListenerProxy(pl, aLoadingPrincipal, withCredentials);
       rv = corsproxy->Init(newChannel, DataURIHandling::Allow);
       if (NS_FAILED(rv)) {
-        MOZ_LOG(GetImgLog(), LogLevel::Debug,
+        MOZ_LOG(gImgLog, LogLevel::Debug,
                ("[this=%p] imgLoader::LoadImage -- nsCORSListenerProxy "
                 "creation failed: 0x%x\n", this, rv));
         request->CancelAndAbort(rv);
         return NS_ERROR_FAILURE;
       }
 
       listener = corsproxy;
     }
 
-    MOZ_LOG(GetImgLog(), LogLevel::Debug,
+    MOZ_LOG(gImgLog, LogLevel::Debug,
            ("[this=%p] imgLoader::LoadImage -- Calling channel->AsyncOpen()\n",
             this));
 
     mozilla::net::PredictorLearn(aURI, aInitialDocumentURI,
         nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE, aLoadGroup);
 
     nsresult openRes = newChannel->AsyncOpen(listener, nullptr);
 
     if (NS_FAILED(openRes)) {
-      MOZ_LOG(GetImgLog(), LogLevel::Debug,
+      MOZ_LOG(gImgLog, LogLevel::Debug,
              ("[this=%p] imgLoader::LoadImage -- AsyncOpen() failed: 0x%x\n",
               this, openRes));
       request->CancelAndAbort(openRes);
       return openRes;
     }
 
     // Try to add the new request into the cache.
     PutIntoCache(key, entry);
   } else {
-    LOG_MSG_WITH_PARAM(GetImgLog(),
+    LOG_MSG_WITH_PARAM(gImgLog,
                        "imgLoader::LoadImage |cache hit|", "request", request);
   }
 
 
   // If we didn't get a proxy when validating the cache entry, we need to
   // create one.
   if (!*_retval) {
     // ValidateEntry() has three return values: "Is valid," "might be valid --
@@ -2232,17 +2232,17 @@ imgLoader::LoadImage(nsIURI* aURI,
     // to SetLoadId here because we know this request is valid for this context.
     //
     // Note, however, that this doesn't guarantee the behaviour we want (one
     // URL maps to the same image on a page) if we load the same image in a
     // different tab (see bug 528003), because its load id will get re-set, and
     // that'll cause us to validate over the network.
     request->SetLoadId(aCX);
 
-    LOG_MSG(GetImgLog(), "imgLoader::LoadImage", "creating proxy request.");
+    LOG_MSG(gImgLog, "imgLoader::LoadImage", "creating proxy request.");
     rv = CreateNewProxyForRequest(request, aLoadGroup, aObserver,
                                   requestFlags, _retval);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     imgRequestProxy* proxy = *_retval;
 
@@ -2361,17 +2361,17 @@ imgLoader::LoadImageWithChannel(nsIChann
           request = entry->GetRequest();
         }
       }
 
       if (request && entry) {
         // If this entry has no proxies, its request has no reference to
         // the entry.
         if (entry->HasNoProxies()) {
-          LOG_FUNC_WITH_PARAM(GetImgLog(),
+          LOG_FUNC_WITH_PARAM(gImgLog,
             "imgLoader::LoadImageWithChannel() adding proxyless entry",
             "uri", key.Spec());
           MOZ_ASSERT(!request->HasCacheEntry(),
             "Proxyless entry's request has cache entry!");
           request->SetCacheEntry(entry);
 
           if (mCacheTracker) {
             mCacheTracker->MarkUsed(entry);
@@ -2649,17 +2649,17 @@ ProxyListener::CheckListenerChain()
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread!");
   nsresult rv = NS_OK;
   nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener =
     do_QueryInterface(mDestListener, &rv);
   if (retargetableListener) {
     rv = retargetableListener->CheckListenerChain();
   }
-  MOZ_LOG(GetImgLog(), LogLevel::Debug,
+  MOZ_LOG(gImgLog, LogLevel::Debug,
          ("ProxyListener::CheckListenerChain %s [this=%p listener=%p rv=%x]",
           (NS_SUCCEEDED(rv) ? "success" : "failure"),
           this, (nsIStreamListener*)mDestListener, rv));
   return rv;
 }
 
 /**
  * http validate class.  check a channel for a 304
@@ -2775,20 +2775,20 @@ imgCacheValidator::OnStartRequest(nsIReq
   // data that's coming in off the channel.
   nsCOMPtr<nsIURI> uri;
   {
     RefPtr<ImageURL> imageURL;
     mRequest->GetURI(getter_AddRefs(imageURL));
     uri = imageURL->ToIURI();
   }
 
-  if (MOZ_LOG_TEST(GetImgLog(), LogLevel::Debug)) {
+  if (MOZ_LOG_TEST(gImgLog, LogLevel::Debug)) {
     nsAutoCString spec;
     uri->GetSpec(spec);
-    LOG_MSG_WITH_PARAM(GetImgLog(),
+    LOG_MSG_WITH_PARAM(gImgLog,
                        "imgCacheValidator::OnStartRequest creating new request",
                        "uri", spec.get());
   }
 
   int32_t corsmode = mRequest->GetCORSMode();
   ReferrerPolicy refpol = mRequest->GetReferrerPolicy();
   nsCOMPtr<nsIPrincipal> loadingPrincipal = mRequest->GetLoadingPrincipal();
 
@@ -2869,17 +2869,17 @@ imgCacheValidator::CheckListenerChain()
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread!");
   nsresult rv = NS_OK;
   nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener =
     do_QueryInterface(mDestListener, &rv);
   if (retargetableListener) {
     rv = retargetableListener->CheckListenerChain();
   }
-  MOZ_LOG(GetImgLog(), LogLevel::Debug,
+  MOZ_LOG(gImgLog, LogLevel::Debug,
          ("[this=%p] imgCacheValidator::CheckListenerChain -- rv %d=%s",
           this, NS_SUCCEEDED(rv) ? "succeeded" : "failed", rv));
   return rv;
 }
 
 /** nsIInterfaceRequestor methods **/
 
 NS_IMETHODIMP
--- a/image/imgRequest.cpp
+++ b/image/imgRequest.cpp
@@ -35,26 +35,17 @@
 #include "plstr.h" // PL_strcasestr(...)
 #include "nsNetUtil.h"
 #include "nsIProtocolHandler.h"
 #include "imgIRequest.h"
 
 using namespace mozilla;
 using namespace mozilla::image;
 
-PRLogModuleInfo*
-GetImgLog()
-{
-  static PRLogModuleInfo* sImgLog;
-  if (!sImgLog) {
-    sImgLog = PR_NewLogModule("imgRequest");
-  }
-  return sImgLog;
-}
-#define LOG_TEST(level) (GetImgLog() && MOZ_LOG_TEST(GetImgLog(), (level)))
+#define LOG_TEST(level) (MOZ_LOG_TEST(gImgLog, (level)))
 
 NS_IMPL_ISUPPORTS(imgRequest,
                   nsIStreamListener, nsIRequestObserver,
                   nsIThreadRetargetableStreamListener,
                   nsIChannelEventSink,
                   nsIInterfaceRequestor,
                   nsIAsyncVerifyRedirectCallback)
 
@@ -81,37 +72,37 @@ imgRequest::imgRequest(imgLoader* aLoade
 imgRequest::~imgRequest()
 {
   if (mLoader) {
     mLoader->RemoveFromUncachedImages(this);
   }
   if (mURI) {
     nsAutoCString spec;
     mURI->GetSpec(spec);
-    LOG_FUNC_WITH_PARAM(GetImgLog(), "imgRequest::~imgRequest()",
+    LOG_FUNC_WITH_PARAM(gImgLog, "imgRequest::~imgRequest()",
                         "keyuri", spec.get());
   } else
-    LOG_FUNC(GetImgLog(), "imgRequest::~imgRequest()");
+    LOG_FUNC(gImgLog, "imgRequest::~imgRequest()");
 }
 
 nsresult
 imgRequest::Init(nsIURI *aURI,
                  nsIURI *aCurrentURI,
                  bool aHadInsecureRedirect,
                  nsIRequest *aRequest,
                  nsIChannel *aChannel,
                  imgCacheEntry *aCacheEntry,
                  nsISupports* aCX,
                  nsIPrincipal* aLoadingPrincipal,
                  int32_t aCORSMode,
                  ReferrerPolicy aReferrerPolicy)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Cannot use nsIURI off main thread!");
 
-  LOG_FUNC(GetImgLog(), "imgRequest::Init");
+  LOG_FUNC(gImgLog, "imgRequest::Init");
 
   MOZ_ASSERT(!mImage, "Multiple calls to init");
   MOZ_ASSERT(aURI, "No uri");
   MOZ_ASSERT(aCurrentURI, "No current uri");
   MOZ_ASSERT(aRequest, "No request");
   MOZ_ASSERT(aChannel, "No channel");
 
   mProperties = do_CreateInstance("@mozilla.org/properties;1");
@@ -211,17 +202,17 @@ imgRequest::ResetCacheEntry()
     mCacheEntry->SetDataSize(0);
   }
 }
 
 void
 imgRequest::AddProxy(imgRequestProxy* proxy)
 {
   NS_PRECONDITION(proxy, "null imgRequestProxy passed in");
-  LOG_SCOPE_WITH_PARAM(GetImgLog(), "imgRequest::AddProxy", "proxy", proxy);
+  LOG_SCOPE_WITH_PARAM(gImgLog, "imgRequest::AddProxy", "proxy", proxy);
 
   if (!mFirstProxy) {
     // Save a raw pointer to the first proxy we see, for use in the network
     // priority logic.
     mFirstProxy = proxy;
   }
 
   // If we're empty before adding, we have to tell the loader we now have
@@ -235,17 +226,17 @@ imgRequest::AddProxy(imgRequestProxy* pr
   }
 
   progressTracker->AddObserver(proxy);
 }
 
 nsresult
 imgRequest::RemoveProxy(imgRequestProxy* proxy, nsresult aStatus)
 {
-  LOG_SCOPE_WITH_PARAM(GetImgLog(), "imgRequest::RemoveProxy", "proxy", proxy);
+  LOG_SCOPE_WITH_PARAM(gImgLog, "imgRequest::RemoveProxy", "proxy", proxy);
 
   // This will remove our animation consumers, so after removing
   // this proxy, we don't end up without proxies with observers, but still
   // have animation consumers.
   proxy->ClearAnimationConsumers();
 
   // Let the status tracker do its thing before we potentially call Cancel()
   // below, because Cancel() may result in OnStopRequest being called back
@@ -261,33 +252,33 @@ imgRequest::RemoveProxy(imgRequestProxy*
     // been cancelled and thus removed from the cache, tell the image loader so
     // we can be evicted from the cache.
     if (mCacheEntry) {
       MOZ_ASSERT(mURI, "Removing last observer without key uri.");
 
       if (mLoader) {
         mLoader->SetHasNoProxies(this, mCacheEntry);
       }
-    } else if (MOZ_LOG_TEST(GetImgLog(), LogLevel::Debug)) {
+    } else if (MOZ_LOG_TEST(gImgLog, LogLevel::Debug)) {
       nsAutoCString spec;
       mURI->GetSpec(spec);
-      LOG_MSG_WITH_PARAM(GetImgLog(),
+      LOG_MSG_WITH_PARAM(gImgLog,
                          "imgRequest::RemoveProxy no cache entry",
                          "uri", spec.get());
     }
 
     /* If |aStatus| is a failure code, then cancel the load if it is still in
        progress.  Otherwise, let the load continue, keeping 'this' in the cache
        with no observers.  This way, if a proxy is destroyed without calling
        cancel on it, it won't leak and won't leave a bad pointer in the observer
        list.
      */
     if (!(progressTracker->GetProgress() & FLAG_LAST_PART_COMPLETE) &&
         NS_FAILED(aStatus)) {
-      LOG_MSG(GetImgLog(), "imgRequest::RemoveProxy",
+      LOG_MSG(gImgLog, "imgRequest::RemoveProxy",
               "load in progress.  canceling");
 
       this->Cancel(NS_BINDING_ABORTED);
     }
 
     /* break the cycle from the cache entry. */
     mCacheEntry = nullptr;
   }
@@ -299,17 +290,17 @@ imgRequest::RemoveProxy(imgRequestProxy*
   }
 
   return NS_OK;
 }
 
 void
 imgRequest::CancelAndAbort(nsresult aStatus)
 {
-  LOG_SCOPE(GetImgLog(), "imgRequest::CancelAndAbort");
+  LOG_SCOPE(gImgLog, "imgRequest::CancelAndAbort");
 
   Cancel(aStatus);
 
   // It's possible for the channel to fail to open after we've set our
   // notification callbacks. In that case, make sure to break the cycle between
   // the channel and us, because it won't.
   if (mChannel) {
     mChannel->SetNotificationCallbacks(mPrevChannelSink);
@@ -338,17 +329,17 @@ private:
   RefPtr<imgRequest> mImgRequest;
   nsresult mStatus;
 };
 
 void
 imgRequest::Cancel(nsresult aStatus)
 {
   /* The Cancel() method here should only be called by this class. */
-  LOG_SCOPE(GetImgLog(), "imgRequest::Cancel");
+  LOG_SCOPE(gImgLog, "imgRequest::Cancel");
 
   if (NS_IsMainThread()) {
     ContinueCancel(aStatus);
   } else {
     NS_DispatchToMainThread(new imgRequestMainThreadCancel(this, aStatus));
   }
 }
 
@@ -387,17 +378,17 @@ private:
   RefPtr<imgRequest> mImgRequest;
 };
 
 // EvictFromCache() is written to allowed to get called from any thread
 void
 imgRequest::EvictFromCache()
 {
   /* The EvictFromCache() method here should only be called by this class. */
-  LOG_SCOPE(GetImgLog(), "imgRequest::EvictFromCache");
+  LOG_SCOPE(gImgLog, "imgRequest::EvictFromCache");
 
   if (NS_IsMainThread()) {
     ContinueEvict();
   } else {
     NS_DispatchToMainThread(new imgRequestMainThreadEvict(this));
   }
 }
 
@@ -423,33 +414,33 @@ imgRequest::IsDecodeRequested() const
   MutexAutoLock lock(mMutex);
   return mDecodeRequested;
 }
 
 nsresult imgRequest::GetURI(ImageURL** aURI)
 {
   MOZ_ASSERT(aURI);
 
-  LOG_FUNC(GetImgLog(), "imgRequest::GetURI");
+  LOG_FUNC(gImgLog, "imgRequest::GetURI");
 
   if (mURI) {
     *aURI = mURI;
     NS_ADDREF(*aURI);
     return NS_OK;
   }
 
   return NS_ERROR_FAILURE;
 }
 
 nsresult
 imgRequest::GetCurrentURI(nsIURI** aURI)
 {
   MOZ_ASSERT(aURI);
 
-  LOG_FUNC(GetImgLog(), "imgRequest::GetCurrentURI");
+  LOG_FUNC(gImgLog, "imgRequest::GetCurrentURI");
 
   if (mCurrentURI) {
     *aURI = mCurrentURI;
     NS_ADDREF(*aURI);
     return NS_OK;
   }
 
   return NS_ERROR_FAILURE;
@@ -469,28 +460,28 @@ nsresult
 imgRequest::GetImageErrorCode()
 {
   return mImageErrorCode;
 }
 
 nsresult
 imgRequest::GetSecurityInfo(nsISupports** aSecurityInfo)
 {
-  LOG_FUNC(GetImgLog(), "imgRequest::GetSecurityInfo");
+  LOG_FUNC(gImgLog, "imgRequest::GetSecurityInfo");
 
   // Missing security info means this is not a security load
   // i.e. it is not an error when security info is missing
   NS_IF_ADDREF(*aSecurityInfo = mSecurityInfo);
   return NS_OK;
 }
 
 void
 imgRequest::RemoveFromCache()
 {
-  LOG_SCOPE(GetImgLog(), "imgRequest::RemoveFromCache");
+  LOG_SCOPE(gImgLog, "imgRequest::RemoveFromCache");
 
   bool isInCache = false;
 
   {
     MutexAutoLock lock(mMutex);
     isInCache = mIsInCache;
   }
 
@@ -556,17 +547,17 @@ imgRequest::HasTransferredData() const
 {
   MutexAutoLock lock(mMutex);
   return mGotData;
 }
 
 void
 imgRequest::SetIsInCache(bool aInCache)
 {
-  LOG_FUNC_WITH_PARAM(GetImgLog(),
+  LOG_FUNC_WITH_PARAM(gImgLog,
                       "imgRequest::SetIsCacheable", "aInCache", aInCache);
   MutexAutoLock lock(mMutex);
   mIsInCache = aInCache;
 }
 
 void
 imgRequest::UpdateCacheEntrySize()
 {
@@ -706,17 +697,17 @@ imgRequest::HadInsecureRedirect() const
   return mHadInsecureRedirect;
 }
 
 /** nsIRequestObserver methods **/
 
 NS_IMETHODIMP
 imgRequest::OnStartRequest(nsIRequest* aRequest, nsISupports* ctxt)
 {
-  LOG_SCOPE(GetImgLog(), "imgRequest::OnStartRequest");
+  LOG_SCOPE(gImgLog, "imgRequest::OnStartRequest");
 
   RefPtr<Image> image;
 
   // Figure out if we're multipart.
   nsCOMPtr<nsIMultiPartChannel> multiPartChannel = do_QueryInterface(aRequest);
   MOZ_ASSERT(multiPartChannel || !mIsMultiPartChannel,
              "Stopped being multipart?"); {
     MutexAutoLock lock(mMutex);
@@ -781,30 +772,30 @@ imgRequest::OnStartRequest(nsIRequest* a
     nsAutoCString mimeType;
     nsresult rv = httpChannel->GetContentType(mimeType);
     if (NS_SUCCEEDED(rv) && !mimeType.EqualsLiteral(IMAGE_SVG_XML)) {
       // Retarget OnDataAvailable to the DecodePool's IO thread.
       nsCOMPtr<nsIEventTarget> target =
         DecodePool::Singleton()->GetIOEventTarget();
       rv = retargetable->RetargetDeliveryTo(target);
     }
-    MOZ_LOG(GetImgLog(), LogLevel::Warning,
+    MOZ_LOG(gImgLog, LogLevel::Warning,
            ("[this=%p] imgRequest::OnStartRequest -- "
             "RetargetDeliveryTo rv %d=%s\n",
             this, rv, NS_SUCCEEDED(rv) ? "succeeded" : "failed"));
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 imgRequest::OnStopRequest(nsIRequest* aRequest,
                           nsISupports* ctxt, nsresult status)
 {
-  LOG_FUNC(GetImgLog(), "imgRequest::OnStopRequest");
+  LOG_FUNC(gImgLog, "imgRequest::OnStopRequest");
   MOZ_ASSERT(NS_IsMainThread(), "Can't send notifications off-main-thread");
 
   RefPtr<Image> image = GetImage();
 
   // XXXldb What if this is a non-last part of a multipart request?
   // xxx before we release our reference to mRequest, lets
   // save the last status that we saw so that the
   // imgRequestProxy will have access to it.
@@ -933,28 +924,28 @@ PrepareForNewPart(nsIRequest* aRequest, 
   uint32_t out;
   aInStr->ReadSegments(sniff_mimetype_callback, &closure, aCount, &out);
 
   nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
   if (result.mContentType.IsEmpty()) {
     nsresult rv = chan ? chan->GetContentType(result.mContentType)
                        : NS_ERROR_FAILURE;
     if (NS_FAILED(rv)) {
-      MOZ_LOG(GetImgLog(),
+      MOZ_LOG(gImgLog,
               LogLevel::Error, ("imgRequest::PrepareForNewPart -- "
                                 "Content type unavailable from the channel\n"));
       return result;
     }
   }
 
   if (chan) {
     chan->GetContentDispositionHeader(result.mContentDisposition);
   }
 
-  MOZ_LOG(GetImgLog(), LogLevel::Debug,
+  MOZ_LOG(gImgLog, LogLevel::Debug,
          ("imgRequest::PrepareForNewPart -- Got content type %s\n",
           result.mContentType.get()));
 
   // XXX If server lied about mimetype and it's SVG, we may need to copy
   // the data and dispatch back to the main thread, AND tell the channel to
   // dispatch there in the future.
 
   // Create the new image and give it ownership of our ProgressTracker.
@@ -1048,17 +1039,17 @@ imgRequest::FinishPreparingForNewPart(co
   }
 }
 
 NS_IMETHODIMP
 imgRequest::OnDataAvailable(nsIRequest* aRequest, nsISupports* aContext,
                             nsIInputStream* aInStr, uint64_t aOffset,
                             uint32_t aCount)
 {
-  LOG_SCOPE_WITH_PARAM(GetImgLog(), "imgRequest::OnDataAvailable",
+  LOG_SCOPE_WITH_PARAM(gImgLog, "imgRequest::OnDataAvailable",
                        "count", aCount);
 
   NS_ASSERTION(aRequest, "imgRequest::OnDataAvailable -- no request!");
 
   RefPtr<Image> image;
   RefPtr<ProgressTracker> progressTracker;
   bool isMultipart = false;
   bool newPartPending = false;
@@ -1110,17 +1101,17 @@ imgRequest::OnDataAvailable(nsIRequest* 
     }
   }
 
   // Notify the image that it has new data.
   nsresult rv =
     image->OnImageDataAvailable(aRequest, aContext, aInStr, aOffset, aCount);
 
   if (NS_FAILED(rv)) {
-    MOZ_LOG(GetImgLog(), LogLevel::Warning,
+    MOZ_LOG(gImgLog, LogLevel::Warning,
            ("[this=%p] imgRequest::OnDataAvailable -- "
             "copy to RasterImage failed\n", this));
     Cancel(NS_IMAGELIB_ERROR_FAILURE);
     return NS_BINDING_ABORTED;
   }
 
   return NS_OK;
 }
@@ -1233,17 +1224,17 @@ imgRequest::OnRedirectVerifyCallback(nsr
   mTimedChannel = do_QueryInterface(mChannel);
   mNewRedirectChannel = nullptr;
 
   if (LOG_TEST(LogLevel::Debug)) {
     nsAutoCString spec;
     if (mCurrentURI) {
       mCurrentURI->GetSpec(spec);
     }
-    LOG_MSG_WITH_PARAM(GetImgLog(),
+    LOG_MSG_WITH_PARAM(gImgLog,
                        "imgRequest::OnChannelRedirect", "old", spec.get());
   }
 
   // If the previous URI is a non-HTTPS URI, record that fact for later use by
   // security code, which needs to know whether there is an insecure load at any
   // point in the redirect chain.
   bool isHttps = false;
   bool isChrome = false;
@@ -1271,17 +1262,17 @@ imgRequest::OnRedirectVerifyCallback(nsr
   // Update the current URI.
   mChannel->GetURI(getter_AddRefs(mCurrentURI));
 
   if (LOG_TEST(LogLevel::Debug)) {
     nsAutoCString spec;
     if (mCurrentURI) {
       mCurrentURI->GetSpec(spec);
     }
-    LOG_MSG_WITH_PARAM(GetImgLog(), "imgRequest::OnChannelRedirect",
+    LOG_MSG_WITH_PARAM(gImgLog, "imgRequest::OnChannelRedirect",
                        "new", spec.get());
   }
 
   // Make sure we have a protocol that returns data rather than opens an
   // external application, e.g. 'mailto:'.
   bool doesNotReturnData = false;
   nsresult rv =
     NS_URIChainHasFlags(mCurrentURI,
--- a/image/imgRequestProxy.cpp
+++ b/image/imgRequestProxy.cpp
@@ -155,17 +155,17 @@ nsresult
 imgRequestProxy::Init(imgRequest* aOwner,
                       nsILoadGroup* aLoadGroup,
                       ImageURL* aURI,
                       imgINotificationObserver* aObserver)
 {
   NS_PRECONDITION(!GetOwner() && !mListener,
                   "imgRequestProxy is already initialized");
 
-  LOG_SCOPE_WITH_PARAM(GetImgLog(), "imgRequestProxy::Init", "request",
+  LOG_SCOPE_WITH_PARAM(gImgLog, "imgRequestProxy::Init", "request",
                        aOwner);
 
   MOZ_ASSERT(mAnimationConsumers == 0, "Cannot have animation before Init");
 
   mBehaviour->SetOwner(aOwner);
   mListener = aObserver;
   // Make sure to addref mListener before the AddProxy call below, since
   // that call might well want to release it if the imgRequest has
@@ -298,17 +298,17 @@ imgRequestProxy::GetStatus(nsresult* aSt
 
 NS_IMETHODIMP
 imgRequestProxy::Cancel(nsresult status)
 {
   if (mCanceled) {
     return NS_ERROR_FAILURE;
   }
 
-  LOG_SCOPE(GetImgLog(), "imgRequestProxy::Cancel");
+  LOG_SCOPE(gImgLog, "imgRequestProxy::Cancel");
 
   mCanceled = true;
 
   nsCOMPtr<nsIRunnable> ev = new imgCancelRunnable(this, status);
   return NS_DispatchToCurrentThread(ev);
 }
 
 void
@@ -329,17 +329,17 @@ imgRequestProxy::CancelAndForgetObserver
   // pending.  We still need to null out mListener before returning
   // from this function in this case.  That means we want to do the
   // RemoveProxy call right now, because we need to deliver the
   // onStopRequest.
   if (mCanceled && !mListener) {
     return NS_ERROR_FAILURE;
   }
 
-  LOG_SCOPE(GetImgLog(), "imgRequestProxy::CancelAndForgetObserver");
+  LOG_SCOPE(gImgLog, "imgRequestProxy::CancelAndForgetObserver");
 
   mCanceled = true;
 
   // Now cheat and make sure our removal from loadgroup happens async
   bool oldIsInLoadGroup = mIsInLoadGroup;
   mIsInLoadGroup = false;
 
   if (GetOwner()) {
@@ -623,17 +623,17 @@ nsresult imgRequestProxy::Clone(imgINoti
 
 nsresult
 imgRequestProxy::PerformClone(imgINotificationObserver* aObserver,
                               imgRequestProxy* (aAllocFn)(imgRequestProxy*),
                               imgRequestProxy** aClone)
 {
   NS_PRECONDITION(aClone, "Null out param");
 
-  LOG_SCOPE(GetImgLog(), "imgRequestProxy::Clone");
+  LOG_SCOPE(gImgLog, "imgRequestProxy::Clone");
 
   *aClone = nullptr;
   RefPtr<imgRequestProxy> clone = aAllocFn(this);
 
   // It is important to call |SetLoadFlags()| before calling |Init()| because
   // |Init()| adds the request to the loadgroup.
   // When a request is added to a loadgroup, its load flags are merged
   // with the load flags of the loadgroup.
@@ -773,36 +773,36 @@ NotificationTypeToString(int32_t aType)
 }
 
 void
 imgRequestProxy::Notify(int32_t aType, const mozilla::gfx::IntRect* aRect)
 {
   MOZ_ASSERT(aType != imgINotificationObserver::LOAD_COMPLETE,
              "Should call OnLoadComplete");
 
-  LOG_FUNC_WITH_PARAM(GetImgLog(), "imgRequestProxy::Notify", "type",
+  LOG_FUNC_WITH_PARAM(gImgLog, "imgRequestProxy::Notify", "type",
                       NotificationTypeToString(aType));
 
   if (!mListener || mCanceled) {
     return;
   }
 
   // Make sure the listener stays alive while we notify.
   nsCOMPtr<imgINotificationObserver> listener(mListener);
 
   mListener->Notify(this, aType, aRect);
 }
 
 void
 imgRequestProxy::OnLoadComplete(bool aLastPart)
 {
-  if (MOZ_LOG_TEST(GetImgLog(), LogLevel::Debug)) {
+  if (MOZ_LOG_TEST(gImgLog, LogLevel::Debug)) {
     nsAutoCString name;
     GetName(name);
-    LOG_FUNC_WITH_PARAM(GetImgLog(), "imgRequestProxy::OnLoadComplete",
+    LOG_FUNC_WITH_PARAM(gImgLog, "imgRequestProxy::OnLoadComplete",
                         "name", name.get());
   }
 
   // There's all sorts of stuff here that could kill us (the OnStopRequest call
   // on the listener, the removal from the loadgroup, the release of the
   // listener, etc).  Don't let them do it.
   nsCOMPtr<imgIRequest> kungFuDeathGrip(this);
 
@@ -835,36 +835,36 @@ imgRequestProxy::OnLoadComplete(bool aLa
     mListenerIsStrongRef = false;
     NS_RELEASE(obs);
   }
 }
 
 void
 imgRequestProxy::BlockOnload()
 {
-  if (MOZ_LOG_TEST(GetImgLog(), LogLevel::Debug)) {
+  if (MOZ_LOG_TEST(gImgLog, LogLevel::Debug)) {
     nsAutoCString name;
     GetName(name);
-    LOG_FUNC_WITH_PARAM(GetImgLog(), "imgRequestProxy::BlockOnload",
+    LOG_FUNC_WITH_PARAM(gImgLog, "imgRequestProxy::BlockOnload",
                         "name", name.get());
   }
 
   nsCOMPtr<imgIOnloadBlocker> blocker = do_QueryInterface(mListener);
   if (blocker) {
     blocker->BlockOnload(this);
   }
 }
 
 void
 imgRequestProxy::UnblockOnload()
 {
-  if (MOZ_LOG_TEST(GetImgLog(), LogLevel::Debug)) {
+  if (MOZ_LOG_TEST(gImgLog, LogLevel::Debug)) {
     nsAutoCString name;
     GetName(name);
-    LOG_FUNC_WITH_PARAM(GetImgLog(), "imgRequestProxy::UnblockOnload",
+    LOG_FUNC_WITH_PARAM(gImgLog, "imgRequestProxy::UnblockOnload",
                         "name", name.get());
   }
 
   nsCOMPtr<imgIOnloadBlocker> blocker = do_QueryInterface(mListener);
   if (blocker) {
     blocker->UnblockOnload(this);
   }
 }
--- a/image/test/gtest/TestStreamingLexer.cpp
+++ b/image/test/gtest/TestStreamingLexer.cpp
@@ -10,19 +10,17 @@
 using namespace mozilla;
 using namespace mozilla::image;
 
 enum class TestState
 {
   ONE,
   TWO,
   THREE,
-  UNBUFFERED,
-  SUCCESS,
-  FAILURE
+  UNBUFFERED
 };
 
 void
 CheckData(const char* aData, size_t aLength)
 {
   EXPECT_TRUE(aLength == 3);
   EXPECT_EQ(1, aData[0]);
   EXPECT_EQ(2, aData[1]);
@@ -36,20 +34,19 @@ DoLex(TestState aState, const char* aDat
     case TestState::ONE:
       CheckData(aData, aLength);
       return Transition::To(TestState::TWO, 3);
     case TestState::TWO:
       CheckData(aData, aLength);
       return Transition::To(TestState::THREE, 3);
     case TestState::THREE:
       CheckData(aData, aLength);
-      return Transition::Terminate(TestState::SUCCESS);
+      return Transition::TerminateSuccess();
     default:
-      EXPECT_TRUE(false);  // Shouldn't get here.
-      return Transition::Terminate(TestState::FAILURE);
+      MOZ_CRASH("Unknown TestState");
   }
 }
 
 LexerTransition<TestState>
 DoLexWithUnbuffered(TestState aState, const char* aData, size_t aLength,
                     Vector<char>& aUnbufferedVector)
 {
   switch (aState) {
@@ -60,207 +57,205 @@ DoLexWithUnbuffered(TestState aState, co
       EXPECT_TRUE(aLength <= 3);
       aUnbufferedVector.append(aData, aLength);
       return Transition::ContinueUnbuffered(TestState::UNBUFFERED);
     case TestState::TWO:
       CheckData(aUnbufferedVector.begin(), aUnbufferedVector.length());
       return Transition::To(TestState::THREE, 3);
     case TestState::THREE:
       CheckData(aData, aLength);
-      return Transition::Terminate(TestState::SUCCESS);
+      return Transition::TerminateSuccess();
     default:
-      EXPECT_TRUE(false);
-      return Transition::Terminate(TestState::FAILURE);
+      MOZ_CRASH("Unknown TestState");
   }
 }
 
 LexerTransition<TestState>
 DoLexWithUnbufferedTerminate(TestState aState, const char* aData, size_t aLength)
 {
   switch (aState) {
     case TestState::ONE:
       CheckData(aData, aLength);
       return Transition::ToUnbuffered(TestState::TWO, TestState::UNBUFFERED, 3);
     case TestState::UNBUFFERED:
-      return Transition::Terminate(TestState::SUCCESS);
+      return Transition::TerminateSuccess();
     default:
-      EXPECT_TRUE(false);
-      return Transition::Terminate(TestState::FAILURE);
+      MOZ_CRASH("Unknown TestState");
   }
 }
 
 TEST(ImageStreamingLexer, SingleChunk)
 {
   StreamingLexer<TestState> lexer(Transition::To(TestState::ONE, 3));
   char data[9] = { 1, 2, 3, 1, 2, 3, 1, 2, 3 };
 
   // Test delivering all the data at once.
-  Maybe<TestState> result = lexer.Lex(data, sizeof(data), DoLex);
+  Maybe<TerminalState> result = lexer.Lex(data, sizeof(data), DoLex);
   EXPECT_TRUE(result.isSome());
-  EXPECT_EQ(TestState::SUCCESS, *result);
+  EXPECT_EQ(Some(TerminalState::SUCCESS), result);
 }
 
 TEST(ImageStreamingLexer, SingleChunkWithUnbuffered)
 {
   StreamingLexer<TestState> lexer(Transition::To(TestState::ONE, 3));
   char data[9] = { 1, 2, 3, 1, 2, 3, 1, 2, 3 };
   Vector<char> unbufferedVector;
 
   // Test delivering all the data at once.
-  Maybe<TestState> result =
+  Maybe<TerminalState> result =
     lexer.Lex(data, sizeof(data),
               [&](TestState aState, const char* aData, size_t aLength) {
       return DoLexWithUnbuffered(aState, aData, aLength, unbufferedVector);
   });
   EXPECT_TRUE(result.isSome());
-  EXPECT_EQ(TestState::SUCCESS, *result);
+  EXPECT_EQ(Some(TerminalState::SUCCESS), result);
 }
 
 TEST(ImageStreamingLexer, ChunkPerState)
 {
   StreamingLexer<TestState> lexer(Transition::To(TestState::ONE, 3));
   char data[9] = { 1, 2, 3, 1, 2, 3, 1, 2, 3 };
 
   // Test delivering in perfectly-sized chunks, one per state.
   for (unsigned i = 0 ; i < 3 ; ++i) {
-    Maybe<TestState> result = lexer.Lex(data + 3 * i, 3, DoLex);
+    Maybe<TerminalState> result = lexer.Lex(data + 3 * i, 3, DoLex);
 
     if (i == 2) {
       EXPECT_TRUE(result.isSome());
-      EXPECT_EQ(TestState::SUCCESS, *result);
+      EXPECT_EQ(Some(TerminalState::SUCCESS), result);
     } else {
       EXPECT_TRUE(result.isNothing());
     }
   }
 }
 
 TEST(ImageStreamingLexer, ChunkPerStateWithUnbuffered)
 {
   StreamingLexer<TestState> lexer(Transition::To(TestState::ONE, 3));
   char data[9] = { 1, 2, 3, 1, 2, 3, 1, 2, 3 };
   Vector<char> unbufferedVector;
 
   // Test delivering in perfectly-sized chunks, one per state.
   for (unsigned i = 0 ; i < 3 ; ++i) {
-    Maybe<TestState> result =
+    Maybe<TerminalState> result =
       lexer.Lex(data + 3 * i, 3,
                 [&](TestState aState, const char* aData, size_t aLength) {
         return DoLexWithUnbuffered(aState, aData, aLength, unbufferedVector);
     });
 
     if (i == 2) {
       EXPECT_TRUE(result.isSome());
-      EXPECT_EQ(TestState::SUCCESS, *result);
+      EXPECT_EQ(Some(TerminalState::SUCCESS), result);
     } else {
       EXPECT_TRUE(result.isNothing());
     }
   }
 }
 
 TEST(ImageStreamingLexer, OneByteChunks)
 {
   StreamingLexer<TestState> lexer(Transition::To(TestState::ONE, 3));
   char data[9] = { 1, 2, 3, 1, 2, 3, 1, 2, 3 };
 
   // Test delivering in one byte chunks.
   for (unsigned i = 0 ; i < 9 ; ++i) {
-    Maybe<TestState> result = lexer.Lex(data + i, 1, DoLex);
+    Maybe<TerminalState> result = lexer.Lex(data + i, 1, DoLex);
 
     if (i == 8) {
       EXPECT_TRUE(result.isSome());
-      EXPECT_EQ(TestState::SUCCESS, *result);
+      EXPECT_EQ(Some(TerminalState::SUCCESS), result);
     } else {
       EXPECT_TRUE(result.isNothing());
     }
   }
 }
 
 TEST(ImageStreamingLexer, OneByteChunksWithUnbuffered)
 {
   StreamingLexer<TestState> lexer(Transition::To(TestState::ONE, 3));
   char data[9] = { 1, 2, 3, 1, 2, 3, 1, 2, 3 };
   Vector<char> unbufferedVector;
 
   // Test delivering in one byte chunks.
   for (unsigned i = 0 ; i < 9 ; ++i) {
-    Maybe<TestState> result =
+    Maybe<TerminalState> result =
       lexer.Lex(data + i, 1,
                 [&](TestState aState, const char* aData, size_t aLength) {
         return DoLexWithUnbuffered(aState, aData, aLength, unbufferedVector);
     });
 
     if (i == 8) {
       EXPECT_TRUE(result.isSome());
-      EXPECT_EQ(TestState::SUCCESS, *result);
+      EXPECT_EQ(Some(TerminalState::SUCCESS), result);
     } else {
       EXPECT_TRUE(result.isNothing());
     }
   }
 }
 
 TEST(ImageStreamingLexer, TerminateSuccess)
 {
   StreamingLexer<TestState> lexer(Transition::To(TestState::ONE, 3));
   char data[9] = { 1, 2, 3, 1, 2, 3, 1, 2, 3 };
 
   // Test that Terminate is "sticky".
-  Maybe<TestState> result =
+  Maybe<TerminalState> result =
     lexer.Lex(data, sizeof(data),
               [&](TestState aState, const char* aData, size_t aLength) {
       EXPECT_TRUE(aState == TestState::ONE);
-      return Transition::Terminate(TestState::SUCCESS);
+      return Transition::TerminateSuccess();
   });
   EXPECT_TRUE(result.isSome());
-  EXPECT_EQ(TestState::SUCCESS, *result);
+  EXPECT_EQ(Some(TerminalState::SUCCESS), result);
 
   result =
     lexer.Lex(data, sizeof(data),
               [&](TestState aState, const char* aData, size_t aLength) {
       EXPECT_TRUE(false);  // Shouldn't get here.
-      return Transition::Terminate(TestState::FAILURE);
+      return Transition::TerminateFailure();
   });
   EXPECT_TRUE(result.isSome());
-  EXPECT_EQ(TestState::SUCCESS, *result);
+  EXPECT_EQ(Some(TerminalState::SUCCESS), result);
 }
 
 TEST(ImageStreamingLexer, TerminateFailure)
 {
   StreamingLexer<TestState> lexer(Transition::To(TestState::ONE, 3));
   char data[9] = { 1, 2, 3, 1, 2, 3, 1, 2, 3 };
 
   // Test that Terminate is "sticky".
-  Maybe<TestState> result =
+  Maybe<TerminalState> result =
     lexer.Lex(data, sizeof(data),
               [&](TestState aState, const char* aData, size_t aLength) {
       EXPECT_TRUE(aState == TestState::ONE);
-      return Transition::Terminate(TestState::FAILURE);
+      return Transition::TerminateFailure();
   });
   EXPECT_TRUE(result.isSome());
-  EXPECT_EQ(TestState::FAILURE, *result);
+  EXPECT_EQ(Some(TerminalState::FAILURE), result);
 
   result =
     lexer.Lex(data, sizeof(data),
               [&](TestState aState, const char* aData, size_t aLength) {
       EXPECT_TRUE(false);  // Shouldn't get here.
-      return Transition::Terminate(TestState::FAILURE);
+      return Transition::TerminateFailure();
   });
   EXPECT_TRUE(result.isSome());
-  EXPECT_EQ(TestState::FAILURE, *result);
+  EXPECT_EQ(Some(TerminalState::FAILURE), result);
 }
 
 TEST(ImageStreamingLexer, TerminateUnbuffered)
 {
   StreamingLexer<TestState> lexer(Transition::To(TestState::ONE, 3));
   char data[9] = { 1, 2, 3, 1, 2, 3, 1, 2, 3 };
 
   // Test that Terminate works during an unbuffered read.
   for (unsigned i = 0 ; i < 9 ; ++i) {
-    Maybe<TestState> result =
+    Maybe<TerminalState> result =
       lexer.Lex(data + i, 1, DoLexWithUnbufferedTerminate);
 
     if (i > 2) {
       EXPECT_TRUE(result.isSome());
-      EXPECT_EQ(TestState::SUCCESS, *result);
+      EXPECT_EQ(Some(TerminalState::SUCCESS), result);
     } else {
       EXPECT_TRUE(result.isNothing());
     }
   }
 }
--- a/ipc/glue/GeckoChildProcessHost.cpp
+++ b/ipc/glue/GeckoChildProcessHost.cpp
@@ -944,16 +944,22 @@ GeckoChildProcessHost::PerformAsyncLaunc
       mSandboxBroker.AllowReadFile(it->c_str());
     }
 
     for (auto it = mAllowedFilesReadWrite.begin();
          it != mAllowedFilesReadWrite.end();
          ++it) {
       mSandboxBroker.AllowReadWriteFile(it->c_str());
     }
+
+    for (auto it = mAllowedDirectories.begin();
+         it != mAllowedDirectories.end();
+         ++it) {
+      mSandboxBroker.AllowDirectory(it->c_str());
+    }
   }
 #endif // XP_WIN && MOZ_SANDBOX
 
   // Add the application directory path (-appdir path)
   AddAppDirToCommandLine(cmdLine);
 
   // XXX Command line params past this point are expected to be at
   // the end of the command line string, and in a specific order.
--- a/ipc/glue/GeckoChildProcessHost.h
+++ b/ipc/glue/GeckoChildProcessHost.h
@@ -155,16 +155,17 @@ protected:
 #ifdef XP_WIN
   void InitWindowsGroupID();
   nsString mGroupId;
 
 #ifdef MOZ_SANDBOX
   SandboxBroker mSandboxBroker;
   std::vector<std::wstring> mAllowedFilesRead;
   std::vector<std::wstring> mAllowedFilesReadWrite;
+  std::vector<std::wstring> mAllowedDirectories;
   bool mEnableSandboxLogging;
   int32_t mSandboxLevel;
 #endif
 #endif // XP_WIN
 
 #if defined(OS_POSIX)
   base::file_handle_mapping_vector mFileMap;
 #endif
--- a/js/src/asmjs/AsmJSLink.cpp
+++ b/js/src/asmjs/AsmJSLink.cpp
@@ -732,22 +732,18 @@ CallAsmJS(JSContext* cx, unsigned argc, 
     }
 
     {
         // Push an AsmJSActivation to describe the asm.js frames we're about to
         // push when running this module. Additionally, push a JitActivation so
         // that the optimized asm.js-to-Ion FFI call path (which we want to be
         // very fast) can avoid doing so. The JitActivation is marked as
         // inactive so stack iteration will skip over it.
-        //
-        // We needn't provide an entry script pointer; that's only used for
-        // reporting entry points to performance-monitoring tools, and asm.js ->
-        // Ion calls will never be entry points.
         AsmJSActivation activation(cx, module);
-        JitActivation jitActivation(cx, /* entryScript */ nullptr, /* active */ false);
+        JitActivation jitActivation(cx, /* active */ false);
 
         // Call the per-exported-function trampoline created by GenerateEntry.
         AsmJSModule::CodePtr enter = module.entryTrampoline(func);
         if (!CALL_GENERATED_2(enter, coercedArgs.begin(), module.globalData()))
             return false;
     }
 
     if (callArgs.isConstructing()) {
--- a/js/src/asmjs/AsmJSValidate.cpp
+++ b/js/src/asmjs/AsmJSValidate.cpp
@@ -64,16 +64,17 @@ using mozilla::CountLeadingZeroes32;
 using mozilla::DebugOnly;
 using mozilla::HashGeneric;
 using mozilla::IsNaN;
 using mozilla::IsNegativeZero;
 using mozilla::Maybe;
 using mozilla::Move;
 using mozilla::PositiveInfinity;
 using mozilla::UniquePtr;
+using JS::AsmJSOption;
 using JS::GenericNaN;
 
 /*****************************************************************************/
 // ParseNode utilities
 
 static inline ParseNode*
 NextNode(ParseNode* pn)
 {
@@ -167,28 +168,32 @@ CallArgList(ParseNode* pn)
 
 static inline ParseNode*
 VarListHead(ParseNode* pn)
 {
     MOZ_ASSERT(pn->isKind(PNK_VAR) || pn->isKind(PNK_CONST));
     return ListHead(pn);
 }
 
+static inline bool
+IsDefaultCase(ParseNode* pn)
+{
+    return pn->as<CaseClause>().isDefault();
+}
+
 static inline ParseNode*
 CaseExpr(ParseNode* pn)
 {
-    MOZ_ASSERT(pn->isKind(PNK_CASE) || pn->isKind(PNK_DEFAULT));
-    return BinaryLeft(pn);
+    return pn->as<CaseClause>().caseExpression();
 }
 
 static inline ParseNode*
 CaseBody(ParseNode* pn)
 {
-    MOZ_ASSERT(pn->isKind(PNK_CASE) || pn->isKind(PNK_DEFAULT));
-    return BinaryRight(pn);
+    return pn->as<CaseClause>().statementList();
 }
 
 static inline ParseNode*
 BinaryOpLeft(ParseNode* pn)
 {
     MOZ_ASSERT(pn->isBinaryOperation());
     MOZ_ASSERT(pn->isArity(PN_LIST));
     MOZ_ASSERT(pn->pn_count == 2);
@@ -5902,43 +5907,42 @@ CheckCaseExpr(FunctionValidator& f, Pars
 
     return true;
 }
 
 static bool
 CheckDefaultAtEnd(FunctionValidator& f, ParseNode* stmt)
 {
     for (; stmt; stmt = NextNode(stmt)) {
-        MOZ_ASSERT(stmt->isKind(PNK_CASE) || stmt->isKind(PNK_DEFAULT));
-        if (stmt->isKind(PNK_DEFAULT) && NextNode(stmt) != nullptr)
+        if (IsDefaultCase(stmt) && NextNode(stmt) != nullptr)
             return f.fail(stmt, "default label must be at the end");
     }
 
     return true;
 }
 
 static bool
 CheckSwitchRange(FunctionValidator& f, ParseNode* stmt, int32_t* low, int32_t* high,
                  int32_t* tableLength)
 {
-    if (stmt->isKind(PNK_DEFAULT)) {
+    if (IsDefaultCase(stmt)) {
         *low = 0;
         *high = -1;
         *tableLength = 0;
         return true;
     }
 
     int32_t i = 0;
     if (!CheckCaseExpr(f, CaseExpr(stmt), &i))
         return false;
 
     *low = *high = i;
 
     ParseNode* initialStmt = stmt;
-    for (stmt = NextNode(stmt); stmt && stmt->isKind(PNK_CASE); stmt = NextNode(stmt)) {
+    for (stmt = NextNode(stmt); stmt && !IsDefaultCase(stmt); stmt = NextNode(stmt)) {
         int32_t i = 0;
         if (!CheckCaseExpr(f, CaseExpr(stmt), &i))
             return false;
 
         *low = Min(*low, i);
         *high = Max(*high, i);
     }
 
@@ -6003,34 +6007,34 @@ CheckSwitch(FunctionValidator& f, ParseN
     if (!CheckSwitchRange(f, stmt, &low, &high, &tableLength))
         return false;
 
     Vector<bool, 8> cases(f.cx());
     if (!cases.resize(tableLength))
         return false;
 
     uint32_t numCases = 0;
-    for (; stmt && stmt->isKind(PNK_CASE); stmt = NextNode(stmt)) {
+    for (; stmt && !IsDefaultCase(stmt); stmt = NextNode(stmt)) {
         int32_t caseValue = ExtractNumericLiteral(f.m(), CaseExpr(stmt)).toInt32();
         unsigned caseIndex = caseValue - low;
 
         if (cases[caseIndex])
             return f.fail(stmt, "no duplicate case labels");
 
         cases[caseIndex] = true;
         numCases += 1;
         f.writeI32(caseValue);
 
         if (!CheckStatement(f, CaseBody(stmt)))
             return false;
 
     }
 
     bool hasDefault = false;
-    if (stmt && stmt->isKind(PNK_DEFAULT)) {
+    if (stmt && IsDefaultCase(stmt)) {
         hasDefault = true;
         if (!CheckStatement(f, CaseBody(stmt)))
             return false;
     }
 
     PatchSwitch(f, hasDefaultAt, hasDefault, lowAt, low, highAt, high, numCasesAt, numCases);
     return true;
 }
@@ -8262,21 +8266,24 @@ EstablishPreconditions(ExclusiveContext*
 #endif
 
     if (!cx->jitSupportsFloatingPoint())
         return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by lack of floating point support");
 
     if (cx->gcSystemPageSize() != AsmJSPageSize)
         return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by non 4KiB system page size");
 
-    if (!parser.options().asmJSOption)
+    switch (parser.options().asmJSOption) {
+      case AsmJSOption::Disabled:
         return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by javascript.options.asmjs in about:config");
-
-    if (cx->compartment()->debuggerObservesAsmJS())
+      case AsmJSOption::DisabledByDebugger:
         return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by debugger");
+      case AsmJSOption::Enabled:
+        break;
+    }
 
     if (parser.pc->isGenerator())
         return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by generator context");
 
     if (parser.pc->isArrowFunction())
         return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by arrow function context");
 
     // Class constructors are also methods
--- a/js/src/builtin/ReflectParse.cpp
+++ b/js/src/builtin/ReflectParse.cpp
@@ -3,16 +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/. */
 
 /* JS reflection package. */
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/DebugOnly.h"
+#include "mozilla/Move.h"
 
 #include <stdlib.h>
 
 #include "jsarray.h"
 #include "jsatom.h"
 #include "jsobj.h"
 #include "jspubtd.h"
 
@@ -27,16 +28,17 @@
 #include "frontend/ParseNode-inl.h"
 
 using namespace js;
 using namespace js::frontend;
 
 using JS::AutoValueArray;
 using mozilla::ArrayLength;
 using mozilla::DebugOnly;
+using mozilla::Forward;
 
 enum class ParseTarget
 {
     Script,
     Module
 };
 
 enum ASTType {
@@ -215,16 +217,23 @@ GetPropertyDefault(JSContext* cx, Handle
         return false;
     if (!found) {
         result.set(defaultValue);
         return true;
     }
     return GetProperty(cx, obj, obj, id, result);
 }
 
+enum class GeneratorStyle
+{
+    None,
+    Legacy,
+    ES6
+};
+
 /*
  * Builder class that constructs JavaScript AST node objects. See:
  *
  *     https://developer.mozilla.org/en/SpiderMonkey/Parser_API
  *
  * Bug 569487: generalize builder interface
  */
 class NodeBuilder
@@ -291,133 +300,53 @@ class NodeBuilder
         return true;
     }
 
     void setTokenStream(TokenStream* ts) {
         tokenStream = ts;
     }
 
   private:
-    bool callback(HandleValue fun, TokenPos* pos, MutableHandleValue dst) {
-        if (saveLoc) {
-            RootedValue loc(cx);
-            if (!newNodeLoc(pos, &loc))
-                return false;
-            AutoValueArray<1> argv(cx);
-            argv[0].set(loc);
-            return Invoke(cx, userv, fun, argv.length(), argv.begin(), dst);
-        }
-
-        AutoValueArray<1> argv(cx);
-        argv[0].setNull(); /* no zero-length arrays allowed! */
-        return Invoke(cx, userv, fun, 0, argv.begin(), dst);
-    }
-
-    bool callback(HandleValue fun, HandleValue v1, TokenPos* pos, MutableHandleValue dst) {
-        if (saveLoc) {
-            RootedValue loc(cx);
-            if (!newNodeLoc(pos, &loc))
-                return false;
-            AutoValueArray<2> argv(cx);
-            argv[0].set(v1);
-            argv[1].set(loc);
-            return Invoke(cx, userv, fun, argv.length(), argv.begin(), dst);
-        }
-
-        AutoValueArray<1> argv(cx);
-        argv[0].set(v1);
-        return Invoke(cx, userv, fun, argv.length(), argv.begin(), dst);
-    }
-
-    bool callback(HandleValue fun, HandleValue v1, HandleValue v2, TokenPos* pos,
-                  MutableHandleValue dst) {
-        if (saveLoc) {
-            RootedValue loc(cx);
-            if (!newNodeLoc(pos, &loc))
-                return false;
-            AutoValueArray<3> argv(cx);
-            argv[0].set(v1);
-            argv[1].set(v2);
-            argv[2].set(loc);
-            return Invoke(cx, userv, fun, argv.length(), argv.begin(), dst);
-        }
-
-        AutoValueArray<2> argv(cx);
-        argv[0].set(v1);
-        argv[1].set(v2);
-        return Invoke(cx, userv, fun, argv.length(), argv.begin(), dst);
-    }
-
-    bool callback(HandleValue fun, HandleValue v1, HandleValue v2, HandleValue v3, TokenPos* pos,
-                  MutableHandleValue dst) {
+    template <size_t N>
+    bool callbackHelper(HandleValue fun, AutoValueArray<N>& args, size_t i,
+                        TokenPos* pos, MutableHandleValue dst)
+    {
+        // The end of the implementation of callback(). All arguments except
+        // loc have already been stored in range [0, i) or args.
+        MOZ_ASSERT(i == N - 1);
         if (saveLoc) {
             RootedValue loc(cx);
             if (!newNodeLoc(pos, &loc))
                 return false;
-            AutoValueArray<4> argv(cx);
-            argv[0].set(v1);
-            argv[1].set(v2);
-            argv[2].set(v3);
-            argv[3].set(loc);
-            return Invoke(cx, userv, fun, argv.length(), argv.begin(), dst);
-        }
-
-        AutoValueArray<3> argv(cx);
-        argv[0].set(v1);
-        argv[1].set(v2);
-        argv[2].set(v3);
-        return Invoke(cx, userv, fun, argv.length(), argv.begin(), dst);
-    }
-
-    bool callback(HandleValue fun, HandleValue v1, HandleValue v2, HandleValue v3, HandleValue v4,
-                  TokenPos* pos, MutableHandleValue dst) {
-        if (saveLoc) {
-            RootedValue loc(cx);
-            if (!newNodeLoc(pos, &loc))
-                return false;
-            AutoValueArray<5> argv(cx);
-            argv[0].set(v1);
-            argv[1].set(v2);
-            argv[2].set(v3);
-            argv[3].set(v4);
-            argv[4].set(loc);
-            return Invoke(cx, userv, fun, argv.length(), argv.begin(), dst);
+            args[i++].set(loc);
         }
-
-        AutoValueArray<4> argv(cx);
-        argv[0].set(v1);
-        argv[1].set(v2);
-        argv[2].set(v3);
-        argv[3].set(v4);
-        return Invoke(cx, userv, fun, argv.length(), argv.begin(), dst);
+        return Invoke(cx, userv, fun, N, args.begin(), dst);
     }
 
-    bool callback(HandleValue fun, HandleValue v1, HandleValue v2, HandleValue v3, HandleValue v4,
-                  HandleValue v5, TokenPos* pos, MutableHandleValue dst) {
-        if (saveLoc) {
-            RootedValue loc(cx);
-            if (!newNodeLoc(pos, &loc))
-                return false;
-            AutoValueArray<6> argv(cx);
-            argv[0].set(v1);
-            argv[1].set(v2);
-            argv[2].set(v3);
-            argv[3].set(v4);
-            argv[4].set(v5);
-            argv[5].set(loc);
-            return Invoke(cx, userv, fun, argv.length(), argv.begin(), dst);
-        }
-
-        AutoValueArray<5> argv(cx);
-        argv[0].set(v1);
-        argv[1].set(v2);
-        argv[2].set(v3);
-        argv[3].set(v4);
-        argv[4].set(v5);
-        return Invoke(cx, userv, fun, argv.length(), argv.begin(), dst);
+    // Helper function for callback(). Note that all Arguments must be types
+    // that convert to HandleValue, so this is not really as template-y as it
+    // seems, just variadic.
+    template <size_t N, typename... Arguments>
+    bool callbackHelper(HandleValue fun, AutoValueArray<N>& args, size_t i,
+                        HandleValue head, Arguments&&... tail)
+    {
+        // Recursive loop to store the arguments in the array. This eventually
+        // bottoms out in a call to the non-template callbackHelper() above.
+        args[i].set(head);
+        return callbackHelper(fun, args, i + 1, Forward<Arguments>(tail)...);
+    }
+
+    // Invoke a user-defined callback. The actual signature is:
+    //
+    //     bool callback(HandleValue fun, HandleValue... args, TokenPos* pos,
+    //                   MutableHandleValue dst);
+    template <typename... Arguments>
+    bool callback(HandleValue fun, Arguments&&... args) {
+        AutoValueArray<sizeof...(args) - 1> argv(cx);
+        return callbackHelper(fun, argv, 0, Forward<Arguments>(args)...);
     }
 
     // WARNING: Returning a Handle is non-standard, but it works in this case
     // because both |v| and |UndefinedHandleValue| are definitely rooted on a
     // previous stack frame (i.e. we're just choosing between two
     // already-rooted values).
     HandleValue opt(HandleValue v) {
         MOZ_ASSERT_IF(v.isMagic(), v.whyMagic() == JS_SERIALIZE_NO_NODE);
@@ -442,143 +371,65 @@ class NodeBuilder
             return false;
 
         dst.set(nobj);
         return true;
     }
 
     bool newArray(NodeVector& elts, MutableHandleValue dst);
 
-    bool newNode(ASTType type, TokenPos* pos, MutableHandleObject dst);
-
-    bool newNode(ASTType type, TokenPos* pos, MutableHandleValue dst) {
-        RootedObject node(cx);
-        return newNode(type, pos, &node) &&
-               setResult(node, dst);
-    }
-
-    bool newNode(ASTType type, TokenPos* pos,
-                 const char* childName, HandleValue child,
-                 MutableHandleValue dst) {
-        RootedObject node(cx);
-        return newNode(type, pos, &node) &&
-               setProperty(node, childName, child) &&
-               setResult(node, dst);
-    }
-
-    bool newNode(ASTType type, TokenPos* pos,
-                 const char* childName1, HandleValue child1,
-                 const char* childName2, HandleValue child2,
-                 MutableHandleValue dst) {
-        RootedObject node(cx);
-        return newNode(type, pos, &node) &&
-               setProperty(node, childName1, child1) &&
-               setProperty(node, childName2, child2) &&
-               setResult(node, dst);
-    }
-
-    bool newNode(ASTType type, TokenPos* pos,
-                 const char* childName1, HandleValue child1,
-                 const char* childName2, HandleValue child2,
-                 const char* childName3, HandleValue child3,
-                 MutableHandleValue dst) {
-        RootedObject node(cx);
-        return newNode(type, pos, &node) &&
-               setProperty(node, childName1, child1) &&
-               setProperty(node, childName2, child2) &&
-               setProperty(node, childName3, child3) &&
-               setResult(node, dst);
-    }
-
-    bool newNode(ASTType type, TokenPos* pos,
-                 const char* childName1, HandleValue child1,
-                 const char* childName2, HandleValue child2,
-                 const char* childName3, HandleValue child3,
-                 const char* childName4, HandleValue child4,
-                 MutableHandleValue dst) {
-        RootedObject node(cx);
-        return newNode(type, pos, &node) &&
-               setProperty(node, childName1, child1) &&
-               setProperty(node, childName2, child2) &&
-               setProperty(node, childName3, child3) &&
-               setProperty(node, childName4, child4) &&
-               setResult(node, dst);
+    bool createNode(ASTType type, TokenPos* pos, MutableHandleObject dst);
+
+    bool newNodeHelper(HandleObject obj, MutableHandleValue dst) {
+        // The end of the implementation of newNode().
+        MOZ_ASSERT(obj);
+        dst.setObject(*obj);
+        return true;
     }
 
-    bool newNode(ASTType type, TokenPos* pos,
-                 const char* childName1, HandleValue child1,
-                 const char* childName2, HandleValue child2,
-                 const char* childName3, HandleValue child3,
-                 const char* childName4, HandleValue child4,
-                 const char* childName5, HandleValue child5,
-                 MutableHandleValue dst) {
-        RootedObject node(cx);
-        return newNode(type, pos, &node) &&
-               setProperty(node, childName1, child1) &&
-               setProperty(node, childName2, child2) &&
-               setProperty(node, childName3, child3) &&
-               setProperty(node, childName4, child4) &&
-               setProperty(node, childName5, child5) &&
-               setResult(node, dst);
+    template <typename... Arguments>
+    bool newNodeHelper(HandleObject obj, const char *name, HandleValue value,
+                       Arguments&&... rest)
+    {
+        // Recursive loop to define properties. Note that the newNodeHelper()
+        // call below passes two fewer arguments than we received, as we omit
+        // `name` and `value`. This eventually bottoms out in a call to the
+        // non-template newNodeHelper() above.
+        return defineProperty(obj, name, value)
+               && newNodeHelper(obj, Forward<Arguments>(rest)...);
     }
 
-    bool newNode(ASTType type, TokenPos* pos,
-                 const char* childName1, HandleValue child1,
-                 const char* childName2, HandleValue child2,
-                 const char* childName3, HandleValue child3,
-                 const char* childName4, HandleValue child4,
-                 const char* childName5, HandleValue child5,
-                 const char* childName6, HandleValue child6,
-                 MutableHandleValue dst) {
+    // Create a node object with "type" and "loc" properties, as well as zero
+    // or more properties passed in as arguments. The signature is really more
+    // like:
+    //
+    //     bool newNode(ASTType type, TokenPos* pos,
+    //                  {const char *name0, HandleValue value0,}...
+    //                  MutableHandleValue dst);
+    template <typename... Arguments>
+    bool newNode(ASTType type, TokenPos* pos, Arguments&&... args) {
         RootedObject node(cx);
-        return newNode(type, pos, &node) &&
-               setProperty(node, childName1, child1) &&
-               setProperty(node, childName2, child2) &&
-               setProperty(node, childName3, child3) &&
-               setProperty(node, childName4, child4) &&
-               setProperty(node, childName5, child5) &&
-               setProperty(node, childName6, child6) &&
-               setResult(node, dst);
-    }
-
-    bool newNode(ASTType type, TokenPos* pos,
-                 const char* childName1, HandleValue child1,
-                 const char* childName2, HandleValue child2,
-                 const char* childName3, HandleValue child3,
-                 const char* childName4, HandleValue child4,
-                 const char* childName5, HandleValue child5,
-                 const char* childName6, HandleValue child6,
-                 const char* childName7, HandleValue child7,
-                 MutableHandleValue dst) {
-        RootedObject node(cx);
-        return newNode(type, pos, &node) &&
-               setProperty(node, childName1, child1) &&
-               setProperty(node, childName2, child2) &&
-               setProperty(node, childName3, child3) &&
-               setProperty(node, childName4, child4) &&
-               setProperty(node, childName5, child5) &&
-               setProperty(node, childName6, child6) &&
-               setProperty(node, childName7, child7) &&
-               setResult(node, dst);
+        return createNode(type, pos, &node) &&
+               newNodeHelper(node, Forward<Arguments>(args)...);
     }
 
     bool listNode(ASTType type, const char* propName, NodeVector& elts, TokenPos* pos,
                   MutableHandleValue dst) {
         RootedValue array(cx);
         if (!newArray(elts, &array))
             return false;
 
         RootedValue cb(cx, callbacks[type]);
         if (!cb.isNull())
             return callback(cb, array, pos, dst);
 
         return newNode(type, pos, propName, array, dst);
     }
 
-    bool setProperty(HandleObject obj, const char* name, HandleValue val) {
+    bool defineProperty(HandleObject obj, const char* name, HandleValue val) {
         MOZ_ASSERT_IF(val.isMagic(), val.whyMagic() == JS_SERIALIZE_NO_NODE);
 
         /*
          * Bug 575416: instead of Atomize, lookup constant atoms in tbl file
          */
         RootedAtom atom(cx, Atomize(cx, name, strlen(name)));
         if (!atom)
             return false;
@@ -587,22 +438,16 @@ class NodeBuilder
         RootedValue optVal(cx, val.isMagic(JS_SERIALIZE_NO_NODE) ? NullValue() : val);
         return DefineProperty(cx, obj, atom->asPropertyName(), optVal);
     }
 
     bool newNodeLoc(TokenPos* pos, MutableHandleValue dst);
 
     bool setNodeLoc(HandleObject node, TokenPos* pos);
 
-    bool setResult(HandleObject obj, MutableHandleValue dst) {
-        MOZ_ASSERT(obj);
-        dst.setObject(*obj);
-        return true;
-    }
-
   public:
     /*
      * All of the public builder methods take as their last two
      * arguments a nullable token position and a non-nullable, rooted
      * outparam.
      *
      * Any Value arguments representing optional subnodes may be a
      * JS_SERIALIZE_NO_NODE magic value.
@@ -615,18 +460,18 @@ class NodeBuilder
     bool program(NodeVector& elts, TokenPos* pos, MutableHandleValue dst);
 
     bool literal(HandleValue val, TokenPos* pos, MutableHandleValue dst);
 
     bool identifier(HandleValue name, TokenPos* pos, MutableHandleValue dst);
 
     bool function(ASTType type, TokenPos* pos,
                   HandleValue id, NodeVector& args, NodeVector& defaults,
-                  HandleValue body, HandleValue rest, bool isGenerator, bool isExpression,
-                  MutableHandleValue dst);
+                  HandleValue body, HandleValue rest, GeneratorStyle generatorStyle,
+                  bool isExpression, MutableHandleValue dst);
 
     bool variableDeclarator(HandleValue id, HandleValue init, TokenPos* pos,
                             MutableHandleValue dst);
 
     bool switchCase(HandleValue expr, NodeVector& elts, TokenPos* pos, MutableHandleValue dst);
 
     bool catchClause(HandleValue var, HandleValue guard, HandleValue body, TokenPos* pos,
                      MutableHandleValue dst);
@@ -783,26 +628,26 @@ class NodeBuilder
 
     bool propertyPattern(HandleValue key, HandleValue patt, bool isShorthand, TokenPos* pos,
                          MutableHandleValue dst);
 };
 
 } /* anonymous namespace */
 
 bool
-NodeBuilder::newNode(ASTType type, TokenPos* pos, MutableHandleObject dst)
+NodeBuilder::createNode(ASTType type, TokenPos* pos, MutableHandleObject dst)
 {
     MOZ_ASSERT(type > AST_ERROR && type < AST_LIMIT);
 
     RootedValue tv(cx);
     RootedPlainObject node(cx, NewBuiltinClassInstance<PlainObject>(cx));
     if (!node ||
         !setNodeLoc(node, pos) ||
         !atomValue(nodeTypeNames[type], &tv) ||
-        !setProperty(node, "type", tv)) {
+        !defineProperty(node, "type", tv)) {
         return false;
     }
 
     dst.set(node);
     return true;
 }
 
 bool
@@ -854,55 +699,55 @@ NodeBuilder::newNodeLoc(TokenPos* pos, M
     uint32_t startLineNum, startColumnIndex;
     uint32_t endLineNum, endColumnIndex;
     tokenStream->srcCoords.lineNumAndColumnIndex(pos->begin, &startLineNum, &startColumnIndex);
     tokenStream->srcCoords.lineNumAndColumnIndex(pos->end, &endLineNum, &endColumnIndex);
 
     if (!newObject(&to))
         return false;
     val.setObject(*to);
-    if (!setProperty(loc, "start", val))
+    if (!defineProperty(loc, "start", val))
         return false;
     val.setNumber(startLineNum);
-    if (!setProperty(to, "line", val))
+    if (!defineProperty(to, "line", val))
         return false;
     val.setNumber(startColumnIndex);
-    if (!setProperty(to, "column", val))
+    if (!defineProperty(to, "column", val))
         return false;
 
     if (!newObject(&to))
         return false;
     val.setObject(*to);
-    if (!setProperty(loc, "end", val))
+    if (!defineProperty(loc, "end", val))
         return false;
     val.setNumber(endLineNum);
-    if (!setProperty(to, "line", val))
+    if (!defineProperty(to, "line", val))
         return false;
     val.setNumber(endColumnIndex);
-    if (!setProperty(to, "column", val))
+    if (!defineProperty(to, "column", val))
         return false;
 
-    if (!setProperty(loc, "source", srcval))
+    if (!defineProperty(loc, "source", srcval))
         return false;
 
     return true;
 }
 
 bool
 NodeBuilder::setNodeLoc(HandleObject node, TokenPos* pos)
 {
     if (!saveLoc) {
         RootedValue nullVal(cx, NullValue());
-        setProperty(node, "loc", nullVal);
+        defineProperty(node, "loc", nullVal);
         return true;
     }
 
     RootedValue loc(cx);
     return newNodeLoc(pos, &loc) &&
-           setProperty(node, "loc", loc);
+           defineProperty(node, "loc", loc);
 }
 
 bool
 NodeBuilder::program(NodeVector& elts, TokenPos* pos, MutableHandleValue dst)
 {
     return listNode(AST_PROGRAM, "body", elts, pos, dst);
 }
 
@@ -1752,33 +1597,55 @@ NodeBuilder::arrayPattern(NodeVector& el
 {
     return listNode(AST_ARRAY_PATT, "elements", elts, pos, dst);
 }
 
 bool
 NodeBuilder::function(ASTType type, TokenPos* pos,
                       HandleValue id, NodeVector& args, NodeVector& defaults,
                       HandleValue body, HandleValue rest,
-                      bool isGenerator, bool isExpression,
+                      GeneratorStyle generatorStyle, bool isExpression,
                       MutableHandleValue dst)
 {
     RootedValue array(cx), defarray(cx);
     if (!newArray(args, &array))
         return false;
     if (!newArray(defaults, &defarray))
         return false;
 
+    bool isGenerator = generatorStyle != GeneratorStyle::None;
     RootedValue isGeneratorVal(cx, BooleanValue(isGenerator));
     RootedValue isExpressionVal(cx, BooleanValue(isExpression));
 
     RootedValue cb(cx, callbacks[type]);
     if (!cb.isNull()) {
         return callback(cb, opt(id), array, body, isGeneratorVal, isExpressionVal, pos, dst);
     }
 
+    if (isGenerator) {
+        // Distinguish ES6 generators from legacy generators.
+        RootedValue styleVal(cx);
+        JSAtom* styleStr = generatorStyle == GeneratorStyle::ES6
+                           ? Atomize(cx, "es6", 3)
+                           : Atomize(cx, "legacy", 6);
+        if (!styleStr)
+            return false;
+        styleVal.setString(styleStr);
+        return newNode(type, pos,
+                       "id", id,
+                       "params", array,
+                       "defaults", defarray,
+                       "body", body,
+                       "rest", rest,
+                       "generator", isGeneratorVal,
+                       "style", styleVal,
+                       "expression", isExpressionVal,
+                       dst);
+    }
+
     return newNode(type, pos,
                    "id", id,
                    "params", array,
                    "defaults", defarray,
                    "body", body,
                    "rest", rest,
                    "generator", isGeneratorVal,
                    "expression", isExpressionVal,
@@ -2382,18 +2249,18 @@ ASTSerializer::switchCase(ParseNode* pn,
 {
     MOZ_ASSERT_IF(pn->pn_left, pn->pn_pos.encloses(pn->pn_left->pn_pos));
     MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos));
 
     NodeVector stmts(cx);
 
     RootedValue expr(cx);
 
-    return optExpression(pn->pn_left, &expr) &&
-           statements(pn->pn_right, stmts) &&
+    return optExpression(pn->as<CaseClause>().caseExpression(), &expr) &&
+           statements(pn->as<CaseClause>().statementList(), stmts) &&
            builder.switchCase(expr, stmts, &pn->pn_pos, dst);
 }
 
 bool
 ASTSerializer::switchStatement(ParseNode* pn, MutableHandleValue dst)
 {
     MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos));
     MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos));
@@ -3390,17 +3257,19 @@ ASTSerializer::property(ParseNode* pn, M
         kind = PROP_SETTER;
         break;
 
       default:
         LOCAL_NOT_REACHED("unexpected object-literal property");
     }
 
     bool isShorthand = pn->isKind(PNK_SHORTHAND);
-    bool isMethod = pn->pn_right->isKind(PNK_FUNCTION) && kind == PROP_INIT;
+    bool isMethod =
+        pn->pn_right->isKind(PNK_FUNCTION) &&
+        pn->pn_right->pn_funbox->function()->kind() == JSFunction::Method;
     RootedValue key(cx), val(cx);
     return propertyName(pn->pn_left, &key) &&
            expression(pn->pn_right, &val) &&
            builder.propertyInitializer(key, val, kind, isShorthand, isMethod, &pn->pn_pos, dst);
 }
 
 bool
 ASTSerializer::literal(ParseNode* pn, MutableHandleValue dst)
@@ -3562,18 +3431,22 @@ ASTSerializer::objectPropertyName(ParseN
     return identifier(pnAtom, &pn->pn_pos, dst);
 }
 
 bool
 ASTSerializer::function(ParseNode* pn, ASTType type, MutableHandleValue dst)
 {
     RootedFunction func(cx, pn->pn_funbox->function());
 
-    // FIXME: Provide more information (legacy generator vs star generator).
-    bool isGenerator = pn->pn_funbox->isGenerator();
+    GeneratorStyle generatorStyle =
+        pn->pn_funbox->isGenerator()
+        ? (pn->pn_funbox->isLegacyGenerator()
+           ? GeneratorStyle::Legacy
+           : GeneratorStyle::ES6)
+        : GeneratorStyle::None;
 
     bool isExpression =
 #if JS_HAS_EXPR_CLOSURES
         func->isExprBody();
 #else
         false;
 #endif
 
@@ -3587,17 +3460,17 @@ ASTSerializer::function(ParseNode* pn, A
 
     RootedValue body(cx), rest(cx);
     if (func->hasRest())
         rest.setUndefined();
     else
         rest.setNull();
     return functionArgsAndBody(pn->pn_body, args, defaults, &body, &rest) &&
         builder.function(type, &pn->pn_pos, id, args, defaults, body,
-                         rest, isGenerator, isExpression, dst);
+                         rest, generatorStyle, isExpression, dst);
 }
 
 bool
 ASTSerializer::functionArgsAndBody(ParseNode* pn, NodeVector& args, NodeVector& defaults,
                                    MutableHandleValue body, MutableHandleValue rest)
 {
     ParseNode* pnargs;
     ParseNode* pnbody;
--- a/js/src/devtools/automation/autospider.sh
+++ b/js/src/devtools/automation/autospider.sh
@@ -78,16 +78,17 @@ echo "OBJDIR is $OBJDIR"
 
 USE_64BIT=false
 
 if [[ "$OSTYPE" == darwin* ]]; then
   USE_64BIT=true
   if [ "$VARIANT" = "arm-sim-osx" ]; then
     USE_64BIT=false
   fi
+  source "$ABSDIR/macbuildenv.sh"
 elif [ "$OSTYPE" = "linux-gnu" ]; then
   if [ -n "$AUTOMATION" ]; then
       GCCDIR="${GCCDIR:-/tools/gcc-4.7.2-0moz1}"
       CONFIGURE_ARGS="$CONFIGURE_ARGS --with-ccache"
   fi
   UNAME_M=$(uname -m)
   MAKEFLAGS=-j4
   if [ "$VARIANT" = "arm-sim" ]; then
@@ -142,17 +143,20 @@ MAKE=${MAKE:-make}
 
 if $USE_64BIT; then
   NSPR64="--enable-64bit"
   if [ "$OSTYPE" = "msys" ]; then
     CONFIGURE_ARGS="$CONFIGURE_ARGS --target=x86_64-pc-mingw32 --host=x86_64-pc-mingw32"
   fi
 else
   NSPR64=""
-  if [ "$OSTYPE" != "msys" ]; then
+  if [ "$OSTYPE" == darwin* ]; then
+    export CC="${CC:-/usr/bin/clang} -arch i386"
+    export CXX="${CXX:-/usr/bin/clang++} -arch i386"
+  elif [ "$OSTYPE" != "msys" ]; then
     export CC="${CC:-/usr/bin/gcc} -m32"
     export CXX="${CXX:-/usr/bin/g++} -m32"
     export AR=ar
   fi
   if [ "$OSTYPE" = "linux-gnu" ]; then
     if [ "$UNAME_M" != "arm" ] && [ -n "$AUTOMATION" ]; then
       CONFIGURE_ARGS="$CONFIGURE_ARGS --target=i686-pc-linux --host=i686-pc-linux"
     fi
@@ -204,16 +208,17 @@ elif [[ "$VARIANT" = "compacting" ]]; th
     win*)
         RUN_JSTESTS=false
     esac
 elif [[ "$VARIANT" = "warnaserr" ||
         "$VARIANT" = "warnaserrdebug" ||
         "$VARIANT" = "plain" ]]; then
     export JSTESTS_EXTRA_ARGS=--jitflags=all
 elif [[ "$VARIANT" = "arm-sim" ||
+        "$VARIANT" = "arm-sim-osx" ||
         "$VARIANT" = "plaindebug" ]]; then
     export JSTESTS_EXTRA_ARGS=--jitflags=debug
 elif [[ "$VARIANT" = arm64* ]]; then
     # The ARM64 JIT is not yet fully functional, and asm.js does not work.
     # Just run "make check" and jsapi-tests.
     RUN_JITTEST=false
     RUN_JSTESTS=false
 fi
new file mode 100644
--- /dev/null
+++ b/js/src/devtools/automation/macbuildenv.sh
@@ -0,0 +1,27 @@
+# We will be sourcing mozconfig files, which end up calling mk_add_options and
+# ac_add_options with various settings. We only need the variable settings they
+# create along the way.
+mk_add_options() {
+  : do nothing
+}
+ac_add_options() {
+  : do nothing
+}
+
+topsrcdir="$SOURCE"
+
+if [ -n "$AUTOMATION" ]; then
+    # Download clang and some other things from tooltool server.
+    # This should be done before running mozconfig to make clang detection
+    # there work properly.
+    TT_SERVER=${TT_SERVER:-https://api.pub.build.mozilla.org/tooltool/}
+    ( cd $SOURCE/..; \
+      ./scripts/scripts/tooltool/tooltool_wrapper.sh \
+          $SOURCE/browser/config/tooltool-manifests/macosx64/releng.manifest \
+          $TT_SERVER \
+          setup.sh \
+          $TOOLTOOL_HOME/tooltool.py )
+fi
+
+# Setup CC and CXX variables
+. $topsrcdir/build/macosx/mozconfig.common
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -2119,17 +2119,16 @@ BytecodeEmitter::checkSideEffects(ParseN
       case PNK_DIV:
       case PNK_MOD:
       case PNK_POW:
         MOZ_ASSERT(pn->isArity(PN_LIST));
         MOZ_ASSERT(pn->pn_count >= 2);
         *answer = true;
         return true;
 
-      case PNK_DEFAULT:
       case PNK_COLON:
       case PNK_CASE:
         MOZ_ASSERT(pn->isArity(PN_BINARY));
         if (!checkSideEffects(pn->pn_left, answer))
             return false;
         if (*answer)
             return true;
         return checkSideEffects(pn->pn_right, answer);
@@ -2571,20 +2570,18 @@ BytecodeEmitter::emitPropLHS(ParseNode* 
      * If the object operand is also a dotted property reference, reverse the
      * list linked via pn_expr temporarily so we can iterate over it from the
      * bottom up (reversing again as we go), to avoid excessive recursion.
      */
     if (pn2->isKind(PNK_DOT) && !pn2->as<PropertyAccess>().isSuper()) {
         ParseNode* pndot = pn2;
         ParseNode* pnup = nullptr;
         ParseNode* pndown;
-        ptrdiff_t top = offset();
         for (;;) {
             /* Reverse pndot->pn_expr to point up, not down. */
-            pndot->pn_offset = top;
             MOZ_ASSERT(!pndot->isUsed());
             pndown = pndot->pn_expr;
             pndot->pn_expr = pnup;
             if (!pndown->isKind(PNK_DOT) || pndown->as<PropertyAccess>().isSuper())
                 break;
             pnup = pndot;
             pndot = pndown;
         }
@@ -3040,75 +3037,76 @@ BytecodeEmitter::enterBlockScope(StmtInf
  * into emitTree which is recursive and uses relatively little stack space.
  */
 MOZ_NEVER_INLINE bool
 BytecodeEmitter::emitSwitch(ParseNode* pn)
 {
     ParseNode* cases = pn->pn_right;
     MOZ_ASSERT(cases->isKind(PNK_LEXICALSCOPE) || cases->isKind(PNK_STATEMENTLIST));
 
-    /* Push the discriminant. */
+    // Emit code for the discriminant.
     if (!emitTree(pn->pn_left))
         return false;
 
     StmtInfoBCE stmtInfo(cx);
     ptrdiff_t top;
     if (cases->isKind(PNK_LEXICALSCOPE)) {
         if (!enterBlockScope(&stmtInfo, cases->pn_objbox, JSOP_UNINITIALIZED, 0))
             return false;
 
         stmtInfo.type = StmtType::SWITCH;
         stmtInfo.update = top = offset();
-        /* Advance |cases| to refer to the switch case list. */
+
+        // Advance |cases| to refer to the switch case list.
         cases = cases->expr();
     } else {
         MOZ_ASSERT(cases->isKind(PNK_STATEMENTLIST));
         top = offset();
         pushStatement(&stmtInfo, StmtType::SWITCH, top);
     }
 
-    /* Switch bytecodes run from here till end of final case. */
+    // Switch bytecodes run from here till end of final case.
     uint32_t caseCount = cases->pn_count;
     if (caseCount > JS_BIT(16)) {
         parser->tokenStream.reportError(JSMSG_TOO_MANY_CASES);
         return false;
     }
 
-    /* Try for most optimal, fall back if not dense ints. */
+    // Try for most optimal, fall back if not dense ints.
     JSOp switchOp = JSOP_TABLESWITCH;
     uint32_t tableLength = 0;
     int32_t low, high;
     bool hasDefault = false;
+    CaseClause* firstCase = cases->pn_head ? &cases->pn_head->as<CaseClause>() : nullptr;
     if (caseCount == 0 ||
-        (caseCount == 1 &&
-         (hasDefault = cases->pn_head->isKind(PNK_DEFAULT)))) {
+        (caseCount == 1 && (hasDefault = firstCase->isDefault())))
+    {
         caseCount = 0;
         low = 0;
         high = -1;
     } else {
         Vector<jsbitmap, 128, SystemAllocPolicy> intmap;
         int32_t intmapBitLength = 0;
 
         low  = JSVAL_INT_MAX;
         high = JSVAL_INT_MIN;
 
-        for (ParseNode* caseNode = cases->pn_head; caseNode; caseNode = caseNode->pn_next) {
-            if (caseNode->isKind(PNK_DEFAULT)) {
+        for (CaseClause* caseNode = firstCase; caseNode; caseNode = caseNode->next()) {
+            if (caseNode->isDefault()) {
                 hasDefault = true;
-                caseCount--;    /* one of the "cases" was the default */
+                caseCount--;  // one of the "cases" was the default
                 continue;
             }
 
-            MOZ_ASSERT(caseNode->isKind(PNK_CASE));
             if (switchOp == JSOP_CONDSWITCH)
                 continue;
 
             MOZ_ASSERT(switchOp == JSOP_TABLESWITCH);
 
-            ParseNode* caseValue = caseNode->pn_left;
+            ParseNode* caseValue = caseNode->caseExpression();
 
             if (caseValue->getKind() != PNK_NUMBER) {
                 switchOp = JSOP_CONDSWITCH;
                 continue;
             }
 
             int32_t i;
             if (!NumberIsInt32(caseValue->pn_dval, &i)) {
@@ -3120,209 +3118,208 @@ BytecodeEmitter::emitSwitch(ParseNode* p
                 switchOp = JSOP_CONDSWITCH;
                 continue;
             }
             if (i < low)
                 low = i;
             if (i > high)
                 high = i;
 
-            /*
-             * Check for duplicates, which require a JSOP_CONDSWITCH.
-             * We bias i by 65536 if it's negative, and hope that's a rare
-             * case (because it requires a malloc'd bitmap).
-             */
+            // Check for duplicates, which require a JSOP_CONDSWITCH.
+            // We bias i by 65536 if it's negative, and hope that's a rare
+            // case (because it requires a malloc'd bitmap).
             if (i < 0)
                 i += JS_BIT(16);
             if (i >= intmapBitLength) {
                 size_t newLength = (i / JS_BITMAP_NBITS) + 1;
                 if (!intmap.resize(newLength))
                     return false;
                 intmapBitLength = newLength * JS_BITMAP_NBITS;
             }
             if (JS_TEST_BIT(intmap, i)) {
                 switchOp = JSOP_CONDSWITCH;
                 continue;
             }
             JS_SET_BIT(intmap, i);
         }
 
-        /*
-         * Compute table length and select condswitch instead if overlarge or
-         * more than half-sparse.
-         */
+        // Compute table length and select condswitch instead if overlarge or
+        // more than half-sparse.
         if (switchOp == JSOP_TABLESWITCH) {
             tableLength = uint32_t(high - low + 1);
             if (tableLength >= JS_BIT(16) || tableLength > 2 * caseCount)
                 switchOp = JSOP_CONDSWITCH;
         }
     }
 
-    /*
-     * The note has one or two offsets: first tells total switch code length;
-     * second (if condswitch) tells offset to first JSOP_CASE.
-     */
+    // The note has one or two offsets: first tells total switch code length;
+    // second (if condswitch) tells offset to first JSOP_CASE.
     unsigned noteIndex;
     size_t switchSize;
     if (switchOp == JSOP_CONDSWITCH) {
-        /* 0 bytes of immediate for unoptimized switch. */
+        // 0 bytes of immediate for unoptimized switch.
         switchSize = 0;
         if (!newSrcNote3(SRC_CONDSWITCH, 0, 0, &noteIndex))
             return false;
     } else {
         MOZ_ASSERT(switchOp == JSOP_TABLESWITCH);
 
-        /* 3 offsets (len, low, high) before the table, 1 per entry. */
-        switchSize = (size_t)(JUMP_OFFSET_LEN * (3 + tableLength));
+        // 3 offsets (len, low, high) before the table, 1 per entry.
+        switchSize = size_t(JUMP_OFFSET_LEN * (3 + tableLength));
         if (!newSrcNote2(SRC_TABLESWITCH, 0, &noteIndex))
             return false;
     }
 
-    /* Emit switchOp followed by switchSize bytes of jump or lookup table. */
+    // Emit switchOp followed by switchSize bytes of jump or lookup table.
     if (!emitN(switchOp, switchSize))
         return false;
 
-    Vector<ParseNode*, 32, SystemAllocPolicy> table;
+    Vector<CaseClause*, 32, SystemAllocPolicy> table;
 
     ptrdiff_t condSwitchDefaultOff = -1;
     if (switchOp == JSOP_CONDSWITCH) {
         unsigned caseNoteIndex;
         bool beforeCases = true;
         ptrdiff_t prevCaseOffset;
 
-        /* Emit code for evaluating cases and jumping to case statements. */
-        for (ParseNode* caseNode = cases->pn_head; caseNode; caseNode = caseNode->pn_next) {
-            ParseNode* caseValue = caseNode->pn_left;
-            // If the expression is a literal, suppress line number
-            // emission so that debugging works more naturally.
-            if (caseValue &&
-                !emitTree(caseValue, caseValue->isLiteral() ? SUPPRESS_LINENOTE :
-                          EMIT_LINENOTE))
-                return false;
+        // Emit code for evaluating cases and jumping to case statements.
+        for (CaseClause* caseNode = firstCase; caseNode; caseNode = caseNode->next()) {
+            ParseNode* caseValue = caseNode->caseExpression();
+
+            // If the expression is a literal, suppress line number emission so
+            // that debugging works more naturally.
+            if (caseValue) {
+                if (!emitTree(caseValue,
+                              caseValue->isLiteral() ? SUPPRESS_LINENOTE : EMIT_LINENOTE))
+                {
+                    return false;
+                }
+            }
+
             if (!beforeCases) {
-                /* prevCaseOffset is the previous JSOP_CASE's bytecode offset. */
+                // prevCaseOffset is the previous JSOP_CASE's bytecode offset.
                 if (!setSrcNoteOffset(caseNoteIndex, 0, offset() - prevCaseOffset))
                     return false;
             }
             if (!caseValue) {
-                MOZ_ASSERT(caseNode->isKind(PNK_DEFAULT));
+                // This is the default clause.
                 continue;
             }
+
             if (!newSrcNote2(SRC_NEXTCASE, 0, &caseNoteIndex))
                 return false;
             if (!emitJump(JSOP_CASE, 0, &prevCaseOffset))
                 return false;
-            caseNode->pn_offset = prevCaseOffset;
+            caseNode->setOffset(prevCaseOffset);
+
             if (beforeCases) {
-                /* Switch note's second offset is to first JSOP_CASE. */
+                // Switch note's second offset is to first JSOP_CASE.
                 unsigned noteCount = notes().length();
                 if (!setSrcNoteOffset(noteIndex, 1, prevCaseOffset - top))
                     return false;
                 unsigned noteCountDelta = notes().length() - noteCount;
                 if (noteCountDelta != 0)
                     caseNoteIndex += noteCountDelta;
                 beforeCases = false;
             }
         }
 
-        /*
-         * If we didn't have an explicit default (which could fall in between
-         * cases, preventing us from fusing this setSrcNoteOffset with the call
-         * in the loop above), link the last case to the implicit default for
-         * the benefit of IonBuilder.
-         */
+        // If we didn't have an explicit default (which could fall in between
+        // cases, preventing us from fusing this setSrcNoteOffset with the call
+        // in the loop above), link the last case to the implicit default for
+        // the benefit of IonBuilder.
         if (!hasDefault &&
             !beforeCases &&
             !setSrcNoteOffset(caseNoteIndex, 0, offset() - prevCaseOffset))
         {
             return false;
         }
 
-        /* Emit default even if no explicit default statement. */
+        // Emit default even if no explicit default statement.
         if (!emitJump(JSOP_DEFAULT, 0, &condSwitchDefaultOff))
             return false;
     } else {
         MOZ_ASSERT(switchOp == JSOP_TABLESWITCH);
         jsbytecode* pc = code(top + JUMP_OFFSET_LEN);
 
-        /* Fill in switch bounds, which we know fit in 16-bit offsets. */
+        // Fill in switch bounds, which we know fit in 16-bit offsets.
         SET_JUMP_OFFSET(pc, low);
         pc += JUMP_OFFSET_LEN;
         SET_JUMP_OFFSET(pc, high);
         pc += JUMP_OFFSET_LEN;
 
         if (tableLength != 0) {
             if (!table.growBy(tableLength))
                 return false;
 
-            for (ParseNode* caseNode = cases->pn_head; caseNode; caseNode = caseNode->pn_next) {
-                if (caseNode->isKind(PNK_DEFAULT))
-                    continue;
-
-                MOZ_ASSERT(caseNode->isKind(PNK_CASE));
-
-                ParseNode* caseValue = caseNode->pn_left;
-                MOZ_ASSERT(caseValue->isKind(PNK_NUMBER));
-
-                int32_t i = int32_t(caseValue->pn_dval);
-                MOZ_ASSERT(double(i) == caseValue->pn_dval);
-
-                i -= low;
-                MOZ_ASSERT(uint32_t(i) < tableLength);
-                MOZ_ASSERT(!table[i]);
-                table[i] = caseNode;
+            for (CaseClause* caseNode = firstCase; caseNode; caseNode = caseNode->next()) {
+                if (ParseNode* caseValue = caseNode->caseExpression()) {
+                    MOZ_ASSERT(caseValue->isKind(PNK_NUMBER));
+
+                    int32_t i = int32_t(caseValue->pn_dval);
+                    MOZ_ASSERT(double(i) == caseValue->pn_dval);
+
+                    i -= low;
+                    MOZ_ASSERT(uint32_t(i) < tableLength);
+                    MOZ_ASSERT(!table[i]);
+                    table[i] = caseNode;
+                }
             }
         }
     }
 
     ptrdiff_t defaultOffset = -1;
 
-    /* Emit code for each case's statements, copying pn_offset up to caseNode. */
-    for (ParseNode* caseNode = cases->pn_head; caseNode; caseNode = caseNode->pn_next) {
-        if (switchOp == JSOP_CONDSWITCH && !caseNode->isKind(PNK_DEFAULT))
-            setJumpOffsetAt(caseNode->pn_offset);
-        ParseNode* caseValue = caseNode->pn_right;
-        if (!emitTree(caseValue))
-            return false;
-        caseNode->pn_offset = caseValue->pn_offset;
-        if (caseNode->isKind(PNK_DEFAULT))
-            defaultOffset = caseNode->pn_offset - top;
+    // Emit code for each case's statements.
+    for (CaseClause* caseNode = firstCase; caseNode; caseNode = caseNode->next()) {
+        if (switchOp == JSOP_CONDSWITCH && !caseNode->isDefault())
+            setJumpOffsetAt(caseNode->offset());
+
+        // If this is emitted as a TABLESWITCH, we'll need to know this case's
+        // offset later when emitting the table. Store it in the node's
+        // pn_offset (giving the field a different meaning vs. how we used it
+        // on the immediately preceding line of code).
+        ptrdiff_t here = offset();
+        caseNode->setOffset(here);
+        if (caseNode->isDefault())
+            defaultOffset = here - top;
+
+        if (!emitTree(caseNode->statementList()))
+            return false;
     }
 
     if (!hasDefault) {
-        /* If no default case, offset for default is to end of switch. */
+        // If no default case, offset for default is to end of switch.
         defaultOffset = offset() - top;
     }
-
-    /* We better have set "defaultOffset" by now. */
     MOZ_ASSERT(defaultOffset != -1);
 
-    /* Set the default offset (to end of switch if no default). */
+    // Set the default offset (to end of switch if no default).
     jsbytecode* pc;
     if (switchOp == JSOP_CONDSWITCH) {
         pc = nullptr;
         SET_JUMP_OFFSET(code(condSwitchDefaultOff), defaultOffset - (condSwitchDefaultOff - top));
     } else {
         pc = code(top);
         SET_JUMP_OFFSET(pc, defaultOffset);
         pc += JUMP_OFFSET_LEN;
     }
 
-    /* Set the SRC_SWITCH note's offset operand to tell end of switch. */
+    // Set the SRC_SWITCH note's offset operand to tell end of switch.
     if (!setSrcNoteOffset(noteIndex, 0, offset() - top))
         return false;
 
     if (switchOp == JSOP_TABLESWITCH) {
-        /* Skip over the already-initialized switch bounds. */
+        // Skip over the already-initialized switch bounds.
         pc += 2 * JUMP_OFFSET_LEN;
 
-        /* Fill in the jump table, if there is one. */
+        // Fill in the jump table, if there is one.
         for (uint32_t i = 0; i < tableLength; i++) {
-            ParseNode* caseNode = table[i];
-            ptrdiff_t off = caseNode ? caseNode->pn_offset - top : 0;
+            CaseClause* caseNode = table[i];
+            ptrdiff_t off = caseNode ? caseNode->offset() - top : 0;
             SET_JUMP_OFFSET(pc, off);
             pc += JUMP_OFFSET_LEN;
         }
     }
 
     if (pn->pn_right->isKind(PNK_LEXICALSCOPE)) {
         if (!leaveNestedScope(&stmtInfo))
             return false;
@@ -5326,22 +5323,23 @@ BytecodeEmitter::emitForInOrOfVariables(
         }
         emittingForInit = false;
     }
 
     return true;
 }
 
 bool
-BytecodeEmitter::emitForOf(StmtType type, ParseNode* pn, ptrdiff_t top)
+BytecodeEmitter::emitForOf(StmtType type, ParseNode* pn)
 {
     MOZ_ASSERT(type == StmtType::FOR_OF_LOOP || type == StmtType::SPREAD);
     MOZ_ASSERT_IF(type == StmtType::FOR_OF_LOOP, pn && pn->pn_left->isKind(PNK_FOROF));
     MOZ_ASSERT_IF(type == StmtType::SPREAD, !pn);
 
+    ptrdiff_t top = offset();
     ParseNode* forHead = pn ? pn->pn_left : nullptr;
     ParseNode* forHeadExpr = forHead ? forHead->pn_kid3 : nullptr;
     ParseNode* forBody = pn ? pn->pn_right : nullptr;
 
     ParseNode* pn1 = forHead ? forHead->pn_kid1 : nullptr;
     bool letDecl = false;
     if (pn1 && !emitForInOrOfVariables(pn1, &letDecl))
         return false;
@@ -5477,18 +5475,19 @@ BytecodeEmitter::emitForOf(StmtType type
             return false;
     }
 
     // Pop the result and the iter.
     return emitUint16Operand(JSOP_POPN, 2);
 }
 
 bool
-BytecodeEmitter::emitForIn(ParseNode* pn, ptrdiff_t top)
-{
+BytecodeEmitter::emitForIn(ParseNode* pn)
+{
+    ptrdiff_t top = offset();
     ParseNode* forHead = pn->pn_left;
     ParseNode* forBody = pn->pn_right;
 
     ParseNode* pn1 = forHead->pn_kid1;
     bool letDecl = false;
     if (pn1 && !emitForInOrOfVariables(pn1, &letDecl))
         return false;
 
@@ -5599,20 +5598,20 @@ BytecodeEmitter::emitForIn(ParseNode* pn
             return false;
     }
 
     return true;
 }
 
 /* C-style `for (init; cond; update) ...` loop. */
 bool
-BytecodeEmitter::emitCStyleFor(ParseNode* pn, ptrdiff_t top)
+BytecodeEmitter::emitCStyleFor(ParseNode* pn)
 {
     LoopStmtInfo stmtInfo(cx);
-    pushLoopStatement(&stmtInfo, StmtType::FOR_LOOP, top);
+    pushLoopStatement(&stmtInfo, StmtType::FOR_LOOP, offset());
 
     ParseNode* forHead = pn->pn_left;
     ParseNode* forBody = pn->pn_right;
 
     // If the head of this for-loop declared any lexical variables, the parser
     // wrapped this PNK_FOR node in a PNK_LEXICALSCOPE representing the
     // implicit scope of those variables. By the time we get here, we have
     // already entered that scope. So far, so good.
@@ -5672,17 +5671,17 @@ BytecodeEmitter::emitCStyleFor(ParseNode
 
     ptrdiff_t jmp = -1;
     if (forHead->pn_kid2) {
         /* Goto the loop condition, which branches back to iterate. */
         if (!emitJump(JSOP_GOTO, 0, &jmp))
             return false;
     }
 
-    top = offset();
+    ptrdiff_t top = offset();
     stmtInfo.setTop(top);
 
     /* Emit code for the loop body. */
     if (!emitLoopHead(forBody))
         return false;
     if (jmp == -1 && !emitLoopEntry(forBody))
         return false;
     if (!emitTree(forBody))
@@ -5774,29 +5773,29 @@ BytecodeEmitter::emitCStyleFor(ParseNode
         return false;
 
     /* Now fixup all breaks and continues. */
     popStatement();
     return true;
 }
 
 bool
-BytecodeEmitter::emitFor(ParseNode* pn, ptrdiff_t top)
+BytecodeEmitter::emitFor(ParseNode* pn)
 {
     if (pn->pn_left->isKind(PNK_FORHEAD))
-        return emitCStyleFor(pn, top);
+        return emitCStyleFor(pn);
 
     if (!updateLineNumberNotes(pn->pn_pos.begin))
         return false;
 
     if (pn->pn_left->isKind(PNK_FORIN))
-        return emitForIn(pn, top);
+        return emitForIn(pn);
 
     MOZ_ASSERT(pn->pn_left->isKind(PNK_FOROF));
-    return emitForOf(StmtType::FOR_OF_LOOP, pn, top);
+    return emitForOf(StmtType::FOR_OF_LOOP, pn);
 }
 
 MOZ_NEVER_INLINE bool
 BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto)
 {
     FunctionBox* funbox = pn->pn_funbox;
     RootedFunction fun(cx, funbox->function());
     MOZ_ASSERT_IF(fun->isInterpretedLazy(), fun->lazyScript());
@@ -6015,17 +6014,17 @@ BytecodeEmitter::emitDo(ParseNode* pn)
     if (!setSrcNoteOffset(noteIndex, 0, 1 + (off - top)))
         return false;
 
     popStatement();
     return true;
 }
 
 bool
-BytecodeEmitter::emitWhile(ParseNode* pn, ptrdiff_t top)
+BytecodeEmitter::emitWhile(ParseNode* pn)
 {
     /*
      * Minimize bytecodes issued for one or more iterations by jumping to
      * the condition below the body and closing the loop if the condition
      * is true with a backward branch. For iteration count i:
      *
      *  i    test at the top                 test at the bottom
      *  =    ===============                 ==================
@@ -6045,27 +6044,27 @@ BytecodeEmitter::emitWhile(ParseNode* pn
     // "cont" stops on each iteration -- but without a stop before the
     // first iteration.
     if (parser->tokenStream.srcCoords.lineNum(pn->pn_pos.begin) ==
         parser->tokenStream.srcCoords.lineNum(pn->pn_pos.end) &&
         !updateSourceCoordNotes(pn->pn_pos.begin))
         return false;
 
     LoopStmtInfo stmtInfo(cx);
-    pushLoopStatement(&stmtInfo, StmtType::WHILE_LOOP, top);
+    pushLoopStatement(&stmtInfo, StmtType::WHILE_LOOP, offset());
 
     unsigned noteIndex;
     if (!newSrcNote(SRC_WHILE, &noteIndex))
         return false;
 
     ptrdiff_t jmp;
     if (!emitJump(JSOP_GOTO, 0, &jmp))
         return false;
 
-    top = offset();
+    ptrdiff_t top = offset();
     if (!emitLoopHead(pn->pn_right))
         return false;
 
     if (!emitTree(pn->pn_right))
         return false;
 
     setJumpOffsetAt(jmp);
     if (!emitLoopEntry(pn->pn_left))
@@ -6378,17 +6377,17 @@ BytecodeEmitter::emitYieldStar(ParseNode
         return false;
 
     MOZ_ASSERT(this->stackDepth == depth - 1);
 
     return true;
 }
 
 bool
-BytecodeEmitter::emitStatementList(ParseNode* pn, ptrdiff_t top)
+BytecodeEmitter::emitStatementList(ParseNode* pn)
 {
     MOZ_ASSERT(pn->isArity(PN_LIST));
     for (ParseNode* pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
         if (!emitTree(pn2))
             return false;
     }
     return true;
 }
@@ -6838,16 +6837,54 @@ BytecodeEmitter::emitCallOrNew(ParseNode
     }
 
     if (pn->isKind(PNK_SUPERCALL) && !emit1(JSOP_SETTHIS))
         return false;
     return true;
 }
 
 bool
+BytecodeEmitter::emitRightAssociative(ParseNode* pn)
+{