merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Tue, 29 Sep 2015 12:00:25 +0200
changeset 264952 acdb22976ff86539dc10413c5f366e1fb429a680
parent 264951 bcd4d4ef46b67912c6c6e296fdebec999df79c44 (current diff)
parent 264888 3537b3915777a5f8b23c689ce65bd950c0422576 (diff)
child 264953 ef87331ae4d3acda0901d243a1640ee8233add14
child 265033 5fad0c497a23a85088814d8df15cfbe62526211b
child 265040 ccee6614fd9d18a31f263fbcfe9676b224d851aa
child 265046 143444f47dd23c0718ce92e0517d1549537c9909
push id65790
push usercbook@mozilla.com
push dateTue, 29 Sep 2015 10:40:27 +0000
treeherdermozilla-inbound@ef87331ae4d3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone44.0a1
first release with
nightly linux32
acdb22976ff8 / 44.0a1 / 20150929030225 / files
nightly linux64
acdb22976ff8 / 44.0a1 / 20150929030225 / files
nightly mac
acdb22976ff8 / 44.0a1 / 20150929030225 / files
nightly win32
acdb22976ff8 / 44.0a1 / 20150929030225 / files
nightly win64
acdb22976ff8 / 44.0a1 / 20150929030225 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
config/glibcversion.sh
js/src/ds/LockGuard.h
js/src/ds/SpinLock.h
js/src/jit-test/tests/arrays/dense-from-sparse.js
js/src/jsapi-tests/testGCUniqueId.cpp
js/src/vm/WeakMapObject.h
security/manager/ssl/tests/unit/tlsserver/cert9.db
security/manager/ssl/tests/unit/tlsserver/expired-ee.der
security/manager/ssl/tests/unit/tlsserver/generate_certs.sh
security/manager/ssl/tests/unit/tlsserver/inadequatekeyusage-ee.der
security/manager/ssl/tests/unit/tlsserver/key4.db
security/manager/ssl/tests/unit/tlsserver/other-issuer-ee.der
security/manager/ssl/tests/unit/tlsserver/other-test-ca.der
security/manager/ssl/tests/unit/tlsserver/pkcs11.txt
security/manager/ssl/tests/unit/tlsserver/same-issuer-ee.der
security/manager/ssl/tests/unit/tlsserver/test-ca.der
security/manager/ssl/tests/unit/tlsserver/test-int-ee.der
security/manager/ssl/tests/unit/tlsserver/test-int.der
security/manager/ssl/tests/unit/tlsserver/unknown-issuer.der
security/manager/ssl/tests/unit/tlsserver/v1Cert.der
testing/web-platform/meta/subresource-integrity/subresource-integrity.html.ini
testing/web-platform/meta/workers/interfaces/WorkerGlobalScope/close/incoming-message.html.ini
testing/web-platform/mozilla/meta/service-workers/service-worker/appcache-ordering-main.https.html.ini
testing/web-platform/tests/DOMEvents/tests/approved/stopPropagation.dispatchEvent.html
testing/web-platform/tests/DOMEvents/tests/submissions/Microsoft/converted/stopPropagation.dispatchEvent.html
testing/web-platform/tests/csp/assert_undefined.py
testing/web-platform/tests/csp/csp-inline-script.html
testing/web-platform/tests/csp/csp-inline-script.html.headers
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,12 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Bug 1108782 - Follow-up: clobber. r=me
-
-There's all kinds fail waiting at the end of these dependency changes.
-Definitely clobber needed!
\ No newline at end of file
+Bug 1205242 - Changed toolchain, needs clobber
--- a/accessible/generic/DocAccessible.cpp
+++ b/accessible/generic/DocAccessible.cpp
@@ -2066,16 +2066,20 @@ void
 DocAccessible::ValidateARIAOwned()
 {
   for (auto it = mARIAOwnsHash.Iter(); !it.Done(); it.Next()) {
     nsTArray<nsIContent*>* childEls = it.UserData();
     for (uint32_t idx = 0; idx < childEls->Length(); idx++) {
       nsIContent* childEl = childEls->ElementAt(idx);
       Accessible* child = GetAccessible(childEl);
       if (child && child->IsInDocument() && !child->GetFrame()) {
+        if (!child->Parent()) {
+          NS_ERROR("An element in the document doesn't have a parent?");
+          continue;
+        }
         UpdateTreeOnRemoval(child->Parent(), childEl);
       }
     }
   }
 }
 
 void
 DocAccessible::CacheChildrenInSubtree(Accessible* aRoot,
--- a/accessible/ipc/PDocAccessible.ipdl
+++ b/accessible/ipc/PDocAccessible.ipdl
@@ -220,17 +220,17 @@ child:
   prio(high) sync KeyboardShortcut(uint64_t aID) returns(uint32_t aKey, uint32_t aModifierMask);
 
   prio(high) sync CurValue(uint64_t aID) returns(double aValue);
   prio(high) sync SetCurValue(uint64_t aID, double aValue) returns(bool aRetVal);
   prio(high) sync MinValue(uint64_t aID) returns(double aValue);
   prio(high) sync MaxValue(uint64_t aID) returns(double aValue);
   prio(high) sync Step(uint64_t aID) returns(double aStep);
 
-  prio(high) sync TakeFocus(uint64_t aID);
+  async TakeFocus(uint64_t aID);
   prio(high) sync EmbeddedChildCount(uint64_t aID) returns(uint32_t aCount);
   prio(high) sync IndexOfEmbeddedChild(uint64_t aID, uint64_t aChildID)
     returns(uint32_t childIdx);
   prio(high) sync EmbeddedChildAt(uint64_t aID, uint32_t aChildIdx)
     returns(uint64_t aChild);
   prio(high) sync FocusedChild(uint64_t aID)
     returns(uint64_t aChild, bool aOk);
 
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1867,16 +1867,19 @@ pref("browser.apps.URL", "https://market
 
 #ifdef NIGHTLY_BUILD
 pref("browser.polaris.enabled", false);
 pref("privacy.trackingprotection.ui.enabled", false);
 #endif
 pref("privacy.trackingprotection.introCount", 0);
 pref("privacy.trackingprotection.introURL", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/tracking-protection/start/");
 
+// Enable Contextual Identity Containers
+pref("privacy.userContext.enabled", false);
+
 #ifndef RELEASE_BUILD
 // At the moment, autostart.2 is used, while autostart.1 is unused.
 // We leave it here set to false to reset users' defaults and allow
 // us to change everybody to true in the future, when desired.
 pref("browser.tabs.remote.autostart.1", false);
 pref("browser.tabs.remote.autostart.2", true);
 #endif
 
--- a/browser/base/content/browser-menubar.inc
+++ b/browser/base/content/browser-menubar.inc
@@ -6,22 +6,54 @@
        <menubar id="main-menubar"
                 onpopupshowing="if (event.target.parentNode.parentNode == this &amp;&amp;
                                     !('@mozilla.org/widget/nativemenuservice;1' in Cc))
                                   this.setAttribute('openedwithkey',
                                                     event.target.parentNode.openedWithKey);"
                 style="border:0px;padding:0px;margin:0px;-moz-appearance:none">
             <menu id="file-menu" label="&fileMenu.label;"
                   accesskey="&fileMenu.accesskey;">
-              <menupopup id="menu_FilePopup">
+              <menupopup id="menu_FilePopup"
+                         onpopupshowing="updateUserContextUIVisibility();">
                 <menuitem id="menu_newNavigatorTab"
                           label="&tabCmd.label;"
                           command="cmd_newNavigatorTab"
                           key="key_newNavigatorTab"
                           accesskey="&tabCmd.accesskey;"/>
+                <menu id="menu_newUserContext"
+                      label="&newUserContext.label;"
+                      accesskey="&newUserContext.accesskey;"
+                      hidden="true">
+                  <menupopup>
+                    <menuitem id="menu_newUserContextTabPersonal"
+                              usercontextid="1"
+                              class="menuitem-iconic"
+                              label="&userContextPersonal.label;"
+                              command="Browser:NewUserContextTab"
+                              accesskey="&userContextPersonal.accesskey;"/>
+                    <menuitem id="menu_newUserContextTabWork"
+                              usercontextid="2"
+                              class="menuitem-iconic"
+                              label="&userContextWork.label;"
+                              command="Browser:NewUserContextTab"
+                              accesskey="&userContextWork.accesskey;"/>
+                    <menuitem id="menu_newUserContextTabBanking"
+                              usercontextid="3"
+                              class="menuitem-iconic"
+                              label="&userContextBanking.label;"
+                              command="Browser:NewUserContextTab"
+                              accesskey="&userContextBanking.accesskey;"/>
+                    <menuitem id="menu_newUserContextTabShopping"
+                              usercontextid="4"
+                              class="menuitem-iconic"
+                              label="&userContextShopping.label;"
+                              command="Browser:NewUserContextTab"
+                              accesskey="&userContextShopping.accesskey;"/>
+                  </menupopup>
+                </menu>
                 <menuitem id="menu_newNavigator"
                           label="&newNavigatorCmd.label;"
                           accesskey="&newNavigatorCmd.accesskey;"
                           key="key_newNavigator"
                           command="cmd_newNavigator"/>
                 <menuitem id="menu_newPrivateWindow"
                           label="&newPrivateWindow.label;"
                           accesskey="&newPrivateWindow.accesskey;"
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -87,16 +87,17 @@
     <command id="cmd_fullZoomEnlarge" oncommand="FullZoom.enlarge()"/>
     <command id="cmd_fullZoomReset"   oncommand="FullZoom.reset()"/>
     <command id="cmd_fullZoomToggle"  oncommand="ZoomManager.toggleZoom();"/>
     <command id="cmd_gestureRotateLeft" oncommand="gGestureSupport.rotate(event.sourceEvent)"/>
     <command id="cmd_gestureRotateRight" oncommand="gGestureSupport.rotate(event.sourceEvent)"/>
     <command id="cmd_gestureRotateEnd" oncommand="gGestureSupport.rotateEnd()"/>
     <command id="Browser:OpenLocation" oncommand="openLocation();"/>
     <command id="Browser:RestoreLastSession" oncommand="restoreLastSession();" disabled="true"/>
+    <command id="Browser:NewUserContextTab" oncommand="openNewUserContextTab(event.sourceEvent);" reserved="true"/>
 
     <command id="Tools:Search" oncommand="BrowserSearch.webSearch();"/>
     <command id="Tools:Downloads" oncommand="BrowserDownloadsUI();"/>
     <command id="Tools:DevToolbox" oncommand="gDevToolsBrowser.toggleToolboxCommand(gBrowser);"/>
     <command id="Tools:DevToolbar" oncommand="DeveloperToolbar.toggle();" disabled="true" hidden="true"/>
     <command id="Tools:DevToolbarFocus" oncommand="DeveloperToolbar.focusToggle();" disabled="true"/>
     <command id="Tools:DevAppMgr" oncommand="gDevToolsBrowser.openAppManager(gBrowser);" disabled="true" hidden="true"/>
     <command id="Tools:WebIDE" oncommand="gDevToolsBrowser.openWebIDE();" disabled="true" hidden="true"/>
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -4056,16 +4056,41 @@ function updateEditUIVisibility()
     goSetCommandEnabled("cmd_selectAll", true);
     goSetCommandEnabled("cmd_delete", true);
     goSetCommandEnabled("cmd_switchTextDirection", true);
   }
 #endif
 }
 
 /**
+ * Opens a new tab with the userContextId specified as an attribute of
+ * sourceEvent. This attribute is propagated to the top level originAttributes
+ * living on the tab's docShell.
+ *
+ * @param event
+ *        A click event on a userContext File Menu option
+ */
+function openNewUserContextTab(event)
+{
+  openUILinkIn(BROWSER_NEW_TAB_URL, "tab", {
+    userContextId: event.target.getAttribute('usercontextid'),
+  });
+}
+
+/**
+ * Updates File Menu User Context UI visibility depending on
+ * privacy.userContext.enabled pref state.
+ */
+function updateUserContextUIVisibility()
+{
+  let userContextEnabled = Services.prefs.getBoolPref("privacy.userContext.enabled");
+  document.getElementById("menu_newUserContext").hidden = !userContextEnabled;
+}
+
+/**
  * Makes the Character Encoding menu enabled or disabled as appropriate.
  * To be called when the View menu or the app menu is opened.
  */
 function updateCharacterEncodingMenuState()
 {
   let charsetMenu = document.getElementById("charsetMenu");
   // gBrowser is null on Mac when the menubar shows in the context of
   // non-browser windows. The above elements may be null depending on
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -1410,32 +1410,34 @@
           <![CDATA[
             var aReferrerPolicy;
             var aFromExternal;
             var aRelatedToCurrent;
             var aAllowMixedContent;
             var aSkipAnimation;
             var aForceNotRemote;
             var aNoReferrer;
+            var aUserContextId;
             if (arguments.length == 2 &&
                 typeof arguments[1] == "object" &&
                 !(arguments[1] instanceof Ci.nsIURI)) {
               let params = arguments[1];
               aReferrerURI          = params.referrerURI;
               aReferrerPolicy       = params.referrerPolicy;
               aCharset              = params.charset;
               aPostData             = params.postData;
               aLoadInBackground     = params.inBackground;
               aAllowThirdPartyFixup = params.allowThirdPartyFixup;
               aFromExternal         = params.fromExternal;
               aRelatedToCurrent     = params.relatedToCurrent;
               aAllowMixedContent    = params.allowMixedContent;
               aSkipAnimation        = params.skipAnimation;
               aForceNotRemote       = params.forceNotRemote;
               aNoReferrer           = params.noReferrer;
+              aUserContextId        = params.userContextId;
             }
 
             var bgLoad = (aLoadInBackground != null) ? aLoadInBackground :
                          Services.prefs.getBoolPref("browser.tabs.loadInBackground");
             var owner = bgLoad ? null : this.selectedTab;
             var tab = this.addTab(aURI, {
                                   referrerURI: aReferrerURI,
                                   referrerPolicy: aReferrerPolicy,
@@ -1443,17 +1445,18 @@
                                   postData: aPostData,
                                   ownerTab: owner,
                                   allowThirdPartyFixup: aAllowThirdPartyFixup,
                                   fromExternal: aFromExternal,
                                   relatedToCurrent: aRelatedToCurrent,
                                   skipAnimation: aSkipAnimation,
                                   allowMixedContent: aAllowMixedContent,
                                   forceNotRemote: aForceNotRemote,
-                                  noReferrer: aNoReferrer });
+                                  noReferrer: aNoReferrer,
+                                  userContextId: aUserContextId });
             if (!bgLoad)
               this.selectedTab = tab;
 
             return tab;
          ]]>
         </body>
       </method>
 
@@ -1685,25 +1688,29 @@
         <parameter name="aParams"/>
         <body>
           <![CDATA[
             const NS_XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
             let remote = aParams && aParams.remote;
             let uriIsAboutBlank = aParams && aParams.uriIsAboutBlank;
             let isPreloadBrowser = aParams && aParams.isPreloadBrowser;
+            let userContextId = aParams && aParams.userContextId;
 
             let b = document.createElementNS(NS_XUL, "browser");
             b.permanentKey = {};
             b.setAttribute("type", "content-targetable");
             b.setAttribute("message", "true");
             b.setAttribute("messagemanagergroup", "browsers");
             b.setAttribute("contextmenu", this.getAttribute("contentcontextmenu"));
             b.setAttribute("tooltip", this.getAttribute("contenttooltip"));
 
+            if (userContextId)
+              b.setAttribute("usercontextid", userContextId);
+
             if (remote)
               b.setAttribute("remote", "true");
 
             if (window.gShowPageResizers && window.windowState == window.STATE_NORMAL) {
               b.setAttribute("showresizer", "true");
             }
 
             if (!isPreloadBrowser && this.hasAttribute("autocompletepopup"))
@@ -1762,32 +1769,34 @@
             const NS_XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
             var aReferrerPolicy;
             var aFromExternal;
             var aRelatedToCurrent;
             var aSkipAnimation;
             var aAllowMixedContent;
             var aForceNotRemote;
             var aNoReferrer;
+            var aUserContextId;
             if (arguments.length == 2 &&
                 typeof arguments[1] == "object" &&
                 !(arguments[1] instanceof Ci.nsIURI)) {
               let params = arguments[1];
               aReferrerURI          = params.referrerURI;
               aReferrerPolicy       = params.referrerPolicy;
               aCharset              = params.charset;
               aPostData             = params.postData;
               aOwner                = params.ownerTab;
               aAllowThirdPartyFixup = params.allowThirdPartyFixup;
               aFromExternal         = params.fromExternal;
               aRelatedToCurrent     = params.relatedToCurrent;
               aSkipAnimation        = params.skipAnimation;
               aAllowMixedContent    = params.allowMixedContent;
               aForceNotRemote       = params.forceNotRemote;
               aNoReferrer           = params.noReferrer;
+              aUserContextId        = params.userContextId;
             }
 
             // if we're adding tabs, we're past interrupt mode, ditch the owner
             if (this.mCurrentTab.owner)
               this.mCurrentTab.owner = null;
 
             var t = document.createElementNS(NS_XUL, "tab");
 
@@ -1826,26 +1835,28 @@
 
             // If this new tab is owned by another, assert that relationship
             if (aOwner)
               t.owner = aOwner;
 
             let b;
             let usingPreloadedContent = false;
 
-            // If we open a new tab with the newtab URL,
-            // check if there is a preloaded browser ready.
-            if (aURI == BROWSER_NEW_TAB_URL) {
+            // If we open a new tab with the newtab URL in the default
+            // userContext, check if there is a preloaded browser ready.
+            if (aURI == BROWSER_NEW_TAB_URL && !aUserContextId) {
               b = this._getPreloadedBrowser();
               usingPreloadedContent = !!b;
             }
 
             if (!b) {
               // No preloaded browser found, create one.
-              b = this._createBrowser({remote, uriIsAboutBlank});
+              b = this._createBrowser({remote: remote,
+                                       uriIsAboutBlank: uriIsAboutBlank,
+                                       userContextId: aUserContextId});
             }
 
             let notificationbox = this.getNotificationBox(b);
             var position = this.tabs.length - 1;
             var uniqueId = this._generateUniquePanelID();
             notificationbox.id = uniqueId;
             t.linkedPanel = uniqueId;
             t.linkedBrowser = b;
@@ -2481,16 +2492,21 @@
 
             let ourBrowser = this.getBrowserForTab(aOurTab);
             let otherBrowser = aOtherTab.linkedBrowser;
 
             // Can't swap between chrome and content processes.
             if (ourBrowser.isRemoteBrowser != otherBrowser.isRemoteBrowser)
               return;
 
+            // Keep the userContextId if set on other browser
+            if (otherBrowser.hasAttribute("usercontextid")) {
+              ourBrowser.setAttribute("usercontextid", otherBrowser.getAttribute("usercontextid"));
+            }
+
             // That's gBrowser for the other window, not the tab's browser!
             var remoteBrowser = aOtherTab.ownerDocument.defaultView.gBrowser;
             var isPending = aOtherTab.hasAttribute("pending");
 
             // First, start teardown of the other browser.  Make sure to not
             // fire the beforeunload event in the process.  Close the other
             // window if this was its last tab.
             if (!remoteBrowser._beginRemoveTab(aOtherTab, true, true))
--- a/browser/base/content/test/general/browser_beforeunload_duplicate_dialogs.js
+++ b/browser/base/content/test/general/browser_beforeunload_duplicate_dialogs.js
@@ -13,16 +13,17 @@ function onTabModalDialogLoaded(node) {
     // This keeps the page open
     node.Dialog.ui.button1.click();
   }
   if (resolveDialogPromise) {
     resolveDialogPromise();
   }
 }
 
+SpecialPowers.pushPrefEnv({"set": [["dom.require_user_interaction_for_beforeunload", false]]});
 
 // Listen for the dialog being created
 Services.obs.addObserver(onTabModalDialogLoaded, "tabmodal-dialog-loaded", false);
 registerCleanupFunction(() => {
   Services.prefs.clearUserPref("browser.tabs.warnOnClose");
   Services.obs.removeObserver(onTabModalDialogLoaded, "tabmodal-dialog-loaded");
 });
 
--- a/browser/base/content/test/general/browser_double_close_tab.js
+++ b/browser/base/content/test/general/browser_double_close_tab.js
@@ -1,12 +1,14 @@
 "use strict";
 const TEST_PAGE = "http://mochi.test:8888/browser/browser/base/content/test/general/file_double_close_tab.html";
 var testTab;
 
+SpecialPowers.pushPrefEnv({"set": [["dom.require_user_interaction_for_beforeunload", false]]});
+
 function waitForDialog(callback) {
   function onTabModalDialogLoaded(node) {
     Services.obs.removeObserver(onTabModalDialogLoaded, "tabmodal-dialog-loaded");
     callback(node);
   }
 
   // Listen for the dialog being created
   Services.obs.addObserver(onTabModalDialogLoaded, "tabmodal-dialog-loaded", false);
--- a/browser/base/content/test/general/browser_tabs_close_beforeunload.js
+++ b/browser/base/content/test/general/browser_tabs_close_beforeunload.js
@@ -1,10 +1,12 @@
 "use strict";
 
+SpecialPowers.pushPrefEnv({"set": [["dom.require_user_interaction_for_beforeunload", false]]});
+
 const FIRST_TAB = getRootDirectory(gTestPath) + "close_beforeunload_opens_second_tab.html";
 const SECOND_TAB = getRootDirectory(gTestPath) + "close_beforeunload.html";
 
 add_task(function*() {
   let firstTab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, FIRST_TAB);
   let newTabPromise = waitForNewTabEvent(gBrowser);
   ContentTask.spawn(firstTab.linkedBrowser, "", function*() {
     content.document.getElementsByTagName("a")[0].click();
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -168,16 +168,17 @@ function whereToOpenLink( e, ignoreButto
  * these properties:
  *   allowThirdPartyFixup (boolean)
  *   postData             (nsIInputStream)
  *   referrerURI          (nsIURI)
  *   relatedToCurrent     (boolean)
  *   skipTabAnimation     (boolean)
  *   allowPinnedTabHostChange (boolean)
  *   allowPopups          (boolean)
+ *   userContextId        (unsigned int)
  */
 function openUILinkIn(url, where, aAllowThirdPartyFixup, aPostData, aReferrerURI) {
   var params;
 
   if (arguments.length == 3 && typeof arguments[2] == "object") {
     params = aAllowThirdPartyFixup;
   } else {
     params = {
@@ -211,16 +212,17 @@ function openLinkIn(url, where, params) 
   var aInBackground         = params.inBackground;
   var aDisallowInheritPrincipal = params.disallowInheritPrincipal;
   var aInitiatingDoc        = params.initiatingDoc;
   var aIsPrivate            = params.private;
   var aSkipTabAnimation     = params.skipTabAnimation;
   var aAllowPinnedTabHostChange = !!params.allowPinnedTabHostChange;
   var aNoReferrer           = params.noReferrer;
   var aAllowPopups          = !!params.allowPopups;
+  var aUserContextId        = params.userContextId;
 
   if (where == "save") {
     if (!aInitiatingDoc) {
       Components.utils.reportError("openUILink/openLinkIn was called with " +
         "where == 'save' but without initiatingDoc.  See bug 814264.");
       return;
     }
     // TODO(1073187): propagate referrerPolicy.
@@ -352,17 +354,18 @@ function openLinkIn(url, where, params) 
       referrerPolicy: aReferrerPolicy,
       charset: aCharset,
       postData: aPostData,
       inBackground: loadInBackground,
       allowThirdPartyFixup: aAllowThirdPartyFixup,
       relatedToCurrent: aRelatedToCurrent,
       skipAnimation: aSkipTabAnimation,
       allowMixedContent: aAllowMixedContent,
-      noReferrer: aNoReferrer
+      noReferrer: aNoReferrer,
+      userContextId: aUserContextId
     });
     break;
   }
 
   w.gBrowser.selectedBrowser.focus();
 
   if (!loadInBackground && w.isBlankPageURL(url)) {
     w.focusAndSelectUrlBar();
--- a/browser/components/tabview/test/browser_tabview_bug599626.js
+++ b/browser/components/tabview/test/browser_tabview_bug599626.js
@@ -1,16 +1,18 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 const TEST_URL = 'data:text/html,<script>window.onbeforeunload=' +
                  'function(e){e.returnValue="?"}</script>';
 
+SpecialPowers.pushPrefEnv({"set": [["dom.require_user_interaction_for_beforeunload", false]]});
+
 function test() {
   waitForExplicitFinish();
   showTabView(onTabViewShown);
 }
 
 function onTabViewShown() {
   let contentWindow = TabView.getContentWindow();
   let groupItemOne = contentWindow.GroupItems.getActiveGroupItem();
--- a/browser/components/tabview/test/browser_tabview_bug626455.js
+++ b/browser/components/tabview/test/browser_tabview_bug626455.js
@@ -10,16 +10,18 @@
 "use strict";
 
 const TEST_URL = 'data:text/html,<script>window.onbeforeunload=' +
                  'function(e){e.returnValue="?"}</script>';
 
 var contentWindow;
 var activeGroup;
 
+SpecialPowers.pushPrefEnv({"set": [["dom.require_user_interaction_for_beforeunload", false]]});
+
 Components.utils.import("resource://gre/modules/Promise.jsm", this);
 
 function test() {
   waitForExplicitFinish();
 
   newWindowWithTabView(win => {
     contentWindow = win.TabView.getContentWindow();
     activeGroup = contentWindow.GroupItems.getActiveGroupItem();
--- a/browser/config/tooltool-manifests/linux32/clang.manifest
+++ b/browser/config/tooltool-manifests/linux32/clang.manifest
@@ -1,12 +1,12 @@
 [
 {
-"clang_version": "r241406"
+"clang_version": "r247539"
 }, 
 {
-"size": 100307285, 
-"digest": "4d147d0072a928945fc1e938f39a5d0a9d3c676399c09e092c8750b2f973cdbbebda8d94d4d05805fae74a5c49c54263dc22b8b443c23c9a0ae830a261d3cf30", 
+"size": 93197192,
+"digest": "6ebd8994ac76cf6694c3d9054104219836f47578223c799cb9ba9669cfdee00112e9de56aea9d1e6d9d50ee94a201970555de19794b5fbb7546f58fdf8e59a99",
 "algorithm": "sha512", 
 "filename": "clang.tar.bz2",
 "unpack": true,
 }
 ]
--- a/browser/config/tooltool-manifests/linux64/clang.manifest
+++ b/browser/config/tooltool-manifests/linux64/clang.manifest
@@ -1,15 +1,15 @@
 [
 {
-"clang_version": "r241406"
+"clang_version": "r247539"
 }, 
 {
-"size": 83297360,
-"digest": "18e3deaf608b58a9153d647e1acbe6e93b7d6461bd4f86f24d13f4b6818b048a65063200d7acb100fa41eb5a7e66dfe6bdc89770c15c87e32a3e9a50d97094c6",
+"size": 93197192,
+"digest": "6ebd8994ac76cf6694c3d9054104219836f47578223c799cb9ba9669cfdee00112e9de56aea9d1e6d9d50ee94a201970555de19794b5fbb7546f58fdf8e59a99",
 "algorithm": "sha512",
 "filename": "clang.tar.xz",
 "unpack": true
 },
 {
 "size": 12057960,
 "digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e",
 "algorithm": "sha512",
--- a/browser/config/tooltool-manifests/linux64/clang.manifest.centos6
+++ b/browser/config/tooltool-manifests/linux64/clang.manifest.centos6
@@ -1,15 +1,15 @@
 [
 {
-"clang_version": "r241406"
-},
+"clang_version": "r247539"
+}, 
 {
-"size": 83297360,
-"digest": "18e3deaf608b58a9153d647e1acbe6e93b7d6461bd4f86f24d13f4b6818b048a65063200d7acb100fa41eb5a7e66dfe6bdc89770c15c87e32a3e9a50d97094c6",
+"size": 93197192,
+"digest": "6ebd8994ac76cf6694c3d9054104219836f47578223c799cb9ba9669cfdee00112e9de56aea9d1e6d9d50ee94a201970555de19794b5fbb7546f58fdf8e59a99",
 "algorithm": "sha512",
 "filename": "clang.tar.xz",
 "unpack": true
 },
 {
 "size": 12057960,
 "digest": "6105d6432943141cffb40020dc5ba3a793650bdeb3af9bd5e56d3796c5f03df9962a73e521646cd71fbfb5e266c1e74716ad722fb6055589dfb7d35175bca89e",
 "algorithm": "sha512",
--- a/browser/config/tooltool-manifests/macosx64/clang.manifest
+++ b/browser/config/tooltool-manifests/macosx64/clang.manifest
@@ -1,17 +1,17 @@
 [
 {
-"clang_version": "r241406"
-}, 
+"clang_version": "r247539"
+},
 {
-"size": 86465808,
-"digest": "947eaaf11ac8cbe12e11b48c8b052721e018d31fb8ce20f8bf14b117b6623c56513b1422d8d9c8011bc0b0b985ef74d8f181e7200c6d7a05d79a1bce0d75ddee",
-"algorithm": "sha512", 
-"filename": "clang.tar.bz2",
+"size": 97314461,
+"digest": "9a74670fa917f760a4767923485d5166bbd258a8023c8aeb899b8c4d22f2847be76508ac5f26d7d2193318a2bb368a71bc62888d1bfe9d81eb45329a60451aa4",
+"algorithm": "sha512",
+"filename": "clang.tar.xz",
 "unpack": true
 },
 {
 "size": 167175,
 "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
 "algorithm": "sha512",
 "filename": "sccache.tar.bz2",
 "unpack": true
--- a/browser/config/tooltool-manifests/macosx64/cross-releng.manifest
+++ b/browser/config/tooltool-manifests/macosx64/cross-releng.manifest
@@ -1,24 +1,17 @@
 [
 {
-"clang_version": "r241406"
+"clang_version": "r247539"
 }, 
 {
-"size": 100307285, 
-"digest": "4d147d0072a928945fc1e938f39a5d0a9d3c676399c09e092c8750b2f973cdbbebda8d94d4d05805fae74a5c49c54263dc22b8b443c23c9a0ae830a261d3cf30", 
-"algorithm": "sha512", 
-"filename": "clang.tar.bz2",
-"unpack": true
-},
-{
-"size": 80458572,
-"digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
-"algorithm": "sha512", 
-"filename": "gcc.tar.xz",
+"size": 93197192,
+"digest": "6ebd8994ac76cf6694c3d9054104219836f47578223c799cb9ba9669cfdee00112e9de56aea9d1e6d9d50ee94a201970555de19794b5fbb7546f58fdf8e59a99",
+"algorithm": "sha512",
+"filename": "clang.tar.xz",
 "unpack": true
 },
 {
 "size": 3008804, 
 "visibility": "public", 
 "digest": "ba6937f14f3d8b26dcb2d39490dee6b0a8afb60f672f5debb71d7b62c1ec52103201b4b1a3d258f945567de531384b36ddb2ce4aa73dc63d72305b11c146847c", 
 "algorithm": "sha512", 
 "unpack": true,
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -303,16 +303,26 @@ These should match what Safari and other
 <!ENTITY devToolbarToolsButton.tooltip     "Toggle developer tools">
 <!ENTITY devToolbarOtherToolsButton.label  "More Tools">
 
 <!ENTITY getMoreDevtoolsCmd.label        "Get More Tools">
 <!ENTITY getMoreDevtoolsCmd.accesskey    "M">
 
 <!ENTITY fileMenu.label         "File"> 
 <!ENTITY fileMenu.accesskey       "F">
+<!ENTITY newUserContext.label             "New Container Tab">
+<!ENTITY newUserContext.accesskey         "C">
+<!ENTITY userContextPersonal.label        "Personal">
+<!ENTITY userContextPersonal.accesskey    "P">
+<!ENTITY userContextWork.label            "Work">
+<!ENTITY userContextWork.accesskey        "W">
+<!ENTITY userContextBanking.label         "Banking">
+<!ENTITY userContextBanking.accesskey     "B">
+<!ENTITY userContextShopping.label        "Shopping">
+<!ENTITY userContextShopping.accesskey    "S">
 <!ENTITY newNavigatorCmd.label        "New Window">
 <!ENTITY newNavigatorCmd.key        "N">
 <!ENTITY newNavigatorCmd.accesskey      "N">
 <!ENTITY newPrivateWindow.label     "New Private Window">
 <!ENTITY newPrivateWindow.accesskey "W">
 <!ENTITY newNonRemoteWindow.label   "New Non-e10s Window">
 
 <!ENTITY editMenu.label         "Edit"> 
--- a/browser/locales/jar.mn
+++ b/browser/locales/jar.mn
@@ -147,18 +147,25 @@
     locale/browser/syncSetup.dtd                (%chrome/browser/syncSetup.dtd)
     locale/browser/syncSetup.properties         (%chrome/browser/syncSetup.properties)
     locale/browser/syncGenericChange.properties         (%chrome/browser/syncGenericChange.properties)
     locale/browser/syncKey.dtd                  (%chrome/browser/syncKey.dtd)
     locale/browser/syncQuota.dtd                (%chrome/browser/syncQuota.dtd)
     locale/browser/syncQuota.properties         (%chrome/browser/syncQuota.properties)
 #endif
 % resource search-plugins chrome://browser/locale/searchplugins/
+# little trick to make the test below work
+#define en_US en-US
+#if AB_CD == en_US
+    locale/browser/searchplugins/list.txt       (%searchplugins/list.txt)
+    locale/browser/searchplugins/               (%searchplugins/*.xml)
+#else
     locale/browser/searchplugins/list.txt       (.deps/generated_@AB_CD@/list.txt)
     locale/browser/searchplugins/               (.deps/generated_@AB_CD@/*.xml)
+#endif
 % locale browser-region @AB_CD@ %locale/browser-region/
     locale/browser-region/region.properties        (%chrome/browser-region/region.properties)
 # the following files are browser-specific overrides
     locale/browser/netError.dtd                (%chrome/overrides/netError.dtd)
     locale/browser/appstrings.properties       (%chrome/overrides/appstrings.properties)
     locale/browser/downloads/settingsChange.dtd  (%chrome/overrides/settingsChange.dtd)
 % override chrome://global/locale/netError.dtd chrome://browser/locale/netError.dtd
 % override chrome://global/locale/appstrings.properties chrome://browser/locale/appstrings.properties
--- a/browser/modules/PluginContent.jsm
+++ b/browser/modules/PluginContent.jsm
@@ -161,17 +161,17 @@ PluginContent.prototype = {
       tagMimetype = pluginElement.type;
     }
 
     if (this.isKnownPlugin(pluginElement)) {
       pluginTag = pluginHost.getPluginTagForType(pluginElement.actualType);
       pluginName = BrowserUtils.makeNicePluginName(pluginTag.name);
 
       // Convert this from nsIPluginTag so it can be serialized.
-      let properties = ["name", "description", "filename", "version", "enabledState"];
+      let properties = ["name", "description", "filename", "version", "enabledState", "niceName"];
       let pluginTagCopy = {};
       for (let prop of properties) {
         pluginTagCopy[prop] = pluginTag[prop];
       }
       pluginTag = pluginTagCopy;
 
       permissionString = pluginHost.getPermissionStringForType(pluginElement.actualType);
       fallbackType = pluginElement.defaultFallbackType;
@@ -417,16 +417,17 @@ PluginContent.prototype = {
 
       case "PluginDisabled":
         let manageLink = this.getPluginUI(plugin, "managePluginsLink");
         this.addLinkClickCallback(manageLink, "forwardCallback", "managePlugins");
         shouldShowNotification = true;
         break;
 
       case "PluginInstantiated":
+        Services.telemetry.getKeyedHistogramById('PLUGIN_ACTIVATION_COUNT').add(this._getPluginInfo(plugin).pluginTag.niceName);
         shouldShowNotification = true;
         break;
     }
 
     if (this._getPluginInfo(plugin).mimetype === "application/x-shockwave-flash") {
       this._recordFlashPluginTelemetry(eventType, plugin);
     }
 
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -1964,8 +1964,10 @@ chatbox {
   /* override toolkit/themes/linux/global/menu.css */
   -moz-padding-end: 0 !important;
   -moz-margin-end: 0 !important;
 }
 
 .browser-action-panel > .panel-arrowcontainer > .panel-arrowcontent {
   padding: 0;
 }
+
+%include ../shared/usercontext/usercontext.inc.css
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -137,16 +137,20 @@ browser.jar:
   skin/classic/browser/tabbrowser/tab-stroke-start.png      (tabbrowser/tab-stroke-start.png)
   skin/classic/browser/tabbrowser/tabDragIndicator.png      (tabbrowser/tabDragIndicator.png)
 
   skin/classic/browser/tabview/edit-light.png         (tabview/edit-light.png)
   skin/classic/browser/tabview/search.png             (tabview/search.png)
   skin/classic/browser/tabview/stack-expander.png     (tabview/stack-expander.png)
   skin/classic/browser/tabview/tabview.png            (tabview/tabview.png)
   skin/classic/browser/tabview/tabview.css            (tabview/tabview.css)
+  skin/classic/browser/usercontext/personal.svg       (../shared/usercontext/personal.svg)
+  skin/classic/browser/usercontext/work.svg           (../shared/usercontext/work.svg)
+  skin/classic/browser/usercontext/banking.svg        (../shared/usercontext/banking.svg)
+  skin/classic/browser/usercontext/shopping.svg       (../shared/usercontext/shopping.svg)
 #ifdef MOZ_SERVICES_SYNC
   skin/classic/browser/sync-16.png
   skin/classic/browser/sync-32.png
   skin/classic/browser/sync-bg.png
   skin/classic/browser/sync-128.png
   skin/classic/browser/sync-desktopIcon.png
   skin/classic/browser/sync-horizontalbar.png
   skin/classic/browser/sync-horizontalbar@2x.png
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -3650,8 +3650,10 @@ window > chatbox {
 #context-navigation > .menuitem-iconic {
   padding-left: 0;
   padding-right: 0;
 }
 
 .browser-action-panel > .panel-arrowcontainer > .panel-arrowcontent {
   padding: 0;
 }
+
+%include ../shared/usercontext/usercontext.inc.css
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -224,16 +224,20 @@ browser.jar:
   skin/classic/browser/tabbrowser/tabDragIndicator.png                   (tabbrowser/tabDragIndicator.png)
   skin/classic/browser/tabbrowser/tabDragIndicator@2x.png                (tabbrowser/tabDragIndicator@2x.png)
   skin/classic/browser/tabview/close.png                    (tabview/close.png)
   skin/classic/browser/tabview/edit-light.png               (tabview/edit-light.png)
   skin/classic/browser/tabview/search.png                   (tabview/search.png)
   skin/classic/browser/tabview/stack-expander.png           (tabview/stack-expander.png)
   skin/classic/browser/tabview/tabview.png                  (tabview/tabview.png)
   skin/classic/browser/tabview/tabview.css                  (tabview/tabview.css)
+  skin/classic/browser/usercontext/personal.svg             (../shared/usercontext/personal.svg)
+  skin/classic/browser/usercontext/work.svg                 (../shared/usercontext/work.svg)
+  skin/classic/browser/usercontext/banking.svg              (../shared/usercontext/banking.svg)
+  skin/classic/browser/usercontext/shopping.svg             (../shared/usercontext/shopping.svg)
 #ifdef MOZ_SERVICES_SYNC
   skin/classic/browser/sync-16.png
   skin/classic/browser/sync-32.png
   skin/classic/browser/sync-bg.png
   skin/classic/browser/sync-128.png
   skin/classic/browser/sync-desktopIcon.png
   skin/classic/browser/sync-horizontalbar.png
   skin/classic/browser/sync-horizontalbar@2x.png
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/usercontext/banking.svg
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<svg xmlns="http://www.w3.org/2000/svg"
+     width="32" height="32" viewBox="0 0 32 32">
+  <path fill="#7dc14c" d="M17.3857868,14.0527919 C14.2304569,13.0862944 13.4913706,12.4609137 13.4913706,11.0964467 C13.4913706,9.61827411 14.7137056,8.85076142 16.4192893,8.85076142 C17.9827411,8.85076142 19.3187817,9.33401015 20.5979695,10.4994924 L22.4456853,8.42436548 C21.1664975,7.20203046 19.3187819,6.26535905 17,6.00952148 L17,2 L15,2 L15,6.00952148 C12.3827412,6.43591742 9.76751269,8.53807107 9.76751269,11.3238579 C9.76751269,14.1664975 11.4730964,15.786802 15.4812183,17.0091371 C18.4375635,17.9187817 19.2335025,18.6294416 19.2335025,20.2213198 C19.2335025,22.0690355 17.7553299,23.035533 15.7370558,23.035533 C13.7756345,23.035533 12.2406091,22.3248731 10.9329949,21.1025381 L9,23.2345178 C10.4213198,24.6274112 12.8659899,25.8324934 15,26.0030518 L15,30 L17,30 L17,26.0030518 C20.7116753,25.4060974 22.9857868,22.893401 22.9857868,20.022335 C22.9857868,16.4690355 20.7116751,15.1045685 17.3857868,14.0527919 Z"/>
+</svg>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/usercontext/personal.svg
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<svg xmlns="http://www.w3.org/2000/svg"
+     width="32" height="32" viewBox="0 0 32 32">
+  <path fill="#00a7e0" d="M7.17741905,12 C7.10965537,12 7.041327,11.9953181 6.97243393,11.985018 C6.33263187,11.8918489 5.90515601,11.3862071 6.01809547,10.8552833 C7.41798011,4.26321358 12.2613889,2.57493207 15.0238882,2.15590491 C19.6448063,1.45690206 24.3408291,3.21541158 25.8344535,5.29743816 C26.1664955,5.76047488 25.9835336,6.35881757 25.4244832,6.63364321 C24.8654329,6.9098734 24.1437497,6.75583996 23.8122724,6.29327142 C22.8923805,5.01043967 19.1749781,3.51130562 15.4479759,4.07406612 C12.8080159,4.474834 9.43056132,6.03623689 8.33561323,11.1942506 C8.23453242,11.666651 7.73816348,12 7.17741905,12 Z M16.63127,26 C16.1452186,26 15.6509104,25.9658335 15.147795,25.8938767 C10.637921,25.257137 6.71207921,21.8114952 6.01575422,17.8807924 C5.91171832,17.2932317 6.33391695,16.7382846 6.95813239,16.6404441 C7.58454965,16.5343208 8.17298555,16.9406954 8.27757192,17.5272206 C8.80876054,20.5255916 11.9766264,23.26409 15.4885263,23.7610576 C17.3975027,24.02766 20.959494,23.8221432 23.3220449,19.3789425 C24.4625867,17.2331815 23.0049831,11.881462 19.9521622,9.34692739 C18.2380468,7.92384005 16.4573263,7.76905536 14.6628445,8.89499751 C13.26469,9.77142052 11.8070864,12.2857658 11.8665355,14.6287608 C11.9127737,16.4835887 12.8386382,17.9325598 14.6171568,18.9363308 C15.2210054,19.2764429 16.9411759,19.4933486 17.9424527,18.8296898 C18.7257495,18.3104622 18.9591422,17.2761485 18.6365758,15.7583267 C18.3822659,14.5650869 17.2219077,12.4452096 16.6664991,12.3711821 C16.6692513,12.3722175 16.4666841,12.4312324 16.1276041,12.9095636 C15.8545786,13.2936782 15.58981,14.7297074 15.9476054,15.3581643 C16.0142104,15.4761941 16.0725586,15.5465978 16.3202632,15.5465978 C16.9532859,15.5465978 17.46686,16.0290705 17.46686,16.6249139 C17.46686,17.2207573 16.9543868,17.7042653 16.3213641,17.7042653 C15.2644914,17.7042653 14.4140391,17.2336992 13.9268868,16.3774655 C13.1083609,14.9388479 13.5536787,12.6548678 14.2202791,11.7137354 C15.2540327,10.2564816 16.3631986,10.1151564 17.1123672,10.2564816 C19.7066595,10.7389543 20.8763754,15.2908666 20.8857331,15.3359043 C21.5303153,18.3648181 20.3594985,19.8665919 19.264094,20.593407 C17.4151172,21.8192603 14.6920186,21.493643 13.4380832,20.7859819 C10.3280151,19.0310652 9.62013053,16.497566 9.5744428,14.6805283 C9.49022326,11.3643051 11.4779146,8.30018945 13.391845,7.10021984 C16.0417332,5.43848454 18.9877658,5.66781436 21.4714167,7.72919442 C25.1176276,10.7565552 27.0871539,17.1229168 25.3746898,20.3433702 C23.4326862,23.9950465 20.2983981,26 16.63127,26 Z M16.0845157,30 C14.9348455,30 13.9050564,29.8557557 13.0394288,29.6610017 C10.2114238,29.0257442 7.58700058,27.4599412 6.18892823,25.5735955 C5.84440518,25.1078371 5.98426642,24.4803503 6.50105099,24.1700066 C7.01675554,23.8596629 7.71552172,23.986423 8.06112477,24.4507244 C9.89498097,26.9252176 15.9397944,29.9781448 22.2508301,26.1937972 C22.7676147,25.8844249 23.4658409,26.0087566 23.8109039,26.474515 C24.155427,26.9397877 24.0161057,27.5672745 23.4993212,27.8776182 C20.7987573,29.4963593 18.2315746,30 16.0845157,30 Z"/>
+</svg>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/usercontext/shopping.svg
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<svg xmlns="http://www.w3.org/2000/svg"
+     width="32" height="32" viewBox="0 0 32 32">
+  <path fill="#ee5195" fill-rule="evenodd" d="M20.8195396,14 L15.1804604,14 L15.1804604,14 L15.8471271,18 L20.1528729,18 L20.8195396,14 Z M22.8471271,14 L27.6125741,14 L27.6125741,14 L26.2792408,18 L22.1804604,18 L22.8471271,14 Z M21.1528729,12 L14.8471271,12 L14.8471271,12 L14.1804604,8 L21.8195396,8 L21.1528729,12 Z M23.1804604,12 L28.2792408,12 L28.2792408,12 L29.6125741,8 L23.8471271,8 L23.1804604,12 Z M13.1528729,14 L8.47703296,14 L10.077033,18 L10.077033,18 L13.8195396,18 L13.1528729,14 Z M12.8195396,12 L7.67703296,12 L6.07703296,8 L12.1528729,8 L12.8195396,12 L12.8195396,12 Z M31.7207592,8 L32,8 L32,6 L31,6 L5.27703296,6 L5.27703296,6 L4,2.8074176 L4,2 L3,2 L1,2 L0,2 L0,4 L1,4 L2.32296704,4 L9.78931928,22.6658806 L9.78931928,22.6658806 C8.71085924,23.3823847 8,24.6081773 8,26 C8,28.209139 9.790861,30 12,30 C14.209139,30 16,28.209139 16,26 C16,25.2714257 15.8052114,24.5883467 15.4648712,24 L22.5351288,24 C22.1947886,24.5883467 22,25.2714257 22,26 C22,28.209139 23.790861,30 26,30 C28.209139,30 30,28.209139 30,26 C30,23.790861 28.209139,22 26,22 L11.677033,22 L10.877033,20 L27,20 L28,20 L28,19.1622777 L31.7207592,8 L31.7207592,8 Z M26,28 C27.1045695,28 28,27.1045695 28,26 C28,24.8954305 27.1045695,24 26,24 C24.8954305,24 24,24.8954305 24,26 C24,27.1045695 24.8954305,28 26,28 Z M12,28 C13.1045695,28 14,27.1045695 14,26 C14,24.8954305 13.1045695,24 12,24 C10.8954305,24 10,24.8954305 10,26 C10,27.1045695 10.8954305,28 12,28 Z"/>
+</svg>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/usercontext/usercontext.inc.css
@@ -0,0 +1,17 @@
+/* User Context UI browser styles */
+
+#menu_newUserContextTabPersonal {
+  list-style-image: url("chrome://browser/skin/usercontext/personal.svg");
+}
+
+#menu_newUserContextTabWork {
+  list-style-image: url("chrome://browser/skin/usercontext/work.svg");
+}
+
+#menu_newUserContextTabBanking {
+  list-style-image: url("chrome://browser/skin/usercontext/banking.svg");
+}
+
+#menu_newUserContextTabShopping {
+  list-style-image: url("chrome://browser/skin/usercontext/shopping.svg");
+}
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/usercontext/work.svg
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<svg xmlns="http://www.w3.org/2000/svg"
+     width="32" height="32" viewBox="0 0 32 32">
+  <path fill="#f89c24" fill-rule="evenodd" d="M22,9.99887085 L21.635468,10 L29.0034652,10 C29.5538362,10 30,10.4449463 30,10.9933977 L30,27.0066023 C30,27.5552407 29.5601869,28 29.0034652,28 L2.99653482,28 C2.44616384,28 2,27.5550537 2,27.0066023 L2,10.9933977 C2,10.4447593 2.43981314,10 2.99653482,10 L8,10 L8,7.99922997 C8,5.79051625 10.0426627,4 12.5635454,4 L19.4364546,4 C21.9568311,4 24,5.79246765 24,7.99922997 L24,9.99267578 L22,9.99887085 L22,10 L10,10 L10,7.99922997 C10,6.89421235 11.0713286,6 12.3917227,6 L19.6082773,6 C20.9273761,6 22,6.89552665 22,7.99922997 L22,9.99887085 Z"/>
+</svg>
\ No newline at end of file
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -2837,8 +2837,10 @@ chatbox {
 
 @media not all and (-moz-os-version: windows-xp) {
 %include browser-aero.css
 }
 
 .browser-action-panel > .panel-arrowcontainer > .panel-arrowcontent {
   padding: 0;
 }
+
+%include ../shared/usercontext/usercontext.inc.css
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -249,16 +249,20 @@ browser.jar:
   skin/classic/browser/tabview/close.png                      (tabview/close.png)
   skin/classic/browser/tabview/edit-light.png                 (tabview/edit-light.png)
   skin/classic/browser/tabview/grain.png                      (tabview/grain.png)
   skin/classic/browser/tabview/search.png                     (tabview/search.png)
   skin/classic/browser/tabview/stack-expander.png             (tabview/stack-expander.png)
   skin/classic/browser/tabview/tabview.png                    (tabview/tabview.png)
   skin/classic/browser/tabview/tabview-inverted.png           (tabview/tabview-inverted.png)
   skin/classic/browser/tabview/tabview.css                    (tabview/tabview.css)
+  skin/classic/browser/usercontext/personal.svg               (../shared/usercontext/personal.svg)
+  skin/classic/browser/usercontext/work.svg                   (../shared/usercontext/work.svg)
+  skin/classic/browser/usercontext/banking.svg                (../shared/usercontext/banking.svg)
+  skin/classic/browser/usercontext/shopping.svg               (../shared/usercontext/shopping.svg)
 #ifdef MOZ_SERVICES_SYNC
   skin/classic/browser/sync-16.png
   skin/classic/browser/sync-32.png
   skin/classic/browser/sync-128.png
   skin/classic/browser/sync-bg.png
   skin/classic/browser/sync-desktopIcon.png
   skin/classic/browser/sync-horizontalbar.png
   skin/classic/browser/sync-horizontalbar@2x.png
--- a/build/autoconf/clang-plugin.m4
+++ b/build/autoconf/clang-plugin.m4
@@ -34,17 +34,17 @@ if test -n "$ENABLE_CLANG_PLUGIN"; then
     fi
     LLVM_CXXFLAGS=`$LLVMCONFIG --cxxflags`
     dnl The clang package we use on OSX is old, and its llvm-config doesn't
     dnl recognize --system-libs, so ask for that separately.  llvm-config's
     dnl failure here is benign, so we can ignore it if it happens.
     LLVM_LDFLAGS=`$LLVMCONFIG --system-libs | xargs`
     LLVM_LDFLAGS="$LLVM_LDFLAGS `$LLVMCONFIG --ldflags --libs core mc analysis asmparser mcparser bitreader option | xargs`"
 
-    if test "${OS_ARCH}" = "Darwin"; then
+    if test "${HOST_OS_ARCH}" = "Darwin"; then
         CLANG_LDFLAGS="-lclangFrontend -lclangDriver -lclangSerialization"
         CLANG_LDFLAGS="$CLANG_LDFLAGS -lclangParse -lclangSema -lclangAnalysis"
         CLANG_LDFLAGS="$CLANG_LDFLAGS -lclangEdit -lclangAST -lclangLex"
         CLANG_LDFLAGS="$CLANG_LDFLAGS -lclangBasic -lclangASTMatchers"
     else
         CLANG_LDFLAGS="-lclangASTMatchers"
     fi
 
--- a/build/autoconf/config.status.m4
+++ b/build/autoconf/config.status.m4
@@ -217,8 +217,20 @@ m4exit(1)
 define([AC_OUTPUT], [ifelse($#_$1, 1_, [MOZ_CREATE_CONFIG_STATUS()
 MOZ_RUN_CONFIG_STATUS()],
 [m4_fatal([Use CONFIGURE_SUBST_FILES in moz.build files to create substituted files.])]
 )])
 
 define([AC_CONFIG_HEADER],
 [m4_fatal([Use CONFIGURE_DEFINE_FILES in moz.build files to produce header files.])
 ])
+
+define([MOZ_BUILD_BACKEND],
+[
+BUILD_BACKENDS="RecursiveMake"
+
+MOZ_ARG_ENABLE_STRING(build-backend,
+[  --enable-build-backend={AndroidEclipse,CppEclipse,VisualStudio,FasterMake,CompileDB}
+                         Enable additional build backends],
+[ BUILD_BACKENDS="RecursiveMake `echo $enableval | sed 's/,/ /g'`"])
+
+AC_SUBST_LIST([BUILD_BACKENDS])
+])
--- a/build/clang-plugin/Makefile.in
+++ b/build/clang-plugin/Makefile.in
@@ -9,16 +9,27 @@ include $(topsrcdir)/config/config.mk
 
 # In the current moz.build world, we need to override essentially every
 # variable to limit ourselves to what we need to build the clang plugin.
 OS_CXXFLAGS := $(LLVM_CXXFLAGS) -fno-rtti -fno-exceptions
 OS_COMPILE_CXXFLAGS :=
 OS_LDFLAGS := $(LLVM_LDFLAGS) $(CLANG_LDFLAGS)
 DSO_LDOPTS := -shared
 
+ifeq ($(HOST_OS_ARCH)_$(OS_ARCH),Linux_Darwin)
+# Use the host compiler instead of the target compiler.
+CXX := $(HOST_CXX)
+# expandlibs doesn't know the distinction between host and target toolchains,
+# and on cross linux/darwin builds, the options to give to the linker for file
+# lists differ between both, so don't use file lists.
+EXPAND_MKSHLIB_ARGS :=
+# Don't pass OSX linker arguments.
+MOZ_FIX_LINK_PATHS :=
+endif
+
 # Use the default OS X deployment target to enable using the libc++ headers
 # correctly.  Note that the binary produced here is a host tool and doesn't need
 # to be distributed.
 MACOSX_DEPLOYMENT_TARGET :=
 
 # Temporarily relax the requirements for libstdc++ symbol versions on static
 # analysis plugin in order to use a recent clang by accepting libstdc++ from
 # gcc 4.4.0 (GLIBCXX_3.4.11).
--- a/build/clang-plugin/moz.build
+++ b/build/clang-plugin/moz.build
@@ -8,11 +8,16 @@ SharedLibrary('clang-plugin')
 
 SOURCES += [
     'clang-plugin.cpp',
 ]
 
 DISABLE_STL_WRAPPING = True
 NO_VISIBILITY_FLAGS = True
 
+# libc++ is required to build plugins against clang on OS X.
+if CONFIG['HOST_OS_ARCH'] == 'Darwin':
+    CXXFLAGS += ['-stdlib=libc++']
+    LDFLAGS += ['-lc++']
+
 DIRS += [
     'tests',
 ]
--- a/build/mach_bootstrap.py
+++ b/build/mach_bootstrap.py
@@ -126,16 +126,17 @@ MACH_MODULES = [
     'layout/tools/reftest/mach_commands.py',
     'python/mach_commands.py',
     'python/mach/mach/commands/commandinfo.py',
     'python/compare-locales/mach_commands.py',
     'python/mozboot/mozboot/mach_commands.py',
     'python/mozbuild/mozbuild/mach_commands.py',
     'python/mozbuild/mozbuild/backend/mach_commands.py',
     'python/mozbuild/mozbuild/compilation/codecomplete.py',
+    'python/mozbuild/mozbuild/compilation/database.py',
     'python/mozbuild/mozbuild/frontend/mach_commands.py',
     'services/common/tests/mach_commands.py',
     'testing/luciddream/mach_commands.py',
     'testing/mach_commands.py',
     'testing/taskcluster/mach_commands.py',
     'testing/marionette/mach_commands.py',
     'testing/mochitest/mach_commands.py',
     'testing/xpcshell/mach_commands.py',
--- a/build/macosx/cross-mozconfig.common
+++ b/build/macosx/cross-mozconfig.common
@@ -33,20 +33,19 @@ export TOOLCHAIN_PREFIX=$CROSS_CCTOOLS_P
 #TODO: bug 1184202 - would be nice if these could be detected with TOOLCHAIN_PREFIX automatically
 export AR=${TOOLCHAIN_PREFIX}ar
 export RANLIB=${TOOLCHAIN_PREFIX}ranlib
 export STRIP=${TOOLCHAIN_PREFIX}strip
 export OTOOL=${TOOLCHAIN_PREFIX}otool
 export GENISOIMAGE=$topsrcdir/genisoimage/genisoimage
 export DMG_TOOL=$topsrcdir/dmg/dmg
 
-# The system gcc installed on CentOS 6 is 4.4, which our
-# build system rejects.
-export HOST_CC="$topsrcdir/gcc/bin/gcc"
-export HOST_CXX="$topsrcdir/gcc/bin/g++"
+export HOST_CC="$topsrcdir/clang/bin/clang"
+export HOST_CXX="$topsrcdir/clang/bin/clang++"
+export HOST_CPP="$topsrcdir/clang/bin/clang -E"
 export HOST_LDFLAGS="-g"
 
 ac_add_options --target=x86_64-apple-darwin
 ac_add_options --with-macos-private-frameworks=$CROSS_PRIVATE_FRAMEWORKS
 
 . "$topsrcdir/build/mozconfig.cache"
 
 export SOCORRO_SYMBOL_UPLOAD_TOKEN_FILE=/builds/crash-stats-api.token
--- a/build/unix/build-clang/clang-trunk.json
+++ b/build/unix/build-clang/clang-trunk.json
@@ -1,10 +1,10 @@
 {
-    "llvm_revision": "241406",
+    "llvm_revision": "247539",
     "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/trunk",
     "clang_repo": "https://llvm.org/svn/llvm-project/cfe/trunk",
     "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/trunk",
     "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/trunk",
     "patches": {
         "macosx64": ["llvm-debug-frame.patch"],
         "linux64": ["llvm-debug-frame.patch"],
         "linux32": ["llvm-debug-frame.patch"]
--- a/config/Makefile.in
+++ b/config/Makefile.in
@@ -40,17 +40,19 @@ INSTALL_TARGETS += HEADERS
 endif
 
 include $(topsrcdir)/config/rules.mk
 
 ifndef JS_STANDALONE
 ifndef MOZ_PROFILE_USE
 # Generate a new buildid every time we "export" in config... that's only
 # supposed to be once per-build!
-export::
+export:: buildid
+
+buildid: FORCE
 ifdef MOZ_BUILD_DATE
 	printf '%s' $(MOZ_BUILD_DATE) > buildid
 else
 	$(PYTHON) $(topsrcdir)/toolkit/xre/make-platformini.py --print-buildid > buildid
 endif
 endif
 endif
 
new file mode 100644
--- /dev/null
+++ b/config/faster/rules.mk
@@ -0,0 +1,209 @@
+# 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/.
+
+# /!\ Please make sure to update the following comment when you touch this
+# file. Thank you /!\
+
+# The traditional Mozilla build system relied on going through the entire
+# build tree a number of times with different targets, and many of the
+# things happening at each step required other things happening in previous
+# steps without any documentation of those dependencies.
+#
+# This new build system tries to start afresh by establishing what files or
+# operations are needed for the build, and applying the necessary rules to
+# have those in place, relying on make dependencies to get them going.
+#
+# As of writing, only building non-compiled parts of Firefox is supported
+# here (a few other things are also left out). This is a starting point, with
+# the intent to grow this build system to make it more complete.
+#
+# This file contains rules and dependencies to get things working. The intent
+# is for a Makefile to define some dependencies and variables, and include
+# this file. What needs to be defined there, and ends up being generated by
+# python/mozbuild/mozbuild/backend/fastermake.py is the following:
+# - TOPSRCDIR/TOPOBJDIR, respectively the top source directory and the top
+#   object directory
+# - PYTHON, the path to the python executable
+# - ACDEFINES, which contains a set of -Dvar=name to be used during
+#   preprocessing
+# - MOZ_CHROME_FILE_FORMAT, which defines whether to use file copies or
+#   symbolic links
+# - JAR_MN_TARGETS, which defines the targets to use for jar manifest
+#   processing, see further below
+# - PP_TARGETS, which defines the file paths of preprocessed files, see
+#   further below
+# - INSTALL_MANIFESTS, which defines the list of base directories handled
+#   by install manifests, see further below
+# - MANIFEST_TARGETS, which defines the file paths of chrome manifests, see
+#   further below
+#
+# A convention used between this file and the Makefile including it is that
+# global Make variables names are uppercase, while "local" Make variables
+# applied to specific targets are lowercase.
+
+# Targets to be triggered for a default build
+default: $(addprefix install-,$(INSTALL_MANIFESTS))
+default: $(addprefix jar-,$(JAR_MN_TARGETS))
+
+# Explicit files to be built for a default build
+default: $(addprefix $(TOPOBJDIR)/,$(PP_TARGETS))
+default: $(addprefix $(TOPOBJDIR)/,$(MANIFEST_TARGETS))
+default: $(TOPOBJDIR)/dist/bin/greprefs.js
+default: $(TOPOBJDIR)/dist/bin/platform.ini
+default: $(TOPOBJDIR)/dist/bin/webapprt/webapprt.ini
+
+.PHONY: FORCE
+
+# Extra define to trigger some workarounds. We should strive to limit the
+# use of those. As of writing the only one is in
+# toolkit/content/buildconfig.html.
+ACDEFINES += -DBUILD_FASTER
+
+# Generic rule to fall back to the recursive make backend
+$(TOPOBJDIR)/%: FORCE
+	$(MAKE) -C $(dir $@) $(notdir $@)
+
+# Files under the faster/ sub-directory, however, are not meant to use the
+# fallback
+$(TOPOBJDIR)/faster/%: ;
+
+# And files under dist/ are meant to be copied from their first dependency
+# if there is no other rule.
+$(TOPOBJDIR)/dist/%:
+	rm -f $@
+	cp $< $@
+
+# Install files using install manifests
+#
+# The list of base directories is given in INSTALL_MANIFESTS. The
+# corresponding install manifests are named correspondingly, with forward
+# slashes replaced with underscores, and prefixed with `install_`. That is,
+# the install manifest for `dist/bin` would be `install_dist_bin`.
+$(addprefix install-,$(INSTALL_MANIFESTS)): install-%:
+	$(PYTHON) -m mozbuild.action.process_install_manifest \
+		--no-remove \
+		--no-remove-empty-directories \
+		$(TOPOBJDIR)/$* \
+		install_$(subst /,_,$*)
+
+# Preprocessed files. Ideally they would be using install manifests but
+# right now, it's not possible because of things like APP_BUILDID or
+# nsURLFormatter.js.
+# Things missing:
+# - XULPPFLAGS
+#
+# The list of preprocessed files is defined in PP_TARGETS. The list is
+# relative to TOPOBJDIR.
+# The source file for each of those preprocessed files is defined as a Make
+# dependency for the $(TOPOBJDIR)/path target. For example:
+#   PP_TARGETS = foo/bar
+#   $(TOPOBJDIR)/foo/bar: /path/to/source/for/foo/bar.in
+# The file name for the source doesn't need to be different.
+# Additionally, extra defines can be specified for a given preprocessing
+# by setting the `defines` variable specifically for the given target.
+# For example:
+#   $(TOPOBJDIR)/foo/bar: defines = -Dqux=foobar
+$(addprefix $(TOPOBJDIR)/,$(PP_TARGETS)): Makefile
+$(addprefix $(TOPOBJDIR)/,$(PP_TARGETS)): $(TOPOBJDIR)/%:
+	$(PYTHON) -m mozbuild.action.preprocessor \
+		--depend $(TOPOBJDIR)/faster/.deps/$(subst /,_,$*) \
+		-DAB_CD=en-US \
+		$(defines) \
+		$(ACDEFINES) \
+		$< \
+		-o $@
+
+# Include the dependency files from the above preprocessed files rule.
+$(foreach pp_target,$(PP_TARGETS), \
+	$(eval -include $(TOPOBJDIR)/faster/.deps/$(subst /,_,$(pp_target))))
+
+# Install files from jar manifests. Ideally, they would be using install
+# manifests, but the code to read jar manifests and emit appropriate
+# install manifests is not there yet.
+# Things missing:
+# - XULPPFLAGS
+# - DEFINES from config/config.mk
+# - L10N
+# - -e when USE_EXTENSION_MANIFEST is set in moz.build
+#
+# The list given in JAR_MN_TARGETS corresponds to the list of `jar-%` targets
+# to be processed, with the `jar-` prefix stripped.
+# The Makefile is expected to specify the source jar manifest as a dependency
+# to each target. There is no expectation that the `jar-%` target name matches
+# the source file name in any way. For example:
+#   JAR_MN_TARGETS = foo
+#   jar-foo: /path/to/some/jar.mn
+# Additionally, extra defines can be specified for the processing of the jar
+# manifest by settig the `defines` variable specifically for the given target.
+# For example:
+#   jar-foo: defines = -Dqux=foo
+# The default base path where files are going to be installed is `dist/bin`.
+# It is possible to use a different path by setting the `install_target`
+# variable. For example:
+#   jar-foo: install_target = dist/bin/foo
+# When processing jar manifests, relative paths given inside a jar manifest
+# can be resolved from an object directory. The default path for that object
+# directory is the translation of the jar manifest directory path from the
+# source directory to the object directory. That is, for
+# $(TOPSRCDIR)/path/to/jar.mn, the default would be $(TOPOBJDIR)/path/to.
+# In case a different path must be used for the object directory, the `objdir`
+# variable can be set. For example:
+#   jar-foo: objdir=/some/other/path
+jar-%: objdir ?= $(dir $(patsubst $(TOPSRCDIR)%,$(TOPOBJDIR)%,$<))
+jar-%: install_target ?= dist/bin
+jar-%:
+	cd $(objdir) && \
+	$(PYTHON) -m mozbuild.action.jar_maker \
+		-j $(TOPOBJDIR)/$(install_target)/chrome \
+		-t $(TOPSRCDIR) \
+		-f $(MOZ_CHROME_FILE_FORMAT) \
+		-c $(dir $<)/en-US \
+		-DAB_CD=en-US \
+		$(defines) \
+		$(ACDEFINES) \
+		$<
+
+# Create some chrome manifests
+# This rule is forced to run every time because it may be updating files that
+# already exit.
+#
+# The list of chrome manifests is given in MANIFEST_TARGETS, relative to the
+# top object directory. The content for those manifests is given in the
+# `content` variable associated with the target. For example:
+#   MANIFEST_TARGETS = foo
+#   $(TOPOBJDIR)/foo: content = "manifest foo.manifest" "manifest bar.manifest"
+$(addprefix $(TOPOBJDIR)/,$(MANIFEST_TARGETS)): FORCE
+	$(PYTHON) -m mozbuild.action.buildlist \
+		$@ \
+		$(content)
+
+# ============================================================================
+# Below is a set of additional dependencies and variables used to build things
+# that are not supported by data in moz.build.
+
+# GENERATED_FILES are not supported yet, and even if they were, the
+# dependencies are missing information.
+$(foreach p,linux osx windows,jar-browser-themes-$(p)-jar.mn): \
+jar-browser-themes-%-jar.mn: \
+	$(TOPOBJDIR)/browser/themes/%/tab-selected-end.svg \
+	$(TOPOBJDIR)/browser/themes/%/tab-selected-start.svg
+
+# These files are manually generated from
+# toolkit/components/urlformatter/Makefile.in and are force-included so that
+# the corresponding defines don't end up in the command lines.
+KEYS = mozilla_api_key google_api_key google_oauth_api_key bing_api_key
+$(TOPOBJDIR)/dist/bin/components/nsURLFormatter.js: \
+	$(addprefix $(TOPOBJDIR)/toolkit/components/urlformatter/, $(KEYS))
+$(TOPOBJDIR)/dist/bin/components/nsURLFormatter.js: defines += \
+	$(addprefix -I $(TOPOBJDIR)/toolkit/components/urlformatter/,$(KEYS))
+
+# Extra dependencies and/or definitions for preprocessed files.
+$(TOPOBJDIR)/dist/bin/application.ini: $(TOPOBJDIR)/config/buildid
+$(TOPOBJDIR)/dist/bin/application.ini: defines += \
+	-DAPP_BUILDID=$(shell cat $(TOPOBJDIR)/config/buildid)
+
+# Files to build with the recursive backend and simply copy
+$(TOPOBJDIR)/dist/bin/greprefs.js: $(TOPOBJDIR)/modules/libpref/greprefs.js
+$(TOPOBJDIR)/dist/bin/platform.ini: $(TOPOBJDIR)/toolkit/xre/platform.ini
+$(TOPOBJDIR)/dist/bin/webapprt/webapprt.ini: $(TOPOBJDIR)/webapprt/webapprt.ini
deleted file mode 100755
--- a/config/glibcversion.sh
+++ /dev/null
@@ -1,202 +0,0 @@
-#!/bin/sh
-#
-# 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/.
-
-##############################################################################
-##
-## Name:		glibcversion.sh - Print __GLIBC__ version if gnu libc 2 is 
-##              found.
-##
-## Description:	This script is needed by the mozilla build system.  It needs
-##              to determine whether the current platform (mostly the 
-##              various linux "platforms") are based on the gnu libc2.  This
-##              information is later used in mozilla to determine whether 
-##              gnu libc 2 specific "features" need to be handled, such
-##              as broken locales.
-##
-## Author:		Ramiro Estrugo <ramiro@netscape.com>
-##
-##############################################################################
-
-##
-## Command Line Flags Supported:
-##
-##  -g  | --is-glibc2:				Print True/False if detected __GLIBC__.
-##
-##  -v  | --print-version:			Print value of __GLIBC__ if found, or none.
-##
-##  -o  | --set-object-name:		Set object name for current system.
-##  -cc | --set-compiler:			Set compiler for building test program.
-##
-
-
-##
-## Constants
-##
-GLIBC_PROG_PREFIX=./get_glibc_info
-
-##
-## Defaults
-##
-GLIBC_PRINT_IS_GLIBC2=False
-
-GLIBC_PRINT_VERSION=False
-
-GLIBC_OBJECT_NAME=`uname`-`uname -r`
-GLIBC_CC=cc
-
-function glibc_usage()
-{
-echo
-echo "Usage:   `basename $0` [options]"
-echo
-echo "  -g,  --is-glibc2:          Print True/False if detected __GLIBC__."
-echo
-echo "  -v,  --print-version:      Print value of __GLIBC__ if found, or none."
-echo
-echo "  -o,  --set-object-name:    Set object name for current system."
-echo "  -cc, --set-compiler:       Set compiler for building test program."
-echo
-echo "  -h,  --help:               Print this blurb."
-echo
-echo "The default is '-v' if no options are given."
-echo
-}
-
-##
-## Parse the command line
-##
-while [ "$*" ]; do
-    case $1 in
-        -h | --help)
-            shift
-            glibc_usage
-			exit 0
-            ;;
-
-        -g | --is-glibc2)
-            shift
-            GLIBC_PRINT_IS_GLIBC2=True
-            ;;
-
-        -v | --print-version)
-            shift
-            GLIBC_PRINT_VERSION=True
-            ;;
-
-        -o | --set-object-name)
-            shift
-            GLIBC_OBJECT_NAME="$1"
-            shift
-            ;;
-
-        -cc | --set-compiler)
-            shift
-            GLIBC_CC="$1"
-            shift
-            ;;
-
-        -*)
-            echo "`basename $0`: invalid option '$1'"
-            shift
-            glibc_usage
-			exit 0
-            ;;
-    esac
-done
-
-##
-## Motif info program name
-##
-GLIBC_PROG="$GLIBC_PROG_PREFIX"_"$GLIBC_OBJECT_NAME"
-GLIBC_SRC="$GLIBC_PROG_PREFIX"_"$GLIBC_OBJECT_NAME.c"
-
-##
-## Cleanup the dummy test source/program
-##
-function glibc_cleanup()
-{
-	true
-
-#	rm -f $GLIBC_PROG
-#	rm -f $GLIBC_SRC
-
-}
-
-glibc_cleanup
-
-if [ ! -f $GLIBC_SRC ]
-then
-cat << EOF > $GLIBC_SRC
-#include <stdio.h>
-
-int main(int argc,char ** argv) 
-{
-#ifdef 	__GLIBC__
-	fprintf(stdout,"%d\n",__GLIBC__);
-#else
-	fprintf(stdout,"none\n");
-#endif
-
-	return 0;
-}
-EOF
-fi
-
-if [ ! -f $GLIBC_SRC ]
-then
-	echo
-	echo "Could not create test program source $GLIBC_SRC."
-	echo
-
-	glibc_cleanup
-
-	exit
-fi
-
-##
-## Compile the dummy test program if needed
-##
-if [ ! -x $GLIBC_PROG ]
-then
-	$GLIBC_CC -o $GLIBC_PROG $GLIBC_SRC
-fi
-
-if [ ! -x $GLIBC_PROG ]
-then
-	echo
-	echo "Could not create test program $GLIBC_PROG."
-	echo
-
-	glibc_cleanup
-
-	exit
-fi
-
-##
-## Execute the dummy test program
-##
-GLIBC_PROG_OUTPUT=`$GLIBC_PROG`
-
-##
-## -g | --is-glibc2
-##
-if [ "$GLIBC_PRINT_IS_GLIBC2" = "True" ]
-then
-	if [ "$GLIBC_PROG_OUTPUT" = "2" ]
-	then
-		echo True
-	else
-		echo False
-	fi
-
-	glibc_cleanup
-
-	exit 0
-fi
-
-echo $GLIBC_PROG_OUTPUT
-
-glibc_cleanup
--- a/configure.in
+++ b/configure.in
@@ -132,16 +132,18 @@ if test "$_conflict_files"; then
 EOF
   exit 1
   break
 fi
 MOZ_BUILD_ROOT=`pwd -W 2>/dev/null || pwd`
 
 MOZ_PYTHON
 
+MOZ_BUILD_BACKEND
+
 MOZ_DEFAULT_COMPILER
 
 COMPILE_ENVIRONMENT=1
 MOZ_ARG_DISABLE_BOOL(compile-environment,
 [  --disable-compile-environment
                           Disable compiler/library checks.],
     COMPILE_ENVIRONMENT= )
 AC_SUBST(COMPILE_ENVIRONMENT)
--- a/devtools/client/styleinspector/test/browser_ruleview_cubicbezier-revert-on-ESC.js
+++ b/devtools/client/styleinspector/test/browser_ruleview_cubicbezier-revert-on-ESC.js
@@ -44,17 +44,17 @@ function* testPressingEscapeRevertsChang
 
   info("Pressing ESCAPE to close the tooltip");
   let onHidden = bezierTooltip.tooltip.once("hidden");
   EventUtils.sendKey("ESCAPE", widget.parent.ownerDocument.defaultView);
   yield onHidden;
   yield ruleEditor.rule._applyingModifications;
 
   yield waitForComputedStyleProperty("body", null, "animation-timing-function",
-    "cubic-bezier(0, 0, 1, 1)");
+    "linear");
   is(propEditor.valueSpan.textContent, "linear",
     "Got expected property value.");
 }
 
 function* testPressingEscapeRevertsChangesAndDisables(view) {
   let ruleEditor = getRuleViewRuleEditor(view, 1);
   let propEditor = ruleEditor.rule.textProps[0].editor;
   let swatch = propEditor.valueSpan.querySelector(".ruleview-bezierswatch");
--- a/devtools/server/tests/browser/browser_navigateEvents.js
+++ b/devtools/server/tests/browser/browser_navigateEvents.js
@@ -5,16 +5,18 @@
 "use strict";
 
 const URL1 = MAIN_DOMAIN + "navigate-first.html";
 const URL2 = MAIN_DOMAIN + "navigate-second.html";
 
 var events = require("sdk/event/core");
 var client;
 
+SpecialPowers.pushPrefEnv({"set": [["dom.require_user_interaction_for_beforeunload", false]]});
+
 // State machine to check events order
 var i = 0;
 function assertEvent(event, data) {
   let x = 0;
   switch(i++) {
     case x++:
       is(event, "request", "Get first page load");
       is(data, URL1);
--- a/docshell/test/browser/browser_onbeforeunload_navigation.js
+++ b/docshell/test/browser/browser_onbeforeunload_navigation.js
@@ -1,16 +1,18 @@
 var contentWindow;
 var originalLocation;
 var currentTest = -1;
 var stayingOnPage = true;
 
 var TEST_PAGE = "http://mochi.test:8888/browser/docshell/test/browser/file_bug1046022.html";
 var TARGETED_PAGE = "data:text/html," + encodeURIComponent("<body>Shouldn't be seeing this</body>");
 
+SpecialPowers.pushPrefEnv({"set": [["dom.require_user_interaction_for_beforeunload", false]]});
+
 var loadExpected = TEST_PAGE;
 var testTab;
 
 var loadStarted = false;
 var tabStateListener = {
   onStateChange: function(webprogress, request, stateFlags, status) {
     let startDocumentFlags = Ci.nsIWebProgressListener.STATE_START |
                              Ci.nsIWebProgressListener.STATE_IS_DOCUMENT;
--- a/dom/animation/KeyframeEffect.cpp
+++ b/dom/animation/KeyframeEffect.cpp
@@ -5,58 +5,104 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/KeyframeEffect.h"
 #include "mozilla/dom/KeyframeEffectBinding.h"
 #include "mozilla/FloatingPoint.h"
 #include "AnimationCommon.h"
 #include "nsCSSPropertySet.h"
 #include "nsCSSProps.h" // For nsCSSProps::PropHasFlags
+#include "nsStyleUtil.h"
 
 namespace mozilla {
 
 void
 ComputedTimingFunction::Init(const nsTimingFunction &aFunction)
 {
   mType = aFunction.mType;
-  if (mType == nsTimingFunction::Function) {
+  if (nsTimingFunction::IsSplineType(mType)) {
     mTimingFunction.Init(aFunction.mFunc.mX1, aFunction.mFunc.mY1,
                          aFunction.mFunc.mX2, aFunction.mFunc.mY2);
   } else {
     mSteps = aFunction.mSteps;
+    mStepSyntax = aFunction.mStepSyntax;
   }
 }
 
 static inline double
 StepEnd(uint32_t aSteps, double aPortion)
 {
   MOZ_ASSERT(0.0 <= aPortion && aPortion <= 1.0, "out of range");
   uint32_t step = uint32_t(aPortion * aSteps); // floor
   return double(step) / double(aSteps);
 }
 
 double
 ComputedTimingFunction::GetValue(double aPortion) const
 {
+  if (HasSpline()) {
+    return mTimingFunction.GetSplineValue(aPortion);
+  }
+  if (mType == nsTimingFunction::Type::StepStart) {
+    // There are diagrams in the spec that seem to suggest this check
+    // and the bounds point should not be symmetric with StepEnd, but
+    // should actually step up at rather than immediately after the
+    // fraction points.  However, we rely on rounding negative values
+    // up to zero, so we can't do that.  And it's not clear the spec
+    // really meant it.
+    return 1.0 - StepEnd(mSteps, 1.0 - aPortion);
+  }
+  MOZ_ASSERT(mType == nsTimingFunction::Type::StepEnd, "bad type");
+  return StepEnd(mSteps, aPortion);
+}
+
+int32_t
+ComputedTimingFunction::Compare(const ComputedTimingFunction& aRhs) const
+{
+  if (mType != aRhs.mType) {
+    return int32_t(mType) - int32_t(aRhs.mType);
+  }
+
+  if (mType == nsTimingFunction::Type::CubicBezier) {
+    int32_t order = mTimingFunction.Compare(aRhs.mTimingFunction);
+    if (order != 0) {
+      return order;
+    }
+  } else if (mType == nsTimingFunction::Type::StepStart ||
+             mType == nsTimingFunction::Type::StepEnd) {
+    if (mSteps != aRhs.mSteps) {
+      return int32_t(mSteps) - int32_t(aRhs.mSteps);
+    }
+    if (mStepSyntax != aRhs.mStepSyntax) {
+      return int32_t(mStepSyntax) - int32_t(aRhs.mStepSyntax);
+    }
+  }
+
+  return 0;
+}
+
+void
+ComputedTimingFunction::AppendToString(nsAString& aResult) const
+{
   switch (mType) {
-    case nsTimingFunction::Function:
-      return mTimingFunction.GetSplineValue(aPortion);
-    case nsTimingFunction::StepStart:
-      // There are diagrams in the spec that seem to suggest this check
-      // and the bounds point should not be symmetric with StepEnd, but
-      // should actually step up at rather than immediately after the
-      // fraction points.  However, we rely on rounding negative values
-      // up to zero, so we can't do that.  And it's not clear the spec
-      // really meant it.
-      return 1.0 - StepEnd(mSteps, 1.0 - aPortion);
+    case nsTimingFunction::Type::CubicBezier:
+      nsStyleUtil::AppendCubicBezierTimingFunction(mTimingFunction.X1(),
+                                                   mTimingFunction.Y1(),
+                                                   mTimingFunction.X2(),
+                                                   mTimingFunction.Y2(),
+                                                   aResult);
+      break;
+    case nsTimingFunction::Type::StepStart:
+    case nsTimingFunction::Type::StepEnd:
+      nsStyleUtil::AppendStepsTimingFunction(mType, mSteps, mStepSyntax,
+                                             aResult);
+      break;
     default:
-      MOZ_ASSERT(false, "bad type");
-      // fall through
-    case nsTimingFunction::StepEnd:
-      return StepEnd(mSteps, aPortion);
+      nsStyleUtil::AppendCubicBezierKeywordTimingFunction(mType, aResult);
+      break;
   }
 }
 
 // In the Web Animations model, the iteration progress can be outside the range
 // [0.0, 1.0] but it shouldn't be Infinity.
 const double ComputedTiming::kNullProgress = PositiveInfinity<double>();
 
 namespace dom {
@@ -451,10 +497,121 @@ KeyframeEffectReadOnly::SetIsRunningOnCo
 void
 KeyframeEffectReadOnly::ResetIsRunningOnCompositor()
 {
   for (bool& isPropertyRunningOnCompositor : mIsPropertyRunningOnCompositor) {
     isPropertyRunningOnCompositor = false;
   }
 }
 
+struct KeyframeValueEntry
+{
+  float mOffset;
+  nsCSSProperty mProperty;
+  nsString mValue;
+  const ComputedTimingFunction* mTimingFunction;
+
+  bool operator==(const KeyframeValueEntry& aRhs) const
+  {
+    NS_ASSERTION(mOffset != aRhs.mOffset || mProperty != aRhs.mProperty,
+                 "shouldn't have duplicate (offset, property) pairs");
+    return false;
+  }
+
+  bool operator<(const KeyframeValueEntry& aRhs) const
+  {
+    NS_ASSERTION(mOffset != aRhs.mOffset || mProperty != aRhs.mProperty,
+                 "shouldn't have duplicate (offset, property) pairs");
+
+    // First, sort by offset.
+    if (mOffset != aRhs.mOffset) {
+      return mOffset < aRhs.mOffset;
+    }
+
+    // Second, by timing function.
+    int32_t order = mTimingFunction->Compare(*aRhs.mTimingFunction);
+    if (order != 0) {
+      return order < 0;
+    }
+
+    // Last, by property IDL name.
+    return nsCSSProps::PropertyIDLNameSortPosition(mProperty) <
+           nsCSSProps::PropertyIDLNameSortPosition(aRhs.mProperty);
+  }
+};
+
+void
+KeyframeEffectReadOnly::GetFrames(JSContext*& aCx,
+                                  nsTArray<JSObject*>& aResult,
+                                  ErrorResult& aRv)
+{
+  // Collect tuples of the form (offset, property, value, easing) from
+  // mProperties, then sort them so we can generate one ComputedKeyframe per
+  // offset/easing pair.  We sort secondarily by property IDL name so that we
+  // have a uniform order that we set properties on the ComputedKeyframe
+  // object.
+  nsAutoTArray<KeyframeValueEntry,4> entries;
+  for (const AnimationProperty& property : mProperties) {
+    if (property.mSegments.IsEmpty()) {
+      continue;
+    }
+    for (size_t i = 0, n = property.mSegments.Length(); i < n; i++) {
+      const AnimationPropertySegment& segment = property.mSegments[i];
+      KeyframeValueEntry* entry = entries.AppendElement();
+      entry->mOffset = segment.mFromKey;
+      entry->mProperty = property.mProperty;
+      entry->mTimingFunction = &segment.mTimingFunction;
+      StyleAnimationValue::UncomputeValue(property.mProperty,
+                                          segment.mFromValue,
+                                          entry->mValue);
+    }
+    const AnimationPropertySegment& segment = property.mSegments.LastElement();
+    KeyframeValueEntry* entry = entries.AppendElement();
+    entry->mOffset = segment.mToKey;
+    entry->mProperty = property.mProperty;
+    // We don't have the an appropriate animation-timing-function value to use,
+    // either from the element or from the 100% keyframe, so we just set it to
+    // the animation-timing-value value used on the previous segment.
+    entry->mTimingFunction = &segment.mTimingFunction;
+    StyleAnimationValue::UncomputeValue(property.mProperty,
+                                        segment.mToValue,
+                                        entry->mValue);
+  }
+  entries.Sort();
+
+  for (size_t i = 0, n = entries.Length(); i < n; ) {
+    // Create a JS object with the explicit ComputedKeyframe dictionary members.
+    ComputedKeyframe keyframeDict;
+    keyframeDict.mOffset.SetValue(entries[i].mOffset);
+    keyframeDict.mComputedOffset.Construct(entries[i].mOffset);
+    keyframeDict.mEasing.Truncate();
+    entries[i].mTimingFunction->AppendToString(keyframeDict.mEasing);
+    keyframeDict.mComposite.SetValue(CompositeOperation::Replace);
+
+    JS::Rooted<JS::Value> keyframeValue(aCx);
+    if (!ToJSValue(aCx, keyframeDict, &keyframeValue)) {
+      aRv.Throw(NS_ERROR_FAILURE);
+      return;
+    }
+
+    JS::Rooted<JSObject*> keyframe(aCx, &keyframeValue.toObject());
+
+    // Set the property name/value pairs on the JS object.
+    do {
+      const KeyframeValueEntry& entry = entries[i];
+      const char* name = nsCSSProps::PropertyIDLName(entry.mProperty);
+      JS::Rooted<JS::Value> value(aCx);
+      if (!ToJSValue(aCx, entry.mValue, &value) ||
+          !JS_DefineProperty(aCx, keyframe, name, value, JSPROP_ENUMERATE)) {
+        aRv.Throw(NS_ERROR_FAILURE);
+        return;
+      }
+      ++i;
+    } while (i < n &&
+             entries[i].mOffset == entries[i - 1].mOffset &&
+             *entries[i].mTimingFunction == *entries[i - 1].mTimingFunction);
+
+    aResult.AppendElement(keyframe);
+  }
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/animation/KeyframeEffect.h
+++ b/dom/animation/KeyframeEffect.h
@@ -14,16 +14,17 @@
 #include "nsWrapperCache.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/LayerAnimationInfo.h" // LayerAnimations::kRecords
 #include "mozilla/StickyTimeDuration.h"
 #include "mozilla/StyleAnimationValue.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/dom/AnimationEffectReadOnly.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/dom/KeyframeBinding.h"
 #include "mozilla/dom/Nullable.h"
 #include "nsSMILKeySpline.h"
 #include "nsStyleStruct.h" // for nsTimingFunction
 
 struct JSContext;
 class nsCSSPropertySet;
 
 namespace mozilla {
@@ -104,38 +105,45 @@ struct ComputedTiming
     AnimationPhase_After
   } mPhase;
 };
 
 class ComputedTimingFunction
 {
 public:
   typedef nsTimingFunction::Type Type;
+  typedef nsTimingFunction::StepSyntax StepSyntax;
   void Init(const nsTimingFunction &aFunction);
   double GetValue(double aPortion) const;
   const nsSMILKeySpline* GetFunction() const {
-    NS_ASSERTION(mType == nsTimingFunction::Function, "Type mismatch");
+    NS_ASSERTION(HasSpline(), "Type mismatch");
     return &mTimingFunction;
   }
   Type GetType() const { return mType; }
+  bool HasSpline() const { return nsTimingFunction::IsSplineType(mType); }
   uint32_t GetSteps() const { return mSteps; }
+  StepSyntax GetStepSyntax() const { return mStepSyntax; }
   bool operator==(const ComputedTimingFunction& aOther) const {
     return mType == aOther.mType &&
-           (mType == nsTimingFunction::Function ?
+           (HasSpline() ?
             mTimingFunction == aOther.mTimingFunction :
-            mSteps == aOther.mSteps);
+            (mSteps == aOther.mSteps &&
+             mStepSyntax == aOther.mStepSyntax));
   }
   bool operator!=(const ComputedTimingFunction& aOther) const {
     return !(*this == aOther);
   }
+  int32_t Compare(const ComputedTimingFunction& aRhs) const;
+  void AppendToString(nsAString& aResult) const;
 
 private:
   Type mType;
   nsSMILKeySpline mTimingFunction;
   uint32_t mSteps;
+  StepSyntax mStepSyntax;
 };
 
 struct AnimationPropertySegment
 {
   float mFromKey, mToKey;
   StyleAnimationValue mFromValue, mToValue;
   ComputedTimingFunction mTimingFunction;
 
@@ -220,16 +228,19 @@ public:
     // Currently we never return animations from the API whose effect
     // targets a pseudo-element so this should never be called when
     // mPseudoType is not 'none' (see bug 1174575).
     MOZ_ASSERT(mPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement,
                "Requesting the target of a KeyframeEffect that targets a"
                " pseudo-element is not yet supported.");
     return mTarget;
   }
+  void GetFrames(JSContext*& aCx,
+                 nsTArray<JSObject*>& aResult,
+                 ErrorResult& aRv);
 
   // Temporary workaround to return both the target element and pseudo-type
   // until we implement PseudoElement (bug 1174575).
   void GetTarget(Element*& aTarget,
                  nsCSSPseudoElements::Type& aPseudoType) const {
     aTarget = mTarget;
     aPseudoType = mPseudoType;
   }
--- a/dom/animation/moz.build
+++ b/dom/animation/moz.build
@@ -25,13 +25,16 @@ UNIFIED_SOURCES += [
     'Animation.cpp',
     'AnimationEffectReadOnly.cpp',
     'AnimationTimeline.cpp',
     'DocumentTimeline.cpp',
     'KeyframeEffect.cpp',
     'PendingAnimationTracker.cpp',
 ]
 
-FINAL_LIBRARY = 'xul'
-
 LOCAL_INCLUDES += [
     '/dom/base',
 ]
+
+FINAL_LIBRARY = 'xul'
+
+if CONFIG['GNU_CXX']:
+    CXXFLAGS += ['-Wshadow']
new file mode 100644
--- /dev/null
+++ b/dom/animation/test/css-animations/file_keyframeeffect-getframes.html
@@ -0,0 +1,455 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="../testcommon.js"></script>
+<style>
+@keyframes anim-empty { }
+
+@keyframes anim-empty-frames {
+  from { }
+  to   { }
+}
+
+@keyframes anim-only-timing {
+  from { animation-timing-function: linear; }
+  to   { }
+}
+
+@keyframes anim-only-non-animatable {
+  from { display: none; }
+  to   { display: inline; }
+}
+
+@keyframes anim-simple {
+  from { color: black; }
+  to   { color: white; }
+}
+
+@keyframes anim-simple-timing {
+  from { color: black; animation-timing-function: linear; }
+  50%  { color: blue;  animation-timing-function: ease-in-out; }
+  to   { color: white; animation-timing-function: step-end; }
+}
+
+@keyframes anim-simple-timing-some {
+  from { color: black; animation-timing-function: linear; }
+  50%  { color: blue; }
+  to   { color: white; }
+}
+
+@keyframes anim-simple-shorthand {
+  from { margin: 8px; }
+  to   { margin: 16px; }
+}
+
+@keyframes anim-omit-to {
+  from { color: blue; }
+}
+
+@keyframes anim-omit-from {
+  to   { color: blue; }
+}
+
+@keyframes anim-omit-from-to {
+  50%  { color: blue; }
+}
+
+@keyframes anim-different-props {
+  from { color: black; margin-top: 8px; }
+  25%  { color: blue; }
+  75%  { margin-top: 12px; }
+  to   { color: white; margin-top: 16px }
+}
+
+@keyframes anim-different-props-and-easing {
+  from { color: black; margin-top: 8px; animation-timing-function: linear; }
+  25%  { color: blue; animation-timing-function: step-end; }
+  75%  { margin-top: 12px; animation-timing-function: ease-in; }
+  to   { color: white; margin-top: 16px }
+}
+
+@keyframes anim-merge-offset {
+  from { color: black; }
+  to   { color: white; }
+  from { margin-top: 8px; }
+  to   { margin-top: 16px; }
+}
+
+@keyframes anim-merge-offset-and-easing {
+  from { color: black; animation-timing-function: step-end; }
+  to   { color: white; }
+  from { margin-top: 8px; animation-timing-function: linear; }
+  to   { margin-top: 16px; }
+  from { font-size: 16px; animation-timing-function: step-end; }
+  to   { font-size: 32px; }
+  from { padding-left: 2px; animation-timing-function: linear; }
+  to   { padding-left: 4px; }
+}
+
+@keyframes anim-no-merge-equiv-easing {
+  from { margin-top: 0px; animation-timing-function: steps(1, end); }
+  from { margin-right: 0px; animation-timing-function: step-end; }
+  from { margin-bottom: 0px; animation-timing-function: steps(1); }
+  50%  { margin-top: 10px; animation-timing-function: step-end; }
+  50%  { margin-right: 10px; animation-timing-function: step-end; }
+  50%  { margin-bottom: 10px; animation-timing-function: step-end; }
+  to   { margin-top: 20px; margin-right: 20px; margin-bottom: 20px; }
+}
+</style>
+<body>
+<script>
+"use strict";
+
+function getFrames(e) {
+  return e.getAnimations()[0].effect.getFrames();
+}
+
+function assert_frames_equal(a, b, name) {
+  assert_equals(Object.keys(a).sort().toString(),
+                Object.keys(b).sort().toString(),
+                "properties on " + name);
+  for (var p in a) {
+    assert_equals(a[p], b[p], "value for '" + p + "' on " + name);
+  }
+}
+
+// animation-timing-function values to test with, where the value
+// is exactly the same as its serialization, sorted by the order
+// getFrames() will group frames with the same easing function
+// together (by nsTimingFunction::Compare).
+const kTimingFunctionValues = [
+  "ease",
+  "linear",
+  "ease-in",
+  "ease-out",
+  "ease-in-out",
+  "step-start",
+  "steps(1, start)",
+  "steps(2, start)",
+  "step-end",
+  "steps(1)",
+  "steps(1, end)",
+  "steps(2)",
+  "steps(2, end)",
+  "cubic-bezier(0, 0, 1, 1)",
+  "cubic-bezier(0, 0.25, 0.75, 1)",
+];
+
+test(function(t) {
+  var div = addDiv(t);
+  var frames;
+
+  div.style.animation = 'anim-empty 100s';
+  assert_equals(getFrames(div).length, 0,
+                "number of frames with empty @keyframes");
+
+  div.style.animation = 'anim-empty-frames 100s';
+  assert_equals(getFrames(div).length, 0,
+                "number of frames when @keyframes has empty keyframes");
+
+  div.style.animation = 'anim-only-timing 100s';
+  assert_equals(getFrames(div).length, 0,
+                "number of frames when @keyframes only has keyframes with " +
+                "animation-timing-function");
+
+  div.style.animation = 'anim-only-non-animatable 100s';
+  assert_equals(getFrames(div).length, 0,
+                "number of frames when @keyframes only has frames with " +
+                "non-animatable properties");
+}, 'KeyframeEffectReadOnly.getFrames() returns no frames for various kinds ' +
+   'of empty enimations');
+
+test(function(t) {
+  var div = addDiv(t);
+
+  div.style.animation = 'anim-simple 100s';
+  var frames = getFrames(div);
+
+  assert_equals(frames.length, 2, "number of frames");
+
+  var expected = [
+    { offset: 0, computedOffset: 0, easing: "ease", composite: "replace",
+      color: "rgb(0, 0, 0)" },
+    { offset: 1, computedOffset: 1, easing: "ease", composite: "replace",
+      color: "rgb(255, 255, 255)" },
+  ];
+
+  for (var i = 0; i < frames.length; i++) {
+    assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
+  }
+}, 'KeyframeEffectReadOnly.getFrames() returns expected frames for a simple ' +
+   'animation');
+
+test(function(t) {
+  kTimingFunctionValues.forEach(function(easing) {
+    var div = addDiv(t);
+
+    div.style.animation = 'anim-simple 100s ' + easing;
+    var frames = getFrames(div);
+
+    assert_equals(frames.length, 2, "number of frames");
+
+    for (var i = 0; i < frames.length; i++) {
+      assert_equals(frames[i].easing, easing,
+                    "value for 'easing' on ComputedKeyframe #" + i);
+    }
+  });
+}, 'KeyframeEffectReadOnly.getFrames() returns frames with expected easing ' +
+   'values, when the easing comes from animation-timing-function on the ' +
+   'element');
+
+test(function(t) {
+  var div = addDiv(t);
+
+  div.style.animation = 'anim-simple-timing 100s';
+  var frames = getFrames(div);
+
+  assert_equals(frames.length, 3, "number of frames");
+  assert_equals(frames[0].easing, "linear",
+                "value of 'easing' on ComputedKeyframe #0");
+  assert_equals(frames[1].easing, "ease-in-out",
+                "value of 'easing' on ComputedKeyframe #1");
+  assert_equals(frames[2].easing, "ease-in-out",
+                "value of 'easing' on ComputedKeyframe #2");
+}, 'KeyframeEffectReadOnly.getFrames() returns frames with expected easing ' +
+   'values, when the easing is specified on each keyframe');
+
+test(function(t) {
+  var div = addDiv(t);
+
+  div.style.animation = 'anim-simple-timing-some 100s step-start';
+  var frames = getFrames(div);
+
+  assert_equals(frames.length, 3, "number of frames");
+  assert_equals(frames[0].easing, "linear",
+                "value of 'easing' on ComputedKeyframe #0");
+  assert_equals(frames[1].easing, "step-start",
+                "value of 'easing' on ComputedKeyframe #1");
+  assert_equals(frames[2].easing, "step-start",
+                "value of 'easing' on ComputedKeyframe #2");
+}, 'KeyframeEffectReadOnly.getFrames() returns frames with expected easing ' +
+   'values, when the easing is specified on some keyframes');
+
+test(function(t) {
+  var div = addDiv(t);
+
+  div.style.animation = 'anim-simple-shorthand 100s';
+  var frames = getFrames(div);
+
+  assert_equals(frames.length, 2, "number of frames");
+
+  var expected = [
+    { offset: 0, computedOffset: 0, easing: "ease", composite: "replace",
+      marginTop: "8px", marginRight: "8px",
+      marginBottom: "8px", marginLeft: "8px" },
+    { offset: 1, computedOffset: 1, easing: "ease", composite: "replace",
+      marginTop: "16px", marginRight: "16px",
+      marginBottom: "16px", marginLeft: "16px" },
+  ];
+
+  for (var i = 0; i < frames.length; i++) {
+    assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
+  }
+}, 'KeyframeEffectReadOnly.getFrames() returns expected frames for a simple ' +
+   'animation that specifies a single shorthand property');
+
+test(function(t) {
+  var div = addDiv(t);
+
+  div.style.animation = 'anim-omit-to 100s';
+  div.style.color = 'white';
+  var frames = getFrames(div);
+
+  assert_equals(frames.length, 2, "number of frames");
+
+  var expected = [
+    { offset: 0, computedOffset: 0, easing: "ease", composite: "replace",
+      color: "rgb(0, 0, 255)" },
+    { offset: 1, computedOffset: 1, easing: "ease", composite: "replace",
+      color: "rgb(255, 255, 255)" },
+  ];
+
+  for (var i = 0; i < frames.length; i++) {
+    assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
+  }
+}, 'KeyframeEffectReadOnly.getFrames() returns expected frames for an ' +
+   'animation with a 0% keyframe and no 100% keyframe');
+
+test(function(t) {
+  var div = addDiv(t);
+
+  div.style.animation = 'anim-omit-from 100s';
+  div.style.color = 'white';
+  var frames = getFrames(div);
+
+  assert_equals(frames.length, 2, "number of frames");
+
+  var expected = [
+    { offset: 0, computedOffset: 0, easing: "ease", composite: "replace",
+      color: "rgb(255, 255, 255)" },
+    { offset: 1, computedOffset: 1, easing: "ease", composite: "replace",
+      color: "rgb(0, 0, 255)" },
+  ];
+
+  for (var i = 0; i < frames.length; i++) {
+    assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
+  }
+}, 'KeyframeEffectReadOnly.getFrames() returns expected frames for an ' +
+   'animation with a 100% keyframe and no 0% keyframe');
+
+test(function(t) {
+  var div = addDiv(t);
+
+  div.style.animation = 'anim-omit-from-to 100s';
+  div.style.color = 'white';
+  var frames = getFrames(div);
+
+  assert_equals(frames.length, 3, "number of frames");
+
+  var expected = [
+    { offset: 0, computedOffset: 0, easing: "ease", composite: "replace",
+      color: "rgb(255, 255, 255)" },
+    { offset: 0.5, computedOffset: 0.5, easing: "ease", composite: "replace",
+      color: "rgb(0, 0, 255)" },
+    { offset: 1, computedOffset: 1, easing: "ease", composite: "replace",
+      color: "rgb(255, 255, 255)" },
+  ];
+
+  for (var i = 0; i < frames.length; i++) {
+    assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
+  }
+}, 'KeyframeEffectReadOnly.getFrames() returns expected frames for an ' +
+   'animation with no 0% or 100% keyframe but with a 50% keyframe');
+
+test(function(t) {
+  var div = addDiv(t);
+
+  div.style.animation = 'anim-different-props 100s';
+  var frames = getFrames(div);
+
+  assert_equals(frames.length, 4, "number of frames");
+
+  var expected = [
+    { offset: 0, computedOffset: 0, easing: "ease", composite: "replace",
+      color: "rgb(0, 0, 0)", marginTop: "8px" },
+    { offset: 0.25, computedOffset: 0.25, easing: "ease", composite: "replace",
+      color: "rgb(0, 0, 255)" },
+    { offset: 0.75, computedOffset: 0.75, easing: "ease", composite: "replace",
+      marginTop: "12px" },
+    { offset: 1, computedOffset: 1, easing: "ease", composite: "replace",
+      color: "rgb(255, 255, 255)", marginTop: "16px" },
+  ];
+
+  for (var i = 0; i < frames.length; i++) {
+    assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
+  }
+}, 'KeyframeEffectReadOnly.getFrames() returns expected frames for an ' +
+   'animation with different properties on different keyframes, all ' +
+   'with the same easing function');
+
+test(function(t) {
+  var div = addDiv(t);
+
+  div.style.animation = 'anim-different-props-and-easing 100s';
+  var frames = getFrames(div);
+
+  assert_equals(frames.length, 5, "number of frames");
+
+  var expected = [
+    { offset: 0, computedOffset: 0, easing: "linear", composite: "replace",
+      color: "rgb(0, 0, 0)", marginTop: "8px" },
+    { offset: 0.25, computedOffset: 0.25, easing: "step-end", composite: "replace",
+      color: "rgb(0, 0, 255)" },
+    { offset: 0.75, computedOffset: 0.75, easing: "ease-in", composite: "replace",
+      marginTop: "12px" },
+    { offset: 1, computedOffset: 1, easing: "ease-in", composite: "replace",
+      marginTop: "16px" },
+    { offset: 1, computedOffset: 1, easing: "step-end", composite: "replace",
+      color: "rgb(255, 255, 255)", },
+  ];
+
+  for (var i = 0; i < frames.length; i++) {
+    assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
+  }
+}, 'KeyframeEffectReadOnly.getFrames() returns expected frames for an ' +
+   'animation with different properties on different keyframes, with ' +
+   'a different easing function on each');
+
+test(function(t) {
+  var div = addDiv(t);
+
+  div.style.animation = 'anim-merge-offset 100s';
+  var frames = getFrames(div);
+
+  assert_equals(frames.length, 2, "number of frames");
+
+  var expected = [
+    { offset: 0, computedOffset: 0, easing: "ease", composite: "replace",
+      color: "rgb(0, 0, 0)", marginTop: "8px" },
+    { offset: 1, computedOffset: 1, easing: "ease", composite: "replace",
+      color: "rgb(255, 255, 255)", marginTop: "16px" },
+  ];
+
+  for (var i = 0; i < frames.length; i++) {
+    assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
+  }
+}, 'KeyframeEffectReadOnly.getFrames() returns expected frames for an ' +
+   'animation with multiple keyframes for the same time, and all with ' +
+   'the same easing function');
+
+test(function(t) {
+  var div = addDiv(t);
+
+  div.style.animation = 'anim-merge-offset-and-easing 100s';
+  var frames = getFrames(div);
+
+  assert_equals(frames.length, 4, "number of frames");
+
+  var expected = [
+    { offset: 0, computedOffset: 0, easing: "linear", composite: "replace",
+      marginTop: "8px", paddingLeft: "2px" },
+    { offset: 0, computedOffset: 0, easing: "step-end", composite: "replace",
+      color: "rgb(0, 0, 0)", fontSize: "16px" },
+    { offset: 1, computedOffset: 1, easing: "linear", composite: "replace",
+      marginTop: "16px", paddingLeft: "4px" },
+    { offset: 1, computedOffset: 1, easing: "step-end", composite: "replace",
+      color: "rgb(255, 255, 255)", fontSize: "32px" },
+  ];
+
+  for (var i = 0; i < frames.length; i++) {
+    assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
+  }
+}, 'KeyframeEffectReadOnly.getFrames() returns expected frames for an ' +
+   'animation with multiple keyframes for the same time and with ' +
+   'different easing functions');
+
+test(function(t) {
+  var div = addDiv(t);
+
+  div.style.animation = 'anim-no-merge-equiv-easing 100s';
+  var frames = getFrames(div);
+
+  assert_equals(frames.length, 5, "number of frames");
+
+  var expected = [
+    { offset: 0, computedOffset: 0, easing: "step-end", composite: "replace",
+      marginRight: "0px" },
+    { offset: 0, computedOffset: 0, easing: "steps(1)", composite: "replace",
+      marginBottom: "0px" },
+    { offset: 0, computedOffset: 0, easing: "steps(1, end)", composite: "replace",
+      marginTop: "0px" },
+    { offset: 0.5, computedOffset: 0.5, easing: "step-end", composite: "replace",
+      marginTop: "10px", marginRight: "10px", marginBottom: "10px" },
+    { offset: 1, computedOffset: 1, easing: "step-end", composite: "replace",
+      marginTop: "20px", marginRight: "20px", marginBottom: "20px" },
+  ];
+
+  for (var i = 0; i < frames.length; i++) {
+    assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
+  }
+}, 'KeyframeEffectReadOnly.getFrames() returns expected frames for an ' +
+   'animation with multiple keyframes for the same time and with ' +
+   'different but equivalent easing functions');
+
+done();
+</script>
+</body>
new file mode 100644
--- /dev/null
+++ b/dom/animation/test/css-animations/test_keyframeeffect-getframes.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<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_keyframeeffect-getframes.html");
+  });
+</script>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/animation/test/css-transitions/file_keyframeeffect-getframes.html
@@ -0,0 +1,73 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="../testcommon.js"></script>
+<body>
+<script>
+'use strict';
+
+function getFrames(e) {
+  return e.getAnimations()[0].effect.getFrames();
+}
+
+function assert_frames_equal(a, b, name) {
+  assert_equals(Object.keys(a).sort().toString(),
+                Object.keys(b).sort().toString(),
+                "properties on " + name);
+  for (var p in a) {
+    assert_equals(a[p], b[p], "value for '" + p + "' on " + name);
+  }
+}
+
+test(function(t) {
+  var div = addDiv(t);
+
+  div.style.left = '0px';
+  window.getComputedStyle(div).transitionProperty;
+  div.style.transition = 'left 100s';
+  div.style.left = '100px';
+
+  var frames = getFrames(div);
+  
+  assert_equals(frames.length, 2, "number of frames");
+
+  var expected = [
+    { offset: 0, computedOffset: 0, easing: "ease", composite: "replace",
+      left: "0px" },
+    { offset: 1, computedOffset: 1, easing: "ease", composite: "replace",
+      left: "100px" },
+  ];
+
+  for (var i = 0; i < frames.length; i++) {
+    assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
+  }
+}, 'KeyframeEffectReadOnly.getFrames() returns expected frames for a simple ' +
+   'transition');
+
+test(function(t) {
+  var div = addDiv(t);
+
+  div.style.left = '0px';
+  window.getComputedStyle(div).transitionProperty;
+  div.style.transition = 'left 100s steps(2,end)';
+  div.style.left = '100px';
+
+  var frames = getFrames(div);
+  
+  assert_equals(frames.length, 2, "number of frames");
+
+  var expected = [
+    { offset: 0, computedOffset: 0, easing: "steps(2, end)", composite: "replace",
+      left: "0px" },
+    { offset: 1, computedOffset: 1, easing: "steps(2, end)", composite: "replace",
+      left: "100px" },
+  ];
+
+  for (var i = 0; i < frames.length; i++) {
+    assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
+  }
+}, 'KeyframeEffectReadOnly.getFrames() returns expected frames for a simple ' +
+   'transition with a non-default easing function');
+
+done();
+</script>
+</body>
new file mode 100644
--- /dev/null
+++ b/dom/animation/test/css-transitions/test_keyframeeffect-getframes.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<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_keyframeeffect-getframes.html");
+  });
+</script>
--- a/dom/animation/test/mochitest.ini
+++ b/dom/animation/test/mochitest.ini
@@ -27,16 +27,18 @@ 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_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-transitions/test_animation-cancel.html]
@@ -48,16 +50,18 @@ support-files = css-transitions/file_ani
 [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_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
 [document-timeline/test_document-timeline.html]
--- a/dom/base/DOMParser.cpp
+++ b/dom/base/DOMParser.cpp
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/DOMParser.h"
 
 #include "nsIDOMDocument.h"
 #include "nsNetUtil.h"
+#include "nsIStreamListener.h"
 #include "nsStringStream.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsCRT.h"
 #include "nsStreamUtils.h"
 #include "nsContentUtils.h"
 #include "nsDOMJSUtils.h"
 #include "nsError.h"
 #include "nsPIDOMWindow.h"
--- a/dom/base/EventSource.cpp
+++ b/dom/base/EventSource.cpp
@@ -12,16 +12,17 @@
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/dom/EventSourceBinding.h"
 #include "mozilla/dom/MessageEvent.h"
 #include "mozilla/dom/ScriptSettings.h"
 
 #include "nsNetUtil.h"
 #include "nsIAuthPrompt.h"
 #include "nsIAuthPrompt2.h"
+#include "nsIInputStream.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsMimeTypes.h"
 #include "nsIPromptFactory.h"
 #include "nsIWindowWatcher.h"
 #include "nsPresContext.h"
 #include "nsContentPolicyUtils.h"
 #include "nsIStringBundle.h"
 #include "nsIConsoleService.h"
--- a/dom/base/FileList.cpp
+++ b/dom/base/FileList.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/FileList.h"
 #include "mozilla/dom/FileListBinding.h"
+#include "mozilla/dom/File.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(FileList, mFiles, mParent)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FileList)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
--- a/dom/base/PostMessageEvent.cpp
+++ b/dom/base/PostMessageEvent.cpp
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "PostMessageEvent.h"
 
 #include "MessageEvent.h"
 #include "mozilla/dom/BlobBinding.h"
+#include "mozilla/dom/File.h"
 #include "mozilla/dom/FileList.h"
 #include "mozilla/dom/FileListBinding.h"
 #include "mozilla/dom/MessagePort.h"
 #include "mozilla/dom/MessagePortBinding.h"
 #include "mozilla/dom/PMessagePort.h"
 #include "mozilla/dom/StructuredCloneTags.h"
 #include "mozilla/EventDispatcher.h"
 #include "nsGlobalWindow.h"
--- a/dom/base/ProcessGlobal.h
+++ b/dom/base/ProcessGlobal.h
@@ -12,17 +12,19 @@
 #include "nsFrameMessageManager.h"
 #include "nsIScriptContext.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsIScriptContext.h"
 #include "nsIClassInfo.h"
 #include "nsIRunnable.h"
 #include "nsIGlobalObject.h"
 #include "nsIScriptObjectPrincipal.h"
+#include "nsServiceManagerUtils.h"
 #include "nsWeakReference.h"
+#include "nsWrapperCache.h"
 
 namespace mozilla {
 namespace dom {
 
 class ProcessGlobal :
   public nsMessageManagerScriptExecutor,
   public nsIContentProcessMessageManager,
   public nsIGlobalObject,
--- a/dom/base/ScreenOrientation.cpp
+++ b/dom/base/ScreenOrientation.cpp
@@ -1,17 +1,26 @@
 /* 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 "ScreenOrientation.h"
 #include "nsIDeviceSensors.h"
+#include "nsIDocShell.h"
+#include "nsIDocument.h"
+#include "nsGlobalWindow.h"
 #include "nsSandboxFlags.h"
 #include "nsScreen.h"
 
+#include "mozilla/DOMEventTargetHelper.h"
+#include "mozilla/Hal.h"
+#include "mozilla/Preferences.h"
+
+#include "mozilla/dom/Promise.h"
+
 using namespace mozilla::dom;
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(ScreenOrientation,
                                    DOMEventTargetHelper,
                                    mScreen);
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ScreenOrientation)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
--- a/dom/base/nsCopySupport.h
+++ b/dom/base/nsCopySupport.h
@@ -1,16 +1,18 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsCopySupport_h__
 #define nsCopySupport_h__
 
+#include "nsError.h"
+#include "nsIDocument.h"
 #include "mozilla/EventForwards.h"
 
 class nsINode;
 class nsISelection;
 class nsIDocument;
 class nsIImageLoadingContent;
 class nsIContent;
 class nsITransferable;
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -26,16 +26,17 @@
 
 #include "nscore.h"
 #include "nsDOMClassInfo.h"
 #include "nsCRT.h"
 #include "nsCRTGlue.h"
 #include "nsICategoryManager.h"
 #include "nsIComponentRegistrar.h"
 #include "nsXPCOM.h"
+#include "nsISimpleEnumerator.h"
 #include "nsISupportsPrimitives.h"
 #include "nsIXPConnect.h"
 #include "xptcall.h"
 #include "nsTArray.h"
 
 // General helper includes
 #include "nsGlobalWindow.h"
 #include "nsIContent.h"
--- a/dom/base/nsDOMDataChannel.cpp
+++ b/dom/base/nsDOMDataChannel.cpp
@@ -208,23 +208,35 @@ nsDOMDataChannel::GetReadyState(nsAStrin
 }
 
 uint32_t
 nsDOMDataChannel::BufferedAmount() const
 {
   return mDataChannel->GetBufferedAmount();
 }
 
+uint32_t
+nsDOMDataChannel::BufferedAmountLowThreshold() const
+{
+  return mDataChannel->GetBufferedAmountLowThreshold();
+}
+
 NS_IMETHODIMP
 nsDOMDataChannel::GetBufferedAmount(uint32_t* aBufferedAmount)
 {
   *aBufferedAmount = BufferedAmount();
   return NS_OK;
 }
 
+void
+nsDOMDataChannel::SetBufferedAmountLowThreshold(uint32_t aThreshold)
+{
+  mDataChannel->SetBufferedAmountLowThreshold(aThreshold);
+}
+
 NS_IMETHODIMP nsDOMDataChannel::GetBinaryType(nsAString & aBinaryType)
 {
   switch (mBinaryType) {
   case DC_BINARY_TYPE_ARRAYBUFFER:
     aBinaryType.AssignLiteral("arraybuffer");
     break;
   case DC_BINARY_TYPE_BLOB:
     aBinaryType.AssignLiteral("blob");
@@ -463,16 +475,24 @@ nsDOMDataChannel::OnChannelConnected(nsI
 nsresult
 nsDOMDataChannel::OnChannelClosed(nsISupports* aContext)
 {
   LOG(("%p(%p): %s - Dispatching\n",this,(void*)mDataChannel,__FUNCTION__));
 
   return OnSimpleEvent(aContext, NS_LITERAL_STRING("close"));
 }
 
+nsresult
+nsDOMDataChannel::OnBufferLow(nsISupports* aContext)
+{
+  LOG(("%p(%p): %s - Dispatching\n",this,(void*)mDataChannel,__FUNCTION__));
+
+  return OnSimpleEvent(aContext, NS_LITERAL_STRING("bufferedamountlow"));
+}
+
 void
 nsDOMDataChannel::AppReady()
 {
   mDataChannel->AppReady();
 }
 
 /* static */
 nsresult
--- a/dom/base/nsDOMDataChannel.h
+++ b/dom/base/nsDOMDataChannel.h
@@ -49,21 +49,24 @@ public:
     return GetOwner();
   }
 
   // WebIDL
   // Uses XPIDL GetLabel.
   bool Reliable() const;
   mozilla::dom::RTCDataChannelState ReadyState() const;
   uint32_t BufferedAmount() const;
+  uint32_t BufferedAmountLowThreshold() const;
+  void SetBufferedAmountLowThreshold(uint32_t aThreshold);
   IMPL_EVENT_HANDLER(open)
   IMPL_EVENT_HANDLER(error)
   IMPL_EVENT_HANDLER(close)
   // Uses XPIDL Close.
   IMPL_EVENT_HANDLER(message)
+  IMPL_EVENT_HANDLER(bufferedamountlow)
   mozilla::dom::RTCDataChannelType BinaryType() const
   {
     return static_cast<mozilla::dom::RTCDataChannelType>(
       static_cast<int>(mBinaryType));
   }
   void SetBinaryType(mozilla::dom::RTCDataChannelType aType)
   {
     mBinaryType = static_cast<DataChannelBinaryType>(
@@ -92,16 +95,19 @@ public:
   virtual nsresult OnSimpleEvent(nsISupports* aContext, const nsAString& aName);
 
   virtual nsresult
   OnChannelConnected(nsISupports* aContext) override;
 
   virtual nsresult
   OnChannelClosed(nsISupports* aContext) override;
 
+  virtual nsresult
+  OnBufferLow(nsISupports* aContext) override;
+
   virtual void
   AppReady();
 
 protected:
   ~nsDOMDataChannel();
 
 private:
   void Send(nsIInputStream* aMsgStream, const nsACString& aMsgString,
--- a/dom/base/nsDOMFileReader.cpp
+++ b/dom/base/nsDOMFileReader.cpp
@@ -7,16 +7,17 @@
 #include "nsDOMFileReader.h"
 
 #include "nsContentCID.h"
 #include "nsContentUtils.h"
 #include "nsDOMClassInfoID.h"
 #include "nsError.h"
 #include "nsIFile.h"
 #include "nsNetCID.h"
+#include "nsNetUtil.h"
 
 #include "nsXPCOM.h"
 #include "nsIDOMEventListener.h"
 #include "nsJSEnvironment.h"
 #include "nsCycleCollectionParticipant.h"
 #include "mozilla/Base64.h"
 #include "mozilla/dom/EncodingUtils.h"
 #include "mozilla/dom/File.h"
--- a/dom/base/nsDOMMutationObserver.cpp
+++ b/dom/base/nsDOMMutationObserver.cpp
@@ -14,16 +14,17 @@
 #include "nsContentUtils.h"
 #include "nsError.h"
 #include "nsIDOMMutationEvent.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsServiceManagerUtils.h"
 #include "nsTextFragment.h"
 #include "nsThreadUtils.h"
 
+using mozilla::dom::TreeOrderComparator;
 using mozilla::dom::Animation;
 
 nsAutoTArray<nsRefPtr<nsDOMMutationObserver>, 4>*
   nsDOMMutationObserver::sScheduledMutationObservers = nullptr;
 
 nsDOMMutationObserver* nsDOMMutationObserver::sCurrentObserver = nullptr;
 
 uint32_t nsDOMMutationObserver::sMutationLevel = 0;
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -1527,17 +1527,18 @@ nsIDocument::nsIDocument()
     // &&-ed in, this is safe.
     mAllowDNSPrefetch(true),
     mIsBeingUsedAsImage(false),
     mHasLinksToUpdate(false),
     mFontFaceSetDirty(true),
     mGetUserFontSetCalled(false),
     mPostedFlushUserFontSet(false),
     mPartID(0),
-    mDidFireDOMContentLoaded(true)
+    mDidFireDOMContentLoaded(true),
+    mUserHasInteracted(false)
 {
   SetInDocument();
 
   PR_INIT_CLIST(&mDOMMediaQueryLists);  
 }
 
 // NOTE! nsDocument::operator new() zeroes out all members, so don't
 // bother initializing members to 0.
--- a/dom/base/nsFocusManager.cpp
+++ b/dom/base/nsFocusManager.cpp
@@ -1338,17 +1338,17 @@ nsFocusManager::SetFocusInner(nsIContent
     // set the focus node and method as needed
     uint32_t focusMethod = aFocusChanged ? aFlags & FOCUSMETHODANDRING_MASK :
                            newWindow->GetFocusMethod() | (aFlags & FLAG_SHOWRING);
     newWindow->SetFocusedNode(contentToFocus, focusMethod);
     if (aFocusChanged) {
       nsCOMPtr<nsIDocShell> docShell = newWindow->GetDocShell();
 
       nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
-      if (presShell)
+      if (presShell && presShell->DidInitialize())
         ScrollIntoView(presShell, contentToFocus, aFlags);
     }
 
     // update the commands even when inactive so that the attributes for that
     // window are up to date.
     if (allowFrameSwitch)
       newWindow->UpdateCommands(NS_LITERAL_STRING("focus"), nullptr, 0);
 
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -9,16 +9,17 @@
  * handling of loads in it, recursion-checking).
  */
 
 #include "base/basictypes.h"
 
 #include "prenv.h"
 
 #include "mozIApplication.h"
+#include "nsDocShell.h"
 #include "nsIDOMHTMLIFrameElement.h"
 #include "nsIDOMHTMLFrameElement.h"
 #include "nsIDOMMozBrowserFrame.h"
 #include "nsIDOMWindow.h"
 #include "nsIPresShell.h"
 #include "nsIContentInlines.h"
 #include "nsIContentViewer.h"
 #include "nsIDocument.h"
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -694,16 +694,17 @@ GK_ATOM(onbeforepaste, "onbeforepaste")
 GK_ATOM(onbeforeevicted, "onbeforeevicted")
 GK_ATOM(onbeforeprint, "onbeforeprint")
 GK_ATOM(onbeforescriptexecute, "onbeforescriptexecute")
 GK_ATOM(onbeforeunload, "onbeforeunload")
 GK_ATOM(onblocked, "onblocked")
 GK_ATOM(onblur, "onblur")
 GK_ATOM(onbroadcast, "onbroadcast")
 GK_ATOM(onbusy, "onbusy")
+GK_ATOM(onbufferedamountlow, "onbufferedamountlow")
 GK_ATOM(oncached, "oncached")
 GK_ATOM(oncallschanged, "oncallschanged")
 GK_ATOM(oncancel, "oncancel")
 GK_ATOM(oncardstatechange, "oncardstatechange")
 GK_ATOM(oncfstatechange, "oncfstatechange")
 GK_ATOM(onchange, "onchange")
 GK_ATOM(oncharacteristicchanged, "oncharacteristicchanged")
 GK_ATOM(onchargingchange, "onchargingchange")
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -2597,16 +2597,26 @@ public:
   void SetDocumentAndPageUseCounter(mozilla::UseCounter aUseCounter)
   {
     SetDocumentUseCounter(aUseCounter);
     SetPageUseCounter(aUseCounter);
   }
 
   void PropagateUseCounters(nsIDocument* aParentDocument);
 
+  void SetUserHasInteracted(bool aUserHasInteracted)
+  {
+    mUserHasInteracted = aUserHasInteracted;
+  }
+
+  bool UserHasInteracted()
+  {
+    return mUserHasInteracted;
+  }
+
 protected:
   bool GetUseCounter(mozilla::UseCounter aUseCounter)
   {
     return mUseCounters[aUseCounter];
   }
 
   void SetChildDocumentUseCounter(mozilla::UseCounter aUseCounter)
   {
@@ -3002,16 +3012,19 @@ protected:
 
   // Flags for use counters used directly by this document.
   std::bitset<mozilla::eUseCounter_Count> mUseCounters;
   // Flags for use counters used by any child documents of this document.
   std::bitset<mozilla::eUseCounter_Count> mChildDocumentUseCounters;
   // Flags for whether we've notified our top-level "page" of a use counter
   // for this child document.
   std::bitset<mozilla::eUseCounter_Count> mNotifiedPageForUseCounter;
+
+  // Whether the user has interacted with the document or not:
+  bool mUserHasInteracted;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocument, NS_IDOCUMENT_IID)
 
 /**
  * mozAutoSubtreeModified batches DOM mutations so that a DOMSubtreeModified
  * event is dispatched, if necessary, when the outermost mozAutoSubtreeModified
  * object is deleted.
--- a/dom/base/nsNoDataProtocolContentPolicy.cpp
+++ b/dom/base/nsNoDataProtocolContentPolicy.cpp
@@ -11,16 +11,17 @@
  */
 
 #include "nsNoDataProtocolContentPolicy.h"
 #include "nsIDOMWindow.h"
 #include "nsString.h"
 #include "nsIProtocolHandler.h"
 #include "nsIIOService.h"
 #include "nsIExternalProtocolHandler.h"
+#include "nsIURI.h"
 #include "nsNetUtil.h"
 #include "nsContentUtils.h"
 
 NS_IMPL_ISUPPORTS(nsNoDataProtocolContentPolicy, nsIContentPolicy)
 
 NS_IMETHODIMP
 nsNoDataProtocolContentPolicy::ShouldLoad(uint32_t aContentType,
                                           nsIURI *aContentLocation,
--- a/dom/base/nsObjectLoadingContent.cpp
+++ b/dom/base/nsObjectLoadingContent.cpp
@@ -1469,16 +1469,47 @@ nsObjectLoadingContent::CheckJavaCodebas
          this));
     return false;
   }
 
   return true;
 }
 
 bool
+nsObjectLoadingContent::IsYoutubeEmbed()
+{
+  nsCOMPtr<nsIContent> thisContent =
+    do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
+  NS_ASSERTION(thisContent, "Must be an instance of content");
+
+  // We're only interested in switching out embed tags
+  if (!thisContent->NodeInfo()->Equals(nsGkAtoms::embed)) {
+    return false;
+  }
+  nsCOMPtr<nsIEffectiveTLDService> tldService =
+    do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
+  // If we can't analyze the URL, just pass on through.
+  if(!tldService) {
+    NS_WARNING("Could not get TLD service!");
+    return false;
+  }
+  nsAutoCString currentBaseDomain;
+  bool ok = NS_SUCCEEDED(tldService->GetBaseDomain(mURI, 0, currentBaseDomain));
+  if (!ok) {
+    NS_WARNING("Could not parse plugin domain!");
+    return false;
+  }
+  nsAutoCString domain("youtube.com");
+  if (StringEndsWith(domain, currentBaseDomain)) {
+    return true;
+  }
+  return false;
+}
+
+bool
 nsObjectLoadingContent::CheckLoadPolicy(int16_t *aContentPolicy)
 {
   if (!aContentPolicy || !mURI) {
     NS_NOTREACHED("Doing it wrong");
     return false;
   }
 
   nsCOMPtr<nsIContent> thisContent =
@@ -2117,16 +2148,21 @@ nsObjectLoadingContent::LoadObject(bool 
   } else if (mChannelLoaded && mChannel != aLoadingChannel) {
     // The only time we should have a loaded channel with a changed state is
     // when the channel has just opened -- in which case this call should
     // have originated from OnStartRequest
     NS_NOTREACHED("Loading with a channel, but state doesn't make sense");
     return NS_OK;
   }
 
+  // Check whether this is a youtube embed.
+  if (IsYoutubeEmbed()) {
+    Telemetry::Accumulate(Telemetry::YOUTUBE_EMBED_SEEN, 1);
+  }
+
   //
   // Security checks
   //
 
   if (mType != eType_Null) {
     bool allowLoad = true;
     if (IsJavaMIME(mContentType)) {
       allowLoad = CheckJavaCodebase();
--- a/dom/base/nsObjectLoadingContent.h
+++ b/dom/base/nsObjectLoadingContent.h
@@ -514,16 +514,18 @@ class nsObjectLoadingContent : public ns
     ObjectType GetTypeOfContent(const nsCString& aMIMEType);
 
     /**
      * Gets the frame that's associated with this content node.
      * Does not flush.
      */
     nsPluginFrame* GetExistingFrame();
 
+    bool IsYoutubeEmbed();
+
     // Helper class for SetupProtoChain
     class SetupProtoChainRunner final : public nsIRunnable
     {
       ~SetupProtoChainRunner();
     public:
       NS_DECL_ISUPPORTS
 
       explicit SetupProtoChainRunner(nsObjectLoadingContent* aContent);
--- a/dom/base/nsXHTMLContentSerializer.cpp
+++ b/dom/base/nsXHTMLContentSerializer.cpp
@@ -26,16 +26,17 @@
 #include "nsNetUtil.h"
 #include "nsEscape.h"
 #include "nsITextToSubURI.h"
 #include "nsCRT.h"
 #include "nsIParserService.h"
 #include "nsContentUtils.h"
 #include "nsLWBrkCIID.h"
 #include "nsIScriptElement.h"
+#include "nsStubMutationObserver.h"
 #include "nsAttrName.h"
 #include "nsParserConstants.h"
 #include "nsComputedDOMStyle.h"
 #include "mozilla/dom/Element.h"
 
 static const int32_t kLongLineLen = 128;
 
 #define kXMLNS "xmlns"
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -498,26 +498,42 @@ EventStateManager::PreHandleEvent(nsPres
   // Do not take account eMouseEnterIntoWidget/ExitFromWidget so that loading
   // a page when user is not active doesn't change the state to active.
   WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
   if (aEvent->mFlags.mIsTrusted &&
       ((mouseEvent && mouseEvent->IsReal() &&
         mouseEvent->mMessage != eMouseEnterIntoWidget &&
         mouseEvent->mMessage != eMouseExitFromWidget) ||
        aEvent->mClass == eWheelEventClass ||
+       aEvent->mClass == ePointerEventClass ||
+       aEvent->mClass == eTouchEventClass ||
        aEvent->mClass == eKeyboardEventClass)) {
     if (gMouseOrKeyboardEventCounter == 0) {
       nsCOMPtr<nsIObserverService> obs =
         mozilla::services::GetObserverService();
       if (obs) {
         obs->NotifyObservers(nullptr, "user-interaction-active", nullptr);
         UpdateUserActivityTimer();
       }
     }
     ++gMouseOrKeyboardEventCounter;
+
+
+    nsCOMPtr<nsINode> node = do_QueryInterface(aTargetContent);
+    if (node &&
+        (aEvent->mMessage == eKeyUp || aEvent->mMessage == eMouseUp ||
+         aEvent->mMessage == eWheel || aEvent->mMessage == eTouchEnd ||
+         aEvent->mMessage == ePointerUp)) {
+      nsIDocument* doc = node->OwnerDoc();
+      while (doc && !doc->UserHasInteracted()) {
+        doc->SetUserHasInteracted(true);
+        doc = nsContentUtils::IsChildOfSameType(doc) ?
+          doc->GetParentDocument() : nullptr;
+      }
+    }
   }
 
   WheelTransaction::OnEvent(aEvent);
 
   // Focus events don't necessarily need a frame.
   if (!mCurrentTarget && !aTargetContent) {
     NS_ERROR("mCurrentTarget and aTargetContent are null");
     return NS_ERROR_NULL_POINTER;
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -1406,25 +1406,34 @@ MediaFormatReader::Seek(int64_t aTime, i
     return SeekPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
   }
 
   mOriginalSeekTime = Some(media::TimeUnit::FromMicroseconds(aTime));
   mPendingSeekTime = mOriginalSeekTime;
 
   nsRefPtr<SeekPromise> p = mSeekPromise.Ensure(__func__);
 
-  AttemptSeek();
+  RefPtr<nsIRunnable> task(
+    NS_NewRunnableMethod(this, &MediaFormatReader::AttemptSeek));
+  OwnerThread()->Dispatch(task.forget());
 
   return p;
 }
 
 void
 MediaFormatReader::AttemptSeek()
 {
   MOZ_ASSERT(OnTaskQueue());
+  if (mPendingSeekTime.isNothing()) {
+    return;
+  }
+  // An internal seek may be pending due to Seek queueing multiple tasks calling
+  // AttemptSeek ; we can ignore those by resetting any pending demuxer's seek.
+  mAudio.mSeekRequest.DisconnectIfExists();
+  mVideo.mSeekRequest.DisconnectIfExists();
   if (HasVideo()) {
     DoVideoSeek();
   } else if (HasAudio()) {
     DoAudioSeek();
   } else {
     MOZ_CRASH();
   }
 }
--- a/dom/media/VideoUtils.cpp
+++ b/dom/media/VideoUtils.cpp
@@ -13,16 +13,19 @@
 #include "TimeUnits.h"
 #include "nsMathUtils.h"
 #include "nsSize.h"
 #include "VorbisUtils.h"
 #include "ImageContainer.h"
 #include "mozilla/SharedThreadPool.h"
 #include "nsIRandomGenerator.h"
 #include "nsIServiceManager.h"
+#include "nsServiceManagerUtils.h"
+#include "nsIConsoleService.h"
+#include "nsThreadUtils.h"
 
 #include <stdint.h>
 
 namespace mozilla {
 
 using layers::PlanarYCbCrImage;
 
 static inline CheckedInt64 SaferMultDiv(int64_t aValue, uint32_t aMul, uint32_t aDiv) {
@@ -364,9 +367,103 @@ CreateMediaDecodeTaskQueue()
 already_AddRefed<FlushableTaskQueue>
 CreateFlushableMediaDecodeTaskQueue()
 {
   nsRefPtr<FlushableTaskQueue> queue = new FlushableTaskQueue(
     GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER));
   return queue.forget();
 }
 
+void
+SimpleTimer::Cancel() {
+  if (mTimer) {
+#ifdef DEBUG
+    nsCOMPtr<nsIEventTarget> target;
+    mTimer->GetTarget(getter_AddRefs(target));
+    nsCOMPtr<nsIThread> thread(do_QueryInterface(target));
+    MOZ_ASSERT(NS_GetCurrentThread() == thread);
+#endif
+    mTimer->Cancel();
+    mTimer = nullptr;
+  }
+  mTask = nullptr;
+}
+
+NS_IMETHODIMP
+SimpleTimer::Notify(nsITimer *timer) {
+  nsRefPtr<SimpleTimer> deathGrip(this);
+  if (mTask) {
+    mTask->Run();
+    mTask = nullptr;
+  }
+  return NS_OK;
+}
+
+nsresult
+SimpleTimer::Init(nsIRunnable* aTask, uint32_t aTimeoutMs, nsIThread* aTarget)
+{
+  nsresult rv;
+
+  // Get target thread first, so we don't have to cancel the timer if it fails.
+  nsCOMPtr<nsIThread> target;
+  if (aTarget) {
+    target = aTarget;
+  } else {
+    rv = NS_GetMainThread(getter_AddRefs(target));
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+  }
+
+  nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  // Note: set target before InitWithCallback in case the timer fires before
+  // we change the event target.
+  rv = timer->SetTarget(aTarget);
+  if (NS_FAILED(rv)) {
+    timer->Cancel();
+    return rv;
+  }
+  rv = timer->InitWithCallback(this, aTimeoutMs, nsITimer::TYPE_ONE_SHOT);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  mTimer = timer.forget();
+  mTask = aTask;
+  return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(SimpleTimer, nsITimerCallback)
+
+already_AddRefed<SimpleTimer>
+SimpleTimer::Create(nsIRunnable* aTask, uint32_t aTimeoutMs, nsIThread* aTarget)
+{
+  nsRefPtr<SimpleTimer> t(new SimpleTimer());
+  if (NS_FAILED(t->Init(aTask, aTimeoutMs, aTarget))) {
+    return nullptr;
+  }
+  return t.forget();
+}
+
+void
+LogToBrowserConsole(const nsAString& aMsg)
+{
+  if (!NS_IsMainThread()) {
+    nsAutoString msg(aMsg);
+    nsCOMPtr<nsIRunnable> task =
+      NS_NewRunnableFunction([msg]() { LogToBrowserConsole(msg); });
+    NS_DispatchToMainThread(task.forget(), NS_DISPATCH_NORMAL);
+    return;
+  }
+  nsCOMPtr<nsIConsoleService> console(
+    do_GetService("@mozilla.org/consoleservice;1"));
+  if (!console) {
+    NS_WARNING("Failed to log message to console.");
+    return;
+  }
+  nsAutoString msg(aMsg);
+  console->LogStringMessage(msg.get());
+}
+
 } // end namespace mozilla
--- a/dom/media/VideoUtils.h
+++ b/dom/media/VideoUtils.h
@@ -17,16 +17,18 @@
 #include "nsIThread.h"
 #include "nsSize.h"
 #include "nsRect.h"
 
 #include "nsThreadUtils.h"
 #include "prtime.h"
 #include "AudioSampleFormat.h"
 #include "TimeUnits.h"
+#include "nsITimer.h"
+#include "nsCOMPtr.h"
 
 using mozilla::CheckedInt64;
 using mozilla::CheckedUint64;
 using mozilla::CheckedInt32;
 using mozilla::CheckedUint32;
 
 // This file contains stuff we'd rather put elsewhere, but which is
 // dependent on other changes which we don't want to wait for. We plan to
@@ -296,11 +298,37 @@ nsRefPtr<GenericPromise> InvokeUntil(Wor
       }
     }
   };
 
   Helper::Iteration(p, aWork, aCondition);
   return p.forget();
 }
 
+// Simple timer to run a runnable after a timeout.
+class SimpleTimer : public nsITimerCallback
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  // Create a new timer to run aTask after aTimeoutMs milliseconds
+  // on thread aTarget. If aTarget is null, task is run on the main thread.
+  static already_AddRefed<SimpleTimer> Create(nsIRunnable* aTask,
+                                              uint32_t aTimeoutMs,
+                                              nsIThread* aTarget = nullptr);
+  void Cancel();
+
+  NS_IMETHOD Notify(nsITimer *timer) override;
+
+private:
+  virtual ~SimpleTimer() {}
+  nsresult Init(nsIRunnable* aTask, uint32_t aTimeoutMs, nsIThread* aTarget);
+
+  nsRefPtr<nsIRunnable> mTask;
+  nsCOMPtr<nsITimer> mTimer;
+};
+
+void
+LogToBrowserConsole(const nsAString& aMsg);
+
 } // end namespace mozilla
 
 #endif
--- a/dom/media/eme/EMEUtils.cpp
+++ b/dom/media/eme/EMEUtils.cpp
@@ -1,17 +1,15 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/EMEUtils.h"
-#include "nsServiceManagerUtils.h"
-#include "nsIConsoleService.h"
 
 namespace mozilla {
 
 PRLogModuleInfo* GetEMELog() {
   static PRLogModuleInfo* log = nullptr;
   if (!log) {
     log = PR_NewLogModule("EME");
   }
@@ -106,29 +104,16 @@ ParseKeySystem(const nsAString& aInputKe
       aOutCDMVersion = minCDMVersion;
       return true;
     }
   }
   return false;
 }
 
 void
-LogToBrowserConsole(const nsAString& aMsg)
-{
-  nsCOMPtr<nsIConsoleService> console(
-    do_GetService("@mozilla.org/consoleservice;1"));
-  if (!console) {
-    NS_WARNING("Failed to log message to console.");
-    return;
-  }
-  nsAutoString msg(aMsg);
-  console->LogStringMessage(msg.get());
-}
-
-void
 ConstructKeySystem(const nsAString& aKeySystem,
                    const nsAString& aCDMVersion,
                    nsAString& aOutKeySystem)
 {
   aOutKeySystem.Append(aKeySystem);
   aOutKeySystem.AppendLiteral(".");
   aOutKeySystem.Append(aCDMVersion);
 }
--- a/dom/media/eme/EMEUtils.h
+++ b/dom/media/eme/EMEUtils.h
@@ -47,18 +47,15 @@ namespace mozilla {
 // On success, aOutKeySystem contains the keySystem string stripped of the
 // min version number, and aOutMinCDMVersion contains the min version number
 // if present. If it was not present, aOutMinCDMVersion is NO_CDM_VERSION.
 bool ParseKeySystem(const nsAString& aKeySystem,
                     nsAString& aOutKeySystem,
                     int32_t& aOutMinCDMVersion);
 
 void
-LogToBrowserConsole(const nsAString& aMsg);
-
-void
 ConstructKeySystem(const nsAString& aKeySystem,
                    const nsAString& aCDMVersion,
                    nsAString& aOutKeySystem);
 
 } // namespace mozilla
 
 #endif // EME_LOG_H_
--- a/dom/media/gmp/GMPVideoDecoderParent.cpp
+++ b/dom/media/gmp/GMPVideoDecoderParent.cpp
@@ -45,16 +45,17 @@ GMPVideoDecoderParent::GMPVideoDecoderPa
   , mShuttingDown(false)
   , mActorDestroyed(false)
   , mIsAwaitingResetComplete(false)
   , mIsAwaitingDrainComplete(false)
   , mPlugin(aPlugin)
   , mCallback(nullptr)
   , mVideoHost(this)
   , mPluginId(aPlugin->GetPluginId())
+  , mFrameCount(0)
 {
   MOZ_ASSERT(mPlugin);
 }
 
 GMPVideoDecoderParent::~GMPVideoDecoderParent()
 {
 }
 
@@ -152,16 +153,17 @@ GMPVideoDecoderParent::Decode(GMPUniqueP
   inputFrameImpl->RelinquishFrameData(frameData);
 
   if (!SendDecode(frameData,
                   aMissingFrames,
                   aCodecSpecificInfo,
                   aRenderTimeMs)) {
     return NS_ERROR_FAILURE;
   }
+  mFrameCount++;
 
   // Async IPC, we don't have access to a return value.
   return NS_OK;
 }
 
 nsresult
 GMPVideoDecoderParent::Reset()
 {
@@ -175,24 +177,43 @@ GMPVideoDecoderParent::Reset()
   MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
 
   if (!SendReset()) {
     return NS_ERROR_FAILURE;
   }
 
   mIsAwaitingResetComplete = true;
 
+  nsRefPtr<GMPVideoDecoderParent> self(this);
+  nsCOMPtr<nsIRunnable> task = NS_NewRunnableFunction([self]() -> void
+  {
+    LOGD(("GMPVideoDecoderParent[%p]::ResetCompleteTimeout() timed out waiting for ResetComplete", self.get()));
+    self->mResetCompleteTimeout = nullptr;
+    LogToBrowserConsole(NS_LITERAL_STRING("GMPVideoDecoderParent timed out waiting for ResetComplete()"));
+  });
+  CancelResetCompleteTimeout();
+  mResetCompleteTimeout = SimpleTimer::Create(task, 5000, mPlugin->GMPThread());
+
   // Async IPC, we don't have access to a return value.
   return NS_OK;
 }
 
+void
+GMPVideoDecoderParent::CancelResetCompleteTimeout()
+{
+  if (mResetCompleteTimeout) {
+    mResetCompleteTimeout->Cancel();
+    mResetCompleteTimeout = nullptr;
+  }
+}
+
 nsresult
 GMPVideoDecoderParent::Drain()
 {
-  LOGD(("GMPVideoDecoderParent[%p]::Drain()", this));
+  LOGD(("GMPVideoDecoderParent[%p]::Drain() frameCount=%d", this, mFrameCount));
 
   if (!mIsOpen) {
     NS_WARNING("Trying to use an dead GMP video decoder");
     return NS_ERROR_FAILURE;
   }
 
   MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
 
@@ -275,18 +296,19 @@ GMPVideoDecoderParent::ActorDestroy(Acto
     mPlugin = nullptr;
   }
   mVideoHost.ActorDestroyed();
 }
 
 bool
 GMPVideoDecoderParent::RecvDecoded(const GMPVideoi420FrameData& aDecodedFrame)
 {
-  LOGV(("GMPVideoDecoderParent[%p]::RecvDecoded() timestamp=%lld",
-        this, aDecodedFrame.mTimestamp()));
+  --mFrameCount;
+  LOGV(("GMPVideoDecoderParent[%p]::RecvDecoded() timestamp=%lld frameCount=%d",
+    this, aDecodedFrame.mTimestamp(), mFrameCount));
 
   if (!mCallback) {
     return false;
   }
 
   if (!GMPVideoi420FrameImpl::CheckFrameData(aDecodedFrame)) {
     LOG(LogLevel::Error,
       ("GMPVideoDecoderParent[%p]::RecvDecoded() "
@@ -340,18 +362,21 @@ GMPVideoDecoderParent::RecvInputDataExha
   mCallback->InputDataExhausted();
 
   return true;
 }
 
 bool
 GMPVideoDecoderParent::RecvDrainComplete()
 {
-  LOGD(("GMPVideoDecoderParent[%p]::RecvDrainComplete()", this));
-
+  LOGD(("GMPVideoDecoderParent[%p]::RecvDrainComplete() frameCount=%d", this, mFrameCount));
+  nsAutoString msg;
+  msg.AppendLiteral("GMPVideoDecoderParent::RecvDrainComplete() outstanding frames=");
+  msg.AppendInt(mFrameCount);
+  LogToBrowserConsole(msg);
   if (!mCallback) {
     return false;
   }
 
   if (!mIsAwaitingDrainComplete) {
     return true;
   }
   mIsAwaitingDrainComplete = false;
@@ -362,24 +387,27 @@ GMPVideoDecoderParent::RecvDrainComplete
   return true;
 }
 
 bool
 GMPVideoDecoderParent::RecvResetComplete()
 {
   LOGD(("GMPVideoDecoderParent[%p]::RecvResetComplete()", this));
 
+  CancelResetCompleteTimeout();
+
   if (!mCallback) {
     return false;
   }
 
   if (!mIsAwaitingResetComplete) {
     return true;
   }
   mIsAwaitingResetComplete = false;
+  mFrameCount = 0;
 
   // Ignore any return code. It is OK for this to fail without killing the process.
   mCallback->ResetComplete();
 
   return true;
 }
 
 bool
@@ -452,27 +480,30 @@ GMPVideoDecoderParent::Recv__delete__()
   }
 
   return true;
 }
 
 void
 GMPVideoDecoderParent::UnblockResetAndDrain()
 {
-  LOGD(("GMPVideoDecoderParent[%p]::UnblockResetAndDrain()", this));
+  LOGD(("GMPVideoDecoderParent[%p]::UnblockResetAndDrain() "
+        "awaitingResetComplete=%d awaitingDrainComplete=%d",
+       this, mIsAwaitingResetComplete, mIsAwaitingDrainComplete));
 
   if (!mCallback) {
     MOZ_ASSERT(!mIsAwaitingResetComplete);
     MOZ_ASSERT(!mIsAwaitingDrainComplete);
     return;
   }
   if (mIsAwaitingResetComplete) {
     mIsAwaitingResetComplete = false;
     mCallback->ResetComplete();
   }
   if (mIsAwaitingDrainComplete) {
     mIsAwaitingDrainComplete = false;
     mCallback->DrainComplete();
   }
+  CancelResetCompleteTimeout();
 }
 
 } // namespace gmp
 } // namespace mozilla
--- a/dom/media/gmp/GMPVideoDecoderParent.h
+++ b/dom/media/gmp/GMPVideoDecoderParent.h
@@ -9,16 +9,17 @@
 #include "mozilla/RefPtr.h"
 #include "gmp-video-decode.h"
 #include "mozilla/gmp/PGMPVideoDecoderParent.h"
 #include "GMPMessageUtils.h"
 #include "GMPSharedMemManager.h"
 #include "GMPUtils.h"
 #include "GMPVideoHost.h"
 #include "GMPVideoDecoderProxy.h"
+#include "VideoUtils.h"
 
 namespace mozilla {
 namespace gmp {
 
 class GMPContentParent;
 
 class GMPVideoDecoderParent final : public PGMPVideoDecoderParent
                                   , public GMPVideoDecoderProxy
@@ -75,24 +76,27 @@ private:
   virtual bool RecvError(const GMPErr& aError) override;
   virtual bool RecvShutdown() override;
   virtual bool RecvParentShmemForPool(Shmem&& aEncodedBuffer) override;
   virtual bool AnswerNeedShmem(const uint32_t& aFrameBufferSize,
                                Shmem* aMem) override;
   virtual bool Recv__delete__() override;
 
   void UnblockResetAndDrain();
+  void CancelResetCompleteTimeout();
 
   bool mIsOpen;
   bool mShuttingDown;
   bool mActorDestroyed;
   bool mIsAwaitingResetComplete;
   bool mIsAwaitingDrainComplete;
   nsRefPtr<GMPContentParent> mPlugin;
   GMPVideoDecoderCallbackProxy* mCallback;
   GMPVideoHostImpl mVideoHost;
   const uint32_t mPluginId;
+  int32_t mFrameCount;
+  nsRefPtr<SimpleTimer> mResetCompleteTimeout;
 };
 
 } // namespace gmp
 } // namespace mozilla
 
 #endif // GMPVideoDecoderParent_h_
--- a/dom/media/mediasource/MediaSourceDemuxer.cpp
+++ b/dom/media/mediasource/MediaSourceDemuxer.cpp
@@ -304,17 +304,17 @@ MediaSourceTrackDemuxer::GetSamples(int3
 
 void
 MediaSourceTrackDemuxer::Reset()
 {
   MOZ_ASSERT(mParent, "Called after BreackCycle()");
   nsRefPtr<MediaSourceTrackDemuxer> self = this;
   nsCOMPtr<nsIRunnable> task =
     NS_NewRunnableFunction([self] () {
-      self->mManager->Seek(self->mType, TimeUnit());
+      self->mManager->Seek(self->mType, TimeUnit(), TimeUnit());
       {
         MonitorAutoLock mon(self->mMonitor);
         self->mNextRandomAccessPoint =
           self->mManager->GetNextRandomAccessPoint(self->mType);
       }
     });
   mParent->GetTaskQueue()->Dispatch(task.forget());
 }
@@ -359,17 +359,18 @@ MediaSourceTrackDemuxer::DoSeek(media::T
   TimeIntervals buffered = mManager->Buffered(mType);
   buffered.SetFuzz(TimeUnit::FromMicroseconds(EOS_FUZZ_US));
 
   if (!buffered.Contains(aTime)) {
     // We don't have the data to seek to.
     return SeekPromise::CreateAndReject(DemuxerFailureReason::WAITING_FOR_DATA,
                                         __func__);
   }
-  TimeUnit seekTime = mManager->Seek(mType, aTime);
+  TimeUnit seekTime =
+    mManager->Seek(mType, aTime, TimeUnit::FromMicroseconds(EOS_FUZZ_US));
   {
     MonitorAutoLock mon(mMonitor);
     mNextRandomAccessPoint = mManager->GetNextRandomAccessPoint(mType);
   }
   return SeekPromise::CreateAndResolve(seekTime, __func__);
 }
 
 nsRefPtr<MediaSourceTrackDemuxer::SamplesPromise>
--- a/dom/media/mediasource/TrackBuffersManager.cpp
+++ b/dom/media/mediasource/TrackBuffersManager.cpp
@@ -1655,21 +1655,24 @@ TrackBuffersManager::InsertFrames(TrackB
   // 16. Add the coded frame with the presentation timestamp, decode timestamp, and frame duration to the track buffer.
   if (!CheckNextInsertionIndex(aTrackData,
                                TimeUnit::FromMicroseconds(aSamples[0]->mTime))) {
     RejectProcessing(NS_ERROR_FAILURE, __func__);
     return;
   }
 
   // Adjust our demuxing index if necessary.
-  if (trackBuffer.mNextGetSampleIndex.isSome() &&
-      (trackBuffer.mNextInsertionIndex.ref() < trackBuffer.mNextGetSampleIndex.ref() ||
-       (trackBuffer.mNextInsertionIndex.ref() == trackBuffer.mNextGetSampleIndex.ref() &&
-        aIntervals.GetEnd() < trackBuffer.mNextSampleTime))) {
-    trackBuffer.mNextGetSampleIndex.ref() += aSamples.Length();
+  if (trackBuffer.mNextGetSampleIndex.isSome()) {
+    if (trackBuffer.mNextInsertionIndex.ref() == trackBuffer.mNextGetSampleIndex.ref() &&
+        aIntervals.GetEnd() >= trackBuffer.mNextSampleTime) {
+      MSE_DEBUG("Next sample to be played got overwritten");
+      trackBuffer.mNextGetSampleIndex.reset();
+    } else if (trackBuffer.mNextInsertionIndex.ref() <= trackBuffer.mNextGetSampleIndex.ref()) {
+      trackBuffer.mNextGetSampleIndex.ref() += aSamples.Length();
+    }
   }
 
   TrackBuffer& data = trackBuffer.mBuffers.LastElement();
   data.InsertElementsAt(trackBuffer.mNextInsertionIndex.ref(), aSamples);
   trackBuffer.mNextInsertionIndex.ref() += aSamples.Length();
 
   // Update our buffered range with new sample interval.
   // We allow a fuzz factor in our interval of half a frame length,
@@ -1885,46 +1888,92 @@ TrackBuffersManager::SafeBuffered(TrackI
 
 const TrackBuffersManager::TrackBuffer&
 TrackBuffersManager::GetTrackBuffer(TrackInfo::TrackType aTrack)
 {
   MOZ_ASSERT(OnTaskQueue());
   return GetTracksData(aTrack).mBuffers.LastElement();
 }
 
+uint32_t TrackBuffersManager::FindSampleIndex(const TrackBuffer& aTrackBuffer,
+                                              const TimeInterval& aInterval)
+{
+  TimeUnit target = aInterval.mStart - aInterval.mFuzz;
+
+  for (uint32_t i = 0; i < aTrackBuffer.Length(); i++) {
+    const nsRefPtr<MediaRawData>& sample = aTrackBuffer[i];
+    if (sample->mTime >= target.ToMicroseconds() ||
+        sample->GetEndTime() > target.ToMicroseconds()) {
+      return i;
+    }
+  }
+  NS_ASSERTION(false, "FindSampleIndex called with invalid arguments");
+
+  return 0;
+}
+
 TimeUnit
 TrackBuffersManager::Seek(TrackInfo::TrackType aTrack,
-                          const TimeUnit& aTime)
+                          const TimeUnit& aTime,
+                          const TimeUnit& aFuzz)
 {
   MOZ_ASSERT(OnTaskQueue());
   auto& trackBuffer = GetTracksData(aTrack);
   const TrackBuffersManager::TrackBuffer& track = GetTrackBuffer(aTrack);
-  TimeUnit lastKeyFrameTime;
+
+  if (!track.Length()) {
+    // This a reset. It will be followed by another valid seek.
+    trackBuffer.mNextGetSampleIndex = Some(uint32_t(0));
+    trackBuffer.mNextSampleTimecode = TimeUnit();
+    trackBuffer.mNextSampleTime = TimeUnit();
+    return TimeUnit();
+  }
+
+  uint32_t i = 0;
+
+  if (aTime != TimeUnit()) {
+    // Determine the interval of samples we're attempting to seek to.
+    TimeIntervals buffered = trackBuffer.mBufferedRanges;
+    TimeIntervals::IndexType index = buffered.Find(aTime);
+    buffered.SetFuzz(aFuzz);
+    index = buffered.Find(aTime);
+    MOZ_ASSERT(index != TimeIntervals::NoIndex);
+
+    TimeInterval target = buffered[index];
+    i = FindSampleIndex(track, target);
+  }
+
+  Maybe<TimeUnit> lastKeyFrameTime;
   TimeUnit lastKeyFrameTimecode;
   uint32_t lastKeyFrameIndex = 0;
-  for (uint32_t i = 0; i < track.Length(); i++) {
+  for (; i < track.Length(); i++) {
     const nsRefPtr<MediaRawData>& sample = track[i];
     TimeUnit sampleTime = TimeUnit::FromMicroseconds(sample->mTime);
-    if (sampleTime > aTime) {
+    if (sampleTime > aTime && lastKeyFrameTime.isSome()) {
       break;
     }
     if (sample->mKeyframe) {
       lastKeyFrameTimecode = TimeUnit::FromMicroseconds(sample->mTimecode);
-      lastKeyFrameTime = sampleTime;
+      lastKeyFrameTime = Some(sampleTime);
       lastKeyFrameIndex = i;
     }
-    if (sampleTime == aTime) {
+    if (sampleTime == aTime ||
+        (sampleTime > aTime && lastKeyFrameTime.isSome())) {
       break;
     }
   }
+  MSE_DEBUG("Keyframe %s found at %lld",
+            lastKeyFrameTime.isSome() ? "" : "not",
+            lastKeyFrameTime.refOr(TimeUnit()).ToMicroseconds());
+
   trackBuffer.mNextGetSampleIndex = Some(lastKeyFrameIndex);
   trackBuffer.mNextSampleTimecode = lastKeyFrameTimecode;
-  trackBuffer.mNextSampleTime = lastKeyFrameTime;
+  trackBuffer.mNextSampleTime = lastKeyFrameTime.refOr(TimeUnit());
 
-  return lastKeyFrameTime;
+  return lastKeyFrameTime.refOr(TimeUnit());
 }
 
 uint32_t
 TrackBuffersManager::SkipToNextRandomAccessPoint(TrackInfo::TrackType aTrack,
                                                  const TimeUnit& aTimeThreadshold,
                                                  bool& aFound)
 {
   MOZ_ASSERT(OnTaskQueue());
--- a/dom/media/mediasource/TrackBuffersManager.h
+++ b/dom/media/mediasource/TrackBuffersManager.h
@@ -83,17 +83,18 @@ public:
   const TrackBuffer& GetTrackBuffer(TrackInfo::TrackType aTrack);
   const media::TimeIntervals& Buffered(TrackInfo::TrackType);
   media::TimeIntervals SafeBuffered(TrackInfo::TrackType) const;
   bool IsEnded() const
   {
     return mEnded;
   }
   media::TimeUnit Seek(TrackInfo::TrackType aTrack,
-                       const media::TimeUnit& aTime);
+                       const media::TimeUnit& aTime,
+                       const media::TimeUnit& aFuzz);
   uint32_t SkipToNextRandomAccessPoint(TrackInfo::TrackType aTrack,
                                        const media::TimeUnit& aTimeThreadshold,
                                        bool& aFound);
   already_AddRefed<MediaRawData> GetSample(TrackInfo::TrackType aTrack,
                                            const media::TimeUnit& aFuzz,
                                            bool& aError);
   media::TimeUnit GetNextRandomAccessPoint(TrackInfo::TrackType aTrack);
 
@@ -257,16 +258,18 @@ private:
     // Byte size of all samples contained in this track buffer.
     uint32_t mSizeBuffer;
     // TrackInfo of the first metadata received.
     nsRefPtr<SharedTrackInfo> mInfo;
     // TrackInfo of the last metadata parsed (updated with each init segment.
     nsRefPtr<SharedTrackInfo> mLastInfo;
 
     // If set, position of the next sample to be retrieved by GetSample().
+    // If the position is equal to the TrackBuffer's length, it indicates that
+    // we've reached EOS.
     Maybe<uint32_t> mNextGetSampleIndex;
     // Approximation of the next sample's decode timestamp.
     media::TimeUnit mNextSampleTimecode;
     // Approximation of the next sample's presentation timestamp.
     media::TimeUnit mNextSampleTime;
 
     void ResetAppendState()
     {
@@ -285,16 +288,19 @@ private:
   bool CheckNextInsertionIndex(TrackData& aTrackData,
                                const media::TimeUnit& aSampleTime);
   void InsertFrames(TrackBuffer& aSamples,
                     const media::TimeIntervals& aIntervals,
                     TrackData& aTrackData);
   void RemoveFrames(const media::TimeIntervals& aIntervals,
                     TrackData& aTrackData,
                     uint32_t aStartIndex);
+  // Find index of sample. Return a negative value if not found.
+  uint32_t FindSampleIndex(const TrackBuffer& aTrackBuffer,
+                           const media::TimeInterval& aInterval);
   void UpdateBufferedRanges();
   void RejectProcessing(nsresult aRejectValue, const char* aName);
   void ResolveProcessing(bool aResolveValue, const char* aName);
   MozPromiseRequestHolder<CodedFrameProcessingPromise> mProcessingRequest;
   MozPromiseHolder<CodedFrameProcessingPromise> mProcessingPromise;
 
   MozPromiseHolder<AppendPromise> mAppendPromise;
   // Set to true while SegmentParserLoop is running. This is used for diagnostic
--- a/dom/media/platforms/android/AndroidDecoderModule.cpp
+++ b/dom/media/platforms/android/AndroidDecoderModule.cpp
@@ -14,35 +14,45 @@
 #include "MediaData.h"
 #include "MediaInfo.h"
 
 #include "nsThreadUtils.h"
 #include "nsAutoPtr.h"
 #include "nsPromiseFlatString.h"
 
 #include <jni.h>
-#include <string.h>
 
 using namespace mozilla;
 using namespace mozilla::gl;
 using namespace mozilla::widget::sdk;
 
 namespace mozilla {
 
 #define ENVOKE_CALLBACK(Func, ...) \
   if (mCallback) { \
     mCallback->Func(__VA_ARGS__); \
   } else { \
     NS_WARNING("callback not set"); \
   }
 
+static const char* TranslateMimeType(const nsACString& aMimeType)
+{
+  if (aMimeType.EqualsLiteral("video/webm; codecs=vp8")) {
+    return "video/x-vnd.on2.vp8";
+  } else if (aMimeType.EqualsLiteral("video/webm; codecs=vp9")) {
+    return "video/x-vnd.on2.vp9";
+  }
+  return PromiseFlatCString(aMimeType).get();
+}
+
 static MediaCodec::LocalRef CreateDecoder(const nsACString& aMimeType)
 {
   MediaCodec::LocalRef codec;
-  NS_ENSURE_SUCCESS(MediaCodec::CreateDecoderByType(PromiseFlatCString(aMimeType).get(), &codec), nullptr);
+  NS_ENSURE_SUCCESS(MediaCodec::CreateDecoderByType(TranslateMimeType(aMimeType),
+                    &codec), nullptr);
   return codec;
 }
 
 class VideoDataDecoder : public MediaCodecDataDecoder {
 public:
   VideoDataDecoder(const VideoInfo& aConfig,
                    MediaFormat::Param aFormat, MediaDataDecoderCallback* aCallback,
                    layers::ImageContainer* aImageContainer)
@@ -71,18 +81,20 @@ public:
     mGLContext = nullptr;
   }
 
   nsresult Input(MediaRawData* aSample) override {
     return MediaCodecDataDecoder::Input(aSample);
   }
 
   bool WantCopy() {
-    // Allocating a texture is incredibly slow on PowerVR
-    return mGLContext->Vendor() != GLVendor::Imagination;
+    // Allocating a texture is incredibly slow on PowerVR and may fail on
+    // emulators, see bug 1190379.
+    return mGLContext->Vendor() != GLVendor::Imagination &&
+           mGLContext->Renderer() != GLRenderer::AndroidEmulator;
   }
 
   EGLImage CopySurface(layers::Image* img) {
     mGLContext->MakeCurrent();
 
     GLuint tex = CreateTextureForOffscreen(mGLContext, mGLContext->GetGLFormats(),
                                            img->GetSize());
 
@@ -282,17 +294,17 @@ AndroidDecoderModule::CreateVideoDecoder
                                 layers::LayersBackend aLayersBackend,
                                 layers::ImageContainer* aImageContainer,
                                 FlushableTaskQueue* aVideoTaskQueue,
                                 MediaDataDecoderCallback* aCallback)
 {
   MediaFormat::LocalRef format;
 
   NS_ENSURE_SUCCESS(MediaFormat::CreateVideoFormat(
-      aConfig.mMimeType,
+      TranslateMimeType(aConfig.mMimeType),
       aConfig.mDisplay.width,
       aConfig.mDisplay.height,
       &format), nullptr);
 
   nsRefPtr<MediaDataDecoder> decoder =
     new VideoDataDecoder(aConfig, format, aCallback, aImageContainer);
 
   return decoder.forget();
--- a/dom/media/tests/mochitest/dataChannel.js
+++ b/dom/media/tests/mochitest/dataChannel.js
@@ -163,14 +163,29 @@ var commandsCheckDataChannel = [
 
     return test.send(message).then(result => {
       is(channels.indexOf(result.channel), channels.length - 1, "Last channel used");
       is(result.data, message, "Received message has the correct content.");
     });
   }
 ];
 
+var commandsCheckLargeXfer = [
+  function SEND_BIG_BUFFER(test) {
+    var size = 512*1024; // SCTP internal buffer is 256K, so we'll have ~256K queued
+    var buffer = new ArrayBuffer(size);
+    // note: type received is always blob for binary data
+    var options = {};
+    options.bufferedAmountLowThreshold = 64*1024;
+    return test.send(buffer, options).then(result => {
+      ok(result.data instanceof Blob, "Received data is of instance Blob");
+      is(result.data.size, size, "Received data has the correct size.");
+    });
+  },
+];
+
 function addInitialDataChannel(chain) {
   chain.insertBefore('PC_LOCAL_CREATE_OFFER', commandsCreateDataChannel);
   chain.insertBefore('PC_LOCAL_CHECK_MEDIA_TRACKS', commandsWaitForDataChannel);
   chain.removeAfter('PC_REMOTE_CHECK_ICE_CONNECTIONS');
+  chain.append(commandsCheckLargeXfer);
   chain.append(commandsCheckDataChannel);
 }
--- a/dom/media/tests/mochitest/pc.js
+++ b/dom/media/tests/mochitest/pc.js
@@ -209,21 +209,31 @@ PeerConnectionTest.prototype.closeDataCh
  *        Data channel to use for receiving the message
  */
 PeerConnectionTest.prototype.send = function(data, options) {
   options = options || { };
   var source = options.sourceChannel ||
            this.pcLocal.dataChannels[this.pcLocal.dataChannels.length - 1];
   var target = options.targetChannel ||
            this.pcRemote.dataChannels[this.pcRemote.dataChannels.length - 1];
+  var bufferedamount = options.bufferedAmountLowThreshold || 0;
+  var bufferlow_fired = true; // to make testing later easier
+  if (bufferedamount != 0) {
+    source.bufferedAmountLowThreshold = bufferedamount;
+    bufferlow_fired = false;
+    source.onbufferedamountlow = function() {
+      bufferlow_fired = true;
+    };
+  }
 
   return new Promise(resolve => {
     // Register event handler for the target channel
-    target.onmessage = e => {
-      resolve({ channel: target, data: e.data });
+      target.onmessage = e => {
+        ok(bufferlow_fired, "bufferedamountlow event fired");
+	resolve({ channel: target, data: e.data });
     };
 
     source.send(data);
   });
 };
 
 /**
  * Create a data channel
@@ -558,16 +568,17 @@ function DataChannelWrapper(dataChannel,
   info("Creating " + this);
 
   /**
    * Setup appropriate callbacks
    */
   createOneShotEventWrapper(this, this._channel, 'close');
   createOneShotEventWrapper(this, this._channel, 'error');
   createOneShotEventWrapper(this, this._channel, 'message');
+  createOneShotEventWrapper(this, this._channel, 'bufferedamountlow');
 
   this.opened = timerGuard(new Promise(resolve => {
     this._channel.onopen = () => {
       this._channel.onopen = unexpectedEvent(this, 'onopen');
       is(this.readyState, "open", "data channel is 'open' after 'onopen'");
       resolve(this);
     };
   }), 180000, "channel didn't open in time");
@@ -636,16 +647,26 @@ DataChannelWrapper.prototype = {
    *
    * @returns {String} The state of the channel
    */
   get readyState() {
     return this._channel.readyState;
   },
 
   /**
+   * Sets the bufferlowthreshold of the channel
+   *
+   * @param {integer} amoutn
+   *        The new threshold for the chanel
+   */
+  set bufferedAmountLowThreshold(amount) {
+    this._channel.bufferedAmountLowThreshold = amount;
+  },
+
+  /**
    * Close the data channel
    */
   close : function () {
     info(this + ": Closing channel");
     this._channel.close();
   },
 
   /**
--- a/dom/media/webm/WebMDemuxer.cpp
+++ b/dom/media/webm/WebMDemuxer.cpp
@@ -284,17 +284,17 @@ WebMDemuxer::ReadMetadata()
   }
 
   for (unsigned int track = 0; track < ntracks; ++track) {
     int id = nestegg_track_codec_id(mContext, track);
     if (id == -1) {
       return NS_ERROR_FAILURE;
     }
     int type = nestegg_track_type(mContext, track);
-    if (type == NESTEGG_TRACK_VIDEO) {
+    if (type == NESTEGG_TRACK_VIDEO && !mHasVideo) {
       nestegg_video_params params;
       r = nestegg_track_video_params(mContext, track, &params);
       if (r == -1) {
         return NS_ERROR_FAILURE;
       }
       mVideoCodec = nestegg_track_codec_id(mContext, track);
       switch(mVideoCodec) {
         case NESTEGG_CODEC_VP8:
@@ -359,17 +359,17 @@ WebMDemuxer::ReadMetadata()
           mInfo.mVideo.mStereoMode = StereoMode::RIGHT_LEFT;
           break;
       }
       uint64_t duration = 0;
       r = nestegg_duration(mContext, &duration);
       if (!r) {
         mInfo.mVideo.mDuration = media::TimeUnit::FromNanoseconds(duration).ToMicroseconds();
       }
-    } else if (type == NESTEGG_TRACK_AUDIO) {
+    } else if (type == NESTEGG_TRACK_AUDIO && !mHasAudio) {
       nestegg_audio_params params;
       r = nestegg_track_audio_params(mContext, track, &params);
       if (r == -1) {
         return NS_ERROR_FAILURE;
       }
 
       mAudioTrack = track;
       mHasAudio = true;
--- a/dom/security/nsContentSecurityManager.cpp
+++ b/dom/security/nsContentSecurityManager.cpp
@@ -204,19 +204,27 @@ DoContentSecurityChecks(nsIURI* aURI, ns
       requestingContext = aLoadInfo->LoadingNode();
       MOZ_ASSERT(!requestingContext ||
                  requestingContext->NodeType() == nsIDOMNode::ELEMENT_NODE,
                  "type_media requires requestingContext of type Element");
       break;
     }
 
     case nsIContentPolicy::TYPE_WEBSOCKET:
-    case nsIContentPolicy::TYPE_CSP_REPORT:
+    case nsIContentPolicy::TYPE_CSP_REPORT: {
+      MOZ_ASSERT(false, "contentPolicyType not supported yet");
+      break;
+    }
+
     case nsIContentPolicy::TYPE_XSLT: {
-      MOZ_ASSERT(false, "contentPolicyType not supported yet");
+      mimeTypeGuess = NS_LITERAL_CSTRING("application/xml");
+      requestingContext = aLoadInfo->LoadingNode();
+      MOZ_ASSERT(!requestingContext ||
+                 requestingContext->NodeType() == nsIDOMNode::DOCUMENT_NODE,
+                 "type_xslt requires requestingContext of type Document");
       break;
     }
 
     case nsIContentPolicy::TYPE_BEACON: {
       mimeTypeGuess = EmptyCString();
       requestingContext = aLoadInfo->LoadingNode();
       MOZ_ASSERT(!requestingContext ||
                  requestingContext->NodeType() == nsIDOMNode::DOCUMENT_NODE,
--- a/dom/smil/nsSMILKeySpline.h
+++ b/dom/smil/nsSMILKeySpline.h
@@ -52,16 +52,23 @@ public:
     return mX1 == aOther.mX1 &&
            mY1 == aOther.mY1 &&
            mX2 == aOther.mX2 &&
            mY2 == aOther.mY2;
   }
   bool operator!=(const nsSMILKeySpline& aOther) const {
     return !(*this == aOther);
   }
+  int32_t Compare(const nsSMILKeySpline& aRhs) const {
+    if (mX1 != aRhs.mX1) return mX1 < aRhs.mX1 ? -1 : 1;
+    if (mY1 != aRhs.mY1) return mY1 < aRhs.mY1 ? -1 : 1;
+    if (mX2 != aRhs.mX2) return mX2 < aRhs.mX2 ? -1 : 1;
+    if (mY2 != aRhs.mY2) return mY2 < aRhs.mY2 ? -1 : 1;
+    return 0;
+  }
 
 private:
   void
   CalcSampleValues();
 
   /**
    * Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2.
    */
--- a/dom/tests/browser/browser_test_new_window_from_content.js
+++ b/dom/tests/browser/browser_test_new_window_from_content.js
@@ -44,16 +44,18 @@ const kXULNS = "http://www.mozilla.org/k
 const kContentDoc = "http://www.example.com/browser/dom/tests/browser/test_new_window_from_content_child.html";
 const kContentScript = "chrome://mochitests/content/browser/dom/tests/browser/test_new_window_from_content_child.js";
 const kNewWindowPrefKey = "browser.link.open_newwindow";
 const kNewWindowRestrictionPrefKey = "browser.link.open_newwindow.restriction";
 const kSameTab = "same tab";
 const kNewWin = "new window";
 const kNewTab = "new tab";
 
+SpecialPowers.pushPrefEnv({"set": [["dom.require_user_interaction_for_beforeunload", false]]});
+
 requestLongerTimeout(2);
 
 // The following "matrices" represent the result of content attempting to
 // open a window with window.open with the default feature set. The key of
 // the kWinOpenDefault object represents the value of browser.link.open_newwindow.
 // The value for each key is an array that represents the result (either opening
 // the link in the same tab, a new window, or a new tab), where the index of each
 // result maps to the browser.link.open_newwindow.restriction pref. I've tried
--- a/dom/webidl/DataChannel.webidl
+++ b/dom/webidl/DataChannel.webidl
@@ -16,21 +16,23 @@ enum RTCDataChannelType {
 
 // XXX This interface is called RTCDataChannel in the spec.
 interface DataChannel : EventTarget
 {
   readonly attribute DOMString label;
   readonly attribute boolean reliable;
   readonly attribute RTCDataChannelState readyState;
   readonly attribute unsigned long bufferedAmount;
+  attribute unsigned long bufferedAmountLowThreshold;
   attribute EventHandler onopen;
   attribute EventHandler onerror;
   attribute EventHandler onclose;
   void close();
   attribute EventHandler onmessage;
+  attribute EventHandler onbufferedamountlow;
   attribute RTCDataChannelType binaryType;
   [Throws]
   void send(DOMString data);
   [Throws]
   void send(Blob data);
   [Throws]
   void send(ArrayBuffer data);
   [Throws]
--- a/dom/webidl/Document.webidl
+++ b/dom/webidl/Document.webidl
@@ -387,15 +387,21 @@ partial interface Document {
   /**
    * Removes the element inserted into the CanvasFrame given an AnonymousContent
    * instance.
    */
   [ChromeOnly, Throws]
   void removeAnonymousContent(AnonymousContent aContent);
 };
 
+// Extension to give chrome JS the ability to determine whether
+// the user has interacted with the document or not.
+partial interface Document {
+  [ChromeOnly] readonly attribute boolean userHasInteracted;
+};
+
 Document implements XPathEvaluator;
 Document implements GlobalEventHandlers;
 Document implements TouchEventHandlers;
 Document implements ParentNode;
 Document implements OnErrorEventHandlerForNodes;
 Document implements GeometryUtils;
 Document implements FontFaceSource;
new file mode 100644
--- /dev/null
+++ b/dom/webidl/Keyframe.webidl
@@ -0,0 +1,25 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * https://w3c.github.io/web-animations/#the-compositeoperation-enumeration
+ * https://w3c.github.io/web-animations/#the-keyframe-dictionary
+ * https://w3c.github.io/web-animations/#the-computedkeyframe-dictionary
+ *
+ * Copyright © 2015 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
+ * liability, trademark and document use rules apply.
+ */
+
+enum CompositeOperation { "replace", "add", "accumulate" };
+
+dictionary Keyframe {
+  double? offset = null;
+  DOMString easing = "linear";
+  CompositeOperation? composite = null;
+};
+
+dictionary ComputedKeyframe : Keyframe {
+  double computedOffset;
+};
--- a/dom/webidl/KeyframeEffect.webidl
+++ b/dom/webidl/KeyframeEffect.webidl
@@ -14,10 +14,13 @@
  Func="nsDocument::IsWebAnimationsEnabled"]
 interface KeyframeEffectReadOnly : AnimationEffectReadOnly {
   readonly attribute Element?  target;
   // Not yet implemented:
   // readonly attribute IterationCompositeOperation iterationComposite;
   // readonly attribute CompositeOperation          composite;
   // readonly attribute DOMString                   spacing;
   // KeyframeEffect             clone();
-  // sequence<ComputedKeyframe> getFrames ();
+
+  // We use object instead of ComputedKeyframe so that we can put the
+  // property-value pairs on the object.
+  [Throws] sequence<object> getFrames();
 };
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -269,16 +269,17 @@ WEBIDL_FILES = [
     'InputPortManager.webidl',
     'InspectorUtils.webidl',
     'InterAppConnection.webidl',
     'InterAppConnectionRequest.webidl',
     'InterAppMessagePort.webidl',
     'KeyAlgorithm.webidl',
     'KeyboardEvent.webidl',
     'KeyEvent.webidl',
+    'Keyframe.webidl',
     'KeyframeEffect.webidl',
     'KillSwitch.webidl',
     'LegacyQueryInterface.webidl',
     'LinkStyle.webidl',
     'ListBoxObject.webidl',
     'LocalMediaStream.webidl',
     'Location.webidl',
     'MediaDeviceInfo.webidl',
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -570,16 +570,23 @@ private:
 
     event->SetTrusted(true);
 
     globalScope->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
 
     return true;
   }
 
+  NS_IMETHOD Cancel() override
+  {
+    // We need to run regardless.
+    Run();
+    return WorkerRunnable::Cancel();
+  }
+
   virtual void
   PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult)
           override
   {
     // Report errors.
     WorkerRunnable::PostRun(aCx, aWorkerPrivate, aRunResult);
 
     // Match the busy count increase from NotifyRunnable.
@@ -1225,16 +1232,23 @@ private:
   {
     if (mTimer) {
       mTimer->Cancel();
       mTimer = nullptr;
     }
 
     return true;
   }
+
+  NS_IMETHOD Cancel() override
+  {
+    // We need to run regardless.
+    Run();
+    return WorkerRunnable::Cancel();
+  }
 };
 
 class UpdateRuntimeOptionsRunnable final : public WorkerControlRunnable
 {
   JS::RuntimeOptions mRuntimeOptions;
 
 public:
   UpdateRuntimeOptionsRunnable(
@@ -3818,16 +3832,17 @@ WorkerPrivate::WorkerPrivate(JSContext* 
   , mErrorHandlerRecursionCount(0)
   , mNextTimeoutId(1)
   , mStatus(Pending)
   , mFrozen(false)
   , mTimerRunning(false)
   , mRunningExpiredTimeouts(false)
   , mCloseHandlerStarted(false)
   , mCloseHandlerFinished(false)
+  , mPendingEventQueueClearing(false)
   , mMemoryReporterRunning(false)
   , mBlockedForMemoryReporter(false)
   , mCancelAllPendingRunnables(false)
   , mPeriodicGCTimerRunning(false)
   , mIdleGCTimerRunning(false)
   , mWorkerScriptExecutedSuccessfully(false)
 {
   MOZ_ASSERT_IF(!IsDedicatedWorker(), !aSharedWorkerName.IsVoid());
@@ -4646,16 +4661,17 @@ WorkerPrivate::IsOnCurrentThread(bool* a
 }
 
 void
 WorkerPrivate::ScheduleDeletion(WorkerRanOrNot aRanOrNot)
 {
   AssertIsOnWorkerThread();
   MOZ_ASSERT(mChildWorkers.IsEmpty());
   MOZ_ASSERT(mSyncLoopStack.IsEmpty());
+  MOZ_ASSERT(!mPendingEventQueueClearing);
 
   ClearMainEventQueue(aRanOrNot);
 #ifdef DEBUG
   if (WorkerRan == aRanOrNot) {
     nsIThread* currentThread = NS_GetCurrentThread();
     MOZ_ASSERT(currentThread);
     MOZ_ASSERT(!NS_HasPendingEvents(currentThread));
   }
@@ -4876,16 +4892,17 @@ WorkerPrivate::ProcessAllControlRunnable
   return result;
 }
 
 void
 WorkerPrivate::ClearMainEventQueue(WorkerRanOrNot aRanOrNot)
 {
   AssertIsOnWorkerThread();
 
+  MOZ_ASSERT(!mSyncLoopStack.Length());
   MOZ_ASSERT(!mCancelAllPendingRunnables);
   mCancelAllPendingRunnables = true;
 
   if (WorkerNeverRan == aRanOrNot) {
     for (uint32_t count = mPreStartRunnables.Length(), index = 0;
          index < count;
          index++) {
       nsRefPtr<WorkerRunnable> runnable = mPreStartRunnables[index].forget();
@@ -5238,16 +5255,21 @@ WorkerPrivate::DestroySyncLoop(uint32_t 
 #endif
 
     // This will delete |loopInfo|!
     mSyncLoopStack.RemoveElementAt(aLoopIndex);
   }
 
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aThread->PopEventQueue(nestedEventTarget)));
 
+  if (!mSyncLoopStack.Length() && mPendingEventQueueClearing) {
+    ClearMainEventQueue(WorkerRan);
+    mPendingEventQueueClearing = false;
+  }
+
   return result;
 }
 
 void
 WorkerPrivate::StopSyncLoop(nsIEventTarget* aSyncLoopTarget, bool aResult)
 {
   AssertIsOnWorkerThread();
   AssertValidSyncLoop(aSyncLoopTarget);
@@ -5504,17 +5526,23 @@ WorkerPrivate::NotifyInternal(JSContext*
   MOZ_ASSERT(previousStatus >= Canceling || mKillTime.IsNull());
 
   // Let all our features know the new status.
   NotifyFeatures(aCx, aStatus);
 
   // If this is the first time our status has changed then we need to clear the
   // main event queue.
   if (previousStatus == Running) {
-    ClearMainEventQueue(WorkerRan);
+    // NB: If we're in a sync loop, we can't clear the queue immediately,
+    // because this is the wrong queue. So we have to defer it until later.
+    if (mSyncLoopStack.Length()) {
+      mPendingEventQueueClearing = true;
+    } else {
+      ClearMainEventQueue(WorkerRan);
+    }
   }
 
   // If we've run the close handler, we don't need to do anything else.
   if (mCloseHandlerFinished) {
     return true;
   }
 
   // If the worker script never ran, or failed to compile, we don't need to do
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -939,16 +939,17 @@ class WorkerPrivate : public WorkerPriva
   uint32_t mErrorHandlerRecursionCount;
   uint32_t mNextTimeoutId;
   Status mStatus;
   bool mFrozen;
   bool mTimerRunning;
   bool mRunningExpiredTimeouts;
   bool mCloseHandlerStarted;
   bool mCloseHandlerFinished;
+  bool mPendingEventQueueClearing;
   bool mMemoryReporterRunning;
   bool mBlockedForMemoryReporter;
   bool mCancelAllPendingRunnables;
   bool mPeriodicGCTimerRunning;
   bool mIdleGCTimerRunning;
   bool mWorkerScriptExecutedSuccessfully;
   bool mPreferences[WORKERPREF_COUNT];
   bool mOnLine;
--- a/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp
+++ b/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp
@@ -85,30 +85,32 @@ public:
     NS_IMETHOD WillResume(void) override { return NS_OK; }
     NS_IMETHOD SetParser(nsParserBase* aParser) override { return NS_OK; }
     virtual void FlushPendingNotifications(mozFlushType aType) override { }
     NS_IMETHOD SetDocumentCharset(nsACString& aCharset) override { return NS_OK; }
     virtual nsISupports *GetTarget() override { return nullptr; }
 
 private:
     nsRefPtr<txStylesheetCompiler> mCompiler;
-    nsCOMPtr<nsIStreamListener> mListener;
+    nsCOMPtr<nsIStreamListener>    mListener;
+    nsCOMPtr<nsIParser>            mParser;
     bool mCheckedForXML;
 
 protected:
     ~txStylesheetSink() {}
 
     // This exists solely to suppress a warning from nsDerivedSafe
     txStylesheetSink();
 };
 
 txStylesheetSink::txStylesheetSink(txStylesheetCompiler* aCompiler,
                                    nsIParser* aParser)
-    : mCompiler(aCompiler),
-      mCheckedForXML(false)
+    : mCompiler(aCompiler)
+    , mParser(aParser)
+    , mCheckedForXML(false)
 {
     mListener = do_QueryInterface(aParser);
 }
 
 NS_IMPL_ISUPPORTS(txStylesheetSink,
                   nsIXMLContentSink,
                   nsIContentSink,
                   nsIExpatSink,
@@ -222,34 +224,33 @@ txStylesheetSink::DidBuildModel(bool aTe
 }
 
 NS_IMETHODIMP
 txStylesheetSink::OnDataAvailable(nsIRequest *aRequest, nsISupports *aContext,
                                   nsIInputStream *aInputStream,
                                   uint64_t aOffset, uint32_t aCount)
 {
     if (!mCheckedForXML) {
-        nsCOMPtr<nsIParser> parser = do_QueryInterface(aContext);
         nsCOMPtr<nsIDTD> dtd;
-        parser->GetDTD(getter_AddRefs(dtd));
+        mParser->GetDTD(getter_AddRefs(dtd));
         if (dtd) {
             mCheckedForXML = true;
             if (!(dtd->GetType() & NS_IPARSER_FLAG_XML)) {
                 nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
                 nsAutoString spec;
                 getSpec(channel, spec);
                 mCompiler->cancel(NS_ERROR_XSLT_WRONG_MIME_TYPE, nullptr,
                                   spec.get());
 
                 return NS_ERROR_XSLT_WRONG_MIME_TYPE;
             }
         }
     }
 
-    return mListener->OnDataAvailable(aRequest, aContext, aInputStream,
+    return mListener->OnDataAvailable(aRequest, mParser, aInputStream,
                                       aOffset, aCount);
 }
 
 NS_IMETHODIMP
 txStylesheetSink::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
 {
     int32_t charsetSource = kCharsetFromDocTypeDefault;
 
@@ -263,18 +264,17 @@ txStylesheetSink::OnStartRequest(nsIRequ
             charsetSource = kCharsetFromChannel;
         }
     }
 
     if (charset.IsEmpty()) {
       charset.AssignLiteral("UTF-8");
     }
 
-    nsCOMPtr<nsIParser> parser = do_QueryInterface(aContext);
-    parser->SetDocumentCharset(charset, charsetSource);
+    mParser->SetDocumentCharset(charset, charsetSource);
 
     nsAutoCString contentType;
     channel->GetContentType(contentType);
 
     // Time to sniff! Note: this should go away once file channels do
     // sniffing themselves.
     nsCOMPtr<nsIURI> uri;
     channel->GetURI(getter_AddRefs(uri));
@@ -284,25 +284,25 @@ txStylesheetSink::OnStartRequest(nsIRequ
         nsresult rv;
         nsCOMPtr<nsIStreamConverterService> serv =
             do_GetService("@mozilla.org/streamConverters;1", &rv);
         if (NS_SUCCEEDED(rv)) {
             nsCOMPtr<nsIStreamListener> converter;
             rv = serv->AsyncConvertData(UNKNOWN_CONTENT_TYPE,
                                         "*/*",
                                         mListener,
-                                        aContext,
+                                        mParser,
                                         getter_AddRefs(converter));
             if (NS_SUCCEEDED(rv)) {
                 mListener = converter;
             }
         }
     }
 
-    return mListener->OnStartRequest(aRequest, aContext);
+    return mListener->OnStartRequest(aRequest, mParser);
 }
 
 NS_IMETHODIMP
 txStylesheetSink::OnStopRequest(nsIRequest *aRequest, nsISupports *aContext,
                                 nsresult aStatusCode)
 {
     bool success = true;
 
@@ -314,33 +314,33 @@ txStylesheetSink::OnStopRequest(nsIReque
     nsresult result = aStatusCode;
     if (!success) {
         // XXX We sometimes want to use aStatusCode here, but the parser resets
         //     it to NS_ERROR_NOINTERFACE because we don't implement
         //     nsIHTMLContentSink.
         result = NS_ERROR_XSLT_NETWORK_ERROR;
     }
     else if (!mCheckedForXML) {
-        nsCOMPtr<nsIParser> parser = do_QueryInterface(aContext);
         nsCOMPtr<nsIDTD> dtd;
-        parser->GetDTD(getter_AddRefs(dtd));
+        mParser->GetDTD(getter_AddRefs(dtd));
         if (dtd && !(dtd->GetType() & NS_IPARSER_FLAG_XML)) {
             result = NS_ERROR_XSLT_WRONG_MIME_TYPE;
         }
     }
 
     if (NS_FAILED(result)) {
         nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
         nsAutoString spec;
         getSpec(channel, spec);
         mCompiler->cancel(result, nullptr, spec.get());
     }
 
-    nsresult rv = mListener->OnStopRequest(aRequest, aContext, aStatusCode);
+    nsresult rv = mListener->OnStopRequest(aRequest, mParser, aStatusCode);
     mListener = nullptr;
+    mParser = nullptr;
     return rv;
 }
 
 NS_IMETHODIMP
 txStylesheetSink::GetInterface(const nsIID& aIID, void** aResult)
 {
     if (aIID.Equals(NS_GET_IID(nsIAuthPrompt))) {
         NS_ENSURE_ARG(aResult);
@@ -417,30 +417,16 @@ txCompileObserver::loadURI(const nsAStri
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsCOMPtr<nsIPrincipal> referrerPrincipal;
     rv = nsContentUtils::GetSecurityManager()->
       GetSimpleCodebasePrincipal(referrerUri,
                                  getter_AddRefs(referrerPrincipal));
     NS_ENSURE_SUCCESS(rv, rv);
 
-    // Content Policy
-    int16_t shouldLoad = nsIContentPolicy::ACCEPT;
-    rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_INTERNAL_STYLESHEET,
-                                   uri,
-                                   referrerPrincipal,
-                                   mLoaderDocument,
-                                   NS_LITERAL_CSTRING("application/xml"),
-                                   nullptr,
-                                   &shouldLoad);
-    NS_ENSURE_SUCCESS(rv, rv);
-    if (NS_CP_REJECTED(shouldLoad)) {
-        return NS_ERROR_DOM_BAD_URI;
-    }
-
     return startLoad(uri, aCompiler, referrerPrincipal, aReferrerPolicy);
 }
 
 void
 txCompileObserver::onDoneCompiling(txStylesheetCompiler* aCompiler,
                                    nsresult aResult,
                                    const char16_t *aErrorText,
                                    const char16_t *aParam)
@@ -464,17 +450,17 @@ txCompileObserver::startLoad(nsIURI* aUr
     }
 
     nsCOMPtr<nsIChannel> channel;
     nsresult rv = NS_NewChannelWithTriggeringPrincipal(
                     getter_AddRefs(channel),
                     aUri,
                     mLoaderDocument,
                     aReferrerPrincipal, // triggeringPrincipal
-                    nsILoadInfo::SEC_NORMAL,
+                    nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS,
                     nsIContentPolicy::TYPE_XSLT,
                     loadGroup);
 
     NS_ENSURE_SUCCESS(rv, rv);
 
     channel->SetContentType(NS_LITERAL_CSTRING("text/xml"));
 
     nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
@@ -497,50 +483,29 @@ txCompileObserver::startLoad(nsIURI* aUr
     NS_ENSURE_TRUE(sink, NS_ERROR_OUT_OF_MEMORY);
 
     channel->SetNotificationCallbacks(sink);
 
     parser->SetCommand(kLoadAsData);
     parser->SetContentSink(sink);
     parser->Parse(aUri);
 
-    // Always install in case of redirects
-    nsRefPtr<nsCORSListenerProxy> listener =
-        new nsCORSListenerProxy(sink, aReferrerPrincipal, false);
-    rv = listener->Init(channel, DataURIHandling::Disallow);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    return channel->AsyncOpen(listener, parser);
+    return channel->AsyncOpen2(sink);
 }
 
 nsresult
 TX_LoadSheet(nsIURI* aUri, txMozillaXSLTProcessor* aProcessor,
              nsIDocument* aLoaderDocument, ReferrerPolicy aReferrerPolicy)
 {
     nsIPrincipal* principal = aLoaderDocument->NodePrincipal();
 
     nsAutoCString spec;
     aUri->GetSpec(spec);
     MOZ_LOG(txLog::xslt, LogLevel::Info, ("TX_LoadSheet: %s\n", spec.get()));
 
-    // Content Policy
-    int16_t shouldLoad = nsIContentPolicy::ACCEPT;
-    nsresult rv =
-        NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_INTERNAL_STYLESHEET,
-                                  aUri,
-                                  principal,
-                                  aLoaderDocument,
-                                  NS_LITERAL_CSTRING("application/xml"),
-                                  nullptr,
-                                  &shouldLoad);
-    NS_ENSURE_SUCCESS(rv, rv);
-    if (NS_CP_REJECTED(shouldLoad)) {
-        return NS_ERROR_DOM_BAD_URI;
-    }
-
     nsRefPtr<txCompileObserver> observer =
         new txCompileObserver(aProcessor, aLoaderDocument);
     NS_ENSURE_TRUE(observer, NS_ERROR_OUT_OF_MEMORY);
 
     nsRefPtr<txStylesheetCompiler> compiler =
         new txStylesheetCompiler(NS_ConvertUTF8toUTF16(spec), aReferrerPolicy,
                                  observer);
     NS_ENSURE_TRUE(compiler, NS_ERROR_OUT_OF_MEMORY);
--- a/gfx/gl/GLContextProviderEGL.cpp
+++ b/gfx/gl/GLContextProviderEGL.cpp
@@ -1034,42 +1034,47 @@ GLContextProviderEGL::CreateOffscreen(co
 
     bool canOffscreenUseHeadless = true;
     if (sEGLLibrary.IsANGLE()) {
         // ANGLE needs to use PBuffers.
         canOffscreenUseHeadless = false;
     }
 
     RefPtr<GLContext> gl;
-    SurfaceCaps offscreenCaps = minCaps;
+    SurfaceCaps minOffscreenCaps = minCaps;
 
     if (canOffscreenUseHeadless) {
         gl = CreateHeadless(flags);
         if (!gl)
             return nullptr;
     } else {
-        SurfaceCaps minBackbufferCaps = minCaps;
-        if (minCaps.antialias) {
+        SurfaceCaps minBackbufferCaps = minOffscreenCaps;
+        if (minOffscreenCaps.antialias) {
             minBackbufferCaps.antialias = false;
             minBackbufferCaps.depth = false;
             minBackbufferCaps.stencil = false;
         }
 
         gl = GLContextEGL::CreateEGLPBufferOffscreenContext(size, minBackbufferCaps);
         if (!gl)
             return nullptr;
 
-        offscreenCaps = gl->Caps();
-        if (minCaps.antialias) {
-            offscreenCaps.depth = minCaps.depth;
-            offscreenCaps.stencil = minCaps.stencil;
+        // Pull the actual resulting caps to ensure that our offscreen matches our
+        // backbuffer.
+        minOffscreenCaps.alpha = gl->Caps().alpha;
+        if (!minOffscreenCaps.antialias) {
+            // Only update these if we don't have AA. If we do have AA, we ignore
+            // backbuffer depth/stencil.
+            minOffscreenCaps.depth = gl->Caps().depth;
+            minOffscreenCaps.stencil = gl->Caps().stencil;
         }
     }
 
-    if (!gl->InitOffscreen(size, offscreenCaps))
+    // Init the offscreen with the updated offscreen caps.
+    if (!gl->InitOffscreen(size, minOffscreenCaps))
         return nullptr;
 
     return gl.forget();
 }
 
 // Don't want a global context on Android as 1) share groups across 2 threads fail on many Tegra drivers (bug 759225)
 // and 2) some mobile devices have a very strict limit on global number of GL contexts (bug 754257)
 // and 3) each EGL context eats 750k on B2G (bug 813783)
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -458,19 +458,21 @@ Layer::SetAnimations(const AnimationArra
           CubicBezierFunction cbf = tf.get_CubicBezierFunction();
           ctf->Init(nsTimingFunction(cbf.x1(), cbf.y1(), cbf.x2(), cbf.y2()));
           break;
         }
         default: {
           NS_ASSERTION(tf.type() == TimingFunction::TStepFunction,
                        "Function must be bezier or step");
           StepFunction sf = tf.get_StepFunction();
-          nsTimingFunction::Type type = sf.type() == 1 ? nsTimingFunction::StepStart
-                                                       : nsTimingFunction::StepEnd;
-          ctf->Init(nsTimingFunction(type, sf.steps()));
+          nsTimingFunction::Type type = sf.type() == 1 ?
+                                          nsTimingFunction::Type::StepStart :
+                                          nsTimingFunction::Type::StepEnd;
+          ctf->Init(nsTimingFunction(type, sf.steps(),
+                                     nsTimingFunction::Keyword::Explicit));
           break;
         }
       }
       functions.AppendElement(ctf);
     }
 
     // Precompute the StyleAnimationValues that we need if this is a transform
     // animation.
@@ -1670,17 +1672,17 @@ Layer::Dump(std::stringstream& aStream, 
       WriteSnapshotLinkToDumpFile(this, aStream);
     }
 #endif
     aStream << ">";
   }
   DumpSelf(aStream, aPrefix);
 
 #ifdef MOZ_DUMP_PAINTING
-  if (gfxUtils::sDumpPainting && AsLayerComposite() && AsLayerComposite()->GetCompositableHost()) {
+  if (gfxUtils::sDumpCompositorTextures && AsLayerComposite() && AsLayerComposite()->GetCompositableHost()) {
     AsLayerComposite()->GetCompositableHost()->Dump(aStream, aPrefix, aDumpHtml);
   }
 #endif
 
   if (aDumpHtml) {
     aStream << "</a>";
   }
 
--- a/gfx/layers/apz/src/WheelScrollAnimation.cpp
+++ b/gfx/layers/apz/src/WheelScrollAnimation.cpp
@@ -48,16 +48,24 @@ WheelScrollAnimation::DoSample(FrameMetr
   // function as normal.
   bool finished = IsFinished(now);
   nsPoint sampledDest = finished
                         ? mDestination
                         : PositionAt(now);
   ParentLayerPoint displacement =
     (CSSPoint::FromAppUnits(sampledDest) - aFrameMetrics.GetScrollOffset()) * zoom;
 
+  if (!IsZero(displacement)) {
+    // Velocity is measured in ParentLayerCoords / Milliseconds
+    float xVelocity = displacement.x / aDelta.ToMilliseconds();
+    float yVelocity = displacement.y / aDelta.ToMilliseconds();
+    mApzc.mX.SetVelocity(xVelocity);
+    mApzc.mY.SetVelocity(yVelocity);
+  }
+
   // Note: we ignore overscroll for wheel animations.
   ParentLayerPoint adjustedOffset, overscroll;
   mApzc.mX.AdjustDisplacement(displacement.x, adjustedOffset.x, overscroll.x);
   mApzc.mY.AdjustDisplacement(displacement.y, adjustedOffset.y, overscroll.y,
                               !aFrameMetrics.AllowVerticalScrollWithWheel());
 
   // If we expected to scroll, but there's no more scroll range on either axis,
   // then end the animation early. Note that the initial displacement could be 0
--- a/gfx/layers/composite/CanvasLayerComposite.cpp
+++ b/gfx/layers/composite/CanvasLayerComposite.cpp
@@ -84,17 +84,17 @@ CanvasLayerComposite::RenderLayer(const 
 {
   if (!mCompositableHost || !mCompositableHost->IsAttached()) {
     return;
   }
 
   mCompositor->MakeCurrent();
 
 #ifdef MOZ_DUMP_PAINTING
-  if (gfxUtils::sDumpPainting) {
+  if (gfxUtils::sDumpCompositorTextures) {
     RefPtr<gfx::DataSourceSurface> surf = mCompositableHost->GetAsSurface();
     WriteSnapshotToDumpFile(this, surf);
   }
 #endif
 
   RenderWithAllMasks(this, mCompositor, aClipRect,
                      [&](EffectChain& effectChain, const Rect& clipRect) {
     mCompositableHost->Composite(this, effectChain,
--- a/gfx/layers/composite/ContainerLayerComposite.cpp
+++ b/gfx/layers/composite/ContainerLayerComposite.cpp
@@ -702,17 +702,17 @@ ContainerRender(ContainerT* aContainer,
     if (!surface) {
       aContainer->mPrepared = nullptr;
       return;
     }
 
     gfx::Rect visibleRect(aContainer->GetEffectiveVisibleRegion().GetBounds());
     nsRefPtr<Compositor> compositor = aManager->GetCompositor();
 #ifdef MOZ_DUMP_PAINTING
-    if (gfxUtils::sDumpPainting) {
+    if (gfxUtils::sDumpCompositorTextures) {
       RefPtr<gfx::DataSourceSurface> surf = surface->Dump(compositor);
       if (surf) {
         WriteSnapshotToDumpFile(aContainer, surf);
       }
     }
 #endif
 
     nsRefPtr<ContainerT> container = aContainer;
--- a/gfx/layers/composite/ImageLayerComposite.cpp
+++ b/gfx/layers/composite/ImageLayerComposite.cpp
@@ -82,17 +82,17 @@ ImageLayerComposite::GetLayer()
 void
 ImageLayerComposite::RenderLayer(const IntRect& aClipRect)
 {
   if (!mImageHost || !mImageHost->IsAttached()) {
     return;
   }
 
 #ifdef MOZ_DUMP_PAINTING
-  if (gfxUtils::sDumpPainting) {
+  if (gfxUtils::sDumpCompositorTextures) {
     RefPtr<gfx::DataSourceSurface> surf = mImageHost->GetAsSurface();
     WriteSnapshotToDumpFile(this, surf);
   }
 #endif
 
   mCompositor->MakeCurrent();
 
   RenderWithAllMasks(this, mCompositor, aClipRect,
--- a/gfx/layers/composite/PaintedLayerComposite.cpp
+++ b/gfx/layers/composite/PaintedLayerComposite.cpp
@@ -110,17 +110,17 @@ PaintedLayerComposite::RenderLayer(const
 
   MOZ_ASSERT(mBuffer->GetCompositor() == compositor &&
              mBuffer->GetLayer() == this,
              "buffer is corrupted");
 
   const nsIntRegion& visibleRegion = GetEffectiveVisibleRegion();
 
 #ifdef MOZ_DUMP_PAINTING
-  if (gfxUtils::sDumpPainting) {
+  if (gfxUtils::sDumpCompositorTextures) {
     RefPtr<gfx::DataSourceSurface> surf = mBuffer->GetAsSurface();
     if (surf) {
       WriteSnapshotToDumpFile(this, surf);
     }
   }
 #endif
 
 
--- a/gfx/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -1410,17 +1410,17 @@ void
 CompositorOGL::EndFrame()
 {
   PROFILER_LABEL("CompositorOGL", "EndFrame",
     js::ProfileEntry::Category::GRAPHICS);
 
   MOZ_ASSERT(mCurrentRenderTarget == mWindowRenderTarget, "Rendering target not properly restored");
 
 #ifdef MOZ_DUMP_PAINTING
-  if (gfxUtils::sDumpPainting) {
+  if (gfxUtils::sDumpCompositorTextures) {
     IntRect rect;
     if (mUseExternalSurfaceSize) {
       rect = IntRect(0, 0, mSurfaceSize.width, mSurfaceSize.height);
     } else {
       mWidget->GetBounds(rect);
     }
     RefPtr<DrawTarget> target = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(IntSize(rect.width, rect.height), SurfaceFormat::B8G8R8A8);
     if (target) {
--- a/gfx/thebes/gfx2DGlue.h
+++ b/gfx/thebes/gfx2DGlue.h
@@ -101,31 +101,16 @@ inline ExtendMode ToExtendMode(gfxPatter
     return ExtendMode::REPEAT;
   case gfxPattern::EXTEND_REFLECT:
     return ExtendMode::REFLECT;
   default:
     return ExtendMode::CLAMP;
   }
 }
 
-inline gfxPattern::GraphicsPatternType
-ThebesPatternType(PatternType aType)
-{
-  switch (aType) {
-  case PatternType::SURFACE:
-    return gfxPattern::PATTERN_SURFACE;
-  case PatternType::LINEAR_GRADIENT:
-    return gfxPattern::PATTERN_LINEAR;
-  case PatternType::RADIAL_GRADIENT:
-    return gfxPattern::PATTERN_RADIAL;
-  default:
-    return gfxPattern::PATTERN_SOLID;
-  }
-}
-
 inline gfxPattern::GraphicsExtend ThebesExtend(ExtendMode aExtend)
 {
   switch (aExtend) {
   case ExtendMode::REPEAT:
     return gfxPattern::EXTEND_REPEAT;
   case ExtendMode::REFLECT:
     return gfxPattern::EXTEND_REFLECT;
   default:
--- a/gfx/thebes/gfxFT2FontList.cpp
+++ b/gfx/thebes/gfxFT2FontList.cpp
@@ -1131,17 +1131,17 @@ FinalizeFamilyMemberList(nsStringHashKey
 }
 
 void
 gfxFT2FontList::FindFonts()
 {
     gfxFontCache *fc = gfxFontCache::GetCache();
     if (fc)
         fc->AgeAllGenerations();
-    mPrefFonts.Clear();
+    ClearLangGroupPrefFonts();
     mCodepointsWithNoFonts.reset();
 
     mCodepointsWithNoFonts.SetRange(0,0x1f);     // C0 controls
     mCodepointsWithNoFonts.SetRange(0x7f,0x9f);  // C1 controls
 
     if (!XRE_IsParentProcess()) {
         // Content process: ask the Chrome process to give us the list
         InfallibleTArray<FontListEntry> fonts;
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -404,16 +404,17 @@ public:
         uint32_t    wordCacheSpaceRules;
         uint32_t    wordCacheLong;
         uint32_t    wordCacheHit;
         uint32_t    wordCacheMiss;
         uint32_t    fallbackPrefs;
         uint32_t    fallbackSystem;
         uint32_t    textrunConst;
         uint32_t    textrunDestr;
+        uint32_t    genericLookups;
     };
 
     uint32_t reflowCount;
 
     // counts per reflow operation
     TextCounts current;
 
     // totals for the lifetime of a document
@@ -437,16 +438,17 @@ public:
         cumulative.wordCacheSpaceRules += current.wordCacheSpaceRules;
         cumulative.wordCacheLong += current.wordCacheLong;
         cumulative.wordCacheHit += current.wordCacheHit;
         cumulative.wordCacheMiss += current.wordCacheMiss;
         cumulative.fallbackPrefs += current.fallbackPrefs;
         cumulative.fallbackSystem += current.fallbackSystem;
         cumulative.textrunConst += current.textrunConst;
         cumulative.textrunDestr += current.textrunDestr;
+        cumulative.genericLookups += current.genericLookups;
         memset(&current, 0, sizeof(current));
     }
 };
 
 class gfxTextRunFactory {
     NS_INLINE_DECL_REFCOUNTING(gfxTextRunFactory)
 
 public:
--- a/gfx/thebes/gfxFontConstants.h
+++ b/gfx/thebes/gfxFontConstants.h
@@ -205,9 +205,21 @@ enum {
 // at smaller sizes <20px the ratio is closer to 0.8 while at
 // larger sizes >45px the ratio is closer to 0.667 and in between
 // a blend of values is used
 #define NS_FONT_SUB_SUPER_SIZE_RATIO_SMALL       (0.82)
 #define NS_FONT_SUB_SUPER_SIZE_RATIO_LARGE       (0.667)
 #define NS_FONT_SUB_SUPER_SMALL_SIZE             (20.0)
 #define NS_FONT_SUB_SUPER_LARGE_SIZE             (45.0)
 
+// pref lang id's for font prefs
+enum eFontPrefLang {
+    #define FONT_PREF_LANG(enum_id_, str_, atom_id_) eFontPrefLang_ ## enum_id_
+    #include "gfxFontPrefLangList.h"
+    #undef FONT_PREF_LANG
+
+    , eFontPrefLang_CJKSet  // special code for CJK set
+    , eFontPrefLang_First = eFontPrefLang_Western
+    , eFontPrefLang_Last = eFontPrefLang_Others
+    , eFontPrefLang_Count = (eFontPrefLang_Last - eFontPrefLang_First + 1)
+};
+
 #endif
--- a/gfx/thebes/gfxFontFamilyList.h
+++ b/gfx/thebes/gfxFontFamilyList.h
@@ -24,25 +24,29 @@ namespace mozilla {
 enum FontFamilyType {
   eFamily_none = 0,  // used when finding generics
 
   // explicitly named font family (e.g. Helvetica)
   eFamily_named,
   eFamily_named_quoted,
 
   // generics
-  eFamily_serif,
+  eFamily_serif,         // pref font code relies on this ordering!!!
   eFamily_sans_serif,
   eFamily_monospace,
   eFamily_cursive,
   eFamily_fantasy,
 
   // special
   eFamily_moz_variable,
-  eFamily_moz_fixed
+  eFamily_moz_fixed,
+
+  eFamily_generic_first = eFamily_serif,
+  eFamily_generic_last = eFamily_fantasy,
+  eFamily_generic_count = (eFamily_fantasy - eFamily_serif + 1)
 };
 
 enum QuotedName { eQuotedName, eUnquotedName };
 
 /**
  * font family name, a string for the name if not a generic and
  * a font type indicated named family or which generic family
  */
--- a/gfx/thebes/gfxFontconfigFonts.cpp
+++ b/gfx/thebes/gfxFontconfigFonts.cpp
@@ -20,16 +20,19 @@
 #include "gfxFT2Utils.h"
 #include "harfbuzz/hb.h"
 #include "harfbuzz/hb-ot.h"
 #include "nsUnicodeProperties.h"
 #include "nsUnicodeScriptCodes.h"
 #include "gfxFontconfigUtils.h"
 #include "gfxUserFontSet.h"
 #include "gfxFontConstants.h"
+#include "nsGkAtoms.h"
+#include "nsILanguageAtomService.h"
+#include "nsServiceManagerUtils.h"
 
 #include <cairo.h>
 #include <cairo-ft.h>
 #include "mozilla/gfx/HelpersCairo.h"
 
 #include <fontconfig/fcfreetype.h>
 #include <pango/pango.h>
 
@@ -1276,19 +1279,150 @@ gfxPangoFontGroup::~gfxPangoFontGroup()
 
 gfxFontGroup *
 gfxPangoFontGroup::Copy(const gfxFontStyle *aStyle)
 {
     return new gfxPangoFontGroup(mFamilyList, aStyle, mUserFontSet);
 }
 
 void
-gfxPangoFontGroup::FindPlatformFont(const nsAString& fontName,
-                                    bool aUseFontSet,
-                                    void *aClosure)
+gfxPangoFontGroup::FindGenericFontsPFG(FontFamilyType aGenericType,
+                                       nsIAtom *aLanguage,
+                                       void *aClosure)
+{
+    nsAutoTArray<nsString, 5> resolvedGenerics;
+    ResolveGenericFontNamesPFG(aGenericType, aLanguage, resolvedGenerics);
+    uint32_t g = 0, numGenerics = resolvedGenerics.Length();
+    for (g = 0; g < numGenerics; g++) {
+        FindPlatformFontPFG(resolvedGenerics[g], false, aClosure);
+    }
+}
+
+/* static */ void
+gfxPangoFontGroup::ResolveGenericFontNamesPFG(FontFamilyType aGenericType,
+                                              nsIAtom *aLanguage,
+                                              nsTArray<nsString>& aGenericFamilies)
+{
+    static const char kGeneric_serif[] = "serif";
+    static const char kGeneric_sans_serif[] = "sans-serif";
+    static const char kGeneric_monospace[] = "monospace";
+    static const char kGeneric_cursive[] = "cursive";
+    static const char kGeneric_fantasy[] = "fantasy";
+
+    // treat -moz-fixed as monospace
+    if (aGenericType == eFamily_moz_fixed) {
+        aGenericType = eFamily_monospace;
+    }
+
+    // type should be standard generic type at this point
+    NS_ASSERTION(aGenericType >= eFamily_serif &&
+                 aGenericType <= eFamily_fantasy,
+                 "standard generic font family type required");
+
+    // create the lang string
+    nsIAtom *langGroupAtom = nullptr;
+    nsAutoCString langGroupString;
+    if (aLanguage) {
+        if (!gLangService) {
+            CallGetService(NS_LANGUAGEATOMSERVICE_CONTRACTID, &gLangService);
+        }
+        if (gLangService) {
+            nsresult rv;
+            langGroupAtom = gLangService->GetLanguageGroup(aLanguage, &rv);
+        }
+    }
+    if (!langGroupAtom) {
+        langGroupAtom = nsGkAtoms::Unicode;
+    }
+    langGroupAtom->ToUTF8String(langGroupString);
+
+    // map generic type to string
+    const char *generic = nullptr;
+    switch (aGenericType) {
+        case eFamily_serif:
+            generic = kGeneric_serif;
+            break;
+        case eFamily_sans_serif:
+            generic = kGeneric_sans_serif;
+            break;
+        case eFamily_monospace:
+            generic = kGeneric_monospace;
+            break;
+        case eFamily_cursive:
+            generic = kGeneric_cursive;
+            break;
+        case eFamily_fantasy:
+            generic = kGeneric_fantasy;
+            break;
+        default:
+            break;
+    }
+
+    if (!generic) {
+        return;
+    }
+
+    aGenericFamilies.Clear();
+
+    // load family for "font.name.generic.lang"
+    nsAutoCString prefFontName("font.name.");
+    prefFontName.Append(generic);
+    prefFontName.Append('.');
+    prefFontName.Append(langGroupString);
+    gfxFontUtils::AppendPrefsFontList(prefFontName.get(),
+                                      aGenericFamilies);
+
+    // if lang has pref fonts, also load fonts for "font.name-list.generic.lang"
+    if (!aGenericFamilies.IsEmpty()) {
+        nsAutoCString prefFontListName("font.name-list.");
+        prefFontListName.Append(generic);
+        prefFontListName.Append('.');
+        prefFontListName.Append(langGroupString);
+        gfxFontUtils::AppendPrefsFontList(prefFontListName.get(),
+                                          aGenericFamilies);
+    }
+
+#if 0  // dump out generic mappings
+    printf("%s ===> ", prefFontName.get());
+    for (uint32_t k = 0; k < aGenericFamilies.Length(); k++) {
+        if (k > 0) printf(", ");
+        printf("%s", NS_ConvertUTF16toUTF8(aGenericFamilies[k]).get());
+    }
+    printf("\n");
+#endif
+}
+
+void gfxPangoFontGroup::EnumerateFontListPFG(nsIAtom *aLanguage, void *aClosure)
+{
+    // initialize fonts in the font family list
+    const nsTArray<FontFamilyName>& fontlist = mFamilyList.GetFontlist();
+
+    // lookup fonts in the fontlist
+    uint32_t i, numFonts = fontlist.Length();
+    for (i = 0; i < numFonts; i++) {
+        const FontFamilyName& name = fontlist[i];
+        if (name.IsNamed()) {
+            FindPlatformFontPFG(name.mName, true, aClosure);
+        } else {
+            FindGenericFontsPFG(name.mType, aLanguage, aClosure);
+        }
+    }
+
+    // if necessary, append default generic onto the end
+    if (mFamilyList.GetDefaultFontType() != eFamily_none &&
+        !mFamilyList.HasDefaultGeneric()) {
+        FindGenericFontsPFG(mFamilyList.GetDefaultFontType(),
+                            aLanguage, aClosure);
+    }
+}
+
+void
+gfxPangoFontGroup::FindPlatformFontPFG(const nsAString& fontName,
+                                       bool aUseFontSet,
+                                       void *aClosure)
 {
     nsTArray<nsString> *list = static_cast<nsTArray<nsString>*>(aClosure);
 
     if (!list->Contains(fontName)) {
         // names present in the user fontset are not matched against system fonts
         if (aUseFontSet && mUserFontSet && mUserFontSet->HasFamily(fontName)) {
             nsAutoString userFontName =
                 NS_LITERAL_STRING(FONT_FACE_FAMILY_PREFIX) + fontName;
@@ -1355,18 +1489,18 @@ gfxPangoFontGroup::MakeFontSet(PangoLang
 
     nsRefPtr <nsIAtom> langGroup;
     if (aLang != mPangoLanguage) {
         // Set up langGroup for Mozilla's font prefs.
         langGroup = do_GetAtom(lang);
     }
 
     nsAutoTArray<nsString, 20> fcFamilyList;
-    EnumerateFontList(langGroup ? langGroup.get() : mStyle.language.get(),
-                      &fcFamilyList);
+    EnumerateFontListPFG(langGroup ? langGroup.get() : mStyle.language.get(),
+                         &fcFamilyList);
 
     // To consider: A fontset cache here could be helpful.
 
     // Get a pattern suitable for matching.
     nsAutoRef<FcPattern> pattern
         (gfxFontconfigUtils::NewPattern(fcFamilyList, mStyle, lang));
 
     PrepareSortPattern(pattern, mStyle.size, aSizeAdjustFactor, mStyle.printerFont);
--- a/gfx/thebes/gfxFontconfigFonts.h
+++ b/gfx/thebes/gfxFontconfigFonts.h
@@ -89,17 +89,35 @@ private:
 
     gfxFloat GetSizeAdjustFactor()
     {
         if (mFontSets.Length() == 0)
             GetBaseFontSet();
         return mSizeAdjustFactor;
     }
 
-    virtual void FindPlatformFont(const nsAString& aName,
-                                  bool aUseFontSet,
-                                  void *aClosure);
+    // old helper methods from gfxFontGroup, moved here so that those methods
+    // can be revamped without affecting the legacy code here
+
+    // iterate over the fontlist, lookup names and expand generics
+    void EnumerateFontListPFG(nsIAtom *aLanguage, void *aClosure);
+
+    // expand a generic to a list of specific names based on prefs
+    void FindGenericFontsPFG(mozilla::FontFamilyType aGenericType,
+                             nsIAtom *aLanguage,
+                             void *aClosure);
+
+    // lookup and add a font with a given name (i.e. *not* a generic!)
+    void FindPlatformFontPFG(const nsAString& aName,
+                             bool aUseFontSet,
+                             void *aClosure);
+
+    static void
+    ResolveGenericFontNamesPFG(mozilla::FontFamilyType aGenericType,
+                               nsIAtom *aLanguage,
+                               nsTArray<nsString>& aGenericFamilies);
+
 
     friend class gfxSystemFcFontEntry;
     static FT_Library GetFTLibrary();
 };
 
 #endif /* GFX_FONTCONFIG_FONTS_H */
--- a/gfx/thebes/gfxPattern.cpp
+++ b/gfx/thebes/gfxPattern.cpp
@@ -214,19 +214,13 @@ gfxPattern::GetSolidColor(Color& aColorO
   if (mGfxPattern.GetPattern()->GetType() == PatternType::COLOR) {
     aColorOut = static_cast<ColorPattern*>(mGfxPattern.GetPattern())->mColor;
     return true;
   }
 
  return false;
 }
 
-gfxPattern::GraphicsPatternType
-gfxPattern::GetType() const
-{
-  return ThebesPatternType(mGfxPattern.GetPattern()->GetType());
-}
-
 int
 gfxPattern::CairoStatus()
 {
   return CAIRO_STATUS_SUCCESS;
 }
--- a/gfx/thebes/gfxPattern.h
+++ b/gfx/thebes/gfxPattern.h
@@ -69,25 +69,16 @@ public:
         // usually becomes PAD.
         EXTEND_PAD_EDGE = 1000
     };
 
     // none, repeat, reflect
     void SetExtend(GraphicsExtend extend);
     GraphicsExtend Extend() const;
 
-    enum GraphicsPatternType {
-        PATTERN_SOLID,
-        PATTERN_SURFACE,
-        PATTERN_LINEAR,
-        PATTERN_RADIAL
-    };
-
-    GraphicsPatternType GetType() const;
-
     int CairoStatus();
 
     void SetFilter(GraphicsFilter filter);
     GraphicsFilter Filter() const;
 
     /* returns TRUE if it succeeded */
     bool GetSolidColor(mozilla::gfx::Color& aColorOut);
 
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -363,37 +363,16 @@ MemoryPressureObserver::Observe(nsISuppo
     NS_ASSERTION(strcmp(aTopic, "memory-pressure") == 0, "unexpected event topic");
     Factory::PurgeAllCaches();
     gfxGradientCache::PurgeAllCaches();
 
     gfxPlatform::GetPlatform()->PurgeSkiaCache();
     return NS_OK;
 }
 
-// xxx - this can probably be eliminated by reworking pref font handling code
-static const char *gPrefLangNames[] = {
-    #define FONT_PREF_LANG(enum_id_, str_, atom_id_) str_
-    #include "gfxFontPrefLangList.h"
-    #undef FONT_PREF_LANG
-};
-
-static nsIAtom* PrefLangToLangGroups(uint32_t aIndex)
-{
-    // static array here avoids static constructor
-    static nsIAtom* gPrefLangToLangGroups[] = {
-        #define FONT_PREF_LANG(enum_id_, str_, atom_id_) nsGkAtoms::atom_id_
-        #include "gfxFontPrefLangList.h"
-        #undef FONT_PREF_LANG
-    };
-
-    return aIndex < ArrayLength(gPrefLangToLangGroups)
-         ? gPrefLangToLangGroups[aIndex]
-         : nsGkAtoms::Unicode;
-}
-
 gfxPlatform::gfxPlatform()
   : mTileWidth(-1)
   , mTileHeight(-1)
   , mAzureCanvasBackendCollector(this, &gfxPlatform::GetAzureBackendInfo)
   , mApzSupportCollector(this, &gfxPlatform::GetApzSupportInfo)
   , mCompositorBackend(layers::LayersBackend::LAYERS_NONE)
   , mScreenDepth(0)
 {
@@ -1356,170 +1335,16 @@ gfxPlatform::MakePlatformFont(const nsAS
     // using the data to instantiate the font, and taking responsibility
     // for freeing it when no longer required.
     if (aFontData) {
         free((void*)aFontData);
     }
     return nullptr;
 }
 
-bool gfxPlatform::ForEachPrefFont(eFontPrefLang aLangArray[], uint32_t aLangArrayLen, PrefFontCallback aCallback,
-                                    void *aClosure)
-{
-    NS_ENSURE_TRUE(Preferences::GetRootBranch(), false);
-
-    uint32_t    i;
-    for (i = 0; i < aLangArrayLen; i++) {
-        eFontPrefLang prefLang = aLangArray[i];
-        const char *langGroup = GetPrefLangName(prefLang);
-
-        nsAutoCString prefName;
-
-        prefName.AssignLiteral("font.default.");
-        prefName.Append(langGroup);
-        nsAdoptingCString genericDotLang = Preferences::GetCString(prefName.get());
-
-        genericDotLang.Append('.');
-        genericDotLang.Append(langGroup);
-
-        // fetch font.name.xxx value
-        prefName.AssignLiteral("font.name.");
-        prefName.Append(genericDotLang);
-        nsAdoptingCString nameValue = Preferences::GetCString(prefName.get());
-        if (nameValue) {
-            if (!aCallback(prefLang, NS_ConvertUTF8toUTF16(nameValue), aClosure))
-                return false;
-        }
-
-        // fetch font.name-list.xxx value
-        prefName.AssignLiteral("font.name-list.");
-        prefName.Append(genericDotLang);
-        nsAdoptingCString nameListValue = Preferences::GetCString(prefName.get());
-        if (nameListValue && !nameListValue.Equals(nameValue)) {
-            const char kComma = ',';
-            const char *p, *p_end;
-            nsAutoCString list(nameListValue);
-            list.BeginReading(p);
-            list.EndReading(p_end);
-            while (p < p_end) {
-                while (nsCRT::IsAsciiSpace(*p)) {
-                    if (++p == p_end)
-                        break;
-                }
-                if (p == p_end)
-                    break;
-                const char *start = p;
-                while (++p != p_end && *p != kComma)
-                    /* nothing */ ;
-                nsAutoCString fontName(Substring(start, p));
-                fontName.CompressWhitespace(false, true);
-                if (!aCallback(prefLang, NS_ConvertUTF8toUTF16(fontName), aClosure))
-                    return false;
-                p++;
-            }
-        }
-    }
-
-    return true;
-}
-
-eFontPrefLang
-gfxPlatform::GetFontPrefLangFor(const char* aLang)
-{
-    if (!aLang || !aLang[0]) {
-        return eFontPrefLang_Others;
-    }
-    for (uint32_t i = 0; i < ArrayLength(gPrefLangNames); ++i) {
-        if (!PL_strcasecmp(gPrefLangNames[i], aLang)) {
-            return eFontPrefLang(i);
-        }
-    }
-    return eFontPrefLang_Others;
-}
-
-eFontPrefLang
-gfxPlatform::GetFontPrefLangFor(nsIAtom *aLang)
-{
-    if (!aLang)
-        return eFontPrefLang_Others;
-    nsAutoCString lang;
-    aLang->ToUTF8String(lang);
-    return GetFontPrefLangFor(lang.get());
-}
-
-nsIAtom*
-gfxPlatform::GetLangGroupForPrefLang(eFontPrefLang aLang)
-{
-    // the special CJK set pref lang should be resolved into separate
-    // calls to individual CJK pref langs before getting here
-    NS_ASSERTION(aLang != eFontPrefLang_CJKSet, "unresolved CJK set pref lang");
-
-    return PrefLangToLangGroups(uint32_t(aLang));
-}
-
-const char*
-gfxPlatform::GetPrefLangName(eFontPrefLang aLang)
-{
-    if (uint32_t(aLang) < ArrayLength(gPrefLangNames)) {
-        return gPrefLangNames[uint32_t(aLang)];
-    }
-    return nullptr;
-}
-
-eFontPrefLang
-gfxPlatform::GetFontPrefLangFor(uint8_t aUnicodeRange)
-{
-    switch (aUnicodeRange) {
-        case kRangeSetLatin:   return eFontPrefLang_Western;
-        case kRangeCyrillic:   return eFontPrefLang_Cyrillic;
-        case kRangeGreek:      return eFontPrefLang_Greek;
-        case kRangeHebrew:     return eFontPrefLang_Hebrew;
-        case kRangeArabic:     return eFontPrefLang_Arabic;
-        case kRangeThai:       return eFontPrefLang_Thai;
-        case kRangeKorean:     return eFontPrefLang_Korean;
-        case kRangeJapanese:   return eFontPrefLang_Japanese;
-        case kRangeSChinese:   return eFontPrefLang_ChineseCN;
-        case kRangeTChinese:   return eFontPrefLang_ChineseTW;
-        case kRangeDevanagari: return eFontPrefLang_Devanagari;
-        case kRangeTamil:      return eFontPrefLang_Tamil;
-        case kRangeArmenian:   return eFontPrefLang_Armenian;
-        case kRangeBengali:    return eFontPrefLang_Bengali;
-        case kRangeCanadian:   return eFontPrefLang_Canadian;
-        case kRangeEthiopic:   return eFontPrefLang_Ethiopic;
-        case kRangeGeorgian:   return eFontPrefLang_Georgian;
-        case kRangeGujarati:   return eFontPrefLang_Gujarati;
-        case kRangeGurmukhi:   return eFontPrefLang_Gurmukhi;
-        case kRangeKhmer:      return eFontPrefLang_Khmer;
-        case kRangeMalayalam:  return eFontPrefLang_Malayalam;
-        case kRangeOriya:      return eFontPrefLang_Oriya;
-        case kRangeTelugu:     return eFontPrefLang_Telugu;
-        case kRangeKannada:    return eFontPrefLang_Kannada;
-        case kRangeSinhala:    return eFontPrefLang_Sinhala;
-        case kRangeTibetan:    return eFontPrefLang_Tibetan;
-        case kRangeSetCJK:     return eFontPrefLang_CJKSet;
-        default:               return eFontPrefLang_Others;
-    }
-}
-
-bool
-gfxPlatform::IsLangCJK(eFontPrefLang aLang)
-{
-    switch (aLang) {
-        case eFontPrefLang_Japanese:
-        case eFontPrefLang_ChineseTW:
-        case eFontPrefLang_ChineseCN:
-        case eFontPrefLang_ChineseHK:
-        case eFontPrefLang_Korean:
-        case eFontPrefLang_CJKSet:
-            return true;
-        default:
-            return false;
-    }
-}
-
 mozilla::layers::DiagnosticTypes
 gfxPlatform::GetLayerDiagnosticTypes()
 {
   mozilla::layers::DiagnosticTypes type = DiagnosticTypes::NO_DIAGNOSTIC;
   if (gfxPrefs::DrawLayerBorders()) {
     type |= mozilla::layers::DiagnosticTypes::LAYER_BORDERS;
   }
   if (gfxPrefs::DrawTileBorders()) {
@@ -1530,153 +1355,16 @@ gfxPlatform::GetLayerDiagnosticTypes()
   }
   if (gfxPrefs::FlashLayerBorders()) {
     type |= mozilla::layers::DiagnosticTypes::FLASH_BORDERS;
   }
   return type;
 }
 
 void
-gfxPlatform::GetLangPrefs(eFontPrefLang aPrefLangs[], uint32_t &aLen, eFontPrefLang aCharLang, eFontPrefLang aPageLang)
-{
-    if (IsLangCJK(aCharLang)) {
-        AppendCJKPrefLangs(aPrefLangs, aLen, aCharLang, aPageLang);
-    } else {
-        AppendPrefLang(aPrefLangs, aLen, aCharLang);
-    }
-
-    AppendPrefLang(aPrefLangs, aLen, eFontPrefLang_Others);
-}
-
-void
-gfxPlatform::AppendCJKPrefLangs(eFontPrefLang aPrefLangs[], uint32_t &aLen, eFontPrefLang aCharLang, eFontPrefLang aPageLang)
-{
-    // prefer the lang specified by the page *if* CJK
-    if (IsLangCJK(aPageLang)) {
-        AppendPrefLang(aPrefLangs, aLen, aPageLang);
-    }
-
-    // if not set up, set up the default CJK order, based on accept lang settings and locale
-    if (mCJKPrefLangs.Length() == 0) {
-
-        // temp array
-        eFontPrefLang tempPrefLangs[kMaxLenPrefLangList];
-        uint32_t tempLen = 0;
-
-        // Add the CJK pref fonts from accept languages, the order should be same order
-        nsAdoptingCString list = Preferences::GetLocalizedCString("intl.accept_languages");
-        if (!list.IsEmpty()) {
-            const char kComma = ',';
-            const char *p, *p_end;
-            list.BeginReading(p);
-            list.EndReading(p_end);
-            while (p < p_end) {
-                while (nsCRT::IsAsciiSpace(*p)) {
-                    if (++p == p_end)
-                        break;
-                }
-                if (p == p_end)
-                    break;
-                const char *start = p;
-                while (++p != p_end && *p != kComma)
-                    /* nothing */ ;
-                nsAutoCString lang(Substring(start, p));
-                lang.CompressWhitespace(false, true);
-                eFontPrefLang fpl = gfxPlatform::GetFontPrefLangFor(lang.get());
-                switch (fpl) {
-                    case eFontPrefLang_Japanese:
-                    case eFontPrefLang_Korean:
-                    case eFontPrefLang_ChineseCN:
-                    case eFontPrefLang_ChineseHK:
-                    case eFontPrefLang_ChineseTW:
-                        AppendPrefLang(tempPrefLangs, tempLen, fpl);
-                        break;
-                    default:
-                        break;
-                }
-                p++;
-            }
-        }
-
-        do { // to allow 'break' to abort this block if a call fails
-            nsresult rv;
-            nsCOMPtr<nsILocaleService> ls =
-                do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv);
-            if (NS_FAILED(rv))
-                break;
-
-            nsCOMPtr<nsILocale> appLocale;
-            rv = ls->GetApplicationLocale(getter_AddRefs(appLocale));
-            if (NS_FAILED(rv))
-                break;
-
-            nsString localeStr;
-            rv = appLocale->
-                GetCategory(NS_LITERAL_STRING(NSILOCALE_MESSAGE), localeStr);
-            if (NS_FAILED(rv))
-                break;
-
-            const nsAString& lang = Substring(localeStr, 0, 2);
-            if (lang.EqualsLiteral("ja")) {
-                AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Japanese);
-            } else if (lang.EqualsLiteral("zh")) {
-                const nsAString& region = Substring(localeStr, 3, 2);
-                if (region.EqualsLiteral("CN")) {
-                    AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseCN);
-                } else if (region.EqualsLiteral("TW")) {
-                    AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseTW);
-                } else if (region.EqualsLiteral("HK")) {
-                    AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseHK);
-                }
-            } else if (lang.EqualsLiteral("ko")) {
-                AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Korean);
-            }
-        } while (0);
-
-        // last resort... (the order is same as old gfx.)
-        AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Japanese);
-        AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Korean);
-        AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseCN);
-        AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseHK);
-        AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseTW);
-
-        // copy into the cached array
-        uint32_t j;
-        for (j = 0; j < tempLen; j++) {
-            mCJKPrefLangs.AppendElement(tempPrefLangs[j]);
-        }
-    }
-
-    // append in cached CJK langs
-    uint32_t  i, numCJKlangs = mCJKPrefLangs.Length();
-
-    for (i = 0; i < numCJKlangs; i++) {
-        AppendPrefLang(aPrefLangs, aLen, (eFontPrefLang) (mCJKPrefLangs[i]));
-    }
-
-}
-
-void
-gfxPlatform::AppendPrefLang(eFontPrefLang aPrefLangs[], uint32_t& aLen, eFontPrefLang aAddLang)
-{
-    if (aLen >= kMaxLenPrefLangList) return;
-
-    // make sure
-    uint32_t  i = 0;
-    while (i < aLen && aPrefLangs[i] != aAddLang) {
-        i++;
-    }
-
-    if (i == aLen) {
-        aPrefLangs[aLen] = aAddLang;
-        aLen++;
-    }
-}
-
-void
 gfxPlatform::InitBackendPrefs(uint32_t aCanvasBitmask, BackendType aCanvasDefault,
                               uint32_t aContentBitmask, BackendType aContentDefault)
 {
     mPreferredCanvasBackend = GetCanvasBackendPref(aCanvasBitmask);
     if (mPreferredCanvasBackend == BackendType::NONE) {
         mPreferredCanvasBackend = aCanvasDefault;
     }
 
--- a/gfx/thebes/gfxPlatform.h
+++ b/gfx/thebes/gfxPlatform.h
@@ -65,25 +65,16 @@ BackendTypeBit(BackendType b)
   do { \
     if (gfxPlatform::PerfWarnings()) { \
       printf_stderr("[" module "] " __VA_ARGS__); \
     } \
   } while (0)
 
 extern cairo_user_data_key_t kDrawTarget;
 
-// pref lang id's for font prefs
-enum eFontPrefLang {
-    #define FONT_PREF_LANG(enum_id_, str_, atom_id_) eFontPrefLang_ ## enum_id_
-    #include "gfxFontPrefLangList.h"
-    #undef FONT_PREF_LANG
-
-    , eFontPrefLang_CJKSet  // special code for CJK set
-};
-
 enum eCMSMode {
     eCMSMode_Off          = 0,     // No color management
     eCMSMode_All          = 1,     // Color manage everything
     eCMSMode_TaggedOnly   = 2,     // Color manage tagged Images Only
     eCMSMode_AllCount     = 3
 };
 
 enum eGfxLog {
@@ -422,51 +413,16 @@ public:
      */
     bool UseGraphiteShaping();
 
     // check whether format is supported on a platform or not (if unclear, returns true)
     virtual bool IsFontFormatSupported(nsIURI *aFontURI, uint32_t aFormatFlags) { return false; }
 
     virtual bool DidRenderingDeviceReset(DeviceResetReason* aResetReason = nullptr) { return false; }
 
-    // in some situations, need to make decisions about ambiguous characters, may need to look at multiple pref langs
-    void GetLangPrefs(eFontPrefLang aPrefLangs[], uint32_t &aLen, eFontPrefLang aCharLang, eFontPrefLang aPageLang);
-    
-    /**
-     * Iterate over pref fonts given a list of lang groups.  For a single lang
-     * group, multiple pref fonts are possible.  If error occurs, returns false,
-     * true otherwise.  Callback returns false to abort process.
-     */
-    typedef bool (*PrefFontCallback) (eFontPrefLang aLang, const nsAString& aName,
-                                        void *aClosure);
-    static bool ForEachPrefFont(eFontPrefLang aLangArray[], uint32_t aLangArrayLen,
-                                  PrefFontCallback aCallback,
-                                  void *aClosure);
-
-    // convert a lang group to enum constant (i.e. "zh-TW" ==> eFontPrefLang_ChineseTW)
-    static eFontPrefLang GetFontPrefLangFor(const char* aLang);
-
-    // convert a lang group atom to enum constant
-    static eFontPrefLang GetFontPrefLangFor(nsIAtom *aLang);
-
-    // convert an enum constant to a lang group atom
-    static nsIAtom* GetLangGroupForPrefLang(eFontPrefLang aLang);
-
-    // convert a enum constant to lang group string (i.e. eFontPrefLang_ChineseTW ==> "zh-TW")
-    static const char* GetPrefLangName(eFontPrefLang aLang);
-   
-    // map a Unicode range (based on char code) to a font language for Preferences
-    static eFontPrefLang GetFontPrefLangFor(uint8_t aUnicodeRange);
-
-    // returns true if a pref lang is CJK
-    static bool IsLangCJK(eFontPrefLang aLang);
-    
-    // helper method to add a pref lang to an array, if not already in array
-    static void AppendPrefLang(eFontPrefLang aPrefLangs[], uint32_t& aLen, eFontPrefLang aAddLang);
-
     // returns a list of commonly used fonts for a given character
     // these are *possible* matches, no cmap-checking is done at this level
     virtual void GetCommonFallbackFonts(uint32_t /*aCh*/, uint32_t /*aNextCh*/,
                                         int32_t /*aRunScript*/,
                                         nsTArray<const char*>& /*aFontList*/)
     {
         // platform-specific override, by default do nothing
     }
@@ -662,19 +618,16 @@ public:
     // Return information on how child processes should initialize graphics
     // devices. Currently this is only used on Windows.
     virtual void GetDeviceInitData(mozilla::gfx::DeviceInitData* aOut);
 
 protected:
     gfxPlatform();
     virtual ~gfxPlatform();
 
-    void AppendCJKPrefLangs(eFontPrefLang aPrefLangs[], uint32_t &aLen,
-                            eFontPrefLang aCharLang, eFontPrefLang aPageLang);
-
     /**
      * Initialized hardware vsync based on each platform.
      */
     virtual already_AddRefed<mozilla::gfx::VsyncSource> CreateHardwareVsyncSource();
 
     // Returns whether or not layers should be accelerated by default on this platform.
     virtual bool AccelerateLayersByDefault();
 
@@ -779,17 +732,16 @@ private:
     void ComputeTileSize();
 
     /**
      * This uses nsIScreenManager to determine the screen size and color depth
      */
     void PopulateScreenInfo();
 
     nsRefPtr<gfxASurface> mScreenReferenceSurface;
-    nsTArray<uint32_t> mCJKPrefLangs;
     nsCOMPtr<nsIObserver> mSRGBOverrideObserver;
     nsCOMPtr<nsIObserver> mFontPrefsObserver;
     nsCOMPtr<nsIObserver> mMemoryPressureObserver;
 
     // The preferred draw target backend to use for canvas
     mozilla::gfx::BackendType mPreferredCanvasBackend;
     // The fallback draw target backend to use for canvas, if the preferred backend fails
     mozilla::gfx::BackendType mFallbackCanvasBackend;
--- a/gfx/thebes/gfxPlatformFontList.cpp
+++ b/gfx/thebes/gfxPlatformFontList.cpp
@@ -4,16 +4,20 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/Logging.h"
 
 #include "gfxPlatformFontList.h"
 #include "gfxTextRun.h"
 #include "gfxUserFontSet.h"
 
+#include "nsCRT.h"
+#include "nsGkAtoms.h"
+#include "nsILocaleService.h"
+#include "nsServiceManagerUtils.h"
 #include "nsUnicharUtils.h"
 #include "nsUnicodeRange.h"
 #include "nsUnicodeProperties.h"
 
 #include "mozilla/Attributes.h"
 #include "mozilla/Likely.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Preferences.h"
@@ -89,16 +93,26 @@ const gfxFontEntry::ScriptRange gfxPlatf
 
 static const char* kObservedPrefs[] = {
     "font.",
     "font.name-list.",
     "intl.accept_languages",  // hmmmm...
     nullptr
 };
 
+// xxx - this can probably be eliminated by reworking pref font handling code
+static const char *gPrefLangNames[] = {
+    #define FONT_PREF_LANG(enum_id_, str_, atom_id_) str_
+    #include "gfxFontPrefLangList.h"
+    #undef FONT_PREF_LANG
+};
+
+static_assert(MOZ_ARRAY_LENGTH(gPrefLangNames) == uint32_t(eFontPrefLang_Count),
+              "size of pref lang name array doesn't match pref lang enum size");
+
 class gfxFontListPrefObserver final : public nsIObserver {
     ~gfxFontListPrefObserver() {}
 public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSIOBSERVER
 };
 
 static gfxFontListPrefObserver* gFontListPrefObserver = nullptr;
@@ -108,17 +122,17 @@ NS_IMPL_ISUPPORTS(gfxFontListPrefObserve
 NS_IMETHODIMP
 gfxFontListPrefObserver::Observe(nsISupports     *aSubject,
                                  const char      *aTopic,
                                  const char16_t *aData)
 {
     NS_ASSERTION(!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID), "invalid topic");
     // XXX this could be made to only clear out the cache for the prefs that were changed
     // but it probably isn't that big a deal.
-    gfxPlatformFontList::PlatformFontList()->ClearPrefFonts();
+    gfxPlatformFontList::PlatformFontList()->ClearLangGroupPrefFonts();
     gfxFontCache::GetCache()->AgeAllGenerations();
     return NS_OK;
 }
 
 MOZ_DEFINE_MALLOC_SIZE_OF(FontListMallocSizeOf)
 
 NS_IMPL_ISUPPORTS(gfxPlatformFontList::MemoryReporter, nsIMemoryReporter)
 
@@ -157,17 +171,17 @@ gfxPlatformFontList::MemoryReporter::Col
                       aClosure);
     }
 
     return NS_OK;
 }
 
 gfxPlatformFontList::gfxPlatformFontList(bool aNeedFullnamePostscriptNames)
     : mFontFamilies(64), mOtherFamilyNames(16),
-      mPrefFonts(8), mBadUnderlineFamilyNames(8), mSharedCmaps(8),
+      mBadUnderlineFamilyNames(8), mSharedCmaps(8),
       mStartIndex(0), mIncrement(1), mNumFamilies(0), mFontlistInitCount(0)
 {
     mOtherFamilyNamesInitialized = false;
 
     if (aNeedFullnamePostscriptNames) {
         mExtraNames = new ExtraNames();
     }
     mFaceNameListsInitialized = false;
@@ -182,21 +196,25 @@ gfxPlatformFontList::gfxPlatformFontList
     Preferences::AddStrongObservers(gFontListPrefObserver, kObservedPrefs);
 
     RegisterStrongMemoryReporter(new MemoryReporter());
 }
 
 gfxPlatformFontList::~gfxPlatformFontList()
 {
     mSharedCmaps.Clear();
+    ClearLangGroupPrefFonts();
     NS_ASSERTION(gFontListPrefObserver, "There is no font list pref observer");
     Preferences::RemoveObservers(gFontListPrefObserver, kObservedPrefs);
     NS_RELEASE(gFontListPrefObserver);
 }
 
+// number of CSS generic font families
+const uint32_t kNumGenerics = 5;
+
 nsresult
 gfxPlatformFontList::InitFontList()
 {
     mFontlistInitCount++;
 
     if (LOG_FONTINIT_ENABLED()) {
         LOG_FONTINIT(("(fontinit) system fontlist initialization\n"));
     }
@@ -211,17 +229,17 @@ gfxPlatformFontList::InitFontList()
     mFontFamilies.Clear();
     mOtherFamilyNames.Clear();
     mOtherFamilyNamesInitialized = false;
     if (mExtraNames) {
         mExtraNames->mFullnames.Clear();
         mExtraNames->mPostscriptNames.Clear();
     }
     mFaceNameListsInitialized = false;
-    mPrefFonts.Clear();
+    ClearLangGroupPrefFonts();
     mReplacementCharFallbackFamily = nullptr;
     CancelLoader();
 
     // initialize ranges of characters for which system-wide font search should be skipped
     mCodepointsWithNoFonts.reset();
     mCodepointsWithNoFonts.SetRange(0,0x1f);     // C0 controls
     mCodepointsWithNoFonts.SetRange(0x7f,0x9f);  // C1 controls
 
@@ -698,28 +716,16 @@ gfxPlatformFontList::FindFontForFamily(c
     aNeedsBold = false;
 
     if (familyEntry)
         return familyEntry->FindFontForStyle(*aStyle, aNeedsBold);
 
     return nullptr;
 }
 
-bool
-gfxPlatformFontList::GetPrefFontFamilyEntries(eFontPrefLang aLangGroup, nsTArray<nsRefPtr<gfxFontFamily> > *array)
-{
-    return mPrefFonts.Get(uint32_t(aLangGroup), array);
-}
-
-void
-gfxPlatformFontList::SetPrefFontFamilyEntries(eFontPrefLang aLangGroup, nsTArray<nsRefPtr<gfxFontFamily> >& array)
-{
-    mPrefFonts.Put(uint32_t(aLangGroup), array);
-}
-
 void 
 gfxPlatformFontList::AddOtherFamilyName(gfxFontFamily *aFamilyEntry, nsAString& aOtherFamilyName)
 {
     nsAutoString key;
     GenerateFontListKey(aOtherFamilyName, key);
 
     if (!mOtherFamilyNames.GetWeak(key)) {
         mOtherFamilyNames.Put(key, aFamilyEntry);
@@ -797,16 +803,429 @@ gfxPlatformFontList::RemoveCmap(const gf
     CharMapHashKey *found =
         mSharedCmaps.GetEntry(const_cast<gfxCharacterMap*>(aCharMap));
     if (found && found->GetKey() == aCharMap) {
         mSharedCmaps.RemoveEntry(const_cast<gfxCharacterMap*>(aCharMap));
     }
 }
 
 void
+gfxPlatformFontList::ResolveGenericFontNames(
+    FontFamilyType aGenericType,
+    eFontPrefLang aPrefLang,
+    nsTArray<nsRefPtr<gfxFontFamily>>* aGenericFamilies
+)
+{
+    const char* langGroupStr = GetPrefLangName(aPrefLang);
+
+    static const char kGeneric_serif[] = "serif";
+    static const char kGeneric_sans_serif[] = "sans-serif";
+    static const char kGeneric_monospace[] = "monospace";
+    static const char kGeneric_cursive[] = "cursive";
+    static const char kGeneric_fantasy[] = "fantasy";
+
+    // type should be standard generic type at this point
+    NS_ASSERTION(aGenericType >= eFamily_serif &&
+                 aGenericType <= eFamily_fantasy,
+                 "standard generic font family type required");
+
+    // map generic type to string
+    const char *generic = nullptr;
+    switch (aGenericType) {
+        case eFamily_serif:
+            generic = kGeneric_serif;
+            break;
+        case eFamily_sans_serif:
+            generic = kGeneric_sans_serif;
+            break;
+        case eFamily_monospace:
+            generic = kGeneric_monospace;
+            break;
+        case eFamily_cursive:
+            generic = kGeneric_cursive;
+            break;
+        case eFamily_fantasy:
+            generic = kGeneric_fantasy;
+            break;
+        default:
+            break;
+    }
+
+    if (!generic) {
+        return;
+    }
+
+    nsAutoTArray<nsString,4> genericFamilies;
+
+    // load family for "font.name.generic.lang"
+    nsAutoCString prefFontName("font.name.");
+    prefFontName.Append(generic);
+    prefFontName.Append('.');
+    prefFontName.Append(langGroupStr);
+    gfxFontUtils::AppendPrefsFontList(prefFontName.get(), genericFamilies);
+
+    // load fonts for "font.name-list.generic.lang"
+    nsAutoCString prefFontListName("font.name-list.");
+    prefFontListName.Append(generic);
+    prefFontListName.Append('.');
+    prefFontListName.Append(langGroupStr);
+    gfxFontUtils::AppendPrefsFontList(prefFontListName.get(), genericFamilies);
+
+    nsIAtom* langGroup = GetLangGroupForPrefLang(aPrefLang);
+    NS_ASSERTION(langGroup, "null lang group for pref lang");
+
+    // lookup and add platform fonts uniquely
+    for (const nsString& genericFamily : genericFamilies) {
+        nsRefPtr<gfxFontFamily> family =
+            FindFamily(genericFamily, langGroup, false);
+        if (family) {
+            bool notFound = true;
+            for (const gfxFontFamily* f : *aGenericFamilies) {
+                if (f == family) {
+                    notFound = false;
+                    break;
+                }
+            }
+            if (notFound) {
+                aGenericFamilies->AppendElement(family);
+            }
+        }
+    }
+
+#if 0  // dump out generic mappings
+    printf("%s ===> ", prefFontName.get());
+    for (uint32_t k = 0; k < aGenericFamilies->Length(); k++) {
+        if (k > 0) printf(", ");
+        printf("%s", NS_ConvertUTF16toUTF8(aGenericFamilies[k]->Name()).get());
+    }
+    printf("\n");
+#endif
+}
+
+nsTArray<nsRefPtr<gfxFontFamily>>*
+gfxPlatformFontList::GetPrefFontsLangGroup(mozilla::FontFamilyType aGenericType,
+                                           eFontPrefLang aPrefLang)
+{
+    // treat -moz-fixed as monospace
+    if (aGenericType == eFamily_moz_fixed) {
+        aGenericType = eFamily_monospace;
+    }
+
+    PrefFontList* prefFonts = mLangGroupPrefFonts[aPrefLang][aGenericType];
+    if (MOZ_UNLIKELY(!prefFonts)) {
+        prefFonts = new PrefFontList;
+        ResolveGenericFontNames(aGenericType, aPrefLang, prefFonts);
+        mLangGroupPrefFonts[aPrefLang][aGenericType] = prefFonts;
+    }
+    return prefFonts;
+}
+
+void
+gfxPlatformFontList::AddGenericFonts(mozilla::FontFamilyType aGenericType,
+                                     nsIAtom* aLanguage,
+                                     nsTArray<gfxFontFamily*>& aFamilyList)
+{
+    // map lang ==> langGroup
+    nsIAtom *langGroup = nullptr;
+    if (aLanguage) {
+        if (!mLangService) {
+            mLangService = do_GetService(NS_LANGUAGEATOMSERVICE_CONTRACTID);
+        }
+        if (mLangService) {
+            nsresult rv;
+            langGroup = mLangService->GetLanguageGroup(aLanguage, &rv);
+        }
+    }
+    if (!langGroup) {
+        langGroup = nsGkAtoms::Unicode;
+    }
+
+    // langGroup ==> prefLang
+    eFontPrefLang prefLang = GetFontPrefLangFor(langGroup);
+
+    // lookup pref fonts
+    nsTArray<nsRefPtr<gfxFontFamily>>* prefFonts =
+        GetPrefFontsLangGroup(aGenericType, prefLang);
+
+    if (!prefFonts->IsEmpty()) {
+        aFamilyList.AppendElements(*prefFonts);
+    }
+}
+
+static nsIAtom* PrefLangToLangGroups(uint32_t aIndex)
+{
+    // static array here avoids static constructor
+    static nsIAtom* gPrefLangToLangGroups[] = {
+        #define FONT_PREF_LANG(enum_id_, str_, atom_id_) nsGkAtoms::atom_id_
+        #include "gfxFontPrefLangList.h"
+        #undef FONT_PREF_LANG
+    };
+
+    return aIndex < ArrayLength(gPrefLangToLangGroups)
+         ? gPrefLangToLangGroups[aIndex]
+         : nsGkAtoms::Unicode;
+}
+
+eFontPrefLang
+gfxPlatformFontList::GetFontPrefLangFor(const char* aLang)
+{
+    if (!aLang || !aLang[0]) {
+        return eFontPrefLang_Others;
+    }
+    for (uint32_t i = 0; i < ArrayLength(gPrefLangNames); ++i) {
+        if (!PL_strcasecmp(gPrefLangNames[i], aLang)) {
+            return eFontPrefLang(i);
+        }
+    }
+    return eFontPrefLang_Others;
+}
+
+eFontPrefLang
+gfxPlatformFontList::GetFontPrefLangFor(nsIAtom *aLang)
+{
+    if (!aLang)
+        return eFontPrefLang_Others;
+    nsAutoCString lang;
+    aLang->ToUTF8String(lang);
+    return GetFontPrefLangFor(lang.get());
+}
+
+nsIAtom*
+gfxPlatformFontList::GetLangGroupForPrefLang(eFontPrefLang aLang)
+{
+    // the special CJK set pref lang should be resolved into separate
+    // calls to individual CJK pref langs before getting here
+    NS_ASSERTION(aLang != eFontPrefLang_CJKSet, "unresolved CJK set pref lang");
+
+    return PrefLangToLangGroups(uint32_t(aLang));
+}
+
+const char*
+gfxPlatformFontList::GetPrefLangName(eFontPrefLang aLang)
+{
+    if (uint32_t(aLang) < ArrayLength(gPrefLangNames)) {
+        return gPrefLangNames[uint32_t(aLang)];
+    }
+    return nullptr;
+}
+
+eFontPrefLang
+gfxPlatformFontList::GetFontPrefLangFor(uint8_t aUnicodeRange)
+{
+    switch (aUnicodeRange) {
+        case kRangeSetLatin:   return eFontPrefLang_Western;
+        case kRangeCyrillic:   return eFontPrefLang_Cyrillic;
+        case kRangeGreek:      return eFontPrefLang_Greek;
+        case kRangeHebrew:     return eFontPrefLang_Hebrew;
+        case kRangeArabic:     return eFontPrefLang_Arabic;
+        case kRangeThai:       return eFontPrefLang_Thai;
+        case kRangeKorean:     return eFontPrefLang_Korean;
+        case kRangeJapanese:   return eFontPrefLang_Japanese;
+        case kRangeSChinese:   return eFontPrefLang_ChineseCN;
+        case kRangeTChinese:   return eFontPrefLang_ChineseTW;
+        case kRangeDevanagari: return eFontPrefLang_Devanagari;
+        case kRangeTamil:      return eFontPrefLang_Tamil;
+        case kRangeArmenian:   return eFontPrefLang_Armenian;
+        case kRangeBengali:    return eFontPrefLang_Bengali;
+        case kRangeCanadian:   return eFontPrefLang_Canadian;
+        case kRangeEthiopic:   return eFontPrefLang_Ethiopic;
+        case kRangeGeorgian:   return eFontPrefLang_Georgian;
+        case kRangeGujarati:   return eFontPrefLang_Gujarati;
+        case kRangeGurmukhi:   return eFontPrefLang_Gurmukhi;
+        case kRangeKhmer:      return eFontPrefLang_Khmer;
+        case kRangeMalayalam:  return eFontPrefLang_Malayalam;
+        case kRangeOriya:      return eFontPrefLang_Oriya;
+        case kRangeTelugu:     return eFontPrefLang_Telugu;
+        case kRangeKannada:    return eFontPrefLang_Kannada;
+        case kRangeSinhala:    return eFontPrefLang_Sinhala;
+        case kRangeTibetan:    return eFontPrefLang_Tibetan;
+        case kRangeSetCJK:     return eFontPrefLang_CJKSet;
+        default:               return eFontPrefLang_Others;
+    }
+}
+
+bool
+gfxPlatformFontList::IsLangCJK(eFontPrefLang aLang)
+{
+    switch (aLang) {
+        case eFontPrefLang_Japanese:
+        case eFontPrefLang_ChineseTW:
+        case eFontPrefLang_ChineseCN:
+        case eFontPrefLang_ChineseHK:
+        case eFontPrefLang_Korean:
+        case eFontPrefLang_CJKSet:
+            return true;
+        default:
+            return false;
+    }
+}
+
+void
+gfxPlatformFontList::GetLangPrefs(eFontPrefLang aPrefLangs[], uint32_t &aLen, eFontPrefLang aCharLang, eFontPrefLang aPageLang)
+{
+    if (IsLangCJK(aCharLang)) {
+        AppendCJKPrefLangs(aPrefLangs, aLen, aCharLang, aPageLang);
+    } else {
+        AppendPrefLang(aPrefLangs, aLen, aCharLang);
+    }
+
+    AppendPrefLang(aPrefLangs, aLen, eFontPrefLang_Others);
+}
+
+void
+gfxPlatformFontList::AppendCJKPrefLangs(eFontPrefLang aPrefLangs[], uint32_t &aLen, eFontPrefLang aCharLang, eFontPrefLang aPageLang)
+{
+    // prefer the lang specified by the page *if* CJK
+    if (IsLangCJK(aPageLang)) {
+        AppendPrefLang(aPrefLangs, aLen, aPageLang);
+    }
+
+    // if not set up, set up the default CJK order, based on accept lang settings and locale
+    if (mCJKPrefLangs.Length() == 0) {
+
+        // temp array
+        eFontPrefLang tempPrefLangs[kMaxLenPrefLangList];
+        uint32_t tempLen = 0;
+
+        // Add the CJK pref fonts from accept languages, the order should be same order
+        nsAdoptingCString list = Preferences::GetLocalizedCString("intl.accept_languages");
+        if (!list.IsEmpty()) {
+            const char kComma = ',';
+            const char *p, *p_end;
+            list.BeginReading(p);
+            list.EndReading(p_end);
+            while (p < p_end) {
+                while (nsCRT::IsAsciiSpace(*p)) {
+                    if (++p == p_end)
+                        break;
+                }
+                if (p == p_end)
+                    break;
+                const char *start = p;
+                while (++p != p_end && *p != kComma)
+                    /* nothing */ ;
+                nsAutoCString lang(Substring(start, p));
+                lang.CompressWhitespace(false, true);
+                eFontPrefLang fpl = gfxPlatformFontList::GetFontPrefLangFor(lang.get());
+                switch (fpl) {
+                    case eFontPrefLang_Japanese:
+                    case eFontPrefLang_Korean:
+                    case eFontPrefLang_ChineseCN:
+                    case eFontPrefLang_ChineseHK:
+                    case eFontPrefLang_ChineseTW:
+                        AppendPrefLang(tempPrefLangs, tempLen, fpl);
+                        break;
+                    default:
+                        break;
+                }
+                p++;
+            }
+        }
+
+        do { // to allow 'break' to abort this block if a call fails
+            nsresult rv;
+            nsCOMPtr<nsILocaleService> ls =
+                do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv);
+            if (NS_FAILED(rv))
+                break;
+
+            nsCOMPtr<nsILocale> appLocale;
+            rv = ls->GetApplicationLocale(getter_AddRefs(appLocale));
+            if (NS_FAILED(rv))
+                break;
+
+            nsString localeStr;
+            rv = appLocale->
+                GetCategory(NS_LITERAL_STRING(NSILOCALE_MESSAGE), localeStr);
+            if (NS_FAILED(rv))
+                break;
+
+            const nsAString& lang = Substring(localeStr, 0, 2);
+            if (lang.EqualsLiteral("ja")) {
+                AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Japanese);
+            } else if (lang.EqualsLiteral("zh")) {
+                const nsAString& region = Substring(localeStr, 3, 2);
+                if (region.EqualsLiteral("CN")) {
+                    AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseCN);
+                } else if (region.EqualsLiteral("TW")) {
+                    AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseTW);
+                } else if (region.EqualsLiteral("HK")) {
+                    AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseHK);
+                }
+            } else if (lang.EqualsLiteral("ko")) {
+                AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Korean);
+            }
+        } while (0);
+
+        // last resort... (the order is same as old gfx.)
+        AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Japanese);
+        AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_Korean);
+        AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseCN);
+        AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseHK);
+        AppendPrefLang(tempPrefLangs, tempLen, eFontPrefLang_ChineseTW);
+
+        // copy into the cached array
+        uint32_t j;
+        for (j = 0; j < tempLen; j++) {
+            mCJKPrefLangs.AppendElement(tempPrefLangs[j]);
+        }
+    }
+
+    // append in cached CJK langs
+    uint32_t  i, numCJKlangs = mCJKPrefLangs.Length();
+
+    for (i = 0; i < numCJKlangs; i++) {
+        AppendPrefLang(aPrefLangs, aLen, (eFontPrefLang) (mCJKPrefLangs[i]));
+    }
+
+}
+
+void
+gfxPlatformFontList::AppendPrefLang(eFontPrefLang aPrefLangs[], uint32_t& aLen, eFontPrefLang aAddLang)
+{
+    if (aLen >= kMaxLenPrefLangList) return;
+
+    // make sure
+    uint32_t  i = 0;
+    while (i < aLen && aPrefLangs[i] != aAddLang) {
+        i++;
+    }
+
+    if (i == aLen) {
+        aPrefLangs[aLen] = aAddLang;
+        aLen++;
+    }
+}
+
+mozilla::FontFamilyType
+gfxPlatformFontList::GetDefaultGeneric(eFontPrefLang aLang)
+{
+    // initialize lang group pref font defaults (i.e. serif/sans-serif)
+    if (MOZ_UNLIKELY(mDefaultGenericsLangGroup.IsEmpty())) {
+        mDefaultGenericsLangGroup.AppendElements(ArrayLength(gPrefLangNames));
+        for (uint32_t i = 0; i < ArrayLength(gPrefLangNames); i++) {
+            nsAutoCString prefDefaultFontType("font.default.");
+            prefDefaultFontType.Append(GetPrefLangName(eFontPrefLang(i)));
+            nsAdoptingCString serifOrSans =
+                Preferences::GetCString(prefDefaultFontType.get());
+            if (serifOrSans.EqualsLiteral("sans-serif")) {
+                mDefaultGenericsLangGroup[i] = eFamily_sans_serif;
+            } else {
+                mDefaultGenericsLangGroup[i] = eFamily_serif;
+            }
+        }
+    }
+
+    if (uint32_t(aLang) < ArrayLength(gPrefLangNames)) {
+        return mDefaultGenericsLangGroup[uint32_t(aLang)];
+    }
+    return eFamily_serif;
+}
+
+void
 gfxPlatformFontList::GetFontFamilyNames(nsTArray<nsString>& aFontFamilyNames)
 {
     for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
         nsRefPtr<gfxFontFamily>& family = iter.Data();
         aFontFamilyNames.AppendElement(family->Name());
     }
 }
 
@@ -947,16 +1366,29 @@ gfxPlatformFontList::ForceGlobalReflow()
 void
 gfxPlatformFontList::RebuildLocalFonts()
 {
     for (auto it = mUserFontSetList.Iter(); !it.Done(); it.Next()) {
         it.Get()->GetKey()->RebuildLocalRules();
     }
 }
 
+void
+gfxPlatformFontList::ClearLangGroupPrefFonts()
+{
+    for (uint32_t i = eFontPrefLang_First;
+         i < eFontPrefLang_First + eFontPrefLang_Count; i++) {
+        auto& prefFontsLangGroup = mLangGroupPrefFonts[i];
+        for (uint32_t j = eFamily_generic_first;
+             j < eFamily_generic_first + eFamily_generic_count; j++) {
+            prefFontsLangGroup[j] = nullptr;
+        }
+    }
+}
+
 // Support for memory reporting
 
 // this is also used by subclasses that hold additional font tables
 /*static*/ size_t
 gfxPlatformFontList::SizeOfFontFamilyTableExcludingThis(
     const FontFamilyTable& aTable,
     MallocSizeOf aMallocSizeOf)
 {
@@ -1003,32 +1435,35 @@ gfxPlatformFontList::AddSizeOfExcludingT
         aSizes->mFontListSize +=
             SizeOfFontEntryTableExcludingThis(mExtraNames->mFullnames,
                                               aMallocSizeOf);
         aSizes->mFontListSize +=
             SizeOfFontEntryTableExcludingThis(mExtraNames->mPostscriptNames,
                                               aMallocSizeOf);
     }
 
+    for (uint32_t i = eFontPrefLang_First;
+         i < eFontPrefLang_First + eFontPrefLang_Count; i++) {
+        auto& prefFontsLangGroup = mLangGroupPrefFonts[i];
+        for (uint32_t j = eFamily_generic_first;
+             j < eFamily_generic_first + eFamily_generic_count; j++) {
+            PrefFontList* pf = prefFontsLangGroup[j];
+            if (pf) {
+                aSizes->mFontListSize +=
+                    pf->ShallowSizeOfExcludingThis(aMallocSizeOf);
+            }
+        }
+    }
+
     aSizes->mFontListSize +=
         mCodepointsWithNoFonts.SizeOfExcludingThis(aMallocSizeOf);
     aSizes->mFontListSize +=
         mFontFamiliesToLoad.ShallowSizeOfExcludingThis(aMallocSizeOf);
 
     aSizes->mFontListSize +=
-        mPrefFonts.ShallowSizeOfExcludingThis(aMallocSizeOf);
-    for (auto iter = mPrefFonts.ConstIter(); !iter.Done(); iter.Next()) {
-        // Again, we only care about the size of the array itself; we don't
-        // follow the refPtrs stored in it, because they point to entries
-        // already owned and accounted-for by the main font list.
-        aSizes->mFontListSize +=
-            iter.Data().ShallowSizeOfExcludingThis(aMallocSizeOf);
-    }
-
-    aSizes->mFontListSize +=
         mBadUnderlineFamilyNames.SizeOfExcludingThis(aMallocSizeOf);
 
     aSizes->mFontListSize +=
         mSharedCmaps.ShallowSizeOfExcludingThis(aMallocSizeOf);
     for (auto iter = mSharedCmaps.ConstIter(); !iter.Done(); iter.Next()) {
         aSizes->mCharMapsSize +=
             iter.Get()->GetKey()->SizeOfIncludingThis(aMallocSizeOf);
     }
--- a/gfx/thebes/gfxPlatformFontList.h
+++ b/gfx/thebes/gfxPlatformFontList.h
@@ -8,21 +8,25 @@
 
 #include "nsDataHashtable.h"
 #include "nsRefPtrHashtable.h"
 #include "nsTHashtable.h"
 
 #include "gfxFontUtils.h"
 #include "gfxFontInfoLoader.h"
 #include "gfxFont.h"
+#include "gfxFontConstants.h"
 #include "gfxPlatform.h"
+#include "gfxFontFamilyList.h"
 
 #include "nsIMemoryReporter.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/MemoryReporting.h"
+#include "mozilla/RangedArray.h"
+#include "nsILanguageAtomService.h"
 
 class CharMapHashKey : public PLDHashEntryHdr
 {
 public:
     typedef gfxCharacterMap* KeyType;
     typedef const gfxCharacterMap* KeyTypePointer;
 
     explicit CharMapHashKey(const gfxCharacterMap *aCharMap) :
@@ -111,34 +115,31 @@ public:
     virtual nsresult InitFontList();
 
     virtual void GetFontList(nsIAtom *aLangGroup,
                              const nsACString& aGenericFamily,
                              nsTArray<nsString>& aListOfFonts);
 
     void UpdateFontList();
 
-    void ClearPrefFonts() { mPrefFonts.Clear(); }
+    void ClearLangGroupPrefFonts();
 
     virtual void GetFontFamilyList(nsTArray<nsRefPtr<gfxFontFamily> >& aFamilyArray);
 
     gfxFontEntry*
     SystemFindFontForChar(uint32_t aCh, uint32_t aNextCh,
                           int32_t aRunScript,
                           const gfxFontStyle* aStyle);
 
     virtual gfxFontFamily* FindFamily(const nsAString& aFamily,
                                       nsIAtom* aLanguage = nullptr,
                                       bool aUseSystemFonts = false);
 
     gfxFontEntry* FindFontForFamily(const nsAString& aFamily, const gfxFontStyle* aStyle, bool& aNeedsBold);
 
-    bool GetPrefFontFamilyEntries(eFontPrefLang aLangGroup, nsTArray<nsRefPtr<gfxFontFamily> > *array);
-    void SetPrefFontFamilyEntries(eFontPrefLang aLangGroup, nsTArray<nsRefPtr<gfxFontFamily> >& array);
-
     // name lookup table methods
 
     void AddOtherFamilyName(gfxFontFamily *aFamilyEntry, nsAString& aOtherFamilyName);
 
     void AddFullname(gfxFontEntry *aFontEntry, nsAString& aFullname);
 
     void AddPostscriptName(gfxFontEntry *aFontEntry, nsAString& aPostscriptName);
 
@@ -194,16 +195,53 @@ public:
 
     static const gfxFontEntry::ScriptRange sComplexScriptRanges[];
 
     void GetFontlistInitInfo(uint32_t& aNumInits, uint32_t& aLoaderState) {
         aNumInits = mFontlistInitCount;
         aLoaderState = (uint32_t) mState;
     }
 
+    virtual void
+    AddGenericFonts(mozilla::FontFamilyType aGenericType,
+                    nsIAtom* aLanguage,
+                    nsTArray<gfxFontFamily*>& aFamilyList);
+
+    nsTArray<nsRefPtr<gfxFontFamily>>*
+    GetPrefFontsLangGroup(mozilla::FontFamilyType aGenericType,
+                          eFontPrefLang aPrefLang);
+
+    // in some situations, need to make decisions about ambiguous characters, may need to look at multiple pref langs
+    void GetLangPrefs(eFontPrefLang aPrefLangs[], uint32_t &aLen, eFontPrefLang aCharLang, eFontPrefLang aPageLang);
+
+    // convert a lang group to enum constant (i.e. "zh-TW" ==> eFontPrefLang_ChineseTW)
+    static eFontPrefLang GetFontPrefLangFor(const char* aLang);
+
+    // convert a lang group atom to enum constant
+    static eFontPrefLang GetFontPrefLangFor(nsIAtom *aLang);
+
+    // convert an enum constant to a lang group atom
+    static nsIAtom* GetLangGroupForPrefLang(eFontPrefLang aLang);
+
+    // convert a enum constant to lang group string (i.e. eFontPrefLang_ChineseTW ==> "zh-TW")
+    static const char* GetPrefLangName(eFontPrefLang aLang);
+
+    // map a Unicode range (based on char code) to a font language for Preferences
+    static eFontPrefLang GetFontPrefLangFor(uint8_t aUnicodeRange);
+
+    // returns true if a pref lang is CJK
+    static bool IsLangCJK(eFontPrefLang aLang);
+
+    // helper method to add a pref lang to an array, if not already in array
+    static void AppendPrefLang(eFontPrefLang aPrefLangs[], uint32_t& aLen, eFontPrefLang aAddLang);
+
+    // default serif/sans-serif choice based on font.default.xxx prefs
+    mozilla::FontFamilyType
+    GetDefaultGeneric(eFontPrefLang aLang);
+
 protected:
     class MemoryReporter final : public nsIMemoryReporter
     {
         ~MemoryReporter() {}
     public:
         NS_DECL_ISUPPORTS
         NS_DECL_NSIMEMORYREPORTER
     };
@@ -236,16 +274,19 @@ protected:
                                              const gfxFontStyle* aMatchStyle,
                                              uint32_t& aCmapCount,
                                              gfxFontFamily** aMatchedFamily);
 
     // whether system-based font fallback is used or not
     // if system fallback is used, no need to load all cmaps
     virtual bool UsesSystemFallback() { return false; }
 
+    void AppendCJKPrefLangs(eFontPrefLang aPrefLangs[], uint32_t &aLen,
+                            eFontPrefLang aCharLang, eFontPrefLang aPageLang);
+
     // verifies that a family contains a non-zero font count
     gfxFontFamily* CheckFamily(gfxFontFamily *aFamily);
 
     // initialize localized family names
     void InitOtherFamilyNames();
 
     // search through font families, looking for a given name, initializing
     // facename lists along the way. first checks all families with names
@@ -282,16 +323,21 @@ protected:
     // read the loader initialization prefs, and start it
     void GetPrefsAndStartLoader();
 
     // for font list changes that affect all documents
     void ForceGlobalReflow();
 
     void RebuildLocalFonts();
 
+    void
+    ResolveGenericFontNames(mozilla::FontFamilyType aGenericType,
+                            eFontPrefLang aPrefLang,
+                            nsTArray<nsRefPtr<gfxFontFamily>>* aGenericFamilies);
+
     typedef nsRefPtrHashtable<nsStringHashKey, gfxFontFamily> FontFamilyTable;
     typedef nsRefPtrHashtable<nsStringHashKey, gfxFontEntry> FontEntryTable;
 
     // used by memory reporter to accumulate sizes of family names in the table
     static size_t
     SizeOfFontFamilyTableExcludingThis(const FontFamilyTable& aTable,
                                        mozilla::MallocSizeOf aMallocSizeOf);
     static size_t
@@ -327,19 +373,23 @@ protected:
     nsAutoPtr<ExtraNames> mExtraNames;
 
     // face names missed when face name loading takes a long time
     nsAutoPtr<nsTHashtable<nsStringHashKey> > mFaceNamesMissed;
 
     // localized family names missed when face name loading takes a long time
     nsAutoPtr<nsTHashtable<nsStringHashKey> > mOtherNamesMissed;
 
-    // cached pref font lists
-    // maps list of family names ==> array of family entries, one per lang group
-    nsDataHashtable<nsUint32HashKey, nsTArray<nsRefPtr<gfxFontFamily> > > mPrefFonts;
+    typedef nsTArray<nsRefPtr<gfxFontFamily>> PrefFontList;
+    typedef mozilla::RangedArray<nsAutoPtr<PrefFontList>,
+                                 mozilla::eFamily_generic_first,
+                                 mozilla::eFamily_generic_count> PrefFontsForLangGroup;
+    mozilla::RangedArray<PrefFontsForLangGroup,
+                         eFontPrefLang_First,
+                         eFontPrefLang_Count> mLangGroupPrefFonts;
 
     // when system-wide font lookup fails for a character, cache it to skip future searches
     gfxSparseBitSet mCodepointsWithNoFonts;
 
     // the family to use for U+FFFD fallback, to avoid expensive search every time
     // on pages with lots of problems
     nsRefPtr<gfxFontFamily> mReplacementCharFallbackFamily;
 
@@ -355,11 +405,15 @@ protected:
     uint32_t mIncrement;
     uint32_t mNumFamilies;
 
     // xxx - info for diagnosing no default font aborts
     // see bugs 636957, 1070983, 1189129
     uint32_t mFontlistInitCount; // num times InitFontList called
 
     nsTHashtable<nsPtrHashKey<gfxUserFontSet> > mUserFontSetList;
+
+    nsCOMPtr<nsILanguageAtomService> mLangService;
+    nsTArray<uint32_t> mCJKPrefLangs;
+    nsTArray<mozilla::FontFamilyType> mDefaultGenericsLangGroup;
 };
 
 #endif /* GFXPLATFORMFONTLIST_H_ */
--- a/gfx/thebes/gfxTextRun.cpp
+++ b/gfx/thebes/gfxTextRun.cpp
@@ -1538,219 +1538,119 @@ gfxFontGroup::gfxFontGroup(const FontFam
                            gfxTextPerfMetrics* aTextPerf,
                            gfxUserFontSet *aUserFontSet)
     : mFamilyList(aFontFamilyList)
     , mStyle(*aStyle)
     , mUnderlineOffset(UNDERLINE_OFFSET_NOT_SET)
     , mHyphenWidth(-1)
     , mUserFontSet(aUserFontSet)
     , mTextPerf(aTextPerf)
-    , mPageLang(gfxPlatform::GetFontPrefLangFor(aStyle->language))
+    , mPageLang(gfxPlatformFontList::GetFontPrefLangFor(aStyle->language))
     , mSkipDrawing(false)
     , mSkipUpdateUserFonts(false)
 {
     // We don't use SetUserFontSet() here, as we want to unconditionally call
     // BuildFontList() rather than only do UpdateUserFonts() if it changed.
     mCurrGeneration = GetGeneration();
     BuildFontList();
 }
 
 gfxFontGroup::~gfxFontGroup()
 {
 }
 
 void
-gfxFontGroup::FindGenericFonts(FontFamilyType aGenericType,
-                               nsIAtom *aLanguage,
-                               void *aClosure)
-{
-    nsAutoTArray<nsString, 5> resolvedGenerics;
-    ResolveGenericFontNames(aGenericType, aLanguage, resolvedGenerics);
-    uint32_t g = 0, numGenerics = resolvedGenerics.Length();
-    for (g = 0; g < numGenerics; g++) {
-        FindPlatformFont(resolvedGenerics[g], false, aClosure);
-    }
-}
-
-/* static */ void
-gfxFontGroup::ResolveGenericFontNames(FontFamilyType aGenericType,
-                                      nsIAtom *aLanguage,
-                                      nsTArray<nsString>& aGenericFamilies)
-{
-    static const char kGeneric_serif[] = "serif";
-    static const char kGeneric_sans_serif[] = "sans-serif";
-    static const char kGeneric_monospace[] = "monospace";
-    static const char kGeneric_cursive[] = "cursive";
-    static const char kGeneric_fantasy[] = "fantasy";
-
-    // treat -moz-fixed as monospace
-    if (aGenericType == eFamily_moz_fixed) {
-        aGenericType = eFamily_monospace;
-    }
-
-    // type should be standard generic type at this point
-    NS_ASSERTION(aGenericType >= eFamily_serif &&
-                 aGenericType <= eFamily_fantasy,
-                 "standard generic font family type required");
-
-    // create the lang string
-    nsIAtom *langGroupAtom = nullptr;
-    nsAutoCString langGroupString;
-    if (aLanguage) {
-        if (!gLangService) {
-            CallGetService(NS_LANGUAGEATOMSERVICE_CONTRACTID, &gLangService);
-        }
-        if (gLangService) {
-            nsresult rv;
-            langGroupAtom = gLangService->GetLanguageGroup(aLanguage, &rv);
-        }
-    }
-    if (!langGroupAtom) {
-        langGroupAtom = nsGkAtoms::Unicode;
-    }
-    langGroupAtom->ToUTF8String(langGroupString);
-
-    // map generic type to string
-    const char *generic = nullptr;
-    switch (aGenericType) {
-        case eFamily_serif:
-            generic = kGeneric_serif;
-            break;
-        case eFamily_sans_serif:
-            generic = kGeneric_sans_serif;
-            break;
-        case eFamily_monospace:
-            generic = kGeneric_monospace;
-            break;
-        case eFamily_cursive:
-            generic = kGeneric_cursive;
-            break;
-        case eFamily_fantasy:
-            generic = kGeneric_fantasy;
-            break;
-        default:
-            break;
-    }
-
-    if (!generic) {
-        return;
-    }
-
-    aGenericFamilies.Clear();
-
-    // load family for "font.name.generic.lang"
-    nsAutoCString prefFontName("font.name.");
-    prefFontName.Append(generic);
-    prefFontName.Append('.');
-    prefFontName.Append(langGroupString);
-    gfxFontUtils::AppendPrefsFontList(prefFontName.get(),
-                                      aGenericFamilies);
-
-    // if lang has pref fonts, also load fonts for "font.name-list.generic.lang"
-    if (!aGenericFamilies.IsEmpty()) {
-        nsAutoCString prefFontListName("font.name-list.");
-        prefFontListName.Append(generic);
-        prefFontListName.Append('.');
-        prefFontListName.Append(langGroupString);
-        gfxFontUtils::AppendPrefsFontList(prefFontListName.get(),
-                                          aGenericFamilies);
-    }
-
-#if 0  // dump out generic mappings
-    printf("%s ===> ", prefFontName.get());
-    for (uint32_t k = 0; k < aGenericFamilies.Length(); k++) {
-        if (k > 0) printf(", ");
-        printf("%s", NS_ConvertUTF16toUTF8(aGenericFamilies[k]).get());
-    }
-    printf("\n");
-#endif
-}
-
-void gfxFontGroup::EnumerateFontList(nsIAtom *aLanguage, void *aClosure)
-{
-    // initialize fonts in the font family list
-    const nsTArray<FontFamilyName>& fontlist = mFamilyList.GetFontlist();
-
-    // lookup fonts in the fontlist
-    uint32_t i, numFonts = fontlist.Length();
-    for (i = 0; i < numFonts; i++) {
-        const FontFamilyName& name = fontlist[i];
-        if (name.IsNamed()) {
-            FindPlatformFont(name.mName, true, aClosure);
-        } else {
-            FindGenericFonts(name.mType, aLanguage, aClosure);
-        }
-    }
-
-    // if necessary, append default generic onto the end
-    if (mFamilyList.GetDefaultFontType() != eFamily_none &&
-        !mFamilyList.HasDefaultGeneric()) {
-        FindGenericFonts(mFamilyList.GetDefaultFontType(),
-                         aLanguage,
-                         aClosure);
-    }
-}
-
-void
 gfxFontGroup::BuildFontList()
 {
     bool enumerateFonts = true;
 
 #if defined(MOZ_WIDGET_GTK)
     // xxx - eliminate this once gfxPangoFontGroup is no longer needed
     enumerateFonts = gfxPlatformGtk::UseFcFontList();
 #elif defined(MOZ_WIDGET_QT)
     enumerateFonts = false;
 #endif
-    if (enumerateFonts) {
-        EnumerateFontList(mStyle.language);
+    if (!enumerateFonts) {
+        return;
+    }
+
+    // initialize fonts in the font family list
+    nsAutoTArray<gfxFontFamily*,4> fonts;
+    gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList();
+
+    // lookup fonts in the fontlist
+    for (const FontFamilyName& name : mFamilyList.GetFontlist()) {
+        if (name.IsNamed()) {
+            AddPlatformFont(name.mName, fonts);
+        } else {
+            pfl->AddGenericFonts(name.mType, mStyle.language, fonts);
+            if (mTextPerf) {
+                mTextPerf->current.genericLookups++;
+            }
+        }
+    }
+
+    // if necessary, append default generic onto the end
+    if (mFamilyList.GetDefaultFontType() != eFamily_none &&
+        !mFamilyList.HasDefaultGeneric()) {
+        pfl->AddGenericFonts(mFamilyList.GetDefaultFontType(),
+                             mStyle.language, fonts);
+        if (mTextPerf) {
+            mTextPerf->current.genericLookups++;
+        }
+    }
+
+    // build the fontlist from the specified families
+    for (gfxFontFamily* fontFamily : fonts) {
+        AddFamilyToFontList(fontFamily);
     }
 }
 
 void
-gfxFontGroup::FindPlatformFont(const nsAString& aName,
-                               bool aUseFontSet,
-                               void *aClosure)
+gfxFontGroup::AddPlatformFont(const nsAString& aName,
+                              nsTArray<gfxFontFamily*>& aFamilyList)
 {
-    bool needsBold;
-    gfxFontFamily *family = nullptr;
-
-    if (aUseFontSet) {
-        // First, look up in the user font set...
-        // If the fontSet matches the family, we must not look for a platform
-        // font of the same name, even if we fail to actually get a fontEntry
-        // here; we'll fall back to the next name in the CSS font-family list.
-        if (mUserFontSet) {
-            // Add userfonts to the fontlist whether already loaded
-            // or not. Loading is initiated during font matching.
-            family = mUserFontSet->LookupFamily(aName);
-        }
+    gfxFontFamily* family = nullptr;
+
+    // First, look up in the user font set...
+    // If the fontSet matches the family, we must not look for a platform
+    // font of the same name, even if we fail to actually get a fontEntry
+    // here; we'll fall back to the next name in the CSS font-family list.
+    if (mUserFontSet) {
+        // Add userfonts to the fontlist whether already loaded
+        // or not. Loading is initiated during font matching.
+        family = mUserFontSet->LookupFamily(aName);
     }
 
     // Not known in the user font set ==> check system fonts
     if (!family) {
-        gfxPlatformFontList *fontList = gfxPlatformFontList::PlatformFontList();
+        gfxPlatformFontList* fontList = gfxPlatformFontList::PlatformFontList();
         family = fontList->FindFamily(aName, mStyle.language, mStyle.systemFont);
     }
 
-    // if family found, do style matching and add all font entries to mFonts
     if (family) {
-        nsAutoTArray<gfxFontEntry*,4> fontEntryList;
-        family->FindAllFontsForStyle(mStyle, fontEntryList, needsBold);
-        // add these to the fontlist
-        uint32_t n = fontEntryList.Length();
-        for (uint32_t i = 0; i < n; i++) {
-            gfxFontEntry* fe = fontEntryList[i];
-            if (!HasFont(fe)) {
-                FamilyFace ff(family, fe, needsBold);
-                if (fe->mIsUserFontContainer) {
-                    ff.CheckState(mSkipDrawing);
-                }
-                mFonts.AppendElement(ff);
+        aFamilyList.AppendElement(family);
+    }
+}
+
+void
+gfxFontGroup::AddFamilyToFontList(gfxFontFamily* aFamily)
+{
+    NS_ASSERTION(aFamily, "trying to add a null font family to fontlist");
+    nsAutoTArray<gfxFontEntry*,4> fontEntryList;
+    bool needsBold;
+    aFamily->FindAllFontsForStyle(mStyle, fontEntryList, needsBold);
+    // add these to the fontlist
+    for (gfxFontEntry* fe : fontEntryList) {
+        if (!HasFont(fe)) {
+            FamilyFace ff(aFamily, fe, needsBold);
+            if (fe->mIsUserFontContainer) {
+                ff.CheckState(mSkipDrawing);
             }
+            mFonts.AppendElement(ff);
         }
     }
 }
 
 bool
 gfxFontGroup::HasFont(const gfxFontEntry *aFontEntry)
 {
     uint32_t count = mFonts.Length();
@@ -3130,69 +3030,63 @@ struct PrefFontCallbackData {
 
     nsTArray<nsRefPtr<gfxFontFamily> >& mPrefFamilies;
 
     static bool AddFontFamilyEntry(eFontPrefLang aLang, const nsAString& aName, void *aClosure)
     {
         PrefFontCallbackData *prefFontData = static_cast<PrefFontCallbackData*>(aClosure);
 
         // map pref lang to langGroup for language-sensitive lookups
-        nsIAtom* lang = gfxPlatform::GetLangGroupForPrefLang(aLang);
+        nsIAtom* lang = gfxPlatformFontList::GetLangGroupForPrefLang(aLang);
         gfxFontFamily *family =
             gfxPlatformFontList::PlatformFontList()->FindFamily(aName, lang);
         if (family) {
             prefFontData->mPrefFamilies.AppendElement(family);
         }
         return true;
     }
 };
 
 already_AddRefed<gfxFont>
 gfxFontGroup::WhichPrefFontSupportsChar(uint32_t aCh)
 {
     nsRefPtr<gfxFont> font;
 
     // get the pref font list if it hasn't been set up already
     uint32_t unicodeRange = FindCharUnicodeRange(aCh);
-    eFontPrefLang charLang = gfxPlatform::GetPlatform()->GetFontPrefLangFor(unicodeRange);
+    gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
+    eFontPrefLang charLang = pfl->GetFontPrefLangFor(unicodeRange);
 
     // if the last pref font was the first family in the pref list, no need to recheck through a list of families
     if (mLastPrefFont && charLang == mLastPrefLang &&
         mLastPrefFirstFont && mLastPrefFont->HasCharacter(aCh)) {
         font = mLastPrefFont;
         return font.forget();
     }
 
     // based on char lang and page lang, set up list of pref lang fonts to check
     eFontPrefLang prefLangs[kMaxLenPrefLangList];
     uint32_t i, numLangs = 0;
 
-    gfxPlatform::GetPlatform()->GetLangPrefs(prefLangs, numLangs, charLang, mPageLang);
+    pfl->GetLangPrefs(prefLangs, numLangs, charLang, mPageLang);
 
     for (i = 0; i < numLangs; i++) {
-        nsAutoTArray<nsRefPtr<gfxFontFamily>, 5> families;
         eFontPrefLang currentLang = prefLangs[i];
-
-        gfxPlatformFontList *fontList = gfxPlatformFontList::PlatformFontList();
-
-        // get the pref families for a single pref lang
-        if (!fontList->GetPrefFontFamilyEntries(currentLang, &families)) {
-            eFontPrefLang prefLangsToSearch[1] = { currentLang };
-            PrefFontCallbackData prefFontData(families);
-            gfxPlatform::ForEachPrefFont(prefLangsToSearch, 1, PrefFontCallbackData::AddFontFamilyEntry,
-                                           &prefFontData);
-            fontList->SetPrefFontFamilyEntries(currentLang, families);
-        }
+        mozilla::FontFamilyType defaultGeneric =
+            pfl->GetDefaultGeneric(currentLang);
+        nsTArray<nsRefPtr<gfxFontFamily>>* families =
+            pfl->GetPrefFontsLangGroup(defaultGeneric, currentLang);
+        NS_ASSERTION(families, "no pref font families found");
 
         // find the first pref font that includes the character
         uint32_t  j, numPrefs;
-        numPrefs = families.Length();
+        numPrefs = families->Length();
         for (j = 0; j < numPrefs; j++) {
             // look up the appropriate face
-            gfxFontFamily *family = families[j];
+            gfxFontFamily *family = (*families)[j];
             if (!family) continue;
 
             // if a pref font is used, it's likely to be used again in the same text run.
             // the style doesn't change so the face lookup can be cached rather than calling
             // FindOrMakeFont repeatedly.  speeds up FindFontForChar lookup times for subsequent
             // pref font lookups
             if (family == mLastPrefFamily && mLastPrefFont->HasCharacter(aCh)) {
                 font = mLastPrefFont;
--- a/gfx/thebes/gfxTextRun.h
+++ b/gfx/thebes/gfxTextRun.h
@@ -5,16 +5,17 @@
 
 #ifndef GFX_TEXTRUN_H
 #define GFX_TEXTRUN_H
 
 #include "gfxTypes.h"
 #include "nsString.h"
 #include "gfxPoint.h"
 #include "gfxFont.h"
+#include "gfxFontConstants.h"
 #include "nsTArray.h"
 #include "gfxSkipChars.h"
 #include "gfxPlatform.h"
 #include "mozilla/MemoryReporting.h"
 #include "DrawMode.h"
 #include "harfbuzz/hb.h"
 #include "nsUnicodeScriptCodes.h"
 
@@ -867,22 +868,16 @@ public:
     // The gfxFontGroup keeps ownership of this textrun.
     // It is only guaranteed to exist until the next call to GetEllipsisTextRun
     // (which might use a different appUnitsPerDev value or flags) for the font
     // group, or until UpdateUserFonts is called, or the fontgroup is destroyed.
     // Get it/use it/forget it :) - don't keep a reference that might go stale.
     gfxTextRun* GetEllipsisTextRun(int32_t aAppUnitsPerDevPixel, uint32_t aFlags,
                                    LazyReferenceContextGetter& aRefContextGetter);
 
-    // helper method for resolving generic font families
-    static void
-    ResolveGenericFontNames(mozilla::FontFamilyType aGenericType,
-                            nsIAtom *aLanguage,
-                            nsTArray<nsString>& aGenericFamilies);
-
 protected:
     // search through pref fonts for a character, return nullptr if no matching pref font
     already_AddRefed<gfxFont> WhichPrefFontSupportsChar(uint32_t aCh);
 
     already_AddRefed<gfxFont>
         WhichSystemFontSupportsChar(uint32_t aCh, uint32_t aNextCh,
                                     int32_t aRunScript);
 
@@ -1112,28 +1107,22 @@ protected:
     // When matching the italic case, allow use of the regular face
     // if it supports a character but the italic one doesn't.
     // Return null if regular face doesn't support aCh
     already_AddRefed<gfxFont>
     FindNonItalicFaceForChar(gfxFontFamily* aFamily, uint32_t aCh);
 
     // helper methods for looking up fonts
 
-    // iterate over the fontlist, lookup names and expand generics
-    void EnumerateFontList(nsIAtom *aLanguage, void *aClosure = nullptr);
+    // lookup and add a font with a given name (i.e. *not* a generic!)
+    void AddPlatformFont(const nsAString& aName,
+                         nsTArray<gfxFontFamily*>& aFamilyList);
 
-    // expand a generic to a list of specific names based on prefs
-    void FindGenericFonts(mozilla::FontFamilyType aGenericType,
-                          nsIAtom *aLanguage,
-                          void *aClosure);
-
-    // lookup and add a font with a given name (i.e. *not* a generic!)
-    virtual void FindPlatformFont(const nsAString& aName,
-                                  bool aUseFontSet,
-                                  void *aClosure);
+    // do style selection and add entries to list
+    void AddFamilyToFontList(gfxFontFamily* aFamily);
 
     static nsILanguageAtomService* gLangService;
 };
 
 // A "missing font recorder" is to be used during text-run creation to keep
 // a record of any scripts encountered for which font coverage was lacking;
 // when Flush() is called, it sends a notification that front-end code can use
 // to download fonts on demand (or whatever else it wants to do).
--- a/gfx/thebes/gfxUtils.cpp
+++ b/gfx/thebes/gfxUtils.cpp
@@ -1557,21 +1557,23 @@ gfxUtils::DumpDisplayList() {
 
 FILE *gfxUtils::sDumpPaintFile = stderr;
 
 #ifdef MOZ_DUMP_PAINTING
 bool gfxUtils::sDumpPainting = getenv("MOZ_DUMP_PAINT") != 0;
 bool gfxUtils::sDumpPaintingIntermediate = getenv("MOZ_DUMP_PAINT_INTERMEDIATE") != 0;
 bool gfxUtils::sDumpPaintingToFile = getenv("MOZ_DUMP_PAINT_TO_FILE") != 0;
 bool gfxUtils::sDumpPaintItems = getenv("MOZ_DUMP_PAINT_ITEMS") != 0;
+bool gfxUtils::sDumpCompositorTextures = getenv("MOZ_DUMP_COMPOSITOR_TEXTURES") != 0;
 #else
 bool gfxUtils::sDumpPainting = false;
 bool gfxUtils::sDumpPaintingIntermediate = false;
 bool gfxUtils::sDumpPaintingToFile = false;
 bool gfxUtils::sDumpPaintItems = false;
+bool gfxUtils::sDumpCompositorTextures = false;
 #endif
 
 namespace mozilla {
 namespace gfx {
 
 Color ToDeviceColor(Color aColor)
 {
   // aColor is pass-by-value since to get return value optimization goodness we
--- a/gfx/thebes/gfxUtils.h
+++ b/gfx/thebes/gfxUtils.h
@@ -291,16 +291,22 @@ public:
     static void CopyAsDataURI(DrawTarget* aDT);
 
     static bool DumpDisplayList();
 
     static bool sDumpPainting;
     static bool sDumpPaintingIntermediate;
     static bool sDumpPaintingToFile;
     static bool sDumpPaintItems;
+    // TODO: Dumping compositor textures is broken pretty badly. For example,
+    //       on Linux it crashes because TextureHost::GetAsSurface() returns
+    //       null. Expect to have to fix things like this if you turn it on.
+    //       Meanwhile, content-side texture dumping (conditioned on
+    //       sDumpPainting) is a good replacement.
+    static bool sDumpCompositorTextures;
     static FILE* sDumpPaintFile;
 };
 
 namespace mozilla {
 namespace gfx {
 
 /**
  * If the CMS mode is eCMSMode_All, these functions transform the passed
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -2405,16 +2405,22 @@ gfxWindowsPlatform::CheckD2DSupport()
   }
 
   // Direct2D is only Vista or higher, but we require a D3D11 compositor to
   // use it. (This check may be implied by the fact that we do not get here
   // without a D3D11 compositor device.)
   if (!IsVistaOrLater()) {
     return FeatureStatus::Unavailable;
   }
+
+  // Normally we don't use D2D content drawing when using WARP. However if
+  // WARP is force-enabled, we will let Direct2D use WARP as well.
+  if (mIsWARP && !gfxPrefs::LayersD3D11ForceWARP()) {
+    return FeatureStatus::Blocked;
+  }
   return FeatureStatus::Available;
 }
 
 void
 gfxWindowsPlatform::InitializeD2D()
 {
   mD2DStatus = CheckD2DSupport();
   if (IsFeatureStatusFailure(mD2DStatus)) {
@@ -2458,21 +2464,16 @@ gfxWindowsPlatform::CheckD2D1Support()
   if (XRE_IsContentProcess()) {
     return GetParentDevicePrefs().useD2D1()
            ? FeatureStatus::Available
            : FeatureStatus::Blocked;
   }
   if (!gfxPrefs::Direct2DUse1_1()) {
     return FeatureStatus::Disabled;
   }
-  // Normally we don't use D2D content drawing when using WARP. However if
-  // WARP is force-enabled, we will let Direct2D use WARP as well.
-  if (mIsWARP && !gfxPrefs::LayersD3D11ForceWARP()) {
-    return FeatureStatus::Blocked;
-  }
   return FeatureStatus::Available;
 }
 
 void
 gfxWindowsPlatform::InitializeD2D1()
 {
   ScopedGfxFeatureReporter d2d1_1("D2D1.1");
 
--- a/image/OrientedImage.cpp
+++ b/image/OrientedImage.cpp
@@ -339,17 +339,17 @@ OrientedImage::GetImageSpaceInvalidation
   nsresult rv = InnerImage()->GetWidth(&innerSize.width);
   rv = NS_FAILED(rv) ? rv : InnerImage()->GetHeight(&innerSize.height);
   if (NS_FAILED(rv)) {
     // Fall back to identity if the width and height aren't available.
     return rect;
   }
 
   // Transform the invalidation rect into the correct orientation.
-  gfxMatrix matrix(OrientationMatrix(innerSize, /* aInvert = */ true));
+  gfxMatrix matrix(OrientationMatrix(innerSize));
   gfxRect invalidRect(matrix.TransformBounds(gfxRect(rect.x, rect.y,
                                                      rect.width, rect.height)));
   invalidRect.RoundOut();
 
   return nsIntRect(invalidRect.x, invalidRect.y,
                    invalidRect.width, invalidRect.height);
 }
 
--- a/js/public/HashTable.h
+++ b/js/public/HashTable.h
@@ -648,35 +648,36 @@ class HashMapEntry
 {
     Key key_;
     Value value_;
 
     template <class, class, class> friend class detail::HashTable;
     template <class> friend class detail::HashTableEntry;
     template <class, class, class, class> friend class HashMap;
 
+    Key & mutableKey() { return key_; }
+
   public:
     template<typename KeyInput, typename ValueInput>
     HashMapEntry(KeyInput&& k, ValueInput&& v)
       : key_(mozilla::Forward<KeyInput>(k)),
         value_(mozilla::Forward<ValueInput>(v))
     {}
 
     HashMapEntry(HashMapEntry&& rhs)
       : key_(mozilla::Move(rhs.key_)),
         value_(mozilla::Move(rhs.value_))
     {}
 
     typedef Key KeyType;
     typedef Value ValueType;
 
-    const Key& key() const { return key_; }
-    Key& mutableKey() { return key_; }
-    const Value& value() const { return value_; }
-    Value& value() { return value_; }
+    const Key & key() const { return key_; }
+    const Value & value() const { return value_; }
+    Value & value() { return value_; }
 
   private:
     HashMapEntry(const HashMapEntry&) = delete;
     void operator=(const HashMapEntry&) = delete;
 };
 
 } // namespace js
 
@@ -735,17 +736,16 @@ class HashTableEntry
     }
 
     void swap(HashTableEntry* other) {
         mozilla::Swap(keyHash, other->keyHash);
         mozilla::Swap(mem, other->mem);
     }
 
     T& get() { MOZ_ASSERT(isLive()); return *mem.addr(); }
-    NonConstT& getMutable() { MOZ_ASSERT(isLive()); return *mem.addr(); }
 
     bool isFree() const    { return keyHash == sFreeKey; }
     void clearLive()       { MOZ_ASSERT(isLive()); keyHash = sFreeKey; mem.addr()->~T(); }
     void clear()           { if (isLive()) mem.addr()->~T(); keyHash = sFreeKey; }
     bool isRemoved() const { return keyHash == sRemovedKey; }
     void removeLive()      { MOZ_ASSERT(isLive()); keyHash = sRemovedKey; mem.addr()->~T(); }
     bool isLive() const    { return isLiveHash(keyHash); }
     void setCollision()               { MOZ_ASSERT(isLive()); keyHash |= sCollisionBit; }
@@ -975,26 +975,16 @@ class HashTable : private AllocPolicy
             table_.remove(*this->cur);
             removed = true;
 #ifdef JS_DEBUG
             this->validEntry = false;
             this->mutationCount = table_.mutationCount;
 #endif
         }
 
-        NonConstT& mutableFront() {
-            MOZ_ASSERT(!this->empty());
-#ifdef JS_DEBUG
-            MOZ_ASSERT(this->validEntry);
-            MOZ_ASSERT(this->generation == this->Range::table_->generation());
-            MOZ_ASSERT(this->mutationCount == this->Range::table_->mutationCount);
-#endif
-            return this->cur->getMutable();
-        }
-
         // Removes the |front()| element and re-inserts it into the table with
         // a new key at the new Lookup position.  |front()| is invalid after
         // this operation until the next call to |popFront()|.
         void rekeyFront(const Lookup& l, const Key& k) {
             MOZ_ASSERT(&k != &HashPolicy::getKey(this->cur->get()));
             Ptr p(*this->cur, table_);
             table_.rekeyWithoutRehash(p, l, k);
             rekeyed = true;
--- a/js/public/HeapAPI.h
+++ b/js/public/HeapAPI.h
@@ -49,18 +49,17 @@ const size_t CellMask = CellSize - 1;
 #ifdef JS_GC_SMALL_CHUNK_SIZE
 const size_t ChunkMarkBitmapOffset = 258104;
 const size_t ChunkMarkBitmapBits = 31744;
 #else
 const size_t ChunkMarkBitmapOffset = 1032352;
 const size_t ChunkMarkBitmapBits = 129024;
 #endif
 const size_t ChunkRuntimeOffset = ChunkSize - sizeof(void*);
-const size_t ChunkTrailerSize = 2 * sizeof(uintptr_t) + sizeof(uint64_t);
-const size_t ChunkLocationOffset = ChunkSize - ChunkTrailerSize;
+const size_t ChunkLocationOffset = ChunkSize - 2 * sizeof(void*) - sizeof(uint64_t);
 const size_t ArenaZoneOffset = 0;
 
 /*
  * Live objects are marked black. How many other additional colors are available
  * depends on the size of the GCThing. Objects marked gray are eligible for
  * cycle collection.
  */
 static const uint32_t BLACK = 0;
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -14,17 +14,16 @@
 #include "jsutil.h"
 
 #include "gc/Marking.h"
 #include "js/Vector.h"
 #include "vm/GlobalObject.h"
 #include "vm/String.h"
 #include "vm/StringBuffer.h"
 #include "vm/TypedArrayObject.h"
-#include "vm/WeakMapObject.h"
 
 #include "jsatominlines.h"
 #include "jsobjinlines.h"
 
 #include "vm/NativeObject-inl.h"
 #include "vm/Shape-inl.h"
 
 using mozilla::AssertedCast;
new file mode 100644
--- /dev/null
+++ b/js/src/builtin/WeakMapObject.cpp
@@ -0,0 +1,485 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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 "builtin/WeakMapObject.h"
+
+#include "jsapi.h"
+#include "jscntxt.h"
+
+#include "vm/Interpreter-inl.h"
+
+using namespace js;
+using namespace js::gc;
+
+using mozilla::UniquePtr;
+
+MOZ_ALWAYS_INLINE bool
+IsWeakMap(HandleValue v)
+{
+    return v.isObject() && v.toObject().is<WeakMapObject>();
+}
+
+MOZ_ALWAYS_INLINE bool
+WeakMap_has_impl(JSContext* cx, const CallArgs& args)
+{
+    MOZ_ASSERT(IsWeakMap(args.thisv()));
+
+    if (!args.get(0).isObject()) {
+        args.rval().setBoolean(false);
+        return true;
+    }
+
+    if (ObjectValueMap* map = args.thisv().toObject().as<WeakMapObject>().getMap()) {
+        JSObject* key = &args[0].toObject();
+        if (map->has(key)) {
+            args.rval().setBoolean(true);
+            return true;
+        }
+    }
+
+    args.rval().setBoolean(false);
+    return true;
+}
+
+bool
+js::WeakMap_has(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    return CallNonGenericMethod<IsWeakMap, WeakMap_has_impl>(cx, args);
+}
+
+MOZ_ALWAYS_INLINE bool
+WeakMap_clear_impl(JSContext* cx, const CallArgs& args)
+{
+    MOZ_ASSERT(IsWeakMap(args.thisv()));
+
+    // We can't js_delete the weakmap because the data gathered during GC is
+    // used by the Cycle Collector.
+    if (ObjectValueMap* map = args.thisv().toObject().as<WeakMapObject>().getMap())
+        map->clear();
+
+    args.rval().setUndefined();
+    return true;
+}
+
+bool
+js::WeakMap_clear(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    return CallNonGenericMethod<IsWeakMap, WeakMap_clear_impl>(cx, args);
+}
+
+MOZ_ALWAYS_INLINE bool
+WeakMap_get_impl(JSContext* cx, const CallArgs& args)
+{
+    MOZ_ASSERT(IsWeakMap(args.thisv()));
+
+    if (!args.get(0).isObject()) {
+        args.rval().setUndefined();
+        return true;
+    }
+
+    if (ObjectValueMap* map = args.thisv().toObject().as<WeakMapObject>().getMap()) {
+        JSObject* key = &args[0].toObject();
+        if (ObjectValueMap::Ptr ptr = map->lookup(key)) {
+            args.rval().set(ptr->value());
+            return true;
+        }
+    }
+
+    args.rval().setUndefined();
+    return true;
+}
+
+bool
+js::WeakMap_get(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    return CallNonGenericMethod<IsWeakMap, WeakMap_get_impl>(cx, args);
+}
+
+MOZ_ALWAYS_INLINE bool
+WeakMap_delete_impl(JSContext* cx, const CallArgs& args)
+{
+    MOZ_ASSERT(IsWeakMap(args.thisv()));
+
+    if (!args.get(0).isObject()) {
+        args.rval().setBoolean(false);
+        return true;
+    }
+
+    if (ObjectValueMap* map = args.thisv().toObject().as<WeakMapObject>().getMap()) {
+        JSObject* key = &args[0].toObject();
+        if (ObjectValueMap::Ptr ptr = map->lookup(key)) {
+            map->remove(ptr);
+            args.rval().setBoolean(true);
+            return true;
+        }
+    }
+
+    args.rval().setBoolean(false);
+    return true;
+}
+
+bool
+js::WeakMap_delete(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    return CallNonGenericMethod<IsWeakMap, WeakMap_delete_impl>(cx, args);
+}
+
+static bool
+TryPreserveReflector(JSContext* cx, HandleObject obj)
+{
+    if (obj->getClass()->ext.isWrappedNative ||
+        (obj->getClass()->flags & JSCLASS_IS_DOMJSCLASS) ||
+        (obj->is<ProxyObject>() &&
+         obj->as<ProxyObject>().handler()->family() == GetDOMProxyHandlerFamily()))
+    {
+        MOZ_ASSERT(cx->runtime()->preserveWrapperCallback);
+        if (!cx->runtime()->preserveWrapperCallback(cx, obj)) {
+            JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_WEAKMAP_KEY);
+            return false;
+        }
+    }
+    return true;
+}
+
+static inline void
+WeakMapPostWriteBarrier(JSRuntime* rt, ObjectValueMap* weakMap, JSObject* key)
+{
+    if (key && IsInsideNursery(key))
+        rt->gc.storeBuffer.putGeneric(gc::HashKeyRef<ObjectValueMap, JSObject*>(weakMap, key));
+}
+
+static MOZ_ALWAYS_INLINE bool
+SetWeakMapEntryInternal(JSContext* cx, Handle<WeakMapObject*> mapObj,
+                        HandleObject key, HandleValue value)
+{
+    ObjectValueMap* map = mapObj->getMap();
+    if (!map) {
+        AutoInitGCManagedObject<ObjectValueMap> newMap(
+            cx->make_unique<ObjectValueMap>(cx, mapObj.get()));
+        if (!newMap)
+            return false;
+        if (!newMap->init()) {
+            JS_ReportOutOfMemory(cx);
+            return false;
+        }
+        map = newMap.release();
+        mapObj->setPrivate(map);
+    }
+
+    // Preserve wrapped native keys to prevent wrapper optimization.
+    if (!TryPreserveReflector(cx, key))
+        return false;
+
+    if (JSWeakmapKeyDelegateOp op = key->getClass()->ext.weakmapKeyDelegateOp) {
+        RootedObject delegate(cx, op(key));
+        if (delegate && !TryPreserveReflector(cx, delegate))
+            return false;
+    }
+
+    MOZ_ASSERT(key->compartment() == mapObj->compartment());
+    MOZ_ASSERT_IF(value.isObject(), value.toObject().compartment() == mapObj->compartment());
+    if (!map->put(key, value)) {
+        JS_ReportOutOfMemory(cx);
+        return false;
+    }
+    WeakMapPostWriteBarrier(cx->runtime(), map, key.get());
+    return true;
+}
+
+MOZ_ALWAYS_INLINE bool
+WeakMap_set_impl(JSContext* cx, const CallArgs& args)
+{
+    MOZ_ASSERT(IsWeakMap(args.thisv()));
+
+    if (!args.get(0).isObject()) {
+        UniquePtr<char[], JS::FreePolicy> bytes =
+            DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, args.get(0), nullptr);
+        if (!bytes)
+            return false;
+        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT, bytes.get());
+        return false;
+    }
+
+    RootedObject key(cx, &args[0].toObject());
+    Rooted<JSObject*> thisObj(cx, &args.thisv().toObject());
+    Rooted<WeakMapObject*> map(cx, &thisObj->as<WeakMapObject>());
+
+    if (!SetWeakMapEntryInternal(cx, map, key, args.get(1)))
+        return false;
+    args.rval().set(args.thisv());
+    return true;
+}
+
+bool
+js::WeakMap_set(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    return CallNonGenericMethod<IsWeakMap, WeakMap_set_impl>(cx, args);
+}
+
+JS_FRIEND_API(bool)
+JS_NondeterministicGetWeakMapKeys(JSContext* cx, HandleObject objArg, MutableHandleObject ret)
+{
+    RootedObject obj(cx, objArg);
+    obj = UncheckedUnwrap(obj);
+    if (!obj || !obj->is<WeakMapObject>()) {
+        ret.set(nullptr);
+        return true;
+    }
+    RootedObject arr(cx, NewDenseEmptyArray(cx));
+    if (!arr)
+        return false;
+    ObjectValueMap* map = obj->as<WeakMapObject>().getMap();
+    if (map) {
+        // Prevent GC from mutating the weakmap while iterating.
+        AutoSuppressGC suppress(cx);
+        for (ObjectValueMap::Base::Range r = map->all(); !r.empty(); r.popFront()) {
+            JS::ExposeObjectToActiveJS(r.front().key());
+            RootedObject key(cx, r.front().key());
+            if (!cx->compartment()->wrap(cx, &key))
+                return false;
+            if (!NewbornArrayPush(cx, arr, ObjectValue(*key)))
+                return false;
+        }
+    }
+    ret.set(arr);
+    return true;
+}
+
+static void
+WeakMap_mark(JSTracer* trc, JSObject* obj)
+{
+    if (ObjectValueMap* map = obj->as<WeakMapObject>().getMap())
+        map->trace(trc);
+}
+
+static void
+WeakMap_finalize(FreeOp* fop, JSObject* obj)
+{
+    if (ObjectValueMap* map = obj->as<WeakMapObject>().getMap()) {
+#ifdef DEBUG
+        map->~ObjectValueMap();
+        memset(static_cast<void*>(map), 0xdc, sizeof(*map));
+        fop->free_(map);
+#else
+        fop->delete_(map);
+#endif
+    }
+}
+
+JS_PUBLIC_API(JSObject*)
+JS::NewWeakMapObject(JSContext* cx)
+{
+    return NewBuiltinClassInstance(cx, &WeakMapObject::class_);
+}
+
+JS_PUBLIC_API(bool)
+JS::IsWeakMapObject(JSObject* obj)
+{
+    return obj->is<WeakMapObject>();
+}
+
+JS_PUBLIC_API(bool)
+JS::GetWeakMapEntry(JSContext* cx, HandleObject mapObj, HandleObject key,
+                    MutableHandleValue rval)
+{
+    CHECK_REQUEST(cx);
+    assertSameCompartment(cx, key);
+    rval.setUndefined();
+    ObjectValueMap* map = mapObj->as<WeakMapObject>().getMap();
+    if (!map)
+        return true;
+    if (ObjectValueMap::Ptr ptr = map->lookup(key)) {
+        // Read barrier to prevent an incorrectly gray value from escaping the
+        // weak map. See the comment before UnmarkGrayChildren in gc/Marking.cpp
+        ExposeValueToActiveJS(ptr->value().get());
+        rval.set(ptr->value());
+    }
+    return true;
+}
+
+JS_PUBLIC_API(bool)
+JS::SetWeakMapEntry(JSContext* cx, HandleObject mapObj, HandleObject key,
+                    HandleValue val)
+{
+    CHECK_REQUEST(cx);
+    assertSameCompartment(cx, key, val);
+    Rooted<WeakMapObject*> rootedMap(cx, &mapObj->as<WeakMapObject>());
+    return SetWeakMapEntryInternal(cx, rootedMap, key, val);
+}
+
+static bool
+WeakMap_construct(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    // ES6 draft rev 31 (15 Jan 2015) 23.3.1.1 step 1.
+    if (!ThrowIfNotConstructing(cx, args, "WeakMap"))
+        return false;
+
+    RootedObject obj(cx, NewBuiltinClassInstance(cx, &WeakMapObject::class_));
+    if (!obj)
+        return false;
+
+    // Steps 5-6, 11.
+    if (!args.get(0).isNullOrUndefined()) {
+        // Steps 7a-b.
+        RootedValue adderVal(cx);
+        if (!GetProperty(cx, obj, obj, cx->names().set, &adderVal))
+            return false;
+
+        // Step 7c.
+        if (!IsCallable(adderVal))
+            return ReportIsNotFunction(cx, adderVal);
+
+        bool isOriginalAdder = IsNativeFunction(adderVal, WeakMap_set);
+        RootedValue mapVal(cx, ObjectValue(*obj));
+        FastInvokeGuard fig(cx, adderVal);
+        InvokeArgs& args2 = fig.args();
+
+        // Steps 7d-e.
+        JS::ForOfIterator iter(cx);
+        if (!iter.init(args[0]))
+            return false;
+
+        RootedValue pairVal(cx);
+        RootedObject pairObject(cx);
+        RootedValue keyVal(cx);
+        RootedObject keyObject(cx);
+        RootedValue val(cx);
+        while (true) {
+            // Steps 12a-e.
+            bool done;
+            if (!iter.next(&pairVal, &done))
+                return false;
+            if (done)
+                break;
+
+            // Step 12f.
+            if (!pairVal.isObject()) {
+                JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
+                                     JSMSG_INVALID_MAP_ITERABLE, "WeakMap");
+                return false;
+            }
+
+            pairObject = &pairVal.toObject();
+            if (!pairObject)
+                return false;
+
+            // Steps 12g-h.
+            if (!GetElement(cx, pairObject, pairObject, 0, &keyVal))
+                return false;
+
+            // Steps 12i-j.
+            if (!GetElement(cx, pairObject, pairObject, 1, &val))
+                return false;
+
+            // Steps 12k-l.
+            if (isOriginalAdder) {
+                if (keyVal.isPrimitive()) {
+                    UniquePtr<char[], JS::FreePolicy> bytes =
+                        DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, keyVal, nullptr);
+                    if (!bytes)
+                        return false;
+                    JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT, bytes.get());
+                    return false;
+                }
+
+                keyObject = &keyVal.toObject();
+                if (!SetWeakMapEntry(cx, obj, keyObject, val))
+                    return false;
+            } else {
+                if (!args2.init(2))
+                    return false;
+
+                args2.setCallee(adderVal);
+                args2.setThis(mapVal);
+                args2[0].set(keyVal);
+                args2[1].set(val);
+
+                if (!fig.invoke(cx))
+                    return false;
+            }
+        }
+    }
+
+    args.rval().setObject(*obj);
+    return true;
+}
+
+const Class WeakMapObject::class_ = {
+    "WeakMap",
+    JSCLASS_HAS_PRIVATE |
+    JSCLASS_HAS_CACHED_PROTO(JSProto_WeakMap),
+    nullptr, /* addProperty */
+    nullptr, /* delProperty */
+    nullptr, /* getProperty */
+    nullptr, /* setProperty */
+    nullptr, /* enumerate */
+    nullptr, /* resolve */
+    nullptr, /* mayResolve */
+    nullptr, /* convert */
+    WeakMap_finalize,
+    nullptr, /* call */
+    nullptr, /* hasInstance */
+    nullptr, /* construct */
+    WeakMap_mark
+};
+
+static const JSFunctionSpec weak_map_methods[] = {
+    JS_FN("has",    WeakMap_has, 1, 0),
+    JS_FN("get",    WeakMap_get, 1, 0),
+    JS_FN("delete", WeakMap_delete, 1, 0),
+    JS_FN("set",    WeakMap_set, 2, 0),
+    JS_FN("clear",  WeakMap_clear, 0, 0),
+    JS_FS_END
+};
+
+static JSObject*
+InitWeakMapClass(JSContext* cx, HandleObject obj, bool defineMembers)
+{
+    MOZ_ASSERT(obj->isNative());
+
+    Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
+
+    RootedPlainObject proto(cx, NewBuiltinClassInstance<PlainObject>(cx));
+    if (!proto)
+        return nullptr;
+
+    RootedFunction ctor(cx, global->createConstructor(cx, WeakMap_construct,
+                                                      cx->names().WeakMap, 0));
+    if (!ctor)
+        return nullptr;
+
+    if (!LinkConstructorAndPrototype(cx, ctor, proto))
+        return nullptr;
+
+    if (defineMembers) {
+        if (!DefinePropertiesAndFunctions(cx, proto, nullptr, weak_map_methods))
+            return nullptr;
+    }
+
+    if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_WeakMap, ctor, proto))
+        return nullptr;
+    return proto;
+}
+
+JSObject*
+js::InitWeakMapClass(JSContext* cx, HandleObject obj)
+{
+    return InitWeakMapClass(cx, obj, true);
+}
+
+JSObject*
+js::InitBareWeakMapCtor(JSContext* cx, HandleObject obj)
+{
+    return InitWeakMapClass(cx, obj, false);
+}
+
rename from js/src/vm/WeakMapObject.h
rename to js/src/builtin/WeakMapObject.h
--- a/js/src/vm/WeakMapObject.h
+++ b/js/src/builtin/WeakMapObject.h
@@ -1,61 +1,25 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * 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 vm_WeakMapObject_h
-#define vm_WeakMapObject_h
+#ifndef builtin_WeakMapObject_h
+#define builtin_WeakMapObject_h
 
 #include "jsobj.h"
 #include "jsweakmap.h"
 
 namespace js {
 
-class ObjectValueMap : public WeakMap<PreBarrieredObject, RelocatableValue>
-{
-  public:
-    ObjectValueMap(JSContext* cx, JSObject* obj)
-      : WeakMap<PreBarrieredObject, RelocatableValue>(cx, obj) {}
-
-    virtual bool findZoneEdges();
-};
-
 class WeakMapObject : public NativeObject
 {
   public:
     static const Class class_;
 
     ObjectValueMap* getMap() { return static_cast<ObjectValueMap*>(getPrivate()); }
 };
 
-// Generic weak map for mapping objects to other objects.
-class ObjectWeakMap
-{
-  private:
-    ObjectValueMap map;
-    typedef gc::HashKeyRef<ObjectValueMap, JSObject*> StoreBufferRef;
-
-  public:
-    explicit ObjectWeakMap(JSContext* cx);
-    bool init();
-    ~ObjectWeakMap();
-
-    JSObject* lookup(const JSObject* obj);
-    bool add(JSContext* cx, JSObject* obj, JSObject* target);
-    void clear();
-
-    void trace(JSTracer* trc);
-    size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
-    size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) {
-        return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf);
-    }
-
-#ifdef JSGC_HASH_TABLE_CHECKS
-    void checkAfterMovingGC();
-#endif
-};
-
 } // namespace js
 
-#endif /* vm_WeakMapObject_h */
+#endif /* builtin_WeakMapObject_h */
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -120,16 +120,18 @@ dnl ====================================
 MOZ_ARG_WITH_STRING(dist-dir,
 [  --with-dist-dir=DIR     Use DIR as 'dist' staging area.  DIR may be
                           relative to the top of SpiderMonkey build tree,
                           or absolute.],
     TOP_DIST=$withval,
     TOP_DIST=dist)
 AC_SUBST(TOP_DIST)
 
+MOZ_BUILD_BACKEND
+
 MOZ_DEFAULT_COMPILER
 
 COMPILE_ENVIRONMENT=1
 MOZ_ARG_DISABLE_BOOL(compile-environment,
 [  --disable-compile-environment
                           Disable compiler/library checks.],
     COMPILE_ENVIRONMENT= )
 AC_SUBST(COMPILE_ENVIRONMENT)
deleted file mode 100644
--- a/js/src/ds/LockGuard.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * 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 js_LockGuard_h
-#define js_LockGuard_h
-
-#include "mozilla/GuardObjects.h"
-
-namespace js {
-
-// An implementation of C++11's std::lock_guard, enhanced with a guard object
-// to help with correct usage.
-template <typename LockType>
-class LockGuard
-{
-    LockType& lockRef_;
-    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER;
-
-  public:
-    explicit LockGuard(LockType& lock
-                       MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-      : lockRef_(lock)
-    {
-        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-        lockRef_.lock();
-    }
-
-    ~LockGuard() {
-        lockRef_.unlock();
-    }
-};
-
-} // namespace js
-
-#endif // js_LockGuard_h
deleted file mode 100644
--- a/js/src/ds/SpinLock.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * 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 js_SpinLock_h
-#define js_SpinLock_h
-
-#include "mozilla/Atomics.h"
-
-#include "ds/LockGuard.h"
-
-namespace js {
-
-// A trivial spin-lock implementation. Extremely fast when rarely-contended.
-class SpinLock
-{
-    mozilla::Atomic<bool, mozilla::ReleaseAcquire> locked_;
-
-  public:
-    SpinLock() : locked_(false) {}
-
-    void lock() {
-        do {
-            while (locked_)
-                ; // Spin until the lock seems free.
-        } while (!locked_.compareExchange(false, true)); // Atomically take the lock.
-    }
-
-    void unlock() {
-        locked_ = false;
-    }
-};
-
-using AutoSpinLock = LockGuard<SpinLock>;
-
-} // namespace js
-
-#endif // js_SpinLock_h
--- a/js/src/gc/Barrier.cpp
+++ b/js/src/gc/Barrier.cpp
@@ -5,19 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "gc/Barrier.h"
 
 #include "jscompartment.h"
 #include "jsobj.h"
 
 #include "gc/Zone.h"
-#include "js/HashTable.h"
 #include "js/Value.h"
-#include "vm/ScopeObject.h"
 #include "vm/Symbol.h"
 
 #ifdef DEBUG
 
 bool
 js::HeapSlot::preconditionForSet(NativeObject* owner, Kind kind, uint32_t slot)
 {
     return kind == Slot
@@ -99,61 +97,8 @@ JS::HeapObjectPostBarrier(JSObject** obj
 }
 
 JS_PUBLIC_API(void)
 JS::HeapValuePostBarrier(JS::Value* valuep, const Value& prev, const Value& next)
 {
     MOZ_ASSERT(valuep);
     js::InternalGCMethods<JS::Value>::postBarrier(valuep, prev, next);
 }
-
-template <typename T>
-/* static */ js::HashNumber
-js::MovableCellHasher<T>::hash(const Lookup& l)
-{
-    if (!l)
-        return 0;
-
-    // We have to access the zone from-any-thread here: a worker thread may be
-    // cloning a self-hosted object from the main-thread-runtime-owned self-
-    // hosting zone into the off-main-thread runtime. The zone's uid lock will
-    // protect against multiple workers doing this simultaneously.
-    MOZ_ASSERT(CurrentThreadCanAccessZone(l->zoneFromAnyThread()) ||
-               l->zoneFromAnyThread()->isSelfHostingZone());
-
-    js::HashNumber hn;
-    if (!l->zoneFromAnyThread()->getHashCode(l, &hn))
-        js::CrashAtUnhandlableOOM("failed to get a stable hash code");
-    return hn;
-}
-
-template <typename T>
-/* static */ bool
-js::MovableCellHasher<T>::match(const Key& k, const Lookup& l)
-{
-    // Return true if both are null or false if only one is null.
-    if (!k)
-        return !l;
-    if (!l)
-        return false;
-
-    MOZ_ASSERT(k);
-    MOZ_ASSERT(l);
-    MOZ_ASSERT(CurrentThreadCanAccessZone(l->zoneFromAnyThread()) ||
-               l->zoneFromAnyThread()->isSelfHostingZone());
-
-    Zone* zone = k->zoneFromAnyThread();
-    if (zone != l->zoneFromAnyThread())
-        return false;
-    MOZ_ASSERT(zone->hasUniqueId(k));
-    MOZ_ASSERT(zone->hasUniqueId(l));
-
-    // Since both already have a uid (from hash), the get is infallible.
-    uint64_t uidK, uidL;
-    MOZ_ALWAYS_TRUE(zone->getUniqueId(k, &uidK));
-    MOZ_ALWAYS_TRUE(zone->getUniqueId(l, &uidL));
-    return uidK == uidL;
-}
-
-template struct js::MovableCellHasher<JSObject*>;
-template struct js::MovableCellHasher<js::GlobalObject*>;
-template struct js::MovableCellHasher<js::SavedFrame*>;
-template struct js::MovableCellHasher<js::ScopeObject*>;
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -688,41 +688,16 @@ class ImmutableTenuredPtr
         MOZ_ASSERT(ptr->isTenured());
         value = ptr;
     }
 
     T get() const { return value; }
     const T* address() { return &value; }
 };
 
-// Provide hash codes for Cell kinds that may be relocated and, thus, not have
-// a stable address to use as the base for a hash code. Instead of the address,
-// this hasher uses Cell::getUniqueId to provide exact matches and as a base
-// for generating hash codes.
-//
-// Note: this hasher, like PointerHasher can "hash" a nullptr. While a nullptr
-// would not likely be a useful key, there are some cases where being able to
-// hash a nullptr is useful, either on purpose or because of bugs:
-// (1) existence checks where the key may happen to be null and (2) some
-// aggregate Lookup kinds embed a JSObject* that is frequently null and do not
-// null test before dispatching to the hasher.
-template <typename T>
-struct MovableCellHasher
-{
-    static_assert(mozilla::IsBaseOf<JSObject, typename mozilla::RemovePointer<T>::Type>::value,
-                  "MovableCellHasher's T must be a Cell type that may move");
-
-    using Key = T;
-    using Lookup = T;
-
-    static HashNumber hash(const Lookup& l);
-    static bool match(const Key& k, const Lookup& l);
-    static void rekey(Key& k, const Key& newKey) { k = newKey; }
-};
-
 /* Useful for hashtables with a HeapPtr as key. */
 template <class T>
 struct HeapPtrHasher
 {
     typedef HeapPtr<T> Key;
     typedef T Lookup;
 
     static HashNumber hash(Lookup obj) { return DefaultHasher<T>::hash(obj); }
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -650,21 +650,16 @@ class GCRuntime
     void maybeVerifyPreBarriers(bool always);
     bool selectForMarking(JSObject* object);
     void clearSelectedForMarking();
     void setDeterministic(bool enable);
 #endif
 
     size_t maxMallocBytesAllocated() { return maxMallocBytes; }
 
-    uint64_t nextCellUniqueId() {
-        MOZ_ASSERT(nextCellUniqueId_ > 0);
-        return nextCellUniqueId_++;
-    }
-
   public:
     // Internal public interface
     js::gc::State state() const { return incrementalState; }
     bool isHeapCompacting() const { return state() == COMPACT; }
     bool isBackgroundSweeping() { return helperState.isBackgroundSweeping(); }
     void waitBackgroundSweepEnd() { helperState.waitBackgroundSweepEnd(); }
     void waitBackgroundSweepOrAllocEnd() {
         helperState.waitBackgroundSweepEnd();
@@ -1013,19 +1008,16 @@ class GCRuntime
     // When all arenas in a chunk are used, it is moved to the fullChunks pool
     // so as to reduce the cost of operations on the available lists.
     ChunkPool             fullChunks_;
 
     RootedValueMap rootsHash;
 
     size_t maxMallocBytes;
 
-    // An incrementing id used to assign unique ids to cells that require one.
-    uint64_t nextCellUniqueId_;
-
     /*
      * Number of the committed arenas in all GC chunks including empty chunks.
      */
     mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> numArenasFreeCommitted;
     VerifyPreTracer* verifyPreData;
     bool chunkAllocationSinceLastGC;
     int64_t nextFullGCTime;
     int64_t lastGCTime;
--- a/js/src/gc/Heap.h
+++ b/js/src/gc/Heap.h
@@ -289,19 +289,16 @@ class TenuredCell : public Cell
     static MOZ_ALWAYS_INLINE void writeBarrierPost(void* cellp, TenuredCell* prior,
                                                    TenuredCell* next);
 
 #ifdef DEBUG
     inline bool isAligned() const;
 #endif
 };
 
-/* Cells are aligned to CellShift, so the largest tagged null pointer is: */
-const uintptr_t LargestTaggedNullCellPointer = (1 << CellShift) - 1;
-
 /*
  * The mark bitmap has one bit per each GC cell. For multi-cell GC things this
  * wastes space but allows to avoid expensive devisions by thing's size when
  * accessing the bitmap. In addition this allows to use some bits for colored
  * marking during the cycle GC.
  */
 const size_t ArenaCellCount = size_t(1) << (ArenaShift - CellShift);
 const size_t ArenaBitmapBits = ArenaCellCount;
@@ -804,40 +801,28 @@ ArenaHeader::getThingSize() const
 
 /*
  * The tail of the chunk info is shared between all chunks in the system, both
  * nursery and tenured. This structure is locatable from any GC pointer by
  * aligning to 1MiB.
  */
 struct ChunkTrailer
 {
-    /* Construct a Nursery ChunkTrailer. */
-    ChunkTrailer(JSRuntime* rt, StoreBuffer* sb)
-      : location(gc::ChunkLocationBitNursery), storeBuffer(sb), runtime(rt)
-    {}
-
-    /* Construct a Tenured heap ChunkTrailer. */
-    explicit ChunkTrailer(JSRuntime* rt)
-      : location(gc::ChunkLocationBitTenuredHeap), storeBuffer(nullptr), runtime(rt)
-    {}
-
-  public:
     /* The index the chunk in the nursery, or LocationTenuredHeap. */
     uint32_t        location;
     uint32_t        padding;
 
     /* The store buffer for writes to things in this chunk or nullptr. */
     StoreBuffer*    storeBuffer;
 
-    /* This provides quick access to the runtime from absolutely anywhere. */
     JSRuntime*      runtime;
 };
 
-static_assert(sizeof(ChunkTrailer) == ChunkTrailerSize,
-              "ChunkTrailer size must match the API defined size.");
+static_assert(sizeof(ChunkTrailer) == 2 * sizeof(uintptr_t) + sizeof(uint64_t),
+              "ChunkTrailer size is incorrect.");
 
 /* The chunk header (located at the end of the chunk to preserve arena alignment). */
 struct ChunkInfo
 {
     void init() {
         next = prev = nullptr;
         age = 0;
     }
@@ -1016,26 +1001,23 @@ struct Chunk
     PerArenaBitmap  decommittedArenas;
     ChunkInfo       info;
 
     static Chunk* fromAddress(uintptr_t addr) {
         addr &= ~ChunkMask;
         return reinterpret_cast<Chunk*>(addr);
     }
 
-    static bool withinValidRange(uintptr_t addr) {
+    static bool withinArenasRange(uintptr_t addr) {
         uintptr_t offset = addr & ChunkMask;
-        return Chunk::fromAddress(addr)->isNurseryChunk()
-               ? offset < ChunkSize - sizeof(ChunkTrailer)
-               : offset < ArenasPerChunk * ArenaSize;
+        return offset < ArenasPerChunk * ArenaSize;
     }
 
     static size_t arenaIndex(uintptr_t addr) {
-        MOZ_ASSERT(!Chunk::fromAddress(addr)->isNurseryChunk());
-        MOZ_ASSERT(withinValidRange(addr));
+        MOZ_ASSERT(withinArenasRange(addr));
         return (addr & ChunkMask) >> ArenaShift;
     }
 
     uintptr_t address() const {
         uintptr_t addr = reinterpret_cast<uintptr_t>(this);
         MOZ_ASSERT(!(addr & ChunkMask));
         return addr;
     }
@@ -1043,20 +1025,16 @@ struct Chunk
     bool unused() const {
         return info.numArenasFree == ArenasPerChunk;
     }
 
     bool hasAvailableArenas() const {
         return info.numArenasFree != 0;
     }
 
-    bool isNurseryChunk() const {
-        return info.trailer.storeBuffer;
-    }
-
     ArenaHeader* allocateArena(JSRuntime* rt, JS::Zone* zone, AllocKind kind,
                                const AutoLockGC& lock);
 
     void releaseArena(JSRuntime* rt, ArenaHeader* aheader, const AutoLockGC& lock);
     void recycleArena(ArenaHeader* aheader, SortedArenaList& dest, AllocKind thingKind,
                       size_t thingsPerArena);
 
     bool decommitOneFreeArena(JSRuntime* rt, AutoLockGC& lock);
@@ -1146,17 +1124,17 @@ class HeapUsage
 };
 
 inline uintptr_t
 ArenaHeader::address() const
 {
     uintptr_t addr = reinterpret_cast<uintptr_t>(this);
     MOZ_ASSERT(addr);
     MOZ_ASSERT(!(addr & ArenaMask));
-    MOZ_ASSERT(Chunk::withinValidRange(addr));
+    MOZ_ASSERT(Chunk::withinArenasRange(addr));
     return addr;
 }
 
 inline Chunk*
 ArenaHeader::chunk() const
 {
     return Chunk::fromAddress(address());
 }
@@ -1315,17 +1293,17 @@ Cell::shadowRuntimeFromAnyThread() const
     return reinterpret_cast<JS::shadow::Runtime*>(runtimeFromAnyThread());
 }
 
 inline uintptr_t
 Cell::address() const
 {
     uintptr_t addr = uintptr_t(this);
     MOZ_ASSERT(addr % CellSize == 0);
-    MOZ_ASSERT(Chunk::withinValidRange(addr));
+    MOZ_ASSERT(Chunk::withinArenasRange(addr));
     return addr;
 }
 
 Chunk*
 Cell::chunk() const
 {
     uintptr_t addr = uintptr_t(this);
     MOZ_ASSERT(addr % CellSize == 0);
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -2112,23 +2112,17 @@ js::TenuringTracer::moveObjectToTenured(
      *
      * For Arrays we're reducing tenuredSize to the smaller srcSize
      * because moveElementsToTenured() accounts for all Array elements,
      * even if they are inlined.
      */
     if (src->is<ArrayObject>())
         tenuredSize = srcSize = sizeof(NativeObject);
 
-    // Copy the Cell contents.
     js_memcpy(dst, src, srcSize);
-
-    // Move any hash code attached to the object.
-    src->zone()->transferUniqueId(dst, src);
-
-    // Move the slots and elements, if we need to.
     if (src->isNative()) {
         NativeObject* ndst = &dst->as<NativeObject>();
         NativeObject* nsrc = &src->as<NativeObject>();
         tenuredSize += moveSlotsToTenured(ndst, nsrc, dstKind);
         tenuredSize += moveElementsToTenured(ndst, nsrc, dstKind);
 
         // The shape's list head may point into the old object. This can only
         // happen for dictionaries, which are native objects.
--- a/js/src/gc/Marking.h
+++ b/js/src/gc/Marking.h
@@ -420,17 +420,17 @@ ToMarkable(Cell* cell)
     return cell;
 }
 
 // Return true if the pointer is nullptr, or if it is a tagged pointer to
 // nullptr.
 MOZ_ALWAYS_INLINE bool
 IsNullTaggedPointer(void* p)
 {
-    return uintptr_t(p) <= LargestTaggedNullCellPointer;
+    return uintptr_t(p) < 32;
 }
 
 // HashKeyRef represents a reference to a HashMap key. This should normally
 // be used through the HashTableWriteBarrierPost function.
 template <typename Map, typename Key>
 class HashKeyRef : public BufferableRef
 {
     Map* map;
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -62,19 +62,16 @@ js::Nursery::init(uint32_t maxNurseryByt
 
     /* If no chunks are specified then the nursery is permenantly disabled. */
     if (numNurseryChunks_ == 0)
         return true;
 
     if (!mallocedBuffers.init())
         return false;
 
-    if (!cellsWithUid_.init())
-        return false;
-
     void* heap = MapAlignedPages(nurserySize(), Alignment);
     if (!heap)
         return false;
 
     freeMallocedBuffersTask = js_new<FreeMallocedBuffersTask>(runtime()->defaultFreeOp());
     if (!freeMallocedBuffersTask || !freeMallocedBuffersTask->init())
         return false;
 
@@ -651,26 +648,16 @@ js::Nursery::waitBackgroundFreeEnd()
 {
     MOZ_ASSERT(freeMallocedBuffersTask);
     freeMallocedBuffersTask->join();
 }
 
 void
 js::Nursery::sweep()
 {
-    /* Sweep unique id's in all in-use chunks. */
-    for (CellsWithUniqueIdSet::Enum e(cellsWithUid_); !e.empty(); e.popFront()) {
-        JSObject* obj = static_cast<JSObject*>(e.front());
-        if (!IsForwarded(obj))
-            obj->zone()->removeUniqueId(obj);
-        else
-            MOZ_ASSERT(Forwarded(obj)->zone()->hasUniqueId(Forwarded(obj)));
-    }
-    cellsWithUid_.clear();
-
 #ifdef JS_GC_ZEAL
     /* Poison the nursery contents so touching a freed object will crash. */
     JS_POISON((void*)start(), JS_SWEPT_NURSERY_PATTERN, nurserySize());
     for (int i = 0; i < numNurseryChunks_; ++i)
         initChunk(i);
 
     if (runtime()->gcZeal() == ZealGenerationalGCValue) {
         MOZ_ASSERT(numActiveChunks_ == numNurseryChunks_);
@@ -678,18 +665,20 @@ js::Nursery::sweep()
         /* Only reset the alloc point when we are close to the end. */
         if (currentChunk_ + 1 == numNurseryChunks_)
             setCurrentChunk(0);
     } else
 #endif
     {
 #ifdef JS_CRASH_DIAGNOSTICS
         JS_POISON((void*)start(), JS_SWEPT_NURSERY_PATTERN, allocationEnd() - start());
-        for (int i = 0; i < numActiveChunks_; ++i)
-            initChunk(i);
+        for (int i = 0; i < numActiveChunks_; ++i) {
+            chunk(i).trailer.location = gc::ChunkLocationBitNursery;
+            chunk(i).trailer.runtime = runtime();
+        }
 #endif
         setCurrentChunk(0);
     }
 
     /* Set current start position for isEmpty checks. */
     currentStart_ = position();
     MemProfiler::SweepNursery(runtime());
 }
--- a/js/src/gc/Nursery.h
+++ b/js/src/gc/Nursery.h
@@ -178,24 +178,16 @@ class Nursery
 
     /* Mark a malloced buffer as no longer needing to be freed. */
     void removeMallocedBuffer(void* buffer) {
         mallocedBuffers.remove(buffer);
     }
 
     void waitBackgroundFreeEnd();
 
-    bool addedUniqueIdToCell(gc::Cell* cell) {
-        if (!IsInsideNursery(cell) || !isEnabled())
-            return true;
-        MOZ_ASSERT(cellsWithUid_.initialized());
-        MOZ_ASSERT(!cellsWithUid_.has(cell));
-        return cellsWithUid_.put(cell);
-    }
-
     size_t sizeOfHeapCommitted() const {
         return numActiveChunks_ * gc::ChunkSize;
     }
     size_t sizeOfHeapDecommitted() const {
         return (numNurseryChunks_ - numActiveChunks_) * gc::ChunkSize;
     }
     size_t sizeOfMallocedBuffers(mozilla::MallocSizeOf mallocSizeOf) const {
         size_t total = 0;
@@ -269,31 +261,16 @@ class Nursery
      * new location with a forwarding pointer at the base. This does not work
      * for buffers whose length is less than pointer width, or when different
      * buffers might overlap each other. For these, an entry in the following
      * table is used.
      */
     typedef HashMap<void*, void*, PointerHasher<void*, 1>, SystemAllocPolicy> ForwardedBufferMap;
     ForwardedBufferMap forwardedBuffers;
 
-    /*
-     * When we assign a unique id to cell in the nursery, that almost always
-     * means that the cell will be in a hash table, and thus, held live,
-     * automatically moving the uid from the nursery to its new home in
-     * tenured. It is possible, if rare, for an object that acquired a uid to
-     * be dead before the next collection, in which case we need to know to
-     * remove it when we sweep.
-     *
-     * Note: we store the pointers as Cell* here, resulting in an ugly cast in
-     *       sweep. This is because this structure is used to help implement
-     *       stable object hashing and we have to break the cycle somehow.
-     */
-    using CellsWithUniqueIdSet = HashSet<gc::Cell*, PointerHasher<gc::Cell*, 3>, SystemAllocPolicy>;
-    CellsWithUniqueIdSet cellsWithUid_;
-
     /* The maximum number of bytes allowed to reside in nursery buffers. */
     static const size_t MaxNurseryBufferSize = 1024;
 
     /* The amount of space in the mapped nursery available to allocations. */
     static const size_t NurseryChunkUsableSize = gc::ChunkSize - sizeof(gc::ChunkTrailer);
 
     struct NurseryChunkLayout {
         char data[NurseryChunkUsableSize];
@@ -305,18 +282,20 @@ class Nursery
                   "Nursery chunk size must match gc::Chunk size.");
     NurseryChunkLayout& chunk(int index) const {
         MOZ_ASSERT(index < numNurseryChunks_);
         MOZ_ASSERT(start());
         return reinterpret_cast<NurseryChunkLayout*>(start())[index];
     }
 
     MOZ_ALWAYS_INLINE void initChunk(int chunkno) {
-        gc::StoreBuffer* sb = JS::shadow::Runtime::asShadowRuntime(runtime())->gcStoreBufferPtr();
-        new (&chunk(chunkno).trailer) gc::ChunkTrailer(runtime(), sb);
+        NurseryChunkLayout& c = chunk(chunkno);
+        c.trailer.storeBuffer = JS::shadow::Runtime::asShadowRuntime(runtime())->gcStoreBufferPtr();
+        c.trailer.location = gc::ChunkLocationBitNursery;
+        c.trailer.runtime = runtime();
     }
 
     MOZ_ALWAYS_INLINE void setCurrentChunk(int chunkno) {
         MOZ_ASSERT(chunkno < numNurseryChunks_);
         MOZ_ASSERT(chunkno < numActiveChunks_);
         currentChunk_ = chunkno;
         position_ = chunk(chunkno).start();
         currentEnd_ = chunk(chunkno).end();
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -19,17 +19,16 @@
 #include "builtin/MapObject.h"
 #include "frontend/BytecodeCompiler.h"
 #include "gc/GCInternals.h"
 #include "gc/Marking.h"
 #include "jit/MacroAssembler.h"
 #include "js/HashTable.h"
 #include "vm/Debugger.h"
 #include "vm/JSONParser.h"
-#include "vm/WeakMapObject.h"
 
 #include "jsgcinlines.h"
 #include "jsobjinlines.h"
 
 using namespace js;
 using namespace js::gc;
 
 using mozilla::ArrayEnd;
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -61,17 +61,17 @@ Zone::~Zone()
 
     js_delete(debuggers);
     js_delete(jitZone_);
 }
 
 bool Zone::init(bool isSystemArg)
 {
     isSystem = isSystemArg;
-    return uniqueIds_.init() && gcZoneGroupEdges.init();
+    return gcZoneGroupEdges.init();
 }
 
 void
 Zone::setNeedsIncrementalBarrier(bool needs, ShouldUpdateJit updateJit)
 {
     if (updateJit == UpdateJit && needs != jitUsingBarriers_) {
         jit::ToggleBarriers(this, needs);
         jitUsingBarriers_ = needs;
@@ -151,16 +151,17 @@ Zone::logPromotionsToTenured()
             if ((*dbgp)->isDebuggee(range.front()->compartment()))
                 (*dbgp)->logTenurePromotion(rt, *range.front(), now);
         }
     }
 
     awaitingTenureLogging.clear();
 }
 
+
 void
 Zone::sweepBreakpoints(FreeOp* fop)
 {
     if (fop->runtime()->debuggerList.isEmpty())
         return;
 
     /*
      * Sweep all compartments in a zone at the same time, since there is no way
@@ -251,25 +252,16 @@ Zone::discardJitCode(FreeOp* fop)
              */
             script->resetWarmUpCounter();
         }
 
         jitZone()->optimizedStubSpace()->free();
     }
 }
 
-#ifdef JSGC_HASH_TABLE_CHECKS
-void
-JS::Zone::checkUniqueIdTableAfterMovingGC()
-{
-    for (UniqueIdMap::Enum e(uniqueIds_); !e.empty(); e.popFront())
-        js::gc::CheckGCThingAfterMovingGC(e.front().key());
-}
-#endif
-
 uint64_t
 Zone::gcNumber()
 {
     // Zones in use by exclusive threads are not collected, and threads using
     // them cannot access the main runtime's gcNumber without racing.
     return usedByExclusiveThread ? 0 : runtimeFromMainThread()->gc.gcNumber();
 }
 
--- a/js/src/gc/Zone.h
+++ b/js/src/gc/Zone.h
@@ -8,22 +8,19 @@
 #define gc_Zone_h
 
 #include "mozilla/Atomics.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/MemoryReporting.h"
 
 #include "jscntxt.h"
 
-#include "ds/SpinLock.h"
-#include "ds/SplayTree.h"
 #include "gc/FindSCCs.h"
 #include "gc/GCRuntime.h"
 #include "js/TracingAPI.h"
-#include "vm/MallocProvider.h"
 #include "vm/TypeInference.h"
 
 namespace js {
 
 namespace jit {
 class JitZone;
 } // namespace jit
 
@@ -56,21 +53,16 @@ class ZoneHeapThreshold
     static double computeZoneHeapGrowthFactorForHeapSize(size_t lastBytes,
                                                          const GCSchedulingTunables& tunables,
                                                          const GCSchedulingState& state);
     static size_t computeZoneTriggerBytes(double growthFactor, size_t lastBytes,
                                           JSGCInvocationKind gckind,
                                           const GCSchedulingTunables& tunables);
 };
 
-// Maps a Cell* to a unique, 64bit id.
-using UniqueIdMap = HashMap<Cell*, uint64_t, PointerHasher<Cell*, 3>, SystemAllocPolicy>;
-
-extern uint64_t NextCellUniqueId(JSRuntime* rt);
-
 } // namespace gc
 } // namespace js
 
 namespace JS {
 
 // A zone is a collection of compartments. Every compartment belongs to exactly
 // one zone. In Firefox, there is roughly one zone per tab along with a system
 // zone for everything else. Zones mainly serve as boundaries for garbage
@@ -245,34 +237,25 @@ struct Zone : public JS::shadow::Zone,
 
   private:
     DebuggerVector* debuggers;
 
     using LogTenurePromotionQueue = js::Vector<JSObject*, 0, js::SystemAllocPolicy>;
     LogTenurePromotionQueue awaitingTenureLogging;
 
     void sweepBreakpoints(js::FreeOp* fop);
-    void sweepUniqueIds(js::FreeOp* fop);
     void sweepWeakMaps();
     void sweepCompartments(js::FreeOp* fop, bool keepAtleastOne, bool lastGC);
 
     js::jit::JitZone* createJitZone(JSContext* cx);
 
     bool isQueuedForBackgroundSweep() {
         return isOnList();
     }
 
-    // Side map for storing a unique ids for cells, independent of address.
-    js::gc::UniqueIdMap uniqueIds_;
-
-    // Guards the uniqueIds_ map, which is accessed concurrently from
-    // off-thread parsing and the main thread. This uses a spinlock, since it
-    // is normally uncontended.
-    js::SpinLock uniqueIdsLock_;
-
   public:
     bool hasDebuggers() const { return debuggers && debuggers->length(); }
     DebuggerVector* getDebuggers() const { return debuggers; }
     DebuggerVector* getOrCreateDebuggers(JSContext* cx);
 
     void enqueueForPromotionToTenuredLogging(JSObject& obj) {
         MOZ_ASSERT(hasDebuggers());
         MOZ_ASSERT(!IsInsideNursery(&obj));
@@ -335,78 +318,16 @@ struct Zone : public JS::shadow::Zone,
 
     bool usedByExclusiveThread;
 
     // True when there are active frames.
     bool active;
 
     mozilla::DebugOnly<unsigned> gcLastZoneGroupIndex;
 
-    // Creates a HashNumber based on getUniqueId. Returns false on OOM.
-    bool getHashCode(js::gc::Cell* cell, js::HashNumber* hashp) {
-        uint64_t uid;
-        if (!getUniqueId(cell, &uid))
-            return false;
-        *hashp = js::HashNumber(uid >> 32) ^ js::HashNumber(uid & 0xFFFFFFFF);
-        return true;
-    }
-
-    // Puts an existing UID in |uidp|, or creates a new UID for this Cell and
-    // puts that into |uidp|. Returns false on OOM.
-    bool getUniqueId(js::gc::Cell* cell, uint64_t* uidp) {
-        MOZ_ASSERT(uidp);
-        js::AutoSpinLock lock(uniqueIdsLock_);
-
-        // Get an existing uid, if one has been set.
-        auto p = uniqueIds_.lookupForAdd(cell);
-        if (p) {
-            *uidp = p->value();
-            return true;
-        }
-
-        // Set a new uid on the cell.
-        *uidp = js::gc::NextCellUniqueId(runtimeFromAnyThread());
-        if (!uniqueIds_.add(p, cell, *uidp))
-            return false;
-
-        // If the cell was in the nursery, hopefully unlikely, then we need to
-        // tell the nursery about it so that it can sweep the uid if the thing
-        // does not get tenured.
-        if (!runtimeFromAnyThread()->gc.nursery.addedUniqueIdToCell(cell))
-            js::CrashAtUnhandlableOOM("failed to allocate tracking data for a nursery uid");
-        return true;
-    }
-
-    // Return true if this cell has a UID associated with it.
-    bool hasUniqueId(js::gc::Cell* cell) {
-        js::AutoSpinLock lock(uniqueIdsLock_);
-        return uniqueIds_.has(cell);
-    }
-
-    // Transfer an id from another cell. This must only be called on behalf of a
-    // moving GC. This method is infallible.
-    void transferUniqueId(js::gc::Cell* tgt, js::gc::Cell* src) {
-        MOZ_ASSERT(src != tgt);
-        MOZ_ASSERT(!IsInsideNursery(tgt));
-        MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtimeFromMainThread()));
-        js::AutoSpinLock lock(uniqueIdsLock_);
-        uniqueIds_.rekeyIfMoved(src, tgt);
-    }
-
-    // Remove any unique id associated with this Cell.
-    void removeUniqueId(js::gc::Cell* cell) {
-        js::AutoSpinLock lock(uniqueIdsLock_);
-        uniqueIds_.remove(cell);
-    }
-
-#ifdef JSGC_HASH_TABLE_CHECKS
-    // Assert that the UniqueId table has been redirected successfully.
-    void checkUniqueIdTableAfterMovingGC();
-#endif
-
   private:
     js::jit::JitZone* jitZone_;
 
     GCState gcState_;
     bool gcScheduled_;
     bool gcPreserveCode_;
     bool jitUsingBarriers_;
 
rename from js/src/jit-test/tests/arrays/dense-from-sparse.js
rename to js/src/jit-test/manual-tests/dense-to-sparse.js
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -295,30 +295,29 @@ MacroAssemblerARM::inc64(AbsoluteAddress
 }
 
 bool
 MacroAssemblerARM::alu_dbl(Register src1, Imm32 imm, Register dest, ALUOp op,
                            SBit s, Condition c)
 {
     if ((s == SetCC && ! condsAreSafe(op)) || !can_dbl(op))
         return false;
+
     ALUOp interop = getDestVariant(op);
     Imm8::TwoImm8mData both = Imm8::EncodeTwoImms(imm.value);
     if (both.fst.invalid)
         return false;
 
-    ScratchRegisterScope scratch(asMasm());
-
     // For the most part, there is no good reason to set the condition codes for
     // the first instruction. We can do better things if the second instruction
     // doesn't have a dest, such as check for overflow by doing first operation
     // don't do second operation if first operation overflowed. This preserves
     // the overflow condition code. Unfortunately, it is horribly brittle.
-    as_alu(scratch, src1, Operand2(both.fst), interop, LeaveCC, c);
-    as_alu(dest, scratch, Operand2(both.snd), op, s, c);
+    as_alu(dest, src1, Operand2(both.fst), interop, LeaveCC, c);
+    as_alu(dest, dest, Operand2(both.snd), op, s, c);
     return true;
 }
 
 
 void
 MacroAssemblerARM::ma_alu(Register src1, Imm32 imm, Register dest,
                           ALUOp op,
                           SBit s, Condition c)
@@ -2010,25 +2009,23 @@ MacroAssemblerARMCompat::load8ZeroExtend
 }
 
 void
 MacroAssemblerARMCompat::load8ZeroExtend(const BaseIndex& src, Register dest)
 {
     Register base = src.base;
     uint32_t scale = Imm32::ShiftOf(src.scale).value;
 
-    ScratchRegisterScope scratch(asMasm());
-
-    if (src.offset != 0) {
-        ma_mov(base, scratch);
-        base = scratch;
-        ma_add(base, Imm32(src.offset), base);
+    if (src.offset == 0) {
+        ma_ldrb(DTRAddr(base, DtrRegImmShift(src.index, LSL, scale)), dest);
+    } else {
+        ScratchRegisterScope scratch(asMasm());
+        ma_add(base, Imm32(src.offset), scratch);
+        ma_ldrb(DTRAddr(scratch, DtrRegImmShift(src.index, LSL, scale)), dest);
     }
-    ma_ldrb(DTRAddr(base, DtrRegImmShift(src.index, LSL, scale)), dest);
-
 }
 
 void
 MacroAssemblerARMCompat::load8SignExtend(const Address& address, Register dest)
 {
     ma_dataTransferN(IsLoad, 8, true, address.base, Imm32(address.offset), dest);
 }
 
--- a/js/src/jit/x86/MacroAssembler-x86.cpp
+++ b/js/src/jit/x86/MacroAssembler-x86.cpp
@@ -25,19 +25,32 @@ using namespace js::jit;
 // See convertUInt64ToDouble for the details.
 MOZ_ALIGNED_DECL(static const uint64_t, 16) TO_DOUBLE[4] = {
     0x4530000043300000LL,
     0x0LL,
     0x4330000000000000LL,
     0x4530000000000000LL
 };
 
+static const double TO_DOUBLE_HIGH_SCALE = 0x100000000;
+
 void
 MacroAssemblerX86::convertUInt64ToDouble(Register64 src, Register temp, FloatRegister dest)
 {
+    // SUBPD needs SSE2, HADDPD needs SSE3.
+    if (!HasSSE3()) {
+        convertUInt32ToDouble(src.high, dest);
+        movePtr(ImmPtr(&TO_DOUBLE_HIGH_SCALE), temp);
+        loadDouble(Address(temp, 0), ScratchDoubleReg);
+        mulDouble(ScratchDoubleReg, dest);
+        convertUInt32ToDouble(src.low, ScratchDoubleReg);
+        addDouble(ScratchDoubleReg, dest);
+        return;
+    }
+
     // Following operation uses entire 128-bit of dest XMM register.
     // Currently higher 64-bit is free when we have access to lower 64-bit.
     MOZ_ASSERT(dest.size() == 8);
     FloatRegister dest128 = FloatRegister(dest.encoding(), FloatRegisters::Int32x4);
 
     // Assume that src is represented as following:
     //   src      = 0x HHHHHHHH LLLLLLLL
 
--- a/js/src/jsapi-tests/moz.build
+++ b/js/src/jsapi-tests/moz.build
@@ -38,17 +38,16 @@ UNIFIED_SOURCES += [
     'testGCCellPtr.cpp',
     'testGCChunkPool.cpp',
     'testGCExactRooting.cpp',
     'testGCFinalizeCallback.cpp',
     'testGCHeapPostBarriers.cpp',
     'testGCMarking.cpp',
     'testGCOutOfMemory.cpp',
     'testGCStoreBufferRemoval.cpp',
-    'testGCUniqueId.cpp',
     'testGetPropertyDescriptor.cpp',
     'testHashTable.cpp',
     'testIndexToString.cpp',
     'testIntern.cpp',
     'testIntlAvailableLocales.cpp',
     'testIntString.cpp',
     'testIntTypesABI.cpp',
     'testIsInsideNursery.cpp',
deleted file mode 100644
--- a/js/src/jsapi-tests/testGCUniqueId.cpp
+++ /dev/null
@@ -1,120 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
-* vim: set ts=8 sts=4 et sw=4 tw=99:
-*/
-/* 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 "gc/GCInternals.h"
-#include "gc/Zone.h"
-
-static void
-MinimizeHeap(JSRuntime* rt)
-{
-    // The second collection is to force us to wait for the background
-    // sweeping that the first GC started to finish.
-    JS_GC(rt);
-    JS_GC(rt);
-    js::gc::AutoFinishGC finish(rt);
-}
-
-BEGIN_TEST(testGCUID)
-{
-#ifdef JS_GC_ZEAL
-    AutoLeaveZeal nozeal(cx);
-#endif /* JS_GC_ZEAL */
-
-    uint64_t uid = 0;
-    uint64_t tmp = 0;
-
-    // Ensure the heap is as minimal as it can get.
-    MinimizeHeap(rt);
-
-    JS::RootedObject obj(cx, JS_NewPlainObject(cx));
-    uintptr_t nurseryAddr = uintptr_t(obj.get());
-    CHECK(obj);
-    CHECK(js::gc::IsInsideNursery(obj));
-
-    // Do not start with an ID.
-    CHECK(!obj->zone()->hasUniqueId(obj));
-
-    // Ensure we can get a new UID.
-    CHECK(obj->zone()->getUniqueId(obj, &uid));
-    CHECK(uid > js::gc::LargestTaggedNullCellPointer);
-
-    // We should now have an id.
-    CHECK(obj->zone()->hasUniqueId(obj));
-
-    // Calling again should get us the same thing.
-    CHECK(obj->zone()->getUniqueId(obj, &tmp));
-    CHECK(uid == tmp);
-
-    // Tenure the thing and check that the UID moved with it.
-    MinimizeHeap(rt);
-    uintptr_t tenuredAddr = uintptr_t(obj.get());
-    CHECK(tenuredAddr != nurseryAddr);
-    CHECK(!js::gc::IsInsideNursery(obj));
-    CHECK(obj->zone()->hasUniqueId(obj));
-    CHECK(obj->zone()->getUniqueId(obj, &tmp));
-    CHECK(uid == tmp);
-
-    // Allocate a new nursery thing in the same location and check that we
-    // removed the prior uid that was attached to the location.
-    obj = JS_NewPlainObject(cx);
-    CHECK(obj);
-    CHECK(uintptr_t(obj.get()) == nurseryAddr);
-    CHECK(!obj->zone()->hasUniqueId(obj));
-
-    // Try to get another tenured object in the same location and check that
-    // the uid was removed correctly.
-    obj = nullptr;
-    MinimizeHeap(rt);
-    obj = JS_NewPlainObject(cx);
-    MinimizeHeap(rt);
-    CHECK(uintptr_t(obj.get()) == tenuredAddr);
-    CHECK(!obj->zone()->hasUniqueId(obj));
-    CHECK(obj->zone()->getUniqueId(obj, &tmp));
-    CHECK(uid != tmp);
-    uid = tmp;
-
-    // Allocate a few arenas worth of objects to ensure we get some compaction.
-    const static size_t N = 2049;
-    using ObjectVector = js::TraceableVector<JSObject*>;
-    JS::Rooted<ObjectVector> vec(cx, ObjectVector(cx));
-    for (size_t i = 0; i < N; ++i) {
-        obj = JS_NewPlainObject(cx);
-        CHECK(obj);
-        CHECK(vec.append(obj));
-    }
-
-    // Transfer our vector to tenured if it isn't there already.
-    MinimizeHeap(rt);
-
-    // Tear holes in the heap by unrooting the even objects and collecting.
-    JS::Rooted<ObjectVector> vec2(cx, ObjectVector(cx));
-    for (size_t i = 0; i < N; ++i) {
-        if (i % 2 == 1)
-            vec2.append(vec[i]);
-    }
-    vec.clear();
-    MinimizeHeap(rt);
-
-    // Grab the last object in the vector as our object of interest.
-    obj = vec2.back();
-    CHECK(obj);
-    tenuredAddr = uintptr_t(obj.get());
-    CHECK(obj->zone()->getUniqueId(obj, &uid));
-
-    // Force a compaction to move the object and check that the uid moved to
-    // the new tenured heap location.
-    JS::PrepareForFullGC(rt);
-    JS::GCForReason(rt, GC_SHRINK, JS::gcreason::API);
-    MinimizeHeap(rt);
-    CHECK(uintptr_t(obj.get()) != tenuredAddr);
-    CHECK(obj->zone()->hasUniqueId(obj));
-    CHECK(obj->zone()->getUniqueId(obj, &tmp));
-    CHECK(uid == tmp);
-
-    return true;
-}
-END_TEST(testGCUID)
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -79,17 +79,16 @@
 #include "vm/Runtime.h"
 #include "vm/SavedStacks.h"
 #include "vm/ScopeObject.h"
 #include "vm/Shape.h"
 #include "vm/StopIterationObject.h"
 #include "vm/StringBuffer.h"
 #include "vm/Symbol.h"
 #include "vm/TypedArrayCommon.h"
-#include "vm/WeakMapObject.h"
 #include "vm/WrapperObject.h"
 #include "vm/Xdr.h"
 
 #include "jsatominlines.h"
 #include "jsfuninlines.h"
 #include "jsscriptinlines.h"
 
 #include "vm/Interpreter-inl.h"
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -19,17 +19,16 @@
 #include "jsweakmap.h"
 #include "jswrapper.h"
 
 #include "builtin/TestingFunctions.h"
 #include "js/Proxy.h"
 #include "proxy/DeadObjectProxy.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/Time.h"
-#include "vm/WeakMapObject.h"
 #include "vm/WrapperObject.h"
 
 #include "jsobjinlines.h"
 #include "jsscriptinlines.h"
 
 #include "vm/NativeObject-inl.h"
 #include "vm/ScopeObject-inl.h"
 
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -828,17 +828,19 @@ Chunk::init(JSRuntime* rt)
     /*
      * Decommit the arenas. We do this after poisoning so that if the OS does
      * not have to recycle the pages, we still get the benefit of poisoning.
      */
     decommitAllArenas(rt);
 
     /* Initialize the chunk info. */
     info.init();
-    new (&info.trailer) ChunkTrailer(rt);
+    info.trailer.storeBuffer = nullptr;
+    info.trailer.location = ChunkLocationBitTenuredHeap;
+    info.trailer.runtime = rt;
 
     /* The rest of info fields are initialized in pickChunk. */
 }
 
 /*
  * Search for and return the next decommitted Arena. Our goal is to keep
  * lastDecommittedArenaOffset "close" to a free arena. We do this by setting
  * it to the most recently freed arena when we free, and forcing it to
@@ -1103,17 +1105,16 @@ GCRuntime::GCRuntime(JSRuntime* rt) :
     systemZone(nullptr),
     nursery(rt),
     storeBuffer(rt, nursery),
     stats(rt),
     marker(rt),
     usage(nullptr),
     mMemProfiler(rt),
     maxMallocBytes(0),
-    nextCellUniqueId_(LargestTaggedNullCellPointer + 1), // Ensure disjoint from null tagged pointers.
     numArenasFreeCommitted(0),
     verifyPreData(nullptr),
     chunkAllocationSinceLastGC(false),
     nextFullGCTime(0),
     lastGCTime(PRMJ_Now()),
     mode(JSGC_MODE_INCREMENTAL),
     numActiveZoneIters(0),
     decommitThreshold(32 * 1024 * 1024),
@@ -2036,19 +2037,16 @@ RelocateCell(Zone* zone, TenuredCell* sr
 
     // Allocate a new cell.
     MOZ_ASSERT(zone == src->zone());
     TenuredCell* dst = AllocRelocatedCell(zone, thingKind, thingSize);
 
     // Copy source cell contents to destination.
     memcpy(dst, src, thingSize);
 
-    // Move any uid attached to the object.
-    src->zone()->transferUniqueId(dst, src);
-
     if (IsObjectAllocKind(thingKind)) {
         JSObject* srcObj = static_cast<JSObject*>(static_cast<Cell*>(src));
         JSObject* dstObj = static_cast<JSObject*>(static_cast<Cell*>(dst));
 
         if (srcObj->isNative()) {
             NativeObject* srcNative = &srcObj->as<NativeObject>();
             NativeObject* dstNative = &dstObj->as<NativeObject>();
 
@@ -2170,16 +2168,17 @@ ShouldRelocateZone(size_t arenaCount, si
 
     return (relocCount * 100.0) / arenaCount >= MIN_ZONE_RECLAIM_PERCENT;
 }
 
 bool
 ArenaLists::relocateArenas(Zone* zone, ArenaHeader*& relocatedListOut, JS::gcreason::Reason reason,
                            SliceBudget& sliceBudget, gcstats::Statistics& stats)
 {
+
     // This is only called from the main thread while we are doing a GC, so
     // there is no need to lock.
     MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime_));
     MOZ_ASSERT(runtime_->gc.isHeapCompacting());
     MOZ_ASSERT(!runtime_->gc.isBackgroundSweeping());
 
     // Flush all the freeLists back into the arena headers
     purge();
@@ -3584,39 +3583,16 @@ GCRuntime::shouldReleaseObservedTypes()
         releaseTypes = true;
 
     if (releaseTypes)
         jitReleaseNumber = majorGCNumber + JIT_SCRIPT_RELEASE_TYPES_PERIOD;
 
     return releaseTypes;
 }
 
-struct IsAboutToBeFinalizedFunctor {
-    template <typename T> bool operator()(Cell** t) {
-        mozilla::DebugOnly<const Cell*> prior = *t;
-        bool result = IsAboutToBeFinalizedUnbarriered(reinterpret_cast<T**>(t));
-        // Sweep should not have to deal with moved pointers, since moving GC
-        // handles updating the UID table manually.
-        MOZ_ASSERT(*t == prior);
-        return result;
-    }
-};
-
-void
-JS::Zone::sweepUniqueIds(js::FreeOp* fop)
-{
-    for (UniqueIdMap::Enum e(uniqueIds_); !e.empty(); e.popFront()) {
-        if (DispatchTraceKindTyped(IsAboutToBeFinalizedFunctor(), e.front().key()->getTraceKind(),
-                                   &e.front().mutableKey()))
-        {
-            e.removeFront();
-        }
-    }
-}
-
 /*
  * It's simpler if we preserve the invariant that every zone has at least one
  * compartment. If we know we're deleting the entire zone, then
  * SweepCompartments is allowed to delete all compartments. In this case,
  * |keepAtleastOne| is false. If some objects remain in the zone so that it
  * cannot be deleted, then we set |keepAtleastOne| to true, which prohibits
  * SweepCompartments from deleting every compartment. Instead, it preserves an
  * arbitrary compartment in the zone.
@@ -5077,22 +5053,16 @@ GCRuntime::beginSweepingZoneGroup()
         }
 
         {
             gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_BREAKPOINT);
             for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
                 zone->sweepBreakpoints(&fop);
             }
         }
-
-        {
-            gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_BREAKPOINT);
-            for (GCZoneGroupIter zone(rt); !zone.done(); zone.next())
-                zone->sweepUniqueIds(&fop);
-        }
     }
 
     if (sweepingAtoms) {
         gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_SYMBOL_REGISTRY);
         rt->symbolRegistry().sweep();
     }
 
     // Rejoin our off-main-thread tasks.
@@ -7175,19 +7145,16 @@ JS::GCCellPtr::mayBeOwnedByOtherRuntime(
 #ifdef JSGC_HASH_TABLE_CHECKS
 void
 js::gc::CheckHashTablesAfterMovingGC(JSRuntime* rt)
 {
     /*
      * Check that internal hash tables no longer have any pointers to things
      * that have been moved.
      */
-    for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
-        zone->checkUniqueIdTableAfterMovingGC();
-    }
     for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
         c->objectGroups.checkTablesAfterMovingGC();
         c->checkInitialShapesTableAfterMovingGC();
         c->checkWrapperMapAfterMovingGC();
         c->checkBaseShapeTableAfterMovingGC();
         if (c->debugScopes)
             c->debugScopes->checkHashTablesAfterMovingGC(rt);
     }
@@ -7403,22 +7370,16 @@ JS::AutoDisableGenerationalGC::~AutoDisa
 }
 
 JS_PUBLIC_API(bool)
 JS::IsGenerationalGCEnabled(JSRuntime* rt)
 {
     return rt->gc.isGenerationalGCEnabled();
 }
 
-uint64_t
-js::gc::NextCellUniqueId(JSRuntime* rt)
-{
-    return rt->gc.nextCellUniqueId();
-}
-
 namespace js {
 namespace gc {
 namespace MemInfo {
 
 static bool
 GCBytesGetter(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -71,17 +71,16 @@ JSObject::finalize(js::FreeOp* fop)
 
 #ifdef DEBUG
     MOZ_ASSERT(isTenured());
     if (!IsBackgroundFinalized(asTenured().getAllocKind())) {
         /* Assert we're on the main thread. */
         MOZ_ASSERT(CurrentThreadCanAccessRuntime(fop->runtime()));
     }
 #endif
-
     const js::Class* clasp = getClass();
     if (clasp->finalize)
         clasp->finalize(fop, this);
 
     if (!clasp->isNative())
         return;
 
     js::NativeObject* nobj = &as<js::NativeObject>();
--- a/js/src/jsweakmap.cpp
+++ b/js/src/jsweakmap.cpp
@@ -11,28 +11,22 @@
 #include "jsapi.h"
 #include "jscntxt.h"
 #include "jsfriendapi.h"
 #include "jsobj.h"
 #include "jswrapper.h"
 
 #include "js/GCAPI.h"
 #include "vm/GlobalObject.h"
-#include "vm/WeakMapObject.h"
 
 #include "jsobjinlines.h"
 
-#include "vm/Interpreter-inl.h"
-#include "vm/NativeObject-inl.h"
-
 using namespace js;
 using namespace js::gc;
 
-using mozilla::UniquePtr;
-
 WeakMapBase::WeakMapBase(JSObject* memOf, Zone* zone)
   : memberOf(memOf),
     zone(zone),
     next(WeakMapNotInList),
     marked(false)
 {
     MOZ_ASSERT_IF(memberOf, memberOf->compartment()->zone() == zone);
 }
@@ -208,483 +202,16 @@ ObjectValueMap::findZoneEdges()
         if (delegateZone == zone)
             continue;
         if (!delegateZone->gcZoneGroupEdges.put(key->zone()))
             return false;
     }
     return true;
 }
 
-MOZ_ALWAYS_INLINE bool
-IsWeakMap(HandleValue v)
-{
-    return v.isObject() && v.toObject().is<WeakMapObject>();
-}
-
-MOZ_ALWAYS_INLINE bool
-WeakMap_has_impl(JSContext* cx, const CallArgs& args)
-{
-    MOZ_ASSERT(IsWeakMap(args.thisv()));
-
-    if (!args.get(0).isObject()) {
-        args.rval().setBoolean(false);
-        return true;
-    }
-
-    if (ObjectValueMap* map = args.thisv().toObject().as<WeakMapObject>().getMap()) {
-        JSObject* key = &args[0].toObject();
-        if (map->has(key)) {
-            args.rval().setBoolean(true);
-            return true;
-        }
-    }
-
-    args.rval().setBoolean(false);
-    return true;
-}
-
-bool
-js::WeakMap_has(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    return CallNonGenericMethod<IsWeakMap, WeakMap_has_impl>(cx, args);
-}
-
-MOZ_ALWAYS_INLINE bool
-WeakMap_clear_impl(JSContext* cx, const CallArgs& args)
-{
-    MOZ_ASSERT(IsWeakMap(args.thisv()));
-
-    // We can't js_delete the weakmap because the data gathered during GC is
-    // used by the Cycle Collector.
-    if (ObjectValueMap* map = args.thisv().toObject().as<WeakMapObject>().getMap())
-        map->clear();
-
-    args.rval().setUndefined();
-    return true;
-}
-
-bool
-js::WeakMap_clear(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    return CallNonGenericMethod<IsWeakMap, WeakMap_clear_impl>(cx, args);
-}
-
-MOZ_ALWAYS_INLINE bool
-WeakMap_get_impl(JSContext* cx, const CallArgs& args)
-{
-    MOZ_ASSERT(IsWeakMap(args.thisv()));
-
-    if (!args.get(0).isObject()) {
-        args.rval().setUndefined();
-        return true;
-    }
-
-    if (ObjectValueMap* map = args.thisv().toObject().as<WeakMapObject>().getMap()) {
-        JSObject* key = &args[0].toObject();
-        if (ObjectValueMap::Ptr ptr = map->lookup(key)) {
-            args.rval().set(ptr->value());
-            return true;
-        }
-    }
-
-    args.rval().setUndefined();
-    return true;
-}
-
-bool
-js::WeakMap_get(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    return CallNonGenericMethod<IsWeakMap, WeakMap_get_impl>(cx, args);
-}
-
-MOZ_ALWAYS_INLINE bool
-WeakMap_delete_impl(JSContext* cx, const CallArgs& args)
-{
-    MOZ_ASSERT(IsWeakMap(args.thisv()));
-
-    if (!args.get(0).isObject()) {
-        args.rval().setBoolean(false);
-        return true;
-    }
-
-    if (ObjectValueMap* map = args.thisv().toObject().as<WeakMapObject>().getMap()) {
-        JSObject* key = &args[0].toObject();
-        if (ObjectValueMap::Ptr ptr = map->lookup(key)) {
-            map->remove(ptr);
-            args.rval().setBoolean(true);
-            return true;
-        }
-    }
-
-    args.rval().setBoolean(false);
-    return true;
-}
-
-bool
-js::WeakMap_delete(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    return CallNonGenericMethod<IsWeakMap, WeakMap_delete_impl>(cx, args);
-}
-
-static bool
-TryPreserveReflector(JSContext* cx, HandleObject obj)
-{
-    if (obj->getClass()->ext.isWrappedNative ||
-        (obj->getClass()->flags & JSCLASS_IS_DOMJSCLASS) ||
-        (obj->is<ProxyObject>() &&
-         obj->as<ProxyObject>().handler()->family() == GetDOMProxyHandlerFamily()))
-    {
-        MOZ_ASSERT(cx->runtime()->preserveWrapperCallback);
-        if (!cx->runtime()->preserveWrapperCallback(cx, obj)) {
-            JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_WEAKMAP_KEY);
-            return false;
-        }
-    }
-    return true;
-}
-
-static inline void
-WeakMapPostWriteBarrier(JSRuntime* rt, ObjectValueMap* weakMap, JSObject* key)
-{
-    if (key && IsInsideNursery(key))
-        rt->gc.storeBuffer.putGeneric(gc::HashKeyRef<ObjectValueMap, JSObject*>(weakMap, key));
-}
-
-static MOZ_ALWAYS_INLINE bool
-SetWeakMapEntryInternal(JSContext* cx, Handle<WeakMapObject*> mapObj,
-                        HandleObject key, HandleValue value)
-{
-    ObjectValueMap* map = mapObj->getMap();
-    if (!map) {
-        AutoInitGCManagedObject<ObjectValueMap> newMap(
-            cx->make_unique<ObjectValueMap>(cx, mapObj.get()));
-        if (!newMap)
-            return false;
-        if (!newMap->init()) {
-            JS_ReportOutOfMemory(cx);
-            return false;
-        }
-        map = newMap.release();
-        mapObj->setPrivate(map);
-    }
-
-    // Preserve wrapped native keys to prevent wrapper optimization.
-    if (!TryPreserveReflector(cx, key))
-        return false;
-
-    if (JSWeakmapKeyDelegateOp op = key->getClass()->ext.weakmapKeyDelegateOp) {
-        RootedObject delegate(cx, op(key));
-        if (delegate && !TryPreserveReflector(cx, delegate))
-            return false;
-    }
-
-    MOZ_ASSERT(key->compartment() == mapObj->compartment());
-    MOZ_ASSERT_IF(value.isObject(), value.toObject().compartment() == mapObj->compartment());
-    if (!map->put(key, value)) {
-        JS_ReportOutOfMemory(cx);
-        return false;
-    }
-    WeakMapPostWriteBarrier(cx->runtime(), map, key.get());
-    return true;
-}
-
-MOZ_ALWAYS_INLINE bool
-WeakMap_set_impl(JSContext* cx, const CallArgs& args)
-{
-    MOZ_ASSERT(IsWeakMap(args.thisv()));
-
-    if (!args.get(0).isObject()) {
-        UniquePtr<char[], JS::FreePolicy> bytes =
-            DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, args.get(0), nullptr);
-        if (!bytes)
-            return false;
-        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT, bytes.get());
-        return false;
-    }
-
-    RootedObject key(cx, &args[0].toObject());
-    Rooted<JSObject*> thisObj(cx, &args.thisv().toObject());
-    Rooted<WeakMapObject*> map(cx, &thisObj->as<WeakMapObject>());
-
-    if (!SetWeakMapEntryInternal(cx, map, key, args.get(1)))
-        return false;
-    args.rval().set(args.thisv());
-    return true;
-}
-
-bool
-js::WeakMap_set(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    return CallNonGenericMethod<IsWeakMap, WeakMap_set_impl>(cx, args);
-}
-
-JS_FRIEND_API(bool)
-JS_NondeterministicGetWeakMapKeys(JSContext* cx, HandleObject objArg, MutableHandleObject ret)
-{
-    RootedObject obj(cx, objArg);
-    obj = UncheckedUnwrap(obj);
-    if (!obj || !obj->is<WeakMapObject>()) {
-        ret.set(nullptr);
-        return true;
-    }
-    RootedObject arr(cx, NewDenseEmptyArray(cx));
-    if (!arr)
-        return false;
-    ObjectValueMap* map = obj->as<WeakMapObject>().getMap();
-    if (map) {
-        // Prevent GC from mutating the weakmap while iterating.
-        AutoSuppressGC suppress(cx);
-        for (ObjectValueMap::Base::Range r = map->all(); !r.empty(); r.popFront()) {
-            JS::ExposeObjectToActiveJS(r.front().key());
-            RootedObject key(cx, r.front().key());
-            if (!cx->compartment()->wrap(cx, &key))
-                return false;
-            if (!NewbornArrayPush(cx, arr, ObjectValue(*key)))
-                return false;
-        }
-    }
-    ret.set(arr);
-    return true;
-}
-
-static void
-WeakMap_mark(JSTracer* trc, JSObject* obj)
-{
-    if (ObjectValueMap* map = obj->as<WeakMapObject>().getMap())
-        map->trace(trc);
-}
-
-static void
-WeakMap_finalize(FreeOp* fop, JSObject* obj)
-{
-    if (ObjectValueMap* map = obj->as<WeakMapObject>().getMap()) {
-#ifdef DEBUG
-        map->~ObjectValueMap();
-        memset(static_cast<void*>(map), 0xdc, sizeof(*map));
-        fop->free_(map);
-#else
-        fop->delete_(map);
-#endif
-    }
-}
-
-JS_PUBLIC_API(JSObject*)
-JS::NewWeakMapObject(JSContext* cx)
-{
-    return NewBuiltinClassInstance(cx, &WeakMapObject::class_);
-}
-
-JS_PUBLIC_API(bool)
-JS::IsWeakMapObject(JSObject* obj)
-{
-    return obj->is<WeakMapObject>();
-}
-
-JS_PUBLIC_API(bool)
-JS::GetWeakMapEntry(JSContext* cx, HandleObject mapObj, HandleObject key,
-                    MutableHandleValue rval)
-{
-    CHECK_REQUEST(cx);
-    assertSameCompartment(cx, key);
-    rval.setUndefined();
-    ObjectValueMap* map = mapObj->as<WeakMapObject>().getMap();
-    if (!map)
-        return true;
-    if (ObjectValueMap::Ptr ptr = map->lookup(key)) {
-        // Read barrier to prevent an incorrectly gray value from escaping the
-        // weak map. See the comment before UnmarkGrayChildren in gc/Marking.cpp
-        ExposeValueToActiveJS(ptr->value().get());
-        rval.set(ptr->value());
-    }
-    return true;
-}
-
-JS_PUBLIC_API(bool)
-JS::SetWeakMapEntry(JSContext* cx, HandleObject mapObj, HandleObject key,
-                    HandleValue val)
-{
-    CHECK_REQUEST(cx);
-    assertSameCompartment(cx, key, val);
-    Rooted<WeakMapObject*> rootedMap(cx, &mapObj->as<WeakMapObject>());
-    return SetWeakMapEntryInternal(cx, rootedMap, key, val);
-}
-
-static bool
-WeakMap_construct(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-
-    // ES6 draft rev 31 (15 Jan 2015) 23.3.1.1 step 1.
-    if (!ThrowIfNotConstructing(cx, args, "WeakMap"))
-        return false;
-
-    RootedObject obj(cx, NewBuiltinClassInstance(cx, &WeakMapObject::class_));
-    if (!obj)
-        return false;
-
-    // Steps 5-6, 11.
-    if (!args.get(0).isNullOrUndefined()) {
-        // Steps 7a-b.
-        RootedValue adderVal(cx);
-        if (!GetProperty(cx, obj, obj, cx->names().set, &adderVal))
-            return false;
-
-        // Step 7c.
-        if (!IsCallable(adderVal))
-            return ReportIsNotFunction(cx, adderVal);
-
-        bool isOriginalAdder = IsNativeFunction(adderVal, WeakMap_set);
-        RootedValue mapVal(cx, ObjectValue(*obj));
-        FastInvokeGuard fig(cx, adderVal);
-        InvokeArgs& args2 = fig.args();
-
-        // Steps 7d-e.
-        JS::ForOfIterator iter(cx);
-        if (!iter.init(args[0]))
-            return false;
-
-        RootedValue pairVal(cx);
-        RootedObject pairObject(cx);
-        RootedValue keyVal(cx);
-        RootedObject keyObject(cx);
-        RootedValue val(cx);
-        while (true) {
-            // Steps 12a-e.
-            bool done;
-            if (!iter.next(&pairVal, &done))
-                return false;
-            if (done)
-                break;
-
-            // Step 12f.
-            if (!pairVal.isObject()) {
-                JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
-                                     JSMSG_INVALID_MAP_ITERABLE, "WeakMap");
-                return false;
-            }
-
-            pairObject = &pairVal.toObject();
-            if (!pairObject)
-                return false;
-
-            // Steps 12g-h.
-            if (!GetElement(cx, pairObject, pairObject, 0, &keyVal))
-                return false;
-
-            // Steps 12i-j.
-            if (!GetElement(cx, pairObject, pairObject, 1, &val))
-                return false;
-
-            // Steps 12k-l.
-            if (isOriginalAdder) {
-                if (keyVal.isPrimitive()) {
-                    UniquePtr<char[], JS::FreePolicy> bytes =
-                        DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, keyVal, nullptr);
-                    if (!bytes)
-                        return false;
-                    JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT, bytes.get());
-                    return false;
-                }
-
-                keyObject = &keyVal.toObject();
-                if (!SetWeakMapEntry(cx, obj, keyObject, val))
-                    return false;
-            } else {
-                if (!args2.init(2))
-                    return false;
-
-                args2.setCallee(adderVal);
-                args2.setThis(mapVal);
-                args2[0].set(keyVal);
-                args2[1].set(val);
-
-                if (!fig.invoke(cx))
-                    return false;
-            }
-        }
-    }
-
-    args.rval().setObject(*obj);
-    return true;
-}
-
-const Class WeakMapObject::class_ = {
-    "WeakMap",
-    JSCLASS_HAS_PRIVATE |
-    JSCLASS_HAS_CACHED_PROTO(JSProto_WeakMap),
-    nullptr, /* addProperty */
-    nullptr, /* delProperty */
-    nullptr, /* getProperty */
-    nullptr, /* setProperty */
-    nullptr, /* enumerate */
-    nullptr, /* resolve */
-    nullptr, /* mayResolve */
-    nullptr, /* convert */
-    WeakMap_finalize,
-    nullptr, /* call */
-    nullptr, /* hasInstance */
-    nullptr, /* construct */
-    WeakMap_mark
-};
-
-static const JSFunctionSpec weak_map_methods[] = {
-    JS_FN("has",    WeakMap_has, 1, 0),
-    JS_FN("get",    WeakMap_get, 1, 0),
-    JS_FN("delete", WeakMap_delete, 1, 0),
-    JS_FN("set",    WeakMap_set, 2, 0),
-    JS_FN("clear",  WeakMap_clear, 0, 0),
-    JS_FS_END
-};
-
-static JSObject*
-InitWeakMapClass(JSContext* cx, HandleObject obj, bool defineMembers)
-{
-    MOZ_ASSERT(obj->isNative());
-
-    Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
-
-    RootedPlainObject proto(cx, NewBuiltinClassInstance<PlainObject>(cx));
-    if (!proto)
-        return nullptr;
-
-    RootedFunction ctor(cx, global->createConstructor(cx, WeakMap_construct,
-                                                      cx->names().WeakMap, 0));
-    if (!ctor)
-        return nullptr;
-
-    if (!LinkConstructorAndPrototype(cx, ctor, proto))
-        return nullptr;
-
-    if (defineMembers) {
-        if (!DefinePropertiesAndFunctions(cx, proto, nullptr, weak_map_methods))
-            return nullptr;
-    }
-
-    if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_WeakMap, ctor, proto))
-        return nullptr;
-    return proto;
-}
-
-JSObject*
-js::InitWeakMapClass(JSContext* cx, HandleObject obj)
-{
-    return InitWeakMapClass(cx, obj, true);
-}
-
-JSObject*
-js::InitBareWeakMapCtor(JSContext* cx, HandleObject obj)
-{
-    return InitWeakMapClass(cx, obj, false);
-}
-
 ObjectWeakMap::ObjectWeakMap(JSContext* cx)
   : map(cx, nullptr)
 {}
 
 bool
 ObjectWeakMap::init()
 {
     return map.init();
--- a/js/src/jsweakmap.h
+++ b/js/src/jsweakmap.h
@@ -392,11 +392,49 @@ extern bool
 WeakMap_delete(JSContext* cx, unsigned argc, Value* vp);
 
 extern bool
 WeakMap_clear(JSContext* cx, unsigned argc, Value* vp);
 
 extern JSObject*
 InitWeakMapClass(JSContext* cx, HandleObject obj);
 
+
+class ObjectValueMap : public WeakMap<PreBarrieredObject, RelocatableValue>
+{
+  public:
+    ObjectValueMap(JSContext* cx, JSObject* obj)
+      : WeakMap<PreBarrieredObject, RelocatableValue>(cx, obj) {}
+
+    virtual bool findZoneEdges();
+};
+
+
+// Generic weak map for mapping objects to other objects.
+class ObjectWeakMap
+{
+  private:
+    ObjectValueMap map;
+    typedef gc::HashKeyRef<ObjectValueMap, JSObject*> StoreBufferRef;
+
+  public:
+    explicit ObjectWeakMap(JSContext* cx);
+    bool init();
+    ~ObjectWeakMap();
+
+    JSObject* lookup(const JSObject* obj);
+    bool add(JSContext* cx, JSObject* obj, JSObject* target);
+    void clear();
+
+    void trace(JSTracer* trc);
+    size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
+    size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) {
+        return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf);
+    }
+
+#ifdef JSGC_HASH_TABLE_CHECKS
+    void checkAfterMovingGC();
+#endif
+};
+
 } /* namespace js */
 
 #endif /* jsweakmap_h */
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -153,16 +153,17 @@ UNIFIED_SOURCES += [
     'builtin/Object.cpp',
     'builtin/Profilers.cpp',
     'builtin/Reflect.cpp',
     'builtin/ReflectParse.cpp',
     'builtin/SIMD.cpp',
     'builtin/SymbolObject.cpp',
     'builtin/TestingFunctions.cpp',
     'builtin/TypedObject.cpp',
+    'builtin/WeakMapObject.cpp',
     'builtin/WeakSetObject.cpp',
     'devtools/sharkctl.cpp',
     'ds/LifoAlloc.cpp',
     'frontend/BytecodeCompiler.cpp',
     'frontend/BytecodeEmitter.cpp',
     'frontend/FoldConstants.cpp',
     'frontend/NameFunctions.cpp',
     'frontend/ParseMaps.cpp',
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -22,23 +22,23 @@
 #endif
 #include "builtin/MapObject.h"
 #include "builtin/ModuleObject.h"
 #include "builtin/Object.h"
 #include "builtin/RegExp.h"
 #include "builtin/SIMD.h"
 #include "builtin/SymbolObject.h"
 #include "builtin/TypedObject.h"
+#include "builtin/WeakMapObject.h"
 #include "builtin/WeakSetObject.h"
 #include "vm/HelperThreads.h"
 #include "vm/PIC.h"
 #include "vm/RegExpStatics.h"
 #include "vm/RegExpStaticsObject.h"
 #include "vm/StopIterationObject.h"
-#include "vm/WeakMapObject.h"
 
 #include "jscompartmentinlines.h"
 #include "jsobjinlines.h"
 #include "jsscriptinlines.h"
 
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -13,17 +13,16 @@
 #include "jsiter.h"
 
 #include "builtin/ModuleObject.h"
 
 #include "vm/ArgumentsObject.h"
 #include "vm/GlobalObject.h"
 #include "vm/ProxyObject.h"
 #include "vm/Shape.h"
-#include "vm/WeakMapObject.h"
 #include "vm/Xdr.h"
 
 #include "jsatominlines.h"
 #include "jsobjinlines.h"
 #include "jsscriptinlines.h"
 
 #include "vm/Stack-inl.h"
 
--- a/js/src/vm/ScopeObject.h
+++ b/js/src/vm/ScopeObject.h
@@ -9,17 +9,16 @@
 
 #include "jscntxt.h"
 #include "jsobj.h"
 #include "jsweakmap.h"
 
 #include "gc/Barrier.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/ProxyObject.h"
-#include "vm/WeakMapObject.h"
 
 namespace js {
 
 namespace frontend {
 struct Definition;
 class FunctionBox;
 class ModuleBox;
 }
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -1037,17 +1037,17 @@ public:
     mAppUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
     mContainerReferenceFrame =
       const_cast<nsIFrame*>(aContainerItem ? aContainerItem->ReferenceFrameForChildren() :
                                              mBuilder->FindReferenceFrameFor(mContainerFrame));
     bool isAtRoot = !aContainerItem || (aContainerItem->Frame() == mBuilder->RootReferenceFrame());
     MOZ_ASSERT_IF(isAtRoot, mContainerReferenceFrame == mBuilder->RootReferenceFrame());
     mContainerAnimatedGeometryRoot = isAtRoot
       ? mContainerReferenceFrame
-      : aContainerItem->AnimatedGeometryRoot();
+      : nsLayoutUtils::GetAnimatedGeometryRootFor(aContainerItem, aBuilder);
     MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDoc(mBuilder->RootReferenceFrame(),
                                                       mContainerAnimatedGeometryRoot));
     NS_ASSERTION(!aContainerItem || !aContainerItem->ShouldFixToViewport(mBuilder),
                  "Container items never return true for ShouldFixToViewport");
     mContainerFixedPosFrame =
         FindFixedPosFrameForLayerData(mContainerAnimatedGeometryRoot, false);
     // When AllowResidualTranslation is false, display items will be drawn
     // scaled with a translation by integer pixels, so we know how the snapping
@@ -2095,17 +2095,18 @@ ContainerState::GetLayerCreationHint(con
   // Check whether the layer will be scrollable. This is used as a hint to
   // influence whether tiled layers are used or not.
 
   // Check whether there's any active scroll frame on the animated geometry
   // root chain.
   nsIFrame* fParent;
   for (const nsIFrame* f = aAnimatedGeometryRoot;
        f != mContainerAnimatedGeometryRoot;
-       f = nsLayoutUtils::GetAnimatedGeometryRootForFrame(mBuilder, fParent)) {
+       f = nsLayoutUtils::GetAnimatedGeometryRootForFrame(mBuilder,
+           fParent, mContainerAnimatedGeometryRoot)) {
     fParent = nsLayoutUtils::GetCrossDocParentFrame(f);
     if (!fParent) {
       break;
     }
     nsIScrollableFrame* scrollable = do_QueryFrame(fParent);
     if (scrollable
   #ifdef MOZ_B2G
         && scrollable->WantAsyncScroll()
@@ -2776,28 +2777,28 @@ PaintedLayerDataTree::GetParentAnimatedG
   MOZ_ASSERT(aAnimatedGeometryRoot);
   MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDoc(Builder()->RootReferenceFrame(), aAnimatedGeometryRoot));
 
   if (aAnimatedGeometryRoot == Builder()->RootReferenceFrame()) {
     return nullptr;
   }
 
   nsIFrame* agr = Builder()->FindAnimatedGeometryRootFor(
-    const_cast<nsIFrame*>(aAnimatedGeometryRoot));
+    const_cast<nsIFrame*>(aAnimatedGeometryRoot), Builder()->RootReferenceFrame());
   MOZ_ASSERT_IF(agr, nsLayoutUtils::IsAncestorFrameCrossDoc(Builder()->RootReferenceFrame(), agr));
   if (agr != aAnimatedGeometryRoot) {
     return agr;
   }
   // aAnimatedGeometryRoot is its own animated geometry root.
   // Find the animated geometry root for its cross-doc parent frame.
   nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrame(aAnimatedGeometryRoot);
   if (!parent) {
     return nullptr;
   }
-  return Builder()->FindAnimatedGeometryRootFor(parent);
+  return Builder()->FindAnimatedGeometryRootFor(parent, Builder()->RootReferenceFrame());
 }
 
 void
 PaintedLayerDataTree::Finish()
 {
   if (mRoot) {
     mRoot->Finish(false);
   }
@@ -3689,17 +3690,18 @@ ContainerState::ChooseAnimatedGeometryRo
     // Don't use an item that won't be part of any PaintedLayers to pick the
     // active scrolled root.
     if (layerState == LAYER_ACTIVE_FORCE) {
       continue;
     }
 
     // Try using the actual active scrolled root of the backmost item, as that
     // should result in the least invalidation when scrolling.
-    *aAnimatedGeometryRoot = item->AnimatedGeometryRoot();
+    *aAnimatedGeometryRoot =
+      nsLayoutUtils::GetAnimatedGeometryRootFor(item, mBuilder);
     return true;
   }
   return false;
 }
 
 nsIntRegion
 ContainerState::ComputeOpaqueRect(nsDisplayItem* aItem,
                                   const nsIFrame* aAnimatedGeometryRoot,
@@ -3764,17 +3766,17 @@ static DisplayItemClip
 GetScrollClipIntersection(nsDisplayListBuilder* aBuilder, const nsIFrame* aAnimatedGeometryRoot,
                           const nsIFrame* aStopAtAnimatedGeometryRoot, bool aIsCaret)
 {
   DisplayItemClip resultClip;
   nsIFrame* fParent;
   for (const nsIFrame* f = aAnimatedGeometryRoot;
        f != aStopAtAnimatedGeometryRoot;
        f = nsLayoutUtils::GetAnimatedGeometryRootForFrame(aBuilder,
-           fParent)) {
+           fParent, aStopAtAnimatedGeometryRoot)) {
     fParent = nsLayoutUtils::GetCrossDocParentFrame(f);
     if (!fParent) {
       // This means aStopAtAnimatedGeometryRoot was not an ancestor
       // of aAnimatedGeometryRoot. This is a weird case but it
       // can happen, e.g. when a scrolled frame contains a frame with opacity
       // which contains a frame that is not scrolled by the scrolled frame.
       // For now, we just don't apply any specific scroll clip to this layer.
       return