merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 07 Jan 2016 11:50:57 +0100
changeset 314053 e0bcd16e1d4b99ba3e542149d0d41e0f60c54b5c
parent 313929 b384917df4e94fd7432c194db70d142e922779cc (current diff)
parent 314052 2f6e12a6f04f8e676c357bd50ec88e81f2037306 (diff)
child 314060 07dfb8b07a61ad480d10f601ed4ae4aa5ba0594a
child 314076 9c82d5b244d7c6a453a0cb2909cd9444de472bed
child 314109 9768993a4e2b82caa216f74126325642a75ca132
push id5703
push userraliiev@mozilla.com
push dateMon, 07 Mar 2016 14:18:41 +0000
treeherdermozilla-beta@31e373ad5b5f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone46.0a1
first release with
nightly linux32
e0bcd16e1d4b / 46.0a1 / 20160107030235 / files
nightly mac
e0bcd16e1d4b / 46.0a1 / 20160107030235 / files
nightly win32
e0bcd16e1d4b / 46.0a1 / 20160107030235 / files
nightly win64
e0bcd16e1d4b / 46.0a1 / 20160107030235 / files
nightly linux64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
dom/animation/test/css-animations/file_timeline-get-animations.html
dom/animation/test/css-animations/test_timeline-get-animations.html
dom/animation/test/css-transitions/file_timeline-get-animations.html
dom/animation/test/css-transitions/test_timeline-get-animations.html
js/src/jit-test/tests/arguments/defaults-bug790424.js
js/src/jit-test/tests/arguments/genexpr-1.js
js/src/jit-test/tests/arguments/genexpr-2.js
js/src/jit-test/tests/arguments/genexpr-3.js
js/src/jit-test/tests/arguments/genexpr-4.js
js/src/jit-test/tests/arguments/genexpr-5.js
js/src/jit-test/tests/arguments/genexpr-6.js
js/src/jit-test/tests/auto-regress/bug487570.js
js/src/jit-test/tests/auto-regress/bug488963.js
js/src/jit-test/tests/auto-regress/bug496270.js
js/src/jit-test/tests/auto-regress/bug589093.js
js/src/jit-test/tests/auto-regress/bug595911.js
js/src/jit-test/tests/auto-regress/bug622318.js
js/src/jit-test/tests/auto-regress/bug691593.js
js/src/jit-test/tests/auto-regress/bug776314.js
js/src/jit-test/tests/auto-regress/bug780405.js
js/src/jit-test/tests/basic/bug510655.js
js/src/jit-test/tests/basic/bug630377.js
js/src/jit-test/tests/basic/bug660538.js
js/src/jit-test/tests/basic/bug790629-1.js
js/src/jit-test/tests/basic/bug790629-2.js
js/src/jit-test/tests/basic/bug790629-3.js
js/src/jit-test/tests/basic/bug790629-4.js
js/src/jit-test/tests/basic/bug864099.js
js/src/jit-test/tests/basic/function-tosource-genexpr.js
js/src/jit-test/tests/basic/testArrayComp1.js
js/src/jit-test/tests/basic/testArrayComp2.js
js/src/jit-test/tests/basic/testBug709929.js
js/src/jit-test/tests/basic/testBug773927.js
js/src/jit-test/tests/basic/testGCNewbornGenerator.js
js/src/jit-test/tests/basic/testImplicitArgumentsInGenExprs.js
js/src/jit-test/tests/closures/flat-closure-3.js
js/src/jit-test/tests/closures/incr-exit-3.js
js/src/jit-test/tests/collections/Map-constructor-generator-2.js
js/src/jit-test/tests/collections/Set-constructor-generator-2.js
js/src/jit-test/tests/collections/WeakMap-constructor-generator-2.js
js/src/jit-test/tests/for-of/array-comprehension.js
js/src/jit-test/tests/for-of/array-holes-7.js
js/src/jit-test/tests/for-of/bug-728079-js17-2.js
js/src/jit-test/tests/for-of/bug-728079-js17-3.js
js/src/jit-test/tests/for-of/generators-4.js
js/src/jit-test/tests/generators/star-generator-forin.js
js/src/jit-test/tests/ion/bug956166.js
js/src/jit-test/tests/jaeger/regalloc-1.js
js/src/tests/js1_5/Regress/regress-352009.js
js/src/tests/js1_6/Regress/regress-350417.js
js/src/tests/js1_6/extensions/regress-475144.js
js/src/tests/js1_7/extensions/regress-455982-01.js
js/src/tests/js1_7/extensions/regress-455982-02.js
js/src/tests/js1_7/geniter/evens.js
js/src/tests/js1_7/geniter/regress-345736.js
js/src/tests/js1_7/iterable/regress-412467.js
js/src/tests/js1_7/regress/regress-428706.js
js/src/tests/js1_7/regress/regress-461235.js
js/src/tests/js1_7/regress/regress-461945.js
js/src/tests/js1_8/extensions/regress-385393-01.js
js/src/tests/js1_8/extensions/regress-385393-10.js
js/src/tests/js1_8/extensions/regress-385393-11.js
js/src/tests/js1_8/extensions/regress-452476.js
js/src/tests/js1_8/extensions/regress-455973.js
js/src/tests/js1_8/genexps/arguments-property-access-in-generator.js
js/src/tests/js1_8/genexps/browser.js
js/src/tests/js1_8/genexps/regress-349331.js
js/src/tests/js1_8/genexps/regress-380237-01.js
js/src/tests/js1_8/genexps/regress-380237-02.js
js/src/tests/js1_8/genexps/regress-380237-04.js
js/src/tests/js1_8/genexps/regress-634472.js
js/src/tests/js1_8/genexps/regress-666852.js
js/src/tests/js1_8/genexps/regress-667131.js
js/src/tests/js1_8/regress/regress-463783.js
js/src/tests/js1_8_1/jit/testDeepBailFromNonNative.js
js/src/tests/js1_8_1/regress/regress-452498-038.js
js/src/tests/js1_8_1/regress/regress-452498-052.js
js/src/tests/js1_8_1/regress/regress-452498-068.js
js/src/tests/js1_8_1/regress/regress-452498-098.js
js/src/tests/js1_8_1/regress/regress-452498-099-a.js
js/src/tests/js1_8_1/regress/regress-452498-099.js
js/src/tests/js1_8_1/regress/regress-452498-119.js
js/src/tests/js1_8_1/regress/regress-452498-130.js
js/src/tests/js1_8_1/regress/regress-452498-135-a.js
js/src/tests/js1_8_1/regress/regress-452498-138.js
js/src/tests/js1_8_1/regress/regress-452498-139.js
js/src/tests/js1_8_1/regress/regress-507424.js
js/src/tests/js1_8_1/regress/regress-515885.js
js/src/tests/js1_8_1/strict/generator-eval-arguments.js
js/src/tests/js1_8_5/extensions/regress-627859.js
js/src/tests/js1_8_5/regress/no-array-comprehension-length-limit.js
js/src/tests/js1_8_5/regress/regress-541255-0.js
js/src/tests/js1_8_5/regress/regress-541255-1.js
js/src/tests/js1_8_5/regress/regress-541255-2.js
js/src/tests/js1_8_5/regress/regress-541255-4.js
js/src/tests/js1_8_5/regress/regress-576847.js
js/src/tests/js1_8_5/regress/regress-620750.js
mobile/android/app/mobile.js
mobile/android/base/java/org/mozilla/gecko/GeckoAppShell.java
testing/mochitest/browser.eslintrc
testing/mozbase/mozdebug/mozdebug/mozdebug.py
widget/android/GeneratedJNIWrappers.cpp
widget/android/GeneratedJNIWrappers.h
--- a/.eslintignore
+++ b/.eslintignore
@@ -78,16 +78,17 @@ browser/components/pocket/**
 browser/components/preferences/**
 browser/components/privatebrowsing/**
 browser/components/sessionstore/**
 browser/components/shell/**
 browser/components/tabview/**
 browser/components/translation/**
 browser/components/uitour/**
 browser/extensions/pdfjs/**
+browser/extensions/pocket/content/panels/js/vendor/**
 browser/extensions/shumway/**
 browser/fuel/**
 browser/locales/**
 
 # Loop specific exclusions
 
 # This file currently uses a non-standard (and not on a standards track)
 # if statement within catch.
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -355,21 +355,21 @@ pref("browser.safebrowsing.downloads.ena
 pref("browser.safebrowsing.downloads.remote.enabled", true);
 pref("browser.safebrowsing.downloads.remote.timeout_ms", 10000);
 pref("browser.safebrowsing.debug", false);
 
 pref("browser.safebrowsing.provider.google.lists", "goog-badbinurl-shavar,goog-downloadwhite-digest256,goog-phish-shavar,goog-malware-shavar,goog-unwanted-shavar");
 pref("browser.safebrowsing.provider.google.updateURL", "https://safebrowsing.google.com/safebrowsing/downloads?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2&key=%GOOGLE_API_KEY%");
 pref("browser.safebrowsing.provider.google.gethashURL", "https://safebrowsing.google.com/safebrowsing/gethash?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2");
 pref("browser.safebrowsing.provider.google.reportURL", "https://safebrowsing.google.com/safebrowsing/diagnostic?client=%NAME%&hl=%LOCALE%&site=");
-pref("browser.safebrowsing.provider.google.appRepURL", "https://sb-ssl.google.com/safebrowsing/clientreport/download?key=%GOOGLE_API_KEY%");
 
 pref("browser.safebrowsing.reportPhishMistakeURL", "https://%LOCALE%.phish-error.mozilla.com/?hl=%LOCALE%&url=");
 pref("browser.safebrowsing.reportPhishURL", "https://%LOCALE%.phish-report.mozilla.com/?hl=%LOCALE%&url=");
 pref("browser.safebrowsing.reportMalwareMistakeURL", "https://%LOCALE%.malware-error.mozilla.com/?hl=%LOCALE%&url=");
+pref("browser.safebrowsing.appRepURL", "https://sb-ssl.google.com/safebrowsing/clientreport/download?key=%GOOGLE_API_KEY%");
 
 pref("browser.safebrowsing.id", "Firefox");
 
 // Tables for application reputation.
 pref("urlclassifier.downloadBlockTable", "goog-badbinurl-shavar");
 
 // The number of random entries to send with a gethash request.
 pref("urlclassifier.gethashnoise", 4);
--- a/b2g/components/PersistentDataBlock.jsm
+++ b/b2g/components/PersistentDataBlock.jsm
@@ -59,19 +59,19 @@ function debug(str) {
 }
 
 function toHexString(data) {
   function toHexChar(charCode) {
     return ("0" + charCode.toString(16).slice(-2));
   }
   let hexString = "";
   if (typeof data === "string") {
-    hexString = [toHexChar(data.charCodeAt(i)) for (i in data)].join("");
+    hexString = Array.from(data, (c, i) => toHexChar(data.charCodeAt(i))).join("");
   } else if (typeof data === "array") {
-    hexString = [toHexChar(data[i]) for (i in data)].join("");
+    hexString = data.map(toHexChar).join("");
   }
   return hexString;
 }
 
 function arr2bstr(arr) {
   let bstr = "";
   for (let i = 0; i < arr.length; i++) {
     bstr += String.fromCharCode(arr[i]);
@@ -382,17 +382,17 @@ this.PersistentDataBlock = {
     let partition;
     return this._computeDigest().then(_digest => {
       digest = _digest;
       return OS.File.open(this._dataBlockFile, {write:true, existing:true, append:false});
     }).then(_partition => {
       partition = _partition;
       return partition.setPosition(DIGEST_OFFSET, OS.File.POS_START);
     }).then(() => {
-      return partition.write(new Uint8Array([digest.calculated.charCodeAt(i) for (i in digest.calculated)]));
+      return partition.write(new Uint8Array(Array.from(digest.calculated, (c, i) => digest.calculated.charCodeAt(i))));
     }).then(bytesWrittenLength => {
       if (bytesWrittenLength != DIGEST_SIZE_BYTES) {
         log("_computeAndWriteDigest: Error writting digest to partition!. Expected: " + DIGEST_SIZE_BYTES + " Written: " + bytesWrittenLength);
         return Promise.reject();
       }
       return partition.close();
     }).then(() => {
       debug("_computeAndWriteDigest: digest written to partition");
--- a/b2g/components/test/unit/file_persistentdatablock.js
+++ b/b2g/components/test/unit/file_persistentdatablock.js
@@ -27,19 +27,19 @@ function log(str) {
 }
 
 function toHexString(data) {
   function toHexChar(charCode) {
     return ("0" + charCode.toString(16).slice(-2));
   }
   let hexString = "";
   if (typeof data === "string") {
-    hexString = [toHexChar(data.charCodeAt(i)) for (i in data)].join("");
+    hexString = Array.from(data, (c, i) => toHexChar(data.charCodeAt(i))).join("");
   } else if (typeof data === "array") {
-    hexString = [toHexChar(data[i]) for (i in data)].join("");
+    hexString = data.map(toHexChar).join("");
   }
   return hexString;
 }
 
 function _prepareConfig(_args) {
   let args = _args || {};
   // This digest has been previously calculated given the data to be written later, and setting the OEM Unlocked Enabled byte
   // to 1. If we need different values, some tests will fail because this precalculated digest won't be valid then.
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -953,21 +953,22 @@ pref("browser.safebrowsing.downloads.ena
 pref("browser.safebrowsing.downloads.remote.enabled", true);
 pref("browser.safebrowsing.downloads.remote.timeout_ms", 10000);
 pref("browser.safebrowsing.debug", false);
 
 pref("browser.safebrowsing.provider.google.lists", "goog-badbinurl-shavar,goog-downloadwhite-digest256,goog-phish-shavar,goog-malware-shavar,goog-unwanted-shavar");
 pref("browser.safebrowsing.provider.google.updateURL", "https://safebrowsing.google.com/safebrowsing/downloads?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2&key=%GOOGLE_API_KEY%");
 pref("browser.safebrowsing.provider.google.gethashURL", "https://safebrowsing.google.com/safebrowsing/gethash?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2");
 pref("browser.safebrowsing.provider.google.reportURL", "https://safebrowsing.google.com/safebrowsing/diagnostic?client=%NAME%&hl=%LOCALE%&site=");
-pref("browser.safebrowsing.provider.google.appRepURL", "https://sb-ssl.google.com/safebrowsing/clientreport/download?key=%GOOGLE_API_KEY%");
 
 pref("browser.safebrowsing.reportPhishMistakeURL", "https://%LOCALE%.phish-error.mozilla.com/?hl=%LOCALE%&url=");
 pref("browser.safebrowsing.reportPhishURL", "https://%LOCALE%.phish-report.mozilla.com/?hl=%LOCALE%&url=");
 pref("browser.safebrowsing.reportMalwareMistakeURL", "https://%LOCALE%.malware-error.mozilla.com/?hl=%LOCALE%&url=");
+pref("browser.safebrowsing.appRepURL", "https://sb-ssl.google.com/safebrowsing/clientreport/download?key=%GOOGLE_API_KEY%");
+
 #ifdef MOZILLA_OFFICIAL
 // Normally the "client ID" sent in updates is appinfo.name, but for
 // official Firefox releases from Mozilla we use a special identifier.
 pref("browser.safebrowsing.id", "navclient-auto-ffox");
 #endif
 
 // Name of the about: page contributed by safebrowsing to handle display of error
 // pages on phishing/malware hits.  (bug 399233)
@@ -1571,21 +1572,19 @@ pref("browser.tabs.crashReporting.sendRe
 pref("browser.tabs.crashReporting.includeURL", false);
 pref("browser.tabs.crashReporting.emailMe", false);
 pref("browser.tabs.crashReporting.email", "");
 
 #ifndef MOZ_MULET
 pref("layers.async-pan-zoom.enabled", true);
 #endif
 
-#ifdef E10S_TESTING_ONLY
 // Enable e10s add-on interposition by default.
 pref("extensions.interposition.enabled", true);
 pref("extensions.interposition.prefetching", true);
-#endif
 
 pref("browser.defaultbrowser.notificationbar", false);
 
 // How often to check for CPOW timeouts. CPOWs are only timed out by
 // the hang monitor.
 pref("dom.ipc.cpow.timeout", 500);
 
 // Enable e10s hang monitoring (slow script checking and plugin hang
--- a/browser/base/content/newtab/grid.js
+++ b/browser/base/content/newtab/grid.js
@@ -128,17 +128,17 @@ var gGrid = {
 
     // Creates all the cells up to the maximum
     let fragment = document.createDocumentFragment();
     for (let i = 0; i < gGridPrefs.gridColumns * gGridPrefs.gridRows; i++) {
       fragment.appendChild(cell.cloneNode(true));
     }
 
     // Create cells.
-    let cells = [new Cell(this, cell) for (cell of fragment.childNodes)];
+    let cells = Array.from(fragment.childNodes, (cell) => new Cell(this, cell));
 
     // Fetch links.
     let links = gLinks.getLinks();
 
     // Create sites.
     let numLinks = Math.min(links.length, cells.length);
     for (let i = 0; i < numLinks; i++) {
       if (links[i]) {
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -331,24 +331,28 @@ nsContextMenu.prototype = {
     // there is more than MENU_LIMIT providers, we show a submenu for them,
     // otherwise we have a menuitem per provider (added in SocialMarks class).
     let markProviders = SocialMarks.getProviders();
     let enablePageMarks = markProviders.length > 0 && !(this.onLink || this.onImage
                             || this.onVideo || this.onAudio);
     this.showItem("context-markpageMenu", enablePageMarks && markProviders.length > SocialMarks.MENU_LIMIT);
     let enablePageMarkItems = enablePageMarks && markProviders.length <= SocialMarks.MENU_LIMIT;
     let linkmenus = document.getElementsByClassName("context-markpage");
-    [m.hidden = !enablePageMarkItems for (m of linkmenus)];
+    for (let m of linkmenus) {
+      m.hidden = !enablePageMarkItems;
+    }
 
     let enableLinkMarks = markProviders.length > 0 &&
                             ((this.onLink && !this.onMailtoLink) || this.onPlainTextLink);
     this.showItem("context-marklinkMenu", enableLinkMarks && markProviders.length > SocialMarks.MENU_LIMIT);
     let enableLinkMarkItems = enableLinkMarks && markProviders.length <= SocialMarks.MENU_LIMIT;
     linkmenus = document.getElementsByClassName("context-marklink");
-    [m.hidden = !enableLinkMarkItems for (m of linkmenus)];
+    for (let m of linkmenus) {
+      m.hidden = !enableLinkMarkItems;
+    }
 
     // SocialShare
     let shareButton = SocialShare.shareButton;
     let shareEnabled = shareButton && !shareButton.disabled && !this.onSocial;
     let pageShare = shareEnabled && !(this.isContentSelected ||
                             this.onTextInput || this.onLink || this.onImage ||
                             this.onVideo || this.onAudio || this.onCanvas);
     this.showItem("context-sharepage", pageShare);
--- a/browser/components/about/AboutRedirector.cpp
+++ b/browser/components/about/AboutRedirector.cpp
@@ -100,19 +100,16 @@ static RedirEntry kRedirMap[] = {
 #ifdef MOZ_SERVICES_HEALTHREPORT
   { "healthreport", "chrome://browser/content/abouthealthreport/abouthealth.xhtml",
     nsIAboutModule::ALLOW_SCRIPT },
 #endif
   { "accounts", "chrome://browser/content/aboutaccounts/aboutaccounts.xhtml",
     nsIAboutModule::ALLOW_SCRIPT },
   { "customizing", "chrome://browser/content/customizableui/aboutCustomizing.xul",
     nsIAboutModule::ALLOW_SCRIPT },
-  {
-    "debugging", "chrome://devtools/content/aboutdebugging/aboutdebugging.xhtml",
-    nsIAboutModule::ALLOW_SCRIPT },
   { "loopconversation", "chrome://loop/content/panels/conversation.html",
     nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
     nsIAboutModule::ALLOW_SCRIPT |
     nsIAboutModule::HIDE_FROM_ABOUTABOUT |
     nsIAboutModule::ENABLE_INDEXED_DB },
   { "looppanel", "chrome://loop/content/panels/panel.html",
     nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
     nsIAboutModule::ALLOW_SCRIPT |
--- a/browser/components/build/nsModule.cpp
+++ b/browser/components/build/nsModule.cpp
@@ -104,17 +104,16 @@ static const mozilla::Module::ContractID
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "remote-newtab", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "preferences", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "downloads", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "accounts", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
 #ifdef MOZ_SERVICES_HEALTHREPORT
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "healthreport", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
 #endif
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "customizing", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
-    { NS_ABOUT_MODULE_CONTRACTID_PREFIX "debugging", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "looppanel", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "loopconversation", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "reader", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
 #if defined(XP_WIN)
     { NS_IEHISTORYENUMERATOR_CONTRACTID, &kNS_WINIEHISTORYENUMERATOR_CID },
 #elif defined(XP_MACOSX)
     { NS_SHELLSERVICE_CONTRACTID, &kNS_SHELLSERVICE_CID },
 #endif
--- a/browser/components/customizableui/CustomizableUI.jsm
+++ b/browser/components/customizableui/CustomizableUI.jsm
@@ -3347,17 +3347,17 @@ this.CustomizableUI = {
     );
   },
   /**
    * Obtain an array of all the area IDs known to CustomizableUI.
    * This array is created for you, so is modifiable without CustomizableUI
    * being affected.
    */
   get areas() {
-    return [area for ([area, props] of gAreas)];
+    return [...gAreas.keys()];
   },
   /**
    * Check what kind of area (toolbar or menu panel) an area is. This is
    * useful if you have a widget that needs to behave differently depending
    * on its location. Note that widget wrappers have a convenience getter
    * property (areaType) for this purpose.
    *
    * @param aArea the ID of the area whose type you want to know
@@ -3749,17 +3749,17 @@ function WidgetGroupWrapper(aWidget) {
     if (!placement) {
       return [];
     }
     let area = placement.area;
     let buildAreas = gBuildAreas.get(area);
     if (!buildAreas) {
       return [];
     }
-    return [this.forWindow(node.ownerDocument.defaultView) for (node of buildAreas)];
+    return Array.from(buildAreas, (node) => this.forWindow(node.ownerDocument.defaultView));
   });
 
   this.__defineGetter__("areaType", function() {
     let areaProps = gAreas.get(aWidget.currentArea);
     return areaProps && areaProps.get("type");
   });
 
   Object.freeze(this);
@@ -3860,17 +3860,17 @@ function XULWidgetGroupWrapper(aWidgetId
       return null;
     }
 
     let areaProps = gAreas.get(placement.area);
     return areaProps && areaProps.get("type");
   });
 
   this.__defineGetter__("instances", function() {
-    return [this.forWindow(win) for ([win,] of gBuildWindows)];
+    return Array.from(gBuildWindows, ([win,]) => this.forWindow(win));
   });
 
   Object.freeze(this);
 }
 
 /**
  * A XULWidgetSingleWrapper is a wrapper around a single instance of a XUL
  * widget in a particular window.
--- a/browser/components/customizableui/PanelWideWidgetTracker.jsm
+++ b/browser/components/customizableui/PanelWideWidgetTracker.jsm
@@ -87,17 +87,17 @@ var PanelWideWidgetTracker = {
     }
     return rv;
   },
   adjustWidgets: function(aWidgetId, aMoveForwards) {
     if (this.adjusting) {
       return;
     }
     this.adjusting = true;
-    let widgetsAffected = [w for (w of gPanelPlacements) if (gWideWidgets.has(w))];
+    let widgetsAffected = gPanelPlacements.filter((w) => gWideWidgets.has(w));
     // If we're moving the wide widgets forwards (down/to the right in the panel)
     // we want to start with the last widgets. Otherwise we move widgets over other wide
     // widgets, which might mess up their order. Likewise, if moving backwards we should start with
     // the first widget and work our way down/right from there.
     let compareFn = aMoveForwards ? ((a, b) => a < b) : ((a, b) => a > b);
     widgetsAffected.sort((a, b) => compareFn(gPanelPlacements.indexOf(a),
                                              gPanelPlacements.indexOf(b)));
     for (let widget of widgetsAffected) {
--- a/browser/components/customizableui/content/toolbar.xml
+++ b/browser/components/customizableui/content/toolbar.xml
@@ -74,18 +74,19 @@
                 toolbox.palette = node;
                 toolbox.removeChild(node);
                 break;
               }
             }
           }
 
           // pass the current set of children for comparison with placements:
-          let children = [node.id for (node of this.childNodes)
-                          if (node.getAttribute("skipintoolbarset") != "true" && node.id)];
+          let children = Array.from(this.childNodes)
+                              .filter(node => node.getAttribute("skipintoolbarset") != "true" && node.id)
+                              .map(node => node.id);
           CustomizableUI.registerToolbarNode(this, children);
         ]]></body>
       </method>
 
       <method name="handleEvent">
         <parameter name="aEvent"/>
         <body><![CDATA[
           if (aEvent.type == "overflow" && aEvent.detail > 0) {
@@ -199,17 +200,17 @@
           return orderedPlacements.filter((x) => currentWidgets.has(x)).join(',');
         ]]></getter>
         <setter><![CDATA[
           // Get list of new and old ids:
           let newVal = (val || '').split(',').filter(x => x);
           let oldIds = CustomizableUI.getWidgetIdsInArea(this.id);
 
           // Get a list of items only in the new list
-          let newIds = [id for (id of newVal) if (oldIds.indexOf(id) == -1)];
+          let newIds = newVal.filter(id => oldIds.indexOf(id) == -1);
           CustomizableUI.beginBatchUpdate();
           try {
             for (let newId of newIds) {
               oldIds = CustomizableUI.getWidgetIdsInArea(this.id);
               let nextId = newId;
               let pos;
               do {
                 // Get the next item
@@ -220,17 +221,17 @@
               } while (pos == -1 && nextId);
               if (pos == -1) {
                 pos = null; // We didn't find anything, insert at the end
               }
               CustomizableUI.addWidgetToArea(newId, this.id, pos);
             }
 
             let currentIds = this.currentSet.split(',');
-            let removedIds = [id for (id of currentIds) if (newIds.indexOf(id) == -1 && newVal.indexOf(id) == -1)];
+            let removedIds = currentIds.filter(id => newIds.indexOf(id) == -1 && newVal.indexOf(id) == -1);
             for (let removedId of removedIds) {
               CustomizableUI.removeWidgetFromArea(removedId);
             }
           } finally {
             CustomizableUI.endBatchUpdate();
           }
         ]]></setter>
       </property>
@@ -580,17 +581,17 @@
       </method>
       <property name="customizationTarget" readonly="true">
         <getter><![CDATA[
           return this;
         ]]></getter>
       </property>
       <property name="currentSet">
         <getter><![CDATA[
-          return [node.id for (node of this.children)].join(',');
+          return Array.from(this.children, node => node.id).join(",");
         ]]></getter>
         <setter><![CDATA[
           let v = val.split(',');
           let newButtons = v.filter(x => x && (!this._whiteListed.has(x) &&
                                                !CustomizableUI.isSpecialWidget(x) &&
                                                !this._currentSetMigrated.has(x)));
           for (let newButton of newButtons) {
             this._currentSetMigrated.add(newButton);
--- a/browser/components/customizableui/test/browser_942581_unregisterArea_keeps_placements.js
+++ b/browser/components/customizableui/test/browser_942581_unregisterArea_keeps_placements.js
@@ -81,17 +81,17 @@ add_task(function() {
     } else {
       CustomizableUI.destroyWidget(widget);
     }
   }
 });
 
 function checkAbstractAndRealPlacements(aNode, aExpectedPlacements) {
   assertAreaPlacements(kToolbarName, aExpectedPlacements);
-  let physicalWidgetIds = [node.id for (node of aNode.childNodes)];
+  let physicalWidgetIds = Array.from(aNode.childNodes, (node) => node.id);
   placementArraysEqual(aNode.id, physicalWidgetIds, aExpectedPlacements);
 }
 
 function checkWidgetFates(aWidgetIds) {
   for (let widget of aWidgetIds) {
     ok(!CustomizableUI.getPlacementOfWidget(widget), "Widget should be in palette");
     ok(!document.getElementById(widget), "Widget should not be in the DOM");
     let widgetInPalette = !!gNavToolbox.palette.querySelector("#" + widget);
--- a/browser/components/migration/360seProfileMigrator.js
+++ b/browser/components/migration/360seProfileMigrator.js
@@ -85,17 +85,17 @@ function getHash(aStr) {
   hasher.init(Ci.nsICryptoHash.MD5);
   let stringStream = Cc["@mozilla.org/io/string-input-stream;1"].
                      createInstance(Ci.nsIStringInputStream);
   stringStream.data = aStr;
   hasher.updateFromStream(stringStream, -1);
 
   // convert the binary hash data to a hex string.
   let binary = hasher.finish(false);
-  return [toHexString(binary.charCodeAt(i)) for (i in binary)].join("").toLowerCase();
+  return Array.from(binary, (c, i) => toHexString(binary.charCodeAt(i))).join("").toLowerCase();
 }
 
 function Bookmarks(aProfileFolder) {
   let file = aProfileFolder.clone();
   file.append("360sefav.db");
 
   this._file = file;
 }
--- a/browser/components/migration/FirefoxProfileMigrator.js
+++ b/browser/components/migration/FirefoxProfileMigrator.js
@@ -56,17 +56,17 @@ FirefoxProfileMigrator.prototype._getAll
 };
 
 function sorter(a, b) {
   return a.id.toLocaleLowerCase().localeCompare(b.id.toLocaleLowerCase());
 }
 
 Object.defineProperty(FirefoxProfileMigrator.prototype, "sourceProfiles", {
   get: function() {
-    return [{id: x, name: x} for (x of this._getAllProfiles().keys())].sort(sorter);
+    return this._getAllProfiles().keys().map(x => ({id: x, name: x})).sort(sorter);
   }
 });
 
 FirefoxProfileMigrator.prototype._getFileObject = function(dir, fileName) {
   let file = dir.clone();
   file.append(fileName);
 
   // File resources are monolithic.  We don't make partial copies since
--- a/browser/components/migration/SafariProfileMigrator.js
+++ b/browser/components/migration/SafariProfileMigrator.js
@@ -528,20 +528,20 @@ SearchStrings.prototype = {
     this._mainPreferencesPropertyList.read(MigrationUtils.wrapMigrateFunction(
       function migrateSearchStrings(aDict) {
         if (!aDict)
           throw new Error("Could not get preferences dictionary");
 
         if (aDict.has("RecentSearchStrings")) {
           let recentSearchStrings = aDict.get("RecentSearchStrings");
           if (recentSearchStrings && recentSearchStrings.length > 0) {
-            let changes = [{op: "add",
+            let changes = recentSearchStrings.map((searchString) => (
+                           {op: "add",
                             fieldname: "searchbar-history",
-                            value: searchString}
-                           for (searchString of recentSearchStrings)];
+                            value: searchString}));
             FormHistory.update(changes);
           }
         }
       }.bind(this), aCallback));
   }
 };
 
 #ifdef XP_MACOSX
--- a/browser/components/migration/tests/unit/test_fx_fhr.js
+++ b/browser/components/migration/tests/unit/test_fx_fhr.js
@@ -38,17 +38,22 @@ function checkDirectoryContains(dir, fil
       checkDirectoryContains(newDir, expectedContents);
     } else {
       Assert.ok(!file.isDirectory(), "should be a regular file");
       let contents = readFile(file);
       Assert.equal(contents, expectedContents);
     }
     seen.add(file.leafName);
   }
-  let missing = [x for (x in files) if (!seen.has(x))];
+  let missing = [];
+  for (let x in files) {
+    if (!seen.has(x)) {
+      missing.push(x);
+    }
+  }
   Assert.deepEqual(missing, [], "no missing files in " + dir.path);
 }
 
 function getTestDirs() {
   // we make a directory structure in a temp dir which mirrors what we are
   // testing.
   let tempDir = do_get_tempdir();
   let srcDir = tempDir.clone();
--- a/browser/components/preferences/in-content/tests/privacypane_tests_perwindow.js
+++ b/browser/components/preferences/in-content/tests/privacypane_tests_perwindow.js
@@ -327,17 +327,17 @@ function reset_preferences(win) {
   let prefs = win.document.querySelectorAll("#privacyPreferences > preference");
   for (let pref of prefs)
     pref.value = gPrefCache.get(pref.name);
 }
 
 var testRunner;
 function run_test_subset(subset) {
   Services.prefs.setBoolPref("browser.preferences.instantApply", true);
-  dump("subset: " + [x.name for (x of subset)].join(",") + "\n");
+  dump("subset: " + Array.from(subset, x => x.name).join(",") + "\n");
 
   waitForExplicitFinish();
   registerCleanupFunction(function() {
     // Reset pref to its default
     Services.prefs.clearUserPref("browser.preferences.instantApply");
   });
 
   testRunner = {
--- a/browser/components/sessionstore/SessionHistory.jsm
+++ b/browser/components/sessionstore/SessionHistory.jsm
@@ -59,20 +59,22 @@ var SessionHistoryInternal = {
 
   /**
    * Collects session history data for a given docShell.
    *
    * @param docShell
    *        The docShell that owns the session history.
    */
   collect: function (docShell) {
-    let data = {entries: [], userContextId: docShell.userContextId };
+    let loadContext = docShell.QueryInterface(Ci.nsILoadContext);
     let webNavigation = docShell.QueryInterface(Ci.nsIWebNavigation);
     let history = webNavigation.sessionHistory.QueryInterface(Ci.nsISHistoryInternal);
 
+    let data = {entries: [], userContextId: loadContext.originAttributes.userContextId };
+
     if (history && history.count > 0) {
       // Loop over the transaction linked list directly so we can get the
       // persist property for each transaction.
       for (let txn = history.rootTransaction; txn; txn = txn.next) {
         let entry = this.serializeEntry(txn.sHEntry);
         entry.persist = txn.persist;
         data.entries.push(entry);
       }
@@ -249,17 +251,17 @@ var SessionHistoryInternal = {
    * @param tabData
    *        The tabdata including all history entries.
    */
   restore: function (docShell, tabData) {
     let webNavigation = docShell.QueryInterface(Ci.nsIWebNavigation);
     let history = webNavigation.sessionHistory;
 
     if ("userContextId" in tabData) {
-      docShell.userContextId = tabData.userContextId;
+      docShell.setUserContextId(tabData.userContextId);
     }
 
     if (history.count > 0) {
       history.PurgeHistory(history.count);
     }
     history.QueryInterface(Ci.nsISHistoryInternal);
 
     let idMap = { used: {} };
--- a/browser/components/sessionstore/SessionStorage.jsm
+++ b/browser/components/sessionstore/SessionStorage.jsm
@@ -99,17 +99,28 @@ var SessionStorageInternal = {
    * @param aStorageData
    *        A nested object with storage data to be restored that has hosts as
    *        keys and per-host session storage data as strings. For example:
    *        {"example.com": {"key": "value", "my_number": "123"}}
    */
   restore: function (aDocShell, aStorageData) {
     for (let origin of Object.keys(aStorageData)) {
       let data = aStorageData[origin];
-      let principal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(origin);
+
+      let principal;
+
+      try {
+        let attrs = ChromeUtils.createOriginAttributesWithUserContextId(origin, aDocShell.userContextId);
+        let originURI = Services.io.newURI(origin, null, null);
+        principal = Services.scriptSecurityManager.createCodebasePrincipal(originURI, attrs);
+      } catch (e) {
+        console.error(e);
+        continue;
+      }
+
       let storageManager = aDocShell.QueryInterface(Ci.nsIDOMStorageManager);
       let window = aDocShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
 
       // There is no need to pass documentURI, it's only used to fill documentURI property of
       // domstorage event, which in this case has no consumer. Prevention of events in case
       // of missing documentURI will be solved in a followup bug to bug 600307.
       let storage = storageManager.createStorage(window, principal, "", aDocShell.usePrivateBrowsing);
 
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -1902,20 +1902,20 @@ var SessionStoreInternal = {
   // Clean up data that has been closed a long time ago.
   // Do not reschedule a save. This will wait for the next regular
   // save.
   onIdleDaily: function() {
     // Remove old closed windows
     this._cleanupOldData([this._closedWindows]);
 
     // Remove closed tabs of closed windows
-    this._cleanupOldData([winData._closedTabs for (winData of this._closedWindows)]);
+    this._cleanupOldData(this._closedWindows.map((winData) => winData._closedTabs));
 
     // Remove closed tabs of open windows
-    this._cleanupOldData([this._windows[key]._closedTabs for (key of Object.keys(this._windows))]);
+    this._cleanupOldData(Object.keys(this._windows).map((key) => this._windows[key]._closedTabs));
   },
 
   // Remove "old" data from an array
   _cleanupOldData: function(targets) {
     const TIME_TO_LIVE = this._prefBranch.getIntPref("sessionstore.cleanup.forget_closed_after");
     const now = Date.now();
 
     for (let array of targets) {
--- a/browser/components/sessionstore/test/browser_sessionStoreContainer.js
+++ b/browser/components/sessionstore/test/browser_sessionStoreContainer.js
@@ -1,15 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 function retrieveUserContextId(browser) {
   return ContentTask.spawn(browser, null, function* () {
-    return docShell.userContextId;
+    let loadContext = docShell.QueryInterface(Ci.nsILoadContext);
+    return loadContext.originAttributes.userContextId;
   });
 }
 
 add_task(function() {
   for (let i = 0; i < 3; ++i) {
     let tab = gBrowser.addTab("about:blank");
     let browser = tab.linkedBrowser;
 
--- a/browser/components/translation/test/bing.sjs
+++ b/browser/components/translation/test/bing.sjs
@@ -89,17 +89,17 @@ function sha1(str) {
   let hash = ch.finish(false);
 
   // Return the two-digit hexadecimal code for a byte.
   function toHexString(charCode) {
     return ("0" + charCode.toString(16)).slice(-2);
   }
 
   // Convert the binary hash data to a hex string.
-  return [toHexString(hash.charCodeAt(i)) for (i in hash)].join("");
+  return Array.from(hash, (c, i) => toHexString(hash.charCodeAt(i))).join("");
 }
 
 function parseXml(body) {
   let DOMParser = Cc["@mozilla.org/xmlextras/domparser;1"]
                     .createInstance(Ci.nsIDOMParser);
   let xml = DOMParser.parseFromString(body, "text/xml");
   if (xml.documentElement.localName == "parsererror")
     throw new Error("Invalid XML");
--- a/browser/components/translation/test/yandex.sjs
+++ b/browser/components/translation/test/yandex.sjs
@@ -94,38 +94,17 @@ function sha1(str) {
   let hash = ch.finish(false);
 
   // Return the two-digit hexadecimal code for a byte.
   function toHexString(charCode) {
     return ("0" + charCode.toString(16)).slice(-2);
   }
 
   // Convert the binary hash data to a hex string.
-  return [toHexString(hash.charCodeAt(i)) for (i in hash)].join("");function sha1(str) {
-  let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
-                    .createInstance(Ci.nsIScriptableUnicodeConverter);
-  converter.charset = "UTF-8";
-  // `result` is an out parameter, `result.value` will contain the array length.
-  let result = {};
-  // `data` is an array of bytes.
-  let data = converter.convertToByteArray(str, result);
-  let ch = Cc["@mozilla.org/security/hash;1"]
-             .createInstance(Ci.nsICryptoHash);
-  ch.init(ch.SHA1);
-  ch.update(data, data.length);
-  let hash = ch.finish(false);
-
-  // Return the two-digit hexadecimal code for a byte.
-  function toHexString(charCode) {
-    return ("0" + charCode.toString(16)).slice(-2);
-  }
-
-  // Convert the binary hash data to a hex string.
-  return [toHexString(hash.charCodeAt(i)) for (i in hash)].join("");
-}
+  return Array.from(hash, (c, i) => toHexString(hash.charCodeAt(i))).join("");
 }
 
 function getRequestBody(req) {
   let avail;
   let bytes = [];
   let body = new BinaryInputStream(req.bodyInputStream);
 
   while ((avail = body.available()) > 0)
--- a/browser/components/uitour/UITour.jsm
+++ b/browser/components/uitour/UITour.jsm
@@ -1826,19 +1826,18 @@ this.UITour = {
       case "search":
       case "selectedSearchEngine":
         Services.search.init(rv => {
           let data;
           if (Components.isSuccessCode(rv)) {
             let engines = Services.search.getVisibleEngines();
             data = {
               searchEngineIdentifier: Services.search.defaultEngine.identifier,
-              engines: [TARGET_SEARCHENGINE_PREFIX + engine.identifier
-                        for (engine of engines)
-                          if (engine.identifier)]
+              engines: engines.filter((engine) => engine.identifier)
+                              .map((engine) => TARGET_SEARCHENGINE_PREFIX + engine.identifier)
             };
           } else {
             data = {engines: [], searchEngineIdentifier: ""};
           }
           this.sendPageCallback(aMessageManager, aCallbackID, data);
         });
         break;
       case "sync":
--- a/browser/components/uitour/test/browser_UITour.js
+++ b/browser/components/uitour/test/browser_UITour.js
@@ -322,19 +322,18 @@ var tests = [
       if (!Components.isSuccessCode(rv)) {
         ok(false, "search service init failed: " + rv);
         done();
         return;
       }
       let defaultEngine = Services.search.defaultEngine;
       gContentAPI.getConfiguration("search", data => {
         let visibleEngines = Services.search.getVisibleEngines();
-        let expectedEngines = ["searchEngine-" + engine.identifier
-                               for (engine of visibleEngines)
-                                 if (engine.identifier)];
+        let expectedEngines = visibleEngines.filter((engine) => engine.identifier)
+                                            .map((engine) => "searchEngine-" + engine.identifier);
 
         let engines = data.engines;
         ok(Array.isArray(engines), "data.engines should be an array");
         is(engines.sort().toString(), expectedEngines.sort().toString(),
            "Engines should be as expected");
 
         is(data.searchEngineIdentifier, defaultEngine.identifier,
            "the searchEngineIdentifier property should contain the defaultEngine's identifier");
--- a/browser/extensions/loop/content/modules/MozLoopAPI.jsm
+++ b/browser/extensions/loop/content/modules/MozLoopAPI.jsm
@@ -655,17 +655,17 @@ const kMessageHandlers = {
                    .createInstance(Ci.nsICryptoHash);
     hasher.init(Ci.nsICryptoHash.MD5);
     let stringStream = Cc["@mozilla.org/io/string-input-stream;1"]
                          .createInstance(Ci.nsIStringInputStream);
     stringStream.data = emailAddress.trim().toLowerCase();
     hasher.updateFromStream(stringStream, -1);
     let hash = hasher.finish(false);
     // Convert the binary hash data to a hex string.
-    let md5Email = [toHexString(hash.charCodeAt(i)) for (i in hash)].join("");
+    let md5Email = Array.from(hash, (c, i) => toHexString(hash.charCodeAt(i))).join("");
 
     // Compose the Gravatar URL.
     reply("https://www.gravatar.com/avatar/" + md5Email +
       ".jpg?default=blank&s=" + (size || 40));
   },
 
   /**
    * Gets an object with data that represents the currently
--- a/browser/extensions/pocket/content/main.js
+++ b/browser/extensions/pocket/content/main.js
@@ -192,28 +192,28 @@ var pktUI = (function() {
 
     	var panelId = showPanel("chrome://pocket/content/panels/saved.html?pockethost=" + Services.prefs.getCharPref("extensions.pocket.site") + "&premiumStatus=" + (pktApi.isPremiumUser() ? '1' : '0') + '&inoverflowmenu='+inOverflowMenu + "&locale=" + getUILocale(), {
     		onShow: function() {
                 var saveLinkMessageId = 'saveLink';
 
                 // Send error message for invalid url
                 if (!isValidURL) {
                     // TODO: Pass key for localized error in error object
-                    var error = {
+                    let error = {
                         message: 'Only links can be saved',
                         localizedKey: "onlylinkssaved"
                     };
                     pktUIMessaging.sendErrorMessageToPanel(panelId, saveLinkMessageId, error);
                     return;
                 }
 
                 // Check online state
                 if (!navigator.onLine) {
                     // TODO: Pass key for localized error in error object
-                    var error = {
+                    let error = {
                         message: 'You must be connected to the Internet in order to save to Pocket. Please connect to the Internet and try again.'
                     };
                     pktUIMessaging.sendErrorMessageToPanel(panelId, saveLinkMessageId, error);
                     return;
                 }
 
                 // Add url
                 var options = {
--- a/browser/extensions/pocket/content/panels/js/messages.js
+++ b/browser/extensions/pocket/content/panels/js/messages.js
@@ -70,9 +70,9 @@ var pktPanelMessaging = (function() {
      * Public functions
      */
     return {
       panelIdFromURL: panelIdFromURL,
         addMessageListener : addMessageListener,
         removeMessageListener : removeMessageListener,
         sendMessage: sendMessage
     };
-}());
\ No newline at end of file
+}());
--- a/browser/extensions/pocket/content/panels/js/tmpl.js
+++ b/browser/extensions/pocket/content/panels/js/tmpl.js
@@ -142,9 +142,9 @@ templates['signupstoryboard_shell'] = te
     + " <a href=\"https://"
     + escapeExpression(((helper = (helper = helpers.pockethost || (depth0 != null ? depth0.pockethost : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pockethost","hash":{},"data":data}) : helper)))
     + "/login?ep=3&src=extension&s=ffi&t=login&v="
     + escapeExpression(((helper = (helper = helpers.variant || (depth0 != null ? depth0.variant : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"variant","hash":{},"data":data}) : helper)))
     + "\" target=\"_blank\">"
     + escapeExpression(((helper = (helper = helpers.loginnow || (depth0 != null ? depth0.loginnow : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"loginnow","hash":{},"data":data}) : helper)))
     + "</a>.</p>\n</div>";
 },"useData":true});
-})();
\ No newline at end of file
+})();
--- a/browser/extensions/pocket/content/pktApi.jsm
+++ b/browser/extensions/pocket/content/pktApi.jsm
@@ -118,17 +118,17 @@ var pktApi = (function() {
      * @return {string} String containing the value of the key. If the key
      *                  does not exist, null is returned
      */
      function getSetting(key) {
      	// TODO : Move this to sqlite or a local file so it's not editable (and is safer)
      	// https://developer.mozilla.org/en-US/Add-ons/Overlay_Extensions/XUL_School/Local_Storage
      	
 		if (!prefBranch.prefHasUserValue(key))
-			return;
+			return undefined;
 		
 		return prefBranch.getComplexValue(key, Components.interfaces.nsISupportsString).data;
      }
 
      /**
       * Wrapper for different plattforms to set a value for a given key in settings
       * @param {string} key     A string containing the name of the key you want
       *                         to create/update.
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -138,16 +138,20 @@
 #endif
 @RESPATH@/platform.ini
 #ifndef MOZ_NATIVE_SQLITE
 #ifndef MOZ_FOLD_LIBS
 @BINPATH@/@DLL_PREFIX@mozsqlite3@DLL_SUFFIX@
 #endif
 #endif
 @BINPATH@/@DLL_PREFIX@lgpllibs@DLL_SUFFIX@
+#ifdef MOZ_FFVPX
+@BINPATH@/@DLL_PREFIX@mozavutil@DLL_SUFFIX@
+@BINPATH@/@DLL_PREFIX@mozavcodec@DLL_SUFFIX@
+#endif
 @RESPATH@/browser/blocklist.xml
 #ifdef XP_UNIX
 #ifndef XP_MACOSX
 @RESPATH@/run-mozilla.sh
 #endif
 #endif
 
 ; [Components]
--- a/browser/installer/windows/nsis/stub.nsi
+++ b/browser/installer/windows/nsis/stub.nsi
@@ -366,16 +366,37 @@ Function .onInit
 !endif
 
   SetShellVarContext all ; Set SHCTX to HKLM
   ${GetSingleInstallPath} "Software\Mozilla\${BrandFullNameInternal}" $R9
 
   ${If} "$R9" == "false"
     SetShellVarContext current ; Set SHCTX to HKCU
     ${GetSingleInstallPath} "Software\Mozilla\${BrandFullNameInternal}" $R9
+
+    ${If} ${RunningX64}
+      ; In HKCU there is no WOW64 redirection, which means we may have gotten
+      ; the path to a 32-bit install even though we're 64-bit, or vice-versa.
+      ; In that case, just use the default path instead of offering an upgrade.
+      ; But only do that override if the existing install is in Program Files,
+      ; because that's the only place we can be sure is specific
+      ; to either 32 or 64 bit applications.
+      ; The WordFind syntax below searches for the first occurence of the
+      ; "delimiter" (the Program Files path) in the install path and returns
+      ; anything that appears before that. If nothing appears before that,
+      ; then the install is under Program Files (32 or 64).
+!ifdef HAVE_64BIT_BUILD
+      ${WordFind} $R9 $PROGRAMFILES32 "+1{" $0
+!else
+      ${WordFind} $R9 $PROGRAMFILES64 "+1{" $0
+!endif
+      ${If} $0 == ""
+        StrCpy $R9 "false"
+      ${EndIf}
+    ${EndIf}
   ${EndIf}
 
   ${If} "$R9" != "false"
     StrCpy $INSTDIR "$R9"
   ${EndIf}
 
   ; Used to determine if the default installation directory was used.
   StrCpy $InitialInstallDir "$INSTDIR"
--- a/caps/tests/unit/test_origin.js
+++ b/caps/tests/unit/test_origin.js
@@ -28,16 +28,19 @@ function checkOriginAttributes(prin, att
   do_check_eq(prin.originSuffix, suffix || '');
   do_check_eq(ChromeUtils.originAttributesToSuffix(attrs), suffix || '');
   do_check_true(ChromeUtils.originAttributesMatchPattern(prin.originAttributes, attrs));
   if (!prin.isNullPrincipal && !prin.origin.startsWith('[')) {
     do_check_true(ssm.createCodebasePrincipalFromOrigin(prin.origin).equals(prin));
   } else {
     checkThrows(() => ssm.createCodebasePrincipalFromOrigin(prin.origin));
   }
+
+  do_check_eq(ChromeUtils.createOriginAttributesWithUserContextId("http://example.org", 2).userContextId, 2);
+  do_check_eq(ChromeUtils.createOriginAttributesWithUserContextId("https://www.example.com:123^userContextId=4", 2).userContextId, 2);
 }
 
 function run_test() {
   // Attributeless origins.
   do_check_eq(ssm.getSystemPrincipal().origin, '[System Principal]');
   checkOriginAttributes(ssm.getSystemPrincipal());
   var exampleOrg = ssm.createCodebasePrincipal(makeURI('http://example.org'), {});
   do_check_eq(exampleOrg.origin, 'http://example.org');
--- a/config/external/moz.build
+++ b/config/external/moz.build
@@ -39,16 +39,19 @@ if CONFIG['CPU_ARCH'] == 'arm':
     external_dirs += ['media/openmax_dl']
 
 if CONFIG['MOZ_WEBSPEECH_POCKETSPHINX']:
     external_dirs += [
         'media/sphinxbase',
         'media/pocketsphinx',
     ]
 
+if CONFIG['MOZ_FFVPX']:
+    external_dirs += ['media/ffvpx']
+
 external_dirs += [
     'media/kiss_fft',
     'media/libcubeb',
     'media/libnestegg',
     'media/libogg',
     'media/libopus',
     'media/libtheora',
     'media/libspeex_resampler',
--- a/config/stl-headers
+++ b/config/stl-headers
@@ -15,16 +15,17 @@
 new
 
 # FIXME: these headers haven't been reviewed yet, but we use them
 # unsafely in Gecko, so we might as well prevent them from
 # throwing exceptions
 algorithm
 atomic
 deque
+initializer_list
 ios
 iosfwd
 iostream
 iterator
 limits
 list
 map
 memory
--- a/config/system-headers
+++ b/config/system-headers
@@ -543,16 +543,17 @@ ia64/sys/inline.h
 Icons.h
 iconv.h
 ieeefp.h
 ifaddrs.h
 image.h
 imagehlp.h
 imm.h
 initguid.h
+initializer_list
 InterfaceDefs.h
 InternetConfig.h
 IntlResources.h
 ints.h
 intshcut.h
 inttypes.h
 iodef.h
 io.h
--- a/configure.in
+++ b/configure.in
@@ -3726,17 +3726,17 @@ MOZ_SAMPLE_TYPE_FLOAT32=
 MOZ_SAMPLE_TYPE_S16=
 MOZ_DIRECTSHOW=
 MOZ_WMF=
 if test -n "$MOZ_FMP4"; then
   MOZ_FMP4=1
 else
   MOZ_FMP4=
 fi
-MOZ_FFMPEG=
+MOZ_FFMPEG=1
 MOZ_WEBRTC=1
 MOZ_PEERCONNECTION=
 MOZ_SRTP=
 MOZ_WEBRTC_SIGNALING=
 MOZ_WEBRTC_ASSERT_ALWAYS=1
 MOZ_WEBRTC_HARDWARE_AEC_NS=
 MOZ_SCTP=
 MOZ_ANDROID_OMX=
@@ -5214,24 +5214,16 @@ MOZ_ARG_DISABLE_BOOL(wmf,
 
 if test -n "$MOZ_WMF"; then
     AC_DEFINE(MOZ_WMF)
 fi;
 
 dnl ========================================================
 dnl FFmpeg H264/AAC Decoding Support
 dnl ========================================================
-case "$OS_TARGET" in
-WINNT|Darwin|Android)
-    ;;
-*)
-    MOZ_FFMPEG=1
-    ;;
-esac
-
 MOZ_ARG_DISABLE_BOOL(ffmpeg,
 [  --disable-ffmpeg         Disable FFmpeg for fragmented H264/AAC decoding],
     MOZ_FFMPEG=,
     MOZ_FFMPEG=1
 )
 
 if test -n "$MOZ_FFMPEG"; then
     AC_DEFINE(MOZ_FFMPEG)
@@ -6179,16 +6171,39 @@ if test -n "$LIBAV_FFT_ASFLAGS"; then
     fi
   fi
 elif test -n "$MOZ_LIBAV_FFT" -a "${CPU_ARCH}" != "arm"; then
   dnl Warn if we're not building either libav or opendl-max optimized routines.
   AC_MSG_WARN([No assembler or assembly support for libav-fft.  Using unoptimized C routines.])
 fi
 
 dnl ========================================================
+dnl = FFmpeg's ffvpx configuration
+dnl ========================================================
+
+MOZ_FFVPX=
+case "$CPU_ARCH" in
+  x86)
+      MOZ_FFVPX=1
+  ;;
+  x86_64)
+      MOZ_FFVPX=1
+  ;;
+esac
+
+dnl Use same conditional as MOZ_LIBAV_FFT to enable FFmpeg's ffvpx assembly decoder.
+if test -n "$MOZ_LIBAV_FFT"; then
+  FFVPX_ASFLAGS=$LIBAV_FFT_ASFLAGS
+  FFVPX_AS=$LIBAV_FFT_AS
+fi
+if test -n "$MOZ_FFVPX"; then
+  AC_DEFINE(MOZ_FFVPX)
+fi
+
+dnl ========================================================
 dnl = Enable compilation of specific extension modules
 dnl ========================================================
 
 MOZ_ARG_ENABLE_STRING(extensions,
 [  --enable-extensions     Enable extensions],
 [ for option in `echo $enableval | sed 's/,/ /g'`; do
     if test "$option" = "yes" -o "$option" = "all"; then
         AC_MSG_ERROR([--enable-extensions=$option is no longer supported.])
@@ -8864,16 +8879,19 @@ AC_SUBST(MSMANIFEST_TOOL)
 AC_SUBST(NS_ENABLE_TSF)
 AC_SUBST(WIN32_CONSOLE_EXE_LDFLAGS)
 AC_SUBST(WIN32_GUI_EXE_LDFLAGS)
 
 AC_SUBST(MOZ_VORBIS)
 AC_SUBST(MOZ_TREMOR)
 AC_SUBST(MOZ_WMF)
 AC_SUBST(MOZ_FFMPEG)
+AC_SUBST(MOZ_FFVPX)
+AC_SUBST(FFVPX_AS)
+AC_SUBST_LIST(FFVPX_ASFLAGS)
 AC_SUBST(MOZ_FMP4)
 AC_SUBST(MOZ_EME)
 AC_SUBST(MOZ_DIRECTSHOW)
 AC_SUBST(MOZ_ANDROID_OMX)
 AC_SUBST(MOZ_APPLEMEDIA)
 AC_SUBST(MOZ_OMX_PLUGIN)
 AC_SUBST(MOZ_VPX_ERROR_CONCEALMENT)
 AC_SUBST(VPX_AS)
--- a/devtools/client/webconsole/test/browser_webconsole_bug_632347_iterators_generators.js
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_632347_iterators_generators.js
@@ -30,22 +30,22 @@ function consoleOpened(HUD) {
 
   // Make sure autocomplete does not walk through iterators and generators.
   let result = container.gen1.next();
   let completion = JSPropertyProvider(dbgWindow, null, "_container.gen1.");
   isnot(completion.matches.length, 0, "Got matches for gen1");
 
   is(result + 1, container.gen1.next(), "gen1.next() did not execute");
 
-  result = container.gen2.next();
+  result = container.gen2.next().value;
 
   completion = JSPropertyProvider(dbgWindow, null, "_container.gen2.");
   isnot(completion.matches.length, 0, "Got matches for gen2");
 
-  is((result / 2 + 1) * 2, container.gen2.next(),
+  is((result / 2 + 1) * 2, container.gen2.next().value,
      "gen2.next() did not execute");
 
   result = container.iter1.next();
   is(result[0], "foo", "iter1.next() [0] is correct");
   is(result[1], "bar", "iter1.next() [1] is correct");
 
   completion = JSPropertyProvider(dbgWindow, null, "_container.iter1.");
   isnot(completion.matches.length, 0, "Got matches for iter1");
--- a/devtools/client/webconsole/test/browser_webconsole_for_of.js
+++ b/devtools/client/webconsole/test/browser_webconsole_for_of.js
@@ -14,18 +14,17 @@ add_task(function* () {
   let hud = yield openConsole();
   yield testForOf(hud);
 });
 
 function testForOf(hud) {
   let deferred = promise.defer();
 
   let jsterm = hud.jsterm;
-  jsterm.execute("{ [x.tagName for (x of document.body.childNodes) " +
-    "if (x.nodeType === 1)].join(' '); }",
+  jsterm.execute("{ let _nodes = []; for (let x of document.body.childNodes) { if (x.nodeType === 1) { _nodes.push(x.tagName); } } _nodes.join(' '); }",
     (node) => {
       ok(/H1 DIV H2 P/.test(node.textContent),
         "for-of loop should find all top-level nodes");
       deferred.resolve();
     });
 
   return deferred.promise;
 }
--- a/devtools/client/webconsole/test/test-bug-632347-iterators-generators.html
+++ b/devtools/client/webconsole/test/test-bug-632347-iterators-generators.html
@@ -41,16 +41,16 @@ RangeIterator.prototype.next = function(
 }
 
 Range.prototype.__iterator__ = function() {
   return new RangeIterator(this);
 }
 
 _container.iter2 = new Range(3, 15);
 
-_container.gen2 = (i * 2 for (i in _container.iter2));
+_container.gen2 = (function* () { for (let i in _container.iter2) yield i * 2; })();
 })();
 </script>
   </head>
   <body>
     <p>Web Console test for bug 632347 - iterators and generators.</p>
   </body>
 </html>
--- a/devtools/server/tests/mochitest/test_inspector-mutations-childlist.html
+++ b/devtools/server/tests/mochitest/test_inspector-mutations-childlist.html
@@ -62,17 +62,17 @@ function setParent(nodeSelector, newPare
 
 function loadSelector(selector) {
   return gWalker.querySelectorAll(gWalker.rootNode, selector).then(nodeList => {
     return nodeList.items();
   });
 }
 
 function loadSelectors(selectors) {
-  return promise.all([loadSelector(sel) for (sel of selectors)]);
+  return promise.all(Array.from(selectors, (sel) => loadSelector(sel)));
 }
 
 function doMoves(moves) {
   for (let move of moves) {
     setParent(move[0], move[1]);
   }
 }
 
--- a/devtools/server/tests/mochitest/test_inspector-traversal.html
+++ b/devtools/server/tests/mochitest/test_inspector-traversal.html
@@ -323,17 +323,17 @@ addTest(function testShortValue() {
     is(valueStr, shortstringText, "Full node value should match the string from the document.");
   }).then(runNextTest));
 });
 
 addTest(function testReleaseWalker() {
   checkActorIDs.push(gWalker.actorID);
 
   promiseDone(gWalker.release().then(() => {
-    let promises = [checkMissing(gClient, id) for (id of checkActorIDs)];
+    let promises = Array.from(checkActorIDs, (id) => checkMissing(gClient, id));
     return promise.all(promises)
   }).then(runNextTest));
 });
 
 addTest(function cleanup() {
   delete gWalker;
   delete gInspectee;
   delete gClient;
--- a/docshell/base/moz.build
+++ b/docshell/base/moz.build
@@ -75,8 +75,11 @@ LOCAL_INCLUDES += [
     '/layout/generic',
     '/layout/xul',
     '/netwerk/protocol/viewsource',
     '/tools/profiler',
 ]
 
 if CONFIG['MOZ_TOOLKIT_SEARCH']:
     DEFINES['MOZ_TOOLKIT_SEARCH'] = True
+
+if CONFIG['MOZ_DEVTOOLS'] == 'all':
+    DEFINES['MOZ_DEVTOOLS_ALL'] = True
--- a/docshell/base/nsAboutRedirector.cpp
+++ b/docshell/base/nsAboutRedirector.cpp
@@ -47,16 +47,22 @@ static RedirEntry kRedirMap[] = {
   { "config", "chrome://global/content/config.xul", 0 },
 #ifdef MOZ_CRASHREPORTER
   { "crashes", "chrome://global/content/crashes.xhtml", 0 },
 #endif
   {
     "credits", "https://www.mozilla.org/credits/",
     nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT
   },
+#ifdef MOZ_DEVTOOLS_ALL
+  {
+    "debugging", "chrome://devtools/content/aboutdebugging/aboutdebugging.xhtml",
+    nsIAboutModule::ALLOW_SCRIPT
+  },
+#endif
   {
     "license", "chrome://global/content/license.html",
     nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT
   },
   {
     "logo", "chrome://branding/content/about.png",
     nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT
   },
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -13832,27 +13832,16 @@ nsDocShell::SetIsBrowserInsideApp(uint32
 NS_IMETHODIMP
 nsDocShell::SetIsSignedPackage(const nsAString& aSignedPkg)
 {
   mSignedPkg = aSignedPkg;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDocShell::GetUserContextId(uint32_t* aUserContextId)
-{
-  if (!aUserContextId) {
-    return NS_ERROR_FAILURE;
-  }
-
-  *aUserContextId = mUserContextId;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 nsDocShell::SetUserContextId(uint32_t aUserContextId)
 {
   mUserContextId = aUserContextId;
   return NS_OK;
 }
 
 /* [infallible] */ NS_IMETHODIMP
 nsDocShell::GetIsBrowserElement(bool* aIsBrowser)
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -38,17 +38,17 @@ interface nsIPrincipal;
 interface nsIWebBrowserPrint;
 interface nsIPrivacyTransitionObserver;
 interface nsIReflowObserver;
 interface nsIScrollObserver;
 interface nsITabParent;
 
 typedef unsigned long nsLoadFlags;
 
-[scriptable, builtinclass, uuid(258a8a33-219f-42f8-8fa8-f8f2dcd2358b)]
+[scriptable, builtinclass, uuid(98358234-3936-4b95-b051-fcda4e55b52d)]
 interface nsIDocShell : nsIDocShellTreeItem
 {
   /**
    * Loads a given URI.  This will give priority to loading the requested URI
    * in the object implementing	this interface.  If it can't be loaded here
    * however, the URL dispatcher will go through its normal process of content
    * loading.
    *
@@ -1092,13 +1092,13 @@ interface nsIDocShell : nsIDocShellTreeI
   attribute boolean windowDraggingAllowed;
 
   /**
    * Sets/gets the current scroll restoration mode.
    * @see https://html.spec.whatwg.org/#dom-history-scroll-restoration
   */
   attribute boolean currentScrollRestorationIsManual;
 
-  /**
-   * Sets/gets the user context ID for this docshell.
+  /*
+   * Sets the user context ID for this docshell.
    */
-  attribute unsigned long userContextId;
+  void setUserContextId(in unsigned long aUserContextId);
 };
--- a/docshell/build/moz.build
+++ b/docshell/build/moz.build
@@ -24,8 +24,11 @@ if CONFIG["MOZ_WIDGET_TOOLKIT"] == "coco
     LOCAL_INCLUDES += ['/uriloader/exthandler/mac']
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
 if CONFIG['GNU_CXX']:
     CXXFLAGS += ['-Wshadow']
+
+if CONFIG['MOZ_DEVTOOLS'] == 'all':
+    DEFINES['MOZ_DEVTOOLS_ALL'] = True
--- a/docshell/build/nsDocShellModule.cpp
+++ b/docshell/build/nsDocShellModule.cpp
@@ -163,16 +163,19 @@ const mozilla::Module::ContractIDEntry k
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "about", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "addons", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "buildconfig", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "config", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
 #ifdef MOZ_CRASHREPORTER
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "crashes", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
 #endif
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "credits", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
+#ifdef MOZ_DEVTOOLS_ALL
+  { NS_ABOUT_MODULE_CONTRACTID_PREFIX "debugging", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
+#endif
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "license", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "logo", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "memory", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "mozilla", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "neterror", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "networking", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "newaddon", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
 #ifdef NIGHTLY_BUILD
--- a/docshell/test/mochitest.ini
+++ b/docshell/test/mochitest.ini
@@ -49,17 +49,17 @@ skip-if = buildapp == 'mulet' || (builda
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_bug413310.html]
 skip-if = true
 # Disabled for too many intermittent failures (bug 719186)
 [test_bug475636.html]
 [test_bug509055.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_bug511449.html]
-skip-if = toolkit != "cocoa" || e10s
+skip-if = toolkit != "cocoa"
 support-files = file_bug511449.html
 [test_bug529119-1.html]
 skip-if = buildapp == 'mulet' || (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_bug529119-2.html]
 skip-if = buildapp == 'mulet' || (buildapp == 'b2g' && (toolkit != 'gonk' || debug)) # b2g-debug(debug-only failure) b2g-desktop(Bug 931116, b2g desktop specific, initial triage)
 [test_bug530396.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Timeouts on B2G desktop
 support-files = bug530396-noref.sjs bug530396-subframe.html
--- a/dom/animation/AnimationTimeline.cpp
+++ b/dom/animation/AnimationTimeline.cpp
@@ -30,58 +30,16 @@ NS_IMPL_CYCLE_COLLECTING_ADDREF(Animatio
 NS_IMPL_CYCLE_COLLECTING_RELEASE(AnimationTimeline)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AnimationTimeline)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 void
-AnimationTimeline::GetAnimations(AnimationSequence& aAnimations)
-{
-  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mWindow);
-  if (mWindow) {
-    nsIDocument* doc = window->GetDoc();
-    if (doc) {
-      doc->FlushPendingNotifications(Flush_Style);
-    }
-  }
-
-  aAnimations.SetCapacity(mAnimations.Count());
-
-  for (Animation* animation = mAnimationOrder.getFirst(); animation;
-       animation = animation->getNext()) {
-
-    // Skip animations which are no longer relevant or which have been
-    // associated with another timeline. These animations will be removed
-    // on the next tick.
-    if (!animation->IsRelevant() || animation->GetTimeline() != this) {
-      continue;
-    }
-
-    // Bug 1174575: Until we implement a suitable PseudoElement interface we
-    // don't have anything to return for the |target| attribute of
-    // KeyframeEffect(ReadOnly) objects that refer to pseudo-elements.
-    // Rather than return some half-baked version of these objects (e.g.
-    // we a null effect attribute) we simply don't provide access to animations
-    // whose effect refers to a pseudo-element until we can support them
-    // properly.
-    Element* target;
-    nsCSSPseudoElements::Type pseudoType;
-    animation->GetEffect()->GetTarget(target, pseudoType);
-    if (pseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement) {
-      aAnimations.AppendElement(animation);
-    }
-  }
-
-  // Sort animations by priority
-  aAnimations.Sort(AnimationPtrComparator<RefPtr<Animation>>());
-}
-
-void
 AnimationTimeline::NotifyAnimationUpdated(Animation& aAnimation)
 {
   if (mAnimations.Contains(&aAnimation)) {
     return;
   }
 
   if (aAnimation.GetTimeline() && aAnimation.GetTimeline() != this) {
     aAnimation.GetTimeline()->RemoveAnimation(&aAnimation);
--- a/dom/animation/AnimationTimeline.h
+++ b/dom/animation/AnimationTimeline.h
@@ -46,21 +46,18 @@ protected:
   }
 
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(AnimationTimeline)
 
   nsIGlobalObject* GetParentObject() const { return mWindow; }
 
-  typedef nsTArray<RefPtr<Animation>> AnimationSequence;
-
   // AnimationTimeline methods
   virtual Nullable<TimeDuration> GetCurrentTime() const = 0;
-  void GetAnimations(AnimationSequence& aAnimations);
 
   // Wrapper functions for AnimationTimeline DOM methods when called from
   // script.
   Nullable<double> GetCurrentTimeAsDouble() const {
     return AnimationUtils::TimeDurationToDouble(GetCurrentTime());
   }
 
   /**
rename from dom/animation/test/css-animations/file_timeline-get-animations.html
rename to dom/animation/test/css-animations/file_document-get-animations.html
--- a/dom/animation/test/css-animations/file_timeline-get-animations.html
+++ b/dom/animation/test/css-animations/file_document-get-animations.html
@@ -19,46 +19,46 @@
   animation: animLeft 100s;
 }
 </style>
 <body>
 <script>
 'use strict';
 
 test(function(t) {
-  assert_equals(document.timeline.getAnimations().length, 0,
+  assert_equals(document.getAnimations().length, 0,
     'getAnimations returns an empty sequence for a document'
     + ' with no animations');
 }, 'getAnimations for non-animated content');
 
 test(function(t) {
   var div = addDiv(t);
 
   // Add an animation
   div.style.animation = 'animLeft 100s';
-  assert_equals(document.timeline.getAnimations().length, 1,
+  assert_equals(document.getAnimations().length, 1,
                 'getAnimations returns a running CSS Animation');
 
   // Add another animation
   div.style.animation = 'animLeft 100s, animTop 100s';
-  assert_equals(document.timeline.getAnimations().length, 2,
+  assert_equals(document.getAnimations().length, 2,
                 'getAnimations returns two running CSS Animations');
 
   // Remove both
   div.style.animation = '';
-  assert_equals(document.timeline.getAnimations().length, 0,
+  assert_equals(document.getAnimations().length, 0,
                 'getAnimations returns no running CSS Animations');
 }, 'getAnimations for CSS Animations');
 
 test(function(t) {
   var div = addDiv(t);
   div.style.animation = 'animLeft 100s, animTop 100s, animRight 100s, ' +
                         'animBottom 100s';
 
-  var animations = document.timeline.getAnimations();
+  var animations = document.getAnimations();
   assert_equals(animations.length, 4,
                 'getAnimations returns all running CSS Animations');
   assert_equals(animations[0].animationName, 'animLeft',
                 'Order of first animation returned');
   assert_equals(animations[1].animationName, 'animTop',
                 'Order of second animation returned');
   assert_equals(animations[2].animationName, 'animRight',
                 'Order of third animation returned');
@@ -67,17 +67,17 @@ test(function(t) {
 }, 'Order of CSS Animations - within an element');
 
 test(function(t) {
   var div1 = addDiv(t, { style: 'animation: animLeft 100s' });
   var div2 = addDiv(t, { style: 'animation: animLeft 100s' });
   var div3 = addDiv(t, { style: 'animation: animLeft 100s' });
   var div4 = addDiv(t, { style: 'animation: animLeft 100s' });
 
-  var animations = document.timeline.getAnimations();
+  var animations = document.getAnimations();
   assert_equals(animations.length, 4,
                 'getAnimations returns all running CSS Animations');
   assert_equals(animations[0].effect.target, div1,
                 'Order of first animation returned');
   assert_equals(animations[1].effect.target, div2,
                 'Order of second animation returned');
   assert_equals(animations[2].effect.target, div3,
                 'Order of third animation returned');
@@ -90,17 +90,17 @@ test(function(t) {
   //       /  |
   //      2   3
   //    /  \
   //   1   4
   //
   // Which should give: 2, 1, 4, 3
   div2.appendChild(div1);
   div2.appendChild(div4);
-  animations = document.timeline.getAnimations();
+  animations = document.getAnimations();
   assert_equals(animations[0].effect.target, div2,
                 'Order of first animation returned after tree surgery');
   assert_equals(animations[1].effect.target, div1,
                 'Order of second animation returned after tree surgery');
   assert_equals(animations[2].effect.target, div4,
                 'Order of third animation returned after tree surgery');
   assert_equals(animations[3].effect.target, div3,
                 'Order of fourth animation returned after tree surgery');
@@ -109,17 +109,17 @@ test(function(t) {
 
 test(function(t) {
   var div1 = addDiv(t, { style: 'animation: animLeft 100s, animTop 100s' });
   var div2 = addDiv(t, { style: 'animation: animBottom 100s' });
 
   var expectedResults = [ [ div1, 'animLeft' ],
                           [ div1, 'animTop' ],
                           [ div2, 'animBottom' ] ];
-  var animations = document.timeline.getAnimations();
+  var animations = document.getAnimations();
   assert_equals(animations.length, expectedResults.length,
                 'getAnimations returns all running CSS Animations');
   animations.forEach(function(anim, i) {
     assert_equals(anim.effect.target, expectedResults[i][0],
                   'Target of animation in position ' + i);
     assert_equals(anim.animationName, expectedResults[i][1],
                   'Name of animation in position ' + i);
   });
@@ -127,139 +127,133 @@ test(function(t) {
   // Modify tree structure and animation list
   div2.appendChild(div1);
   div1.style.animation = 'animLeft 100s, animRight 100s, animTop 100s';
 
   expectedResults = [ [ div2, 'animBottom' ],
                       [ div1, 'animLeft' ],
                       [ div1, 'animRight' ],
                       [ div1, 'animTop' ] ];
-  animations = document.timeline.getAnimations();
+  animations = document.getAnimations();
   assert_equals(animations.length, expectedResults.length,
                 'getAnimations returns all running CSS Animations after ' +
                 'making changes');
   animations.forEach(function(anim, i) {
     assert_equals(anim.effect.target, expectedResults[i][0],
                   'Target of animation in position ' + i + ' after changes');
     assert_equals(anim.animationName, expectedResults[i][1],
                   'Name of animation in position ' + i + ' after changes');
   });
 }, 'Order of CSS Animations - across and within elements');
 
 test(function(t) {
-  cancelAllAnimationsOnEnd(t);
-
   var div = addDiv(t, { style: 'animation: animLeft 100s, animTop 100s' });
-  var animLeft = document.timeline.getAnimations()[0];
+  var animLeft = document.getAnimations()[0];
   assert_equals(animLeft.animationName, 'animLeft',
                 'Originally, animLeft animation comes first');
 
   // Disassociate animLeft from markup and restart
   div.style.animation = 'animTop 100s';
   animLeft.play();
 
-  var animations = document.timeline.getAnimations();
+  var animations = document.getAnimations();
   assert_equals(animations.length, 2,
                 'getAnimations returns markup-bound and free animations');
   assert_equals(animations[0].animationName, 'animTop',
                 'Markup-bound animations come first');
   assert_equals(animations[1], animLeft, 'Free animations come last');
 }, 'Order of CSS Animations - markup-bound vs free animations');
 
 test(function(t) {
-  cancelAllAnimationsOnEnd(t);
-
   var div = addDiv(t, { style: 'animation: animLeft 100s, animTop 100s' });
-  var animLeft = document.timeline.getAnimations()[0];
-  var animTop  = document.timeline.getAnimations()[1];
+  var animLeft = document.getAnimations()[0];
+  var animTop  = document.getAnimations()[1];
 
   // Disassociate both animations from markup and restart in opposite order
   div.style.animation = '';
   animTop.play();
   animLeft.play();
 
-  var animations = document.timeline.getAnimations();
+  var animations = document.getAnimations();
   assert_equals(animations.length, 2,
                 'getAnimations returns free animations');
   assert_equals(animations[0], animTop,
                 'Free animations are returned in the order they are started');
   assert_equals(animations[1], animLeft,
                 'Animations started later are returned later');
 
   // Restarting an animation should have no effect
   animTop.cancel();
   animTop.play();
-  assert_equals(document.timeline.getAnimations()[0], animTop,
+  assert_equals(document.getAnimations()[0], animTop,
                 'After restarting, the ordering of free animations' +
                 ' does not change');
 }, 'Order of CSS Animations - free animations');
 
 test(function(t) {
   // Add an animation first
   var div = addDiv(t, { style: 'animation: animLeft 100s' });
   div.style.top = '0px';
   div.style.transition = 'all 100s';
   flushComputedStyle(div);
 
   // *Then* add a transition
   div.style.top = '100px';
   flushComputedStyle(div);
 
   // Although the transition was added later, it should come first in the list
-  var animations = document.timeline.getAnimations();
+  var animations = document.getAnimations();
   assert_equals(animations.length, 2,
                 'Both CSS animations and transitions are returned');
   assert_class_string(animations[0], 'CSSTransition', 'Transition comes first');
   assert_class_string(animations[1], 'CSSAnimation', 'Animation comes second');
 }, 'Order of CSS Animations and CSS Transitions');
 
 test(function(t) {
   var div = addDiv(t, { style: 'animation: animLeft 100s forwards' });
   div.getAnimations()[0].finish();
-  assert_equals(document.timeline.getAnimations().length, 1,
+  assert_equals(document.getAnimations().length, 1,
                 'Forwards-filling CSS animations are returned');
 }, 'Finished but filling CSS Animations are returned');
 
 test(function(t) {
   var div = addDiv(t, { style: 'animation: animLeft 100s' });
   div.getAnimations()[0].finish();
-  assert_equals(document.timeline.getAnimations().length, 0,
+  assert_equals(document.getAnimations().length, 0,
                 'Non-filling finished CSS animations are not returned');
 }, 'Finished but not filling CSS Animations are not returned');
 
 test(function(t) {
   var div = addDiv(t, { style: 'animation: animLeft 100s 100s' });
-  assert_equals(document.timeline.getAnimations().length, 1,
+  assert_equals(document.getAnimations().length, 1,
                 'Yet-to-start CSS animations are returned');
 }, 'Yet-to-start CSS Animations are returned');
 
 test(function(t) {
   var div = addDiv(t, { style: 'animation: animLeft 100s' });
   div.getAnimations()[0].cancel();
-  assert_equals(document.timeline.getAnimations().length, 0,
+  assert_equals(document.getAnimations().length, 0,
                 'CSS animations cancelled by the API are not returned');
 }, 'CSS Animations cancelled via the API are not returned');
 
 test(function(t) {
-  cancelAllAnimationsOnEnd(t);
-
   var div = addDiv(t, { style: 'animation: animLeft 100s' });
   var anim = div.getAnimations()[0];
   anim.cancel();
   anim.play();
-  assert_equals(document.timeline.getAnimations().length, 1,
+  assert_equals(document.getAnimations().length, 1,
                 'CSS animations cancelled and restarted by the API are ' +
                 'returned');
 }, 'CSS Animations cancelled and restarted via the API are returned');
 
 test(function(t) {
   var div = addDiv(t, { class: 'with-before-animation' });
   // FIXME: This should actually return the animation on the pseudo element
   // but we haven't implemented a PseudoElement interface to use as
   // animation's target (bug 1174575) so we simply don't return these animations
   // until we can support them properly.
-  assert_equals(document.timeline.getAnimations().length, 0,
+  assert_equals(document.getAnimations().length, 0,
                 'CSS animations on pseudo elements are not returned');
 }, 'CSS Animations targetting pseudo-elements are not returned');
 
 done();
 </script>
 </body>
rename from dom/animation/test/css-animations/test_timeline-get-animations.html
rename to dom/animation/test/css-animations/test_document-get-animations.html
--- a/dom/animation/test/css-animations/test_timeline-get-animations.html
+++ b/dom/animation/test/css-animations/test_document-get-animations.html
@@ -4,12 +4,12 @@
 <script src="/resources/testharnessreport.js"></script>
 <div id="log"></div>
 <script>
 'use strict';
 setup({explicit_done: true});
 SpecialPowers.pushPrefEnv(
   { "set": [["dom.animations-api.core.enabled", true]]},
   function() {
-    window.open("file_timeline-get-animations.html");
+    window.open("file_document-get-animations.html");
   });
 </script>
 </html>
rename from dom/animation/test/css-transitions/file_timeline-get-animations.html
rename to dom/animation/test/css-transitions/file_document-get-animations.html
--- a/dom/animation/test/css-transitions/file_timeline-get-animations.html
+++ b/dom/animation/test/css-transitions/file_document-get-animations.html
@@ -1,50 +1,50 @@
 <!doctype html>
 <meta charset=utf-8>
 <script src="../testcommon.js"></script>
 <body>
 <script>
 'use strict';
 
 test(function(t) {
-  assert_equals(document.timeline.getAnimations().length, 0,
+  assert_equals(document.getAnimations().length, 0,
     'getAnimations returns an empty sequence for a document'
     + ' with no animations');
 }, 'getAnimations for non-animated content');
 
 test(function(t) {
   var div = addDiv(t);
 
   // Add a couple of transitions
   div.style.left = '0px';
   div.style.top = '0px';
   getComputedStyle(div).transitionProperty;
 
   div.style.transition = 'all 100s';
   div.style.left = '100px';
   div.style.top = '100px';
-  assert_equals(document.timeline.getAnimations().length, 2,
+  assert_equals(document.getAnimations().length, 2,
                 'getAnimations returns two running CSS Transitions');
 
   // Remove both
   div.style.transitionProperty = 'none';
-  assert_equals(document.timeline.getAnimations().length, 0,
+  assert_equals(document.getAnimations().length, 0,
                 'getAnimations returns no running CSS Transitions');
 }, 'getAnimations for CSS Transitions');
 
 async_test(function(t) {
   var div = addDiv(t, { style: 'left: 0px; transition: all 50ms' });
   flushComputedStyle(div);
 
   div.style.left = '100px';
   var animations = div.getAnimations();
   assert_equals(animations.length, 1, 'Got transition');
   animations[0].finished.then(t.step_func(function() {
-    assert_equals(document.timeline.getAnimations().length, 0,
+    assert_equals(document.getAnimations().length, 0,
                   'No animations returned');
     t.done();
   }));
 }, 'Transitions are not returned after they have finished');
 
 done();
 </script>
 </body>
rename from dom/animation/test/css-transitions/test_timeline-get-animations.html
rename to dom/animation/test/css-transitions/test_document-get-animations.html
--- a/dom/animation/test/css-transitions/test_timeline-get-animations.html
+++ b/dom/animation/test/css-transitions/test_document-get-animations.html
@@ -4,12 +4,12 @@
 <script src="/resources/testharnessreport.js"></script>
 <div id="log"></div>
 <script>
 'use strict';
 setup({explicit_done: true});
 SpecialPowers.pushPrefEnv(
   { "set": [["dom.animations-api.core.enabled", true]]},
   function() {
-    window.open("file_timeline-get-animations.html");
+    window.open("file_document-get-animations.html");
   });
 </script>
 </html>
--- a/dom/animation/test/mochitest.ini
+++ b/dom/animation/test/mochitest.ini
@@ -29,50 +29,50 @@ support-files = css-animations/file_anim
 [css-animations/test_animation-ready.html]
 support-files = css-animations/file_animation-ready.html
 [css-animations/test_animation-reverse.html]
 support-files = css-animations/file_animation-reverse.html
 [css-animations/test_animation-starttime.html]
 support-files = css-animations/file_animation-starttime.html
 [css-animations/test_cssanimation-animationname.html]
 support-files = css-animations/file_cssanimation-animationname.html
-[css-animations/test_keyframeeffect-getframes.html]
-support-files = css-animations/file_keyframeeffect-getframes.html
+[css-animations/test_document-get-animations.html]
+support-files = css-animations/file_document-get-animations.html
 [css-animations/test_effect-target.html]
 support-files = css-animations/file_effect-target.html
 [css-animations/test_element-get-animations.html]
 skip-if = buildapp == 'mulet'
 support-files = css-animations/file_element-get-animations.html
-[css-animations/test_timeline-get-animations.html]
-support-files = css-animations/file_timeline-get-animations.html
+[css-animations/test_keyframeeffect-getframes.html]
+support-files = css-animations/file_keyframeeffect-getframes.html
 [css-transitions/test_animation-cancel.html]
 support-files = css-transitions/file_animation-cancel.html
 [css-transitions/test_animation-computed-timing.html]
 support-files = css-transitions/file_animation-computed-timing.html
 [css-transitions/test_animation-currenttime.html]
 support-files = css-transitions/file_animation-currenttime.html
 [css-transitions/test_animation-finished.html]
 support-files = css-transitions/file_animation-finished.html
 [css-transitions/test_animation-pausing.html]
 support-files = css-transitions/file_animation-pausing.html
 [css-transitions/test_animation-ready.html]
 support-files = css-transitions/file_animation-ready.html
 [css-transitions/test_animation-starttime.html]
 support-files = css-transitions/file_animation-starttime.html
 [css-transitions/test_csstransition-transitionproperty.html]
 support-files = css-transitions/file_csstransition-transitionproperty.html
-[css-transitions/test_keyframeeffect-getframes.html]
-support-files = css-transitions/file_keyframeeffect-getframes.html
+[css-transitions/test_document-get-animations.html]
+support-files = css-transitions/file_document-get-animations.html
 [css-transitions/test_effect-target.html]
 support-files = css-transitions/file_effect-target.html
 [css-transitions/test_element-get-animations.html]
 skip-if = buildapp == 'mulet'
 support-files = css-transitions/file_element-get-animations.html
-[css-transitions/test_timeline-get-animations.html]
-support-files = css-transitions/file_timeline-get-animations.html
+[css-transitions/test_keyframeeffect-getframes.html]
+support-files = css-transitions/file_keyframeeffect-getframes.html
 [document-timeline/test_document-timeline.html]
 support-files = document-timeline/file_document-timeline.html
 [document-timeline/test_request_animation_frame.html]
 skip-if = buildapp == 'mulet'
 [mozilla/test_deferred_start.html]
 support-files = mozilla/file_deferred_start.html
 skip-if = (toolkit == 'gonk' && debug)
 [mozilla/test_hide_and_show.html]
--- a/dom/animation/test/testcommon.js
+++ b/dom/animation/test/testcommon.js
@@ -25,28 +25,16 @@ function addDiv(t, attrs) {
         div.parentNode.removeChild(div);
       }
     });
   }
   return div;
 }
 
 /**
- * Some tests cause animations to continue to exist even after their target
- * element has been removed from the document tree. To ensure that these
- * animations do not impact other tests we should cancel them when the test
- * is complete.
- */
-function cancelAllAnimationsOnEnd(t) {
-  t.add_cleanup(function() {
-    document.timeline.getAnimations().forEach(animation => animation.cancel());
-  });
-}
-
-/**
  * Promise wrapper for requestAnimationFrame.
  */
 function waitForFrame() {
   return new Promise(function(resolve, reject) {
     window.requestAnimationFrame(resolve);
   });
 }
 
--- a/dom/apps/AppsUtils.jsm
+++ b/dom/apps/AppsUtils.jsm
@@ -782,17 +782,17 @@ this.AppsUtils = {
     // We're passing false to get the binary hash and not base64.
     let hash = hasher.finish(false);
 
     function toHexString(charCode) {
       return ("0" + charCode.toString(16)).slice(-2);
     }
 
     // Convert the binary hash data to a hex string.
-    return [toHexString(hash.charCodeAt(i)) for (i in hash)].join("");
+    return Array.from(hash, (c, i) => toHexString(hash.charCodeAt(i))).join("");
   },
 
   // Returns the hash for a JS object.
   computeObjectHash: function(aObject) {
     return this.computeHash(JSON.stringify(aObject));
   },
 
   getAppManifestURLFromWindow: function(aWindow) {
--- a/dom/apps/Webapps.jsm
+++ b/dom/apps/Webapps.jsm
@@ -3626,17 +3626,17 @@ this.DOMApplicationRegistry = {
       // Return the two-digit hexadecimal code for a byte.
       function toHexString(charCode) {
         return ("0" + charCode.toString(16)).slice(-2);
       }
 
       // We're passing false to get the binary hash and not base64.
       let data = hasher.finish(false);
       // Convert the binary hash data to a hex string.
-      let hash = [toHexString(data.charCodeAt(i)) for (i in data)].join("");
+      let hash = Array.from(data, (c, i) => toHexString(data.charCodeAt(i))).join("");
       debug("File hash computed: " + hash);
 
       deferred.resolve(hash);
     });
 
     return deferred.promise;
   },
 
--- a/dom/base/ChromeUtils.cpp
+++ b/dom/base/ChromeUtils.cpp
@@ -65,10 +65,28 @@ ChromeUtils::OriginAttributesMatchPatter
                                           const dom::OriginAttributesDictionary& aAttrs,
                                           const dom::OriginAttributesPatternDictionary& aPattern)
 {
   GenericOriginAttributes attrs(aAttrs);
   OriginAttributesPattern pattern(aPattern);
   return pattern.Matches(attrs);
 }
 
+/* static */ void
+ChromeUtils::CreateOriginAttributesWithUserContextId(dom::GlobalObject& aGlobal,
+                                                     const nsAString& aOrigin,
+                                                     uint32_t aUserContextId,
+                                                     dom::OriginAttributesDictionary& aAttrs,
+                                                     ErrorResult& aRv)
+{
+  GenericOriginAttributes attrs;
+  nsAutoCString suffix;
+  if (!attrs.PopulateFromOrigin(NS_ConvertUTF16toUTF8(aOrigin), suffix)) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return;
+  }
+
+  attrs.mUserContextId = aUserContextId;
+  aAttrs = attrs;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/base/ChromeUtils.h
+++ b/dom/base/ChromeUtils.h
@@ -52,14 +52,21 @@ public:
   OriginAttributesToSuffix(dom::GlobalObject& aGlobal,
                            const dom::OriginAttributesDictionary& aAttrs,
                            nsCString& aSuffix);
 
   static bool
   OriginAttributesMatchPattern(dom::GlobalObject& aGlobal,
                                const dom::OriginAttributesDictionary& aAttrs,
                                const dom::OriginAttributesPatternDictionary& aPattern);
+
+  static void
+  CreateOriginAttributesWithUserContextId(dom::GlobalObject& aGlobal,
+                                          const nsAString& aOrigin,
+                                          uint32_t aUserContextId,
+                                          dom::OriginAttributesDictionary& aAttrs,
+                                          ErrorResult& aRv);
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_ChromeUtils__
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -3313,16 +3313,23 @@ Element::MozRequestPointerLock()
 void
 Element::GetAnimations(nsTArray<RefPtr<Animation>>& aAnimations)
 {
   nsIDocument* doc = GetComposedDoc();
   if (doc) {
     doc->FlushPendingNotifications(Flush_Style);
   }
 
+  GetAnimationsUnsorted(aAnimations);
+  aAnimations.Sort(AnimationPtrComparator<RefPtr<Animation>>());
+}
+
+void
+Element::GetAnimationsUnsorted(nsTArray<RefPtr<Animation>>& aAnimations)
+{
   EffectSet* effects = EffectSet::GetEffectSet(this,
                          nsCSSPseudoElements::ePseudo_NotPseudoElement);
   if (!effects) {
     return;
   }
 
   for (KeyframeEffectReadOnly* effect : *effects) {
     MOZ_ASSERT(effect && effect->GetAnimation(),
@@ -3330,18 +3337,16 @@ Element::GetAnimations(nsTArray<RefPtr<A
                "added to an element's effect set");
     Animation* animation = effect->GetAnimation();
 
     MOZ_ASSERT(animation->IsRelevant(),
                "Only relevant animations should be added to an element's "
                "effect set");
     aAnimations.AppendElement(animation);
   }
-
-  aAnimations.Sort(AnimationPtrComparator<RefPtr<Animation>>());
 }
 
 NS_IMETHODIMP
 Element::GetInnerHTML(nsAString& aInnerHTML)
 {
   GetMarkup(false, aInnerHTML);
   return NS_OK;
 }
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -816,17 +816,19 @@ public:
   {
     return false;
   }
 
   virtual void SetUndoScope(bool aUndoScope, ErrorResult& aError)
   {
   }
 
+  // Note: GetAnimations will flush style while GetAnimationsUnsorted won't.
   void GetAnimations(nsTArray<RefPtr<Animation>>& aAnimations);
+  void GetAnimationsUnsorted(nsTArray<RefPtr<Animation>>& aAnimations);
 
   NS_IMETHOD GetInnerHTML(nsAString& aInnerHTML);
   virtual void SetInnerHTML(const nsAString& aInnerHTML, ErrorResult& aError);
   void GetOuterHTML(nsAString& aOuterHTML);
   void SetOuterHTML(const nsAString& aOuterHTML, ErrorResult& aError);
   void InsertAdjacentHTML(const nsAString& aPosition, const nsAString& aText,
                           ErrorResult& aError);
 
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -283,16 +283,18 @@ nsHtml5StringParser* nsContentUtils::sHT
 nsIParser* nsContentUtils::sXMLFragmentParser = nullptr;
 nsIFragmentContentSink* nsContentUtils::sXMLFragmentSink = nullptr;
 bool nsContentUtils::sFragmentParsingActive = false;
 
 #if !(defined(DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
 bool nsContentUtils::sDOMWindowDumpEnabled;
 #endif
 
+mozilla::LazyLogModule nsContentUtils::sDOMDumpLog("Dump");
+
 // Subset of http://www.whatwg.org/specs/web-apps/current-work/#autofill-field-name
 enum AutocompleteFieldName
 {
   #define AUTOCOMPLETE_FIELD_NAME(name_, value_) \
     eAutocompleteFieldName_##name_,
   #define AUTOCOMPLETE_CONTACT_FIELD_NAME(name_, value_) \
     AUTOCOMPLETE_FIELD_NAME(name_, value_)
   #include "AutocompleteFieldList.h"
@@ -7111,16 +7113,22 @@ nsContentUtils::DOMWindowDumpEnabled()
   // enable output from dump() or not, in debug builds it's always
   // enabled.
   return nsContentUtils::sDOMWindowDumpEnabled;
 #else
   return true;
 #endif
 }
 
+mozilla::LogModule*
+nsContentUtils::DOMDumpLog()
+{
+  return sDOMDumpLog;
+}
+
 bool
 nsContentUtils::GetNodeTextContent(nsINode* aNode, bool aDeep, nsAString& aResult,
                                    const fallible_t& aFallible)
 {
   aResult.Truncate();
   return AppendNodeTextContent(aNode, aDeep, aResult, aFallible);
 }
 
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -26,16 +26,17 @@
 #include "nsContentListDeclarations.h"
 #include "nsMathUtils.h"
 #include "nsTArrayForwardDeclare.h"
 #include "Units.h"
 #include "mozilla/dom/AutocompleteInfoBinding.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/net/ReferrerPolicy.h"
+#include "mozilla/Logging.h"
 #include "nsIContentPolicy.h"
 
 #if defined(XP_WIN)
 // Undefine LoadImage to prevent naming conflict with Windows.
 #undef LoadImage
 #endif
 
 class imgICache;
@@ -2360,21 +2361,28 @@ public:
    * @param aVersion the version ("1.0", "2.0", ...)
    * @return whether the feature is supported or not
    */
   static bool InternalIsSupported(nsISupports* aObject,
                                   const nsAString& aFeature,
                                   const nsAString& aVersion);
 
   /**
-   * Return true if the browser.dom.window.dump.enabled pref is set.
+   * Returns true if the browser.dom.window.dump.enabled pref is set.
    */
   static bool DOMWindowDumpEnabled();
 
   /**
+   * Returns a LogModule that dump calls from content script are logged to.
+   * This can be enabled with the 'Dump' module, and is useful for synchronizing
+   * content JS to other logging modules.
+   */
+  static mozilla::LogModule* DOMDumpLog();
+
+  /**
    * Returns whether a content is an insertion point for XBL
    * bindings or web components ShadowRoot. In web components,
    * this corresponds to a <content> element that participates
    * in node distribution. In XBL this corresponds to an
    * <xbl:children> element in anonymous content.
    *
    * @param aContent The content to test for being an insertion point.
    */
@@ -2717,16 +2725,17 @@ private:
   static nsString* sMetaText;
   static nsString* sOSText;
   static nsString* sAltText;
   static nsString* sModifierSeparator;
 
 #if !(defined(DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
   static bool sDOMWindowDumpEnabled;
 #endif
+  static mozilla::LazyLogModule sDOMDumpLog;
 };
 
 class MOZ_RAII nsAutoScriptBlocker {
 public:
   explicit nsAutoScriptBlocker(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM) {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     nsContentUtils::AddScriptBlocker();
   }
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -5,20 +5,22 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /*
  * Base class for all our document implementations.
  */
 
 #include "nsDocument.h"
 
+#include "mozilla/AnimationComparator.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/BinarySearch.h"
 #include "mozilla/DebugOnly.h"
+#include "mozilla/EffectSet.h"
 #include "mozilla/IntegerRange.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Likely.h"
 #include <algorithm>
 
 #include "mozilla/Logging.h"
 #include "plstr.h"
 #include "prprf.h"
@@ -3129,16 +3131,42 @@ nsDocument::Timeline()
 {
   if (!mDocumentTimeline) {
     mDocumentTimeline = new DocumentTimeline(this);
   }
 
   return mDocumentTimeline;
 }
 
+void
+nsDocument::GetAnimations(nsTArray<RefPtr<Animation>>& aAnimations)
+{
+  FlushPendingNotifications(Flush_Style);
+
+  // Bug 1174575: Until we implement a suitable PseudoElement interface we
+  // don't have anything to return for the |target| attribute of
+  // KeyframeEffect(ReadOnly) objects that refer to pseudo-elements.
+  // Rather than return some half-baked version of these objects (e.g.
+  // we a null effect attribute) we simply don't provide access to animations
+  // whose effect refers to a pseudo-element until we can support them
+  // properly.
+  for (nsIContent* node = nsINode::GetFirstChild();
+       node;
+       node = node->GetNextNode(this)) {
+    if (!node->IsElement()) {
+      continue;
+    }
+
+    node->AsElement()->GetAnimationsUnsorted(aAnimations);
+  }
+
+  // Sort animations by priority
+  aAnimations.Sort(AnimationPtrComparator<RefPtr<Animation>>());
+}
+
 /* Return true if the document is in the focused top-level window, and is an
  * ancestor of the focused DOMWindow. */
 NS_IMETHODIMP
 nsDocument::HasFocus(bool* aResult)
 {
   ErrorResult rv;
   *aResult = nsIDocument::HasFocus(rv);
   return rv.StealNSResult();
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -782,16 +782,18 @@ public:
   virtual void DeleteShell() override;
 
   virtual nsresult GetAllowPlugins(bool* aAllowPlugins) override;
 
   virtual already_AddRefed<mozilla::dom::UndoManager> GetUndoManager() override;
 
   static bool IsWebAnimationsEnabled(JSContext* aCx, JSObject* aObject);
   virtual mozilla::dom::DocumentTimeline* Timeline() override;
+  virtual void GetAnimations(
+      nsTArray<RefPtr<mozilla::dom::Animation>>& aAnimations) override;
 
   virtual nsresult SetSubDocumentFor(Element* aContent,
                                      nsIDocument* aSubDoc) override;
   virtual nsIDocument* GetSubDocumentFor(nsIContent* aContent) const override;
   virtual Element* FindContentForSubDocument(nsIDocument *aDocument) const override;
   virtual Element* GetRootElementInternal() const override;
 
   virtual void EnsureOnDemandBuiltInUASheet(mozilla::CSSStyleSheet* aSheet) override;
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -1794,20 +1794,23 @@ nsFrameLoader::MaybeCreateDocShell()
     if (mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::usercontextid)) {
       mOwnerContent->GetAttr(kNameSpaceID_None,
                              nsGkAtoms::usercontextid,
                              userContextIdStr);
     }
   }
 
   if (!userContextIdStr.IsEmpty()) {
-    nsresult err;
-    nsDocShell * ds = nsDocShell::Cast(mDocShell);
-    ds->SetUserContextId(userContextIdStr.ToInteger(&err));
-    NS_ENSURE_SUCCESS(err, err);
+    nsresult rv;
+    uint32_t userContextId =
+      static_cast<uint32_t>(userContextIdStr.ToInteger(&rv));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = mDocShell->SetUserContextId(userContextId);
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // Inform our docShell that it has a new child.
   // Note: This logic duplicates a lot of logic in
   // nsSubDocumentFrame::AttributeChanged.  We should fix that.
 
   int32_t parentType = docShell->ItemType();
 
@@ -3080,29 +3083,27 @@ nsFrameLoader::GetNewTabContext(MutableT
   }
   attrs.mAppId = appId;
 
   // Populate packageId to signedPkg.
   attrs.mSignedPkg = NS_ConvertUTF8toUTF16(aPackageId);
 
   // set the userContextId on the attrs before we pass them into
   // the tab context
-  if (mOwnerContent) {
-    nsAutoString userContextIdStr;
-    if (mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::usercontextid)) {
-      mOwnerContent->GetAttr(kNameSpaceID_None,
-                             nsGkAtoms::usercontextid,
-                             userContextIdStr);
-    }
-    if (!userContextIdStr.IsEmpty()) {
-      nsresult err;
-      uint32_t userContextId = userContextIdStr.ToInteger(&err);
-      NS_ENSURE_SUCCESS(err, err);
-      attrs.mUserContextId = userContextId;
-    }
+  nsAutoString userContextIdStr;
+  if (mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::usercontextid)) {
+    mOwnerContent->GetAttr(kNameSpaceID_None,
+                           nsGkAtoms::usercontextid,
+                           userContextIdStr);
+  }
+  if (!userContextIdStr.IsEmpty()) {
+    nsresult err;
+    uint32_t userContextId = userContextIdStr.ToInteger(&err);
+    NS_ENSURE_SUCCESS(err, err);
+    attrs.mUserContextId = userContextId;
   }
 
   bool tabContextUpdated =
     aTabContext->SetTabContext(ownApp, containingApp, attrs, signedPkgOrigin);
   NS_ENSURE_STATE(tabContextUpdated);
 
   return NS_OK;
 }
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -6245,16 +6245,17 @@ nsGlobalWindow::Dump(const nsAString& aS
   while (c < cEnd) {
     if (*c == '\r')
       *c = '\n';
     c++;
   }
 #endif
 
   if (cstr) {
+    MOZ_LOG(nsContentUtils::DOMDumpLog(), LogLevel::Debug, ("[Window.Dump] %s", cstr));
 #ifdef XP_WIN
     PrintToDebugger(cstr);
 #endif
 #ifdef ANDROID
     __android_log_write(ANDROID_LOG_INFO, "GeckoDump", cstr);
 #endif
     FILE *fp = gDumpFile ? gDumpFile : stdout;
     fputs(cstr, fp);
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -98,16 +98,17 @@ template<typename> class OwningNonNull;
 
 namespace css {
 class Loader;
 class ImageLoader;
 class Rule;
 } // namespace css
 
 namespace dom {
+class Animation;
 class AnonymousContent;
 class Attr;
 class BoxObject;
 class CDATASection;
 class Comment;
 struct CustomElementDefinition;
 class DocumentFragment;
 class DocumentTimeline;
@@ -2151,16 +2152,19 @@ public:
    * @return the element associated with |aId|
    */
   virtual Element* LookupImageElement(const nsAString& aElementId) = 0;
 
   virtual already_AddRefed<mozilla::dom::UndoManager> GetUndoManager() = 0;
 
   virtual mozilla::dom::DocumentTimeline* Timeline() = 0;
 
+  virtual void GetAnimations(
+      nsTArray<RefPtr<mozilla::dom::Animation>>& aAnimations) = 0;
+
   nsresult ScheduleFrameRequestCallback(mozilla::dom::FrameRequestCallback& aCallback,
                                         int32_t *aHandle);
   void CancelFrameRequestCallback(int32_t aHandle);
 
   typedef nsTArray<RefPtr<mozilla::dom::FrameRequestCallback>> FrameRequestCallbackList;
   /**
    * Put this document's frame request callbacks into the provided
    * list, and forget about them.
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -88,45 +88,47 @@ ThrowErrorMessage(JSContext* aCx, const 
   JS_ReportErrorNumberVA(aCx, GetErrorMessage, nullptr,
                          static_cast<const unsigned>(aErrorNumber), ap);
   va_end(ap);
   return false;
 }
 
 bool
 ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs,
-                 const ErrNum aErrorNumber,
-                 const char* aInterfaceName)
+                 bool aSecurityError, const char* aInterfaceName)
 {
   NS_ConvertASCIItoUTF16 ifaceName(aInterfaceName);
   // This should only be called for DOM methods/getters/setters, which
   // are JSNative-backed functions, so we can assume that
   // JS_ValueToFunction and JS_GetFunctionDisplayId will both return
   // non-null and that JS_GetStringCharsZ returns non-null.
   JS::Rooted<JSFunction*> func(aCx, JS_ValueToFunction(aCx, aArgs.calleev()));
   MOZ_ASSERT(func);
   JS::Rooted<JSString*> funcName(aCx, JS_GetFunctionDisplayId(func));
   MOZ_ASSERT(funcName);
   nsAutoJSString funcNameStr;
   if (!funcNameStr.init(aCx, funcName)) {
     return false;
   }
-  MOZ_RELEASE_ASSERT(GetErrorArgCount(aErrorNumber) <= 2);
+  const ErrNum errorNumber = aSecurityError ?
+                             MSG_METHOD_THIS_UNWRAPPING_DENIED :
+                             MSG_METHOD_THIS_DOES_NOT_IMPLEMENT_INTERFACE;
+  MOZ_RELEASE_ASSERT(GetErrorArgCount(errorNumber) <= 2);
   JS_ReportErrorNumberUC(aCx, GetErrorMessage, nullptr,
-                         static_cast<const unsigned>(aErrorNumber),
+                         static_cast<const unsigned>(errorNumber),
                          funcNameStr.get(), ifaceName.get());
   return false;
 }
 
 bool
 ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs,
-                 const ErrNum aErrorNumber,
+                 bool aSecurityError,
                  prototypes::ID aProtoId)
 {
-  return ThrowInvalidThis(aCx, aArgs, aErrorNumber,
+  return ThrowInvalidThis(aCx, aArgs, aSecurityError,
                           NamesOfInterfacesWithProtos(aProtoId));
 }
 
 bool
 ThrowNoSetterArg(JSContext* aCx, prototypes::ID aProtoId)
 {
   nsPrintfCString errorMessage("%s attribute setter",
                                NamesOfInterfacesWithProtos(aProtoId));
@@ -2622,28 +2624,26 @@ EnforceNotInPrerendering(JSContext* aCx,
 
 bool
 GenericBindingGetter(JSContext* cx, unsigned argc, JS::Value* vp)
 {
   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
   const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
   prototypes::ID protoID = static_cast<prototypes::ID>(info->protoID);
   if (!args.thisv().isObject()) {
-    return ThrowInvalidThis(cx, args,
-                            MSG_GETTER_THIS_DOES_NOT_IMPLEMENT_INTERFACE,
-                            protoID);
+    return ThrowInvalidThis(cx, args, false, protoID);
   }
   JS::Rooted<JSObject*> obj(cx, &args.thisv().toObject());
 
   void* self;
   {
     nsresult rv = UnwrapObject<void>(obj, self, protoID, info->depth);
     if (NS_FAILED(rv)) {
       return ThrowInvalidThis(cx, args,
-                              GetInvalidThisErrorForGetter(rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO),
+                              rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO,
                               protoID);
     }
   }
 
   MOZ_ASSERT(info->type() == JSJitInfo::Getter);
   JSJitGetterOp getter = info->getter;
   bool ok = getter(cx, obj, self, JSJitGetterCallArgs(args));
 #ifdef DEBUG
@@ -2656,28 +2656,26 @@ GenericBindingGetter(JSContext* cx, unsi
 
 bool
 GenericBindingSetter(JSContext* cx, unsigned argc, JS::Value* vp)
 {
   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
   const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
   prototypes::ID protoID = static_cast<prototypes::ID>(info->protoID);
   if (!args.thisv().isObject()) {
-    return ThrowInvalidThis(cx, args,
-                            MSG_SETTER_THIS_DOES_NOT_IMPLEMENT_INTERFACE,
-                            protoID);
+    return ThrowInvalidThis(cx, args, false, protoID);
   }
   JS::Rooted<JSObject*> obj(cx, &args.thisv().toObject());
 
   void* self;
   {
     nsresult rv = UnwrapObject<void>(obj, self, protoID, info->depth);
     if (NS_FAILED(rv)) {
       return ThrowInvalidThis(cx, args,
-                              GetInvalidThisErrorForSetter(rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO),
+                              rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO,
                               protoID);
     }
   }
   if (args.length() == 0) {
     return ThrowNoSetterArg(cx, protoID);
   }
   MOZ_ASSERT(info->type() == JSJitInfo::Setter);
   JSJitSetterOp setter = info->setter;
@@ -2693,28 +2691,26 @@ GenericBindingSetter(JSContext* cx, unsi
 
 bool
 GenericBindingMethod(JSContext* cx, unsigned argc, JS::Value* vp)
 {
   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
   const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
   prototypes::ID protoID = static_cast<prototypes::ID>(info->protoID);
   if (!args.thisv().isObject()) {
-    return ThrowInvalidThis(cx, args,
-                            MSG_METHOD_THIS_DOES_NOT_IMPLEMENT_INTERFACE,
-                            protoID);
+    return ThrowInvalidThis(cx, args, false, protoID);
   }
   JS::Rooted<JSObject*> obj(cx, &args.thisv().toObject());
 
   void* self;
   {
     nsresult rv = UnwrapObject<void>(obj, self, protoID, info->depth);
     if (NS_FAILED(rv)) {
       return ThrowInvalidThis(cx, args,
-                              GetInvalidThisErrorForMethod(rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO),
+                              rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO,
                               protoID);
     }
   }
   MOZ_ASSERT(info->type() == JSJitInfo::Method);
   JSJitMethodOp method = info->method;
   bool ok = method(cx, obj, self, JSJitMethodCallArgs(args));
 #ifdef DEBUG
   if (ok) {
@@ -2731,30 +2727,27 @@ GenericPromiseReturningBindingMethod(JSC
   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
   JS::Rooted<JSObject*> callee(cx, &args.callee());
 
   // We could invoke GenericBindingMethod here, but that involves an
   // extra call.  Manually inline it instead.
   const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
   prototypes::ID protoID = static_cast<prototypes::ID>(info->protoID);
   if (!args.thisv().isObject()) {
-    ThrowInvalidThis(cx, args,
-                     MSG_METHOD_THIS_DOES_NOT_IMPLEMENT_INTERFACE,
-                     protoID);
+    ThrowInvalidThis(cx, args, false, protoID);
     return ConvertExceptionToPromise(cx, xpc::XrayAwareCalleeGlobal(callee),
                                      args.rval());
   }
   JS::Rooted<JSObject*> obj(cx, &args.thisv().toObject());
 
   void* self;
   {
     nsresult rv = UnwrapObject<void>(obj, self, protoID, info->depth);
     if (NS_FAILED(rv)) {
-      ThrowInvalidThis(cx, args,
-                       GetInvalidThisErrorForMethod(rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO),
+      ThrowInvalidThis(cx, args, rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO,
                        protoID);
       return ConvertExceptionToPromise(cx, xpc::XrayAwareCalleeGlobal(callee),
                                        args.rval());
     }
   }
   MOZ_ASSERT(info->type() == JSJitInfo::Method);
   JSJitMethodOp method = info->method;
   bool ok = method(cx, obj, self, JSJitMethodCallArgs(args));
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -56,46 +56,23 @@ UnwrapArgImpl(JS::Handle<JSObject*> src,
 template <class Interface>
 inline nsresult
 UnwrapArg(JS::Handle<JSObject*> src, Interface** ppArg)
 {
   return UnwrapArgImpl(src, NS_GET_TEMPLATE_IID(Interface),
                        reinterpret_cast<void**>(ppArg));
 }
 
-inline const ErrNum
-GetInvalidThisErrorForMethod(bool aSecurityError)
-{
-  return aSecurityError ? MSG_METHOD_THIS_UNWRAPPING_DENIED :
-                          MSG_METHOD_THIS_DOES_NOT_IMPLEMENT_INTERFACE;
-}
-
-inline const ErrNum
-GetInvalidThisErrorForGetter(bool aSecurityError)
-{
-  return aSecurityError ? MSG_GETTER_THIS_UNWRAPPING_DENIED :
-                          MSG_GETTER_THIS_DOES_NOT_IMPLEMENT_INTERFACE;
-}
-
-inline const ErrNum
-GetInvalidThisErrorForSetter(bool aSecurityError)
-{
-  return aSecurityError ? MSG_SETTER_THIS_UNWRAPPING_DENIED :
-                          MSG_SETTER_THIS_DOES_NOT_IMPLEMENT_INTERFACE;
-}
+bool
+ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs,
+                 bool aSecurityError, const char* aInterfaceName);
 
 bool
 ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs,
-                 const ErrNum aErrorNumber,
-                 const char* aInterfaceName);
-
-bool
-ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs,
-                 const ErrNum aErrorNumber,
-                 prototypes::ID aProtoId);
+                 bool aSecurityError, prototypes::ID aProtoId);
 
 // Returns true if the JSClass is used for DOM objects.
 inline bool
 IsDOMClass(const JSClass* clasp)
 {
   return clasp->flags & JSCLASS_IS_DOMJSCLASS;
 }
 
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -8100,17 +8100,17 @@ class CGGenericMethod(CGAbstractBindingM
     """
     A class for generating the C++ code for an IDL method.
 
     If allowCrossOriginThis is true, then this-unwrapping will first do an
     UncheckedUnwrap and after that operate on the result.
     """
     def __init__(self, descriptor, allowCrossOriginThis=False):
         unwrapFailureCode = (
-            'return ThrowInvalidThis(cx, args, GetInvalidThisErrorForMethod(%%(securityError)s), "%s");\n' %
+            'return ThrowInvalidThis(cx, args, %%(securityError)s, "%s");\n' %
             descriptor.interface.identifier.name)
         name = "genericCrossOriginMethod" if allowCrossOriginThis else "genericMethod"
         CGAbstractBindingMethod.__init__(self, descriptor, name,
                                          JSNativeArguments(),
                                          unwrapFailureCode=unwrapFailureCode,
                                          allowCrossOriginThis=allowCrossOriginThis)
 
     def generate_code(self):
@@ -8131,17 +8131,17 @@ class CGGenericMethod(CGAbstractBindingM
 class CGGenericPromiseReturningMethod(CGAbstractBindingMethod):
     """
     A class for generating the C++ code for an IDL method that returns a Promise.
 
     Does not handle cross-origin this.
     """
     def __init__(self, descriptor):
         unwrapFailureCode = dedent("""
-            ThrowInvalidThis(cx, args, GetInvalidThisErrorForMethod(%%(securityError)s), "%s");\n
+            ThrowInvalidThis(cx, args, %%(securityError)s, "%s");\n
             return ConvertExceptionToPromise(cx, xpc::XrayAwareCalleeGlobal(callee),
                                              args.rval());\n""" %
                                    descriptor.interface.identifier.name)
 
         name = "genericPromiseReturningMethod"
         customCallArgs = dedent("""
             JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
             // Make sure to save the callee before someone maybe messes with rval().
@@ -8472,17 +8472,17 @@ class CGGenericGetter(CGAbstractBindingM
                 return true;
                 """)
         else:
             if allowCrossOriginThis:
                 name = "genericCrossOriginGetter"
             else:
                 name = "genericGetter"
             unwrapFailureCode = (
-                'return ThrowInvalidThis(cx, args, GetInvalidThisErrorForGetter(%%(securityError)s), "%s");\n' %
+                'return ThrowInvalidThis(cx, args, %%(securityError)s, "%s");\n' %
                 descriptor.interface.identifier.name)
         CGAbstractBindingMethod.__init__(self, descriptor, name, JSNativeArguments(),
                                          unwrapFailureCode,
                                          allowCrossOriginThis=allowCrossOriginThis)
 
     def generate_code(self):
         return CGGeneric(dedent("""
             const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
@@ -8603,17 +8603,17 @@ class CGGenericSetter(CGAbstractBindingM
                 return true;
                 """)
         else:
             if allowCrossOriginThis:
                 name = "genericCrossOriginSetter"
             else:
                 name = "genericSetter"
             unwrapFailureCode = (
-                'return ThrowInvalidThis(cx, args, GetInvalidThisErrorForSetter(%%(securityError)s), "%s");\n' %
+                'return ThrowInvalidThis(cx, args, %%(securityError)s, "%s");\n' %
                 descriptor.interface.identifier.name)
 
         CGAbstractBindingMethod.__init__(self, descriptor, name, JSNativeArguments(),
                                          unwrapFailureCode,
                                          allowCrossOriginThis=allowCrossOriginThis)
 
     def generate_code(self):
         return CGGeneric(fill(
--- a/dom/bindings/Errors.msg
+++ b/dom/bindings/Errors.msg
@@ -24,20 +24,16 @@
 
 MSG_DEF(MSG_INVALID_ENUM_VALUE, 3, JSEXN_TYPEERR, "{0} '{1}' is not a valid value for enumeration {2}.")
 MSG_DEF(MSG_MISSING_ARGUMENTS, 1, JSEXN_TYPEERR, "Not enough arguments to {0}.")
 MSG_DEF(MSG_NOT_OBJECT, 1, JSEXN_TYPEERR, "{0} is not an object.")
 MSG_DEF(MSG_NOT_CALLABLE, 1, JSEXN_TYPEERR, "{0} is not callable.")
 MSG_DEF(MSG_DOES_NOT_IMPLEMENT_INTERFACE, 2, JSEXN_TYPEERR, "{0} does not implement interface {1}.")
 MSG_DEF(MSG_METHOD_THIS_DOES_NOT_IMPLEMENT_INTERFACE, 2, JSEXN_TYPEERR, "'{0}' called on an object that does not implement interface {1}.")
 MSG_DEF(MSG_METHOD_THIS_UNWRAPPING_DENIED, 1, JSEXN_TYPEERR, "Permission to call '{0}' denied.")
-MSG_DEF(MSG_GETTER_THIS_DOES_NOT_IMPLEMENT_INTERFACE, 2, JSEXN_TYPEERR, "'{0}' getter called on an object that does not implement interface {1}.")
-MSG_DEF(MSG_GETTER_THIS_UNWRAPPING_DENIED, 1, JSEXN_TYPEERR, "Permission to call '{0}' getter denied.")
-MSG_DEF(MSG_SETTER_THIS_DOES_NOT_IMPLEMENT_INTERFACE, 2, JSEXN_TYPEERR, "'{0}' setter called on an object that does not implement interface {1}.")
-MSG_DEF(MSG_SETTER_THIS_UNWRAPPING_DENIED, 1, JSEXN_TYPEERR, "Permission to call '{0}' setter denied.")
 MSG_DEF(MSG_THIS_DOES_NOT_IMPLEMENT_INTERFACE, 1, JSEXN_TYPEERR, "\"this\" object does not implement interface {0}.")
 MSG_DEF(MSG_NOT_IN_UNION, 2, JSEXN_TYPEERR, "{0} could not be converted to any of: {1}.")
 MSG_DEF(MSG_ILLEGAL_CONSTRUCTOR, 0, JSEXN_TYPEERR, "Illegal constructor.")
 MSG_DEF(MSG_CONSTRUCTOR_WITHOUT_NEW, 1, JSEXN_TYPEERR, "Constructor {0} requires 'new'")
 MSG_DEF(MSG_ENFORCE_RANGE_NON_FINITE, 1, JSEXN_TYPEERR, "Non-finite value is out of range for {0}.")
 MSG_DEF(MSG_ENFORCE_RANGE_OUT_OF_RANGE, 1, JSEXN_TYPEERR, "Value is out of range for {0}.")
 MSG_DEF(MSG_NOT_SEQUENCE, 1, JSEXN_TYPEERR, "{0} can't be converted to a sequence.")
 MSG_DEF(MSG_NOT_DICTIONARY, 1, JSEXN_TYPEERR, "{0} can't be converted to a dictionary.")
--- a/dom/bindings/test/test_exception_messages.html
+++ b/dom/bindings/test/test_exception_messages.html
@@ -13,20 +13,20 @@ https://bugzilla.mozilla.org/show_bug.cg
   /** Test for Bug 882653 **/
   // Each test is a string to eval, the expected exception message, and the
   // test description.
   var tests = [
       [ 'document.documentElement.appendChild.call({}, new Image())',
         "'appendChild' called on an object that does not implement interface Node.",
         "bogus method this object" ],
       [ 'Object.getOwnPropertyDescriptor(Document.prototype, "documentElement").get.call({})',
-        "'documentElement' getter called on an object that does not implement interface Document.",
+        "'get documentElement' called on an object that does not implement interface Document.",
         "bogus getter this object" ],
       [ 'Object.getOwnPropertyDescriptor(Element.prototype, "innerHTML").set.call({})',
-        "'innerHTML' setter called on an object that does not implement interface Element.",
+        "'set innerHTML' called on an object that does not implement interface Element.",
         "bogus setter this object" ],
       [ 'document.documentElement.appendChild(5)',
         "Argument 1 of Node.appendChild is not an object.",
         "bogus interface argument" ],
       [ 'document.documentElement.appendChild(null)',
         "Argument 1 of Node.appendChild is not an object.",
         "null interface argument" ],
       [ 'document.createTreeWalker(document).currentNode = 5',
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..d7f6a0ccf47fbc30e936b47e3f4d2cf8f4a90a16
GIT binary patch
literal 16521
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
DFy$Y2
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/browserElement_MultipleAudioChannels.js
@@ -0,0 +1,136 @@
+"use strict";
+
+SimpleTest.waitForExplicitFinish();
+
+var tests = [false /* INPROC */, true /* OOP */];
+var rootURI = "http://test/chrome/dom/browser-element/mochitest/";
+var manifestURI = rootURI + "multipleAudioChannels_manifest.webapp";
+var srcURI = rootURI +  "file_browserElement_MultipleAudioChannels.html";
+var generator = startTest();
+var app = null;
+var channelsNum = 2;
+
+addLoadEvent(() => {
+  SpecialPowers.pushPermissions(
+    [{ "type": "webapps-manage", "allow": 1, "context": document },
+     { "type": "browser", "allow": 1, "context": document },
+     { "type": "embed-apps", "allow": 1, "context": document }],
+    function() {
+      SpecialPowers.pushPrefEnv(
+        {'set': [["dom.mozBrowserFramesEnabled", true],
+                 ["dom.webapps.useCurrentProfile", true],
+                 ["media.useAudioChannelAPI", true],
+                 ["b2g.system_manifest_url", "http://mochi.test:8888/manifest.webapp"]]},
+        () => { generator.next(); })
+    });
+});
+
+function error(message) {
+  ok(false, message);
+  SimpleTest.finish();
+}
+
+function continueTest() {
+  try {
+    generator.next();
+  } catch (e if e instanceof StopIteration) {
+    error("Stop test because of exception!");
+  }
+}
+
+function showTestInfo(aOOPCEnabled) {
+  if (aOOPCEnabled) {
+    info("=== Start OOP testing ===");
+  } else {
+    info("=== Start INPROC testing ===");
+  }
+}
+
+function uninstallApp(aApp) {
+  if (aApp) {
+    var request = navigator.mozApps.mgmt.uninstall(app);
+    app = null;
+    request.onerror = () => {
+      error("Uninstall app failed!");
+    };
+    request.onsuccess = () => {
+      is(request.result, manifestURI, "App uninstalled.");
+      runNextTest();
+    }
+  }
+}
+
+function runTest(aOOPCEnabled) {
+  var request = navigator.mozApps.install(manifestURI, {});
+  request.onerror = () => {
+    error("Install app failed!");
+  };
+
+  request.onsuccess = () => {
+    app = request.result;
+    ok(app, "App is installed.");
+    is(app.manifestURL, manifestURI, "App manifest url is correct.");
+
+    var iframe = document.createElement('iframe');
+    iframe.setAttribute('mozbrowser', true);
+    iframe.setAttribute('remote', aOOPCEnabled);
+    iframe.setAttribute('mozapp', manifestURI);
+    iframe.src = srcURI;
+
+    iframe.addEventListener('mozbrowserloadend', () => {
+      var channels = iframe.allowedAudioChannels;
+      is(channels.length, channelsNum, "Have two channels.");
+
+      var activeCounter = 0;
+      for (var idx = 0; idx < channelsNum; idx++) {
+        let ac = channels[idx];
+        ok(ac instanceof BrowserElementAudioChannel, "Correct class.");
+        ok("getMuted" in ac, "ac.getMuted exists");
+        ok("onactivestatechanged" in ac, "ac.onactivestatechanged exists");
+
+        if (ac.name == "normal" || ac.name == "content") {
+          ok(true, "Get correct channel type.");
+        } else {
+          error("Get unexpected channel type!");
+        }
+
+        ac.getMuted().onsuccess = (e) => {
+          is(e.target.result, false, "Channel is unmuted.")
+        }
+
+        ac.onactivestatechanged = () => {
+          ok(true, "Receive activestatechanged event from " + ac.name);
+          ac.onactivestatechanged = null;
+          if (++activeCounter == channelsNum) {
+            document.body.removeChild(iframe);
+            uninstallApp(app);
+          }
+        };
+      }
+    });
+
+    document.body.appendChild(iframe);
+  };
+}
+
+function runNextTest() {
+  if (tests.length) {
+    var isEnabledOOP = tests.shift();
+    showTestInfo(isEnabledOOP);
+    runTest(isEnabledOOP);
+  } else {
+    SimpleTest.finish();
+  }
+}
+
+function startTest() {
+  SpecialPowers.setAllAppsLaunchable(true);
+  SpecialPowers.autoConfirmAppInstall(continueTest);
+  yield undefined;
+
+  SpecialPowers.autoConfirmAppUninstall(continueTest);
+  yield undefined;
+
+  runNextTest();
+  yield undefined;
+}
--- a/dom/browser-element/mochitest/chrome.ini
+++ b/dom/browser-element/mochitest/chrome.ini
@@ -1,10 +1,16 @@
 [DEFAULT]
 skip-if = buildapp == 'mulet' || (buildapp == 'b2g' && (toolkit != 'gonk' || debug))
 
 support-files =
+  audio.ogg
+  browserElement_MultipleAudioChannels.js
   browserElement_NotifyChannel.js
+  file_browserElement_MultipleAudioChannels.html
   file_browserElement_NotifyChannel.html
   manifest.webapp
   manifest.webapp^headers^
+  multipleAudioChannels_manifest.webapp
+  multipleAudioChannels_manifest.webapp^headers^
 
+[test_browserElement_MultipleAudioChannels.html]
 [test_browserElement_NotifyChannel.html]
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/file_browserElement_MultipleAudioChannels.html
@@ -0,0 +1,12 @@
+<html>
+<body>
+<script>
+var audio1 = new Audio("audio.ogg");
+var audio2 = new Audio("audio.ogg");
+audio2.mozAudioChannelType = "content";
+
+audio1.play();
+audio2.play();
+</script>
+</body>
+</html>
--- a/dom/browser-element/mochitest/mochitest.ini
+++ b/dom/browser-element/mochitest/mochitest.ini
@@ -1,12 +1,12 @@
 [DEFAULT]
 skip-if = buildapp == 'mulet' || (buildapp == 'b2g' && (toolkit != 'gonk' || debug)) || e10s
 support-files =
-  ../../../browser/base/content/test/general/audio.ogg
+  audio.ogg
   ../../../dom/media/test/short-video.ogv
   async.js
   browserElementTestHelpers.js
   browserElement_ActiveStateChangeOnChangingMutedOrVolume.js
   browserElement_Alert.js
   browserElement_AlertInFrame.js
   browserElement_AllowEmbedAppsInNestedOOIframe.js
   browserElement_AppFramePermission.js
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/multipleAudioChannels_manifest.webapp
@@ -0,0 +1,7 @@
+{
+  "name": "Multiple audio channels test",
+  "launch_path": "/index.html",
+  "permissions": {
+    "audio-channel-content": {}
+  }
+}
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/multipleAudioChannels_manifest.webapp^headers^
@@ -0,0 +1,1 @@
+Content-Type: application/manifest+json
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/test_browserElement_MultipleAudioChannels.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for multiple audio channels.</title>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/chrome-harness.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<script type="application/javascript;version=1.7" src="browserElement_MultipleAudioChannels.js">
+</script>
+</body>
+</html>
--- a/dom/canvas/test/test_offscreencanvas_serviceworker.html
+++ b/dom/canvas/test/test_offscreencanvas_serviceworker.html
@@ -6,29 +6,36 @@
 <link rel="stylesheet" href="/tests/SimpleTest/test.css">
 </head>
 <body>
 <script>
 
 SimpleTest.waitForExplicitFinish();
 
 function runTest() {
+  var registration;
+
   window.onmessage = function(evt) {
     var msg = evt.data || {};
     if (msg.type == "test") {
       ok(msg.result, msg.name);
     }
     if (msg.type == "finish") {
-      SimpleTest.finish();
+      registration.unregister().then(function() {
+        SimpleTest.finish();
+      });
     }
   }
 
   navigator.serviceWorker.register('offscreencanvas.js', { scope: "."})
     // Wait until the service worker is active.
-    .then(navigator.serviceWorker.ready)
+    .then(function(swr) {
+      registration = swr;
+      return navigator.serviceWorker.ready;
+    })
     // ...and then show the interface for the commands once it's ready.
     .then(function() {
       iframe = document.createElement("iframe");
       iframe.setAttribute('src', "offscreencanvas_serviceworker_inner.html");
       document.body.appendChild(iframe);
     })
 }
 
--- a/dom/events/EventListenerManager.cpp
+++ b/dom/events/EventListenerManager.cpp
@@ -228,17 +228,17 @@ EventListenerManager::AddEventListenerIn
                         const nsAString& aTypeString,
                         const EventListenerFlags& aFlags,
                         bool aHandler,
                         bool aAllEvents)
 {
   MOZ_ASSERT(// Main thread
              (NS_IsMainThread() && aEventMessage && aTypeAtom) ||
              // non-main-thread
-             (!NS_IsMainThread() && aEventMessage && !aTypeString.IsEmpty()) ||
+             (!NS_IsMainThread() && aEventMessage) ||
              aAllEvents, "Missing type"); // all-events listener
 
   if (!aListenerHolder || mClearingListeners) {
     return;
   }
 
   // Since there is no public API to call us with an EventListenerHolder, we
   // know that there's an EventListenerHolder on the stack holding a strong ref
--- a/dom/html/test/test_anchor_ping.html
+++ b/dom/html/test/test_anchor_ping.html
@@ -239,19 +239,17 @@ function createToPathHandler(server, pat
   return deferred.promise;
 }
 
 // Register multiple path handlers for the given server that will receive
 // pings as sent when an <a ping> element is clicked. This method uses
 // createPingPathHandler() defined below to ensure all headers are sent
 // and received as expected.
 function createPingPathHandlers(server, paths, lazyHeaders) {
-  return [
-    createPingPathHandler(server, path, lazyHeaders) for (path of paths)
-  ]
+  return Array.from(paths, (path) => createPingPathHandler(server, path, lazyHeaders));
 }
 
 // Registers a path handler for the given server that will receive pings as
 // sent when an <a ping> element has been clicked. It will check that the
 // correct http method has been used, the post data is correct and all headers
 // are given as expected. It returns a promise that will be resolved once the
 // ping has been received.
 function createPingPathHandler(server, path, lazyHeaders) {
--- a/dom/media/ADTSDemuxer.cpp
+++ b/dom/media/ADTSDemuxer.cpp
@@ -473,17 +473,18 @@ ADTSTrackDemuxer::Seek(media::TimeUnit a
   return SeekPromise::CreateAndResolve(seekTime, __func__);
 }
 
 media::TimeUnit
 ADTSTrackDemuxer::FastSeek(const media::TimeUnit& aTime)
 {
   ADTSLOG("FastSeek(%" PRId64 ") avgFrameLen=%f mNumParsedFrames=%" PRIu64
          " mFrameIndex=%" PRId64 " mOffset=%" PRIu64,
-         aTime, AverageFrameLength(), mNumParsedFrames, mFrameIndex, mOffset);
+         aTime.ToMicroseconds(), AverageFrameLength(), mNumParsedFrames,
+         mFrameIndex, mOffset);
 
   const int64_t firstFrameOffset = mParser->FirstFrame().Offset();
   if (!aTime.ToMicroseconds()) {
     // Quick seek to the beginning of the stream.
     mOffset = firstFrameOffset;
   } else if (AverageFrameLength() > 0) {
     mOffset = firstFrameOffset + FrameIndexFromTime(aTime) *
       AverageFrameLength();
@@ -505,36 +506,37 @@ ADTSTrackDemuxer::FastSeek(const media::
   return Duration(mFrameIndex);
 }
 
 media::TimeUnit
 ADTSTrackDemuxer::ScanUntil(const media::TimeUnit& aTime)
 {
   ADTSLOG("ScanUntil(%" PRId64 ") avgFrameLen=%f mNumParsedFrames=%" PRIu64
           " mFrameIndex=%" PRId64 " mOffset=%" PRIu64,
-          aTime, AverageFrameLength(), mNumParsedFrames, mFrameIndex, mOffset);
+          aTime.ToMicroseconds(), AverageFrameLength(), mNumParsedFrames,
+          mFrameIndex, mOffset);
 
   if (!aTime.ToMicroseconds()) {
     return FastSeek(aTime);
   }
 
   if (Duration(mFrameIndex) > aTime) {
     FastSeek(aTime);
   }
 
   while (SkipNextFrame(FindNextFrame()) && Duration(mFrameIndex + 1) < aTime) {
     ADTSLOGV("ScanUntil* avgFrameLen=%f mNumParsedFrames=%" PRIu64
              " mFrameIndex=%" PRId64 " mOffset=%" PRIu64 " Duration=%" PRId64,
-             aTime, AverageFrameLength(), mNumParsedFrames, mFrameIndex,
-             mOffset, Duration(mFrameIndex + 1));
+             AverageFrameLength(), mNumParsedFrames, mFrameIndex,
+             mOffset, Duration(mFrameIndex + 1).ToMicroseconds());
   }
 
   ADTSLOG("ScanUntil End avgFrameLen=%f mNumParsedFrames=%" PRIu64
           " mFrameIndex=%" PRId64 " mOffset=%" PRIu64,
-          aTime, AverageFrameLength(), mNumParsedFrames, mFrameIndex, mOffset);
+          AverageFrameLength(), mNumParsedFrames, mFrameIndex, mOffset);
 
   return Duration(mFrameIndex);
 }
 
 RefPtr<ADTSTrackDemuxer::SamplesPromise>
 ADTSTrackDemuxer::GetSamples(int32_t aNumSamples)
 {
   ADTSLOGV("GetSamples(%d) Begin mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64
--- a/dom/media/MP3Demuxer.cpp
+++ b/dom/media/MP3Demuxer.cpp
@@ -193,17 +193,18 @@ MP3TrackDemuxer::Seek(TimeUnit aTime) {
 
   return SeekPromise::CreateAndResolve(seekTime, __func__);
 }
 
 TimeUnit
 MP3TrackDemuxer::FastSeek(const TimeUnit& aTime) {
   MP3LOG("FastSeek(%" PRId64 ") avgFrameLen=%f mNumParsedFrames=%" PRIu64
          " mFrameIndex=%" PRId64 " mOffset=%" PRIu64,
-         aTime, AverageFrameLength(), mNumParsedFrames, mFrameIndex, mOffset);
+         aTime.ToMicroseconds(), AverageFrameLength(), mNumParsedFrames,
+         mFrameIndex, mOffset);
 
   const auto& vbr = mParser.VBRInfo();
   if (!aTime.ToMicroseconds()) {
     // Quick seek to the beginning of the stream.
     mFrameIndex = 0;
   } else if (vbr.IsTOCPresent()) {
     // Use TOC for more precise seeking.
     const float durationFrac = static_cast<float>(aTime.ToMicroseconds()) /
@@ -229,17 +230,18 @@ MP3TrackDemuxer::FastSeek(const TimeUnit
 
   return Duration(mFrameIndex);
 }
 
 TimeUnit
 MP3TrackDemuxer::ScanUntil(const TimeUnit& aTime) {
   MP3LOG("ScanUntil(%" PRId64 ") avgFrameLen=%f mNumParsedFrames=%" PRIu64
          " mFrameIndex=%" PRId64 " mOffset=%" PRIu64,
-         aTime, AverageFrameLength(), mNumParsedFrames, mFrameIndex, mOffset);
+         aTime.ToMicroseconds(), AverageFrameLength(), mNumParsedFrames,
+         mFrameIndex, mOffset);
 
   if (!aTime.ToMicroseconds()) {
     return FastSeek(aTime);
   }
 
   if (Duration(mFrameIndex) > aTime) {
     FastSeek(aTime);
   }
@@ -248,23 +250,23 @@ MP3TrackDemuxer::ScanUntil(const TimeUni
     return SeekPosition();
   }
 
   MediaByteRange nextRange = FindNextFrame();
   while (SkipNextFrame(nextRange) && Duration(mFrameIndex + 1) < aTime) {
     nextRange = FindNextFrame();
     MP3LOGV("ScanUntil* avgFrameLen=%f mNumParsedFrames=%" PRIu64
             " mFrameIndex=%" PRId64 " mOffset=%" PRIu64 " Duration=%" PRId64,
-            aTime, AverageFrameLength(), mNumParsedFrames, mFrameIndex,
-            mOffset, Duration(mFrameIndex + 1));
+            AverageFrameLength(), mNumParsedFrames,
+            mFrameIndex, mOffset, Duration(mFrameIndex + 1).ToMicroseconds());
   }
 
   MP3LOG("ScanUntil End avgFrameLen=%f mNumParsedFrames=%" PRIu64
          " mFrameIndex=%" PRId64 " mOffset=%" PRIu64,
-         aTime, AverageFrameLength(), mNumParsedFrames, mFrameIndex, mOffset);
+         AverageFrameLength(), mNumParsedFrames, mFrameIndex, mOffset);
 
   return SeekPosition();
 }
 
 RefPtr<MP3TrackDemuxer::SamplesPromise>
 MP3TrackDemuxer::GetSamples(int32_t aNumSamples) {
   MP3LOGV("GetSamples(%d) Begin mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64
           " mFrameIndex=%" PRId64 " mTotalFrameLen=%" PRIu64 " mSamplesPerFrame=%d "
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -236,18 +236,16 @@ MediaDecoderStateMachine::MediaDecoderSt
   mDecodeToSeekTarget(false),
   mCurrentTimeBeforeSeek(0),
   mCorruptFrames(60),
   mDecodingFirstFrame(true),
   mSentLoadedMetadataEvent(false),
   mSentFirstFrameLoadedEvent(false, "MediaDecoderStateMachine::mSentFirstFrameLoadedEvent"),
   mSentPlaybackEndedEvent(false),
   mOutputStreamManager(new OutputStreamManager()),
-  mStreamSink(new DecodedStream(
-    mTaskQueue, mAudioQueue, mVideoQueue, mOutputStreamManager)),
   mResource(aDecoder->GetResource()),
   mAudioOffloading(false),
   mBuffered(mTaskQueue, TimeIntervals(),
             "MediaDecoderStateMachine::mBuffered (Mirror)"),
   mEstimatedDuration(mTaskQueue, NullableTimeUnit(),
                     "MediaDecoderStateMachine::mEstimatedDuration (Mirror)"),
   mExplicitDuration(mTaskQueue, Maybe<double>(),
                     "MediaDecoderStateMachine::mExplicitDuration (Mirror)"),
@@ -334,16 +332,19 @@ MediaDecoderStateMachine::~MediaDecoderS
 #endif
 }
 
 void
 MediaDecoderStateMachine::InitializationTask(MediaDecoder* aDecoder)
 {
   MOZ_ASSERT(OnTaskQueue());
 
+  mStreamSink = new DecodedStream(mTaskQueue, mAudioQueue, mVideoQueue,
+                                  mOutputStreamManager, mSameOriginMedia.Ref());
+
   // Connect mirrors.
   mBuffered.Connect(mReader->CanonicalBuffered());
   mEstimatedDuration.Connect(aDecoder->CanonicalEstimatedDuration());
   mExplicitDuration.Connect(aDecoder->CanonicalExplicitDuration());
   mPlayState.Connect(aDecoder->CanonicalPlayState());
   mNextPlayState.Connect(aDecoder->CanonicalNextPlayState());
   mLogicallySeeking.Connect(aDecoder->CanonicalLogicallySeeking());
   mVolume.Connect(aDecoder->CanonicalVolume());
@@ -363,22 +364,18 @@ MediaDecoderStateMachine::Initialization
   mWatchManager.Watch(mVolume, &MediaDecoderStateMachine::VolumeChanged);
   mWatchManager.Watch(mLogicalPlaybackRate, &MediaDecoderStateMachine::LogicalPlaybackRateChanged);
   mWatchManager.Watch(mPreservesPitch, &MediaDecoderStateMachine::PreservesPitchChanged);
   mWatchManager.Watch(mEstimatedDuration, &MediaDecoderStateMachine::RecomputeDuration);
   mWatchManager.Watch(mExplicitDuration, &MediaDecoderStateMachine::RecomputeDuration);
   mWatchManager.Watch(mObservedDuration, &MediaDecoderStateMachine::RecomputeDuration);
   mWatchManager.Watch(mPlayState, &MediaDecoderStateMachine::PlayStateChanged);
   mWatchManager.Watch(mLogicallySeeking, &MediaDecoderStateMachine::LogicallySeekingChanged);
-  mWatchManager.Watch(mSameOriginMedia, &MediaDecoderStateMachine::SameOriginMediaChanged);
   mWatchManager.Watch(mSentFirstFrameLoadedEvent, &MediaDecoderStateMachine::AdjustAudioThresholds);
   mWatchManager.Watch(mAudioCaptured, &MediaDecoderStateMachine::AdjustAudioThresholds);
-
-  // Propagate mSameOriginMedia to mDecodedStream.
-  SameOriginMediaChanged();
 }
 
 media::MediaSink*
 MediaDecoderStateMachine::CreateAudioSink()
 {
   RefPtr<MediaDecoderStateMachine> self = this;
   auto audioSinkCreator = [self] () {
     MOZ_ASSERT(self->OnTaskQueue());
@@ -1452,22 +1449,16 @@ void MediaDecoderStateMachine::PlayState
 }
 
 void MediaDecoderStateMachine::LogicallySeekingChanged()
 {
   MOZ_ASSERT(OnTaskQueue());
   ScheduleStateMachine();
 }
 
-void MediaDecoderStateMachine::SameOriginMediaChanged()
-{
-  MOZ_ASSERT(OnTaskQueue());
-  mStreamSink->SetSameOrigin(mSameOriginMedia);
-}
-
 void MediaDecoderStateMachine::BufferedRangeUpdated()
 {
   MOZ_ASSERT(OnTaskQueue());
 
   // While playing an unseekable stream of unknown duration, mObservedDuration
   // is updated (in AdvanceFrame()) as we play. But if data is being downloaded
   // faster than played, mObserved won't reflect the end of playable data
   // since we haven't played the frame at the end of buffered data. So update
@@ -2356,23 +2347,16 @@ nsresult MediaDecoderStateMachine::RunSt
                      "Must have timer scheduled");
         return NS_OK;
       }
 
       // StopPlayback in order to reset the IsPlaying() state so audio
       // is restarted correctly.
       StopPlayback();
 
-      if (mState != DECODER_STATE_COMPLETED) {
-        // While we're presenting a frame we can change state. Whatever changed
-        // our state should have scheduled another state machine run.
-        NS_ASSERTION(IsStateMachineScheduled(), "Must have timer scheduled");
-        return NS_OK;
-      }
-
       if (mPlayState == MediaDecoder::PLAY_STATE_PLAYING &&
           !mSentPlaybackEndedEvent)
       {
         int64_t clockTime = std::max(AudioEndTime(), VideoEndTime());
         clockTime = std::max(int64_t(0), std::max(clockTime, Duration().ToMicroseconds()));
         UpdatePlaybackPosition(clockTime);
 
         // Ensure readyState is updated before firing the 'ended' event.
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -502,19 +502,16 @@ protected:
   void StartMediaSink();
 
   // Notification method invoked when mPlayState changes.
   void PlayStateChanged();
 
   // Notification method invoked when mLogicallySeeking changes.
   void LogicallySeekingChanged();
 
-  // Notification method invoked when mSameOriginMedia changes.
-  void SameOriginMediaChanged();
-
   // Sets internal state which causes playback of media to pause.
   // The decoder monitor must be held.
   void StopPlayback();
 
   // If the conditions are right, sets internal state which causes playback
   // of media to begin or resume.
   // Must be called with the decode monitor held.
   void MaybeStartPlayback();
--- a/dom/media/MediaStreamGraph.cpp
+++ b/dom/media/MediaStreamGraph.cpp
@@ -2723,17 +2723,17 @@ MediaStreamGraph::GetInstance(MediaStrea
     graph = new MediaStreamGraphImpl(aGraphDriverRequested,
                                      CubebUtils::PreferredSampleRate(),
                                      aChannel);
 
     gGraphs.Put(channel, graph);
 
     STREAM_LOG(LogLevel::Debug,
         ("Starting up MediaStreamGraph %p for channel %s",
-         graph, AudioChannelValues::strings[channel]));
+         graph, AudioChannelValues::strings[channel].value));
   }
 
   return graph;
 }
 
 MediaStreamGraph*
 MediaStreamGraph::CreateNonRealtimeInstance(TrackRate aSampleRate)
 {
--- a/dom/media/gmp/GMPStorageParent.cpp
+++ b/dom/media/gmp/GMPStorageParent.cpp
@@ -563,17 +563,17 @@ private:
 
   nsClassHashtable<nsCStringHashKey, Record> mRecords;
 };
 
 GMPStorageParent::GMPStorageParent(const nsCString& aNodeId,
                                    GMPParent* aPlugin)
   : mNodeId(aNodeId)
   , mPlugin(aPlugin)
-  , mShutdown(false)
+  , mShutdown(true)
 {
 }
 
 nsresult
 GMPStorageParent::Init()
 {
   LOGD(("GMPStorageParent[%p]::Init()", this));
 
@@ -597,16 +597,17 @@ GMPStorageParent::Init()
       NS_WARNING("Failed to initialize on disk GMP storage");
       return NS_ERROR_FAILURE;
     }
     mStorage = Move(storage);
   } else {
     mStorage = MakeUnique<GMPMemoryStorage>();
   }
 
+  mShutdown = false;
   return NS_OK;
 }
 
 bool
 GMPStorageParent::RecvOpen(const nsCString& aRecordName)
 {
   LOGD(("GMPStorageParent[%p]::RecvOpen(record='%s')",
        this, aRecordName.get()));
--- a/dom/media/gmp/GMPStorageParent.h
+++ b/dom/media/gmp/GMPStorageParent.h
@@ -49,15 +49,16 @@ protected:
 
 private:
   ~GMPStorageParent() {}
 
   UniquePtr<GMPStorage> mStorage;
 
   const nsCString mNodeId;
   RefPtr<GMPParent> mPlugin;
+  // True after Shutdown(), or if Init() has not completed successfully.
   bool mShutdown;
 };
 
 } // namespace gmp
 } // namespace mozilla
 
 #endif // GMPStorageParent_h_
--- a/dom/media/mediasink/DecodedStream.cpp
+++ b/dom/media/mediasink/DecodedStream.cpp
@@ -183,21 +183,22 @@ DecodedStreamData::SetPlaying(bool aPlay
     mPlaying = aPlaying;
     UpdateStreamSuspended(mStream, !mPlaying);
   }
 }
 
 DecodedStream::DecodedStream(AbstractThread* aOwnerThread,
                              MediaQueue<MediaData>& aAudioQueue,
                              MediaQueue<MediaData>& aVideoQueue,
-                             OutputStreamManager* aOutputStreamManager)
+                             OutputStreamManager* aOutputStreamManager,
+                             const bool& aSameOrigin)
   : mOwnerThread(aOwnerThread)
   , mOutputStreamManager(aOutputStreamManager)
   , mPlaying(false)
-  , mSameOrigin(false)
+  , mSameOrigin(aSameOrigin)
   , mAudioQueue(aAudioQueue)
   , mVideoQueue(aVideoQueue)
 {
 }
 
 DecodedStream::~DecodedStream()
 {
   MOZ_ASSERT(mStartTime.isNothing(), "playback should've ended.");
@@ -426,23 +427,16 @@ DecodedStream::SetPlaybackRate(double aP
 void
 DecodedStream::SetPreservesPitch(bool aPreservesPitch)
 {
   AssertOwnerThread();
   mParams.mPreservesPitch = aPreservesPitch;
 }
 
 void
-DecodedStream::SetSameOrigin(bool aSameOrigin)
-{
-  AssertOwnerThread();
-  mSameOrigin = aSameOrigin;
-}
-
-void
 DecodedStream::InitTracks()
 {
   AssertOwnerThread();
 
   if (mData->mStreamInitialized) {
     return;
   }
 
--- a/dom/media/mediasink/DecodedStream.h
+++ b/dom/media/mediasink/DecodedStream.h
@@ -30,17 +30,18 @@ template <class T> class MediaQueue;
 
 class DecodedStream : public media::MediaSink {
   using media::MediaSink::PlaybackParams;
 
 public:
   DecodedStream(AbstractThread* aOwnerThread,
                 MediaQueue<MediaData>& aAudioQueue,
                 MediaQueue<MediaData>& aVideoQueue,
-                OutputStreamManager* aOutputStreamManager);
+                OutputStreamManager* aOutputStreamManager,
+                const bool& aSameOrigin);
 
   // MediaSink functions.
   const PlaybackParams& GetPlaybackParams() const override;
   void SetPlaybackParams(const PlaybackParams& aParams) override;
 
   RefPtr<GenericPromise> OnEnded(TrackType aType) override;
   int64_t GetEndTime(TrackType aType) const override;
   int64_t GetPosition(TimeStamp* aTimeStamp = nullptr) const override;
@@ -55,19 +56,16 @@ public:
   void SetPreservesPitch(bool aPreservesPitch) override;
   void SetPlaying(bool aPlaying) override;
 
   void Start(int64_t aStartTime, const MediaInfo& aInfo) override;
   void Stop() override;
   bool IsStarted() const override;
   bool IsPlaying() const override;
 
-  // TODO: fix these functions that don't fit into the interface of MediaSink.
-  void SetSameOrigin(bool aSameOrigin);
-
 protected:
   virtual ~DecodedStream();
 
 private:
   void CreateData(MozPromiseHolder<GenericPromise>&& aPromise);
   void DestroyData(UniquePtr<DecodedStreamData> aData);
   void OnDataCreated(UniquePtr<DecodedStreamData> aData);
   void InitTracks();
@@ -93,17 +91,17 @@ private:
 
   /*
    * Worker thread only members.
    */
   UniquePtr<DecodedStreamData> mData;
   RefPtr<GenericPromise> mFinishPromise;
 
   bool mPlaying;
-  bool mSameOrigin;
+  const bool& mSameOrigin; // valid until Shutdown() is called.
   PlaybackParams mParams;
 
   Maybe<int64_t> mStartTime;
   MediaInfo mInfo;
 
   MediaQueue<MediaData>& mAudioQueue;
   MediaQueue<MediaData>& mVideoQueue;
 
--- a/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp
+++ b/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp
@@ -30,16 +30,32 @@ RefPtr<MediaDataDecoder::InitPromise>
 FFmpegAudioDecoder<LIBAV_VER>::Init()
 {
   nsresult rv = InitDecoder();
 
   return rv == NS_OK ? InitPromise::CreateAndResolve(TrackInfo::kAudioTrack, __func__)
                      : InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
 }
 
+void
+FFmpegAudioDecoder<LIBAV_VER>::InitCodecContext()
+{
+  MOZ_ASSERT(mCodecContext);
+  // We do not want to set this value to 0 as FFmpeg by default will
+  // use the number of cores, which with our mozlibavutil get_cpu_count
+  // isn't implemented.
+  mCodecContext->thread_count = 1;
+  // FFmpeg takes this as a suggestion for what format to use for audio samples.
+  uint32_t major, minor;
+  FFmpegRuntimeLinker::GetVersion(major, minor);
+  // LibAV 0.8 produces rubbish float interleaved samples, request 16 bits audio.
+  mCodecContext->request_sample_fmt =
+    (major == 53) ? AV_SAMPLE_FMT_S16 : AV_SAMPLE_FMT_FLT;
+}
+
 static UniquePtr<AudioDataValue[]>
 CopyAndPackAudio(AVFrame* aFrame, uint32_t aNumChannels, uint32_t aNumAFrames)
 {
   MOZ_ASSERT(aNumChannels <= MAX_CHANNELS);
 
   auto audio = MakeUnique<AudioDataValue[]>(aNumChannels * aNumAFrames);
 
   if (aFrame->format == AV_SAMPLE_FMT_FLT) {
--- a/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.h
+++ b/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.h
@@ -23,16 +23,17 @@ public:
   FFmpegAudioDecoder(FlushableTaskQueue* aTaskQueue,
                      MediaDataDecoderCallback* aCallback,
                      const AudioInfo& aConfig);
   virtual ~FFmpegAudioDecoder();
 
   RefPtr<InitPromise> Init() override;
   nsresult Input(MediaRawData* aSample) override;
   void ProcessDrain() override;
+  void InitCodecContext() override;
   static AVCodecID GetCodecId(const nsACString& aMimeType);
 
 private:
   void DecodePacket(MediaRawData* aSample);
 };
 
 } // namespace mozilla
 
--- a/dom/media/platforms/ffmpeg/FFmpegDataDecoder.cpp
+++ b/dom/media/platforms/ffmpeg/FFmpegDataDecoder.cpp
@@ -2,17 +2,19 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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/TaskQueue.h"
 
 #include <string.h>
+#ifdef __GNUC__
 #include <unistd.h>
+#endif
 
 #include "FFmpegLibs.h"
 #include "FFmpegLog.h"
 #include "FFmpegDataDecoder.h"
 #include "prsystem.h"
 #include "FFmpegRuntimeLinker.h"
 
 namespace mozilla
@@ -27,49 +29,23 @@ FFmpegDataDecoder<LIBAV_VER>::FFmpegData
   : mTaskQueue(aTaskQueue)
   , mCallback(aCallback)
   , mCodecContext(nullptr)
   , mFrame(NULL)
   , mExtraData(nullptr)
   , mCodecID(aCodecID)
   , mMonitor("FFMpegaDataDecoder")
   , mIsFlushing(false)
-  , mCodecParser(nullptr)
 {
   MOZ_COUNT_CTOR(FFmpegDataDecoder);
 }
 
 FFmpegDataDecoder<LIBAV_VER>::~FFmpegDataDecoder()
 {
   MOZ_COUNT_DTOR(FFmpegDataDecoder);
-  if (mCodecParser) {
-    av_parser_close(mCodecParser);
-    mCodecParser = nullptr;
-  }
-}
-
-/**
- * FFmpeg calls back to this function with a list of pixel formats it supports.
- * We choose a pixel format that we support and return it.
- * For now, we just look for YUV420P as it is the only non-HW accelerated format
- * supported by FFmpeg's H264 decoder.
- */
-static PixelFormat
-ChoosePixelFormat(AVCodecContext* aCodecContext, const PixelFormat* aFormats)
-{
-  FFMPEG_LOG("Choosing FFmpeg pixel format for video decoding.");
-  for (; *aFormats > -1; aFormats++) {
-    if (*aFormats == PIX_FMT_YUV420P || *aFormats == PIX_FMT_YUVJ420P) {
-      FFMPEG_LOG("Requesting pixel format YUV420P.");
-      return PIX_FMT_YUV420P;
-    }
-  }
-
-  NS_WARNING("FFmpeg does not share any supported pixel formats.");
-  return PIX_FMT_NONE;
 }
 
 nsresult
 FFmpegDataDecoder<LIBAV_VER>::InitDecoder()
 {
   FFMPEG_LOG("Initialising FFmpeg decoder.");
 
   AVCodec* codec = FindAVCodec(mCodecID);
@@ -82,29 +58,17 @@ FFmpegDataDecoder<LIBAV_VER>::InitDecode
 
   if (!(mCodecContext = avcodec_alloc_context3(codec))) {
     NS_WARNING("Couldn't init ffmpeg context");
     return NS_ERROR_FAILURE;
   }
 
   mCodecContext->opaque = this;
 
-  // FFmpeg takes this as a suggestion for what format to use for audio samples.
-  uint32_t major, minor;
-  FFmpegRuntimeLinker::GetVersion(major, minor);
-  // LibAV 0.8 produces rubbish float interleaved samples, request 16 bits audio.
-  mCodecContext->request_sample_fmt = major == 53 ?
-    AV_SAMPLE_FMT_S16 : AV_SAMPLE_FMT_FLT;
-
-  // FFmpeg will call back to this to negotiate a video pixel format.
-  mCodecContext->get_format = ChoosePixelFormat;
-
-  mCodecContext->thread_count = PR_GetNumberOfProcessors();
-  mCodecContext->thread_type = FF_THREAD_SLICE | FF_THREAD_FRAME;
-  mCodecContext->thread_safe_callbacks = false;
+  InitCodecContext();
 
   if (mExtraData) {
     mCodecContext->extradata_size = mExtraData->Length();
     // FFmpeg may use SIMD instructions to access the data which reads the
     // data in 32 bytes block. Must ensure we have enough data to read.
     mExtraData->AppendElements(FF_INPUT_BUFFER_PADDING_SIZE);
     mCodecContext->extradata = mExtraData->Elements();
   } else {
@@ -126,21 +90,16 @@ FFmpegDataDecoder<LIBAV_VER>::InitDecode
       mCodecContext->sample_fmt != AV_SAMPLE_FMT_FLT &&
       mCodecContext->sample_fmt != AV_SAMPLE_FMT_FLTP &&
       mCodecContext->sample_fmt != AV_SAMPLE_FMT_S16 &&
       mCodecContext->sample_fmt != AV_SAMPLE_FMT_S16P) {
     NS_WARNING("FFmpeg audio decoder outputs unsupported audio format.");
     return NS_ERROR_FAILURE;
   }
 
-  mCodecParser = av_parser_init(mCodecID);
-  if (mCodecParser) {
-    mCodecParser->flags |= PARSER_FLAG_COMPLETE_FRAMES;
-  }
-
   FFMPEG_LOG("FFmpeg init successful.");
   return NS_OK;
 }
 
 nsresult
 FFmpegDataDecoder<LIBAV_VER>::Shutdown()
 {
   if (mTaskQueue) {
--- a/dom/media/platforms/ffmpeg/FFmpegDataDecoder.h
+++ b/dom/media/platforms/ffmpeg/FFmpegDataDecoder.h
@@ -38,16 +38,17 @@ public:
 
   static AVCodec* FindAVCodec(AVCodecID aCodec);
 
 protected:
   // Flush and Drain operation, always run
   virtual void ProcessFlush();
   virtual void ProcessDrain() = 0;
   virtual void ProcessShutdown();
+  virtual void InitCodecContext() {}
   AVFrame*        PrepareFrame();
   nsresult        InitDecoder();
 
   RefPtr<FlushableTaskQueue> mTaskQueue;
   MediaDataDecoderCallback* mCallback;
 
   AVCodecContext* mCodecContext;
   AVFrame*        mFrame;
@@ -56,17 +57,16 @@ protected:
 
   // For wait on mIsFlushing during Shutdown() process.
   // Protects mReorderQueue.
   Monitor mMonitor;
   // Set on reader/decode thread calling Flush() to indicate that output is
   // not required and so input samples on mTaskQueue need not be processed.
   // Cleared on mTaskQueue in ProcessDrain().
   Atomic<bool> mIsFlushing;
-  AVCodecParserContext* mCodecParser;
 
 private:
   static bool sFFmpegInitDone;
   static StaticMutex sMonitor;
 };
 
 } // namespace mozilla
 
--- a/dom/media/platforms/ffmpeg/FFmpegFunctionList.h
+++ b/dom/media/platforms/ffmpeg/FFmpegFunctionList.h
@@ -1,37 +1,45 @@
 /* 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/. */
 
+// A value of 0 indicates that the entry is located in libavcodec.dll
+// A negative value indicates that the entry is to always be loaded from
+// libavutil.dll, regardless of the version.
+// A positive value indicates that it is to be loaded from libavutil
+// and in that version only.
+#define AV_FUNC_AVCODEC_ALL 0
+#define AV_FUNC_AVUTIL_ALL  (-1)
+
 /* libavcodec */
-AV_FUNC(avcodec_align_dimensions2, 0)
-AV_FUNC(avcodec_get_frame_defaults, 0)
-AV_FUNC(avcodec_close, 0)
-AV_FUNC(avcodec_decode_audio4, 0)
-AV_FUNC(avcodec_decode_video2, 0)
-AV_FUNC(avcodec_default_get_buffer, 0)
-AV_FUNC(avcodec_default_release_buffer, 0)
-AV_FUNC(avcodec_find_decoder, 0)
-AV_FUNC(avcodec_flush_buffers, 0)
-AV_FUNC(avcodec_alloc_context3, 0)
-AV_FUNC(avcodec_get_edge_width, 0)
-AV_FUNC(avcodec_open2, 0)
-AV_FUNC(av_init_packet, 0)
-AV_FUNC(av_parser_init, 0)
-AV_FUNC(av_parser_close, 0)
-AV_FUNC(av_parser_parse2, 0)
-AV_FUNC(avcodec_register_all, 0)
+AV_FUNC(avcodec_align_dimensions2, AV_FUNC_AVCODEC_ALL)
+AV_FUNC(avcodec_get_frame_defaults, AV_FUNC_AVCODEC_ALL)
+AV_FUNC(avcodec_close, AV_FUNC_AVCODEC_ALL)
+AV_FUNC(avcodec_decode_audio4, AV_FUNC_AVCODEC_ALL)
+AV_FUNC(avcodec_decode_video2, AV_FUNC_AVCODEC_ALL)
+AV_FUNC(avcodec_default_get_buffer, AV_FUNC_AVCODEC_ALL)
+AV_FUNC(avcodec_default_release_buffer, AV_FUNC_AVCODEC_ALL)
+AV_FUNC(avcodec_find_decoder, AV_FUNC_AVCODEC_ALL)
+AV_FUNC(avcodec_flush_buffers, AV_FUNC_AVCODEC_ALL)
+AV_FUNC(avcodec_alloc_context3, AV_FUNC_AVCODEC_ALL)
+AV_FUNC(avcodec_get_edge_width, AV_FUNC_AVCODEC_ALL)
+AV_FUNC(avcodec_open2, AV_FUNC_AVCODEC_ALL)
+AV_FUNC(av_init_packet, AV_FUNC_AVCODEC_ALL)
+AV_FUNC(av_parser_init, AV_FUNC_AVCODEC_ALL)
+AV_FUNC(av_parser_close, AV_FUNC_AVCODEC_ALL)
+AV_FUNC(av_parser_parse2, AV_FUNC_AVCODEC_ALL)
+AV_FUNC(avcodec_register_all, AV_FUNC_AVCODEC_ALL)
 
 /* libavutil */
-AV_FUNC(av_image_fill_linesizes, 0)
-AV_FUNC(av_image_fill_pointers, 0)
-AV_FUNC(av_log_set_level, 0)
-AV_FUNC(av_malloc, 0)
-AV_FUNC(av_freep, 0)
+AV_FUNC(av_image_fill_linesizes, AV_FUNC_AVUTIL_ALL)
+AV_FUNC(av_image_fill_pointers, AV_FUNC_AVUTIL_ALL)
+AV_FUNC(av_log_set_level, AV_FUNC_AVUTIL_ALL)
+AV_FUNC(av_malloc, AV_FUNC_AVUTIL_ALL)
+AV_FUNC(av_freep, AV_FUNC_AVUTIL_ALL)
 
 #if defined(LIBAVCODEC_VERSION_MAJOR) || defined(LIBAVCODEC_ALLVERSION)
 #if LIBAVCODEC_VERSION_MAJOR == 54 || defined(LIBAVCODEC_ALLVERSION)
 /* libavutil v54 only */
 AV_FUNC(avcodec_alloc_frame, 54)
 AV_FUNC(avcodec_free_frame, 54)
 #endif
 #if LIBAVCODEC_VERSION_MAJOR >= 55 || defined(LIBAVCODEC_ALLVERSION)
--- a/dom/media/platforms/ffmpeg/FFmpegH264Decoder.cpp
+++ b/dom/media/platforms/ffmpeg/FFmpegH264Decoder.cpp
@@ -17,16 +17,37 @@
 #include "mozilla/PodOperations.h"
 
 typedef mozilla::layers::Image Image;
 typedef mozilla::layers::PlanarYCbCrImage PlanarYCbCrImage;
 
 namespace mozilla
 {
 
+/**
+ * FFmpeg calls back to this function with a list of pixel formats it supports.
+ * We choose a pixel format that we support and return it.
+ * For now, we just look for YUV420P as it is the only non-HW accelerated format
+ * supported by FFmpeg's H264 decoder.
+ */
+static PixelFormat
+ChoosePixelFormat(AVCodecContext* aCodecContext, const PixelFormat* aFormats)
+{
+  FFMPEG_LOG("Choosing FFmpeg pixel format for video decoding.");
+  for (; *aFormats > -1; aFormats++) {
+    if (*aFormats == PIX_FMT_YUV420P || *aFormats == PIX_FMT_YUVJ420P) {
+      FFMPEG_LOG("Requesting pixel format YUV420P.");
+      return PIX_FMT_YUV420P;
+    }
+  }
+
+  NS_WARNING("FFmpeg does not share any supported pixel formats.");
+  return PIX_FMT_NONE;
+}
+
 FFmpegH264Decoder<LIBAV_VER>::PtsCorrectionContext::PtsCorrectionContext()
   : mNumFaultyPts(0)
   , mNumFaultyDts(0)
   , mLastPts(INT64_MIN)
   , mLastDts(INT64_MIN)
 {
 }
 
@@ -64,34 +85,62 @@ FFmpegH264Decoder<LIBAV_VER>::PtsCorrect
 FFmpegH264Decoder<LIBAV_VER>::FFmpegH264Decoder(
   FlushableTaskQueue* aTaskQueue, MediaDataDecoderCallback* aCallback,
   const VideoInfo& aConfig,
   ImageContainer* aImageContainer)
   : FFmpegDataDecoder(aTaskQueue, aCallback, GetCodecId(aConfig.mMimeType))
   , mImageContainer(aImageContainer)
   , mDisplay(aConfig.mDisplay)
   , mImage(aConfig.mImage)
+  , mCodecParser(nullptr)
 {
   MOZ_COUNT_CTOR(FFmpegH264Decoder);
   // Use a new MediaByteBuffer as the object will be modified during initialization.
   mExtraData = new MediaByteBuffer;
   mExtraData->AppendElements(*aConfig.mExtraData);
 }
 
 RefPtr<MediaDataDecoder::InitPromise>
 FFmpegH264Decoder<LIBAV_VER>::Init()
 {
   if (NS_FAILED(InitDecoder())) {
     return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
   }
 
+  return InitPromise::CreateAndResolve(TrackInfo::kVideoTrack, __func__);
+}
+
+void
+FFmpegH264Decoder<LIBAV_VER>::InitCodecContext()
+{
   mCodecContext->width = mImage.width;
   mCodecContext->height = mImage.height;
 
-  return InitPromise::CreateAndResolve(TrackInfo::kVideoTrack, __func__);
+  // We use the same logic as libvpx in determining the number of threads to use
+  // so that we end up behaving in the same fashion when using ffmpeg as
+  // we would otherwise cause various crashes (see bug 1236167)
+  int decode_threads = 2;
+  if (mCodecID != AV_CODEC_ID_VP8) {
+    if (mDisplay.width >= 2048) {
+      decode_threads = 8;
+    } else if (mDisplay.width >= 1024) {
+      decode_threads = 4;
+    }
+  }
+  decode_threads = std::min(decode_threads, PR_GetNumberOfProcessors());
+  mCodecContext->thread_count = decode_threads;
+  mCodecContext->thread_type = FF_THREAD_SLICE | FF_THREAD_FRAME;
+
+  // FFmpeg will call back to this to negotiate a video pixel format.
+  mCodecContext->get_format = ChoosePixelFormat;
+
+  mCodecParser = av_parser_init(mCodecID);
+  if (mCodecParser) {
+    mCodecParser->flags |= PARSER_FLAG_COMPLETE_FRAMES;
+  }
 }
 
 FFmpegH264Decoder<LIBAV_VER>::DecodeResult
 FFmpegH264Decoder<LIBAV_VER>::DoDecodeFrame(MediaRawData* aSample)
 {
   MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
 
   uint8_t* inputData = const_cast<uint8_t*>(aSample->Data());
@@ -283,16 +332,20 @@ FFmpegH264Decoder<LIBAV_VER>::ProcessFlu
   mPtsContext.Reset();
   mDurationMap.Clear();
   FFmpegDataDecoder::ProcessFlush();
 }
 
 FFmpegH264Decoder<LIBAV_VER>::~FFmpegH264Decoder()
 {
   MOZ_COUNT_DTOR(FFmpegH264Decoder);
+  if (mCodecParser) {
+    av_parser_close(mCodecParser);
+    mCodecParser = nullptr;
+  }
 }
 
 AVCodecID
 FFmpegH264Decoder<LIBAV_VER>::GetCodecId(const nsACString& aMimeType)
 {
   if (aMimeType.EqualsLiteral("video/avc") || aMimeType.EqualsLiteral("video/mp4")) {
     return AV_CODEC_ID_H264;
   }
--- a/dom/media/platforms/ffmpeg/FFmpegH264Decoder.h
+++ b/dom/media/platforms/ffmpeg/FFmpegH264Decoder.h
@@ -37,16 +37,17 @@ public:
                     const VideoInfo& aConfig,
                     ImageContainer* aImageContainer);
   virtual ~FFmpegH264Decoder();
 
   RefPtr<InitPromise> Init() override;
   nsresult Input(MediaRawData* aSample) override;
   void ProcessDrain() override;
   void ProcessFlush() override;
+  void InitCodecContext() override;
   static AVCodecID GetCodecId(const nsACString& aMimeType);
 
 private:
   void DecodeFrame(MediaRawData* aSample);
   DecodeResult DoDecodeFrame(MediaRawData* aSample);
   DecodeResult DoDecodeFrame(MediaRawData* aSample, uint8_t* aData, int aSize);
   void DoDrain();
   void OutputDelayedFrames();
@@ -59,16 +60,19 @@ private:
    */
   int AllocateYUV420PVideoBuffer(AVCodecContext* aCodecContext,
                                  AVFrame* aFrame);
 
   RefPtr<ImageContainer> mImageContainer;
   nsIntSize mDisplay;
   nsIntRect mImage;
 
+  // Parser used for VP8 and VP9 decoding.
+  AVCodecParserContext* mCodecParser;
+
   class PtsCorrectionContext {
   public:
     PtsCorrectionContext();
     int64_t GuessCorrectPts(int64_t aPts, int64_t aDts);
     void Reset();
 
   private:
     int64_t mNumFaultyPts; /// Number of incorrect PTS values so far
--- a/dom/media/platforms/ffmpeg/FFmpegLibs.h
+++ b/dom/media/platforms/ffmpeg/FFmpegLibs.h
@@ -2,37 +2,43 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef __FFmpegLibs_h__
 #define __FFmpegLibs_h__
 
+#include "mozilla/Types.h"
+
 extern "C" {
+#ifdef __GNUC__
 #pragma GCC visibility push(default)
-#include <libavcodec/avcodec.h>
-#include <libavformat/avformat.h>
-#include <libavutil/imgutils.h>
+#endif
+#include "libavcodec/avcodec.h"
+#include "libavformat/avformat.h"
+#include "libavutil/imgutils.h"
+#ifdef __GNUC__
 #pragma GCC visibility pop
+#endif
 }
 
 #if LIBAVCODEC_VERSION_MAJOR < 55
 #define AV_CODEC_ID_VP6F CODEC_ID_VP6F
 #define AV_CODEC_ID_H264 CODEC_ID_H264
 #define AV_CODEC_ID_AAC CODEC_ID_AAC
 #define AV_CODEC_ID_MP3 CODEC_ID_MP3
 #define AV_CODEC_ID_VP8 CODEC_ID_VP8
 #define AV_CODEC_ID_NONE CODEC_ID_NONE
 typedef CodecID AVCodecID;
 #endif
 
 enum { LIBAV_VER = LIBAVFORMAT_VERSION_MAJOR };
 
 namespace mozilla {
 
-#define AV_FUNC(func, ver) extern typeof(func)* func;
+#define AV_FUNC(func, ver) extern decltype(func)* func;
 #include "FFmpegFunctionList.h"
 #undef AV_FUNC
 
 }
 
 #endif // __FFmpegLibs_h__
--- a/dom/media/platforms/ffmpeg/FFmpegRuntimeLinker.cpp
+++ b/dom/media/platforms/ffmpeg/FFmpegRuntimeLinker.cpp
@@ -3,18 +3,25 @@
 /* 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 "FFmpegRuntimeLinker.h"
 #include "mozilla/ArrayUtils.h"
 #include "FFmpegLog.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/Types.h"
 #include "prlink.h"
 
+#if defined(XP_WIN)
+#include "libavcodec/avcodec.h"
+#include "libavutil/avutil.h"
+#include "libavutil/imgutils.h"
+#endif
+
 namespace mozilla
 {
 
 FFmpegRuntimeLinker::LinkStatus FFmpegRuntimeLinker::sLinkStatus =
   LinkStatus_INIT;
 
 template <int V> class FFmpegDecoderModule
 {
@@ -23,31 +30,45 @@ public:
 };
 
 static const char* sLibs[] = {
 #if defined(XP_DARWIN)
   "libavcodec.56.dylib",
   "libavcodec.55.dylib",
   "libavcodec.54.dylib",
   "libavcodec.53.dylib",
+#if defined(MOZ_FFVPX)
+  "libmozavcodec.dylib"
+#endif
+#elif defined(XP_WIN)
+#if defined(MOZ_FFVPX)
+  "mozavcodec.dll"
+#endif
 #else
   "libavcodec-ffmpeg.so.56",
   "libavcodec.so.56",
   "libavcodec.so.55",
   "libavcodec.so.54",
   "libavcodec.so.53",
+#if defined(MOZ_FFVPX)
+  "libmozavcodec.so"
+#endif
 #endif
 };
 
 PRLibrary* FFmpegRuntimeLinker::sLinkedLib = nullptr;
+PRLibrary* FFmpegRuntimeLinker::sLinkedUtilLib = nullptr;
 const char* FFmpegRuntimeLinker::sLib = nullptr;
 static unsigned (*avcodec_version)() = nullptr;
 
+#ifdef __GNUC__
 #define AV_FUNC(func, ver) void (*func)();
-
+#else
+#define AV_FUNC(func, ver) decltype(func)* func;
+#endif
 #define LIBAVCODEC_ALLVERSION
 #include "FFmpegFunctionList.h"
 #undef LIBAVCODEC_ALLVERSION
 #undef AV_FUNC
 
 /* static */ bool
 FFmpegRuntimeLinker::Link()
 {
@@ -58,17 +79,28 @@ FFmpegRuntimeLinker::Link()
   MOZ_ASSERT(NS_IsMainThread());
 
   for (size_t i = 0; i < ArrayLength(sLibs); i++) {
     const char* lib = sLibs[i];
     PRLibSpec lspec;
     lspec.type = PR_LibSpec_Pathname;
     lspec.value.pathname = lib;
     sLinkedLib = PR_LoadLibraryWithFlags(lspec, PR_LD_NOW | PR_LD_LOCAL);
-    if (sLinkedLib) {
+#if defined(XP_WIN) && defined(MOZ_FFVPX)
+    // Unlike Unix, Linux and Mac ; loading libavcodec doesn't automatically
+    // load libavutil even though libavcodec depends on it.
+    // So manually load it.
+    PRLibSpec lspec2;
+    lspec2.type = PR_LibSpec_Pathname;
+    lspec2.value.pathname = "mozavutil.dll";
+    sLinkedUtilLib = PR_LoadLibraryWithFlags(lspec2, PR_LD_NOW | PR_LD_LOCAL);
+#else
+    sLinkedUtilLib = sLinkedLib;
+#endif
+    if (sLinkedLib && sLinkedUtilLib) {
       if (Bind(lib)) {
         sLib = lib;
         sLinkStatus = LinkStatus_SUCCEEDED;
         return true;
       }
       // Shouldn't happen but if it does then we try the next lib..
       Unlink();
     }
@@ -84,31 +116,31 @@ FFmpegRuntimeLinker::Link()
 
   sLinkStatus = LinkStatus_FAILED;
   return false;
 }
 
 /* static */ bool
 FFmpegRuntimeLinker::Bind(const char* aLibName)
 {
-  avcodec_version = (typeof(avcodec_version))PR_FindSymbol(sLinkedLib,
+  avcodec_version = (decltype(avcodec_version))PR_FindSymbol(sLinkedLib,
                                                            "avcodec_version");
   uint32_t major, minor;
   if (!GetVersion(major, minor)) {
     return false;
   }
   if (major > 55) {
     // All major greater than 56 currently use the same ABI as 55.
     major = 55;
   }
 
 #define LIBAVCODEC_ALLVERSION
 #define AV_FUNC(func, ver)                                                     \
-  if (ver == 0 || ver == major) {                                              \
-    if (!(func = (typeof(func))PR_FindSymbol(sLinkedLib, #func))) {            \
+  if (ver <= 0 || ver == major) {                                              \
+    if (!(func = (decltype(func))PR_FindSymbol(ver != 0 ? sLinkedUtilLib : sLinkedLib, #func))) { \
       FFMPEG_LOG("Couldn't load function " #func " from %s.", aLibName);       \
       return false;                                                            \
     }                                                                          \
   }
 #include "FFmpegFunctionList.h"
 #undef AV_FUNC
 #undef LIBAVCODEC_ALLVERSION
   return true;
@@ -122,33 +154,40 @@ FFmpegRuntimeLinker::CreateDecoderModule
   }
   uint32_t major, minor;
   if (!GetVersion(major, minor)) {
     return  nullptr;
   }
 
   RefPtr<PlatformDecoderModule> module;
   switch (major) {
+#ifndef XP_WIN
     case 53: module = FFmpegDecoderModule<53>::Create(); break;
     case 54: module = FFmpegDecoderModule<54>::Create(); break;
+#endif
+    case 55:
     default: module = FFmpegDecoderModule<55>::Create(); break;
   }
   return module.forget();
 }
 
 /* static */ void
 FFmpegRuntimeLinker::Unlink()
 {
+  if (sLinkedUtilLib && sLinkedUtilLib != sLinkedLib) {
+    PR_UnloadLibrary(sLinkedUtilLib);
+  }
   if (sLinkedLib) {
     PR_UnloadLibrary(sLinkedLib);
     sLinkedLib = nullptr;
     sLib = nullptr;
     sLinkStatus = LinkStatus_INIT;
     avcodec_version = nullptr;
   }
+  sLinkedUtilLib = nullptr;
 }
 
 /* static */ bool
 FFmpegRuntimeLinker::GetVersion(uint32_t& aMajor, uint32_t& aMinor)
 {
   if (!avcodec_version) {
     return false;
   }
--- a/dom/media/platforms/ffmpeg/FFmpegRuntimeLinker.h
+++ b/dom/media/platforms/ffmpeg/FFmpegRuntimeLinker.h
@@ -20,16 +20,17 @@ class FFmpegRuntimeLinker
 public:
   static bool Link();
   static void Unlink();
   static already_AddRefed<PlatformDecoderModule> CreateDecoderModule();
   static bool GetVersion(uint32_t& aMajor, uint32_t& aMinor);
 
 private:
   static PRLibrary* sLinkedLib;
+  static PRLibrary* sLinkedUtilLib;
   static const char* sLib;
 
   static bool Bind(const char* aLibName);
 
   static enum LinkStatus {
     LinkStatus_INIT = 0,
     LinkStatus_FAILED,
     LinkStatus_SUCCEEDED
--- a/dom/media/platforms/ffmpeg/libav55/moz.build
+++ b/dom/media/platforms/ffmpeg/libav55/moz.build
@@ -9,16 +9,21 @@ UNIFIED_SOURCES += [
     '../FFmpegDataDecoder.cpp',
     '../FFmpegDecoderModule.cpp',
     '../FFmpegH264Decoder.cpp',
 ]
 LOCAL_INCLUDES += [
     '..',
     'include',
 ]
-CXXFLAGS += [ '-Wno-deprecated-declarations' ]
 
-FINAL_LIBRARY = 'xul'
-
+if CONFIG['GNU_CXX']:
+  CXXFLAGS += [ '-Wno-deprecated-declarations' ]
 if CONFIG['CLANG_CXX']:
   CXXFLAGS += [
     '-Wno-unknown-attributes',
   ]
+if CONFIG['_MSC_VER']:
+  CXXFLAGS += [
+    '-wd4996', # deprecated declaration
+  ]
+
+FINAL_LIBRARY = 'xul'
--- a/dom/media/platforms/moz.build
+++ b/dom/media/platforms/moz.build
@@ -40,24 +40,29 @@ if CONFIG['MOZ_EME']:
 
 if CONFIG['MOZ_FFMPEG']:
     EXPORTS += [
         'ffmpeg/FFmpegRuntimeLinker.h',
     ]
     UNIFIED_SOURCES += [
         'ffmpeg/FFmpegRuntimeLinker.cpp',
     ]
+    if CONFIG['OS_ARCH'] != 'WINNT':
+        DIRS += [
+            'ffmpeg/libav53',
+            'ffmpeg/libav54',
+        ]
+    else:
+        LOCAL_INCLUDES += [
+            'ffmpeg/libav55/include',
+        ]
+
     DIRS += [
-        'ffmpeg/libav53',
-        'ffmpeg/libav54',
         'ffmpeg/libav55',
     ]
-    LOCAL_INCLUDES += [
-        'ffmpeg',
-    ]
 
 if CONFIG['MOZ_APPLEMEDIA']:
   EXPORTS += [
       'apple/AppleDecoderModule.h',
   ]
   UNIFIED_SOURCES += [
       'apple/AppleATDecoder.cpp',
       'apple/AppleCMLinker.cpp',
--- a/dom/media/test/test_can_play_type_mpeg.html
+++ b/dom/media/test/test_can_play_type_mpeg.html
@@ -107,40 +107,44 @@ function IsMacOSSnowLeopardOrLater() {
   if (!ver || ver.length != 3) {
     return false;
   }
   var major = ver[1] | 0;
   var minor = ver[2] | 0;
   return major == 10 && minor >= 6;
 }
 
+function IsLinux() {
+  return navigator.userAgent.indexOf("Linux") != -1;
+}
+
 function getPref(name) {
   var pref = false;
   try {
     pref = SpecialPowers.getBoolPref(name);
   } catch(ex) { }
   return pref;
 }
 
 function IsJellyBeanOrLater() {
   return androidVersion >= 16;
 }
 
 var haveMp4 = (getPref("media.wmf.enabled") && IsWindowsVistaOrLater()) ||
               IsMacOSSnowLeopardOrLater() ||
               IsJellyBeanOrLater() ||
               getPref("media.omx.enabled") ||
-              getPref("media.ffmpeg.enabled");
+              (IsLinux() && getPref("media.ffmpeg.enabled"));
 
 check_mp4(document.getElementById('v'), haveMp4);
 
 var haveMp3 = getPref("media.directshow.enabled") ||
               (getPref("media.wmf.enabled") && IsWindowsVistaOrLater()) ||
               (IsJellyBeanOrLater() && getPref("media.android-media-codec.enabled")) ||
-              getPref("media.ffmpeg.enabled") ||
+              (IsLinux() && getPref("media.ffmpeg.enabled")) ||
               getPref("media.apple.mp3.enabled");
 check_mp3(document.getElementById('v'), haveMp3);
 
 mediaTestCleanup();
 </script>
 </pre>
 </body>
 </html>
--- a/dom/promise/tests/unit/test_monitor_uncaught.js
+++ b/dom/promise/tests/unit/test_monitor_uncaught.js
@@ -60,17 +60,17 @@ add_task(function* test_observe_uncaught
                 names.get(promise)  + " (" + PromiseDebugging.getPromiseID(promise) + ")");
       Assert.ok(!this.observed.has(promise),
                 this.name + " observed a Promise that it has not observed yet");
       this.observed.add(promise);
       if (this.expected.size == 0) {
         this.resolve();
       } else {
         do_print(this.name + " is still waiting for " + this.expected.size + " observations:");
-        do_print(JSON.stringify([names.get(x) for (x of this.expected.values())]));
+        do_print(JSON.stringify(Array.from(this.expected.values(), (x) => names.get(x))));
       }
     },
   };
 
   let onLeftUncaught = new CallbackResults("onLeftUncaught");
   let onConsumed = new CallbackResults("onConsumed");
 
   let observer = {
@@ -208,17 +208,17 @@ add_task(function* test_observe_uncaught
   }
 
   do_print("Test setup, waiting for callbacks.");
   yield onLeftUncaught.blocker;
 
   do_print("All calls to onLeftUncaught are complete.");
   if (onConsumed.expected.size != 0) {
     do_print("onConsumed is still waiting for the following Promise:");
-    do_print(JSON.stringify([names.get(x) for (x of onConsumed.expected.values())]));
+    do_print(JSON.stringify(Array.from(onConsumed.expected.values(), (x) => names.get(x))));
     yield onConsumed.blocker;
   }
 
   do_print("All calls to onConsumed are complete.");
   let removed = PromiseDebugging.removeUncaughtRejectionObserver(observer);
   Assert.ok(removed, "removeUncaughtRejectionObserver succeeded");
   removed = PromiseDebugging.removeUncaughtRejectionObserver(observer);
   Assert.ok(!removed, "second call to removeUncaughtRejectionObserver didn't remove anything");
--- a/dom/security/test/cors/test_CrossSiteXHR.html
+++ b/dom/security/test/cors/test_CrossSiteXHR.html
@@ -666,17 +666,17 @@ function runTest() {
                (lName != "content-type" ||
                 ["text/plain",
                  "multipart/form-data",
                  "application/x-www-form-urlencoded"]
                    .indexOf(test.headers[name].toLowerCase()) == -1);
       }
       req.url += "&headers=" + escape(test.headers.toSource());
       reqHeaders =
-        escape([name for (name in test.headers)]
+        escape(Object.keys(test.headers)
                .filter(isUnsafeHeader)
                .map(String.toLowerCase)
                .sort()
                .join(","));
       req.url += reqHeaders ? "&requestHeaders=" + reqHeaders : "";
     }
     if ("allowHeaders" in test)
       req.url += "&allowHeaders=" + escape(test.allowHeaders);
--- a/dom/tests/mochitest/bugs/test_bug691707.html
+++ b/dom/tests/mochitest/bugs/test_bug691707.html
@@ -16,17 +16,20 @@ https://bugzilla.mozilla.org/show_bug.cg
   
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 691707 **/
 
 var nodeList = document.body.childNodes;
-var properties = [i for (i in nodeList)];
+var properties = [];
+for (let i in nodeList) {
+  properties.push(i);
+}
 for (var j = 1; j < nodeList.length; ++j)
     ok(properties.indexOf("" + j) >= 0, "Enumerating hit all numeric properties");
 ok(properties.indexOf("item") >= 0, "Enumerating hit 'item' from the prototype");
 ok(properties.indexOf("length") >= 0, "Enumerating hit 'length' from the prototype");
 
 </script>
 </pre>
 </body>
--- a/dom/tests/mochitest/fetch/test_fetch_cors.js
+++ b/dom/tests/mochitest/fetch/test_fetch_cors.js
@@ -751,17 +751,17 @@ function testModeCors() {
                (lName != "content-type" ||
                 ["text/plain",
                  "multipart/form-data",
                  "application/x-www-form-urlencoded"]
                    .indexOf(test.headers[name].toLowerCase()) == -1);
       }
       req.url += "&headers=" + escape(test.headers.toSource());
       reqHeaders =
-        escape([name for (name in test.headers)]
+        escape(Object.keys(test.headers)
                .filter(isUnsafeHeader)
                .map(String.toLowerCase)
                .sort()
                .join(","));
       req.url += reqHeaders ? "&requestHeaders=" + reqHeaders : "";
     }
     if ("allowHeaders" in test)
       req.url += "&allowHeaders=" + escape(test.allowHeaders);
--- a/dom/tests/mochitest/webapps/test_cross_origin.xul
+++ b/dom/tests/mochitest/webapps/test_cross_origin.xul
@@ -65,32 +65,32 @@ function installAppFromOtherOrigin(next)
       window.removeEventListener("message", onMessage, false);
       next();
     }
   }, false);
 }
 
 function getInstalled(next) {
   navigator.mozApps.getInstalled().onsuccess = function onGetInstalled() {
-    var app1 = [a for (a of this.result) if (a.manifestURL == url1)][0];
+    var app1 = Array.from(this.result).filter((a) => a.manifestURL == url1)[0];
     ok(app1, "getInstalled() includes app installed from own origin");
 
-    var app2 = [a for (a of this.result) if (a.manifestURL == url2)][0];
+    var app2 = Array.from(this.result).filter((a) => a.manifestURL == url2)[0];
     ok(!app2, "getInstalled() excludes app installed from other origin");
 
     next();
   };
 }
 
 function getAll(next) {
   navigator.mozApps.mgmt.getAll().onsuccess = function onMgmtGetAll() {
-    var app1 = [a for (a of this.result) if (a.manifestURL == url1)][0];
+    var app1 = Array.from(this.result).filter((a) => a.manifestURL == url1)[0];
     ok(app1, "mgmt.getAll() includes app installed from own origin");
 
-    var app2 = [a for (a of this.result) if (a.manifestURL == url2)][0];
+    var app2 = Array.from(this.result).filter((a) => a.manifestURL == url2)[0];
     ok(app2, "mgmt.getAll() includes app installed from other origin");
 
     next();
   };
 }
 
 function uninstall(next) {
   confirmNextPopup();
--- a/dom/tests/mochitest/webapps/test_install_app.xul
+++ b/dom/tests/mochitest/webapps/test_install_app.xul
@@ -60,17 +60,17 @@ function install(next) {
 
     next();
   }
 }
 
 function getInstalledReturnsApp(next) {
   navigator.mozApps.getInstalled().onsuccess = function onGetInstalled() {
     // Retrieve the app we just installed from the list of installed apps.
-    var a = [a for (a of this.result) if (a.manifestURL == url)][0];
+    var a = Array.from(this.result).filter((a) => a.manifestURL == url)[0];
 
     // Compare the values of the two app objects to make sure install()
     // and getInstalled() return identical objects.
     isDeeply(a, app, "getInstalled() returns app identical to install()");
 
     next();
   };
 }
@@ -84,17 +84,17 @@ function getSelf(next) {
 }
 
 function uninstall(next) {
   confirmNextPopup();
   navigator.mozApps.mgmt.uninstall(app).onsuccess = function onUninstall() {
     // Try to retrieve the app we just uninstalled, to make sure it no longer
     // exists in the registry.
     navigator.mozApps.getInstalled().onsuccess = function onGetInstalled() {
-      var a = [a for (a of this.result) if (a.manifestURL == url)][0];
+      var a = Array.from(this.result).filter((a) => a.manifestURL == url)[0];
       is(a, undefined, "getInstalled() returns nothing again after uninstall");
 
       next();
     };
   };
 }
 
 ]]>
--- a/dom/tests/mochitest/webapps/test_launch_paths.xul
+++ b/dom/tests/mochitest/webapps/test_launch_paths.xul
@@ -60,17 +60,17 @@ function install(next) {
 
     next();
   }
 }
 
 function getInstalledReturnsApp(next) {
   navigator.mozApps.getInstalled().onsuccess = function onGetInstalled() {
     // Retrieve the app we just installed from the list of installed apps.
-    var a = [a for (a of this.result) if (a.manifestURL == url)][0];
+    var a = Array.from(this.result).filter((a) => a.manifestURL == url)[0];
 
     // Compare the values of the two app objects to make sure install()
     // and getInstalled() return identical objects.
     isDeeply(a, app, "getInstalled() returns app identical to install()");
 
     next();
   };
 }
@@ -84,17 +84,17 @@ function getSelf(next) {
 }
 
 function uninstall(next) {
   confirmNextPopup();
   navigator.mozApps.mgmt.uninstall(app).onsuccess = function onUninstall() {
     // Try to retrieve the app we just uninstalled, to make sure it no longer
     // exists in the registry.
     navigator.mozApps.getInstalled().onsuccess = function onGetInstalled() {
-      var a = [a for (a of this.result) if (a.manifestURL == url)][0];
+      var a = Array.from(this.result).filter((a) => a.manifestURL == url)[0];
       is(a, undefined, "getInstalled() returns nothing again after uninstall");
 
       next();
     };
   };
 }
 
 ]]>
--- a/dom/tests/mochitest/webapps/test_list_api.xul
+++ b/dom/tests/mochitest/webapps/test_list_api.xul
@@ -24,17 +24,21 @@ var props = {
   getInstalled: "function",
   getLocalizationResource: "function",
   getSelf: "function",
   install: "function",
   installPackage: "function",
   mgmt: "object",
 };
 
-isDeeply([p for (p in navigator.mozApps)].sort(), Object.keys(props).sort(),
+var apps = [];
+for (let p in navigator.mozApps) {
+  apps.push(p);
+}
+isDeeply(apps.sort(), Object.keys(props).sort(),
          "navigator.mozApps has only the expected properties");
 
 for (var p in props) {
   is(typeof navigator.mozApps[p], props[p], "typeof " + p);
 }
 
 var mgmtProps = {
   addEventListener: "function",
@@ -51,17 +55,22 @@ var mgmtProps = {
   ownerGlobal: "object",
   removeEventListener: "function",
   setEventHandler: "function",
   extractManifest: "function",
   import: "function",
   setEnabled: "function"
 };
 
-isDeeply([p for (p in navigator.mozApps.mgmt)].sort(),
+var mgmts = [];
+for (let p in navigator.mozApps.mgmt) {
+  mgmts.push(p);
+}
+
+isDeeply(mgmts.sort(),
          Object.keys(mgmtProps).sort(),
          "navigator.mozApps.mgmt has only the expected properties");
 
 for (var p in mgmtProps) {
   is(typeof navigator.mozApps.mgmt[p], mgmtProps[p], "typeof mgmt." + p);
 }
 
 </script>
--- a/dom/webidl/AnimationTimeline.webidl
+++ b/dom/webidl/AnimationTimeline.webidl
@@ -9,10 +9,9 @@
  * Copyright © 2015 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 [Func="nsDocument::IsWebAnimationsEnabled"]
 interface AnimationTimeline {
   [BinaryName="currentTimeAsDouble"]
   readonly attribute double? currentTime;
-  sequence<Animation>        getAnimations ();
 };
--- a/dom/webidl/ChromeUtils.webidl
+++ b/dom/webidl/ChromeUtils.webidl
@@ -23,16 +23,25 @@ interface ChromeUtils : ThreadSafeChrome
    * of |pattern|.
    *
    * @param originAttrs       The originAttributes under consideration.
    * @param pattern           The pattern to use for matching.
    */
   static boolean
   originAttributesMatchPattern(optional OriginAttributesDictionary originAttrs,
                                optional OriginAttributesPatternDictionary pattern);
+
+  /**
+   * Returns an OriginAttributes dictionary using the origin URI but forcing
+   * the passed userContextId.
+   */
+  [Throws]
+  static OriginAttributesDictionary
+  createOriginAttributesWithUserContextId(DOMString origin,
+                                          unsigned long userContextId);
 };
 
 /**
  * Used by principals and the script security manager to represent origin
  * attributes. The first dictionary is designed to contain the full set of
  * OriginAttributes, the second is used for pattern-matching (i.e. does this
  * OriginAttributesDictionary match the non-empty attributes in this pattern).
  *
--- a/dom/webidl/Document.webidl
+++ b/dom/webidl/Document.webidl
@@ -298,16 +298,18 @@ partial interface Document {
   //(Not implemented)Element?  find(DOMString selectors, optional (Element or sequence<Node>)? refNodes);
   //(Not implemented)NodeList  findAll(DOMString selectors, optional (Element or sequence<Node>)? refNodes);
 };
 
 // http://w3c.github.io/web-animations/#extensions-to-the-document-interface
 partial interface Document {
   [Func="nsDocument::IsWebAnimationsEnabled"]
   readonly attribute DocumentTimeline timeline;
+  [Func="nsDocument::IsWebAnimationsEnabled"]
+  sequence<Animation> getAnimations();
 };
 
 //  Mozilla extensions of various sorts
 partial interface Document {
   // nsIDOMDocumentXBL.  Wish we could make these [ChromeOnly], but
   // that would likely break bindings running with the page principal.
   [Func="IsChromeOrXBL"]
   NodeList? getAnonymousNodes(Element elt);
--- a/dom/webidl/FontFace.webidl
+++ b/dom/webidl/FontFace.webidl
@@ -14,16 +14,17 @@ typedef (ArrayBuffer or ArrayBufferView)
 
 dictionary FontFaceDescriptors {
   DOMString style = "normal";
   DOMString weight = "normal";
   DOMString stretch = "normal";
   DOMString unicodeRange = "U+0-10FFFF";
   DOMString variant = "normal";
   DOMString featureSettings = "normal";
+  DOMString display = "auto";
 };
 
 enum FontFaceLoadStatus { "unloaded", "loading", "loaded", "error" };
 
 // Bug 1072107 is for exposing this in workers.
 // [Exposed=(Window,Worker)]
 [Constructor(DOMString family,
              (DOMString or BinaryData) source,
@@ -32,16 +33,17 @@ enum FontFaceLoadStatus { "unloaded", "l
 interface FontFace {
   [SetterThrows] attribute DOMString family;
   [SetterThrows] attribute DOMString style;
   [SetterThrows] attribute DOMString weight;
   [SetterThrows] attribute DOMString stretch;
   [SetterThrows] attribute DOMString unicodeRange;
   [SetterThrows] attribute DOMString variant;
   [SetterThrows] attribute DOMString featureSettings;
+  [SetterThrows, Pref="layout.css.font-display.enabled"] attribute DOMString display;
 
   readonly attribute FontFaceLoadStatus status;
 
   [Throws]
   Promise<FontFace> load();
 
   [Throws]
   readonly attribute Promise<FontFace> loaded;
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -111,21 +111,32 @@
 // JS_MaybeGC will run once every second during normal execution.
 #define PERIODIC_GC_TIMER_DELAY_SEC 1
 
 // A shrinking GC will run five seconds after the last event is processed.
 #define IDLE_GC_TIMER_DELAY_SEC 5
 
 #define PREF_WORKERS_ENABLED "dom.workers.enabled"
 
-#ifdef WORKER_LOGGING
-#define LOG(_args) do { printf _args ; fflush(stdout); } while (0)
-#else
-#define LOG(_args) do { } while (0)
-#endif
+static mozilla::LazyLogModule sWorkerPrivateLog("WorkerPrivate");
+static mozilla::LazyLogModule sWorkerTimeoutsLog("WorkerTimeouts");
+
+mozilla::LogModule*
+WorkerLog()
+{
+  return sWorkerPrivateLog;
+}
+
+mozilla::LogModule*
+TimeoutsLog()
+{
+  return sWorkerTimeoutsLog;
+}
+
+#define LOG(log, _args) MOZ_LOG(log, LogLevel::Debug, _args);
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::ipc;
 
 USING_WORKERS_NAMESPACE
 
 MOZ_DEFINE_MALLOC_SIZE_OF(JsWorkerMallocSizeOf)
@@ -1114,24 +1125,29 @@ private:
     }
 
     return ReportError(aCx, parent, fireAtScope, aWorkerPrivate, mMessage,
                        mFilename, mLine, mLineNumber, mColumnNumber, mFlags,
                        mErrorNumber, mExnType, mMutedError, innerWindowId);
   }
 };
 
-class TimerRunnable final : public WorkerRunnable
+class TimerRunnable final : public WorkerRunnable,
+                            public nsITimerCallback
 {
 public:
+  NS_DECL_ISUPPORTS_INHERITED
+
   explicit TimerRunnable(WorkerPrivate* aWorkerPrivate)
   : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
   { }
 
 private:
+  ~TimerRunnable() {}
+
   virtual bool
   PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
   {
     // Silence bad assertions.
     return true;
   }
 
   virtual void
@@ -1141,18 +1157,26 @@ private:
     // Silence bad assertions.
   }
 
   virtual bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
   {
     return aWorkerPrivate->RunExpiredTimeouts(aCx);
   }
+
+  NS_IMETHOD
+  Notify(nsITimer* aTimer) override
+  {
+    return Run();
+  }
 };
 
+NS_IMPL_ISUPPORTS_INHERITED(TimerRunnable, WorkerRunnable, nsITimerCallback)
+
 class DebuggerImmediateRunnable : public WorkerRunnable
 {
   RefPtr<dom::Function> mHandler;
 
 public:
   explicit DebuggerImmediateRunnable(WorkerPrivate* aWorkerPrivate,
                                      dom::Function& aHandler)
   : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
@@ -4632,18 +4656,18 @@ WorkerPrivate::SetGCTimerMode(GCTimerMod
       (aMode == IdleTimer && mIdleGCTimerRunning)) {
     return;
   }
 
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mGCTimer->Cancel()));
 
   mPeriodicGCTimerRunning = false;
   mIdleGCTimerRunning = false;
-
-  LOG(("Worker %p canceled GC timer because %s\n", this,
+  LOG(WorkerLog(),
+      ("Worker %p canceled GC timer because %s\n", this,
        aMode == PeriodicTimer ?
        "periodic" :
        aMode == IdleTimer ? "idle" : "none"));
 
   if (aMode == NoTimer) {
     return;
   }
 
@@ -4665,36 +4689,36 @@ WorkerPrivate::SetGCTimerMode(GCTimerMod
   }
 
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mGCTimer->SetTarget(target)));
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
     mGCTimer->InitWithNamedFuncCallback(DummyCallback, nullptr, delay, type,
                                         "dom::workers::DummyCallback(2)")));
 
   if (aMode == PeriodicTimer) {
-    LOG(("Worker %p scheduled periodic GC timer\n", this));
+    LOG(WorkerLog(), ("Worker %p scheduled periodic GC timer\n", this));
     mPeriodicGCTimerRunning = true;
   }
   else {
-    LOG(("Worker %p scheduled idle GC timer\n", this));
+    LOG(WorkerLog(), ("Worker %p scheduled idle GC timer\n", this));
     mIdleGCTimerRunning = true;
   }
 }
 
 void
 WorkerPrivate::ShutdownGCTimers()
 {
   AssertIsOnWorkerThread();
 
   MOZ_ASSERT(mGCTimer);
 
   // Always make sure the timer is canceled.
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mGCTimer->Cancel()));
 
-  LOG(("Worker %p killed the GC timer\n", this));
+  LOG(WorkerLog(), ("Worker %p killed the GC timer\n", this));
 
   mGCTimer = nullptr;
   mPeriodicGCTimerTarget = nullptr;
   mIdleGCTimerTarget = nullptr;
   mPeriodicGCTimerRunning = false;
   mIdleGCTimerRunning = false;
 }
 
@@ -5199,18 +5223,20 @@ WorkerPrivate::NotifyFeatures(JSContext*
   }
 }
 
 void
 WorkerPrivate::CancelAllTimeouts(JSContext* aCx)
 {
   AssertIsOnWorkerThread();
 
+  LOG(TimeoutsLog(), ("Worker %p CancelAllTimeouts.\n", this));
+
   if (mTimerRunning) {
-    NS_ASSERTION(mTimer, "Huh?!");
+    NS_ASSERTION(mTimer && mTimerRunnable, "Huh?!");
     NS_ASSERTION(!mTimeouts.IsEmpty(), "Huh?!");
 
     if (NS_FAILED(mTimer->Cancel())) {
       NS_WARNING("Failed to cancel timer!");
     }
 
     for (uint32_t index = 0; index < mTimeouts.Length(); index++) {
       mTimeouts[index]->mCanceled = true;
@@ -5224,16 +5250,17 @@ WorkerPrivate::CancelAllTimeouts(JSConte
   }
 #ifdef DEBUG
   else if (!mRunningExpiredTimeouts) {
     NS_ASSERTION(mTimeouts.IsEmpty(), "Huh?!");
   }
 #endif
 
   mTimer = nullptr;
+  mTimerRunnable = nullptr;
 }
 
 already_AddRefed<nsIEventTarget>
 WorkerPrivate::CreateNewSyncLoop()
 {
   AssertIsOnWorkerThread();
 
   nsCOMPtr<nsIThreadInternal> thread = do_QueryInterface(NS_GetCurrentThread());
@@ -5895,40 +5922,32 @@ WorkerPrivate::SetTimeout(JSContext* aCx
     if (!nsJSUtils::GetCallingLocation(aCx, newInfo->mFilename, &newInfo->mLineNumber)) {
       NS_WARNING("Failed to get calling location!");
     }
   }
 
   nsAutoPtr<TimeoutInfo>* insertedInfo =
     mTimeouts.InsertElementSorted(newInfo.forget(), GetAutoPtrComparator(mTimeouts));
 
+  LOG(TimeoutsLog(), ("Worker %p has new timeout: delay=%d interval=%s\n",
+                      this, aTimeout, aIsInterval ? "yes" : "no"));
+
   // If the timeout we just made is set to fire next then we need to update the
-  // timer.
-  if (insertedInfo == mTimeouts.Elements()) {
+  // timer, unless we're currently running timeouts.
+  if (insertedInfo == mTimeouts.Elements() && !mRunningExpiredTimeouts) {
     nsresult rv;
 
     if (!mTimer) {
-      nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
+      mTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
       if (NS_FAILED(rv)) {
         aRv.Throw(rv);
         return 0;
       }
 
-      RefPtr<TimerRunnable> runnable = new TimerRunnable(this);
-
-      RefPtr<TimerThreadEventTarget> target =
-        new TimerThreadEventTarget(this, runnable);
-
-      rv = timer->SetTarget(target);
-      if (NS_FAILED(rv)) {
-        aRv.Throw(rv);
-        return 0;
-      }
-
-      timer.swap(mTimer);
+      mTimerRunnable = new TimerRunnable(this);
     }
 
     if (!mTimerRunning) {
       if (!ModifyBusyCountFromWorker(aCx, true)) {
         aRv.Throw(NS_ERROR_FAILURE);
         return 0;
       }
       mTimerRunning = true;
@@ -5968,27 +5987,33 @@ WorkerPrivate::RunExpiredTimeouts(JSCont
 
   // We may be called recursively (e.g. close() inside a timeout) or we could
   // have been canceled while this event was pending, bail out if there is
   // nothing to do.
   if (mRunningExpiredTimeouts || !mTimerRunning) {
     return true;
   }
 
-  NS_ASSERTION(mTimer, "Must have a timer!");
+  NS_ASSERTION(mTimer && mTimerRunnable, "Must have a timer!");
   NS_ASSERTION(!mTimeouts.IsEmpty(), "Should have some work to do!");
 
   bool retval = true;
 
   AutoPtrComparator<TimeoutInfo> comparator = GetAutoPtrComparator(mTimeouts);
   JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
 
   // We want to make sure to run *something*, even if the timer fired a little
   // early. Fudge the value of now to at least include the first timeout.
-  const TimeStamp now = std::max(TimeStamp::Now(), mTimeouts[0]->mTargetTime);
+  const TimeStamp actual_now = TimeStamp::Now();
+  const TimeStamp now = std::max(actual_now, mTimeouts[0]->mTargetTime);
+
+  if (now != actual_now) {
+    LOG(TimeoutsLog(), ("Worker %p fudged timeout by %f ms.\n", this,
+                        (now - actual_now).ToMilliseconds()));
+  }
 
   nsAutoTArray<TimeoutInfo*, 10> expiredTimeouts;
   for (uint32_t index = 0; index < mTimeouts.Length(); index++) {
     nsAutoPtr<TimeoutInfo>& info = mTimeouts[index];
     if (info->mTargetTime > now) {
       break;
     }
     expiredTimeouts.AppendElement(info);
@@ -6000,16 +6025,19 @@ WorkerPrivate::RunExpiredTimeouts(JSCont
   // Run expired timeouts.
   for (uint32_t index = 0; index < expiredTimeouts.Length(); index++) {
     TimeoutInfo*& info = expiredTimeouts[index];
 
     if (info->mCanceled) {
       continue;
     }
 
+    LOG(TimeoutsLog(), ("Worker %p executing timeout with original delay %f ms.\n",
+                        this, info->mInterval.ToMilliseconds()));
+
     // Always call JS_ReportPendingException if something fails, and if
     // JS_ReportPendingException returns false (i.e. uncatchable exception) then
     // break out of the loop.
 
     if (!info->mTimeoutCallable.isUndefined()) {
       JS::Rooted<JS::Value> rval(aCx);
       JS::HandleValueArray args =
         JS::HandleValueArray::fromMarkedLocation(info->mExtraArgVals.Length(),
@@ -6097,26 +6125,36 @@ WorkerPrivate::RunExpiredTimeouts(JSCont
 
   return retval;
 }
 
 bool
 WorkerPrivate::RescheduleTimeoutTimer(JSContext* aCx)
 {
   AssertIsOnWorkerThread();
+  MOZ_ASSERT(!mRunningExpiredTimeouts);
   NS_ASSERTION(!mTimeouts.IsEmpty(), "Should have some timeouts!");
-  NS_ASSERTION(mTimer, "Should have a timer!");
+  NS_ASSERTION(mTimer && mTimerRunnable, "Should have a timer!");
+
+  // NB: This is important! The timer may have already fired, e.g. if a timeout
+  // callback itself calls setTimeout for a short duration and then takes longer
+  // than that to finish executing. If that has happened, it's very important
+  // that we don't execute the event that is now pending in our event queue, or
+  // our code in RunExpiredTimeouts to "fudge" the timeout value will unleash an
+  // early timeout when we execute the event we're about to queue.
+  mTimer->Cancel();
 
   double delta =
     (mTimeouts[0]->mTargetTime - TimeStamp::Now()).ToMilliseconds();
   uint32_t delay = delta > 0 ? std::min(delta, double(UINT32_MAX)) : 0;
 
-  nsresult rv = mTimer->InitWithNamedFuncCallback(
-    DummyCallback, nullptr, delay, nsITimer::TYPE_ONE_SHOT,
-    "dom::workers::DummyCallback(3)");
+  LOG(TimeoutsLog(), ("Worker %p scheduled timer for %d ms, %d pending timeouts\n",
+                      this, delay, mTimeouts.Length()));
+
+  nsresult rv = mTimer->InitWithCallback(mTimerRunnable, delay, nsITimer::TYPE_ONE_SHOT);
   if (NS_FAILED(rv)) {
     JS_ReportError(aCx, "Failed to start timer!");
     return false;
   }
 
   return true;
 }
 
@@ -6213,27 +6251,27 @@ WorkerPrivate::GarbageCollectInternal(JS
   if (aShrinking || aCollectChildren) {
     JSRuntime* rt = JS_GetRuntime(aCx);
     JS::PrepareForFullGC(rt);
 
     if (aShrinking) {
       JS::GCForReason(rt, GC_SHRINK, JS::gcreason::DOM_WORKER);
 
       if (!aCollectChildren) {
-        LOG(("Worker %p collected idle garbage\n", this));
+        LOG(WorkerLog(), ("Worker %p collected idle garbage\n", this));
       }
     }
     else {
       JS::GCForReason(rt, GC_NORMAL, JS::gcreason::DOM_WORKER);
-      LOG(("Worker %p collected garbage\n", this));
+      LOG(WorkerLog(), ("Worker %p collected garbage\n", this));
     }
   }
   else {
     JS_MaybeGC(aCx);
-    LOG(("Worker %p collected periodic garbage\n", this));
+    LOG(WorkerLog(), ("Worker %p collected periodic garbage\n", this));
   }
 
   if (aCollectChildren) {
     for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
       mChildWorkers[index]->GarbageCollect(aCx, aShrinking);
     }
   }
 }
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -909,16 +909,17 @@ class WorkerPrivate : public WorkerPriva
   };
 
   // This is only modified on the worker thread, but in DEBUG builds
   // AssertValidSyncLoop function iterates it on other threads. Therefore
   // modifications are done with mMutex held *only* in DEBUG builds.
   nsTArray<nsAutoPtr<SyncLoopInfo>> mSyncLoopStack;
 
   nsCOMPtr<nsITimer> mTimer;
+  nsCOMPtr<nsITimerCallback> mTimerRunnable;
 
   nsCOMPtr<nsITimer> mGCTimer;
   nsCOMPtr<nsIEventTarget> mPeriodicGCTimerTarget;
   nsCOMPtr<nsIEventTarget> mIdleGCTimerTarget;
 
   RefPtr<MemoryReporter> mMemoryReporter;
 
   // fired on the main thread if the worker script fails to load
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -321,16 +321,17 @@ WorkerGlobalScope::Dump(const Optional<n
 #if !(defined(DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
   if (!mWorkerPrivate->DumpEnabled()) {
     return;
   }
 #endif
 
   NS_ConvertUTF16toUTF8 str(aString.Value());
 
+  MOZ_LOG(nsContentUtils::DOMDumpLog(), LogLevel::Debug, ("[Worker.Dump] %s", str.get()));
 #ifdef ANDROID
   __android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", str.get());
 #endif
   fputs(str.get(), stdout);
   fflush(stdout);
 }
 
 Performance*
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/crashtests/1153636.html
@@ -0,0 +1,5 @@
+<script>
+
+new Worker("data:text/javascript;charset=UTF-8,self.addEventListener('',function(){},false);");
+
+</script>
--- a/dom/workers/test/crashtests/crashtests.list
+++ b/dom/workers/test/crashtests/crashtests.list
@@ -1,3 +1,4 @@
 load 779707.html
 load 943516.html
+load 1153636.html
 load 1158031.html
--- a/dom/xslt/tests/mochitest/test_bug427060.html
+++ b/dom/xslt/tests/mochitest/test_bug427060.html
@@ -15,32 +15,28 @@ https://bugzilla.mozilla.org/show_bug.cg
   
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 /** Test for Bug 427060 **/
 
 var xmldoc, xsltdoc;
-[ xmldoc, xsltdoc ] = [ new DOMParser().parseFromString(xml, "text/xml") for (xml of [ 
 
-    '<opml version="1.0"><body></body></opml>' ,
-
-    '<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">\n\
+xmldoc = new DOMParser().parseFromString('<opml version="1.0"><body></body></opml>', "text/xml");
+xsltdoc = new DOMParser().parseFromString('<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">\n\
     	<xsl:template match="/opml">\n\
     		<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\n\
     			<head>\n\
     				<base target="_blank"></base>\n\
     			</head>\n\
     			<body></body>\n\
     		</html>\n\
     	</xsl:template>\n\
-    </xsl:stylesheet>'
-
-]) ];
+    </xsl:stylesheet>', "text/xml");
 
 var processor = new XSLTProcessor;
 processor.importStylesheet(xsltdoc);
 try
 {
   var result = processor.transformToDocument(xmldoc);
 }
 catch (e)
--- a/dom/xslt/tests/mochitest/test_bug440974.html
+++ b/dom/xslt/tests/mochitest/test_bug440974.html
@@ -21,26 +21,23 @@ https://bugzilla.mozilla.org/show_bug.cg
 
 function isTxResult(node)
 {
   return node.namespaceURI == "http://www.mozilla.org/TransforMiix" &&
          node.localName == "result";
 }
 
 var xmldoc, xsltdoc;
-[ xmldoc, xsltdoc ] = [ new DOMParser().parseFromString(xml, "text/xml") for (xml of [ 
 
-    '<items><item><id>1</id></item><item><id>2</id></item><item><id>3</id></item></items>' ,
-
-    '<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">\n\
+xmldoc = new DOMParser().parseFromString('<items><item><id>1</id></item><item><id>2</id></item><item><id>3</id></item></items>', "text/xml");
+xsltdoc = new DOMParser().parseFromString('<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">\n\
         <xsl:output method="xml" />\n\
         <xsl:template match="item"><foo id="{id}"/></xsl:template>\n\
-    </xsl:stylesheet>'
+    </xsl:stylesheet>', "text/xml");
 
-]) ];
 var processor = new XSLTProcessor;
 processor.importStylesheet(xsltdoc);
 var result = processor.transformToDocument(xmldoc);
 var resultElements = Array.prototype.filter.call(result.getElementsByTagName('*'), isTxResult);
 is(resultElements.length, 1, "there should be only one 'transformiix:result' element"); 
 is(resultElements[0], result.documentElement, "the 'transformiix:result' element should be the document element"); 
 
 </script>
--- a/dom/xul/nsXULElement.cpp
+++ b/dom/xul/nsXULElement.cpp
@@ -30,16 +30,17 @@
 #include "nsIDOMNodeList.h"
 #include "nsIDOMXULCommandDispatcher.h"
 #include "nsIDOMXULElement.h"
 #include "nsIDOMElementCSSInlineStyle.h"
 #include "nsIDOMXULSelectCntrlItemEl.h"
 #include "nsIDocument.h"
 #include "nsLayoutStylesheetCache.h"
 #include "mozilla/AsyncEventDispatcher.h"
+#include "mozilla/ClearOnShutdown.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/EventStateManager.h"
 #include "mozilla/EventStates.h"
 #include "nsFocusManager.h"
 #include "nsHTMLStyleSheet.h"
 #include "nsNameSpaceManager.h"
 #include "nsIObjectInputStream.h"
 #include "nsIObjectOutputStream.h"
@@ -2696,55 +2697,90 @@ nsXULPrototypeScript::DeserializeOutOfLi
             }
         }
     }
     return rv;
 }
 
 class NotifyOffThreadScriptCompletedRunnable : public nsRunnable
 {
-    RefPtr<nsIOffThreadScriptReceiver> mReceiver;
+    // An array of all outstanding script receivers. All reference counting of
+    // these objects happens on the main thread. When we return to the main
+    // thread from script compilation we make sure our receiver is still in
+    // this array (still alive) before proceeding. This array is cleared during
+    // shutdown, potentially before all outstanding script compilations have
+    // finished. We do not need to worry about pointer replay here, because
+    // a) we should not be starting script compilation after clearing this
+    // array and b) in all other cases the receiver will still be alive.
+    static StaticAutoPtr<nsTArray<nsCOMPtr<nsIOffThreadScriptReceiver>>> sReceivers;
+    static bool sSetupClearOnShutdown;
+
+    nsIOffThreadScriptReceiver* mReceiver;
     void *mToken;
 
 public:
-    NotifyOffThreadScriptCompletedRunnable(already_AddRefed<nsIOffThreadScriptReceiver> aReceiver,
+    NotifyOffThreadScriptCompletedRunnable(nsIOffThreadScriptReceiver* aReceiver,
                                            void *aToken)
       : mReceiver(aReceiver), mToken(aToken)
     {}
 
+    static void NoteReceiver(nsIOffThreadScriptReceiver* aReceiver) {
+        if (!sSetupClearOnShutdown) {
+            ClearOnShutdown(&sReceivers);
+            sSetupClearOnShutdown = true;
+            sReceivers = new nsTArray<nsCOMPtr<nsIOffThreadScriptReceiver>>();
+        }
+
+        // If we ever crash here, it's because we tried to lazy compile script
+        // too late in shutdown.
+        sReceivers->AppendElement(aReceiver);
+    }
+
     NS_DECL_NSIRUNNABLE
 };
 
+StaticAutoPtr<nsTArray<nsCOMPtr<nsIOffThreadScriptReceiver>>> NotifyOffThreadScriptCompletedRunnable::sReceivers;
+bool NotifyOffThreadScriptCompletedRunnable::sSetupClearOnShutdown = false;
+
 NS_IMETHODIMP
 NotifyOffThreadScriptCompletedRunnable::Run()
 {
     MOZ_ASSERT(NS_IsMainThread());
 
     // Note: this unroots mScript so that it is available to be collected by the
     // JS GC. The receiver needs to root the script before performing a call that
     // could GC.
     JSScript *script;
     {
         AutoSafeJSContext cx;
         JSAutoCompartment ac(cx, xpc::CompilationScope());
         script = JS::FinishOffThreadScript(cx, JS_GetRuntime(cx), mToken);
     }
 
-    return mReceiver->OnScriptCompileComplete(script, script ? NS_OK : NS_ERROR_FAILURE);
+    if (!sReceivers) {
+        // We've already shut down.
+        return NS_OK;
+    }
+
+    auto index = sReceivers->IndexOf(mReceiver);
+    MOZ_RELEASE_ASSERT(index != sReceivers->NoIndex);
+    nsCOMPtr<nsIOffThreadScriptReceiver> receiver = (*sReceivers)[index].forget();
+    sReceivers->RemoveElementAt(index);
+
+    return receiver->OnScriptCompileComplete(script, script ? NS_OK : NS_ERROR_FAILURE);
 }
 
 static void
 OffThreadScriptReceiverCallback(void *aToken, void *aCallbackData)
 {
     // Be careful not to adjust the refcount on the receiver, as this callback
     // may be invoked off the main thread.
     nsIOffThreadScriptReceiver* aReceiver = static_cast<nsIOffThreadScriptReceiver*>(aCallbackData);
     RefPtr<NotifyOffThreadScriptCompletedRunnable> notify =
-        new NotifyOffThreadScriptCompletedRunnable(
-            already_AddRefed<nsIOffThreadScriptReceiver>(aReceiver), aToken);
+        new NotifyOffThreadScriptCompletedRunnable(aReceiver, aToken);
     NS_DispatchToMainThread(notify);
 }
 
 nsresult
 nsXULPrototypeScript::Compile(JS::SourceBufferHolder& aSrcBuf,
                               nsIURI* aURI, uint32_t aLineNo,
                               nsIDocument* aDocument,
                               nsIOffThreadScriptReceiver *aOffThreadReceiver /* = nullptr */)
@@ -2773,18 +2809,17 @@ nsXULPrototypeScript::Compile(JS::Source
 
     if (aOffThreadReceiver && JS::CanCompileOffThread(cx, options, aSrcBuf.length())) {
         if (!JS::CompileOffThread(cx, options,
                                   aSrcBuf.get(), aSrcBuf.length(),
                                   OffThreadScriptReceiverCallback,
                                   static_cast<void*>(aOffThreadReceiver))) {
             return NS_ERROR_OUT_OF_MEMORY;
         }
-        // This reference will be consumed by the NotifyOffThreadScriptCompletedRunnable.
-        NS_ADDREF(aOffThreadReceiver);
+        NotifyOffThreadScriptCompletedRunnable::NoteReceiver(aOffThreadReceiver);
     } else {
         JS::Rooted<JSScript*> script(cx);
         if (!JS::Compile(cx, options, aSrcBuf, &script))
             return NS_ERROR_OUT_OF_MEMORY;
         Set(script);
     }
     return NS_OK;
 }
--- a/dom/xul/templates/tests/chrome/templates_shared.js
+++ b/dom/xul/templates/tests/chrome/templates_shared.js
@@ -153,17 +153,17 @@ function checkResults(root, step)
   setForCurrentStep(output, step);
 
   var error;
   var actualoutput = root;
   if (isTreeBuilder) {
     // convert the tree's view data into the equivalent DOM structure
     // for easier comparison
     actualoutput = treeViewToDOM(root);
-    var treechildrenElements = [e for (e of output.children) if (e.localName === "treechildren")];
+    var treechildrenElements = [...output.children].filter((e) => e.localName === "treechildren");
     error = compareOutput(actualoutput, treechildrenElements[0], false);
   }
   else {
     error = compareOutput(actualoutput, output, true);
   }
 
   var adjtestid = testid;
   if (step > 0)
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -37,16 +37,19 @@ struct _cairo_scaled_font;
 typedef _cairo_scaled_font cairo_scaled_font_t;
 
 struct ID3D10Device1;
 struct ID3D10Texture2D;
 struct ID3D11Texture2D;
 struct ID3D11Device;
 struct ID2D1Device;
 struct IDWriteRenderingParams;
+struct IDWriteFont;
+struct IDWriteFontFamily;
+struct IDWriteFontFace;
 
 class GrContext;
 
 struct CGContext;
 typedef struct CGContext *CGContextRef;
 
 namespace mozilla {
 
@@ -960,16 +963,45 @@ public:
   virtual void PushClipRect(const Rect &aRect) = 0;
 
   /** Pop a clip from the DrawTarget. A pop without a corresponding push will
    * be ignored.
    */
   virtual void PopClip() = 0;
 
   /**
+   * Push a 'layer' to the DrawTarget, a layer is a temporary surface that all
+   * drawing will be redirected to, this is used for example to support group
+   * opacity or the masking of groups. Clips must be balanced within a layer,
+   * i.e. between a matching PushLayer/PopLayer pair there must be as many
+   * PushClip(Rect) calls as there are PopClip calls.
+   *
+   * @param aOpaque Whether the layer will be opaque
+   * @param aOpacity Opacity of the layer
+   * @param aMask Mask applied to the layer
+   * @param aMaskTransform Transform applied to the layer mask
+   * @param aBounds Optional bounds in device space to which the layer is
+   *                limited in size.
+   * @param aCopyBackground Whether to copy the background into the layer, this
+   *                        is only supported when aOpaque is true.
+   */
+  virtual void PushLayer(bool aOpaque, Float aOpacity,
+                         SourceSurface* aMask,
+                         const Matrix& aMaskTransform,
+                         const IntRect& aBounds = IntRect(),
+                         bool aCopyBackground = false) { MOZ_CRASH(); }
+
+  /**
+   * This balances a call to PushLayer and proceeds to blend the layer back
+   * onto the background. This blend will blend the temporary surface back
+   * onto the target in device space using POINT sampling and operator over.
+   */
+  virtual void PopLayer() { MOZ_CRASH(); }
+
+  /**
    * Create a SourceSurface optimized for use with this DrawTarget from
    * existing bitmap data in memory.
    *
    * The SourceSurface does not take ownership of aData, and may be freed at any time.
    */
   virtual already_AddRefed<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
                                                                   const IntSize &aSize,
                                                                   int32_t aStride,
@@ -1105,16 +1137,20 @@ public:
   void SetOpaqueRect(const IntRect &aRect) {
     mOpaqueRect = aRect;
   }
 
   const IntRect &GetOpaqueRect() const {
     return mOpaqueRect;
   }
 
+  virtual bool IsCurrentGroupOpaque() {
+    return GetFormat() == SurfaceFormat::B8G8R8X8;
+  }
+
   virtual void SetPermitSubpixelAA(bool aPermitSubpixelAA) {
     mPermitSubpixelAA = aPermitSubpixelAA;
   }
 
   bool GetPermitSubpixelAA() {
     return mPermitSubpixelAA;
   }
 
@@ -1333,16 +1369,22 @@ public:
 
   static already_AddRefed<GlyphRenderingOptions>
     CreateDWriteGlyphRenderingOptions(IDWriteRenderingParams *aParams);
 
   static uint64_t GetD2DVRAMUsageDrawTarget();
   static uint64_t GetD2DVRAMUsageSourceSurface();
   static void D2DCleanup();
 
+  static already_AddRefed<ScaledFont>
+    CreateScaledFontForDWriteFont(IDWriteFont* aFont,
+                                  IDWriteFontFamily* aFontFamily,
+                                  IDWriteFontFace* aFontFace,
+                                  Float aSize);
+
 private:
   static ID2D1Device *mD2D1Device;
   static ID3D10Device1 *mD3D10Device;
   static ID3D11Device *mD3D11Device;
 #endif
 
   static DrawEventRecorder *mRecorder;
 };
--- a/gfx/2d/DrawTargetCairo.cpp
+++ b/gfx/2d/DrawTargetCairo.cpp
@@ -1229,16 +1229,28 @@ DrawTargetCairo::Fill(const Path *aPath,
     return;
 
   PathCairo* path = const_cast<PathCairo*>(static_cast<const PathCairo*>(aPath));
   path->SetPathOnContext(mContext);
 
   DrawPattern(aPattern, StrokeOptions(), aOptions, DRAW_FILL);
 }
 
+bool
+DrawTargetCairo::IsCurrentGroupOpaque()
+{
+  cairo_surface_t* surf = cairo_get_group_target(mContext);
+
+  if (!surf) {
+    return false;
+  }
+
+  return cairo_surface_get_content(surf) == CAIRO_CONTENT_COLOR;
+}
+
 void
 DrawTargetCairo::SetPermitSubpixelAA(bool aPermitSubpixelAA)
 {
   DrawTarget::SetPermitSubpixelAA(aPermitSubpixelAA);
 #ifdef MOZ_TREE_CAIRO
   cairo_surface_set_subpixel_antialiasing(mSurface,
     aPermitSubpixelAA ? CAIRO_SUBPIXEL_ANTIALIASING_ENABLED : CAIRO_SUBPIXEL_ANTIALIASING_DISABLED);
 #endif
@@ -1450,16 +1462,97 @@ DrawTargetCairo::PopClip()
   // so we'll save it now and restore it after the cairo_restore
   cairo_matrix_t mat;
   cairo_get_matrix(mContext, &mat);
 
   cairo_restore(mContext);
 
   cairo_set_matrix(mContext, &mat);
 }
+ 
+void
+DrawTargetCairo::PushLayer(bool aOpaque, Float aOpacity, SourceSurface* aMask,
+                          const Matrix& aMaskTransform, const IntRect& aBounds,
+                          bool aCopyBackground)
+{
+  cairo_content_t content = CAIRO_CONTENT_COLOR_ALPHA;
+
+  if (mFormat == SurfaceFormat::A8) {
+    content = CAIRO_CONTENT_ALPHA;
+  } else if (aOpaque) {
+    content = CAIRO_CONTENT_COLOR;
+  }
+
+  if (aCopyBackground) {
+    cairo_surface_t* source = cairo_get_group_target(mContext);
+    cairo_push_group_with_content(mContext, content);
+    cairo_surface_t* dest = cairo_get_group_target(mContext);
+    cairo_t* ctx = cairo_create(dest);
+    cairo_set_source_surface(ctx, source, 0, 0);
+    cairo_set_operator(ctx, CAIRO_OPERATOR_SOURCE);
+    cairo_paint(ctx);
+    cairo_destroy(ctx);
+  } else {
+    cairo_push_group_with_content(mContext, content);
+  }
+
+  PushedLayer layer(aOpacity, mPermitSubpixelAA);
+
+  if (aMask) {
+    cairo_surface_t* surf = GetCairoSurfaceForSourceSurface(aMask);
+    if (surf) {
+      layer.mMaskPattern = cairo_pattern_create_for_surface(surf);
+      cairo_matrix_t mat;
+      GfxMatrixToCairoMatrix(aMaskTransform, mat);
+      cairo_matrix_invert(&mat);
+      cairo_pattern_set_matrix(layer.mMaskPattern, &mat);
+      cairo_surface_destroy(surf);
+    } else {
+      gfxCriticalError() << "Failed to get cairo surface for mask surface!";
+    }
+  }
+
+  mPushedLayers.push_back(layer);
+
+  SetPermitSubpixelAA(aOpaque);
+}
+
+void
+DrawTargetCairo::PopLayer()
+{
+  MOZ_ASSERT(mPushedLayers.size());
+
+  cairo_set_operator(mContext, CAIRO_OPERATOR_OVER);
+
+  cairo_pop_group_to_source(mContext);
+
+  PushedLayer layer = mPushedLayers.back();
+  mPushedLayers.pop_back();
+
+  if (!layer.mMaskPattern) {
+    cairo_paint_with_alpha(mContext, layer.mOpacity);
+  } else {
+    if (layer.mOpacity != Float(1.0)) {
+      cairo_push_group_with_content(mContext, CAIRO_CONTENT_COLOR_ALPHA);
+
+      // Now draw the content using the desired operator
+      cairo_paint_with_alpha(mContext, layer.mOpacity);
+
+      cairo_pop_group_to_source(mContext);
+    }
+    cairo_mask(mContext, layer.mMaskPattern);
+  }
+
+  cairo_matrix_t mat;
+  GfxMatrixToCairoMatrix(mTransform, mat);
+  cairo_set_matrix(mContext, &mat);
+
+  cairo_pattern_destroy(layer.mMaskPattern);
+  SetPermitSubpixelAA(layer.mWasPermittingSubpixelAA);
+}
 
 already_AddRefed<PathBuilder>
 DrawTargetCairo::CreatePathBuilder(FillRule aFillRule /* = FillRule::FILL_WINDING */) const
 {
   return MakeAndAddRef<PathBuilderCairo>(aFillRule);
 }
 
 void
@@ -1617,17 +1710,17 @@ DrawTargetCairo::OptimizeSourceSurface(S
 #endif
 
   return surface.forget();
 }
 
 already_AddRefed<SourceSurface>
 DrawTargetCairo::CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const
 {
-  if (aSurface.mType == NativeSurfaceType::CAIRO_SURFACE) {
+  if (aSurface.mType == NativeSurfaceType::CAIRO_CONTEXT) {
     if (aSurface.mSize.width <= 0 ||
         aSurface.mSize.height <= 0) {
       gfxWarning() << "Can't create a SourceSurface without a valid size";
       return nullptr;
     }
     cairo_surface_t* surf = static_cast<cairo_surface_t*>(aSurface.mSurface);
     return MakeAndAddRef<SourceSurfaceCairo>(surf, aSurface.mSize, aSurface.mFormat);
   }
@@ -1766,19 +1859,16 @@ DrawTargetCairo::Init(unsigned char* aDa
                                         aSize.height,
                                         aStride);
   return InitAlreadyReferenced(surf, aSize);
 }
 
 void *
 DrawTargetCairo::GetNativeSurface(NativeSurfaceType aType)
 {
-  if (aType == NativeSurfaceType::CAIRO_SURFACE) {
-    return cairo_get_target(mContext);
-  }
   if (aType == NativeSurfaceType::CAIRO_CONTEXT) {
     return mContext;
   }
 
   return nullptr;
 }
 
 void
--- a/gfx/2d/DrawTargetCairo.h
+++ b/gfx/2d/DrawTargetCairo.h
@@ -60,16 +60,18 @@ public:
   virtual ~DrawTargetCairo();
 
   virtual bool IsValid() const override;
   virtual DrawTargetType GetType() const override;
   virtual BackendType GetBackendType() const override { return BackendType::CAIRO; }
   virtual already_AddRefed<SourceSurface> Snapshot() override;
   virtual IntSize GetSize() override;
 
+  virtual bool IsCurrentGroupOpaque() override;
+
   virtual void SetPermitSubpixelAA(bool aPermitSubpixelAA) override;
 
   virtual bool LockBits(uint8_t** aData, IntSize* aSize,
                         int32_t* aStride, SurfaceFormat* aFormat) override;
   virtual void ReleaseBits(uint8_t* aData) override;
 
   virtual void Flush() override;
   virtual void DrawSurface(SourceSurface *aSurface,
@@ -129,16 +131,22 @@ public:
   virtual void MaskSurface(const Pattern &aSource,
                            SourceSurface *aMask,
                            Point aOffset,
                            const DrawOptions &aOptions = DrawOptions()) override;
 
   virtual void PushClip(const Path *aPath) override;
   virtual void PushClipRect(const Rect &aRect) override;
   virtual void PopClip() override;
+  virtual void PushLayer(bool aOpaque, Float aOpacity,
+                         SourceSurface* aMask,
+                         const Matrix& aMaskTransform,
+                         const IntRect& aBounds = IntRect(),
+                         bool aCopyBackground = false) override;
+  virtual void PopLayer() override;
 
   virtual already_AddRefed<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const override;
 
   virtual already_AddRefed<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
                                                             const IntSize &aSize,
                                                             int32_t aStride,
                                                             SurfaceFormat aFormat) const override;
   virtual already_AddRefed<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const override;
@@ -209,16 +217,29 @@ private: // methods
 private: // data
   cairo_t* mContext;
   cairo_surface_t* mSurface;
   IntSize mSize;
   bool mTransformSingular;
 
   uint8_t* mLockedBits;
 
+  struct PushedLayer
+  {
+    PushedLayer(Float aOpacity, bool aWasPermittingSubpixelAA)
+      : mOpacity(aOpacity)
+      , mMaskPattern(nullptr)
+      , mWasPermittingSubpixelAA(aWasPermittingSubpixelAA)
+    {}
+    Float mOpacity;
+    cairo_pattern_t* mMaskPattern;
+    bool mWasPermittingSubpixelAA;
+  };
+  std::vector<PushedLayer> mPushedLayers;
+
   // The latest snapshot of this surface. This needs to be told when this
   // target is modified. We keep it alive as a cache.
   RefPtr<SourceSurfaceCairo> mSnapshot;
   static cairo_surface_t *mDummySurface;
 };
 
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/2d/DrawTargetD2D1.cpp
+++ b/gfx/2d/DrawTargetD2D1.cpp
@@ -26,17 +26,17 @@ uint64_t DrawTargetD2D1::mVRAMUsageSS;
 ID2D1Factory1* DrawTargetD2D1::mFactory = nullptr;
 
 ID2D1Factory1 *D2DFactory1()
 {
   return DrawTargetD2D1::factory();
 }
 
 DrawTargetD2D1::DrawTargetD2D1()
-  : mClipsArePushed(false)
+  : mPushedLayers(1)
 {
 }
 
 DrawTargetD2D1::~DrawTargetD2D1()
 {
   PopAllClips();
 
   if (mSnapshot) {
@@ -262,17 +262,17 @@ DrawTargetD2D1::ClearRect(const Rect &aR
   RefPtr<ID2D1Geometry> geom = GetClippedGeometry(&addClipRect);
 
   RefPtr<ID2D1SolidColorBrush> brush;
   mDC->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), getter_AddRefs(brush));
   mDC->PushAxisAlignedClip(D2D1::RectF(addClipRect.x, addClipRect.y, addClipRect.XMost(), addClipRect.YMost()), D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
   mDC->FillGeometry(geom, brush);
   mDC->PopAxisAlignedClip();
 
-  mDC->SetTarget(mBitmap);
+  mDC->SetTarget(CurrentTarget());
   list->Close();
 
   mDC->DrawImage(list, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_DESTINATION_OUT);
 
   PopClip();
 
   return;
 }
@@ -510,17 +510,17 @@ DrawTargetD2D1::FillGlyphs(ScaledFont *a
 
   if (aOptions.mAntialiasMode != AntialiasMode::DEFAULT) {
     aaMode = aOptions.mAntialiasMode;
   }
 
   PrepareForDrawing(aOptions.mCompositionOp, aPattern);
 
   bool forceClearType = false;
-  if (mFormat == SurfaceFormat::B8G8R8A8 && mPermitSubpixelAA &&
+  if (!CurrentLayer().mIsOpaque && mPermitSubpixelAA &&
       aOptions.mCompositionOp == CompositionOp::OP_OVER && aaMode == AntialiasMode::SUBPIXEL) {
     forceClearType = true;    
   }
 
 
   D2D1_TEXT_ANTIALIAS_MODE d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT;
 
   switch (aaMode) {
@@ -533,37 +533,37 @@ DrawTargetD2D1::FillGlyphs(ScaledFont *a
   case AntialiasMode::SUBPIXEL:
     d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE;
     break;
   default:
     d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT;
   }
 
   if (d2dAAMode == D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE &&
-      mFormat != SurfaceFormat::B8G8R8X8 && !forceClearType) {
+      !CurrentLayer().mIsOpaque && !forceClearType) {
     d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE;
   }
 
   mDC->SetTextAntialiasMode(d2dAAMode);
 
   if (params != mTextRenderingParams) {
     mDC->SetTextRenderingParams(params);
     mTextRenderingParams = params;
   }
 
   RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
 
   AutoDWriteGlyphRun autoRun;
   DWriteGlyphRunFromGlyphs(aBuffer, font, &autoRun);
 
   bool needsRepushedLayers = false;
-  if (d2dAAMode == D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE && mFormat != SurfaceFormat::B8G8R8X8) {
+  if (forceClearType) {
     D2D1_RECT_F rect;
     bool isAligned;
-    needsRepushedLayers = mPushedClips.size() && !GetDeviceSpaceClipRect(rect, isAligned);
+    needsRepushedLayers = CurrentLayer().mPushedClips.size() && !GetDeviceSpaceClipRect(rect, isAligned);
 
     // If we have a complex clip in our stack and we have a transparent
     // background, and subpixel AA is permitted, we need to repush our layer
     // stack limited by the glyph run bounds initializing our layers for
     // subpixel AA.
     if (needsRepushedLayers) {
       mDC->GetGlyphRunWorldBounds(D2D1::Point2F(), &autoRun,
                                   DWRITE_MEASURING_MODE_NATURAL, &rect);
@@ -582,21 +582,17 @@ DrawTargetD2D1::FillGlyphs(ScaledFont *a
         mDC->SetTransform(D2D1::IdentityMatrix());
         mDC->GetGlyphRunWorldBounds(D2D1::Point2F(), &autoRun,
                                     DWRITE_MEASURING_MODE_NATURAL, &userRect);
 
         RefPtr<ID2D1PathGeometry> path;
         D2DFactory()->CreatePathGeometry(getter_AddRefs(path));
         RefPtr<ID2D1GeometrySink> sink;
         path->Open(getter_AddRefs(sink));
-        sink->BeginFigure(D2D1::Point2F(userRect.left, userRect.top), D2D1_FIGURE_BEGIN_FILLED);
-        sink->AddLine(D2D1::Point2F(userRect.right, userRect.top));
-        sink->AddLine(D2D1::Point2F(userRect.right, userRect.bottom));
-        sink->AddLine(D2D1::Point2F(userRect.left, userRect.bottom));
-        sink->EndFigure(D2D1_FIGURE_END_CLOSED);
+        AddRectToSink(sink, userRect);
         sink->Close();
 
         mDC->PushLayer(D2D1::LayerParameters1(D2D1::InfiniteRect(), path, D2D1_ANTIALIAS_MODE_ALIASED,
                                               D2DMatrix(mTransform), 1.0f, nullptr,
                                               D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND |
                                               D2D1_LAYER_OPTIONS1_IGNORE_ALPHA), nullptr);
       }
 
@@ -659,24 +655,24 @@ DrawTargetD2D1::PushClip(const Path *aPa
   RefPtr<PathD2D> pathD2D = static_cast<PathD2D*>(const_cast<Path*>(aPath));
 
   PushedClip clip;
   clip.mTransform = D2DMatrix(mTransform);
   clip.mPath = pathD2D;
   
   pathD2D->mGeometry->GetBounds(clip.mTransform, &clip.mBounds);
 
-  mPushedClips.push_back(clip);
+  CurrentLayer().mPushedClips.push_back(clip);
 
   // The transform of clips is relative to the world matrix, since we use the total
   // transform for the clips, make the world matrix identity.
   mDC->SetTransform(D2D1::IdentityMatrix());
   mTransformDirty = true;
 
-  if (mClipsArePushed) {
+  if (CurrentLayer().mClipsArePushed) {
     PushD2DLayer(mDC, pathD2D->mGeometry, clip.mTransform);
   }
 }
 
 void
 DrawTargetD2D1::PushClipRect(const Rect &aRect)
 {
   if (!mTransform.IsRectilinear()) {
@@ -699,39 +695,120 @@ DrawTargetD2D1::PushClipRect(const Rect 
   PushedClip clip;
   Rect rect = mTransform.TransformBounds(aRect);
   IntRect intRect;
   clip.mIsPixelAligned = rect.ToIntRect(&intRect);
 
   // Do not store the transform, just store the device space rectangle directly.
   clip.mBounds = D2DRect(rect);
 
-  mPushedClips.push_back(clip);
+  CurrentLayer().mPushedClips.push_back(clip);
 
   mDC->SetTransform(D2D1::IdentityMatrix());
   mTransformDirty = true;
 
-  if (mClipsArePushed) {
+  if (CurrentLayer().mClipsArePushed) {
     mDC->PushAxisAlignedClip(clip.mBounds, clip.mIsPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
   }
 }
 
 void
 DrawTargetD2D1::PopClip()
 {
   mCurrentClippedGeometry = nullptr;
 
-  if (mClipsArePushed) {
-    if (mPushedClips.back().mPath) {
+  if (CurrentLayer().mClipsArePushed) {
+    if (CurrentLayer().mPushedClips.back().mPath) {
       mDC->PopLayer();
     } else {
       mDC->PopAxisAlignedClip();
     }
   }
-  mPushedClips.pop_back();
+  CurrentLayer().mPushedClips.pop_back();
+}
+
+void
+DrawTargetD2D1::PushLayer(bool aOpaque, Float aOpacity, SourceSurface* aMask,
+                          const Matrix& aMaskTransform, const IntRect& aBounds,
+                          bool aCopyBackground)
+{
+  D2D1_LAYER_OPTIONS1 options = D2D1_LAYER_OPTIONS1_NONE;
+
+  if (aOpaque) {
+    options |= D2D1_LAYER_OPTIONS1_IGNORE_ALPHA;
+  }
+  if (aCopyBackground) {
+    options |= D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND;
+  }
+
+  RefPtr<ID2D1BitmapBrush> mask;
+
+  Matrix maskTransform = aMaskTransform;
+
+  RefPtr<ID2D1PathGeometry> clip;
+  if (aMask) {
+    mDC->SetTransform(D2D1::IdentityMatrix());
+    mTransformDirty = true;
+
+    RefPtr<ID2D1Image> image = GetImageForSurface(aMask, maskTransform, ExtendMode::CLAMP);
+
+    // The mask is given in user space. Our layer will apply it in device space.
+    maskTransform = maskTransform * mTransform;
+
+    if (image) {
+      RefPtr<ID2D1Bitmap> bitmap;
+      image->QueryInterface((ID2D1Bitmap**)getter_AddRefs(bitmap));
+
+      mDC->CreateBitmapBrush(bitmap, D2D1::BitmapBrushProperties(), D2D1::BrushProperties(1.0f, D2DMatrix(maskTransform)), getter_AddRefs(mask));
+      MOZ_ASSERT(bitmap); // This should always be true since it was created for a surface.
+
+      factory()->CreatePathGeometry(getter_AddRefs(clip));
+      RefPtr<ID2D1GeometrySink> sink;
+      clip->Open(getter_AddRefs(sink));
+      AddRectToSink(sink, D2D1::RectF(0, 0, aMask->GetSize().width, aMask->GetSize().height));
+      sink->Close();
+    } else {
+      gfxCriticalError() << "Failed to get image for mask surface!";
+    }
+  }
+
+  PushAllClips();
+
+  mDC->PushLayer(D2D1::LayerParameters1(D2D1::InfiniteRect(), clip, D2D1_ANTIALIAS_MODE_ALIASED, D2DMatrix(maskTransform), aOpacity, mask, options), nullptr);
+  PushedLayer pushedLayer;
+  pushedLayer.mClipsArePushed = false;
+  pushedLayer.mIsOpaque = aOpaque;
+  pushedLayer.mOldPermitSubpixelAA = mPermitSubpixelAA;
+  mPermitSubpixelAA = aOpaque;
+
+  mDC->CreateCommandList(getter_AddRefs(pushedLayer.mCurrentList));
+  mPushedLayers.push_back(pushedLayer);
+
+  mDC->SetTarget(CurrentTarget());
+}
+
+void
+DrawTargetD2D1::PopLayer()
+{
+  MOZ_ASSERT(CurrentLayer().mPushedClips.size() == 0);
+
+  RefPtr<ID2D1CommandList> list = CurrentLayer().mCurrentList;
+  mPermitSubpixelAA = CurrentLayer().mOldPermitSubpixelAA;
+
+  mPushedLayers.pop_back();
+  mDC->SetTarget(CurrentTarget());
+
+  list->Close();
+  mDC->SetTransform(D2D1::IdentityMatrix());
+  mTransformDirty = true;
+
+  DCCommandSink sink(mDC);
+  list->Stream(&sink);
+
+  mDC->PopLayer();
 }
 
 already_AddRefed<SourceSurface>
 DrawTargetD2D1::CreateSourceSurfaceFromData(unsigned char *aData,
                                             const IntSize &aSize,
                                             int32_t aStride,
                                             SurfaceFormat aFormat) const
 {
@@ -873,20 +950,22 @@ DrawTargetD2D1::Init(ID3D11Texture2D* aT
   // ordering in that situation anyway.
   hr = mDC->CreateSolidColorBrush(D2D1::ColorF(0, 0), getter_AddRefs(mSolidColorBrush));
 
   if (FAILED(hr)) {
     gfxCriticalError() << "[D2D1.1] Failure creating solid color brush (I1).";
     return false;
   }
 
-  mDC->SetTarget(mBitmap);
+  mDC->SetTarget(CurrentTarget());
 
   mDC->BeginDraw();
 
+  CurrentLayer().mIsOpaque = aFormat == SurfaceFormat::B8G8R8X8;
+
   return true;
 }
 
 bool
 DrawTargetD2D1::Init(const IntSize &aSize, SurfaceFormat aFormat)
 {
   HRESULT hr;
 
@@ -917,27 +996,29 @@ DrawTargetD2D1::Init(const IntSize &aSiz
   props.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET;
   hr = mDC->CreateBitmap(D2DIntSize(aSize), nullptr, 0, props, (ID2D1Bitmap1**)getter_AddRefs(mBitmap));
 
   if (FAILED(hr)) {
     gfxCriticalError() << "[D2D1.1] 3CreateBitmap failure " << aSize << " Code: " << hexa(hr) << " format " << (int)aFormat;
     return false;
   }
 
-  mDC->SetTarget(mBitmap);
+  mDC->SetTarget(CurrentTarget());
 
   hr = mDC->CreateSolidColorBrush(D2D1::ColorF(0, 0), getter_AddRefs(mSolidColorBrush));
 
   if (FAILED(hr)) {
     gfxCriticalError() << "[D2D1.1] Failure creating solid color brush (I2).";
     return false;
   }
 
   mDC->BeginDraw();
 
+  CurrentLayer().mIsOpaque = aFormat == SurfaceFormat::B8G8R8X8;
+
   mDC->Clear();
 
   mFormat = aFormat;
   mSize = aSize;
 
   return true;
 }
 
@@ -1043,93 +1124,71 @@ DrawTargetD2D1::FinalizeDrawing(Composit
   if (D2DSupportsPrimitiveBlendMode(aOp) && patternSupported) {
     if (aOp != CompositionOp::OP_OVER)
       mDC->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_SOURCE_OVER);
     return;
   }
 
   PopAllClips();
 
-  mDC->SetTarget(mBitmap);
+  mDC->SetTarget(CurrentTarget());
   mCommandList->Close();
 
   RefPtr<ID2D1CommandList> source = mCommandList;
   mCommandList = nullptr;
 
   mDC->SetTransform(D2D1::IdentityMatrix());
   mTransformDirty = true;
 
   if (patternSupported) {
     if (D2DSupportsCompositeMode(aOp)) {
       D2D1_RECT_F rect;
       bool isAligned;
-      RefPtr<ID2D1Bitmap> tmpBitmap;
-      bool clipIsComplex = mPushedClips.size() && !GetDeviceSpaceClipRect(rect, isAligned);
+      RefPtr<ID2D1Image> tmpImage;
+      bool clipIsComplex = CurrentLayer().mPushedClips.size() && !GetDeviceSpaceClipRect(rect, isAligned);
 
       if (clipIsComplex) {
         if (!IsOperatorBoundByMask(aOp)) {
-          HRESULT hr = mDC->CreateBitmap(D2DIntSize(mSize), D2D1::BitmapProperties(D2DPixelFormat(mFormat)), getter_AddRefs(tmpBitmap));
-          if (FAILED(hr)) {
-            gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(mSize))) << "[D2D1.1] 6CreateBitmap failure " << mSize << " Code: " << hexa(hr) << " format " << (int)mFormat;
-            // For now, crash in this scenario; this should happen because tmpBitmap is
-            // null and CopyFromBitmap call below dereferences it.
-            // return;
-          }
-          mDC->Flush();
-
-          tmpBitmap->CopyFromBitmap(nullptr, mBitmap, nullptr);
+          tmpImage = GetImageForLayerContent();
         }
       } else {
         PushAllClips();
       }
       mDC->DrawImage(source, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2DCompositionMode(aOp));
 
-      if (tmpBitmap) {
-        RefPtr<ID2D1BitmapBrush> brush;
+      if (tmpImage) {
+        RefPtr<ID2D1ImageBrush> brush;
         RefPtr<ID2D1Geometry> inverseGeom = GetInverseClippedGeometry();
-        mDC->CreateBitmapBrush(tmpBitmap, getter_AddRefs(brush));
+        mDC->CreateImageBrush(tmpImage, D2D1::ImageBrushProperties(D2D1::RectF(0, 0, mSize.width, mSize.height)),
+                              getter_AddRefs(brush));
 
         mDC->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_COPY);
         mDC->FillGeometry(inverseGeom, brush);
         mDC->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_SOURCE_OVER);
       }
       return;
     }
 
-    if (!mBlendEffect) {
-      HRESULT hr = mDC->CreateEffect(CLSID_D2D1Blend, getter_AddRefs(mBlendEffect));
+    RefPtr<ID2D1Effect> blendEffect;
+    HRESULT hr = mDC->CreateEffect(CLSID_D2D1Blend, getter_AddRefs(blendEffect));
 
-      if (FAILED(hr) || !mBlendEffect) {
-        gfxWarning() << "Failed to create blend effect!";
-        return;
-      }
-    }
-
-    RefPtr<ID2D1Bitmap> tmpBitmap;
-    HRESULT hr = mDC->CreateBitmap(D2DIntSize(mSize), D2D1::BitmapProperties(D2DPixelFormat(mFormat)), getter_AddRefs(tmpBitmap));
-    if (FAILED(hr)) {
-      gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(mSize))) << "[D2D1.1] 5CreateBitmap failure " << mSize << " Code: " << hexa(hr) << " format " << (int)mFormat;
+    if (FAILED(hr) || !blendEffect) {
+      gfxWarning() << "Failed to create blend effect!";
       return;
     }
 
-    // This flush is important since the copy method will not know about the context drawing to the surface.
-    // We also need to pop all the clips to make sure any drawn content will have made it to the final bitmap.
-    mDC->Flush();
+    RefPtr<ID2D1Image> tmpImage = GetImageForLayerContent();
 
-    // We need to use a copy here because affects don't accept a surface on
-    // both their in- and outputs.
-    tmpBitmap->CopyFromBitmap(nullptr, mBitmap, nullptr);
-
-    mBlendEffect->SetInput(0, tmpBitmap);
-    mBlendEffect->SetInput(1, source);
-    mBlendEffect->SetValue(D2D1_BLEND_PROP_MODE, D2DBlendMode(aOp));
+    blendEffect->SetInput(0, tmpImage);
+    blendEffect->SetInput(1, source);
+    blendEffect->SetValue(D2D1_BLEND_PROP_MODE, D2DBlendMode(aOp));
 
     PushAllClips();
 
-    mDC->DrawImage(mBlendEffect, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_BOUNDED_SOURCE_COPY);
+    mDC->DrawImage(blendEffect, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_BOUNDED_SOURCE_COPY);
     return;
   }
 
   const RadialGradientPattern *pat = static_cast<const RadialGradientPattern*>(&aPattern);
   if (pat->mCenter1 == pat->mCenter2 && pat->mRadius1 == pat->mRadius2) {
     // Draw nothing!
     return;
   }
@@ -1179,61 +1238,90 @@ IntersectRect(const D2D1_RECT_F& aRect1,
   result.bottom = max(result.bottom, result.top);
 
   return result;
 }
 
 bool
 DrawTargetD2D1::GetDeviceSpaceClipRect(D2D1_RECT_F& aClipRect, bool& aIsPixelAligned)
 {
-  if (!mPushedClips.size()) {
+  if (!CurrentLayer().mPushedClips.size()) {
     return false;
   }
 
   aClipRect = D2D1::RectF(0, 0, mSize.width, mSize.height);
-  for (auto iter = mPushedClips.begin();iter != mPushedClips.end(); iter++) {
+  for (auto iter = CurrentLayer().mPushedClips.begin();iter != CurrentLayer().mPushedClips.end(); iter++) {
     if (iter->mPath) {
       return false;
     }
     aClipRect = IntersectRect(aClipRect, iter->mBounds);
     if (!iter->mIsPixelAligned) {
       aIsPixelAligned = false;
     }
   }
   return true;
 }
 
+already_AddRefed<ID2D1Image>
+DrawTargetD2D1::GetImageForLayerContent()
+{
+  if (!CurrentLayer().mCurrentList) {
+    RefPtr<ID2D1Bitmap> tmpBitmap;
+    HRESULT hr = mDC->CreateBitmap(D2DIntSize(mSize), D2D1::BitmapProperties(D2DPixelFormat(mFormat)), getter_AddRefs(tmpBitmap));
+    if (FAILED(hr)) {
+      gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(mSize))) << "[D2D1.1] 6CreateBitmap failure " << mSize << " Code: " << hexa(hr) << " format " << (int)mFormat;
+      // For now, crash in this scenario; this should happen because tmpBitmap is
+      // null and CopyFromBitmap call below dereferences it.
+      // return;
+    }
+    mDC->Flush();
+
+    tmpBitmap->CopyFromBitmap(nullptr, mBitmap, nullptr);
+    return tmpBitmap.forget();
+  } else {
+    RefPtr<ID2D1CommandList> list = CurrentLayer().mCurrentList;
+    mDC->CreateCommandList(getter_AddRefs(CurrentLayer().mCurrentList));
+    mDC->SetTarget(CurrentTarget());
+    list->Close();
+
+    DCCommandSink sink(mDC);
+    list->Stream(&sink);
+
+    return list.forget();
+  }
+}
+
 already_AddRefed<ID2D1Geometry>
 DrawTargetD2D1::GetClippedGeometry(IntRect *aClipBounds)
 {
   if (mCurrentClippedGeometry) {
     *aClipBounds = mCurrentClipBounds;
     RefPtr<ID2D1Geometry> clippedGeometry(mCurrentClippedGeometry);
     return clippedGeometry.forget();
   }
 
-  MOZ_ASSERT(mPushedClips.size());
+  MOZ_ASSERT(CurrentLayer().mPushedClips.size());
 
   mCurrentClipBounds = IntRect(IntPoint(0, 0), mSize);
 
   // if pathGeom is null then pathRect represents the path.
   RefPtr<ID2D1Geometry> pathGeom;
   D2D1_RECT_F pathRect;
   bool pathRectIsAxisAligned = false;
-  auto iter = mPushedClips.begin();
+  auto iter = CurrentLayer().mPushedClips.begin();
 
   if (iter->mPath) {
     pathGeom = GetTransformedGeometry(iter->mPath->GetGeometry(), iter->mTransform);
   } else {
     pathRect = iter->mBounds;
     pathRectIsAxisAligned = iter->mIsPixelAligned;
   }
 
   iter++;
-  for (;iter != mPushedClips.end(); iter++) {
+  for (;iter != CurrentLayer().mPushedClips.end(); iter++) {
     // Do nothing but add it to the current clip bounds.
     if (!iter->mPath && iter->mIsPixelAligned) {
       mCurrentClipBounds.IntersectRect(mCurrentClipBounds,
         IntRect(int32_t(iter->mBounds.left), int32_t(iter->mBounds.top),
                 int32_t(iter->mBounds.right - iter->mBounds.left),
                 int32_t(iter->mBounds.bottom - iter->mBounds.top)));
       continue;
     }
@@ -1308,54 +1396,53 @@ DrawTargetD2D1::GetInverseClippedGeometr
   sink->Close();
 
   return inverseGeom.forget();
 }
 
 void
 DrawTargetD2D1::PopAllClips()
 {
-  if (mClipsArePushed) {
+  if (CurrentLayer().mClipsArePushed) {
     PopClipsFromDC(mDC);
   
-    mClipsArePushed = false;
+    CurrentLayer().mClipsArePushed = false;
   }
 }
 
 void
 DrawTargetD2D1::PushAllClips()
 {
-  if (!mClipsArePushed) {
+  if (!CurrentLayer().mClipsArePushed) {
     PushClipsToDC(mDC);
   
-    mClipsArePushed = true;
+    CurrentLayer().mClipsArePushed = true;
   }
 }
 
 void
 DrawTargetD2D1::PushClipsToDC(ID2D1DeviceContext *aDC, bool aForceIgnoreAlpha, const D2D1_RECT_F& aMaxRect)
 {
   mDC->SetTransform(D2D1::IdentityMatrix());
   mTransformDirty = true;
 
-  for (std::vector<PushedClip>::iterator iter = mPushedClips.begin();
-        iter != mPushedClips.end(); iter++) {
+  for (auto iter = CurrentLayer().mPushedClips.begin(); iter != CurrentLayer().mPushedClips.end(); iter++) {
     if (iter->mPath) {
       PushD2DLayer(aDC, iter->mPath->mGeometry, iter->mTransform, aForceIgnoreAlpha, aMaxRect);
     } else {
       mDC->PushAxisAlignedClip(iter->mBounds, iter->mIsPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
     }
   }
 }
 
 void
 DrawTargetD2D1::PopClipsFromDC(ID2D1DeviceContext *aDC)
 {
-  for (int i = mPushedClips.size() - 1; i >= 0; i--) {
-    if (mPushedClips[i].mPath) {
+  for (int i = CurrentLayer().mPushedClips.size() - 1; i >= 0; i--) {
+    if (CurrentLayer().mPushedClips[i].mPath) {
       aDC->PopLayer();
     } else {
       aDC->PopAxisAlignedClip();
     }
   }
 }
 
 already_AddRefed<ID2D1Brush>
--- a/gfx/2d/DrawTargetD2D1.h
+++ b/gfx/2d/DrawTargetD2D1.h
@@ -89,16 +89,22 @@ public:
                           const DrawOptions &aOptions = DrawOptions(),
                           const GlyphRenderingOptions *aRenderingOptions = nullptr) override;
   virtual void Mask(const Pattern &aSource,
                     const Pattern &aMask,
                     const DrawOptions &aOptions = DrawOptions()) override;
   virtual void PushClip(const Path *aPath) override;
   virtual void PushClipRect(const Rect &aRect) override;
   virtual void PopClip() override;
+  virtual void PushLayer(bool aOpaque, Float aOpacity,
+                         SourceSurface* aMask,
+                         const Matrix& aMaskTransform,
+                         const IntRect& aBounds = IntRect(),
+                         bool aCopyBackground = false) override;
+  virtual void PopLayer() override;
 
   virtual already_AddRefed<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
                                                                   const IntSize &aSize,
                                                                   int32_t aStride,
                                                                   SurfaceFormat aFormat) const override;
   virtual already_AddRefed<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const override;
 
   virtual already_AddRefed<SourceSurface>
@@ -112,16 +118,17 @@ public:
   virtual already_AddRefed<GradientStops>
     CreateGradientStops(GradientStop *aStops,
                         uint32_t aNumStops,
                         ExtendMode aExtendMode = ExtendMode::CLAMP) const override;
 
   virtual already_AddRefed<FilterNode> CreateFilter(FilterType aType) override;
 
   virtual bool SupportsRegionClipping() const override { return false; }
+  virtual bool IsCurrentGroupOpaque() override { return CurrentLayer().mIsOpaque; }
 
   virtual void *GetNativeSurface(NativeSurfaceType aType) override { return nullptr; }
 
   bool Init(const IntSize &aSize, SurfaceFormat aFormat);
   bool Init(ID3D11Texture2D* aTexture, SurfaceFormat aFormat);
   uint32_t GetByteSize() const;
 
   already_AddRefed<ID2D1Image> GetImageForSurface(SourceSurface *aSurface, Matrix &aSourceTransform,
@@ -162,24 +169,37 @@ private:
   void FlushTransformToDC() {
     if (mTransformDirty) {
       mDC->SetTransform(D2DMatrix(mTransform));
       mTransformDirty = false;
     }
   }
   void AddDependencyOnSource(SourceSurfaceD2D1* aSource);
 
+  // Must be called with all clips popped and an identity matrix set.
+  already_AddRefed<ID2D1Image> GetImageForLayerContent();
+
+  ID2D1Image* CurrentTarget()
+  {
+    if (CurrentLayer().mCurrentList) {
+      return CurrentLayer().mCurrentList;
+    }
+    return mBitmap;
+  }
+
   // This returns the clipped geometry, in addition it returns aClipBounds which
   // represents the intersection of all pixel-aligned rectangular clips that
   // are currently set. The returned clipped geometry must be clipped by these
-  // bounds to correctly reflect the total clip. This is in device space.
+  // bounds to correctly reflect the total clip. This is in device space and
+  // only for clips applied to the -current layer-.
   already_AddRefed<ID2D1Geometry> GetClippedGeometry(IntRect *aClipBounds);
 
   already_AddRefed<ID2D1Geometry> GetInverseClippedGeometry();
 
+  // This gives the device space clip rect applied to the -current layer-.
   bool GetDeviceSpaceClipRect(D2D1_RECT_F& aClipRect, bool& aIsPixelAligned);
 
   void PopAllClips();
   void PushAllClips();
   void PushClipsToDC(ID2D1DeviceContext *aDC, bool aForceIgnoreAlpha = false, const D2D1_RECT_F& aMaxRect = D2D1::InfiniteRect());
   void PopClipsFromDC(ID2D1DeviceContext *aDC);
 
   already_AddRefed<ID2D1Brush> CreateTransparentBlackBrush();
@@ -196,17 +216,16 @@ private:
   RefPtr<ID2D1Geometry> mCurrentClippedGeometry;
   // This is only valid if mCurrentClippedGeometry is non-null. And will
   // only be the intersection of all pixel-aligned retangular clips. This is in
   // device space.
   IntRect mCurrentClipBounds;
   mutable RefPtr<ID2D1DeviceContext> mDC;
   RefPtr<ID2D1Bitmap1> mBitmap;
   RefPtr<ID2D1CommandList> mCommandList;
-  RefPtr<ID2D1Effect> mBlendEffect;
 
   RefPtr<ID2D1SolidColorBrush> mSolidColorBrush;
 
   // We store this to prevent excessive SetTextRenderingParams calls.
   RefPtr<IDWriteRenderingParams> mTextRenderingParams;
 
   // List of pushed clips.
   struct PushedClip
@@ -215,28 +234,43 @@ private:
     union {
       // If mPath is non-null, the mTransform member will be used, otherwise
       // the mIsPixelAligned member is valid.
       D2D1_MATRIX_3X2_F mTransform;
       bool mIsPixelAligned;
     };
     RefPtr<PathD2D> mPath;
   };
-  std::vector<PushedClip> mPushedClips;
+
+  // List of pushed layers.
+  struct PushedLayer
+  {
+    PushedLayer() : mClipsArePushed(false), mIsOpaque(false), mOldPermitSubpixelAA(false) {}
+
+    std::vector<PushedClip> mPushedClips;
+    RefPtr<ID2D1CommandList> mCurrentList;
+    // True if the current clip stack is pushed to the CurrentTarget().
+    bool mClipsArePushed;
+    bool mIsOpaque;
+    bool mOldPermitSubpixelAA;
+  };
+  std::vector<PushedLayer> mPushedLayers;
+  PushedLayer& CurrentLayer()
+  {
+    return mPushedLayers.back();
+  }
 
   // The latest snapshot of this surface. This needs to be told when this
   // target is modified. We keep it alive as a cache.
   RefPtr<SourceSurfaceD2D1> mSnapshot;
   // A list of targets we need to flush when we're modified.
   TargetSet mDependentTargets;
   // A list of targets which have this object in their mDependentTargets set
   TargetSet mDependingOnTargets;
 
-  // True of the current clip stack is pushed to the main RT.
-  bool mClipsArePushed;
   static ID2D1Factory1 *mFactory;
   static IDWriteFactory *mDWriteFactory;
 };
 
 }
 }
 
 #endif /* MOZILLA_GFX_DRAWTARGETD2D_H_ */
--- a/gfx/2d/DrawTargetDual.cpp
+++ b/gfx/2d/DrawTargetDual.cpp
@@ -10,16 +10,21 @@
 namespace mozilla {
 namespace gfx {
 
 class DualSurface
 {
 public:
   inline explicit DualSurface(SourceSurface *aSurface)
   {
+    if (!aSurface) {
+      mA = mB = nullptr;
+      return;
+    }
+
     if (aSurface->GetType() != SurfaceType::DUAL_DT) {
       mA = mB = aSurface;
       return;
     }
 
     SourceSurfaceDual *ssDual =
       static_cast<SourceSurfaceDual*>(aSurface);
     mA = ssDual->mA;
@@ -176,16 +181,26 @@ void
 DrawTargetDual::Mask(const Pattern &aSource, const Pattern &aMask, const DrawOptions &aOptions)
 {
   DualPattern source(aSource);
   DualPattern mask(aMask);
   mA->Mask(*source.mA, *mask.mA, aOptions);
   mB->Mask(*source.mB, *mask.mB, aOptions);
 }
 
+void
+DrawTargetDual::PushLayer(bool aOpaque, Float aOpacity, SourceSurface* aMask,
+                          const Matrix& aMaskTransform, const IntRect& aBounds,
+                          bool aCopyBackground)
+{
+  DualSurface mask(aMask);
+  mA->PushLayer(aOpaque, aOpacity, mask.mA, aMaskTransform, aBounds, aCopyBackground);
+  mB->PushLayer(aOpaque, aOpacity, mask.mB, aMaskTransform, aBounds, aCopyBackground);
+}
+
 already_AddRefed<DrawTarget>
 DrawTargetDual::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
 {
   RefPtr<DrawTarget> dtA = mA->CreateSimilarDrawTarget(aSize, aFormat);
   RefPtr<DrawTarget> dtB = mB->CreateSimilarDrawTarget(aSize, aFormat);
 
   if (!dtA || !dtB) {
     gfxWarning() << "Failure to allocate a similar DrawTargetDual. Size: " << aSize;
--- a/gfx/2d/DrawTargetDual.h
+++ b/gfx/2d/DrawTargetDual.h
@@ -49,16 +49,17 @@ public:
     return MakeAndAddRef<SourceSurfaceDual>(mA, mB);
   }
   virtual IntSize GetSize() override { return mA->GetSize(); }
      
   FORWARD_FUNCTION(Flush)
   FORWARD_FUNCTION1(PushClip, const Path *, aPath)
   FORWARD_FUNCTION1(PushClipRect, const Rect &, aRect)
   FORWARD_FUNCTION(PopClip)
+  FORWARD_FUNCTION(PopLayer)
   FORWARD_FUNCTION1(ClearRect, const Rect &, aRect)
 
   virtual void SetTransform(const Matrix &aTransform) override {
     mTransform = aTransform;
     mA->SetTransform(aTransform);
     mB->SetTransform(aTransform);
   }
 
@@ -99,17 +100,23 @@ public:
 
   virtual void Fill(const Path *aPath, const Pattern &aPattern, const DrawOptions &aOptions) override;
 
   virtual void FillGlyphs(ScaledFont *aScaledFont, const GlyphBuffer &aBuffer,
                           const Pattern &aPattern, const DrawOptions &aOptions,
                           const GlyphRenderingOptions *aRenderingOptions) override;
   
   virtual void Mask(const Pattern &aSource, const Pattern &aMask, const DrawOptions &aOptions) override;
-     
+
+  virtual void PushLayer(bool aOpaque, Float aOpacity,
+                         SourceSurface* aMask,
+                         const Matrix& aMaskTransform,
+                         const IntRect& aBounds = IntRect(),
+                         bool aCopyBackground = false) override;
+
   virtual already_AddRefed<SourceSurface>
     CreateSourceSurfaceFromData(unsigned char *aData,
                                 const IntSize &aSize,
                                 int32_t aStride,
                                 SurfaceFormat aFormat) const override
   {
     return mA->CreateSourceSurfaceFromData(aData, aSize, aStride, aFormat);
   }
@@ -150,16 +157,18 @@ public:
   {
     return nullptr;
   }
 
   virtual bool IsDualDrawTarget() const override
   {
     return true;
   }
+
+  virtual bool IsCurrentGroupOpaque() override { return mA->IsCurrentGroupOpaque(); }
      
 private:
   RefPtr<DrawTarget> mA;
   RefPtr<DrawTarget> mB;
 };
      
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/2d/DrawTargetSkia.cpp
+++ b/gfx/2d/DrawTargetSkia.cpp
@@ -578,17 +578,18 @@ void
 DrawTargetSkia::FillGlyphs(ScaledFont *aFont,
                            const GlyphBuffer &aBuffer,
                            const Pattern &aPattern,
                            const DrawOptions &aOptions,
                            const GlyphRenderingOptions *aRenderingOptions)
 {
   if (aFont->GetType() != FontType::MAC &&
       aFont->GetType() != FontType::SKIA &&
-      aFont->GetType() != FontType::GDI) {
+      aFont->GetType() != FontType::GDI &&
+      aFont->GetType() != FontType::DWRITE) {
     return;
   }
 
   MarkChanged();
 
   ScaledFontBase* skiaFont = static_cast<ScaledFontBase*>(aFont);
 
   AutoPaintSetup paint(mCanvas.get(), aOptions, aPattern);
@@ -757,17 +758,17 @@ DrawTargetSkia::OptimizeSourceSurface(So
                                                              dataSurf->GetFormat());
   dataSurf->Unmap();
   return result.forget();
 }
 
 already_AddRefed<SourceSurface>
 DrawTargetSkia::CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const
 {
-  if (aSurface.mType == NativeSurfaceType::CAIRO_SURFACE) {
+  if (aSurface.mType == NativeSurfaceType::CAIRO_CONTEXT) {
     if (aSurface.mSize.width <= 0 ||
         aSurface.mSize.height <= 0) {
       gfxWarning() << "Can't create a SourceSurface without a valid size";
       return nullptr;
     }
     cairo_surface_t* surf = static_cast<cairo_surface_t*>(aSurface.mSurface);
     return MakeAndAddRef<SourceSurfaceCairo>(surf, aSurface.mSize, aSurface.mFormat);
 #if USE_SKIA_GPU
--- a/gfx/2d/DrawTargetTiled.cpp
+++ b/gfx/2d/DrawTargetTiled.cpp
@@ -300,10 +300,34 @@ DrawTargetTiled::Fill(const Path* aPath,
                                    mTiles[i].mTileOrigin.y,
                                    mTiles[i].mDrawTarget->GetSize().width,
                                    mTiles[i].mDrawTarget->GetSize().height))) {
       mTiles[i].mDrawTarget->Fill(aPath, aPattern, aDrawOptions);
     }
   }
 }
 
+void
+DrawTargetTiled::PushLayer(bool aOpaque, Float aOpacity, SourceSurface* aMask,
+                           const Matrix& aMaskTransform, const IntRect& aBounds,
+                           bool aCopyBackground)
+{
+  // XXX - not sure this is what we want or whether we want to continue drawing to a larger
+  // intermediate surface, that would require tweaking the code in here a little though.
+  for (size_t i = 0; i < mTiles.size(); i++) {
+    IntRect bounds = aBounds;
+    bounds.MoveBy(-mTiles[i].mTileOrigin);
+    mTiles[i].mDrawTarget->PushLayer(aOpaque, aOpacity, aMask, aMaskTransform, aBounds);
+  }
+}
+
+void
+DrawTargetTiled::PopLayer()
+{
+  // XXX - not sure this is what we want or whether we want to continue drawing to a larger
+  // intermediate surface, that would require tweaking the code in here a little though.
+  for (size_t i = 0; i < mTiles.size(); i++) {
+    mTiles[i].mDrawTarget->PopLayer();
+  }
+}
+
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/2d/DrawTargetTiled.h
+++ b/gfx/2d/DrawTargetTiled.h
@@ -98,16 +98,23 @@ public:
                           const DrawOptions &aOptions = DrawOptions(),
                           const GlyphRenderingOptions *aRenderingOptions = nullptr) override;
   virtual void Mask(const Pattern &aSource,
                     const Pattern &aMask,
                     const DrawOptions &aOptions = DrawOptions()) override;
   virtual void PushClip(const Path *aPath) override;
   virtual void PushClipRect(const Rect &aRect) override;
   virtual void PopClip() override;
+  virtual void PushLayer(bool aOpaque, Float aOpacity,
+                         SourceSurface* aMask,
+                         const Matrix& aMaskTransform,
+                         const IntRect& aBounds = IntRect(),
+                         bool aCopyBackground = false) override;
+  virtual void PopLayer() override;
+
 
   virtual void SetTransform(const Matrix &aTransform) override;
 
   virtual already_AddRefed<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
                                                                   const IntSize &aSize,
                                                                   int32_t aStride,
                                                                   SurfaceFormat aFormat) const override
   {
--- a/gfx/2d/Factory.cpp
+++ b/gfx/2d/Factory.cpp
@@ -781,16 +781,25 @@ Factory::D2DCleanup()
   if (mD2D1Device) {
     mD2D1Device->Release();
     mD2D1Device = nullptr;
   }
   DrawTargetD2D1::CleanupD2D();
   DrawTargetD2D::CleanupD2D();
 }
 
+already_AddRefed<ScaledFont>
+Factory::CreateScaledFontForDWriteFont(IDWriteFont* aFont,
+                                       IDWriteFontFamily* aFontFamily,
+                                       IDWriteFontFace* aFontFace,
+                                       float aSize)
+{
+  return MakeAndAddRef<ScaledFontDWrite>(aFont, aFontFamily, aFontFace, aSize);
+}
+
 #endif // XP_WIN
 
 #ifdef USE_SKIA_GPU
 already_AddRefed<DrawTarget>
 Factory::CreateDrawTargetSkiaWithGrContext(GrContext* aGrContext,
                                            const IntSize &aSize,
                                            SurfaceFormat aFormat)
 {
--- a/gfx/2d/HelpersD2D.h
+++ b/gfx/2d/HelpersD2D.h
@@ -691,12 +691,273 @@ CreatePartialBitmapForSurface(DataSource
 
       aSourceTransform.PreScale(Float(size.width) / newSize.width,
                                 Float(size.height) / newSize.height);
     }
     return bitmap.forget();
   }
 }
 
+static inline void AddRectToSink(ID2D1GeometrySink* aSink, const D2D1_RECT_F& aRect)
+{
+  aSink->BeginFigure(D2D1::Point2F(aRect.left, aRect.top), D2D1_FIGURE_BEGIN_FILLED);
+  aSink->AddLine(D2D1::Point2F(aRect.right, aRect.top));
+  aSink->AddLine(D2D1::Point2F(aRect.right, aRect.bottom));
+  aSink->AddLine(D2D1::Point2F(aRect.left, aRect.bottom));
+  aSink->EndFigure(D2D1_FIGURE_END_CLOSED);
+}
+
+class DCCommandSink : public ID2D1CommandSink
+{
+public:
+  DCCommandSink(ID2D1DeviceContext* aCtx) : mCtx(aCtx)
+  {
+  }
+
+  HRESULT STDMETHODCALLTYPE QueryInterface(const IID &aIID, void **aPtr)
+  {
+    if (!aPtr) {
+      return E_POINTER;
+    }
+
+    if (aIID == IID_IUnknown) {
+      *aPtr = static_cast<IUnknown*>(this);
+      return S_OK;
+    } else if (aIID == IID_ID2D1CommandSink) {
+      *aPtr = static_cast<ID2D1CommandSink*>(this);
+      return S_OK;
+    }
+
+    return E_NOINTERFACE;
+  }
+
+  ULONG STDMETHODCALLTYPE AddRef()
+  {
+    return 1;
+  }
+
+  ULONG STDMETHODCALLTYPE Release()
+  {
+    return 1;
+  }
+
+  STDMETHODIMP BeginDraw()
+  {
+    // We don't want to do anything here!
+    return S_OK;
+  }
+  STDMETHODIMP EndDraw()
+  {
+    // We don't want to do anything here!
+    return S_OK;
+  }
+
+  STDMETHODIMP SetAntialiasMode(
+    D2D1_ANTIALIAS_MODE antialiasMode
+    )
+  {
+    mCtx->SetAntialiasMode(antialiasMode);
+    return S_OK;
+  }
+
+  STDMETHODIMP SetTags(D2D1_TAG tag1, D2D1_TAG tag2)
+  {
+    mCtx->SetTags(tag1, tag2);
+    return S_OK;
+  }
+
+  STDMETHODIMP SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE textAntialiasMode)
+  {
+    mCtx->SetTextAntialiasMode(textAntialiasMode);
+    return S_OK;
+  }
+
+  STDMETHODIMP SetTextRenderingParams(_In_opt_ IDWriteRenderingParams *textRenderingParams)
+  {
+    mCtx->SetTextRenderingParams(textRenderingParams);
+    return S_OK;
+  }
+
+  STDMETHODIMP  SetTransform(_In_ CONST D2D1_MATRIX_3X2_F *transform)
+  {
+    mCtx->SetTransform(transform);
+    return S_OK;
+  }
+
+  STDMETHODIMP SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND primitiveBlend)
+  {
+    mCtx->SetPrimitiveBlend(primitiveBlend);
+    return S_OK;
+  }
+
+  STDMETHODIMP SetUnitMode(D2D1_UNIT_MODE unitMode)
+  {
+    mCtx->SetUnitMode(unitMode);
+    return S_OK;
+  }
+
+  STDMETHODIMP Clear(_In_opt_ CONST D2D1_COLOR_F *color)
+  {
+    mCtx->Clear(color);
+    return S_OK;
+  }
+
+  STDMETHODIMP DrawGlyphRun(
+      D2D1_POINT_2F baselineOrigin,
+      _In_ CONST DWRITE_GLYPH_RUN *glyphRun,
+      _In_opt_ CONST DWRITE_GLYPH_RUN_DESCRIPTION *glyphRunDescription,
+      _In_ ID2D1Brush *foregroundBrush,
+      DWRITE_MEASURING_MODE measuringMode
+    )
+  {
+    mCtx->DrawGlyphRun(baselineOrigin, glyphRun, glyphRunDescription,
+                       foregroundBrush, measuringMode);
+    return S_OK;
+  }
+
+  STDMETHODIMP DrawLine(
+      D2D1_POINT_2F point0,
+      D2D1_POINT_2F point1,
+      _In_ ID2D1Brush *brush,
+      FLOAT strokeWidth,
+      _In_opt_ ID2D1StrokeStyle *strokeStyle
+    )
+  {
+    mCtx->DrawLine(point0, point1, brush, strokeWidth, strokeStyle);
+    return S_OK;
+  }
+
+  STDMETHODIMP DrawGeometry(
+      _In_ ID2D1Geometry *geometry,
+      _In_ ID2D1Brush *brush,
+      FLOAT strokeWidth,
+      _In_opt_ ID2D1StrokeStyle *strokeStyle
+    )
+  {
+    mCtx->DrawGeometry(geometry, brush, strokeWidth, strokeStyle);
+    return S_OK;
+  }
+
+  STDMETHODIMP DrawRectangle(
+      _In_ CONST D2D1_RECT_F *rect,
+      _In_ ID2D1Brush *brush,
+      FLOAT strokeWidth,
+      _In_opt_ ID2D1StrokeStyle *strokeStyle
+    )
+  {
+    mCtx->DrawRectangle(rect, brush, strokeWidth, strokeStyle);
+    return S_OK;
+  }
+
+  STDMETHODIMP DrawBitmap(
+      _In_ ID2D1Bitmap *bitmap,
+      _In_opt_ CONST D2D1_RECT_F *destinationRectangle,
+      FLOAT opacity,
+      D2D1_INTERPOLATION_MODE interpolationMode,
+      _In_opt_ CONST D2D1_RECT_F *sourceRectangle,
+      _In_opt_ CONST D2D1_MATRIX_4X4_F *perspectiveTransform
+    )
+  {
+    mCtx->DrawBitmap(bitmap, destinationRectangle, opacity,
+                     interpolationMode, sourceRectangle,
+                     perspectiveTransform);
+    return S_OK;
+  }
+
+  STDMETHODIMP DrawImage(
+      _In_ ID2D1Image *image,
+      _In_opt_ CONST D2D1_POINT_2F *targetOffset,
+      _In_opt_ CONST D2D1_RECT_F *imageRectangle,
+      D2D1_INTERPOLATION_MODE interpolationMode,
+      D2D1_COMPOSITE_MODE compositeMode
+    )
+  {
+    mCtx->DrawImage(image, targetOffset, imageRectangle,
+                    interpolationMode, compositeMode);
+    return S_OK;
+  }
+
+  STDMETHODIMP DrawGdiMetafile(
+      _In_ ID2D1GdiMetafile *gdiMetafile,
+      _In_opt_ CONST D2D1_POINT_2F *targetOffset
+    )
+  {
+    mCtx->DrawGdiMetafile(gdiMetafile, targetOffset);
+    return S_OK;
+  }
+
+  STDMETHODIMP FillMesh(
+      _In_ ID2D1Mesh *mesh,
+      _In_ ID2D1Brush *brush
+    )
+  {
+    mCtx->FillMesh(mesh, brush);
+    return S_OK;
+  }
+
+  STDMETHODIMP FillOpacityMask(
+      _In_ ID2D1Bitmap *opacityMask,
+      _In_ ID2D1Brush *brush,
+      _In_opt_ CONST D2D1_RECT_F *destinationRectangle,
+      _In_opt_ CONST D2D1_RECT_F *sourceRectangle
+    )
+  {
+    mCtx->FillOpacityMask(opacityMask, brush, destinationRectangle,
+                          sourceRectangle);
+    return S_OK;
+  }
+
+  STDMETHODIMP FillGeometry(
+      _In_ ID2D1Geometry *geometry,
+      _In_ ID2D1Brush *brush,
+      _In_opt_ ID2D1Brush *opacityBrush
+    )
+  {
+    mCtx->FillGeometry(geometry, brush, opacityBrush);
+    return S_OK;
+  }
+
+  STDMETHODIMP FillRectangle(
+      _In_ CONST D2D1_RECT_F *rect,
+      _In_ ID2D1Brush *brush
+    )
+  {
+    mCtx->FillRectangle(rect, brush);
+    return S_OK;
+  }
+
+  STDMETHODIMP PushAxisAlignedClip(
+      _In_ CONST D2D1_RECT_F *clipRect,
+      D2D1_ANTIALIAS_MODE antialiasMode
+    )
+  {
+    mCtx->PushAxisAlignedClip(clipRect, antialiasMode);
+    return S_OK;
+  }
+
+  STDMETHODIMP PushLayer(
+      _In_ CONST D2D1_LAYER_PARAMETERS1 *layerParameters1,
+      _In_opt_ ID2D1Layer *layer
+    )
+  {
+    mCtx->PushLayer(layerParameters1, layer);
+    return S_OK;
+  }
+
+  STDMETHODIMP PopAxisAlignedClip()
+  {
+    mCtx->PopAxisAlignedClip();
+    return S_OK;
+  }
+
+  STDMETHODIMP PopLayer()
+  {
+    mCtx->PopLayer();
+    return S_OK;
+  }
+
+  ID2D1DeviceContext* mCtx;
+};
+
 }
 }
 
 #endif /* MOZILLA_GFX_HELPERSD2D_H_ */
--- a/gfx/2d/ScaledFontDWrite.cpp
+++ b/gfx/2d/ScaledFontDWrite.cpp
@@ -1,16 +1,24 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "DrawTargetD2D.h"
 #include "ScaledFontDWrite.h"
 #include "PathD2D.h"
 
+#ifdef USE_SKIA
+#include "PathSkia.h"
+#include "skia/include/core/SkPaint.h"
+#include "skia/include/core/SkPath.h"
+#include "skia/include/ports/SkTypeface_win.h"
+#endif
+
 #include <vector>
 
 #ifdef USE_CAIRO_SCALED_FONT
 #include "cairo-win32.h"
 #endif
 
 namespace mozilla {
 namespace gfx {
@@ -100,16 +108,30 @@ ScaledFontDWrite::GetPathForGlyphs(const
   PathBuilderD2D *pathBuilderD2D =
     static_cast<PathBuilderD2D*>(pathBuilder.get());
 
   CopyGlyphsToSink(aBuffer, pathBuilderD2D->GetSink());
 
   return pathBuilder->Finish();
 }
 
+
+#ifdef USE_SKIA
+SkTypeface*
+ScaledFontDWrite::GetSkTypeface()
+{
+  MOZ_ASSERT(mFont);
+  if (!mTypeface) {
+    IDWriteFactory *factory = DrawTargetD2D::GetDWriteFactory();
+    mTypeface = SkCreateTypefaceFromDWriteFont(factory, mFontFace, mFont, mFontFamily);
+  }
+  return mTypeface;
+}
+#endif
+
 void
 ScaledFontDWrite::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, BackendType aBackendType, const Matrix *aTransformHint)
 {
   if (aBackendType != BackendType::DIRECT2D && aBackendType != BackendType::DIRECT2D1_1) {
     ScaledFontBase::CopyGlyphsToBuilder(aBuffer, aBuilder, aBackendType, aTransformHint);
     return;
   }
 
--- a/gfx/2d/ScaledFontDWrite.h
+++ b/gfx/2d/ScaledFontDWrite.h
@@ -15,38 +15,46 @@ namespace mozilla {
 namespace gfx {
 
 class ScaledFontDWrite final : public ScaledFontBase
 {
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFontDwrite)
   ScaledFontDWrite(IDWriteFontFace *aFont, Float aSize)
     : ScaledFontBase(aSize)
+    , mFont(nullptr)
+    , mFontFamily(nullptr)
     , mFontFace(aFont)
   {}
 
+  ScaledFontDWrite(IDWriteFont* aFont, IDWriteFontFamily* aFontFamily,
+                   IDWriteFontFace *aFontFace, Float aSize)
+    : ScaledFontBase(aSize)
+    , mFont(aFont)
+    , mFontFamily(aFontFamily)
+    , mFontFace(aFontFace)
+  {}
+
   virtual FontType GetType() const { return FontType::DWRITE; }
 
   virtual already_AddRefed<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget);
   virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, BackendType aBackendType, const Matrix *aTransformHint);
 
   void CopyGlyphsToSink(const GlyphBuffer &aBuffer, ID2D1GeometrySink *aSink);
 
   virtual bool GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton);
 
   virtual AntialiasMode GetDefaultAAMode();
 
 #ifdef USE_SKIA
-  virtual SkTypeface* GetSkTypeface()
-  {
-    MOZ_ASSERT(false, "Skia and DirectWrite do not mix");
-    return nullptr;
-  }
+  virtual SkTypeface* GetSkTypeface();
 #endif
 
+  RefPtr<IDWriteFont> mFont;
+  RefPtr<IDWriteFontFamily> mFontFamily;
   RefPtr<IDWriteFontFace> mFontFace;
 
 protected:
 #ifdef USE_CAIRO_SCALED_FONT
   cairo_font_face_t* GetCairoFontFace() override;
 #endif
 };
 
--- a/gfx/2d/Types.h
+++ b/gfx/2d/Types.h
@@ -138,17 +138,16 @@ enum class FontType : int8_t {
   MAC,
   SKIA,
   CAIRO,
   COREGRAPHICS
 };
 
 enum class NativeSurfaceType : int8_t {
   D3D10_TEXTURE,
-  CAIRO_SURFACE,
   CAIRO_CONTEXT,
   CGCONTEXT,
   CGCONTEXT_ACCELERATED,
   OPENGL_TEXTURE
 };
 
 enum class NativeFontType : int8_t {
   DWRITE_FONT_FACE,
--- a/gfx/cairo/libpixman/src/pixman-region.c
+++ b/gfx/cairo/libpixman/src/pixman-region.c
@@ -1329,16 +1329,25 @@ PREFIX(_intersect_rect) (region_type_t *
     region_type_t region;
 
     region.data = NULL;
     region.extents.x1 = x;
     region.extents.y1 = y;
     region.extents.x2 = x + width;
     region.extents.y2 = y + height;
 
+    if (!GOOD_RECT (&region.extents))
+    {
+        if (BAD_RECT (&region.extents))
+            _pixman_log_error (FUNC, "Invalid rectangle passed");
+        FREE_DATA (dest);
+        PREFIX (_init) (dest);
+        return TRUE;
+    }
+
     return PREFIX(_intersect) (dest, source, &region);
 }
 
 /* Convenience function for performing union of region with a
  * single rectangle
  */
 PIXMAN_EXPORT pixman_bool_t
 PREFIX (_union_rect) (region_type_t *dest,
--- a/gfx/ipc/GfxMessageUtils.h
+++ b/gfx/ipc/GfxMessageUtils.h
@@ -372,17 +372,17 @@ template<typename Region, typename Rect,
 struct RegionParamTraits
 {
   typedef Region paramType;
 
   static void Write(Message* msg, const paramType& param)
   {
     Iter it(param);
     while (const Rect* r = it.Next()) {
-      MOZ_ASSERT(!r->IsEmpty());
+      MOZ_RELEASE_ASSERT(!r->IsEmpty());
       WriteParam(msg, *r);
     }
     // empty rects are sentinel values because nsRegions will never
     // contain them
     WriteParam(msg, Rect());
   }
 
   static bool Read(const Message* msg, void** iter, paramType* result)
@@ -679,96 +679,110 @@ struct ParamTraits<nsRegion>
 
 template <>
 struct ParamTraits<mozilla::layers::FrameMetrics>
 {
   typedef mozilla::layers::FrameMetrics paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
   {
-    WriteParam(aMsg, aParam.mScrollableRect);
-    WriteParam(aMsg, aParam.mViewport);
-    WriteParam(aMsg, aParam.mScrollOffset);
-    WriteParam(aMsg, aParam.mDisplayPort);
-    WriteParam(aMsg, aParam.mDisplayPortMargins);
-    WriteParam(aMsg, aParam.mUseDisplayPortMargins);
-    WriteParam(aMsg, aParam.mCriticalDisplayPort);
-    WriteParam(aMsg, aParam.mCompositionBounds);
-    WriteParam(aMsg, aParam.mRootCompositionSize);
     WriteParam(aMsg, aParam.mScrollId);
     WriteParam(aMsg, aParam.mScrollParentId);
     WriteParam(aMsg, aParam.mPresShellResolution);
+    WriteParam(aMsg, aParam.mCompositionBounds);
+    WriteParam(aMsg, aParam.mDisplayPort);
+    WriteParam(aMsg, aParam.mCriticalDisplayPort);
+    WriteParam(aMsg, aParam.mScrollableRect);
     WriteParam(aMsg, aParam.mCumulativeResolution);
+    WriteParam(aMsg, aParam.mDevPixelsPerCSSPixel);
+    WriteParam(aMsg, aParam.mScrollOffset);
     WriteParam(aMsg, aParam.mZoom);
-    WriteParam(aMsg, aParam.mDevPixelsPerCSSPixel);
+    WriteParam(aMsg, aParam.mScrollGeneration);
+    WriteParam(aMsg, aParam.mSmoothScrollOffset);
+    WriteParam(aMsg, aParam.mRootCompositionSize);
+    WriteParam(aMsg, aParam.mDisplayPortMargins);
     WriteParam(aMsg, aParam.mPresShellId);
+    WriteParam(aMsg, aParam.mViewport);
+    WriteParam(aMsg, aParam.mExtraResolution);
+    WriteParam(aMsg, aParam.mBackgroundColor);
+    WriteParam(aMsg, aParam.GetContentDescription());
+    WriteParam(aMsg, aParam.mLineScrollAmount);
+    WriteParam(aMsg, aParam.mPageScrollAmount);
+    WriteParam(aMsg, aParam.mClipRect);
+    WriteParam(aMsg, aParam.mMaskLayerIndex);
     WriteParam(aMsg, aParam.mIsRootContent);
     WriteParam(aMsg, aParam.mHasScrollgrab);
     WriteParam(aMsg, aParam.mUpdateScrollOffset);
-    WriteParam(aMsg, aParam.mScrollGeneration);
-    WriteParam(aMsg, aParam.mExtraResolution);
-    WriteParam(aMsg, aParam.mBackgroundColor);
     WriteParam(aMsg, aParam.mDoSmoothScroll);
-    WriteParam(aMsg, aParam.mSmoothScrollOffset);
-    WriteParam(aMsg, aParam.GetLineScrollAmount());
-    WriteParam(aMsg, aParam.GetPageScrollAmount());
-    WriteParam(aMsg, aParam.AllowVerticalScrollWithWheel());
-    WriteParam(aMsg, aParam.mClipRect);
-    WriteParam(aMsg, aParam.mMaskLayerIndex);
+    WriteParam(aMsg, aParam.mUseDisplayPortMargins);
+    WriteParam(aMsg, aParam.mAllowVerticalScrollWithWheel);
     WriteParam(aMsg, aParam.mIsLayersIdRoot);
     WriteParam(aMsg, aParam.mUsesContainerScrolling);
     WriteParam(aMsg, aParam.mIsScrollInfoLayer);
-    WriteParam(aMsg, aParam.GetContentDescription());
   }
 
   static bool ReadContentDescription(const Message* aMsg, void** aIter, paramType* aResult)
   {
     nsCString str;
     if (!ReadParam(aMsg, aIter, &str)) {
       return false;
     }
     aResult->SetContentDescription(str);
     return true;
   }
 
+  // We need this helper because we can't get the address of a bitfield to
+  // pass directly to ReadParam. So instead we read it into a temporary bool
+  // and set the bitfield using a setter function
+  static bool ReadBoolForBitfield(const Message* aMsg, void** aIter,
+        paramType* aResult, void (paramType::*aSetter)(bool))
+  {
+    bool value;
+    if (ReadParam(aMsg, aIter, &value)) {
+      (aResult->*aSetter)(value);
+      return true;
+    }
+    return false;
+  }
+
   static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
   {
-    return (ReadParam(aMsg, aIter, &aResult->mScrollableRect) &&
-            ReadParam(aMsg, aIter, &aResult->mViewport) &&
-            ReadParam(aMsg, aIter, &aResult->mScrollOffset) &&
-            ReadParam(aMsg, aIter, &aResult->mDisplayPort) &&
-            ReadParam(aMsg, aIter, &aResult->mDisplayPortMargins) &&
-            ReadParam(aMsg, aIter, &aResult->mUseDisplayPortMargins) &&
-            ReadParam(aMsg, aIter, &aResult->mCriticalDisplayPort) &&
-            ReadParam(aMsg, aIter, &aResult->mCompositionBounds) &&
-            ReadParam(aMsg, aIter, &aResult->mRootCompositionSize) &&
-            ReadParam(aMsg, aIter, &aResult->mScrollId) &&
+    return (ReadParam(aMsg, aIter, &aResult->mScrollId) &&
             ReadParam(aMsg, aIter, &aResult->mScrollParentId) &&
             ReadParam(aMsg, aIter, &aResult->mPresShellResolution) &&
+            ReadParam(aMsg, aIter, &aResult->mCompositionBounds) &&
+            ReadParam(aMsg, aIter, &aResult->mDisplayPort) &&
+            ReadParam(aMsg, aIter, &aResult->mCriticalDisplayPort) &&
+            ReadParam(aMsg, aIter, &aResult->mScrollableRect) &&
             ReadParam(aMsg, aIter, &aResult->mCumulativeResolution) &&
+            ReadParam(aMsg, aIter, &aResult->mDevPixelsPerCSSPixel) &&
+            ReadParam(aMsg, aIter, &aResult->mScrollOffset) &&
             ReadParam(aMsg, aIter, &aResult->mZoom) &&
-            ReadParam(aMsg, aIter, &aResult->mDevPixelsPerCSSPixel) &&
+            ReadParam(aMsg, aIter, &aResult->mScrollGeneration) &&
+            ReadParam(aMsg, aIter, &aResult->mSmoothScrollOffset) &&
+            ReadParam(aMsg, aIter, &aResult->mRootCompositionSize) &&
+            ReadParam(aMsg, aIter, &aResult->mDisplayPortMargins) &&
             ReadParam(aMsg, aIter, &aResult->mPresShellId) &&
-            ReadParam(aMsg, aIter, &aResult->mIsRootContent) &&
-            ReadParam(aMsg, aIter, &aResult->mHasScrollgrab) &&
-            ReadParam(aMsg, aIter, &aResult->mUpdateScrollOffset) &&
-            ReadParam(aMsg, aIter, &aResult->mScrollGeneration) &&
+            ReadParam(aMsg, aIter, &aResult->mViewport) &&
             ReadParam(aMsg, aIter, &aResult->mExtraResolution) &&
             ReadParam(aMsg, aIter, &aResult->mBackgroundColor) &&
-            ReadParam(aMsg, aIter, &aResult->mDoSmoothScroll) &&
-            ReadParam(aMsg, aIter, &aResult->mSmoothScrollOffset) &&
+            ReadContentDescription(aMsg, aIter, aResult) &&
             ReadParam(aMsg, aIter, &aResult->mLineScrollAmount) &&
             ReadParam(aMsg, aIter, &aResult->mPageScrollAmount) &&
-            ReadParam(aMsg, aIter, &aResult->mAllowVerticalScrollWithWheel) &&
             ReadParam(aMsg, aIter, &aResult->mClipRect) &&
             ReadParam(aMsg, aIter, &aResult->mMaskLayerIndex) &&
-            ReadParam(aMsg, aIter, &aResult->mIsLayersIdRoot) &&
-            ReadParam(aMsg, aIter, &aResult->mUsesContainerScrolling) &&
-            ReadParam(aMsg, aIter, &aResult->mIsScrollInfoLayer) &&
-            ReadContentDescription(aMsg, aIter, aResult));
+            ReadBoolForBitfield(aMsg, aIter, aResult, &paramType::SetIsRootContent) &&
+            ReadBoolForBitfield(aMsg, aIter, aResult, &paramType::SetHasScrollgrab) &&
+            ReadBoolForBitfield(aMsg, aIter, aResult, &paramType::SetUpdateScrollOffset) &&
+            ReadBoolForBitfield(aMsg, aIter, aResult, &paramType::SetDoSmoothScroll) &&
+            ReadBoolForBitfield(aMsg, aIter, aResult, &paramType::SetUseDisplayPortMargins) &&
+            ReadBoolForBitfield(aMsg, aIter, aResult, &paramType::SetAllowVerticalScrollWithWheel) &&
+            ReadBoolForBitfield(aMsg, aIter, aResult, &paramType::SetIsLayersIdRoot) &&
+            ReadBoolForBitfield(aMsg, aIter, aResult, &paramType::SetUsesContainerScrolling) &&
+            ReadBoolForBitfield(aMsg, aIter, aResult, &paramType::SetIsScrollInfoLayer));
   }
 };
 
 template<>
 struct ParamTraits<mozilla::layers::TextureFactoryIdentifier>
 {
   typedef mozilla::layers::TextureFactoryIdentifier paramType;
 
--- a/gfx/layers/FrameMetrics.h
+++ b/gfx/layers/FrameMetrics.h
@@ -34,85 +34,92 @@ public:
   // We use IDs to identify frames across processes.
   typedef uint64_t ViewID;
   static const ViewID NULL_SCROLL_ID;   // This container layer does not scroll.
   static const ViewID START_SCROLL_ID = 2;  // This is the ID that scrolling subframes
                                         // will begin at.
   static const FrameMetrics sNullMetrics;   // We often need an empty metrics
 
   FrameMetrics()
-    : mPresShellResolution(1)
+    : mScrollId(NULL_SCROLL_ID)
+    , mScrollParentId(NULL_SCROLL_ID)
+    , mPresShellResolution(1)
     , mCompositionBounds(0, 0, 0, 0)
     , mDisplayPort(0, 0, 0, 0)
     , mCriticalDisplayPort(0, 0, 0, 0)
     , mScrollableRect(0, 0, 0, 0)
     , mCumulativeResolution()
     , mDevPixelsPerCSSPixel(1)
-    , mIsRootContent(false)
-    , mHasScrollgrab(false)
-    , mScrollId(NULL_SCROLL_ID)
-    , mScrollParentId(NULL_SCROLL_ID)
     , mScrollOffset(0, 0)
     , mZoom()
-    , mUpdateScrollOffset(false)
     , mScrollGeneration(0)
-    , mDoSmoothScroll(false)
     , mSmoothScrollOffset(0, 0)
     , mRootCompositionSize(0, 0)
     , mDisplayPortMargins(0, 0, 0, 0)
-    , mUseDisplayPortMargins(false)
     , mPresShellId(-1)
     , mViewport(0, 0, 0, 0)
     , mExtraResolution()
     , mBackgroundColor()
+    , mContentDescription()
     , mLineScrollAmount(0, 0)
     , mPageScrollAmount(0, 0)
+    , mClipRect()
+    , mMaskLayerIndex()
+    , mIsRootContent(false)
+    , mHasScrollgrab(false)
+    , mUpdateScrollOffset(false)
+    , mDoSmoothScroll(false)
+    , mUseDisplayPortMargins(false)
     , mAllowVerticalScrollWithWheel(false)
     , mIsLayersIdRoot(false)
     , mUsesContainerScrolling(false)
     , mIsScrollInfoLayer(false)
   {
   }
 
   // Default copy ctor and operator= are fine
 
   bool operator==(const FrameMetrics& aOther) const
   {
-    return mCompositionBounds.IsEqualEdges(aOther.mCompositionBounds) &&
-           mRootCompositionSize == aOther.mRootCompositionSize &&
+    // Put mScrollId at the top since it's the most likely one to fail.
+    return mScrollId == aOther.mScrollId &&
+           mScrollParentId == aOther.mScrollParentId &&
+           mPresShellResolution == aOther.mPresShellResolution &&
+           mCompositionBounds.IsEqualEdges(aOther.mCompositionBounds) &&
            mDisplayPort.IsEqualEdges(aOther.mDisplayPort) &&
-           mDisplayPortMargins == aOther.mDisplayPortMargins &&
-           mUseDisplayPortMargins == aOther.mUseDisplayPortMargins &&
            mCriticalDisplayPort.IsEqualEdges(aOther.mCriticalDisplayPort) &&
-           mViewport.IsEqualEdges(aOther.mViewport) &&
            mScrollableRect.IsEqualEdges(aOther.mScrollableRect) &&
-           mPresShellResolution == aOther.mPresShellResolution &&
            mCumulativeResolution == aOther.mCumulativeResolution &&
            mDevPixelsPerCSSPixel == aOther.mDevPixelsPerCSSPixel &&
+           mScrollOffset == aOther.mScrollOffset &&
+           // don't compare mZoom
+           mScrollGeneration == aOther.mScrollGeneration &&
+           mSmoothScrollOffset == aOther.mSmoothScrollOffset &&
+           mRootCompositionSize == aOther.mRootCompositionSize &&
+           mDisplayPortMargins == aOther.mDisplayPortMargins &&
            mPresShellId == aOther.mPresShellId &&
+           mViewport.IsEqualEdges(aOther.mViewport) &&
+           mExtraResolution == aOther.mExtraResolution &&
+           mBackgroundColor == aOther.mBackgroundColor &&
+           // don't compare mContentDescription
+           mLineScrollAmount == aOther.mLineScrollAmount &&
+           mPageScrollAmount == aOther.mPageScrollAmount &&
+           mClipRect == aOther.mClipRect &&
+           mMaskLayerIndex == aOther.mMaskLayerIndex &&
            mIsRootContent == aOther.mIsRootContent &&
-           mScrollId == aOther.mScrollId &&
-           mScrollParentId == aOther.mScrollParentId &&
-           mScrollOffset == aOther.mScrollOffset &&
-           mSmoothScrollOffset == aOther.mSmoothScrollOffset &&
            mHasScrollgrab == aOther.mHasScrollgrab &&
            mUpdateScrollOffset == aOther.mUpdateScrollOffset &&
-           mScrollGeneration == aOther.mScrollGeneration &&
-           mExtraResolution == aOther.mExtraResolution &&
-           mBackgroundColor == aOther.mBackgroundColor &&
            mDoSmoothScroll == aOther.mDoSmoothScroll &&
-           mLineScrollAmount == aOther.mLineScrollAmount &&
-           mPageScrollAmount == aOther.mPageScrollAmount &&
+           mUseDisplayPortMargins == aOther.mUseDisplayPortMargins &&
            mAllowVerticalScrollWithWheel == aOther.mAllowVerticalScrollWithWheel &&
-           mClipRect == aOther.mClipRect &&
-           mMaskLayerIndex == aOther.mMaskLayerIndex &&
            mIsLayersIdRoot == aOther.mIsLayersIdRoot &&
            mUsesContainerScrolling == aOther.mUsesContainerScrolling &&
            mIsScrollInfoLayer == aOther.mIsScrollInfoLayer;
   }
+
   bool operator!=(const FrameMetrics& aOther) const
   {
     return !operator==(aOther);
   }
 
   bool IsDefault() const
   {
     FrameMetrics def;
@@ -406,19 +413,19 @@ public:
     mDisplayPortMargins = aDisplayPortMargins;
   }
 
   const ScreenMargin& GetDisplayPortMargins() const
   {
     return mDisplayPortMargins;
   }
 
-  void SetUseDisplayPortMargins()
+  void SetUseDisplayPortMargins(bool aValue)
   {
-    mUseDisplayPortMargins = true;
+    mUseDisplayPortMargins = aValue;
   }
 
   bool GetUseDisplayPortMargins() const
   {
     return mUseDisplayPortMargins;
   }
 
   uint32_t GetPresShellId() const
@@ -501,19 +508,19 @@ public:
     mScrollableRect = aScrollableRect;
   }
 
   bool AllowVerticalScrollWithWheel() const
   {
     return mAllowVerticalScrollWithWheel;
   }
 
-  void SetAllowVerticalScrollWithWheel()
+  void SetAllowVerticalScrollWithWheel(bool aValue)
   {
-    mAllowVerticalScrollWithWheel = true;
+    mAllowVerticalScrollWithWheel = aValue;
   }
 
   void SetClipRect(const Maybe<ParentLayerIntRect>& aClipRect)
   {
     mClipRect = aClipRect;
   }
   const Maybe<ParentLayerIntRect>& GetClipRect() const
   {
@@ -550,16 +557,21 @@ public:
   void SetIsScrollInfoLayer(bool aIsScrollInfoLayer) {
     mIsScrollInfoLayer = aIsScrollInfoLayer;
   }
   bool IsScrollInfoLayer() const {
     return mIsScrollInfoLayer;
   }
 
 private:
+  // A unique ID assigned to each scrollable frame.
+  ViewID mScrollId;
+
+  // The ViewID of the scrollable frame to which overscroll should be handed off.
+  ViewID mScrollParentId;
 
   // The pres-shell resolution that has been induced on the document containing
   // this scroll frame as a result of zooming this scroll frame (whether via
   // user action, or choosing an initial zoom level on page load). This can
   // only be different from 1.0 for frames that are zoomable, which currently
   // is just the root content document's root scroll frame (mIsRoot = true).
   // This is a plain float rather than a ScaleFactor because in and of itself
   // it does not convert between any coordinate spaces for which we have names.
@@ -629,28 +641,16 @@ private:
   // be refactored to be private.
 
   // The conversion factor between CSS pixels and device pixels for this frame.
   // This can vary based on a variety of things, such as reflowing-zoom. The
   // conversion factor for device pixels to layers pixels is just the
   // resolution.
   CSSToLayoutDeviceScale mDevPixelsPerCSSPixel;
 
-  // Whether or not this is the root scroll frame for the root content document.
-  bool mIsRootContent;
-
-  // Whether or not this frame is for an element marked 'scrollgrab'.
-  bool mHasScrollgrab;
-
-  // A unique ID assigned to each scrollable frame.
-  ViewID mScrollId;
-
-  // The ViewID of the scrollable frame to which overscroll should be handed off.
-  ViewID mScrollParentId;
-
   // The position of the top-left of the CSS viewport, relative to the document
   // (or the document relative to the viewport, if that helps understand it).
   //
   // Thus it is relative to the document. It is in the same coordinate space as
   // |mScrollableRect|, but a different coordinate space than |mViewport| and
   // |mDisplayPort|.
   //
   // It is required that the rect:
@@ -664,38 +664,30 @@ private:
   CSSPoint mScrollOffset;
 
   // The "user zoom". Content is painted by gecko at mResolution * mDevPixelsPerCSSPixel,
   // but will be drawn to the screen at mZoom. In the steady state, the
   // two will be the same, but during an async zoom action the two may
   // diverge. This information is initialized in Gecko but updated in the APZC.
   CSSToParentLayerScale2D mZoom;
 
-  // Whether mScrollOffset was updated by something other than the APZ code, and
-  // if the APZC receiving this metrics should update its local copy.
-  bool mUpdateScrollOffset;
   // The scroll generation counter used to acknowledge the scroll offset update.
   uint32_t mScrollGeneration;
 
-  // When mDoSmoothScroll, the scroll offset should be animated to
-  // smoothly transition to mScrollOffset rather than be updated instantly.
-  bool mDoSmoothScroll;
+  // If mDoSmoothScroll is true, the scroll offset will be animated smoothly
+  // to this value.
   CSSPoint mSmoothScrollOffset;
 
   // The size of the root scrollable's composition bounds, but in local CSS pixels.
   CSSSize mRootCompositionSize;
 
   // A display port expressed as layer margins that apply to the rect of what
   // is drawn of the scrollable element.
   ScreenMargin mDisplayPortMargins;
 
-  // If this is true then we use the display port margins on this metrics,
-  // otherwise use the display port rect.
-  bool mUseDisplayPortMargins;
-
   uint32_t mPresShellId;
 
   // The CSS viewport, which is the dimensions we're using to constrain the
   // <html> element of this frame, relative to the top-left of the layer. Note
   // that its offset is structured in such a way that it doesn't depend on the
   // method layout uses to scroll content.
   //
   // This is mainly useful on the root layer, however nested iframes can have
@@ -717,47 +709,74 @@ private:
   nsCString mContentDescription;
 
   // The value of GetLineScrollAmount(), for scroll frames.
   LayoutDeviceIntSize mLineScrollAmount;
 
   // The value of GetPageScrollAmount(), for scroll frames.
   LayoutDeviceIntSize mPageScrollAmount;
 
-  // Whether or not the frame can be vertically scrolled with a mouse wheel.
-  bool mAllowVerticalScrollWithWheel;
-
   // The clip rect to use when compositing a layer with this FrameMetrics.
   Maybe<ParentLayerIntRect> mClipRect;
 
   // An extra clip mask layer to use when compositing a layer with this
   // FrameMetrics. This is an index into the MetricsMaskLayers array on
   // the Layer.
   Maybe<size_t> mMaskLayerIndex;
 
+  // Whether or not this is the root scroll frame for the root content document.
+  bool mIsRootContent:1;
+
+  // Whether or not this frame is for an element marked 'scrollgrab'.
+  bool mHasScrollgrab:1;
+
+  // Whether mScrollOffset was updated by something other than the APZ code, and
+  // if the APZC receiving this metrics should update its local copy.
+  bool mUpdateScrollOffset:1;
+
+  // When mDoSmoothScroll, the scroll offset should be animated to
+  // smoothly transition to mScrollOffset rather than be updated instantly.
+  bool mDoSmoothScroll:1;
+
+  // If this is true then we use the display port margins on this metrics,
+  // otherwise use the display port rect.
+  bool mUseDisplayPortMargins:1;
+
+  // Whether or not the frame can be vertically scrolled with a mouse wheel.
+  bool mAllowVerticalScrollWithWheel:1;
+
   // Whether these framemetrics are for the root scroll frame (root element if
   // we don't have a root scroll frame) for its layers id.
-  bool mIsLayersIdRoot;
+  bool mIsLayersIdRoot:1;
 
   // True if scrolling using containers, false otherwise. This can be removed
   // when containerful scrolling is eliminated.
-  bool mUsesContainerScrolling;
+  bool mUsesContainerScrolling:1;
 
   // Whether or not this frame has a "scroll info layer" to capture events.
-  bool mIsScrollInfoLayer;
+  bool mIsScrollInfoLayer:1;
 
   // WARNING!!!!
   //
   // When adding new fields to FrameMetrics, the following places should be
   // updated to include them (as needed):
   //    FrameMetrics::operator ==
   //    AsyncPanZoomController::NotifyLayersUpdated
   //    The ParamTraits specialization in GfxMessageUtils.h
   //
   // Please add new fields above this comment.
+
+
+  // Private helpers for IPC purposes
+  void SetUpdateScrollOffset(bool aValue) {
+    mUpdateScrollOffset = aValue;
+  }
+  void SetDoSmoothScroll(bool aValue) {
+    mDoSmoothScroll = aValue;
+  }
 };
 
 /**
  * This class allows us to uniquely identify a scrollable layer. The
  * mLayersId identifies the layer tree (corresponding to a child process
  * and/or tab) that the scrollable layer belongs to. The mPresShellId
  * is a temporal identifier (corresponding to the document loaded that
  * contains the scrollable layer, which may change over time). The
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -2426,17 +2426,17 @@ PrintInfo(std::stringstream& aStream, La
     AppendToString(aStream, aLayerComposite->GetShadowVisibleRegion().ToUnknownRegion(), " [shadow-visible=", "]");
   }
 }
 
 void
 SetAntialiasingFlags(Layer* aLayer, DrawTarget* aTarget)
 {
   bool permitSubpixelAA = !(aLayer->GetContentFlags() & Layer::CONTENT_DISABLE_SUBPIXEL_AA);
-  if (aTarget->GetFormat() != SurfaceFormat::B8G8R8A8) {
+  if (aTarget->IsCurrentGroupOpaque()) {
     aTarget->SetPermitSubpixelAA(permitSubpixelAA);
     return;
   }
 
   const IntRect& bounds = aLayer->GetVisibleRegion().ToUnknownRegion().GetBounds();
   gfx::Rect transformedBounds = aTarget->GetTransform().TransformBounds(gfx::Rect(Float(bounds.x), Float(bounds.y),
                                                                                   Float(bounds.width), Float(bounds.height)));
   transformedBounds.RoundOut();
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -2800,17 +2800,17 @@ int32_t AsyncPanZoomController::GetLastT
 }
 
 void AsyncPanZoomController::RequestContentRepaint() {
   RequestContentRepaint(mFrameMetrics);
 }
 
 void AsyncPanZoomController::RequestContentRepaint(FrameMetrics& aFrameMetrics) {
   aFrameMetrics.SetDisplayPortMargins(CalculatePendingDisplayPort(aFrameMetrics, GetVelocityVector()));
-  aFrameMetrics.SetUseDisplayPortMargins();
+  aFrameMetrics.SetUseDisplayPortMargins(true);
 
   // If we're trying to paint what we already think is painted, discard this
   // request since it's a pointless paint.
   ScreenMargin marginDelta = (mLastPaintRequestMetrics.GetDisplayPortMargins()
                            - aFrameMetrics.GetDisplayPortMargins());
   if (fabsf(marginDelta.left) < EPSILON &&
       fabsf(marginDelta.top) < EPSILON &&
       fabsf(marginDelta.right) < EPSILON &&
@@ -3394,17 +3394,17 @@ void AsyncPanZoomController::ZoomToRect(
       if (aRect.y < 0.0f) {
         aRect.y = 0.0f;
       }
     }
 
     endZoomToMetrics.SetScrollOffset(aRect.TopLeft());
     endZoomToMetrics.SetDisplayPortMargins(
       CalculatePendingDisplayPort(endZoomToMetrics, ParentLayerPoint(0,0)));
-    endZoomToMetrics.SetUseDisplayPortMargins();
+    endZoomToMetrics.SetUseDisplayPortMargins(true);
 
     StartAnimation(new ZoomAnimation(
         mFrameMetrics.GetScrollOffset(),
         mFrameMetrics.GetZoom(),
         endZoomToMetrics.GetScrollOffset(),
         endZoomToMetrics.GetZoom()));
 
     // Schedule a repaint now, so the new displayport will be painted before the
--- a/gfx/layers/apz/test/gtest/TestAsyncPanZoomController.cpp
+++ b/gfx/layers/apz/test/gtest/TestAsyncPanZoomController.cpp
@@ -2077,17 +2077,17 @@ protected:
       metrics.SetIsLayersIdRoot(true);
     }
     IntRect layerBound = aLayer->GetVisibleRegion().ToUnknownRegion().GetBounds();
     metrics.SetCompositionBounds(ParentLayerRect(layerBound.x, layerBound.y,
                                                  layerBound.width, layerBound.height));
     metrics.SetScrollableRect(aScrollableRect);
     metrics.SetScrollOffset(CSSPoint(0, 0));
     metrics.SetPageScrollAmount(LayoutDeviceIntSize(50, 100));
-    metrics.SetAllowVerticalScrollWithWheel();
+    metrics.SetAllowVerticalScrollWithWheel(true);
     aLayer->SetFrameMetrics(metrics);
     aLayer->SetClipRect(Some(ViewAs<ParentLayerPixel>(layerBound)));
     if (!aScrollableRect.IsEqualEdges(CSSRect(-1, -1, -1, -1))) {
       // The purpose of this is to roughly mimic what layout would do in the
       // case of a scrollable frame with the event regions and clip. This lets
       // us exercise the hit-testing code in APZCTreeManager
       EventRegions er = aLayer->GetEventRegions();
       IntRect scrollRect = RoundedToInt(aScrollableRect * metrics.LayersPixelsPerCSSPixel()).ToUnknownRect();
--- a/gfx/layers/basic/X11TextureSourceBasic.cpp
+++ b/gfx/layers/basic/X11TextureSourceBasic.cpp
@@ -32,17 +32,17 @@ X11TextureSourceBasic::GetFormat() const
 }
 
 SourceSurface*
 X11TextureSourceBasic::GetSurface(DrawTarget* aTarget)
 {
   if (!mSourceSurface) {
     NativeSurface surf;
     surf.mFormat = GetFormat();
-    surf.mType = NativeSurfaceType::CAIRO_SURFACE;
+    surf.mType = NativeSurfaceType::CAIRO_CONTEXT;
     surf.mSurface = mSurface->CairoSurface();
     surf.mSize = GetSize();
     mSourceSurface = aTarget->CreateSourceSurfaceFromNativeSurface(surf);
   }
   return mSourceSurface;
 }
 
 void
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -509,36 +509,37 @@ ClientLayerManager::MakeSnapshotIfRequir
         bounds =
           RotateRect(bounds, outerBounds.ToUnknownRect(), mTargetRotation);
       }
 
       SurfaceDescriptor inSnapshot;
       if (!bounds.IsEmpty() &&
           mForwarder->AllocSurfaceDescriptor(bounds.Size(),
                                              gfxContentType::COLOR_ALPHA,
-                                             &inSnapshot) &&
-          remoteRenderer->SendMakeSnapshot(inSnapshot, bounds)) {
-        RefPtr<DataSourceSurface> surf = GetSurfaceForDescriptor(inSnapshot);
-        DrawTarget* dt = mShadowTarget->GetDrawTarget();
+                                             &inSnapshot)) {
+        if (remoteRenderer->SendMakeSnapshot(inSnapshot, bounds)) {
+          RefPtr<DataSourceSurface> surf = GetSurfaceForDescriptor(inSnapshot);
+          DrawTarget* dt = mShadowTarget->GetDrawTarget();
 
-        Rect dstRect(bounds.x, bounds.y, bounds.width, bounds.height);
-        Rect srcRect(0, 0, bounds.width, bounds.height);
+          Rect dstRect(bounds.x, bounds.y, bounds.width, bounds.height);
+          Rect srcRect(0, 0, bounds.width, bounds.height);
 
-        gfx::Matrix rotate =
-          ComputeTransformForUnRotation(outerBounds.ToUnknownRect(),
-                                        mTargetRotation);
+          gfx::Matrix rotate =
+            ComputeTransformForUnRotation(outerBounds.ToUnknownRect(),
+                                          mTargetRotation);
 
-        gfx::Matrix oldMatrix = dt->GetTransform();
-        dt->SetTransform(rotate * oldMatrix);
-        dt->DrawSurface(surf, dstRect, srcRect,
-                        DrawSurfaceOptions(),
-                        DrawOptions(1.0f, CompositionOp::OP_OVER));
-        dt->SetTransform(oldMatrix);
+          gfx::Matrix oldMatrix = dt->GetTransform();
+          dt->SetTransform(rotate * oldMatrix);
+          dt->DrawSurface(surf, dstRect, srcRect,
+                          DrawSurfaceOptions(),
+                          DrawOptions(1.0f, CompositionOp::OP_OVER));
+          dt->SetTransform(oldMatrix);
+        }
+        mForwarder->DestroySharedSurface(&inSnapshot);
       }
-      mForwarder->DestroySharedSurface(&inSnapshot);
     }
   }
   mShadowTarget = nullptr;
 }
 
 void
 ClientLayerManager::FlushRendering()
 {
--- a/gfx/layers/d3d11/TextureD3D11.cpp
+++ b/gfx/layers/d3d11/TextureD3D11.cpp
@@ -115,17 +115,22 @@ GetTileRectD3D11(uint32_t aID, IntSize a
 ID3D11ShaderResourceView*
 TextureSourceD3D11::GetShaderResourceView()
 {
   MOZ_ASSERT(mTexture == GetD3D11Texture(), "You need to override GetShaderResourceView if you're overriding GetD3D11Texture!");
 
   if (!mSRV && mTexture) {
     RefPtr<ID3D11Device> device;
     mTexture->GetDevice(getter_AddRefs(device));
-    HRESULT hr = device->CreateShaderResourceView(mTexture, nullptr, getter_AddRefs(mSRV));
+
+    // see comment in CompositingRenderTargetD3D11 constructor
+    CD3D11_SHADER_RESOURCE_VIEW_DESC srvDesc(D3D11_SRV_DIMENSION_TEXTURE2D, mFormatOverride);
+    D3D11_SHADER_RESOURCE_VIEW_DESC *desc = mFormatOverride == DXGI_FORMAT_UNKNOWN ? nullptr : &srvDesc;
+
+    HRESULT hr = device->CreateShaderResourceView(mTexture, desc, getter_AddRefs(mSRV));
     if (FAILED(hr)) {
       gfxCriticalNote << "[D3D11] TextureSourceD3D11:GetShaderResourceView CreateSRV failure " << gfx::hexa(hr);
       return nullptr;
     }
   }
   return mSRV;
 }
 
@@ -1161,27 +1166,36 @@ DataTextureSourceD3D11::SetCompositor(Co
   MOZ_ASSERT(aCompositor);
   mCompositor = static_cast<CompositorD3D11*>(aCompositor);
   if (mNextSibling) {
     mNextSibling->SetCompositor(aCompositor);
   }
 }
 
 CompositingRenderTargetD3D11::CompositingRenderTargetD3D11(ID3D11Texture2D* aTexture,
-                                                           const gfx::IntPoint& aOrigin)
+                                                           const gfx::IntPoint& aOrigin,
+                                                           DXGI_FORMAT aFormatOverride)
   : CompositingRenderTarget(aOrigin)
 {
   MOZ_ASSERT(aTexture);
 
   mTexture = aTexture;
 
   RefPtr<ID3D11Device> device;
   mTexture->GetDevice(getter_AddRefs(device));
 
-  HRESULT hr = device->CreateRenderTargetView(mTexture, nullptr, getter_AddRefs(mRTView));
+  mFormatOverride = aFormatOverride;
+
+  // If we happen to have a typeless underlying DXGI surface, we need to be explicit
+  // about the format here. (Such a surface could come from an external source, such
+  // as the Oculus compositor)
+  CD3D11_RENDER_TARGET_VIEW_DESC rtvDesc(D3D11_RTV_DIMENSION_TEXTURE2D, mFormatOverride);
+  D3D11_RENDER_TARGET_VIEW_DESC *desc = aFormatOverride == DXGI_FORMAT_UNKNOWN ? nullptr : &rtvDesc;
+
+  HRESULT hr = device->CreateRenderTargetView(mTexture, desc, getter_AddRefs(mRTView));
 
   if (FAILED(hr)) {
     LOGD3D11("Failed to create RenderTargetView.");
   }
 }
 
 void
 CompositingRenderTargetD3D11::BindRenderTarget(ID3D11DeviceContext* aContext)
--- a/gfx/layers/d3d11/TextureD3D11.h
+++ b/gfx/layers/d3d11/TextureD3D11.h
@@ -216,27 +216,28 @@ protected:
 
 /**
  * TextureSource that provides with the necessary APIs to be composited by a
  * CompositorD3D11.
  */
 class TextureSourceD3D11
 {
 public:
-  TextureSourceD3D11() {}
+  TextureSourceD3D11() : mFormatOverride(DXGI_FORMAT_UNKNOWN) {}
   virtual ~TextureSourceD3D11() {}
 
   virtual ID3D11Texture2D* GetD3D11Texture() const { return mTexture; }
   virtual ID3D11ShaderResourceView* GetShaderResourceView();
 protected:
   virtual gfx::IntSize GetSize() const { return mSize; }
 
   gfx::IntSize mSize;
   RefPtr<ID3D11Texture2D> mTexture;
   RefPtr<ID3D11ShaderResourceView> mSRV;
+  DXGI_FORMAT mFormatOverride;
 };
 
 /**
  * A TextureSource that implements the DataTextureSource interface.
  * it can be used without a TextureHost and is able to upload texture data
  * from a gfx::DataSourceSurface.
  */
 class DataTextureSourceD3D11 : public DataTextureSource
@@ -399,17 +400,18 @@ protected:
   bool mIsLocked;
 };
 
 class CompositingRenderTargetD3D11 : public CompositingRenderTarget,
                                      public TextureSourceD3D11
 {
 public:
   CompositingRenderTargetD3D11(ID3D11Texture2D* aTexture,
-                               const gfx::IntPoint& aOrigin);
+                               const gfx::IntPoint& aOrigin,
+                               DXGI_FORMAT aFormatOverride = DXGI_FORMAT_UNKNOWN);
 
   virtual TextureSourceD3D11* AsSourceD3D11() override { return this; }
 
   void BindRenderTarget(ID3D11DeviceContext* aContext);
 
   virtual gfx::IntSize GetSize() const override;
 
   void SetSize(const gfx::IntSize& aSize) { mSize = aSize; }
--- a/gfx/layers/ipc/CompositorParent.cpp
+++ b/gfx/layers/ipc/CompositorParent.cpp
@@ -737,28 +737,39 @@ CompositorParent::RecvResume()
   return true;
 }
 
 bool
 CompositorParent::RecvMakeSnapshot(const SurfaceDescriptor& aInSnapshot,
                                    const gfx::IntRect& aRect)
 {
   RefPtr<DrawTarget> target = GetDrawTargetForDescriptor(aInSnapshot, gfx::BackendType::CAIRO);
+  MOZ_ASSERT(target);
+  if (!target) {
+    // We kill the content process rather than have it continue with an invalid
+    // snapshot, that may be too harsh and we could decide to return some sort
+    // of error to the child process and let it deal with it...
+    return false;
+  }
   ForceComposeToTarget(target, &aRect);
   return true;
 }
 
 bool
 CompositorParent::RecvMakeWidgetSnapshot(const SurfaceDescriptor& aInSnapshot)
 {
   if (!mCompositor || !mCompositor->GetWidget()) {
     return false;
   }
 
   RefPtr<DrawTarget> target = GetDrawTargetForDescriptor(aInSnapshot, gfx::BackendType::CAIRO);
+  MOZ_ASSERT(target);
+  if (!target) {
+    return false;
+  }
   mCompositor->GetWidget()->CaptureWidgetOnScreen(target);
   return true;
 }
 
 bool
 CompositorParent::RecvFlushRendering()
 {
   if (mCompositorScheduler->NeedsComposite())
--- a/gfx/skia/skia/include/ports/SkTypeface_win.h
+++ b/gfx/skia/skia/include/ports/SkTypeface_win.h
@@ -4,16 +4,17 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
 
 #ifndef SkTypeface_win_DEFINED
 #define SkTypeface_win_DEFINED
 
 #include "SkTypeface.h"
+#include <dwrite.h>
 
 #ifdef SK_BUILD_FOR_WIN
 
 /**
  *  Like the other Typeface create methods, this returns a new reference to the
  *  corresponding typeface for the specified logfont. The caller is responsible
  *  for calling unref() when it is finished.
  */
@@ -36,16 +37,26 @@ SK_API void SkLOGFONTFromTypeface(const 
 SK_API void SkTypeface_SetEnsureLOGFONTAccessibleProc(void (*)(const LOGFONT&));
 
 // Experimental!
 //
 class SkFontMgr;
 class SkRemotableFontMgr;
 struct IDWriteFactory;
 
+/**
+ *  Like the other Typeface create methods, this returns a new reference to the
+ *  corresponding typeface for the specified dwrite font. The caller is responsible
+ *  for calling unref() when it is finished.
+ */
+SK_API SkTypeface* SkCreateTypefaceFromDWriteFont(IDWriteFactory* aFactory,
+                                                  IDWriteFontFace* aFontFace,
+                                                  IDWriteFont* aFont,
+                                                  IDWriteFontFamily* aFontFamily);
+
 SK_API SkFontMgr* SkFontMgr_New_GDI();
 SK_API SkFontMgr* SkFontMgr_New_DirectWrite(IDWriteFactory* factory = NULL);
 
 /**
  *  Creates an SkFontMgr which renders using DirectWrite and obtains its data
  *  from the SkRemotableFontMgr.
  *
  *  If DirectWrite could not be initialized, will return NULL.
--- a/gfx/skia/skia/src/ports/SkFontHost_win.cpp
+++ b/gfx/skia/skia/src/ports/SkFontHost_win.cpp
@@ -22,16 +22,17 @@
 #include "SkOTTable_name.h"
 #include "SkOTUtils.h"
 #include "SkPath.h"
 #include "SkSFNTHeader.h"
 #include "SkStream.h"
 #include "SkString.h"
 #include "SkTemplates.h"
 #include "SkTypeface_win.h"
+#include "SkTypeface_win_dw.h"
 #include "SkTypefaceCache.h"
 #include "SkUtils.h"
 
 #include "SkTypes.h"
 #include <tchar.h>
 #include <usp10.h>
 #include <objbase.h>
 
@@ -325,16 +326,28 @@ SkTypeface* SkCreateTypefaceFromLOGFONT(
     SkTypeface* face = SkTypefaceCache::FindByProcAndRef(FindByLogFont, &lf);
     if (nullptr == face) {
         face = LogFontTypeface::Create(lf);
         SkTypefaceCache::Add(face, get_style(lf));
     }
     return face;
 }
 
+/***
+ * This guy is public.
+ */
+
+SkTypeface* SkCreateTypefaceFromDWriteFont(IDWriteFactory* aFactory,
+                                           IDWriteFontFace* aFontFace,
+                                           IDWriteFont* aFont,
+                                           IDWriteFontFamily* aFontFamily)
+{
+  return DWriteFontTypeface::Create(aFactory, aFontFace, aFont, aFontFamily);
+}
+
 /**
  *  The created SkTypeface takes ownership of fontMemResource.
  */
 SkTypeface* SkCreateFontMemResourceTypefaceFromLOGFONT(const LOGFONT& origLF, HANDLE fontMemResource) {
     LOGFONT lf = origLF;
     make_canonical(&lf);
     // We'll never get a cache hit, so no point in putting this in SkTypefaceCache.
     return FontMemResourceTypeface::Create(lf, fontMemResource);
--- a/gfx/thebes/gfxContext.cpp
+++ b/gfx/thebes/gfxContext.cpp
@@ -13,16 +13,17 @@
 #include "gfxContext.h"
 
 #include "gfxMatrix.h"
 #include "gfxUtils.h"
 #include "gfxASurface.h"
 #include "gfxPattern.h"
 #include "gfxPlatform.h"
 #include "gfxTeeSurface.h"
+#include "gfxPrefs.h"
 #include "GeckoProfiler.h"
 #include "gfx2DGlue.h"
 #include "mozilla/gfx/PathHelpers.h"
 #include "mozilla/gfx/DrawTargetTiled.h"
 #include <algorithm>
 
 #if XP_WIN
 #include "gfxWindowsPlatform.h"
@@ -106,24 +107,29 @@ gfxContext::~gfxContext()
   mDT->Flush();
   MOZ_COUNT_DTOR(gfxContext);
 }
 
 already_AddRefed<gfxASurface>
 gfxContext::CurrentSurface(gfxFloat *dx, gfxFloat *dy)
 {
   if (mDT->GetBackendType() == BackendType::CAIRO) {
-    cairo_surface_t *s =
-    (cairo_surface_t*)mDT->GetNativeSurface(NativeSurfaceType::CAIRO_SURFACE);
-    if (s) {
-      if (dx && dy) {
-        *dx = -CurrentState().deviceOffset.x;
-        *dy = -CurrentState().deviceOffset.y;
+    cairo_t* ctx = static_cast<cairo_t*>
+      (mDT->GetNativeSurface(NativeSurfaceType::CAIRO_CONTEXT));
+    if (ctx) {
+      cairo_surface_t* s = cairo_get_group_target(ctx);
+      if (s) {
+        if (dx && dy) {
+          double sdx, sdy;
+          cairo_surface_get_device_offset(s, &sdx, &sdy);
+          *dx = -CurrentState().deviceOffset.x + sdx;
+          *dy = -CurrentState().deviceOffset.y + sdy;
+        }
+        return gfxASurface::Wrap(s);
       }
-      return gfxASurface::Wrap(s);
     }
   }
 
   if (dx && dy) {
     *dx = *dy = 0;
   }
   // An Azure context doesn't have a surface backing it.
   return nullptr;
@@ -802,29 +808,34 @@ gfxContext::Paint(gfxFloat alpha)
 
   mDT->FillRect(paintRect, PatternFromState(this),
                 DrawOptions(Float(alpha), GetOp()));
 }
 
 void
 gfxContext::PushGroupForBlendBack(gfxContentType content, Float aOpacity, SourceSurface* aMask, const Matrix& aMaskTransform)
 {
-  DrawTarget* oldDT = mDT;
+  if (gfxPrefs::UseNativePushLayer()) {
+    Save();
+    mDT->PushLayer(content == gfxContentType::COLOR, aOpacity, aMask, aMaskTransform);
+  } else {
+    DrawTarget* oldDT = mDT;
+
+    PushNewDT(content);
 
-  PushNewDT(content);
+    if (oldDT != mDT) {
+      PushClipsToDT(mDT);
+    }
+    mDT->SetTransform(GetDTTransform());
 
-  if (oldDT != mDT) {
-    PushClipsToDT(mDT);
+    CurrentState().mBlendOpacity = aOpacity;
+    CurrentState().mBlendMask = aMask;
+    CurrentState().mWasPushedForBlendBack = true;
+    CurrentState().mBlendMaskTransform = aMaskTransform;
   }
-  mDT->SetTransform(GetDTTransform());
-
-  CurrentState().mBlendOpacity = aOpacity;
-  CurrentState().mBlendMask = aMask;
-  CurrentState().mWasPushedForBlendBack = true;
-  CurrentState().mBlendMaskTransform = aMaskTransform;
 }
 
 static gfxRect
 GetRoundOutDeviceClipExtents(gfxContext* aCtx)
 {
   gfxContextMatrixAutoSaveRestore save(aCtx);
   aCtx->SetMatrix(gfxMatrix());
   gfxRect r = aCtx->GetClipExtents();
@@ -835,121 +846,138 @@ GetRoundOutDeviceClipExtents(gfxContext*
 void
 gfxContext::PushGroupAndCopyBackground(gfxContentType content, Float aOpacity, SourceSurface* aMask, const Matrix& aMaskTransform)
 {
   IntRect clipExtents;
   if (mDT->GetFormat() != SurfaceFormat::B8G8R8X8) {
     gfxRect clipRect = GetRoundOutDeviceClipExtents(this);
     clipExtents = IntRect(clipRect.x, clipRect.y, clipRect.width, clipRect.height);
   }
-  if ((mDT->GetFormat() == SurfaceFormat::B8G8R8X8 ||
-       mDT->GetOpaqueRect().Contains(clipExtents)) &&
-      !mDT->GetUserData(&sDontUseAsSourceKey)) {
-    DrawTarget *oldDT = mDT;
-    RefPtr<SourceSurface> source = mDT->Snapshot();
-    Point oldDeviceOffset = CurrentState().deviceOffset;
+  bool pushOpaqueWithCopiedBG = (mDT->GetFormat() == SurfaceFormat::B8G8R8X8 ||
+                                 mDT->GetOpaqueRect().Contains(clipExtents)) &&
+                                !mDT->GetUserData(&sDontUseAsSourceKey);
+
+  if (gfxPrefs::UseNativePushLayer()) {
+    Save();
+
+    if (pushOpaqueWithCopiedBG) {
+      mDT->PushLayer(true, aOpacity, aMask, aMaskTransform, IntRect(), true);
+    } else {
+      mDT->PushLayer(content == gfxContentType::COLOR, aOpacity, aMask, aMaskTransform, IntRect(), false);
+    }
+  } else {
+    if (pushOpaqueWithCopiedBG) {
+      DrawTarget *oldDT = mDT;
+      RefPtr<SourceSurface> source = mDT->Snapshot();
+      Point oldDeviceOffset = CurrentState().deviceOffset;
+
+      PushNewDT(gfxContentType::COLOR);
+
+      if (oldDT == mDT) {
+        // Creating new DT failed.
+        return;
+      }
+
+      CurrentState().mBlendOpacity = aOpacity;
+      CurrentState().mBlendMask = aMask;
+      CurrentState().mWasPushedForBlendBack = true;
+      CurrentState().mBlendMaskTransform = aMaskTransform;
 
-    PushNewDT(gfxContentType::COLOR);
+      Point offset = CurrentState().deviceOffset - oldDeviceOffset;
+      Rect surfRect(0, 0, Float(mDT->GetSize().width), Float(mDT->GetSize().height));
+      Rect sourceRect = surfRect + offset;
+
+      mDT->SetTransform(Matrix());
 
-    if (oldDT == mDT) {
-      // Creating new DT failed.
+      // XXX: It's really sad that we have to do this (for performance).
+      // Once DrawTarget gets a PushLayer API we can implement this within
+      // DrawTargetTiled.
+      if (source->GetType() == SurfaceType::TILED) {
+        SnapshotTiled *sourceTiled = static_cast<SnapshotTiled*>(source.get());
+        for (uint32_t i = 0; i < sourceTiled->mSnapshots.size(); i++) {
+          Rect tileSourceRect = sourceRect.Intersect(Rect(sourceTiled->mOrigins[i].x,
+                                                          sourceTiled->mOrigins[i].y,
+                                                          sourceTiled->mSnapshots[i]->GetSize().width,
+                                                          sourceTiled->mSnapshots[i]->GetSize().height));
+
+          if (tileSourceRect.IsEmpty()) {
+            continue;
+          }
+          Rect tileDestRect = tileSourceRect - offset;
+          tileSourceRect -= sourceTiled->mOrigins[i];
+
+          mDT->DrawSurface(sourceTiled->mSnapshots[i], tileDestRect, tileSourceRect);
+        }
+      } else {
+        mDT->DrawSurface(source, surfRect, sourceRect);
+      }
+      mDT->SetOpaqueRect(oldDT->GetOpaqueRect());
+
+      PushClipsToDT(mDT);
+      mDT->SetTransform(GetDTTransform());
       return;
     }
+    DrawTarget* oldDT = mDT;
 
+    PushNewDT(content);
+
+    if (oldDT != mDT) {
+      PushClipsToDT(mDT);
+    }
+
+    mDT->SetTransform(GetDTTransform());
     CurrentState().mBlendOpacity = aOpacity;
     CurrentState().mBlendMask = aMask;
     CurrentState().mWasPushedForBlendBack = true;
     CurrentState().mBlendMaskTransform = aMaskTransform;
-
-    Point offset = CurrentState().deviceOffset - oldDeviceOffset;
-    Rect surfRect(0, 0, Float(mDT->GetSize().width), Float(mDT->GetSize().height));
-    Rect sourceRect = surfRect + offset;
-
-    mDT->SetTransform(Matrix());
-
-    // XXX: It's really sad that we have to do this (for performance).
-    // Once DrawTarget gets a PushLayer API we can implement this within
-    // DrawTargetTiled.
-    if (source->GetType() == SurfaceType::TILED) {
-      SnapshotTiled *sourceTiled = static_cast<SnapshotTiled*>(source.get());
-      for (uint32_t i = 0; i < sourceTiled->mSnapshots.size(); i++) {
-        Rect tileSourceRect = sourceRect.Intersect(Rect(sourceTiled->mOrigins[i].x,
-                                                        sourceTiled->mOrigins[i].y,
-                                                        sourceTiled->mSnapshots[i]->GetSize().width,
-                                                        sourceTiled->mSnapshots[i]->GetSize().height));
-
-        if (tileSourceRect.IsEmpty()) {
-          continue;
-        }
-        Rect tileDestRect = tileSourceRect - offset;
-        tileSourceRect -= sourceTiled->mOrigins[i];
-
-        mDT->DrawSurface(sourceTiled->mSnapshots[i], tileDestRect, tileSourceRect);
-      }
-    } else {
-      mDT->DrawSurface(source, surfRect, sourceRect);
-    }
-    mDT->SetOpaqueRect(oldDT->GetOpaqueRect());
-
-    PushClipsToDT(mDT);
-    mDT->SetTransform(GetDTTransform());
-    return;
   }
-  DrawTarget* oldDT = mDT;
-
-  PushNewDT(content);
-
-  if (oldDT != mDT) {
-    PushClipsToDT(mDT);
-  }
-
-  mDT->SetTransform(GetDTTransform());
-  CurrentState().mBlendOpacity = aOpacity;
-  CurrentState().mBlendMask = aMask;
-  CurrentState().mWasPushedForBlendBack = true;
-  CurrentState().mBlendMaskTransform = aMaskTransform;
 }
 
 void
 gfxContext::PopGroupAndBlend()
 {
-  MOZ_ASSERT(CurrentState().mWasPushedForBlendBack);
-  Float opacity = CurrentState().mBlendOpacity;
-  RefPtr<SourceSurface> mask = CurrentState().mBlendMask;
-  Matrix maskTransform = CurrentState().mBlendMaskTransform;
+  if (gfxPrefs::UseNativePushLayer()) {
+    mDT->PopLayer();
+    Restore();
+  } else {
+    MOZ_ASSERT(CurrentState().mWasPushedForBlendBack);
+    Float opacity = CurrentState().mBlendOpacity;
+    RefPtr<SourceSurface> mask = CurrentState().mBlendMask;
+    Matrix maskTransform = CurrentState().mBlendMaskTransform;
 
-  RefPtr<SourceSurface> src = mDT->Snapshot();
-  Point deviceOffset = CurrentState().deviceOffset;
-  Restore();
-  CurrentState().sourceSurfCairo = nullptr;
-  CurrentState().sourceSurface = src;
-  CurrentState().sourceSurfaceDeviceOffset = deviceOffset;
-  CurrentState().pattern = nullptr;
-  CurrentState().patternTransformChanged = false;
+    RefPtr<SourceSurface> src = mDT->Snapshot();
+    Point deviceOffset = CurrentState().deviceOffset;
+    Restore();
+    CurrentState().sourceSurfCairo = nullptr;
+    CurrentState().sourceSurface = src;
+    CurrentState().sourceSurfaceDeviceOffset = deviceOffset;
+    CurrentState().pattern = nullptr;
+    CurrentState().patternTransformChanged = false;
 
-  Matrix mat = mTransform;
-  mat.Invert();
-  mat.PreTranslate(deviceOffset.x, deviceOffset.y); // device offset translation
+    Matrix mat = mTransform;
+    mat.Invert();
+    mat.PreTranslate(deviceOffset.x, deviceOffset.y); // device offset translation
 
-  CurrentState().surfTransform = mat;
+    CurrentState().surfTransform = mat;
 
-  CompositionOp oldOp = GetOp();
-  SetOp(CompositionOp::OP_OVER);
+    CompositionOp oldOp = GetOp();
+    SetOp(CompositionOp::OP_OVER);
 
-  if (mask) {
-    if (!maskTransform.HasNonTranslation()) {
-      Mask(mask, opacity, Point(maskTransform._31, maskTransform._32));
+    if (mask) {
+      if (!maskTransform.HasNonTranslation()) {
+        Mask(mask, opacity, Point(maskTransform._31, maskTransform._32));
+      } else {
+        Mask(mask, opacity, maskTransform);
+      }
     } else {
-      Mask(mask, opacity, maskTransform);
+      Paint(opacity);
     }
-  } else {
-    Paint(opacity);
+
+    SetOp(oldOp);
   }
-
-  SetOp(oldOp);
 }
 
 #ifdef MOZ_DUMP_PAINTING
 void
 gfxContext::WriteAsPNG(const char* aFile)
 {
   gfxUtils::WriteAsPNG(mDT, aFile);
 }
--- a/gfx/thebes/gfxDWriteFontList.cpp
+++ b/gfx/thebes/gfxDWriteFontList.cpp
@@ -337,16 +337,34 @@ gfxDWriteFontFamily::AddSizeOfExcludingT
 void
 gfxDWriteFontFamily::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
                                             FontListSizes* aSizes) const
 {
     aSizes->mFontListSize += aMallocSizeOf(this);
     AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
 }
 
+already_AddRefed<IDWriteFont>
+gfxDWriteFontFamily::GetDefaultFont()
+{
+  RefPtr<IDWriteFont> font;
+  for (UINT32 i = 0; i < mDWFamily->GetFontCount(); i++) {
+    HRESULT hr = mDWFamily->GetFont(i, getter_AddRefs(font));
+    if (FAILED(hr)) {
+      NS_WARNING("Failed to get default font from existing family");
+      continue;
+    }
+
+    return font.forget();
+  }
+
+  NS_WARNING("No available DWrite fonts. Returning null");
+  return nullptr;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // gfxDWriteFontEntry
 
 gfxDWriteFontEntry::~gfxDWriteFontEntry()
 {
 }
 
 bool
--- a/gfx/thebes/gfxDWriteFontList.h
+++ b/gfx/thebes/gfxDWriteFontList.h
@@ -52,16 +52,17 @@ public:
 
     void SetForceGDIClassic(bool aForce) { mForceGDIClassic = aForce; }
 
     virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                                         FontListSizes* aSizes) const;
     virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                                         FontListSizes* aSizes) const;
 
+    already_AddRefed<IDWriteFont> GetDefaultFont();
 protected:
     /** This font family's directwrite fontfamily object */
     RefPtr<IDWriteFontFamily> mDWFamily;
     bool mForceGDIClassic;
 };
 
 /**
  * \brief Class representing DirectWrite FontEntry (a unique font style/family)
@@ -160,16 +161,20 @@ public:
     void SetForceGDIClassic(bool aForce) { mForceGDIClassic = aForce; }
     bool GetForceGDIClassic() { return mForceGDIClassic; }
 
     virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                                         FontListSizes* aSizes) const;
     virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                                         FontListSizes* aSizes) const;
 
+    IDWriteFont* GetFont() {
+      return mFont;
+    }
+
 protected:
     friend class gfxDWriteFont;
     friend class gfxDWriteFontList;
 
     virtual nsresult CopyFontTable(uint32_t aTableTag,
                                    FallibleTArray<uint8_t>& aBuffer) override;
 
     virtual gfxFont *CreateFontInstance(const gfxFontStyle *aFontStyle,
--- a/gfx/thebes/gfxDWriteFonts.cpp
+++ b/gfx/thebes/gfxDWriteFonts.cpp
@@ -77,17 +77,17 @@ gfxDWriteFont::gfxDWriteFont(gfxFontEntr
     , mMetrics(nullptr)
     , mSpaceGlyph(0)
     , mNeedsOblique(false)
     , mNeedsBold(aNeedsBold)
     , mUseSubpixelPositions(false)
     , mAllowManualShowGlyphs(true)
 {
     gfxDWriteFontEntry *fe =
-        static_cast<gfxDWriteFontEntry*>(aFontEntry);
+	        static_cast<gfxDWriteFontEntry*>(aFontEntry);
     nsresult rv;
     DWRITE_FONT_SIMULATIONS sims = DWRITE_FONT_SIMULATIONS_NONE;
     if ((GetStyle()->style != NS_FONT_STYLE_NORMAL) &&
         fe->IsUpright() &&
         GetStyle()->allowSyntheticStyle) {
             // For this we always use the font_matrix for uniformity. Not the
             // DWrite simulation.
             mNeedsOblique = true;
@@ -98,16 +98,31 @@ gfxDWriteFont::gfxDWriteFont(gfxFontEntr
 
     rv = fe->CreateFontFace(getter_AddRefs(mFontFace), sims);
 
     if (NS_FAILED(rv)) {
         mIsValid = false;
         return;
     }
 
+    mFont = fe->GetFont();
+    if (!mFont) {
+        gfxPlatformFontList* fontList = gfxPlatformFontList::PlatformFontList();
+        gfxDWriteFontFamily* defaultFontFamily =
+            static_cast<gfxDWriteFontFamily*>(fontList->GetDefaultFont(aFontStyle));
+
+        mFont = defaultFontFamily->GetDefaultFont();
+        NS_WARNING("Using default font");
+    }
+
+    HRESULT hr = mFont->GetFontFamily(getter_AddRefs(mFontFamily));
+    if (FAILED(hr)) {
+        MOZ_ASSERT(false);
+    }
+
     ComputeMetrics(anAAOption);
 }
 
 gfxDWriteFont::~gfxDWriteFont()
 {
     if (mCairoFontFace) {
         cairo_font_face_destroy(mCairoFontFace);
     }
@@ -679,16 +694,20 @@ gfxDWriteFont::GetScaledFont(mozilla::gf
   NativeFont nativeFont;
   nativeFont.mType = NativeFontType::DWRITE_FONT_FACE;
   nativeFont.mFont = GetFontFace();
 
   if (wantCairo) {
     mAzureScaledFont = Factory::CreateScaledFontWithCairo(nativeFont,
                                                         GetAdjustedSize(),
                                                         GetCairoScaledFont());
+  } else if (aTarget->GetBackendType() == BackendType::SKIA) {
+    mAzureScaledFont =
+            Factory::CreateScaledFontForDWriteFont(mFont, mFontFamily,
+                                                   mFontFace, GetAdjustedSize());
   } else {
     mAzureScaledFont = Factory::CreateScaledFontForNativeFont(nativeFont,
                                                             GetAdjustedSize());
   }
 
   mAzureScaledFontIsCairo = wantCairo;
 
   RefPtr<ScaledFont> scaledFont(mAzureScaledFont);
--- a/gfx/thebes/gfxDWriteFonts.h
+++ b/gfx/thebes/gfxDWriteFonts.h
@@ -86,16 +86,18 @@ protected:
     cairo_font_face_t *CairoFontFace();
 
     gfxFloat MeasureGlyphWidth(uint16_t aGlyph);
 
     DWRITE_MEASURING_MODE GetMeasuringMode();
     bool GetForceGDIClassic();
 
     RefPtr<IDWriteFontFace> mFontFace;
+    RefPtr<IDWriteFont> mFont;
+    RefPtr<IDWriteFontFamily> mFontFamily;
     cairo_font_face_t *mCairoFontFace;
 
     Metrics *mMetrics;
 
     // cache of glyph widths in 16.16 fixed-point pixels
     nsAutoPtr<nsDataHashtable<nsUint32HashKey,int32_t> > mGlyphWidths;
 
     uint32_t mSpaceGlyph;
--- a/gfx/thebes/gfxFontConstants.h
+++ b/gfx/thebes/gfxFontConstants.h
@@ -36,16 +36,22 @@
 
 #define NS_FONT_KERNING_AUTO                        0
 #define NS_FONT_KERNING_NONE                        1
 #define NS_FONT_KERNING_NORMAL                      2
 
 #define NS_FONT_SYNTHESIS_WEIGHT                    0x1
 #define NS_FONT_SYNTHESIS_STYLE                     0x2
 
+#define NS_FONT_DISPLAY_AUTO            0
+#define NS_FONT_DISPLAY_BLOCK           1
+#define NS_FONT_DISPLAY_SWAP            2
+#define NS_FONT_DISPLAY_FALLBACK        3
+#define NS_FONT_DISPLAY_OPTIONAL        4
+
 enum {
     eFeatureAlternates_historical,
     eFeatureAlternates_stylistic,
     eFeatureAlternates_styleset,
     eFeatureAlternates_character_variant,
     eFeatureAlternates_swash,
     eFeatureAlternates_ornaments,
     eFeatureAlternates_annotation,
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -895,17 +895,17 @@ gfxPlatform::GetSourceSurfaceForSurface(
     // If we're going to be used with a CAIRO DrawTarget, then just create a
     // SourceSurfaceCairo since we don't know the underlying type of the CAIRO
     // DrawTarget and can't pick a better surface type. Doing this also avoids
     // readback of aSurface's surface into memory if, for example, aSurface
     // wraps an xlib cairo surface (which can be important to avoid a major
     // slowdown).
     NativeSurface surf;
     surf.mFormat = format;
-    surf.mType = NativeSurfaceType::CAIRO_SURFACE;
+    surf.mType = NativeSurfaceType::CAIRO_CONTEXT;
     surf.mSurface = aSurface->CairoSurface();
     surf.mSize = aSurface->GetSize();
     // We return here regardless of whether CreateSourceSurfaceFromNativeSurface
     // succeeds or not since we don't expect to be able to do any better below
     // if it fails.
     //
     // Note that the returned SourceSurfaceCairo holds a strong reference to
     // the cairo_surface_t* that it wraps, which essencially means it holds a
@@ -953,17 +953,17 @@ gfxPlatform::GetSourceSurfaceForSurface(
     // using a temporary DrawTargetCairo and then optimizing it to aTarget's
     // actual type. The CreateSourceSurfaceFromNativeSurface() call will
     // likely create a DataSourceSurface (possibly involving copying and/or
     // readback), and the OptimizeSourceSurface may well copy again and upload
     // to the GPU. So, while this code path is rarely hit, hitting it may be
     // very slow.
     NativeSurface surf;
     surf.mFormat = format;
-    surf.mType = NativeSurfaceType::CAIRO_SURFACE;
+    surf.mType = NativeSurfaceType::CAIRO_CONTEXT;
     surf.mSurface = aSurface->CairoSurface();
     surf.mSize = aSurface->GetSize();
     RefPtr<DrawTarget> drawTarget =
       Factory::CreateDrawTarget(BackendType::CAIRO, IntSize(1, 1), format);
     if (!drawTarget) {
       gfxWarning() << "gfxPlatform::GetSourceSurfaceForSurface failed in CreateDrawTarget";
       return nullptr;
     }
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -252,16 +252,18 @@ private:
   // The maximums here are quite conservative, we can tighten them if problems show up.
   DECL_GFX_PREF(Once, "gfx.max-alloc-size",                    MaxAllocSize, int32_t, (int32_t)500000000);
   DECL_GFX_PREF(Once, "gfx.max-texture-size",                  MaxTextureSize, int32_t, (int32_t)32767);
   DECL_GFX_PREF(Live, "gfx.perf-warnings.enabled",             PerfWarnings, bool, false);
   DECL_GFX_PREF(Live, "gfx.SurfaceTexture.detach.enabled",     SurfaceTextureDetachEnabled, bool, true);
   DECL_GFX_PREF(Live, "gfx.testing.device-reset",              DeviceResetForTesting, int32_t, 0);
   DECL_GFX_PREF(Live, "gfx.testing.device-fail",               DeviceFailForTesting, bool, false);
 
+  DECL_GFX_PREF(Live, "gfx.content.use-native-pushlayer",      UseNativePushLayer, bool, false);
+
   // These times should be in milliseconds
   DECL_GFX_PREF(Once, "gfx.touch.resample.delay-threshold",    TouchResampleVsyncDelayThreshold, int32_t, 20);
   DECL_GFX_PREF(Once, "gfx.touch.resample.max-predict",        TouchResampleMaxPredict, int32_t, 8);
   DECL_GFX_PREF(Once, "gfx.touch.resample.min-delta",          TouchResampleMinDelta, int32_t, 2);
   DECL_GFX_PREF(Once, "gfx.touch.resample.old-touch-threshold",TouchResampleOldTouchThreshold, int32_t, 17);
   DECL_GFX_PREF(Once, "gfx.touch.resample.vsync-adjust",       TouchVsyncSampleAdjust, int32_t, 5);
 
   DECL_GFX_PREF(Once, "gfx.vr.mirror-textures",                VRMirrorTextures, bool, false);
--- a/gfx/thebes/gfxUserFontSet.cpp
+++ b/gfx/thebes/gfxUserFontSet.cpp
@@ -106,21 +106,23 @@ private:
 
 gfxUserFontEntry::gfxUserFontEntry(gfxUserFontSet* aFontSet,
              const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
              uint32_t aWeight,
              int32_t aStretch,
              uint8_t aStyle,
              const nsTArray<gfxFontFeature>& aFeatureSettings,
              uint32_t aLanguageOverride,
-             gfxSparseBitSet* aUnicodeRanges)
+             gfxSparseBitSet* aUnicodeRanges,
+             uint8_t aFontDisplay)
     : gfxFontEntry(NS_LITERAL_STRING("userfont")),
       mUserFontLoadState(STATUS_NOT_LOADED),
       mFontDataLoadingState(NOT_LOADING),
       mUnsupportedFormat(false),
+      mFontDisplay(aFontDisplay),
       mLoader(nullptr),
       mFontSet(aFontSet)
 {
     MOZ_ASSERT(aWeight != 0,
                "aWeight must not be 0; use NS_FONT_WEIGHT_NORMAL instead");
     mIsUserFontContainer = true;
     mSrcList = aFontFaceSrcList;
     mSrcIndex = 0;
@@ -141,24 +143,26 @@ gfxUserFontEntry::~gfxUserFontEntry()
 
 bool
 gfxUserFontEntry::Matches(const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
                           uint32_t aWeight,
                           int32_t aStretch,
                           uint8_t aStyle,
                           const nsTArray<gfxFontFeature>& aFeatureSettings,
                           uint32_t aLanguageOverride,
-                          gfxSparseBitSet* aUnicodeRanges)
+                          gfxSparseBitSet* aUnicodeRanges,
+                          uint8_t aFontDisplay)
 {
     return mWeight == aWeight &&
            mStretch == aStretch &&
            mStyle == aStyle &&
            mFeatureSettings == aFeatureSettings &&
            mLanguageOverride == aLanguageOverride &&
            mSrcList == aFontFaceSrcList &&
+           mFontDisplay == aFontDisplay &&
            ((!aUnicodeRanges && !mCharacterMap) ||
             (aUnicodeRanges && mCharacterMap && mCharacterMap->Equals(aUnicodeRanges)));
 }
 
 gfxFont*
 gfxUserFontEntry::CreateFontInstance(const gfxFontStyle* aFontStyle, bool aNeedsBold)
 {
     NS_NOTREACHED("should only be creating a gfxFont"
@@ -722,38 +726,43 @@ gfxUserFontEntry::FontDataDownloadComple
                                            uint32_t aLength,
                                            nsresult aDownloadStatus)
 {
     // forget about the loader, as we no longer potentially need to cancel it
     // if the entry is obsoleted
     mLoader = nullptr;
 
     // download successful, make platform font using font data
-    if (NS_SUCCEEDED(aDownloadStatus)) {
+    if (NS_SUCCEEDED(aDownloadStatus) &&
+        mFontDataLoadingState != LOADING_TIMED_OUT) {
         bool loaded = LoadPlatformFont(aFontData, aLength);
         aFontData = nullptr;
 
         if (loaded) {
             IncrementGeneration();
             return true;
         }
 
     } else {
         // download failed
         mFontSet->LogMessage(this,
-                             "download failed", nsIScriptError::errorFlag,
+                             (mFontDataLoadingState != LOADING_TIMED_OUT ?
+                              "download failed" : "download timed out"),
+                             nsIScriptError::errorFlag,
                              aDownloadStatus);
     }
 
     if (aFontData) {
         free((void*)aFontData);
     }
 
-    // error occurred, load next src
-    LoadNextSrc();
+    // error occurred, load next src if load not yet timed out
+    if (mFontDataLoadingState != LOADING_TIMED_OUT) {
+      LoadNextSrc();
+    }
 
     // We ignore the status returned by LoadNext();
     // even if loading failed, we need to bump the font-set generation
     // and return true in order to trigger reflow, so that fallback
     // will be used where the text was "masked" by the pending download
     IncrementGeneration();
     return true;
 }
@@ -790,89 +799,93 @@ already_AddRefed<gfxUserFontEntry>
 gfxUserFontSet::FindOrCreateUserFontEntry(
                                const nsAString& aFamilyName,
                                const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
                                uint32_t aWeight,
                                int32_t aStretch,
                                uint8_t aStyle,
                                const nsTArray<gfxFontFeature>& aFeatureSettings,
                                uint32_t aLanguageOverride,
-                               gfxSparseBitSet* aUnicodeRanges)
+                               gfxSparseBitSet* aUnicodeRanges,
+                               uint8_t aFontDisplay)
 {
     RefPtr<gfxUserFontEntry> entry;
 
     // If there's already a userfont entry in the family whose descriptors all match,
     // we can just move it to the end of the list instead of adding a new
     // face that will always "shadow" the old one.
     // Note that we can't do this for platform font entries, even if the
     // style descriptors match, as they might have had a different source list,
     // but we no longer have the old source list available to check.
     gfxUserFontFamily* family = LookupFamily(aFamilyName);
     if (family) {
         entry = FindExistingUserFontEntry(family, aFontFaceSrcList, aWeight,
                                           aStretch, aStyle,
                                           aFeatureSettings, aLanguageOverride,
-                                          aUnicodeRanges);
+                                          aUnicodeRanges, aFontDisplay);
     }
 
     if (!entry) {
       entry = CreateUserFontEntry(aFontFaceSrcList, aWeight, aStretch,
                                   aStyle, aFeatureSettings,
-                                  aLanguageOverride, aUnicodeRanges);
+                                  aLanguageOverride, aUnicodeRanges,
+                                  aFontDisplay);
       entry->mFamilyName = aFamilyName;
     }
 
     return entry.forget();
 }
 
 already_AddRefed<gfxUserFontEntry>
 gfxUserFontSet::CreateUserFontEntry(
                                const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
                                uint32_t aWeight,
                                int32_t aStretch,
                                uint8_t aStyle,
                                const nsTArray<gfxFontFeature>& aFeatureSettings,
                                uint32_t aLanguageOverride,
-                               gfxSparseBitSet* aUnicodeRanges)
+                               gfxSparseBitSet* aUnicodeRanges,
+                               uint8_t aFontDisplay)
 {
 
     RefPtr<gfxUserFontEntry> userFontEntry =
         new gfxUserFontEntry(this, aFontFaceSrcList, aWeight,
                               aStretch, aStyle, aFeatureSettings,
-                              aLanguageOverride, aUnicodeRanges);
+                              aLanguageOverride, aUnicodeRanges, aFontDisplay);
     return userFontEntry.forget();
 }
 
 gfxUserFontEntry*
 gfxUserFontSet::FindExistingUserFontEntry(
                                gfxUserFontFamily* aFamily,
                                const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
                                uint32_t aWeight,
                                int32_t aStretch,
                                uint8_t aStyle,
                                const nsTArray<gfxFontFeature>& aFeatureSettings,
                                uint32_t aLanguageOverride,
-                               gfxSparseBitSet* aUnicodeRanges)
+                               gfxSparseBitSet* aUnicodeRanges,
+                               uint8_t aFontDisplay)
 {
     MOZ_ASSERT(aWeight != 0,
                "aWeight must not be 0; use NS_FONT_WEIGHT_NORMAL instead");
 
     nsTArray<RefPtr<gfxFontEntry>>& fontList = aFamily->GetFontList();
 
     for (size_t i = 0, count = fontList.Length(); i < count; i++) {
         if (!fontList[i]->mIsUserFontContainer) {
             continue;
         }
 
         gfxUserFontEntry* existingUserFontEntry =
             static_cast<gfxUserFontEntry*>(fontList[i].get());
         if (!existingUserFontEntry->Matches(aFontFaceSrcList,
                                             aWeight, aStretch, aStyle,
                                             aFeatureSettings, aLanguageOverride,
-                                            aUnicodeRanges)) {
+                                            aUnicodeRanges, aFontDisplay)) {
             continue;
         }
 
         return existingUserFontEntry;
     }
 
     return nullptr;
 }
@@ -881,21 +894,22 @@ void
 gfxUserFontSet::AddUserFontEntry(const nsAString& aFamilyName,
                                  gfxUserFontEntry* aUserFontEntry)
 {
     gfxUserFontFamily* family = GetFamily(aFamilyName);
     family->AddFontEntry(aUserFontEntry);
 
     if (LOG_ENABLED()) {
         LOG(("userfonts (%p) added to \"%s\" (%p) style: %s weight: %d "
-             "stretch: %d",
+             "stretch: %d display: %d",
              this, NS_ConvertUTF16toUTF8(aFamilyName).get(), aUserFontEntry,
              (aUserFontEntry->IsItalic() ? "italic" :
               (aUserFontEntry->IsOblique() ? "oblique" : "normal")),
-             aUserFontEntry->Weight(), aUserFontEntry->Stretch()));
+             aUserFontEntry->Weight(), aUserFontEntry->Stretch(),
+             aUserFontEntry->GetFontDisplay()));
     }
 }
 
 gfxUserFontEntry*
 gfxUserFontSet::FindUserFontEntryAndLoad(gfxFontFamily* aFamily,
                                          const gfxFontStyle& aFontStyle,
                                          bool& aNeedsBold,
                                          bool& aWaitForUserFont)
--- a/gfx/thebes/gfxUserFontSet.h
+++ b/gfx/thebes/gfxUserFontSet.h
@@ -209,29 +209,31 @@ public:
     // TODO: support for unicode ranges not yet implemented
     virtual already_AddRefed<gfxUserFontEntry> CreateUserFontEntry(
                               const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
                               uint32_t aWeight,
                               int32_t aStretch,
                               uint8_t aStyle,
                               const nsTArray<gfxFontFeature>& aFeatureSettings,
                               uint32_t aLanguageOverride,
-                              gfxSparseBitSet* aUnicodeRanges) = 0;
+                              gfxSparseBitSet* aUnicodeRanges,
+                              uint8_t aFontDisplay) = 0;
 
     // creates a font face for the specified family, or returns an existing
     // matching entry on the family if there is one
     already_AddRefed<gfxUserFontEntry> FindOrCreateUserFontEntry(
                                const nsAString& aFamilyName,
                                const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
                                uint32_t aWeight,
                                int32_t aStretch,
                                uint8_t aStyle,
                                const nsTArray<gfxFontFeature>& aFeatureSettings,
                                uint32_t aLanguageOverride,
-                               gfxSparseBitSet* aUnicodeRanges);
+                               gfxSparseBitSet* aUnicodeRanges,
+                               uint8_t aFontDisplay);
 
     // add in a font face for which we have the gfxUserFontEntry already
     void AddUserFontEntry(const nsAString& aFamilyName,
                           gfxUserFontEntry* aUserFontEntry);
 
     // Whether there is a face with this family name
     bool HasFamily(const nsAString& aFamilyName) const
     {
@@ -503,17 +505,18 @@ protected:
     gfxUserFontEntry* FindExistingUserFontEntry(
                                    gfxUserFontFamily* aFamily,
                                    const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
                                    uint32_t aWeight,
                                    int32_t aStretch,
                                    uint8_t aStyle,
                                    const nsTArray<gfxFontFeature>& aFeatureSettings,
                                    uint32_t aLanguageOverride,
-                                   gfxSparseBitSet* aUnicodeRanges);
+                                   gfxSparseBitSet* aUnicodeRanges,
+                                   uint8_t aFontDisplay);
 
     // creates a new gfxUserFontFamily in mFontFamilies, or returns an existing
     // family if there is one
     gfxUserFontFamily* GetFamily(const nsAString& aFamilyName);
 
     // font families defined by @font-face rules
     nsRefPtrHashtable<nsStringHashKey, gfxUserFontFamily> mFontFamilies;
 
@@ -546,28 +549,30 @@ public:
 
     gfxUserFontEntry(gfxUserFontSet* aFontSet,
                      const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
                      uint32_t aWeight,
                      int32_t aStretch,
                      uint8_t aStyle,
                      const nsTArray<gfxFontFeature>& aFeatureSettings,
                      uint32_t aLanguageOverride,
-                     gfxSparseBitSet* aUnicodeRanges);
+                     gfxSparseBitSet* aUnicodeRanges,
+                     uint8_t aFontDisplay);
 
     virtual ~gfxUserFontEntry();
 
     // Return whether the entry matches the given list of attributes
     bool Matches(const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
                  uint32_t aWeight,
                  int32_t aStretch,
                  uint8_t aStyle,
                  const nsTArray<gfxFontFeature>& aFeatureSettings,
                  uint32_t aLanguageOverride,
-                 gfxSparseBitSet* aUnicodeRanges);
+                 gfxSparseBitSet* aUnicodeRanges,
+                 uint8_t aFontDisplay);
 
     virtual gfxFont* CreateFontInstance(const gfxFontStyle* aFontStyle,
                                         bool aNeedsBold);
 
     gfxFontEntry* GetPlatformFontEntry() const { return mPlatformFontEntry; }
 
     // is the font loading or loaded, or did it fail?
     UserFontLoadState LoadState() const { return mUserFontLoadState; }
@@ -586,16 +591,18 @@ public:
         }
         return true;
     }
 
     gfxCharacterMap* GetUnicodeRangeMap() const {
         return mCharacterMap.get();
     }
 
+    uint8_t GetFontDisplay() const { return mFontDisplay; }
+
     // load the font - starts the loading of sources which continues until
     // a valid font resource is found or all sources fail
     void Load();
 
     // methods to expose some information to FontFaceSet::UserFontSet
     // since we can't make that class a friend
     void SetLoader(nsFontFaceLoader* aLoader) { mLoader = aLoader; }
     nsFontFaceLoader* GetLoader() { return mLoader; }
@@ -656,21 +663,23 @@ protected:
     // note that code depends on the ordering of these values!
     enum FontDataLoadingState {
         NOT_LOADING = 0,     // not started to load any font resources yet
         LOADING_STARTED,     // loading has started; hide fallback font
         LOADING_ALMOST_DONE, // timeout happened but we're nearly done,
                              // so keep hiding fallback font
         LOADING_SLOWLY,      // timeout happened and we're not nearly done,
                              // so use the fallback font
+        LOADING_TIMED_OUT,   // font load took too long
         LOADING_FAILED       // failed to load any source: use fallback
     };
     FontDataLoadingState     mFontDataLoadingState;
 
     bool                     mUnsupportedFormat;
+    uint8_t                  mFontDisplay; // timing of userfont fallback
 
     RefPtr<gfxFontEntry>   mPlatformFontEntry;
     nsTArray<gfxFontFaceSrc> mSrcList;
     uint32_t                 mSrcIndex; // index of loading src item
     // This field is managed by the nsFontFaceLoader. In the destructor and Cancel()
     // methods of nsFontFaceLoader this reference is nulled out.
     nsFontFaceLoader* MOZ_NON_OWNING_REF mLoader; // current loader for this entry, if any
     gfxUserFontSet*          mFontSet; // font-set which owns this userfont entry
--- a/gfx/thebes/gfxWindowsNativeDrawing.cpp
+++ b/gfx/thebes/gfxWindowsNativeDrawing.cpp
@@ -48,19 +48,23 @@ gfxWindowsNativeDrawing::BeginNativeDraw
     if (mRenderState == RENDER_STATE_INIT) {
         RefPtr<gfxASurface> surf;
         DrawTarget* drawTarget = mContext->GetDrawTarget();
         cairo_t* cairo = nullptr;
         if (drawTarget->GetBackendType() == BackendType::CAIRO) {
             cairo = static_cast<cairo_t*>
                 (drawTarget->GetNativeSurface(NativeSurfaceType::CAIRO_CONTEXT));
             if (cairo) {
-                cairo_surface_t* s = cairo_get_target(cairo);
+                cairo_surface_t* s = cairo_get_group_target(cairo);
                 if (s) {
                     mDeviceOffset = mContext->GetDeviceOffset();
+                    double sdx, sdy;
+                    cairo_surface_get_device_offset(s, &sdx, &sdy);
+                    mDeviceOffset.x -= sdx;
+                    mDeviceOffset.y -= sdy;
                     surf = gfxASurface::Wrap(s);
                 }
             }
         }
 
         if (surf && surf->CairoStatus() != 0)
             return nullptr;
 
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -82,29 +82,30 @@ using namespace mozilla::gfx;
 using namespace mozilla::layers;
 using namespace mozilla::widget;
 using namespace mozilla::image;
 
 DCFromDrawTarget::DCFromDrawTarget(DrawTarget& aDrawTarget)
 {
   mDC = nullptr;
   if (aDrawTarget.GetBackendType() == BackendType::CAIRO) {
-    cairo_surface_t *surf = (cairo_surface_t*)
-        aDrawTarget.GetNativeSurface(NativeSurfaceType::CAIRO_SURFACE);
-    if (surf) {
-      cairo_surface_type_t surfaceType = cairo_surface_get_type(surf);
-      if (surfaceType == CAIRO_SURFACE_TYPE_WIN32 ||
-          surfaceType == CAIRO_SURFACE_TYPE_WIN32_PRINTING) {
-        mDC = cairo_win32_surface_get_dc(surf);
-        mNeedsRelease = false;
-        SaveDC(mDC);
-        cairo_t* ctx = (cairo_t*)
-            aDrawTarget.GetNativeSurface(NativeSurfaceType::CAIRO_CONTEXT);
-        cairo_scaled_font_t* scaled = cairo_get_scaled_font(ctx);
-        cairo_win32_scaled_font_select_font(scaled, mDC);
+    cairo_t* ctx = static_cast<cairo_t*>
+      (aDrawTarget.GetNativeSurface(NativeSurfaceType::CAIRO_CONTEXT));
+    if (ctx) {
+      cairo_surface_t* surf = cairo_get_group_target(ctx);
+      if (surf) {
+        cairo_surface_type_t surfaceType = cairo_surface_get_type(surf);
+        if (surfaceType == CAIRO_SURFACE_TYPE_WIN32 ||
+            surfaceType == CAIRO_SURFACE_TYPE_WIN32_PRINTING) {
+          mDC = cairo_win32_surface_get_dc(surf);
+          mNeedsRelease = false;
+          SaveDC(mDC);
+          cairo_scaled_font_t* scaled = cairo_get_scaled_font(ctx);
+          cairo_win32_scaled_font_select_font(scaled, mDC);
+        }
       }
     }
   }
 
   if (!mDC) {
     // Get the whole screen DC:
     mDC = GetDC(nullptr);
     SetGraphicsMode(mDC, GM_ADVANCED);
@@ -695,19 +696,17 @@ gfxWindowsPlatform::VerifyD2DDevice(bool
 gfxPlatformFontList*
 gfxWindowsPlatform::CreatePlatformFontList()
 {
     gfxPlatformFontList *pfl;
 
 #ifdef CAIRO_HAS_DWRITE_FONT
     // bug 630201 - older pre-RTM versions of Direct2D/DirectWrite cause odd
     // crashers so blacklist them altogether
-    if (IsNotWin7PreRTM() && GetDWriteFactory() &&
-        // Skia doesn't support DirectWrite fonts yet.
-       (gfxPlatform::GetDefaultContentBackend() != BackendType::SKIA)) {
+    if (IsNotWin7PreRTM() && GetDWriteFactory()) {
         pfl = new gfxDWriteFontList();
         if (NS_SUCCEEDED(pfl->InitFontList())) {
             return pfl;
         }
         // DWrite font initialization failed! Don't know why this would happen,
         // but apparently it can - see bug 594865.
         // So we're going to fall back to GDI fonts & rendering.
         gfxPlatformFontList::Shutdown();
--- a/gfx/thebes/gfxXlibNativeRenderer.cpp
+++ b/gfx/thebes/gfxXlibNativeRenderer.cpp
@@ -525,19 +525,22 @@ gfxXlibNativeRenderer::Draw(gfxContext* 
                          int32_t(clipExtents.Height()));
     drawingRect.IntersectRect(drawingRect, intExtents);
 
     gfxPoint offset(drawingRect.x, drawingRect.y);
 
     DrawingMethod method;
     Matrix dtTransform = drawTarget->GetTransform();
     gfxPoint deviceTranslation = gfxPoint(dtTransform._31, dtTransform._32);
-    cairo_surface_t* cairoTarget = static_cast<cairo_surface_t*>
-            (drawTarget->GetNativeSurface(NativeSurfaceType::CAIRO_SURFACE));
+    cairo_t* cairo = static_cast<cairo_t*>
+        (drawTarget->GetNativeSurface(NativeSurfaceType::CAIRO_CONTEXT));
+    if (!cairo)
+        return;
 
+    cairo_surface_t* cairoTarget = cairo_get_group_target(cairo);
     cairo_surface_t* tempXlibSurface =
         CreateTempXlibSurface(cairoTarget, drawTarget, size,
                               canDrawOverBackground, flags, screen, visual,
                               &method);
     if (!tempXlibSurface)
         return;
 
     bool drawIsOpaque = (flags & DRAW_IS_OPAQUE) != 0;
@@ -568,17 +571,17 @@ gfxXlibNativeRenderer::Draw(gfxContext* 
 
     SurfaceFormat moz2DFormat =
         cairo_surface_get_content(tempXlibSurface) == CAIRO_CONTENT_COLOR ?
             SurfaceFormat::B8G8R8A8 : SurfaceFormat::B8G8R8X8;
     if (method != eAlphaExtraction) {
         if (drawTarget) {
             NativeSurface native;
             native.mFormat = moz2DFormat;
-            native.mType = NativeSurfaceType::CAIRO_SURFACE;
+            native.mType = NativeSurfaceType::CAIRO_CONTEXT;
             native.mSurface = tempXlibSurface;
             native.mSize = size;
             RefPtr<SourceSurface> sourceSurface =
                 drawTarget->CreateSourceSurfaceFromNativeSurface(native);
             if (sourceSurface) {
                 drawTarget->DrawSurface(sourceSurface,
                     Rect(offset.x, offset.y, size.width, size.height),
                     Rect(0, 0, size.width, size.height));
@@ -610,17 +613,17 @@ gfxXlibNativeRenderer::Draw(gfxContext* 
             cairo_surface_destroy(tempXlibSurface);
             return;
         }
 
         gfxASurface* paintSurface = blackImage;
         if (drawTarget) {
             NativeSurface native;
             native.mFormat = moz2DFormat;
-            native.mType = NativeSurfaceType::CAIRO_SURFACE;
+            native.mType = NativeSurfaceType::CAIRO_CONTEXT;
             native.mSurface = paintSurface->CairoSurface();
             native.mSize = size;
             RefPtr<SourceSurface> sourceSurface =
                 drawTarget->CreateSourceSurfaceFromNativeSurface(native);
             if (sourceSurface) {
                 drawTarget->DrawSurface(sourceSurface,
                     Rect(offset.x, offset.y, size.width, size.height),
                     Rect(0, 0, size.width, size.height));
--- a/gfx/vr/gfxVROculus.cpp
+++ b/gfx/vr/gfxVROculus.cpp
@@ -29,44 +29,42 @@ using namespace mozilla::gfx;
 using namespace mozilla::gfx::impl;
 
 namespace {
 
 #ifdef OVR_CAPI_LIMITED_MOZILLA
 static pfn_ovr_Initialize ovr_Initialize = nullptr;
 static pfn_ovr_Shutdown ovr_Shutdown = nullptr;
 static pfn_ovr_GetTimeInSeconds ovr_GetTimeInSeconds = nullptr;
+static pfn_ovr_GetHmdDesc ovr_GetHmdDesc = nullptr;
 
-static pfn_ovrHmd_Detect ovrHmd_Detect = nullptr;
-static pfn_ovrHmd_Create ovrHmd_Create = nullptr;
-static pfn_ovrHmd_CreateDebug ovrHmd_CreateDebug = nullptr;
-static pfn_ovrHmd_Destroy ovrHmd_Destroy = nullptr;
+static pfn_ovr_Create ovr_Create = nullptr;
+static pfn_ovr_Destroy ovr_Destroy = nullptr;
 
-static pfn_ovrHmd_ConfigureTracking ovrHmd_ConfigureTracking = nullptr;
-static pfn_ovrHmd_RecenterPose ovrHmd_RecenterPose = nullptr;
-static pfn_ovrHmd_GetTrackingState ovrHmd_GetTrackingState = nullptr;
-static pfn_ovrHmd_GetFovTextureSize ovrHmd_GetFovTextureSize = nullptr;
-static pfn_ovrHmd_GetRenderDesc ovrHmd_GetRenderDesc = nullptr;
+static pfn_ovr_RecenterPose ovr_RecenterPose = nullptr;
+static pfn_ovr_GetTrackingState ovr_GetTrackingState = nullptr;
+static pfn_ovr_GetFovTextureSize ovr_GetFovTextureSize = nullptr;
+static pfn_ovr_GetRenderDesc ovr_GetRenderDesc = nullptr;
 
-static pfn_ovrHmd_DestroySwapTextureSet ovrHmd_DestroySwapTextureSet = nullptr;
-static pfn_ovrHmd_SubmitFrame ovrHmd_SubmitFrame = nullptr;
+static pfn_ovr_DestroySwapTextureSet ovr_DestroySwapTextureSet = nullptr;
+static pfn_ovr_SubmitFrame ovr_SubmitFrame = nullptr;
 
 #ifdef XP_WIN
-static pfn_ovrHmd_CreateSwapTextureSetD3D11 ovrHmd_CreateSwapTextureSetD3D11 = nullptr;
+static pfn_ovr_CreateSwapTextureSetD3D11 ovr_CreateSwapTextureSetD3D11 = nullptr;
 #endif
-static pfn_ovrHmd_CreateSwapTextureSetGL ovrHmd_CreateSwapTextureSetGL = nullptr;
+static pfn_ovr_CreateSwapTextureSetGL ovr_CreateSwapTextureSetGL = nullptr;
 
 #ifdef HAVE_64BIT_BUILD
 #define BUILD_BITS 64
 #else
 #define BUILD_BITS 32
 #endif
 
 #define OVR_PRODUCT_VERSION 0
-#define OVR_MAJOR_VERSION   6
+#define OVR_MAJOR_VERSION   8
 #define OVR_MINOR_VERSION   0
 
 static bool
 InitializeOculusCAPI()
 {
   static PRLibrary *ovrlib = nullptr;
 
   if (!ovrlib) {
@@ -160,34 +158,32 @@ InitializeOculusCAPI()
 #define REQUIRE_FUNCTION(_x) do { \
     *(void **)&_x = (void *) PR_FindSymbol(ovrlib, #_x);                \
     if (!_x) { printf_stderr(#_x " symbol missing\n"); goto fail; }       \
   } while (0)
 
   REQUIRE_FUNCTION(ovr_Initialize);
   REQUIRE_FUNCTION(ovr_Shutdown);
   REQUIRE_FUNCTION(ovr_GetTimeInSeconds);
+  REQUIRE_FUNCTION(ovr_GetHmdDesc);
   
-  REQUIRE_FUNCTION(ovrHmd_Detect);
-  REQUIRE_FUNCTION(ovrHmd_Create);
-  REQUIRE_FUNCTION(ovrHmd_CreateDebug);
-  REQUIRE_FUNCTION(ovrHmd_Destroy);
+  REQUIRE_FUNCTION(ovr_Create);
+  REQUIRE_FUNCTION(ovr_Destroy);
   
-  REQUIRE_FUNCTION(ovrHmd_ConfigureTracking);
-  REQUIRE_FUNCTION(ovrHmd_RecenterPose);
-  REQUIRE_FUNCTION(ovrHmd_GetTrackingState);
-  REQUIRE_FUNCTION(ovrHmd_GetFovTextureSize);
-  REQUIRE_FUNCTION(ovrHmd_GetRenderDesc);
+  REQUIRE_FUNCTION(ovr_RecenterPose);
+  REQUIRE_FUNCTION(ovr_GetTrackingState);
+  REQUIRE_FUNCTION(ovr_GetFovTextureSize);
+  REQUIRE_FUNCTION(ovr_GetRenderDesc);
 
-  REQUIRE_FUNCTION(ovrHmd_DestroySwapTextureSet);
-  REQUIRE_FUNCTION(ovrHmd_SubmitFrame);
+  REQUIRE_FUNCTION(ovr_DestroySwapTextureSet);
+  REQUIRE_FUNCTION(ovr_SubmitFrame);
 #ifdef XP_WIN
-  REQUIRE_FUNCTION(ovrHmd_CreateSwapTextureSetD3D11);
+  REQUIRE_FUNCTION(ovr_CreateSwapTextureSetD3D11);
 #endif
-  REQUIRE_FUNCTION(ovrHmd_CreateSwapTextureSetGL);
+  REQUIRE_FUNCTION(ovr_CreateSwapTextureSetGL);
 
 #undef REQUIRE_FUNCTION
 
   return true;
 
  fail:
   ovr_Initialize = nullptr;
   return false;
@@ -242,103 +238,84 @@ FromFovPort(const ovrFovPort& aFOV)
   fovInfo.rightDegrees = atan(aFOV.RightTan) * 180.0 / M_PI;
   fovInfo.upDegrees = atan(aFOV.UpTan) * 180.0 / M_PI;
   fovInfo.downDegrees = atan(aFOV.DownTan) * 180.0 / M_PI;
   return fovInfo;
 }
 
 } // namespace
 
-HMDInfoOculus::HMDInfoOculus(ovrHmd aHMD, bool aDebug, int aDeviceID)
+HMDInfoOculus::HMDInfoOculus(ovrSession aSession)
   : VRHMDInfo(VRHMDType::Oculus, false)
-  , mHMD(aHMD)
-  , mTracking(false)
-  , mDebug(aDebug)
-  , mDeviceID(aDeviceID)
-  , mSensorTrackingFramesRemaining(0)
+  , mSession(aSession)
 {
   MOZ_ASSERT(sizeof(HMDInfoOculus::DistortionVertex) == sizeof(VRDistortionVertex),
              "HMDInfoOculus::DistortionVertex must match the size of VRDistortionVertex");
 
   MOZ_COUNT_CTOR_INHERITED(HMDInfoOculus, VRHMDInfo);
 
-  if (aDebug) {
-    mDeviceInfo.mDeviceName.AssignLiteral("Oculus VR HMD Debug)");
-  } else {
-    mDeviceInfo.mDeviceName.AssignLiteral("Oculus VR HMD");
-  }
+  mDeviceInfo.mDeviceName.AssignLiteral("Oculus VR HMD");
+
+  mDesc = ovr_GetHmdDesc(aSession);
 
   mDeviceInfo.mSupportedSensorBits = VRStateValidFlags::State_None;
-  if (mHMD->TrackingCaps & ovrTrackingCap_Orientation) {
+  if (mDesc.AvailableTrackingCaps & ovrTrackingCap_Orientation) {
     mDeviceInfo.mSupportedSensorBits |= VRStateValidFlags::State_Orientation;
   }
-  if (mHMD->TrackingCaps & ovrTrackingCap_Position) {
+  if (mDesc.AvailableTrackingCaps & ovrTrackingCap_Position) {
     mDeviceInfo.mSupportedSensorBits |= VRStateValidFlags::State_Position;
   }
 
-  mDeviceInfo.mRecommendedEyeFOV[VRDeviceInfo::Eye_Left] = FromFovPort(mHMD->DefaultEyeFov[ovrEye_Left]);
-  mDeviceInfo.mRecommendedEyeFOV[VRDeviceInfo::Eye_Right] = FromFovPort(mHMD->DefaultEyeFov[ovrEye_Right]);
+  mDeviceInfo.mRecommendedEyeFOV[VRDeviceInfo::Eye_Left] = FromFovPort(mDesc.DefaultEyeFov[ovrEye_Left]);
+  mDeviceInfo.mRecommendedEyeFOV[VRDeviceInfo::Eye_Right] = FromFovPort(mDesc.DefaultEyeFov[ovrEye_Right]);
 
-  mDeviceInfo.mMaximumEyeFOV[VRDeviceInfo::Eye_Left] = FromFovPort(mHMD->MaxEyeFov[ovrEye_Left]);
-  mDeviceInfo.mMaximumEyeFOV[VRDeviceInfo::Eye_Right] = FromFovPort(mHMD->MaxEyeFov[ovrEye_Right]);
+  mDeviceInfo.mMaximumEyeFOV[VRDeviceInfo::Eye_Left] = FromFovPort(mDesc.MaxEyeFov[ovrEye_Left]);
+  mDeviceInfo.mMaximumEyeFOV[VRDeviceInfo::Eye_Right] = FromFovPort(mDesc.MaxEyeFov[ovrEye_Right]);
 
-  uint32_t w = mHMD->Resolution.w;
-  uint32_t h = mHMD->Resolution.h;
+  uint32_t w = mDesc.Resolution.w;
+  uint32_t h = mDesc.Resolution.h;
   mDeviceInfo.mScreenRect.x = 0;
   mDeviceInfo.mScreenRect.y = 0;
   mDeviceInfo.mScreenRect.width = std::max(w, h);
   mDeviceInfo.mScreenRect.height = std::min(w, h);
   mDeviceInfo.mIsFakeScreen = true;
 
   SetFOV(mDeviceInfo.mRecommendedEyeFOV[VRDeviceInfo::Eye_Left], mDeviceInfo.mRecommendedEyeFOV[VRDeviceInfo::Eye_Right], 0.01, 10000.0);
 }
 
-bool
-HMDInfoOculus::GetIsDebug() const
-{
-  return mDebug;
-}
-
-int
-HMDInfoOculus::GetDeviceID() const
-{
-  return mDeviceID;
-}
-
 void
 HMDInfoOculus::Destroy()
 {
-  StopSensorTracking();
-  if (mHMD) {
-    ovrHmd_Destroy(mHMD);
-    mHMD = nullptr;
+  if (mSession) {
+    ovr_Destroy(mSession);
+    mSession = nullptr;
   }
 }
 
 bool
 HMDInfoOculus::SetFOV(const gfx::VRFieldOfView& aFOVLeft, const gfx::VRFieldOfView& aFOVRight,
                       double zNear, double zFar)
 {
   float pixelsPerDisplayPixel = 1.0;
   ovrSizei texSize[2];
 
   // get eye parameters and create the mesh
   for (uint32_t eye = 0; eye < VRDeviceInfo::NumEyes; eye++) {
     mDeviceInfo.mEyeFOV[eye] = eye == 0 ? aFOVLeft : aFOVRight;
     mFOVPort[eye] = ToFovPort(mDeviceInfo.mEyeFOV[eye]);
 
-    ovrEyeRenderDesc renderDesc = ovrHmd_GetRenderDesc(mHMD, (ovrEyeType) eye, mFOVPort[eye]);
+    ovrEyeRenderDesc renderDesc = ovr_GetRenderDesc(mSession, (ovrEyeType)eye, mFOVPort[eye]);
 
     // As of Oculus 0.6.0, the HmdToEyeViewOffset values are correct and don't need to be negated.
     mDeviceInfo.mEyeTranslation[eye] = Point3D(renderDesc.HmdToEyeViewOffset.x, renderDesc.HmdToEyeViewOffset.y, renderDesc.HmdToEyeViewOffset.z);
 
     // note that we are using a right-handed coordinate system here, to match CSS
     mDeviceInfo.mEyeProjectionMatrix[eye] = mDeviceInfo.mEyeFOV[eye].ConstructProjectionMatrix(zNear, zFar, true);
 
-    texSize[eye] = ovrHmd_GetFovTextureSize(mHMD, (ovrEyeType) eye, mFOVPort[eye], pixelsPerDisplayPixel);
+    texSize[eye] = ovr_GetFovTextureSize(mSession, (ovrEyeType)eye, mFOVPort[eye], pixelsPerDisplayPixel);
   }
 
   // take the max of both for eye resolution
   mDeviceInfo.mEyeResolution.width = std::max(texSize[VRDeviceInfo::Eye_Left].w, texSize[VRDeviceInfo::Eye_Right].w);
   mDeviceInfo.mEyeResolution.height = std::max(texSize[VRDeviceInfo::Eye_Left].h, texSize[VRDeviceInfo::Eye_Right].h);
 
   mConfiguration.hmdType = mDeviceInfo.mType;
   mConfiguration.value = 0;
@@ -356,77 +333,41 @@ HMDInfoOculus::FillDistortionConstants(u
                                        const Rect& destRect,
                                        VRDistortionConstants& values)
 {
 }
 
 bool
 HMDInfoOculus::KeepSensorTracking()
 {
-  // Keep sensor tracking alive for short time after the last request for
-  // tracking state by content.  Value conservatively high to accomodate
-  // potentially high frame rates.
-  const uint32_t kKeepAliveFrames = 200;
-
-  bool success = true;
-  if (mSensorTrackingFramesRemaining == 0) {
-    success = StartSensorTracking();
-  }
-  if (success) {
-    mSensorTrackingFramesRemaining = kKeepAliveFrames;
-  }
-
-  return success;
+  // Oculus PC SDK 0.8 and newer enable tracking by default
+  return true;
 }
 
 void
 HMDInfoOculus::NotifyVsync(const mozilla::TimeStamp& aVsyncTimestamp)
 {
-  if (mSensorTrackingFramesRemaining == 1) {
-    StopSensorTracking();
-  }
-  if (mSensorTrackingFramesRemaining) {
-    --mSensorTrackingFramesRemaining;
-  }
-}
 
-bool
-HMDInfoOculus::StartSensorTracking()
-{
-  if (!mTracking) {
-    mTracking = ovrHmd_ConfigureTracking(mHMD, ovrTrackingCap_Orientation | ovrTrackingCap_Position, 0);
-  }
-
-  return mTracking;
-}
-
-void
-HMDInfoOculus::StopSensorTracking()
-{
-  if (mTracking) {
-    ovrHmd_ConfigureTracking(mHMD, 0, 0);
-    mTracking = false;
-  }
 }
 
 void
 HMDInfoOculus::ZeroSensor()
 {
-  ovrHmd_RecenterPose(mHMD);
+  ovr_RecenterPose(mSession);
 }
 
 VRHMDSensorState
 HMDInfoOculus::GetSensorState(double timeOffset)
 {
   VRHMDSensorState result;
   result.Clear();
 
   // XXX this is the wrong time base for timeOffset; we need to figure out how to synchronize
   // the Oculus time base and the browser one.
-  ovrTrackingState state = ovrHmd_GetTrackingState(mHMD, ovr_GetTimeInSeconds() + timeOffset);
+  ovrTrackingState state = ovr_GetTrackingState(mSession, ovr_GetTimeInSeconds() + timeOffset, true);
   ovrPoseStatef& pose(state.HeadPose);
 
   result.timestamp = pose.TimeInSeconds;
 
   if (state.StatusFlags & ovrStatus_OrientationTracked) {
     result.flags |= VRStateValidFlags::State_Orientation;
 
     result.orientation[0] = pose.ThePose.Orientation.x;
@@ -482,20 +423,20 @@ struct RenderTargetSetOculus : public VR
     RefPtr<layers::CompositingRenderTarget> rt = renderTargets[currentRenderTarget];
     return rt.forget();
   }
 
   void Destroy() {
     if (!hmd)
       return;
     
-    if (hmd->GetOculusHMD()) {
-      // If the ovrHmd was already destroyed, so were all associated
+    if (hmd->GetOculusSession()) {
+      // If the ovrSession was already destroyed, so were all associated
       // texture sets
-      ovrHmd_DestroySwapTextureSet(hmd->GetOculusHMD(), textureSet);
+      ovr_DestroySwapTextureSet(hmd->GetOculusSession(), textureSet);
     }
     hmd = nullptr;
     textureSet = nullptr;
   }
   
   ~RenderTargetSetOculus() {
     Destroy();
   }
@@ -528,37 +469,37 @@ struct RenderTargetSetD3D11 : public Ren
     
     currentRenderTarget = aTS->CurrentIndex;
 
     for (int i = 0; i < aTS->TextureCount; ++i) {
       ovrD3D11Texture *tex11;
       RefPtr<layers::CompositingRenderTargetD3D11> rt;
       
       tex11 = (ovrD3D11Texture*)&aTS->Textures[i];
-      rt = new layers::CompositingRenderTargetD3D11(tex11->D3D11.pTexture, IntPoint(0, 0));
+      rt = new layers::CompositingRenderTargetD3D11(tex11->D3D11.pTexture, IntPoint(0, 0), DXGI_FORMAT_B8G8R8A8_UNORM);
       rt->SetSize(size);
       renderTargets[i] = rt;
     }
   }
 };
 #endif
 
 already_AddRefed<VRHMDRenderingSupport::RenderTargetSet>
 HMDInfoOculus::CreateRenderTargetSet(layers::Compositor *aCompositor, const IntSize& aSize)
 {
 #ifdef XP_WIN
   if (aCompositor->GetBackendType() == layers::LayersBackend::LAYERS_D3D11)
   {
     layers::CompositorD3D11 *comp11 = static_cast<layers::CompositorD3D11*>(aCompositor);
 
-    CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, aSize.width, aSize.height, 1, 1,
+    CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM_SRGB, aSize.width, aSize.height, 1, 1,
                                D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET);
     ovrSwapTextureSet *ts = nullptr;
     
-    ovrResult orv = ovrHmd_CreateSwapTextureSetD3D11(mHMD, comp11->GetDevice(), &desc, &ts);
+    ovrResult orv = ovr_CreateSwapTextureSetD3D11(mSession, comp11->GetDevice(), &desc, ovrSwapTextureSetD3D11_Typeless, &ts);
     if (orv != ovrSuccess) {
       return nullptr;
     }
 
     RefPtr<RenderTargetSetD3D11> rts = new RenderTargetSetD3D11(comp11, aSize, this, ts);
     return rts.forget();
   }
 #endif
@@ -601,17 +542,17 @@ HMDInfoOculus::SubmitFrame(RenderTargetS
 
   const Point3D& l = rts->hmd->mDeviceInfo.mEyeTranslation[0];
   const Point3D& r = rts->hmd->mDeviceInfo.mEyeTranslation[1];
   const ovrVector3f hmdToEyeViewOffset[2] = { { l.x, l.y, l.z },
                                               { r.x, r.y, r.z } };
   do_CalcEyePoses(rts->hmd->mLastTrackingState.HeadPose.ThePose, hmdToEyeViewOffset, layer.RenderPose);
 
   ovrLayerHeader *layers = &layer.Header;
-  ovrResult orv = ovrHmd_SubmitFrame(mHMD, 0, nullptr, &layers, 1);
+  ovrResult orv = ovr_SubmitFrame(mSession, 0, nullptr, &layers, 1);
   //printf_stderr("Submitted frame %d, result: %d\n", rts->textureSet->CurrentIndex, orv);
   if (orv != ovrSuccess) {
     // not visible? failed?
   }
 }
 
 /*static*/ already_AddRefed<VRHMDManagerOculus>
 VRHMDManagerOculus::Create()
@@ -657,83 +598,43 @@ VRHMDManagerOculus::Init()
 
 void
 VRHMDManagerOculus::Destroy()
 {
   if(mOculusInitialized) {
     MOZ_ASSERT(NS_GetCurrentThread() == mOculusThread);
     mOculusThread = nullptr;
 
-    for (size_t i = 0; i < mOculusHMDs.Length(); ++i) {
-      mOculusHMDs[i]->Destroy();
-    }
-
-    mOculusHMDs.Clear();
+    mHMDInfo = nullptr;
 
     ovr_Shutdown();
     mOculusInitialized = false;
   }
 }
 
 void
 VRHMDManagerOculus::GetHMDs(nsTArray<RefPtr<VRHMDInfo>>& aHMDResult)
 {
   if (!mOculusInitialized) {
     return;
   }
 
-  nsTArray<RefPtr<impl::HMDInfoOculus> > newHMDs;
-
-  ovrResult orv;
-
-  int count = ovrHmd_Detect();
-
-  for (int j = 0; j < count; ++j) {
-    bool is_new = true;
-    for (size_t i = 0; i < mOculusHMDs.Length(); ++i) {
-      if (mOculusHMDs[i]->GetDeviceID() == j) {
-        newHMDs.AppendElement(mOculusHMDs[i]);
-        is_new = false;
-        break;
-      }
-    }
-
-    if (is_new) {
-      ovrHmd hmd;
-      orv = ovrHmd_Create(j, &hmd);
-      if (orv == ovrSuccess) {
-        RefPtr<HMDInfoOculus> oc = new HMDInfoOculus(hmd, false, j);
-        newHMDs.AppendElement(oc);
-      }
+  // ovr_Create can be slow when no HMD is present and we wish
+  // to keep the same oculus session when possible, so we detect
+  // presence of an HMD with ovr_GetHmdDesc before calling ovr_Create
+  ovrHmdDesc desc = ovr_GetHmdDesc(NULL);
+  if (desc.Type == ovrHmd_None) {
+    // No HMD connected.
+    mHMDInfo = nullptr;
+  } else if (mHMDInfo == nullptr) {
+    // HMD Detected
+    ovrSession session;
+    ovrGraphicsLuid luid;
+    ovrResult orv = ovr_Create(&session, &luid);
+    if (orv == ovrSuccess) {
+      mHMDInfo = new HMDInfoOculus(session);
     }
   }
 
-  // VRAddTestDevices == 1: add test device only if no real devices present
-  // VRAddTestDevices == 2: add test device always
-  if ((count == 0 && gfxPrefs::VRAddTestDevices() == 1) ||
-    (gfxPrefs::VRAddTestDevices() == 2))
-  {
-    // Keep existing debug HMD if possible
-    bool foundDebug = false;
-    for (size_t i = 0; i < mOculusHMDs.Length(); ++i) {
-      if (mOculusHMDs[i]->GetIsDebug()) {
-        newHMDs.AppendElement(mOculusHMDs[i]);
-        foundDebug = true;
-      }
-    }
-
-    // If there isn't already a debug HMD, create one
-    if (!foundDebug) {
-      ovrHmd hmd;
-      orv = ovrHmd_CreateDebug(ovrHmd_DK2, &hmd);
-      if (orv == ovrSuccess) {
-        RefPtr<HMDInfoOculus> oc = new HMDInfoOculus(hmd, true, -1);
-        newHMDs.AppendElement(oc);
-      }
-    }
-  }
-
-  mOculusHMDs = newHMDs;
-
-  for (size_t j = 0; j < mOculusHMDs.Length(); ++j) {
-    aHMDResult.AppendElement(mOculusHMDs[j]);
+  if (mHMDInfo) {
+    aHMDResult.AppendElement(mHMDInfo);
   }
 }
--- a/gfx/vr/gfxVROculus.h
+++ b/gfx/vr/gfxVROculus.h
@@ -18,17 +18,17 @@
 #include "ovr_capi_dynamic.h"
 
 namespace mozilla {
 namespace gfx {
 namespace impl {
 
 class HMDInfoOculus : public VRHMDInfo, public VRHMDRenderingSupport {
 public:
-  explicit HMDInfoOculus(ovrHmd aHMD, bool aDebug, int aDeviceID);
+  explicit HMDInfoOc