Merge mozilla-central to autoland. a=merge CLOSED TREE
authorCiure Andrei <aciure@mozilla.com>
Tue, 24 Apr 2018 04:26:26 +0300
changeset 468714 1e3173e0ad058647182f3aa2d94b03b30eb0fe4f
parent 468713 cf1022433399232d089891e4979fced0289b0e7c (current diff)
parent 468705 28db2c96ac695ce77c532901f5431a18a1d44374 (diff)
child 468715 60cf97933726a504197477aab9f8092cf9fd97b5
push id9165
push userasasaki@mozilla.com
push dateThu, 26 Apr 2018 21:04:54 +0000
treeherdermozilla-beta@064c3804de2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to autoland. a=merge CLOSED TREE
build/pgo/certs/jartests-object.ca
build/pgo/certs/pgoca.p12
build/pgo/certs/secmod.db
devtools/client/themes/images/font-swatch.svg
testing/mozharness/configs/disable_signing.py
testing/web-platform/meta/css/css-fonts/variations/font-parse-numeric-stretch-style-weight.html.ini
testing/web-platform/meta/css/css-fonts/variations/font-shorthand.html.ini
testing/web-platform/meta/css/css-fonts/variations/font-style-interpolation.html.ini
testing/web-platform/meta/css/css-fonts/variations/font-style-parsing.html.ini
testing/web-platform/meta/css/cssom/MediaList.html.ini
testing/web-platform/meta/css/cssom/cssimportrule.html.ini
--- a/.taskcluster.yml
+++ b/.taskcluster.yml
@@ -47,29 +47,35 @@ tasks:
         then: {createdForUser: "${ownerEmail}"}
         else:
           $if: 'tasks_for == "action"'
           then:
             createdForUser: '${ownerEmail}'
             kind: 'action-callback'
 
       routes:
-        $if: 'tasks_for == "hg-push"'
-        then:
-          - "index.gecko.v2.${repository.project}.latest.firefox.decision"
-          - "index.gecko.v2.${repository.project}.revision.${push.revision}.firefox.decision"
-          - "index.gecko.v2.${repository.project}.pushlog-id.${push.pushlog_id}.decision"
+        $flatten:
           - "tc-treeherder.v2.${repository.project}.${push.revision}.${push.pushlog_id}"
-          - "notify.email.${ownerEmail}.on-failed"
-          - "notify.email.${ownerEmail}.on-exception"
-        else:
-          - "tc-treeherder.v2.${repository.project}.${push.revision}.${push.pushlog_id}"
-          - $if: 'tasks_for == "action"'
-            then: "index.gecko.v2.${repository.project}.pushlog-id.${push.pushlog_id}.actions.${ownTaskId}"
-            else: "index.gecko.v2.${repository.project}.latest.firefox.decision-${cron.job_name}"
+          - $if: 'tasks_for == "hg-push"'
+            then:
+              - "index.gecko.v2.${repository.project}.latest.taskgraph.decision"
+              - "index.gecko.v2.${repository.project}.revision.${push.revision}.taskgraph.decision"
+              - "index.gecko.v2.${repository.project}.pushlog-id.${push.pushlog_id}.decision"
+              - "notify.email.${ownerEmail}.on-failed"
+              - "notify.email.${ownerEmail}.on-exception"
+              # These are the old index routes for the decision task.
+              # They are still here so external tools that referenced them continue to work.
+              - "index.gecko.v2.${repository.project}.latest.firefox.decision"
+              - "index.gecko.v2.${repository.project}.revision.${push.revision}.firefox.decision"
+            else:
+              $if: 'tasks_for == "action"'
+              then: "index.gecko.v2.${repository.project}.pushlog-id.${push.pushlog_id}.actions.${ownTaskId}"
+              else:
+              - "index.gecko.v2.${repository.project}.latest.taskgraph.decision-${cron.job_name}"
+              - "index.gecko.v2.${repository.project}.latest.firefox.decision-${cron.job_name}"
 
       scopes:
         $if: 'tasks_for == "hg-push"'
         then:
           - 'assume:repo:${repoUrl[8:]}:branch:default'
           - 'queue:route:notify.email.${ownerEmail}.*'
         else:
           $if: 'tasks_for == "action"'
--- a/accessible/base/StyleInfo.cpp
+++ b/accessible/base/StyleInfo.cpp
@@ -98,23 +98,15 @@ StyleInfo::FormatColor(const nscolor& aV
   aFormattedValue.AppendLiteral(", ");
   aFormattedValue.AppendInt(NS_GET_G(aValue));
   aFormattedValue.AppendLiteral(", ");
   aFormattedValue.AppendInt(NS_GET_B(aValue));
   aFormattedValue.Append(')');
 }
 
 void
-StyleInfo::FormatFontStyle(const nscoord& aValue, nsAString& aFormattedValue)
-{
-  nsCSSKeyword keyword =
-    nsCSSProps::ValueToKeywordEnum(aValue, nsCSSProps::kFontStyleKTable);
-  AppendUTF8toUTF16(nsCSSKeywords::GetStringValue(keyword), aFormattedValue);
-}
-
-void
 StyleInfo::FormatTextDecorationStyle(uint8_t aValue, nsAString& aFormattedValue)
 {
   nsCSSKeyword keyword =
     nsCSSProps::ValueToKeywordEnum(aValue,
                                    nsCSSProps::kTextDecorationStyleKTable);
   AppendUTF8toUTF16(nsCSSKeywords::GetStringValue(keyword), aFormattedValue);
 }
--- a/accessible/base/StyleInfo.h
+++ b/accessible/base/StyleInfo.h
@@ -26,17 +26,16 @@ public:
   void TextAlign(nsAString& aValue);
   void TextIndent(nsAString& aValue);
   void MarginLeft(nsAString& aValue) { Margin(eSideLeft, aValue); }
   void MarginRight(nsAString& aValue) { Margin(eSideRight, aValue); }
   void MarginTop(nsAString& aValue) { Margin(eSideTop, aValue); }
   void MarginBottom(nsAString& aValue) { Margin(eSideBottom, aValue); }
 
   static void FormatColor(const nscolor& aValue, nsString& aFormattedValue);
-  static void FormatFontStyle(const nscoord& aValue, nsAString& aFormattedValue);
   static void FormatTextDecorationStyle(uint8_t aValue, nsAString& aFormattedValue);
 
 private:
   StyleInfo() = delete;
   StyleInfo(const StyleInfo&) = delete;
   StyleInfo& operator = (const StyleInfo&) = delete;
 
   void Margin(Side aSide, nsAString& aValue);
--- a/accessible/base/TextAttrs.cpp
+++ b/accessible/base/TextAttrs.cpp
@@ -9,16 +9,17 @@
 #include "nsAccUtils.h"
 #include "nsCoreUtils.h"
 #include "StyleInfo.h"
 
 #include "gfxFont.h"
 #include "nsFontMetrics.h"
 #include "nsLayoutUtils.h"
 #include "nsContainerFrame.h"
+#include "nsStyleUtil.h"
 #include "HyperTextAccessible.h"
 #include "mozilla/AppUnits.h"
 #include "mozilla/gfx/2D.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -544,50 +545,50 @@ TextAttrsMgr::FontSizeTextAttr::
 
 
 ////////////////////////////////////////////////////////////////////////////////
 // FontStyleTextAttr
 ////////////////////////////////////////////////////////////////////////////////
 
 TextAttrsMgr::FontStyleTextAttr::
   FontStyleTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame) :
-  TTextAttr<nscoord>(!aFrame)
+  TTextAttr<FontSlantStyle>(!aFrame)
 {
   mRootNativeValue = aRootFrame->StyleFont()->mFont.style;
   mIsRootDefined = true;
 
   if (aFrame) {
     mNativeValue = aFrame->StyleFont()->mFont.style;
     mIsDefined = true;
   }
 }
 
 bool
 TextAttrsMgr::FontStyleTextAttr::
-  GetValueFor(Accessible* aAccessible, nscoord* aValue)
+  GetValueFor(Accessible* aAccessible, FontSlantStyle* aValue)
 {
   nsIContent* elm = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
   if (elm) {
     nsIFrame* frame = elm->GetPrimaryFrame();
     if (frame) {
       *aValue = frame->StyleFont()->mFont.style;
       return true;
     }
   }
   return false;
 }
 
 void
 TextAttrsMgr::FontStyleTextAttr::
-  ExposeValue(nsIPersistentProperties* aAttributes, const nscoord& aValue)
+  ExposeValue(nsIPersistentProperties* aAttributes,
+              const FontSlantStyle& aValue)
 {
-  nsAutoString formattedValue;
-  StyleInfo::FormatFontStyle(aValue, formattedValue);
-
-  nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::font_style, formattedValue);
+  nsAutoString string;
+  nsStyleUtil::AppendFontSlantStyle(aValue, string);
+  nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::font_style, string);
 }
 
 
 ////////////////////////////////////////////////////////////////////////////////
 // FontWeightTextAttr
 ////////////////////////////////////////////////////////////////////////////////
 
 TextAttrsMgr::FontWeightTextAttr::
--- a/accessible/base/TextAttrs.h
+++ b/accessible/base/TextAttrs.h
@@ -331,29 +331,29 @@ protected:
   private:
     nsDeviceContext* mDC;
   };
 
 
   /**
    * Class is used for the work with "font-style" text attribute.
    */
-  class FontStyleTextAttr : public TTextAttr<nscoord>
+  class FontStyleTextAttr : public TTextAttr<mozilla::FontSlantStyle>
   {
   public:
     FontStyleTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame);
     virtual ~FontStyleTextAttr() { }
 
   protected:
 
     // TTextAttr
-    virtual bool GetValueFor(Accessible* aContent, nscoord* aValue)
+    virtual bool GetValueFor(Accessible* aContent, mozilla::FontSlantStyle* aValue)
       override;
     virtual void ExposeValue(nsIPersistentProperties* aAttributes,
-                             const nscoord& aValue) override;
+                             const mozilla::FontSlantStyle& aValue) override;
   };
 
 
   /**
    * Class is used for the work with "font-weight" text attribute.
    */
   class FontWeightTextAttr : public TTextAttr<mozilla::FontWeight>
   {
--- a/browser/app/blocklist.xml
+++ b/browser/app/blocklist.xml
@@ -1,10 +1,10 @@
 <?xml version='1.0' encoding='UTF-8'?>
-<blocklist lastupdate="1523897202689" xmlns="http://www.mozilla.org/2006/addons-blocklist">
+<blocklist lastupdate="1524147337556" xmlns="http://www.mozilla.org/2006/addons-blocklist">
   <emItems>
     <emItem blockID="i334" id="{0F827075-B026-42F3-885D-98981EE7B1AE}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="i1211" id="flvto@hotger.com">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
@@ -2238,16 +2238,24 @@
     <emItem blockID="36f97298-8bef-4372-a548-eb829413bee9" id="/(__TEMPLATE__APPLICATION__@ruta-mapa\.com)|(application-3@findizer\.fr)|(application2@allo-pages\.fr)|(application2@bilan-imc\.fr)|(application2@lettres\.net)|(application2@search-maps-finder\.com)|(application-imcpeso@imc-peso\.com)|(application-meuimc@meu-imc\.com)|(application-us2@factorlove)|(application-us@misterdirections)|(application-us@yummmi\.es)|(application@amiouze\.fr)|(application@astrolignes\.com)|(application@blotyn\.com)|(application@bmi-result\.com)|(application@bmi-tw\.com)|(application@calcolo-bmi\.com)|(application@cartes-itineraires\.com)|(application@convertisseur\.pro)|(application@de-findizer\.fr)|(application@de-super-rezepte\.com)|(application@dermabeauty\.fr)|(application@dev\.squel\.v2)|(application@eu-my-drivingdirections\.com)|(application@fr-allo-pages\.fr)|(application@fr-catizz\.com)|(application@fr-mr-traduction\.com)|(application@good-recettes\.com)|(application@horaires\.voyage)|(application@imc-calcular\.com)|(application@imc-peso\.com)|(application@it-mio-percorso\.com)|(application@iti-maps\.fr)|(application@itineraire\.info)|(application@lbc-search\.com)|(application@les-pages\.com)|(application@lovincalculator\.com)|(application@lovintest\.com)|(application@masowe\.com)|(application@matchs\.direct)|(application@mein-bmi\.com)|(application@mes-resultats\.com)|(application@mestaf\.com)|(application@meu-imc\.com)|(application@mon-calcul-imc\.fr)|(application@mon-juste-poids\.com)|(application@mon-trajet\.com)|(application@my-drivingdirections\.com)|(application@people-show\.com)|(application@plans-reduc\.fr)|(application@point-meteo\.fr)|(application@poulixo\.com)|(application@quipage\.fr)|(application@quizdeamor\.com)|(application@quizdoamor\.com)|(application@quotient-retraite\.fr)|(application@recettes\.net)|(application@routenplaner-karten\.com)|(application@ruta-mapa\.com)|(application@satellite\.dev\.squel\.v2)|(application@search-bilan-imc\.fr)|(application@search-maps-finder\.com)|(application@slimness\.fr)|(application@start-bmi\.com)|(application@tests-moi\.com)|(application@tousmesjeux\.fr)|(application@toutlannuaire\.fr)|(application@tuto-diy\.com)|(application@ubersetzung-app\.com)|(application@uk-cookyummy\.com)|(application@uk-howlogin\.me)|(application@uk-myloap\.com)|(application@voyagevoyage\.co)|(application@wikimot\.fr)|(application@www\.plans-reduc\.fr)|(application@yummmi\.es)|(application@yummmies\.be)|(application@yummmies\.ch)|(application@yummmies\.fr)|(application@yummmies\.lu)|(application@zikplay\.fr)|(applicationY@search-maps-finder\.com)|(cmesapps@findizer\.fr)|(findizer-shopping@jetpack)|(\{8aaebb36-1488-4022-b7ec-29b790d12c17\})/">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
     <emItem blockID="1e5f5cb2-346c-422a-9aaa-29d8760949d2" id="{872f20ea-196e-4d11-8835-1cc4c877b1b8}">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="3"/>
     </emItem>
+    <emItem blockID="feb2d0d7-1b76-4dba-bf84-42873a92af5f" id="/^({6ecb9f49-90f0-43a1-8f8a-e809ea4f732b})|(@googledashboard)|(@smashdashboard)|(@smash_tv)|(@smash_mov)|(@smashmovs)|(@smashtvs)|(@FirefoxUpdate)|({92b9e511-ac81-4d47-9b8f-f92dc872447e})|({3c841114-da8c-44ea-8303-78264edfe60b})|({116a0754-20eb-4fe5-bd35-575867a0b89e})|({6e6ff0fd-4ae4-49ae-ac0c-e2527e12359b})|({f992ac88-79d3-4960-870e-92c342ed3491})|({6ecb9f49-90f0-43a1-8f8a-e809ea4f732b})|({a512297e-4d3a-468c-bd1a-f77bd093f925})|({08c28c16-9fb6-4b32-9868-db37c1668f94})|({b4ab1a1d-e137-4c59-94d5-4f509358a81d})|({feedf4f8-08c1-451f-a717-f08233a64ec9})$/">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="3"/>
+    </emItem>
+    <emItem blockID="96b137e6-8cb5-44d6-9a34-4a4a76fb5e38" id="/^({b99ae7b1-aabb-4674-ba8f-14ed32d04e76})|({dfa77d38-f67b-4c41-80d5-96470d804d09})$/">
+      <prefs/>
+      <versionRange minVersion="0" maxVersion="*" severity="3"/>
+    </emItem>
   </emItems>
   <pluginItems>
     <pluginItem blockID="p332">
       <match exp="libflashplayer\.so" name="filename"/>
       <match exp="^Shockwave Flash 11.(0|1) r[0-9]{1,3}$" name="description"/>
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
       <versionRange severity="0" vulnerabilitystatus="1">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
--- a/browser/base/content/test/general/browser_ssl_error_reports.js
+++ b/browser/base/content/test/general/browser_ssl_error_reports.js
@@ -26,17 +26,16 @@ registerCleanupFunction(() => {
 });
 
 add_task(async function test_send_report_neterror() {
   await testSendReportAutomatically(URL_BAD_CHAIN, "succeed", "neterror");
   await testSendReportAutomatically(URL_NO_CERT, "nocert", "neterror");
   await testSetAutomatic(URL_NO_CERT, "nocert", "neterror");
 });
 
-
 add_task(async function test_send_report_certerror() {
   await testSendReportAutomatically(URL_BAD_CERT, "badcert", "certerror");
   await testSetAutomatic(URL_BAD_CERT, "badcert", "certerror");
 });
 
 add_task(async function test_send_disabled() {
   Services.prefs.setBoolPref(PREF_REPORT_ENABLED, false);
   Services.prefs.setBoolPref(PREF_REPORT_AUTOMATIC, true);
@@ -154,21 +153,24 @@ function isErrorStatus(status) {
 // use the observer service to see when a report is sent
 function createReportResponseStatusPromise(expectedURI) {
   return new Promise(resolve => {
     let observer = (subject, topic, data) => {
       subject.QueryInterface(Ci.nsIHttpChannel);
       let requestURI = subject.URI.spec;
       if (requestURI == expectedURI) {
         Services.obs.removeObserver(observer, "http-on-examine-response");
+        console.log(subject.responseStatus);
+        console.log(subject.URI);
+        console.log(requestURI);
         resolve(subject.responseStatus);
       }
     };
     Services.obs.addObserver(observer, "http-on-examine-response");
   });
 }
 
 function checkErrorPage(browser, suffix) {
   return ContentTask.spawn(browser, { suffix }, async function(args) {
     let uri = content.document.documentURI;
-    Assert.ok(uri.startsWith(`about:${args.suffix}`), "correct error page loaded");
+    Assert.ok(uri.startsWith(`about:${args.suffix}`), `correct error page loaded: ${args.suffix}`);
   });
 }
--- a/browser/base/content/test/general/pinning_headers.sjs
+++ b/browser/base/content/test/general/pinning_headers.sjs
@@ -1,11 +1,11 @@
 const INVALIDPIN1 = "pin-sha256=\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\";";
 const INVALIDPIN2 = "pin-sha256=\"AAAAAAAAAAAAAAAAAAAAAAAAAj0e1Md7GkYYkVoZWmM=\";";
-const VALIDPIN = "pin-sha256=\"hXweb81C3HnmM2Ai1dnUzFba40UJMhuu8qZmvN/6WWc=\";";
+const VALIDPIN = "pin-sha256=\"VCIlmPM9NkgFQtrs4Oa5TeFcDu6MWRTKSNdePEhOgD8=\";";
 
 function handleRequest(request, response)
 {
   // avoid confusing cache behaviors
   response.setHeader("Cache-Control", "no-cache", false);
 
   response.setHeader("Content-Type", "text/plain; charset=utf-8", false);
   switch (request.queryString) {
--- a/browser/base/content/test/general/ssl_error_reports.sjs
+++ b/browser/base/content/test/general/ssl_error_reports.sjs
@@ -1,11 +1,11 @@
 const EXPECTED_CHAIN = [
-  "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"
+  "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"
   ];
 
 const MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE = -16384;
 
 function parseReport(request) {
   // read the report from the request
   let inputStream = Components.classes["@mozilla.org/scriptableinputstream;1"].createInstance(Components.interfaces.nsIScriptableInputStream);
   inputStream.init(request.bodyInputStream, 0x01, 0004, 0);
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -2500,19 +2500,16 @@ var SessionStoreInternal = {
     let tab = tabbrowser.selectedTab = tabbrowser.addTab(null, state);
 
     // restore tab content
     this.restoreTab(tab, state);
 
     // restore the tab's position
     tabbrowser.moveTabTo(tab, pos);
 
-    // focus the tab's content area (bug 342432)
-    tab.linkedBrowser.focus();
-
     // Notify of changes to closed objects.
     this._notifyOfClosedObjectsChange();
 
     return tab;
   },
 
   forgetClosedTab: function ssi_forgetClosedTab(aWindow, aIndex) {
     if (!aWindow.__SSi) {
@@ -4014,16 +4011,21 @@ var SessionStoreInternal = {
       new ViewSourceBrowser(browser);
     }
 
     browser.messageManager.sendAsyncMessage("SessionStore:restoreTabContent",
       {loadArguments, isRemotenessUpdate,
        reason: aOptions.restoreContentReason ||
                RESTORE_TAB_CONTENT_REASON.SET_STATE,
        requestTime: Services.telemetry.msSystemNow()});
+
+    // Focus the tab's content area.
+    if (aTab.selected) {
+      browser.focus();
+    }
   },
 
   /**
    * Marks a given pending tab as restoring.
    *
    * @param aTab
    *        the pending tab to mark as restoring
    */
--- a/browser/components/sessionstore/test/browser.ini
+++ b/browser/components/sessionstore/test/browser.ini
@@ -232,25 +232,26 @@ skip-if = true
 # Disabled on OS X:
 [browser_625016.js]
 skip-if = os == "mac" || (os == "linux" && debug) # linux, Bug 1348583
 
 [browser_906076_lazy_tabs.js]
 [browser_911547.js]
 [browser_1284886_suspend_tab.js]
 skip-if = !e10s
-[browser_send_async_message_oom.js]
+[browser_async_window_flushing.js]
+[browser_focus_after_restore.js]
+[browser_forget_async_closings.js]
 [browser_multiple_navigateAndRestore.js]
 run-if = e10s
-[browser_async_window_flushing.js]
-[browser_forget_async_closings.js]
 [browser_newtab_userTypedValue.js]
 [browser_parentProcessRestoreHash.js]
 run-if = e10s
 tags = openUILinkIn
+[browser_send_async_message_oom.js]
 [browser_sessionStoreContainer.js]
 [browser_windowStateContainer.js]
 skip-if = os == "linux" && !debug
 [browser_1234021.js]
 [browser_remoteness_flip_on_restore.js]
 run-if = e10s
 [browser_background_tab_crash.js]
 run-if = e10s && crashreporter
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/test/browser_focus_after_restore.js
@@ -0,0 +1,19 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+add_task(async function test() {
+  gURLBar.focus();
+  is(document.activeElement, gURLBar.inputField, "urlbar is focused before restoring");
+
+  await promiseBrowserState({
+    windows: [{
+      tabs: [{
+        entries: [{
+          url: "http://example.org/", triggeringPrincipal_base64
+        }]
+      }],
+      selected: 1
+    }]
+  });
+  is(document.activeElement, gBrowser.selectedBrowser, "content area is focused after restoring");
+});
--- a/build/pgo/certs/README
+++ b/build/pgo/certs/README
@@ -1,16 +1,31 @@
-The certificate authority and server certificates here are generated by $topsrcdir/build/pgo/genpgocert.py.
+The certificate authority and server certificates here are generated by
+$topsrcdir/build/pgo/genpgocert.py.
+
+You can regenerate the certificates by running: ./mach python
+build/pgo/genpgocert.py
+
+To add a new CA, add a ${cert_name}.ca.keyspec as well as a corresponding
+${cert_name}.certspec to this folder.
 
-You can generate a new CA cert by running:
-./mach python build/pgo/genpgocert.py --gen-ca
+To add new server certificates, add a ${cert_name}.certspec file to this folder.
+If it needs a non-default private key, add a corresponding
+${cert_name}.server.keyspec.
 
-You can generate new server certificates by running:
-./mach python build/pgo/genpgocert.py --gen-server
+For new client certificates, add a ${cert_name}.client.keyspec and corresponding
+${cert_name}.certspec.
+
+The naming convention here is because the generated ".client" and ".ca" PEM
+files need to be copied into this folder for Mochitests' runtests.py to import.
 
 These commands will modify cert9.db and key4.db. The changes to these should be
 committed.
 
-WARNING: These commands do not recreate all necessary certificates; some are
-mentioned only on their tests. Before completely replacing these DBs, you should
-be careful that you include all the correct certificates. Or fix genpgocert.py
-to create the correct certs. See bug 1441338.
+Specific notes for certs:
 
+  dynamicPinningGood: Changing this keyspec will require changing
+  browser/base/content/test/general/pinning_headers.sjs . You can obtain a new
+  valid pin via:
+
+  certutil -L -d . -n dynamicPinningGood -r | openssl x509 -inform der -pubkey \
+  -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary \
+  | openssl enc -base64
--- a/build/pgo/certs/alternateroot.ca
+++ b/build/pgo/certs/alternateroot.ca
@@ -1,18 +1,18 @@
 -----BEGIN CERTIFICATE-----
-MIIC2jCCAcKgAwIBAgIBATANBgkqhkiG9w0BAQsFADAmMSQwIgYDVQQDExtBbHRl
-cm5hdGUgVHJ1c3RlZCBBdXRob3JpdHkwHhcNMTQwOTI1MjEyMTU0WhcNMjQwOTI1
-MjEyMTU0WjAmMSQwIgYDVQQDExtBbHRlcm5hdGUgVHJ1c3RlZCBBdXRob3JpdHkw
-ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDBT+BwAhO52IWgSIdZZifU
-9LHOs3IR/+8DCC0WP5d/OuyKlZ6Rqd0tsd3i7durhQyjHSbLf2lJStcnFjcVEbEn
-NI76RuvlN8xLLn5eV+2Ayr4cZYKztudwRmw+DV/iYAiMSy0hs7m3ssfX7qpoi1aN
-RjUanwU0VTCPQhF1bEKAC2du+C5Z8e92zN5t87w7bYr7lt+m8197XliXEu+0s9Rg
-nGwGaZ296BIRz6NOoJYTa43n06LU1I1+Z4d6lPdzUFrSR0GBaMhUSurUBtOin3yW
-iMhg1VHX/KwqGc4als5GyCVXy8HGrA/0zQPOhetxrlhEVAdK/xBt7CZvByj1Rcc7
-AgMBAAGjEzARMA8GA1UdEwQIMAYBAf8CAQAwDQYJKoZIhvcNAQELBQADggEBAJq/
-hogSRqzPWTwX4wTn/DVSNdWwFLv53qep9YrSMJ8ZsfbfK9Es4VP4dBLRQAVMJ0Z5
-mW1I6d/n0KayTanuUBvemYdxPi/qQNSs8UJcllqdhqWzmzAg6a0LxrMnEeKzPBPD
-6q8PwQ7tYP+B4sBN9tnnsnyPgti9ZiNZn5FwXZliHXseQ7FE9/SqHlLw5LXW3YtK
-juti6RmuV6fq3j+D4oeC5vb1mKgIyoTqGN6ze57v8RHi+pQ8Q+kmoUn/L3Z2YmFe
-4SKN/4WoyXr8TdejpThGOCGCAd3565s5gOx5QfSQX11P8NZKO8hcN0tme3VzmGpH
-K0Z/6MTmdpNaTwQ6odk=
+MIIC+zCCAeOgAwIBAgIUb/+pohOlRCuQgMy2GJLCUQq+HeMwDQYJKoZIhvcNAQEL
+BQAwJjEkMCIGA1UEAwwbQWx0ZXJuYXRlIFRydXN0ZWQgQXV0aG9yaXR5MCIYDzIw
+MTAwMTAxMDAwMDAwWhgPMjA1MDAxMDEwMDAwMDBaMCYxJDAiBgNVBAMMG0FsdGVy
+bmF0ZSBUcnVzdGVkIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBAMF1xlJmCZ93CCpnkfG4dsN/XOU4sGxKzSKxy9RvplraKt1ByMJJisSj
+s8H2FIf0G2mJQb2ApRw8EgJExYSkxEgzBeUTjAEGzwi+moYnYLrmoujzbyPF2YMT
+ud+vN4NF2s5R1Nbc0qbLPMcG680wcOyYzOQKpZHXKVp/ccW+ZmkdKy3+yElEWQvF
+o+pJ/ZOx11NAXxdzdpmVhmYlR5ftQmkIiAgRQiBpmIpD/uSM5oeB3SK2ppzSg3UT
+H5MrEozihvp9JRwGKtJ+8Bbxh83VToMrNbiTD3S6kKqLx2FnJCqx/W1iFA0YxMC4
+xo/DdIRXMkrX3obmVS8dHhkdcSFo07sCAwEAAaMdMBswCwYDVR0PBAQDAgEGMAwG
+A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAAS+qy/sIFV+oia7zsyFhe3X
+j3ZHSvmqJ4mxIg5KOPVP2NvDaxD/+pysxGLf69QDRjIsePBdRJz0zZoVl9pSXIn1
+Kpk0sjzKX2bJtAomog+ZnAZUxtLzoXy/aqaheWm8cRJ8qFOJtSMDRrLISqBXCQLO
+ECqXIxf3Nt3S+Riu2Pam3YymFdtmqUJvLhhekWtEEnXyh/xfAsoUgS3SQ27c4dCY
+R7XGnFsaXrKXv93QeJmtfvrAZMXEuKaBGPSNHV6QH0S0Loh9Jed2Zp7GxnFtIPYe
+J2Q5qtxa8KD/tgGFpAD74eMBdgQ4SxbA/YqqXIt1lLNcr7wm0cPRpP0vIY3hk8k=
 -----END CERTIFICATE-----
new file mode 100644
--- /dev/null
+++ b/build/pgo/certs/alternateroot.ca.keyspec
@@ -0,0 +1,1 @@
+alternate
new file mode 100644
--- /dev/null
+++ b/build/pgo/certs/alternateroot.certspec
@@ -0,0 +1,7 @@
+issuer:Alternate Trusted Authority
+subject:Alternate Trusted Authority
+validity:20100101-20500101
+extension:keyUsage:keyCertSign,cRLSign
+extension:basicConstraints:cA,
+issuerKey:alternate
+subjectKey:alternate
new file mode 100644
--- /dev/null
+++ b/build/pgo/certs/bug413909cert.certspec
@@ -0,0 +1,3 @@
+subject:bug413909.xn--hxajbheg2az3al.xn--jxalpdlp
+issuer:printableString/CN=Temporary Certificate Authority/O=Mozilla Testing/OU=Profile Guided Optimization
+extension:subjectAlternativeName:bug413909.xn--hxajbheg2az3al.xn--jxalpdlp
index 023644e48df7b605e69d62ce8645184a27ed80f4..dff0b7ef1e7c815d0568d95ad919ebe7b449df1a
GIT binary patch
literal 229376
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
z+o9v!cIf=w_<C5&0xv7-5WTbN^Omo@ZgKgvPapH0XU^<zwWALl{2^3#-{`k1$yt;;
zO%qKIOq?90&62T;sd+@tPW!ly_4dt4=3$?SQ_k!?&Ga2o*@x4A)3cQOO0je9R^jhc
zDf|_#8dIxk@r=36fjbY>8V{Q~;5gHN9Gx+4sG(8F{d1($H8(9vj)a>Zz1zQV<-vnW
z3hY^P$}Lv-x=l?oAwMv^JuymW*>sagZ}URN?Mc%=`-FeEe`?ng7A@LfLUjDfhL|-v
zw>O`h$ygL|>!WE+2H#wZe;4mj5?-WIKZ1V*f8yxO-VyZ$ZpE{L%tBsqoeSh&+MbZ@
zyLA)Ev!Y%Ff8eFu%Ny5bp=V#$N335uJvxm3io$l-^5<g&?*vZ;cLi4jrv--uje^aB
zHG*2f9Klq<I6<*sq+o;~MG!9-B<L#$5_A_31@;2GKu@45km0xRU-BRFZ}89akMZ~N
zxAWKYmkSpM0000000000000000000000000000000000000000000000000000000
z00000000000000000000000000000000000000000N}q)9w&`e@-b+!6(xuz=WRp@
z*KY3CqJ&cqYb!~@%2JfDkGN_fNn9}(C2Twj@sdP=nJ8fyJkC^-7;7R*;K^5wC5bCW
zqJ&9CsG%egVjxNwR><m05;A(CgdVe0SCS~v5hb)MCTU9&6SYJMjTM(PC5ej~qJ(Ox
zo4O=HR1+nXYI>?l5<OHz33;x*vLvCWBudB>TvwDNt|^ETQjGWVlEgbXG@<x8Ge}mF
z2$T^eT6$lVmL#s=M2YwAF;a3kX$4_okfN}MZ8;<i$B)yUL&_@7O6KNs$s95#IIBmN
zPWGDY;!J7YIqqKWOu=}834b}?JAYq(bgtgWMI+sEcIEWXzLKqxRg<|Olfa{KUvi#u
z^7B}^WnF#D0000000000000000000000000000000000000000000000000000000
z000000000000000000000000000000004llC5^*iZPAGhI<Ypwb;BY$1T92E78fmz
zQ&x;-rZHJrOnjif)nDXUj*u>-Vv$sW9bzNO)G`}LOV41j$?P0_AdStXC($WnE)DO`
z<EF6KbZ!noAJIiiDr%|qWsRgW7-alF8iz|~CKId>3p7Vb%P4})N}@Ap_z)hQN~7ZY
zXK?B1^pRvPoyD~H<7+hki}vIHBG1eT7l%b`zN`eNY~;`2(%4LN;|8*M9IkLZUpLb9
zR|=c_@jfvA<7+hfl@2ldmBI#pye;~Fe2sc`xPe&2f`CU%#T}x@p)r!2IrL;Ejp{<n
zCZ}gGXf701y6(>w)cK1eUHdQcwElS8HUIb;HGbvrQ~#C1YJa>fs(*ZqDt~;9%71)~
zN`HKfihq2K3WZeZ;&3cdc$uJtD4^wKwWOk>zC4lFk_z-E=px$a6FDuFzAPS-OQtjN
zgXpv@f+b=uUbo?wbvxsO2jQu-L|!tUE8GlWC?S|2M&ixTj-pX`=r-ennaOk}jmAc|
zl7zU6Tk0&VDcN+qop3GaS(U|Nb5nBcg^O`zlGACvv`lAovCb4S!3lAYRd6T?EqSYg
zRlw?+P*TWj2ANA{`f<ez`8E#9|DR~3+#f%BWdHaYW&ZdYrT_REaesV`QW?TED*Vw%
zCww(yo12Rf926`NjNq5>2k?>nXZbtwC+D;CgYpgYj^_=@GtRxA+mJgsKa1}nSdzOt
z-z<Md?u`7f{3bzW?qhz8;8>n{{_DImdBgc*bI0Zu3l|3f00000000000000000000
z00000000000000000000000000000000000000000000000000000000000000000
z00000006+RlEZ0Y7hz&;B|mK>Kdq&47_5~IE&_|-38sj#=<k3l>q!1aJ&pS9Uz)c>
z%UW3C!m)@6!3Z%Bm(}E?kO?%}w*}47f_N8P0v53&*dSKof+k-V>P*YdptIXA+zhQ_
z>WO1x5dy&#aTeFHp))BA9+l?I;U!X8>0~;S<B~yVGU?3ZZ!4Rim5u-SA5b?!S8Es~
zU8smfd<oqVZ}CPG6UkJU&Ntkdn!_Zg(<$FJzyMv6z7?(y7BM9lBZlGyX?4&~dT2>q
zJKR7lVnM(ors9%%92z6ZnL|%zwlA%Nmew}Hb;BY$1T92ETv{=jnZ{&gG4X-^T4(`H
zYg|7pVn#4QjKl@Bh5MA~L2AEOHPE8!PPjNMV)LbcaLPvh3@(k$MEiB1c<;9LuNqoI
zwHq!Qi?|br2qLawpU6vgC%Abd-Y(fpXXlh`a%y4<EtyCj=|*OVW~FA685vYYh6-9=
z*%;@8MRW<;h^Dx{(l^_rgqBeJ;YiEM%5q60Gt&rY8I1TD)j64$gFaV4>&O>UrHh3J
za^Yoy5~6@slhu-nj{5RMUP~&_pP-9qqfg|tRQj@n2d{7^;s?=bSp-YOTzuFVemQKM
z@xg=eRGP45c&_jW5_asD&Q#LUj-pX`Xm{X)naOk}jmAa~FcRV}K1iK~H6=$K-cE9g
zWU<)XlpK5EVqArX3eA_6>5MMcnL;KwAr7(%4ke)_Z&k1gSX~oJ3YpCybID9Uu6QBe
z?lL*Fsj@%pGRXnx+`*Y7gVvBX$MwP@Mg#*yPh3O2FN=~w7qyJSpwpOK99m2Y!6hjm
zjs$zeR@@YPSbDm!flMwV2OmV|NX~X)v(X1v*aM2fKxE6;#t7U5efb*%YX$j&>HJfI
zdx8`E1%hP$J$|@AhhM^P<-7Bf1lI%y1j_{D1w28#AXwliP~pGfU*jL*ujE(p`TXJh
z0KOC7if=4j8~^|S00000000000000000000000000000000000000000000000000
z0000000000000000000000000000000000000000008_?ki%(W7hz&;B|mK>KdmJ{
ztyFNPSRPaIx8yA)b1k%R4p>eKnGjE7QiTunkIS1&^6+{%Vp|O|lS<3ZptEUIGfBRw
zHqHsl;gY#@N(7zBq%)HP$W#+arm+G}7n_mHqL7Uwa}0HGE?8;~lbrr-c>_tVz7oz5
zD_Mb_WS*`n4v*!~7)cy@GLuHtk<8XcXY-h8OjZ^%o<i1=%+^GgFKi0YgY->fG$gs|
z8aNv)nZc#8nPlOF#p0?-a#YcrL|(Ex!Oa`-rqI}26-kaV+GWBvaA@pI8e2&+R}n2w
z<50*MG%}Y<rlb)RBsub$I6EvYlg<>bAe}~~3s)p3$&%H@A>TDVgvFxDNV28THKnsC
zDReH4!^KJFO6lWB3iR}JI#bvdMov7H&XMd4MdALo&7Xx4+!E{&)C$T3X@b52SAhoq
zCI1Y66MrT@mp_c(lW)$)<=+wL@(cJu`G*Df1qTHy1Q~(>0(bs9{zd*a{v3WH--a)r
z|1f`@pi;Ov0000000000000000000000000000000000000000000000000000000
z00000000000000000000000000000000000000000QfJ`$GKxChtD~oucEwkYTEf)
z)z#B(`$o!~vPCoUa<}yuHO>0jwpxL6uap}p9?RD_+MpSv<eKe`_i-(+3k|SOD$A`e
z7pf#!OXDzDtIn!fqPZ5G<(i|p_|9_8&|K5b)@y?18h2LL2+cL@tgZo?tKV5&Jv3Lh
zvs@iCSG%)ZEi_lNvs?`{SG}{XQbTi9JFBaL<|=oVtAyq%c9yGv=E`@r&*jiu+0N?9
Upt;hW<>Js>DLtIKLPxp(4++`IRsaA1
new file mode 100644
--- /dev/null
+++ b/build/pgo/certs/dynamicPinningBad.certspec
@@ -0,0 +1,5 @@
+subject:bad.include-subdomains.pinning-dynamic.example.com
+issuer:Alternate Trusted Authority
+extension:subjectAlternativeName:bad.include-subdomains.pinning-dynamic.example.com
+subjectKey:alternate
+issuerKey:alternate
new file mode 100644
--- /dev/null
+++ b/build/pgo/certs/dynamicPinningBad.server.keyspec
@@ -0,0 +1,1 @@
+alternate
new file mode 100644
--- /dev/null
+++ b/build/pgo/certs/dynamicPinningGood.certspec
@@ -0,0 +1,3 @@
+subject:dynamic-pinning.example.com
+issuer:printableString/CN=Temporary Certificate Authority/O=Mozilla Testing/OU=Profile Guided Optimization
+extension:subjectAlternativeName:*.include-subdomains.pinning-dynamic.example.com,*.pinning-dynamic.example.com
new file mode 100644
--- /dev/null
+++ b/build/pgo/certs/escapeattack1.certspec
@@ -0,0 +1,3 @@
+subject:www.bank1.com\00www.bad-guy.com
+issuer:printableString/CN=Temporary Certificate Authority/O=Mozilla Testing/OU=Profile Guided Optimization
+extension:subjectAlternativeName:www.bank1.com\00www.bad-guy.com
--- a/build/pgo/certs/evintermediate.ca
+++ b/build/pgo/certs/evintermediate.ca
@@ -1,34 +1,26 @@
 -----BEGIN CERTIFICATE-----
-MIIF9zCCBN+gAwIBAgIBAzANBgkqhkiG9w0BAQUFADCB4TELMAkGA1UEBhMCVVMx
-CzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MSMwIQYDVQQKExpN
-b3ppbGxhIC0gRVYgZGVidWcgdGVzdCBDQTEdMBsGA1UECxMUU2VjdXJpdHkgRW5n
-aW5lZXJpbmcxJjAkBgNVBAMTHUVWIFRlc3RpbmcgKHVudHJ1c3R3b3J0aHkpIENB
-MRMwEQYDVQQpEwpldi10ZXN0LWNhMSwwKgYJKoZIhvcNAQkBFh1jaGFybGF0YW5A
-dGVzdGluZy5leGFtcGxlLmNvbTAeFw0xMzAyMTQxNzU5MDlaFw0yMzAyMTIxNzU5
-MDlaMIHRMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDU1vdW50
-YWluIFZpZXcxIzAhBgNVBAoTGk1vemlsbGEgLSBFViBkZWJ1ZyB0ZXN0IENBMR0w
-GwYDVQQLExRTZWN1cml0eSBFbmdpbmVlcmluZzEWMBQGA1UEAxMNaW50ZXJtZWRp
-YXRlMzETMBEGA1UEKRMKZXYtdGVzdC1jYTEsMCoGCSqGSIb3DQEJARYdY2hhcmxh
-dGFuQHRlc3RpbmcuZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
-ggEKAoIBAQDAfzrlJdawr7v8m7lslODk5FTqCiBO7tPxnWhAOEL5g05knLTZTc5J
-3ywmGoW6ae6RwPlWuqRuFd2Ea+yCawyjkUoLOpFH/xziDzvaS6LXNdJoxQqWk/LX
-8YYQVFfmxh8E11fz74IoCzX++mY1byaNONf3bLU2HU8vnVvENr1gy9Bzpm8wUuKm
-HkBYuG0SVzaeym2H/mo5PJICPVhPa+YxfEVS8EIFCigXGH7xrz/bPXnpfgsSJTnN
-4amBNkORfjf7H9x6IWkJGEkIvkVoYKT4iQ9q6/C4YDjWa9p5lA4F/qxnJefezH/I
-6hcqEODSaDsY+I6vsN8ks8r8MTTnd7BjAgMBAAGjggHGMIIBwjAdBgNVHQ4EFgQU
-fluXMAT0ZS21pV13vv46m8k7nRkwggEYBgNVHSMEggEPMIIBC4AUyJg651hwk+3B
-V0rQvQZv9n2bWPahgeekgeQwgeExCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEW
-MBQGA1UEBxMNTW91bnRhaW4gVmlldzEjMCEGA1UEChMaTW96aWxsYSAtIEVWIGRl
-YnVnIHRlc3QgQ0ExHTAbBgNVBAsTFFNlY3VyaXR5IEVuZ2luZWVyaW5nMSYwJAYD
-VQQDEx1FViBUZXN0aW5nICh1bnRydXN0d29ydGh5KSBDQTETMBEGA1UEKRMKZXYt
-dGVzdC1jYTEsMCoGCSqGSIb3DQEJARYdY2hhcmxhdGFuQHRlc3RpbmcuZXhhbXBs
-ZS5jb22CCQCvxT0iZiZJMjAMBgNVHRMEBTADAQH/MDYGA1UdHwQvMC0wK6ApoCeG
-JWh0dHA6Ly9leGFtcGxlLmNvbS9yb290LWV2LXRlc3Rlci5jcmwwPwYDVR0gBDgw
-NjA0BgRVHSAAMCwwKgYIKwYBBQUHAgEWHmh0dHA6Ly9teXRlc3Rkb21haW4ubG9j
-YWwvY3BzOzANBgkqhkiG9w0BAQUFAAOCAQEAC4grNTV5K8yqiAJ/0f6oIkTMqyJ4
-lyHXvvKXMHTpRZ7Jdy0aq5KTSHswx64ZRN7V2ds+czzDWgxX3rBuZZAgOW1JYva3
-Ps3XRYUiaTW8eeaWjuVRFAp7ytRmSsOGeOtHbez8jDmTqPRQ1mTMsMzpY4bFD8do
-5y0xsbz4DYIeeNnX9+XGB5u2ml8t5L8Cj65wwMAx9HlsjTrfQTMIwpwbNle6GuZ3
-9FzmE2piAND73yCgU5W66K2lZg8N6vHBq0UhPDCF72y8MlHxQOpTr3/jIGr4X7k9
-uyYq0Pw5Y/LKyGbyW5iMFdLzabm1ua8IWAf7DSFMH6L3WlK8mngCfJ1icQ==
+MIIEfDCCA2SgAwIBAgIUETbLA86peOWkUFhyKYIuZVGUEygwDQYJKoZIhvcNAQEL
+BQAwgdwxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRh
+aW4gVmlldzEjMCEGA1UEChMaTW96aWxsYSAtIEVWIGRlYnVnIHRlc3QgQ0ExHTAb
+BgNVBAsTFFNlY3VyaXR5IEVuZ2luZWVyaW5nMTYwNAYDVQQDEy1FViBUZXN0aW5n
+ICh1bnRydXN0d29ydGh5KSBDQS9uYW1lPWV2LXRlc3QtY2ExLDAqBgkqhkiG9w0B
+CQEWHWNoYXJsYXRhbkB0ZXN0aW5nLmV4YW1wbGUuY29tMCIYDzIwMTAwMTAxMDAw
+MDAwWhgPMjA1MDAxMDEwMDAwMDBaMIHcMQswCQYDVQQGEwJVUzELMAkGA1UECBMC
+Q0ExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxIzAhBgNVBAoTGk1vemlsbGEgLSBF
+ViBkZWJ1ZyB0ZXN0IENBMR0wGwYDVQQLExRTZWN1cml0eSBFbmdpbmVlcmluZzE2
+MDQGA1UEAxMtRVYgVGVzdGluZyAodW50cnVzdHdvcnRoeSkgQ0EvbmFtZT1ldi10
+ZXN0LWNhMSwwKgYJKoZIhvcNAQkBFh1jaGFybGF0YW5AdGVzdGluZy5leGFtcGxl
+LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALVJiVydABCNEaH5
+n4ep49Gl21367PGI2le/ZBNojyzkciz/EJA4wXQCyToqRz29KGrtP9zTY89aKRR3
+Ab3YGNdhW/k1a9XTyDNqqowJcTaKBsPNRGG5PlFCThdEuy6q1GqrOM4ZaCGWH4dx
+ShZjaT8JdhzfTWuhJerOx74nDTiPeJ9s33iuMUTtKMReeSk4Y6eiKkiYCjakDnLV
+ecm5Jd/4x5M2L/1ol6fBdUxel8lnw+rdGq6KoszONIoBabgOKKLXDBqWDG8zXy2g
+m5tkP1q/uknoqqmB6WDifYdIC91V3ZQX+hhQn7tVTM+BpDl+i6gSijS98nhlwYnl
+c0+yKQUCAwEAAaMwMC4wCwYDVR0PBAQDAgEGMAwGA1UdEwQFMAMBAf8wEQYDVR0g
+BAowCDAGBgRVHSAAMA0GCSqGSIb3DQEBCwUAA4IBAQArG5slgBRJuytlKFa4qcHW
+pAOfjN9fwi57fDds1yNv6tXhESdkbVPhIgw+GanVbrVcorGdCkfB51+dPJM+cBgH
+HSwEB7TQnNYvm/csA1zH4n+CnX9nBL7dwK63n6dyR9f1uvu6KSB+YJm3amKil85a
+d7HeDWdh+gNhC58lEC2QzuOMivP593aS5vLJHfp8pjc21XJkO8M7SRw44OJKYq9/
+v0k6v4SznbfZzSLg3gM4aSNuCLExUtUY2myxPFwJs9QQ4xx5zJTjJTRlpxUm630Z
+n4IYlseao949U+UbBNU4PZKH7dzSQzfhdFJpvK3dsPOPNnHYiXO0xAhsEvvjq8zQ
 -----END CERTIFICATE-----
new file mode 100644
--- /dev/null
+++ b/build/pgo/certs/evintermediate.ca.keyspec
@@ -0,0 +1,1 @@
+ev
new file mode 100644
--- /dev/null
+++ b/build/pgo/certs/evintermediate.certspec
@@ -0,0 +1,7 @@
+issuer:printableString/C=US/ST=CA/L=Mountain View/O=Mozilla - EV debug test CA/OU=Security Engineering/CN=EV Testing (untrustworthy) CA/name=ev-test-ca/emailAddress=charlatan@testing.example.com
+subject:printableString/C=US/ST=CA/L=Mountain View/O=Mozilla - EV debug test CA/OU=Security Engineering/CN=EV Testing (untrustworthy) CA/name=ev-test-ca/emailAddress=charlatan@testing.example.com
+subjectKey:ev
+validity:20100101-20500101
+extension:keyUsage:keyCertSign,cRLSign
+extension:basicConstraints:cA,
+extension:certificatePolicies:any
new file mode 100644
--- /dev/null
+++ b/build/pgo/certs/expired.certspec
@@ -0,0 +1,4 @@
+subject:expired.example.com
+issuer:printableString/CN=Temporary Certificate Authority/O=Mozilla Testing/OU=Profile Guided Optimization
+extension:subjectAlternativeName:expired.example.com
+validity:20100105-20100106
new file mode 100644
--- /dev/null
+++ b/build/pgo/certs/imminently_distrusted.certspec
@@ -0,0 +1,4 @@
+issuer:printableString/CN=Temporary Certificate Authority/O=Mozilla Testing/OU=Profile Guided Optimization
+subject:printableString/CN=Imminently Distrusted End Entity
+validity:20100101-20500101
+extension:subjectAlternativeName:imminently-distrusted.example.com
deleted file mode 100644
--- a/build/pgo/certs/jartests-object.ca
+++ /dev/null
@@ -1,15 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICTTCCAbagAwIBAgIBADANBgkqhkiG9w0BAQUFADBaMRMwEQYDVQQLEwpVbml0
-IFRlc3RzMRgwFgYDVQQKEw9Nb3ppbGxhIFRlc3RpbmcxKTAnBgNVBAMTIFNpZ25l
-ZCBKQVIgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTA5MDExNTE0MDkwM1oXDTM0
-MDExNTE0MDkwM1owWjETMBEGA1UECxMKVW5pdCBUZXN0czEYMBYGA1UEChMPTW96
-aWxsYSBUZXN0aW5nMSkwJwYDVQQDEyBTaWduZWQgSkFSIENlcnRpZmljYXRlIEF1
-dGhvcml0eTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAsQd8eUw4WSK7YoKl
-hqe+CjEgI5Rs3TirWtDsfmMtMBmTvRhJpdTeMAFTpWvlOPuXJwkKXMMFLxE8ayNX
-fO5ixCgJ7LrpguOVZ3pY4RvEyE6yh3Hv81Ztblbo120IdcrkyN4KMs5EgeauDllU
-ehhbq9lmnmQxIQs3KPcoMteqAXcCAwEAAaMjMCEwEQYJYIZIAYb4QgEBBAQDAgAH
-MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAAZHhPT133TcavAKnn37X
-0VE9davrX7t20CLb06KYpgkg7yO0BjIjTnYeJBQgaH652pZVEFT7dbi0JTn4BMXz
-EwOQ2JjzjwNUDHpWAopiCKxAnjwy/kGcZfkKUydwQHKr8m1Faywu1Cyrj0gBHClL
-b2b9ywK4pb545mE6V9pi1zg=
------END CERTIFICATE-----
index e5c08aafc61b2f6eaf5f3657b7210c73faf245f5..daedff91f957b276dba44e71e4ae4ecf232fb223
GIT binary patch
literal 294912
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
zs`AuKvAJf6<Bv;^Z2ro9<vhRngyR>{bmHJ|4_~`QU86@NH1Rv{U(1a_kV!@m)A2lE
z+mZ97VJ=8P_T2}IuaA|@PgE?{?B{w%+~nFEue&7CG7zRRFqV;);j^o}!0~;}HTo5w
z$4W%uJs#;v*U4-f%Fetywa|?&<_ZibeVN>b^l0iu3#3|{GAlL5e5g{0e?syK>9M(e
z^8cUS*$D{9oBwyu+yDRo0QkRxYU_<SbebS^HT2WgI{*Lx00000000000000000000
z00000000000000000000000000000000000000000000000000000000000000000
z000000002s|6vrwB!txRd%h8GuZTqs6Oj{>P*MxaiBoMoN*wxyAao;iW$PUP00000
z00000000000000000000000000000000000000000000000000000000000000000
z00000000000000000000000000PsHq2{8d7@y`VjF);xpRp=lAafmuWh<d1P$l3qf
z*#H0l000000000000000000000000000000000000000000000000000000000000
z000000000000000000000000000000007{>IVoZ<N)b^}VG%(xViOW#n*Gl1cCNnm
zXhCmZTYC>Tl(W0Hkf*b|yR*Ba5IO+m=IM$Svh#4m*pq~76N+HQM5#q6f9^@fNMvI4
z#|;WbB2^VpW)Y^JH^>=jk9qjI`)mz`Fm*=zi*ku@{=A=xk@c9z8E02l6haW8Zi=u+
z+xj{pe9+!Lh^?JDMA&|AM9s)#gtqhba`p*CsJlBlyQ9%wTTw)%L?pKJG74_(X}q=1
z)}096t!MG__4e`i@bYmA<l7oX*d67DRzUj+{v1}&4kda}grAI(KT<Ptg_e+#keS2I
z3FYOA@<F*P`}`W@-#<v=(<B&sLJ=2HUXlI3o^78o+Rf9$3*{AvP(^$BI6FAop?uH?
z6<;4GkDnPucZ)Fp`q=3I_}KmxPLx{&`RfB>)A#alaCSu_G<==y(e?;EPakJD=QAiD
zXAgIxts`;}-$O?g`iX!z^d3QIf9Tt-cK`qY000000000000000000000000000000
z0000000000000000000000000000000000000000000000000000000000000002M
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
new file mode 100644
--- /dev/null
+++ b/build/pgo/certs/mochitest.certspec
@@ -0,0 +1,3 @@
+subject:Mochitest client
+issuer:printableString/CN=Temporary Certificate Authority/O=Mozilla Testing/OU=Profile Guided Optimization
+serialNumber:3
index 5d72cd2b739b3127724e12d63c0c7e79c62666ed..124d04377341d01a30d7cdac4822937e2f310de3
GIT binary patch
literal 2448
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
F{{}8WdprOD
new file mode 100644
--- /dev/null
+++ b/build/pgo/certs/mochitest.client.keyspec
@@ -0,0 +1,1 @@
+default
--- a/build/pgo/certs/pgoca.ca
+++ b/build/pgo/certs/pgoca.ca
@@ -1,15 +1,21 @@
 -----BEGIN CERTIFICATE-----
-MIICXTCCAcagAwIBAgIBATANBgkqhkiG9w0BAQUFADBqMSQwIgYDVQQLExtQcm9m
-aWxlIEd1aWRlZCBPcHRpbWl6YXRpb24xGDAWBgNVBAoTD01vemlsbGEgVGVzdGlu
-ZzEoMCYGA1UEAxMfVGVtcG9yYXJ5IENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0w
-ODA1MjIwMDM4MDVaFw0xODA1MjIwMDM4MDVaMGoxJDAiBgNVBAsTG1Byb2ZpbGUg
-R3VpZGVkIE9wdGltaXphdGlvbjEYMBYGA1UEChMPTW96aWxsYSBUZXN0aW5nMSgw
-JgYDVQQDEx9UZW1wb3JhcnkgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MIGfMA0GCSqG
-SIb3DQEBAQUAA4GNADCBiQKBgQDg6iipAXGZYmgTcHfx8M2hcLqmqDalcj7sZ1A7
-a3LiCBb+1uHKKy9hUxRUe61aJF4NgMAF5oc+HpXN0hpvkiNHxqqD7R6hrkP3gAJ3
-eczEFKsFUI6AqaCL0+xpyhaaZmmarcHxU+PL2h5zq6VssxfBAsO0DkzWzk6E8vM+
-jrku7QIDAQABoxMwETAPBgNVHRMECDAGAQH/AgEAMA0GCSqGSIb3DQEBBQUAA4GB
-ALPbn3Ztg0m8qDt8Vkf5You6HEqIxZe+ffDTrfq/L7ofHk/OXEpL7OWKRHU33pNG
-QS8khBG+sO461C51s6u9giW+eq2PaQv2HGASBpDbvPqc/Hf+zupZsdsXzHv6rt0V
-lu5B6nOpMse1nhA494i1ARSuBNzLv5mas38YWG8Rr6jR
+MIIDgzCCAmugAwIBAgIUQx5pxD+JMg1qPztfSg1Ucw8xsz0wDQYJKoZIhvcNAQEL
+BQAwajEoMCYGA1UEAxMfVGVtcG9yYXJ5IENlcnRpZmljYXRlIEF1dGhvcml0eTEY
+MBYGA1UEChMPTW96aWxsYSBUZXN0aW5nMSQwIgYDVQQLExtQcm9maWxlIEd1aWRl
+ZCBPcHRpbWl6YXRpb24wIhgPMjAxMDAxMDEwMDAwMDBaGA8yMDUwMDEwMTAwMDAw
+MFowajEoMCYGA1UEAxMfVGVtcG9yYXJ5IENlcnRpZmljYXRlIEF1dGhvcml0eTEY
+MBYGA1UEChMPTW96aWxsYSBUZXN0aW5nMSQwIgYDVQQLExtQcm9maWxlIEd1aWRl
+ZCBPcHRpbWl6YXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6
+iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr
+4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP
+8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OI
+Q+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ
+77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5J
+I/pyUcQx1QOs2hgKNe2NAgMBAAGjHTAbMAsGA1UdDwQEAwIBBjAMBgNVHRMEBTAD
+AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAYFnzom5ROuxDR3WFQatxHs5ekni4uUbEx
+6pN8fOzcsllEfCwvmMLVCh36ffSguf/UlmR5Hq1s/S7iMiic5mnK4aaVwixzS4Z3
+ug7Dc+fG7j0VOcBTKWU983xUK/1F409ghQ5KlO38KA7hyx1kzjYjzvxLaweDXRqr
+J/RZ1ACP2fKNziEOCbXzzzEx39oc17NBV+LotPFzKZ+pcxMDrtiNts4hwCw/UUw7
+Gp0tKte2CevGJbzjPHP3/6FUzHfOatZSpxEmvAcSTDp5sjdVuOStx4v6jVrwvyAz
+VQzDPzaRWh3NtY5JNasrhExr5qxQlygfBngCMgZ9gESG9FvLG+sx
 -----END CERTIFICATE-----
new file mode 100644
--- /dev/null
+++ b/build/pgo/certs/pgoca.ca.keyspec
@@ -0,0 +1,1 @@
+default
new file mode 100644
--- /dev/null
+++ b/build/pgo/certs/pgoca.certspec
@@ -0,0 +1,5 @@
+issuer:printableString/CN=Temporary Certificate Authority/O=Mozilla Testing/OU=Profile Guided Optimization
+subject:printableString/CN=Temporary Certificate Authority/O=Mozilla Testing/OU=Profile Guided Optimization
+validity:20100101-20500101
+extension:keyUsage:keyCertSign,cRLSign
+extension:basicConstraints:cA,
deleted file mode 100644
index 4867c286bbf6950a51078caedff875a945b2d93c..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index a3341f767ce01ccdc4a54adf64e82f9ef6b0c2f1..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/build/pgo/certs/selfsigned.certspec
@@ -0,0 +1,3 @@
+issuer:self-signed.example.com
+subject:self-signed.example.com
+extension:subjectAlternativeName:self-signed.example.com
new file mode 100644
--- /dev/null
+++ b/build/pgo/certs/sha1_end_entity.certspec
@@ -0,0 +1,4 @@
+subject:sha1ee.example.com
+issuer:printableString/CN=Temporary Certificate Authority/O=Mozilla Testing/OU=Profile Guided Optimization
+extension:subjectAlternativeName:sha1ee.example.com
+signature:sha1WithRSAEncryption
new file mode 100644
--- /dev/null
+++ b/build/pgo/certs/sha256_end_entity.certspec
@@ -0,0 +1,4 @@
+subject:sha256ee.example.com
+issuer:printableString/CN=Temporary Certificate Authority/O=Mozilla Testing/OU=Profile Guided Optimization
+extension:subjectAlternativeName:sha256ee.example.com
+signature:sha256WithRSAEncryption
new file mode 100644
--- /dev/null
+++ b/build/pgo/certs/staticPinningBad.certspec
@@ -0,0 +1,5 @@
+subject:include-subdomains.pinning.example.com
+issuer:Alternate Trusted Authority
+extension:subjectAlternativeName:include-subdomains.pinning.example.com
+subjectKey:alternate
+issuerKey:alternate
new file mode 100644
--- /dev/null
+++ b/build/pgo/certs/staticPinningBad.server.keyspec
@@ -0,0 +1,1 @@
+alternate
new file mode 100644
--- /dev/null
+++ b/build/pgo/certs/unknown_ca.certspec
@@ -0,0 +1,5 @@
+issuer:Unknown CA
+subject:Unknown CA
+validity:20100101-20500101
+extension:keyUsage:keyCertSign,cRLSign
+extension:basicConstraints:cA,
new file mode 100644
--- /dev/null
+++ b/build/pgo/certs/untrusted.certspec
@@ -0,0 +1,3 @@
+subject:untrusted.example.com
+issuer:Unknown CA
+extension:subjectAlternativeName:untrusted.example.com
new file mode 100644
--- /dev/null
+++ b/build/pgo/certs/untrustedandexpired.certspec
@@ -0,0 +1,4 @@
+subject:untrusted-expired.example.com
+issuer:Unknown CA
+extension:subjectAlternativeName:untrusted-expired.example.com
+validity:20121012-20121012
--- a/build/pgo/genpgocert.py
+++ b/build/pgo/genpgocert.py
@@ -10,19 +10,20 @@
 import mozinfo
 import os
 import random
 import re
 import shutil
 import subprocess
 import sys
 import tempfile
+import distutils
 
 from mozbuild.base import MozbuildObject
-from mozfile import NamedTemporaryFile
+from mozfile import NamedTemporaryFile, TemporaryDirectory
 from mozprofile.permissions import ServerLocations
 
 dbFiles = [
   re.compile("^cert[0-9]+\.db$"),
   re.compile("^key[0-9]+\.db$"),
   re.compile("^secmod\.db$")
 ]
 
@@ -36,181 +37,147 @@ def unlinkDbFiles(path):
 def dbFilesExist(path):
   for root, dirs, files in os.walk(path):
     for name in files:
       for dbFile in dbFiles:
         if dbFile.match(name) and os.path.exists(os.path.join(root, name)):
           return True
   return False
 
-
-def runUtil(util, args, inputdata = None):
+def runUtil(util, args, inputdata = None, outputstream = None):
   env = os.environ.copy()
   if mozinfo.os == "linux":
     pathvar = "LD_LIBRARY_PATH"
     app_path = os.path.dirname(util)
     if pathvar in env:
       env[pathvar] = "%s%s%s" % (app_path, os.pathsep, env[pathvar])
     else:
       env[pathvar] = app_path
   proc = subprocess.Popen([util] + args, env=env,
-                          stdin=subprocess.PIPE if inputdata else None)
+                          stdin=subprocess.PIPE if inputdata else None,
+                          stdout=outputstream)
   proc.communicate(inputdata)
   return proc.returncode
 
-
 def createRandomFile(randomFile):
   for count in xrange(0, 2048):
     randomFile.write(chr(random.randint(0, 255)))
 
-
-def createCertificateAuthority(build, srcDir):
-  certutil = build.get_binary_path(what="certutil")
-  pk12util = build.get_binary_path(what="pk12util")
-
-  #TODO: mozfile.TemporaryDirectory
-  tempDbDir = tempfile.mkdtemp()
-  with NamedTemporaryFile() as pwfile, NamedTemporaryFile() as rndfile:
-    pgoCAModulePathSrc = os.path.join(srcDir, "pgoca.p12")
-    pgoCAPathSrc = os.path.join(srcDir, "pgoca.ca")
-
-    pwfile.write("\n")
-
-    # Create temporary certification database for CA generation
-    status = runUtil(certutil, ["-N", "-d", tempDbDir, "-f", pwfile.name])
-    if status:
-      return status
-
-    createRandomFile(rndfile)
-    status = runUtil(certutil, ["-S", "-d", tempDbDir, "-s", "CN=Temporary Certificate Authority, O=Mozilla Testing, OU=Profile Guided Optimization", "-t", "C,,", "-x", "-m", "1", "-v", "120", "-n", "pgo temporary ca", "-2", "-f", pwfile.name, "-z", rndfile.name], "Y\n0\nN\n")
-    if status:
-      return status
-
-    status = runUtil(certutil, ["-L", "-d", tempDbDir, "-n", "pgo temporary ca", "-a", "-o", pgoCAPathSrc, "-f", pwfile.name])
-    if status:
-      return status
-
-    status = runUtil(pk12util, ["-o", pgoCAModulePathSrc, "-n", "pgo temporary ca", "-d", tempDbDir, "-w", pwfile.name, "-k", pwfile.name])
-    if status:
-      return status
-
-  shutil.rmtree(tempDbDir)
-  return 0
-
-
-def createSSLServerCertificate(build, srcDir):
-  certutil = build.get_binary_path(what="certutil")
-  pk12util = build.get_binary_path(what="pk12util")
-
-  with NamedTemporaryFile() as pwfile, NamedTemporaryFile() as rndfile:
-    pgoCAPath = os.path.join(srcDir, "pgoca.p12")
-
-    pwfile.write("\n")
-
-    if not dbFilesExist(srcDir):
-      # Make sure all DB files from src are really deleted
-      unlinkDbFiles(srcDir)
-
-      # Create certification database for ssltunnel
-      status = runUtil(certutil, ["-N", "-d", srcDir, "-f", pwfile.name])
-      if status:
-        return status
-
-      status = runUtil(pk12util, ["-i", pgoCAPath, "-w", pwfile.name, "-d", srcDir, "-k", pwfile.name])
-      if status:
-        return status
-
-    # Generate automatic certificate
+def writeCertspecForServerLocations(fd):
     locations = ServerLocations(os.path.join(build.topsrcdir,
                                              "build", "pgo",
                                              "server-locations.txt"))
-    iterator = iter(locations)
+    SAN=[]
+    for loc in [i for i in iter(locations) if i.scheme == "https" and "nocert" not in i.options]:
+      customCertOption = False
+      customCertRE = re.compile("^cert=(?:\w+)")
+      for _ in [i for i in loc.options if customCertRE.match(i)]:
+        customCertOption = True
+        break
+
+      if not customCertOption:
+        SAN.append(loc.host)
 
-    # Skips the first entry, I don't know why: bug 879740
-    iterator.next()
+    fd.write("issuer:printableString/CN=Temporary Certificate Authority/O=Mozilla Testing/OU=Profile Guided Optimization\n")
+    fd.write("subject:{}\n".format(SAN[0]))
+    fd.write("extension:subjectAlternativeName:{}\n".format(",".join(SAN)))
+
+def constructCertDatabase(build, srcDir):
+  certutil = build.get_binary_path(what="certutil")
+  pk12util = build.get_binary_path(what="pk12util")
+  openssl = distutils.spawn.find_executable("openssl")
+  pycert = os.path.join(build.topsrcdir, "security", "manager", "ssl", "tests",
+                        "unit", "pycert.py")
+  pykey = os.path.join(build.topsrcdir, "security", "manager", "ssl", "tests",
+                        "unit", "pykey.py")
+
 
-    locationsParam = ""
-    firstLocation = ""
-    for loc in iterator:
-      if loc.scheme == "https" and "nocert" not in loc.options:
-        customCertOption = False
-        customCertRE = re.compile("^cert=(?:\w+)")
-        for option in loc.options:
-          match = customCertRE.match(option)
-          if match:
-            customCertOption = True
-            break
+  with NamedTemporaryFile() as pwfile, NamedTemporaryFile() as rndfile, TemporaryDirectory() as pemfolder:
+    pgoCAPath = os.path.join(srcDir, "pgoca.p12")
+
+    pwfile.write("\n")
+    pwfile.flush()
+
+    if dbFilesExist(srcDir):
+      # Make sure all DB files from src are really deleted
+      unlinkDbFiles(srcDir)
 
-        if not customCertOption:
-          if len(locationsParam) > 0:
-            locationsParam += ","
-          locationsParam += loc.host
+    # Copy  all .certspec and .keyspec files to a temporary directory
+    for root, dirs, files in os.walk(srcDir):
+      for spec in [i for i in files if i.endswith(".certspec") or i.endswith(".keyspec")]:
+        shutil.copyfile(os.path.join(root, spec), os.path.join(pemfolder, spec))
 
-          if firstLocation == "":
-            firstLocation = loc.host
+    # Write a certspec for the "server-locations.txt" file to that temporary directory
+    pgoserver_certspec = os.path.join(pemfolder, "pgoserver.certspec")
+    if os.path.exists(pgoserver_certspec):
+      raise Exception("{} already exists, which isn't allowed".format(pgoserver_certspec))
+    with open(pgoserver_certspec, "w") as fd:
+      writeCertspecForServerLocations(fd)
 
-    if not firstLocation:
-      print "Nothing to generate, no automatic secure hosts specified"
-    else:
-      createRandomFile(rndfile)
+    # Generate certs for all certspecs
+    for root, dirs, files in os.walk(pemfolder):
+      for certspec in [i for i in files if i.endswith(".certspec")]:
+        name = certspec.split(".certspec")[0]
+        pem = os.path.join(pemfolder, "{}.cert.pem".format(name))
 
-      runUtil(certutil, ["-D", "-n", "pgo server certificate", "-d", srcDir, "-z", rndfile.name, "-f", pwfile.name])
-      # Ignore the result, the certificate may not be present when new database is being built
+        print("Generating public certificate {} (pem={})".format(name, pem))
 
-      status = runUtil(certutil, ["-S", "-s", "CN=%s" % firstLocation, "-t", "Pu,,", "-c", "pgo temporary ca", "-m", "2", "-8", locationsParam, "-v", "120", "-n", "pgo server certificate", "-d", srcDir, "-z", rndfile.name, "-f", pwfile.name])
-      if status:
-        return status
+        with open(os.path.join(root, certspec), "r") as certspec_file:
+          certspec_data = certspec_file.read()
+          with open(pem, "w") as pem_file:
+            status = runUtil(pycert, [], inputdata=certspec_data, outputstream=pem_file)
+            if status:
+              return status
 
-      status = runUtil(certutil, ["-S", "-s", "CN=Imminently Distrusted End Entity", "-t", "P,,", "-c", "pgo temporary ca", "-k", "rsa", "-g", "2048", "-Z", "SHA256", "-m", "1519140221", "-n", "imminently_distrusted", "-v", "120", "-8", "imminently-distrusted.example.com", "-d", srcDir, "-z", rndfile.name, "-f", pwfile.name])
-      if status:
-        return status
+        status = runUtil(certutil, ["-A", "-n", name, "-t", "P,,", "-i", pem, "-d", srcDir, "-f", pwfile.name])
+        if status:
+          return status
+
 
-    """
-    As of February 2018, there are 15 more certificates which are not created by
-    this script. See bug 1441338:
+      for keyspec in [i for i in files if i.endswith(".keyspec")]:
+        parts = keyspec.split(".")
+        name = parts[0]
+        key_type = parts[1]
+        if key_type not in ["ca", "client", "server"]:
+          raise Exception("{}: keyspec filenames must be of the form XXX.client.keyspec or XXX.ca.keyspec (key_type={})".format(keyspec, key_type))
+        key_pem = os.path.join(pemfolder, "{}.key.pem".format(name))
+
+        print("Generating private key {} (pem={})".format(name, key_pem))
 
-    selfsigned                                                   Pu,u,u
-    Unknown CA                                                   Cu,u,u
-    escapeattack1                                                Pu,u,u
-    untrustedandexpired                                          Pu,u,u
-    alternateTrustedAuthority                                    Cu,u,u
-    dynamicPinningGood                                           Pu,u,u
-    staticPinningBad                                             Pu,u,u
-    sha1_end_entity                                              Pu,u,u
-    bug413909cert                                                u,u,u
-    untrusted                                                    Pu,u,u
-    escapeattack2                                                Pu,u,u
-    expired                                                      Pu,u,u
-    dynamicPinningBad                                            Pu,u,u
-    sha256_end_entity                                            Pu,u,u
-    """
+        with open(os.path.join(root, keyspec), "r") as keyspec_file:
+          keyspec_data = keyspec_file.read()
+          with open(key_pem, "w") as pem_file:
+            status = runUtil(pykey, [], inputdata=keyspec_data, outputstream=pem_file)
+            if status:
+              return status
+
+        cert_pem = os.path.join(pemfolder, "{}.cert.pem".format(name))
+        if not os.path.exists(cert_pem):
+          raise Exception("There has to be a corresponding certificate named {} for the keyspec {}".format(cert_pem, keyspec))
+
+        p12 = os.path.join(pemfolder, "{}.key.p12".format(name))
+        print("Converting private key {} to PKCS12 (p12={})".format(key_pem, p12))
+        status = runUtil(openssl, ["pkcs12", "-export", "-inkey", key_pem, "-in", cert_pem, "-name", name, "-out", p12, "-passout", "file:"+pwfile.name])
+        if status:
+          return status
+
+        print("Importing private key {} to database".format(key_pem))
+        status = runUtil(pk12util, ["-i", p12, "-d", srcDir, "-w", pwfile.name, "-k", pwfile.name])
+        if status:
+          return status
+
+        if key_type == "ca":
+          shutil.copyfile(cert_pem, os.path.join(srcDir, "{}.ca".format(name)))
+        elif key_type == "client":
+          shutil.copyfile(p12, os.path.join(srcDir, "{}.client".format(name)))
+        elif key_type == "server":
+          pass # Nothing to do for server keys
+        else:
+          raise Exception("State error: Unknown keyspec key_type: {}".format(key_type))
 
   return 0
 
-if len(sys.argv) == 1:
-  print "Specify --gen-server or --gen-ca"
-  sys.exit(1)
-
 build = MozbuildObject.from_environment()
 certdir = os.path.join(build.topsrcdir, "build", "pgo", "certs")
-if sys.argv[1] == "--gen-server":
-  certificateStatus = createSSLServerCertificate(build, certdir)
-  if certificateStatus:
-    print "TEST-UNEXPECTED-FAIL | SSL Server Certificate generation"
-
-  sys.exit(certificateStatus)
-
-if sys.argv[1] == "--gen-ca":
-  certificateStatus = createCertificateAuthority(build, certdir)
-  if certificateStatus:
-    print "TEST-UNEXPECTED-FAIL | Certificate Authority generation"
-  else:
-    print "\n\n"
-    print "==================================================="
-    print " IMPORTANT:"
-    print " To use this new certificate authority in tests"
-    print " run 'make' at testing/mochitest"
-    print "==================================================="
-
-  sys.exit(certificateStatus)
-
-print "Invalid option specified"
-sys.exit(1)
+certificateStatus = constructCertDatabase(build, certdir)
+if certificateStatus:
+  print "TEST-UNEXPECTED-FAIL | SSL Server Certificate generation"
+sys.exit(certificateStatus)
--- a/devtools/client/inspector/inspector.js
+++ b/devtools/client/inspector/inspector.js
@@ -52,16 +52,17 @@ const INITIAL_SIDEBAR_SIZE = 350;
 const PORTRAIT_MODE_WIDTH_THRESHOLD = 700;
 // If the toolbox's width docked to the side is smaller than the given amount of pixels,
 // the sidebar automatically switches from 'landscape/horizontal' to 'portrait/vertical'
 // mode.
 const SIDE_PORTAIT_MODE_WIDTH_THRESHOLD = 1000;
 
 const SHOW_THREE_PANE_TOGGLE_PREF = "devtools.inspector.three-pane-toggle";
 const THREE_PANE_ENABLED_PREF = "devtools.inspector.three-pane-enabled";
+const THREE_PANE_ENABLED_SCALAR = "devtools.inspector.three_pane_enabled";
 
 /**
  * Represents an open instance of the Inspector for a tab.
  * The inspector controls the breadcrumbs, the markup view, and the sidebar
  * (computed view, rule view, font view and animation inspector).
  *
  * Events:
  * - ready
@@ -286,18 +287,24 @@ Inspector.prototype = {
     // All the components are initialized. Let's select a node.
     if (defaultSelection) {
       let onAllPanelsUpdated = this.once("inspector-updated");
       this.selection.setNodeFront(defaultSelection, { reason: "inspector-open" });
       await onAllPanelsUpdated;
       await this.markup.expandNode(this.selection.nodeFront);
     }
 
-    // And setup the toolbar only now because it may depend on the document.
+    // Setup the toolbar only now because it may depend on the document.
     await this.setupToolbar();
+
+    // Log the 3 pane inspector setting on inspector open. The question we want to answer
+    // is:
+    // "What proportion of users use the 3 pane vs 2 pane inspector on inspector open?"
+    this.telemetry.logKeyedScalar(THREE_PANE_ENABLED_SCALAR, this.is3PaneModeEnabled, 1);
+
     this.emit("ready");
     return this;
   },
 
   _onBeforeNavigate: function() {
     this._defaultNode = null;
     this.selection.setNodeFront(null);
     this._destroyMarkup();
--- a/devtools/client/inspector/markup/test/browser_markup_display_node_01.js
+++ b/devtools/client/inspector/markup/test/browser_markup_display_node_01.js
@@ -26,33 +26,33 @@ const TEST_URI = `
 
 add_task(async function() {
   let {inspector} = await openInspectorForURL("data:text/html;charset=utf-8," +
     encodeURIComponent(TEST_URI));
 
   info("Check the display node is shown and the value of #grid.");
   await selectNode("#grid", inspector);
   let gridContainer = await getContainerForSelector("#grid", inspector);
-  let gridDisplayNode = gridContainer.elt.querySelector(".markupview-display");
+  let gridDisplayNode = gridContainer.elt.querySelector(".markupview-display-badge");
   is(gridDisplayNode.textContent, "grid", "Got the correct display type for #grid.");
   is(gridDisplayNode.style.display, "inline-block", "#grid display node is shown.");
 
   info("Check the display node is shown and the value of #flex.");
   await selectNode("#flex", inspector);
   let flexContainer = await getContainerForSelector("#flex", inspector);
-  let flexDisplayNode = flexContainer.elt.querySelector(".markupview-display");
+  let flexDisplayNode = flexContainer.elt.querySelector(".markupview-display-badge");
   is(flexDisplayNode.textContent, "flex", "Got the correct display type for #flex");
   is(flexDisplayNode.style.display, "inline-block", "#flex display node is shown.");
 
   info("Check the display node is shown and the value of #block.");
   await selectNode("#block", inspector);
   let blockContainer = await getContainerForSelector("#block", inspector);
-  let blockDisplayNode = blockContainer.elt.querySelector(".markupview-display");
+  let blockDisplayNode = blockContainer.elt.querySelector(".markupview-display-badge");
   is(blockDisplayNode.textContent, "block", "Got the correct display type for #block");
   is(blockDisplayNode.style.display, "none", "#block display node is hidden.");
 
   info("Check the display node is shown and the value of span.");
   await selectNode("span", inspector);
   let spanContainer = await getContainerForSelector("span", inspector);
-  let spanDisplayNode = spanContainer.elt.querySelector(".markupview-display");
+  let spanDisplayNode = spanContainer.elt.querySelector(".markupview-display-badge");
   is(spanDisplayNode.textContent, "inline", "Got the correct display type for #span");
   is(spanDisplayNode.style.display, "none", "span display node is hidden.");
 });
--- a/devtools/client/inspector/markup/test/browser_markup_display_node_02.js
+++ b/devtools/client/inspector/markup/test/browser_markup_display_node_02.js
@@ -92,17 +92,17 @@ add_task(async function() {
     await runTestData(inspector, testActor, data);
   }
 });
 
 async function runTestData(inspector, testActor,
                       {selector, before, changeStyle, after}) {
   await selectNode(selector, inspector);
   let container = await getContainerForSelector(selector, inspector);
-  let displayNode = container.elt.querySelector(".markupview-display");
+  let displayNode = container.elt.querySelector(".markupview-display-badge");
 
   is(displayNode.textContent, before.textContent,
     `Got the correct before display type for ${selector}: ${displayNode.textContent}`);
   is(displayNode.style.display, before.display,
     `Got the correct before display style for ${selector}: ${displayNode.style.display}`);
 
   info("Listening for the display-change event");
   let onDisplayChanged = inspector.markup.walker.once("display-change");
--- a/devtools/client/inspector/markup/test/browser_markup_events-overflow.js
+++ b/devtools/client/inspector/markup/test/browser_markup_events-overflow.js
@@ -29,17 +29,17 @@ const TEST_DATA = [
     alignTop: false,
   },
 ];
 
 add_task(async function() {
   let { inspector } = await openInspectorForURL(TEST_URL);
 
   let markupContainer = await getContainerForSelector("#events", inspector);
-  let evHolder = markupContainer.elt.querySelector(".markupview-events");
+  let evHolder = markupContainer.elt.querySelector(".markupview-event-badge");
   let tooltip = inspector.markup.eventDetailsTooltip;
 
   info("Clicking to open event tooltip.");
   EventUtils.synthesizeMouseAtCenter(evHolder, {},
     inspector.markup.doc.defaultView);
   await tooltip.once("shown");
   info("EventTooltip visible.");
 
--- a/devtools/client/inspector/markup/test/browser_markup_events-windowed-host.js
+++ b/devtools/client/inspector/markup/test/browser_markup_events-windowed-host.js
@@ -25,17 +25,17 @@ add_task(async function() {
   await toolbox.switchHost("bottom");
   await runTests(inspector);
 
   await toolbox.destroy();
 });
 
 async function runTests(inspector) {
   let markupContainer = await getContainerForSelector("#events", inspector);
-  let evHolder = markupContainer.elt.querySelector(".markupview-events");
+  let evHolder = markupContainer.elt.querySelector(".markupview-event-badge");
   let tooltip = inspector.markup.eventDetailsTooltip;
 
   info("Clicking to open event tooltip.");
 
   let onInspectorUpdated = inspector.once("inspector-updated");
   let onTooltipShown = tooltip.once("shown");
   EventUtils.synthesizeMouseAtCenter(evHolder, {}, inspector.markup.doc.defaultView);
 
--- a/devtools/client/inspector/markup/test/browser_markup_events_click_to_close.js
+++ b/devtools/client/inspector/markup/test/browser_markup_events_click_to_close.js
@@ -22,20 +22,20 @@ const TEST_URL = `
 
 add_task(async function() {
   let {inspector, toolbox} = await openInspectorForURL(
     "data:text/html;charset=utf-8," + encodeURI(TEST_URL));
 
   await inspector.markup.expandAll();
 
   let container1 = await getContainerForSelector("#d1", inspector);
-  let evHolder1 = container1.elt.querySelector(".markupview-events");
+  let evHolder1 = container1.elt.querySelector(".markupview-event-badge");
 
   let container2 = await getContainerForSelector("#d2", inspector);
-  let evHolder2 = container2.elt.querySelector(".markupview-events");
+  let evHolder2 = container2.elt.querySelector(".markupview-event-badge");
 
   let tooltip = inspector.markup.eventDetailsTooltip;
 
   info("Click the event icon for the first element");
   let onShown = tooltip.once("shown");
   EventUtils.synthesizeMouseAtCenter(evHolder1, {},
     inspector.markup.doc.defaultView);
   await onShown;
--- a/devtools/client/inspector/markup/test/helper_events_test_runner.js
+++ b/devtools/client/inspector/markup/test/helper_events_test_runner.js
@@ -50,17 +50,17 @@ async function runEventPopupTests(url, t
 async function checkEventsForNode(test, inspector, testActor) {
   let {selector, expected, beforeTest, isSourceMapped} = test;
   let container = await getContainerForSelector(selector, inspector);
 
   if (typeof beforeTest === "function") {
     await beforeTest(inspector, testActor);
   }
 
-  let evHolder = container.elt.querySelector(".markupview-events");
+  let evHolder = container.elt.querySelector(".markupview-event-badge");
 
   if (expected.length === 0) {
     // if no event is expected, simply check that the event bubble is hidden
     is(evHolder.style.display, "none", "event bubble should be hidden");
     return;
   }
 
   let tooltip = inspector.markup.eventDetailsTooltip;
--- a/devtools/client/inspector/markup/views/element-editor.js
+++ b/devtools/client/inspector/markup/views/element-editor.js
@@ -166,24 +166,24 @@ ElementEditor.prototype = {
 
     this.closeTag = this.doc.createElement("span");
     this.closeTag.classList.add("tag", "theme-fg-color3");
     close.appendChild(this.closeTag);
 
     close.appendChild(this.doc.createTextNode(">"));
 
     this.eventNode = this.doc.createElement("div");
-    this.eventNode.classList.add("markupview-events");
+    this.eventNode.classList.add("markupview-event-badge");
     this.eventNode.dataset.event = "true";
-    this.eventNode.textContent = "ev";
+    this.eventNode.textContent = "event";
     this.eventNode.title = INSPECTOR_L10N.getStr("markupView.event.tooltiptext");
     this.elt.appendChild(this.eventNode);
 
     this.displayNode = this.doc.createElement("div");
-    this.displayNode.classList.add("markupview-display");
+    this.displayNode.classList.add("markupview-display-badge");
     this.elt.appendChild(this.displayNode);
   },
 
   set selected(value) {
     if (this.textEditor) {
       this.textEditor.selected = value;
     }
   },
--- a/devtools/client/inspector/rules/views/rule-editor.js
+++ b/devtools/client/inspector/rules/views/rule-editor.js
@@ -241,18 +241,19 @@ RuleEditor.prototype = {
       editableItem({ element: this.closeBrace }, () => {
         this.newProperty();
       });
     }
 
     // Create the font editor toggle icon visible on hover.
     if (this.ruleView.showFontEditor) {
       this.fontSwatch = createChild(this.element, "div", {
-        class: "ruleview-fontswatch"
+        class: "ruleview-font-editor-toggle"
       });
+      this.fontSwatch.textContent = "Aa";
 
       this.fontSwatch.addEventListener("click", this._onFontSwatchClick);
     }
   },
 
   /**
    * Handler for clicks on font swatch icon.
    * Toggles the selected state of the the current rule for the font editor.
--- a/devtools/client/jar.mn
+++ b/devtools/client/jar.mn
@@ -112,17 +112,16 @@ devtools.jar:
     skin/toolbars.css (themes/toolbars.css)
     skin/toolbox.css (themes/toolbox.css)
     skin/tooltips.css (themes/tooltips.css)
     skin/images/accessibility.svg (themes/images/accessibility.svg)
     skin/images/add.svg (themes/images/add.svg)
     skin/images/breadcrumbs-divider.svg (themes/images/breadcrumbs-divider.svg)
     skin/images/filters.svg (themes/images/filters.svg)
     skin/images/filter-swatch.svg (themes/images/filter-swatch.svg)
-    skin/images/font-swatch.svg (themes/images/font-swatch.svg)
     skin/images/grid.svg (themes/images/grid.svg)
     skin/images/angle-swatch.svg (themes/images/angle-swatch.svg)
     skin/images/pseudo-class.svg (themes/images/pseudo-class.svg)
     skin/images/controls.png (themes/images/controls.png)
     skin/images/controls@2x.png (themes/images/controls@2x.png)
     skin/images/copy.svg (themes/images/copy.svg)
     skin/images/animation-fast-track.svg (themes/images/animation-fast-track.svg)
     skin/images/performance-details-waterfall.svg (themes/images/performance-details-waterfall.svg)
deleted file mode 100644
--- a/devtools/client/themes/images/font-swatch.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-<!-- 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 viewBox="0 0 16 11" version="1.1" xmlns="http://www.w3.org/2000/svg">
-  <path fill-rule="evenodd" fill="context-fill" d="M2,0 L14,0 C15.1045695,-2.02906125e-16 16,0.8954305 16,2 L16,9 C16,10.1045695 15.1045695,11 14,11 L2,11 C0.8954305,11 1.3527075e-16,10.1045695 0,9 L0,2 C-1.3527075e-16,0.8954305 0.8954305,2.02906125e-16 2,0 Z M6.84277344,8.70001221 L8.27978516,8.70001221 L6.078125,2.35870361 L4.51806641,2.35870361 L2.31640625,8.70001221 L3.64794922,8.70001221 L4.13574219,7.18829346 L6.36376953,7.18829346 L6.84277344,8.70001221 Z M5.21679688,3.67706299 L4.42138672,6.16436768 L6.08251953,6.16436768 L5.29589844,3.67706299 L5.21679688,3.67706299 Z M10.4946289,8.77471924 C9.56298828,8.77471924 8.90820312,8.20343018 8.90820312,7.31573486 C8.90820312,6.43682861 9.58056641,5.92706299 10.784668,5.85675049 L11.9580078,5.78643799 L11.9580078,5.39093018 C11.9580078,4.9866333 11.6679688,4.75811768 11.140625,4.75811768 C10.6791992,4.75811768 10.371582,4.9163208 10.2661133,5.20635986 L9.08837891,5.20635986 C9.171875,4.30987549 9.99365234,3.74737549 11.2109375,3.74737549 C12.4941406,3.74737549 13.2192383,4.3538208 13.2192383,5.39093018 L13.2192383,8.70001221 L11.9799805,8.70001221 L11.9799805,8.06719971 L11.9008789,8.06719971 C11.6503906,8.51104736 11.1230469,8.77471924 10.4946289,8.77471924 Z M10.9165039,7.81231689 C11.5097656,7.81231689 11.9580078,7.43438721 11.9580078,6.94219971 L11.9580078,6.58624268 L10.9780273,6.64776611 C10.4287109,6.68731689 10.1606445,6.8850708 10.1606445,7.23223877 C10.1606445,7.59259033 10.4726562,7.81231689 10.9165039,7.81231689 Z"></path>
-</svg>
--- a/devtools/client/themes/markup.css
+++ b/devtools/client/themes/markup.css
@@ -1,21 +1,29 @@
 /* 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/. */
 
 :root {
+  --markup-badge-background-color: var(--grey-20);
+  --markup-badge-border-color: #CACAD1;
+  --markup-badge-color: var(--grey-90);
+  --markup-badge-hover-background-color: #DFDFE8;
   --markup-hidden-attr-name-color: #BA89B8;
   --markup-hidden-attr-value-color: #5C6D87;
   --markup-hidden-punctuation-color: #909090;
   --markup-hidden-tag-color: #97A4B3;
   --markup-outline: var(--theme-splitter-color);
 }
 
 .theme-dark:root {
+  --markup-badge-background-color: var(--grey-70);
+  --markup-badge-border-color: var(--grey-50);
+  --markup-badge-color: var(--grey-30);
+  --markup-badge-hover-background-color: var(--grey-80);
   --markup-hidden-attr-name-color: #B07EB3;
   --markup-hidden-attr-value-color: #9893A3;
   --markup-hidden-punctuation-color: #909090;
   --markup-hidden-tag-color: #AFB5BF;
   --markup-outline: var(--theme-selection-background);
 }
 
 * {
@@ -381,26 +389,33 @@ ul.children + .tag-line::before {
   color: var(--theme-selection-color);
 }
 
 /* Applicable to the DOCTYPE */
 .doctype {
   font-style: italic;
 }
 
-/* Display and Events Bubble */
-.markupview-display,
-.markupview-events {
+/* Display and Event Badges */
+.markupview-display-badge,
+.markupview-event-badge {
   display: none;
-  font-size: 8px;
-  font-weight: bold;
-  line-height: 10px;
+  font-size: 9px;
+  font-weight: normal;
+  line-height: 11px;
+  vertical-align: 1px;
+  border: 1px solid var(--markup-badge-border-color);
   border-radius: 3px;
   padding: 0px 2px;
   margin-inline-start: 5px;
   -moz-user-select: none;
-  background-color: var(--theme-body-color-alt);
-  color: var(--theme-body-background);
+  background-color: var(--markup-badge-background-color);
+  color: var(--markup-badge-color);
 }
 
-.markupview-events {
+.markupview-event-badge {
   cursor: pointer;
 }
+
+.markupview-event-badge:focus,
+.markupview-event-badge:hover {
+  background-color: var(--markup-badge-hover-background-color);
+}
--- a/devtools/client/themes/rules.css
+++ b/devtools/client/themes/rules.css
@@ -1,22 +1,32 @@
 /* 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/. */
 
 /* CSS Variables specific to this panel that aren't defined by the themes */
 :root {
+  --rule-badge-active-background-color: var(--blue-50);
+  --rule-badge-background-color: var(--grey-20);
+  --rule-badge-border-color: #CACAD1;
+  --rule-badge-color: var(--grey-90);
+  --rule-badge-hover-background-color: #DFDFE8;
   --rule-highlight-background-color: var(--theme-highlight-yellow);
   --rule-overridden-item-border-color: var(--theme-content-color3);
   --rule-header-background-color: var(--theme-toolbar-background);
   --rule-flex-toggle-color: var(--grey-90);
   --rule-shape-toggle-color: var(--grey-90);
 }
 
 :root.theme-dark {
+  --rule-badge-active-background-color: var(--blue-60);
+  --rule-badge-background-color: var(--grey-70);
+  --rule-badge-border-color: var(--grey-50);
+  --rule-badge-color: var(--grey-30);
+  --rule-badge-hover-background-color: var(--grey-80);
   --rule-highlight-background-color: #521C76;
   --rule-overridden-item-border-color: var(--theme-content-color1);
   --rule-header-background-color: #222225;
   --rule-flex-toggle-color: var(--grey-10);
   --rule-shape-toggle-color: var(--grey-10);
 }
 
 /* Rule View Tabpanel */
@@ -448,40 +458,16 @@
   background-size: 1em;
 }
 
 .ruleview-angleswatch {
   background: url("chrome://devtools/skin/images/angle-swatch.svg");
   background-size: 1em;
 }
 
-.ruleview-rule:not(:hover) .ruleview-fontswatch:not(.active) {
-  visibility: hidden;
-}
-
-.ruleview-fontswatch {
-  position: absolute;
-  width: 1.45em;
-  height: 1em;
-  right: 1.5em;
-  bottom: 0.5em;
-  font-size: 1em;
-
-  background: url("chrome://devtools/skin/images/font-swatch.svg");
-  -moz-context-properties: fill;
-  fill: var(--grey-40);
-  background-size: contain;
-  cursor: pointer;
-  -moz-user-select: none;
-}
-
-.ruleview-fontswatch.active {
-  fill: var(--blue-50);
-}
-
 .ruleview-shapeswatch {
   background: url("chrome://devtools/skin/images/tool-shadereditor.svg");
   -moz-context-properties: fill;
   fill: var(--rule-shape-toggle-color);
   border-radius: 0;
   background-size: 1em;
   box-shadow: none;
 }
@@ -607,8 +593,39 @@
 }
 
 .ruleview-overridden-rule-filter {
   opacity: 0.8;
 }
 .ruleview-overridden-rule-filter:hover {
   opacity: 1;
 }
+
+.ruleview-rule:not(:hover) .ruleview-font-editor-toggle:not(.active) {
+  visibility: hidden;
+}
+
+.ruleview-font-editor-toggle {
+  position: absolute;
+  right: 1.5em;
+  bottom: 0.5em;
+  font-size: 11px;
+  font-weight: normal;
+  line-height: 11px;
+  border: 1px solid var(--rule-badge-border-color);
+  border-radius: 3px;
+  padding: 0px 4px;
+  -moz-user-select: none;
+  cursor: pointer;
+  background-color: var(--rule-badge-background-color);
+  color: var(--rule-badge-color);
+}
+
+.ruleview-font-editor-toggle:focus,
+.ruleview-font-editor-toggle:hover {
+  background-color: var(--rule-badge-hover-background-color);
+}
+
+.ruleview-font-editor-toggle.active {
+  background-color: var(--rule-badge-active-background-color);
+  border-color: var(--rule-badge-active-background-color);
+  color: var(--theme-selection-color);
+}
--- a/devtools/server/actors/highlighters/flexbox.js
+++ b/devtools/server/actors/highlighters/flexbox.js
@@ -251,29 +251,67 @@ class FlexboxHighlighter extends AutoRef
     gCachedFlexboxPattern.set(devicePixelRatio, flexboxPatternMap);
 
     return pattern;
   }
 
   /**
    * The AutoRefreshHighlighter's _hasMoved method returns true only if the
    * element's quads have changed. Override it so it also returns true if the
-   * element and its flex items have changed.
+   * flex container and its flex items have changed.
    */
   _hasMoved() {
     let hasMoved = AutoRefreshHighlighter.prototype._hasMoved.call(this);
 
-    // TODO: Implement a check of old and new flex container and flex items to react
-    // to any alignment and size changes. This is blocked on Bug 1414920 that implements
-    // a platform API to retrieve the flex container and flex item information.
+    if (!this.computedStyle) {
+      this.computedStyle = getComputedStyle(this.currentNode);
+    }
+
+    let oldFlexData = this.flexData;
+    this.flexData = this.currentNode.getAsFlexContainer();
+    let hasFlexDataChanged = compareFlexData(oldFlexData, this.flexData);
+
+    let oldAlignItems = this.alignItemsValue;
+    this.alignItemsValue = this.computedStyle.alignItems;
+    let newAlignItems = this.alignItemsValue;
+
+    let oldFlexBasis = this.flexBasis;
+    this.flexBasis = this.computedStyle.flexBasis;
+    let newFlexBasis = this.flexBasis;
 
-    return hasMoved;
+    let oldFlexDirection = this.flexDirection;
+    this.flexDirection = this.computedStyle.flexDirection;
+    let newFlexDirection = this.flexDirection;
+
+    let oldFlexWrap = this.flexWrap;
+    this.flexWrap = this.computedStyle.flexWrap;
+    let newFlexWrap = this.flexWrap;
+
+    let oldJustifyContent = this.justifyContentValue;
+    this.justifyContentValue = this.computedStyle.justifyContent;
+    let newJustifyContent = this.justifyContentValue;
+
+    return hasMoved ||
+           hasFlexDataChanged ||
+           oldAlignItems !== newAlignItems ||
+           oldFlexBasis !== newFlexBasis ||
+           oldFlexDirection !== newFlexDirection ||
+           oldFlexWrap !== newFlexWrap ||
+           oldJustifyContent !== newJustifyContent;
   }
 
   _hide() {
+    this.alignItemsValue = null;
+    this.computedStyle = null;
+    this.flexBasis = null;
+    this.flexData = null;
+    this.flexDirection = null;
+    this.flexWrap = null;
+    this.justifyContentValue = null;
+
     setIgnoreLayoutChanges(true);
     this._hideFlexbox();
     setIgnoreLayoutChanges(false, this.highlighterEnv.document.documentElement);
   }
 
   _hideFlexbox() {
     this.getElement("canvas").setAttribute("hidden", "true");
   }
@@ -319,43 +357,41 @@ class FlexboxHighlighter extends AutoRef
     this.clearCache();
 
     if (isTopLevel) {
       this.hide();
     }
   }
 
   renderAlignItemLine() {
-    if (!this.currentQuads.content || !this.currentQuads.content[0]) {
+    if (!this.flexData || !this.currentQuads.content || !this.currentQuads.content[0]) {
       return;
     }
 
     let { devicePixelRatio } = this.win;
     let lineWidth = getDisplayPixelRatio(this.win);
     let offset = (lineWidth / 2) % 1;
     let canvasX = Math.round(this._canvasPosition.x * devicePixelRatio);
     let canvasY = Math.round(this._canvasPosition.y * devicePixelRatio);
 
     this.ctx.save();
     this.ctx.translate(offset - canvasX, offset - canvasY);
     this.ctx.setLineDash(FLEXBOX_LINES_PROPERTIES.alignItems.lineDash);
     this.ctx.lineWidth = lineWidth * 3;
     this.ctx.strokeStyle = DEFAULT_COLOR;
 
     let { bounds } = this.currentQuads.content[0];
-    let flexLines = this.currentNode.getAsFlexContainer().getLines();
-    let computedStyle = getComputedStyle(this.currentNode);
-    let alignItemsType = computedStyle.getPropertyValue("align-items");
-    let isColumn = computedStyle.getPropertyValue("flex-direction").startsWith("column");
+    let flexLines = this.flexData.getLines();
+    let isColumn = this.flexDirection.startsWith("column");
     let options = { matrix: this.currentMatrix };
 
     for (let flexLine of flexLines) {
       let { crossStart, crossSize } = flexLine;
 
-      switch (alignItemsType) {
+      switch (this.alignItemsValue) {
         case "baseline":
         case "first baseline":
           let { firstBaselineOffset } = flexLine;
 
           if (firstBaselineOffset < 0) {
             break;
           }
 
@@ -482,55 +518,53 @@ class FlexboxHighlighter extends AutoRef
     this.ctx.stroke();
     this.ctx.restore();
   }
 
   /**
    * Renders the flex basis for a given flex item.
    */
   renderFlexItemBasis(flexItem, left, top, right, bottom, boundsWidth) {
-    let computedStyle = getComputedStyle(flexItem);
-
-    if (!computedStyle) {
+    if (!this.computedStyle) {
       return;
     }
 
-    let basis = computedStyle.getPropertyValue("flex-basis");
+    let basis = this.flexBasis;
 
     if (basis.endsWith("px")) {
       right = Math.round(left + parseFloat(basis));
     } else if (basis.endsWith("%")) {
       basis = parseFloat(basis) / 100 * boundsWidth;
       right = Math.round(left + basis);
     }
 
     this.ctx.fillStyle = BASIS_FILL_COLOR;
     drawRect(this.ctx, left, top, right, bottom, this.currentMatrix);
     this.ctx.fill();
   }
 
   renderFlexItems() {
-    if (!this.currentQuads.content || !this.currentQuads.content[0]) {
+    if (!this.flexData || !this.currentQuads.content || !this.currentQuads.content[0]) {
       return;
     }
 
     let { devicePixelRatio } = this.win;
     let lineWidth = getDisplayPixelRatio(this.win);
     let offset = (lineWidth / 2) % 1;
     let canvasX = Math.round(this._canvasPosition.x * devicePixelRatio);
     let canvasY = Math.round(this._canvasPosition.y * devicePixelRatio);
 
     this.ctx.save();
     this.ctx.translate(offset - canvasX, offset - canvasY);
     this.ctx.setLineDash(FLEXBOX_LINES_PROPERTIES.item.lineDash);
     this.ctx.lineWidth = lineWidth;
     this.ctx.strokeStyle = DEFAULT_COLOR;
 
     let { bounds } = this.currentQuads.content[0];
-    let flexLines = this.currentNode.getAsFlexContainer().getLines();
+    let flexLines = this.flexData.getLines();
 
     for (let flexLine of flexLines) {
       let flexItems = flexLine.getItems();
 
       for (let flexItem of flexItems) {
         let { node } = flexItem;
         let quads = getAdjustedQuads(this.win, node, "border");
 
@@ -552,35 +586,34 @@ class FlexboxHighlighter extends AutoRef
         this.renderFlexItemBasis(node, left, top, right, bottom, bounds.width);
       }
     }
 
     this.ctx.restore();
   }
 
   renderFlexLines() {
-    if (!this.currentQuads.content || !this.currentQuads.content[0]) {
+    if (!this.flexData || !this.currentQuads.content || !this.currentQuads.content[0]) {
       return;
     }
 
     let { devicePixelRatio } = this.win;
     let lineWidth = getDisplayPixelRatio(this.win);
     let offset = (lineWidth / 2) % 1;
     let canvasX = Math.round(this._canvasPosition.x * devicePixelRatio);
     let canvasY = Math.round(this._canvasPosition.y * devicePixelRatio);
 
     this.ctx.save();
     this.ctx.translate(offset - canvasX, offset - canvasY);
     this.ctx.lineWidth = lineWidth;
     this.ctx.strokeStyle = DEFAULT_COLOR;
 
     let { bounds } = this.currentQuads.content[0];
-    let flexLines = this.currentNode.getAsFlexContainer().getLines();
-    let computedStyle = getComputedStyle(this.currentNode);
-    let isColumn = computedStyle.getPropertyValue("flex-direction").startsWith("column");
+    let flexLines = this.flexData.getLines();
+    let isColumn = this.flexDirection.startsWith("column");
     let options = { matrix: this.currentMatrix };
 
     for (let flexLine of flexLines) {
       let { crossStart, crossSize } = flexLine;
 
       if (isColumn) {
         clearRect(this.ctx, crossStart, 0, crossStart + crossSize, bounds.height,
           this.currentMatrix);
@@ -615,24 +648,23 @@ class FlexboxHighlighter extends AutoRef
         }
       }
     }
 
     this.ctx.restore();
   }
 
   renderJustifyContent() {
-    if (!this.currentQuads.content || !this.currentQuads.content[0]) {
+    if (!this.flexData || !this.currentQuads.content || !this.currentQuads.content[0]) {
       return;
     }
 
     let { bounds } = this.currentQuads.content[0];
-    let flexLines = this.currentNode.getAsFlexContainer().getLines();
-    let computedStyle = getComputedStyle(this.currentNode);
-    let isColumn = computedStyle.getPropertyValue("flex-direction").startsWith("column");
+    let flexLines = this.flexData.getLines();
+    let isColumn = this.flexDirection.startsWith("column");
 
     for (let flexLine of flexLines) {
       let { crossStart, crossSize } = flexLine;
       let flexItems = flexLine.getItems();
       let mainStart = 0;
 
       for (let flexItem of flexItems) {
         let { node } = flexItem;
@@ -703,9 +735,67 @@ class FlexboxHighlighter extends AutoRef
     root.setAttribute("style",
       `position: absolute; width: ${width}px; height: ${height}px; overflow: hidden`);
 
     setIgnoreLayoutChanges(false, this.highlighterEnv.document.documentElement);
     return true;
   }
 }
 
+/**
+ * Returns whether or not the flex data has changed.
+ *
+ * @param  {Flex} oldFlexData
+ *         The old Flex data object.
+ * @param  {Flex} newFlexData
+ *         The new Flex data object.
+ * @return {Boolean} true if the flex data has changed and false otherwise.
+ */
+function compareFlexData(oldFlexData, newFlexData) {
+  if (!oldFlexData || !newFlexData) {
+    return true;
+  }
+
+  const oldLines = oldFlexData.getLines();
+  const newLines = newFlexData.getLines();
+
+  if (oldLines.length !== newLines.length) {
+    return true;
+  }
+
+  for (let i = 0; i < oldLines.length; i++) {
+    let oldLine = oldLines[i];
+    let newLine = newLines[i];
+
+    if (oldLine.crossSize !== newLine.crossSize ||
+        oldLine.crossStart !== newLine.crossStart ||
+        oldLine.firstBaselineOffset !== newLine.firstBaselineOffset ||
+        oldLine.growthState !== newLine.growthState ||
+        oldLine.lastBaselineOffset !== newLine.lastBaselineOffset) {
+      return true;
+    }
+
+    let oldItems = oldLine.getItems();
+    let newItems = newLine.getItems();
+
+    if (oldItems.length !== newItems.length) {
+      return true;
+    }
+
+    for (let j = 0; j < oldItems.length; j++) {
+      let oldItem = oldItems[j];
+      let newItem = newItems[j];
+
+      if (oldItem.crossMaxSize !== newItem.crossMaxSize ||
+          oldItem.crossMinSize !== newItem.crossMinSize ||
+          oldItem.mainBaseSize !== newItem.mainBaseSize ||
+          oldItem.mainDeltaSize !== newItem.mainDeltaSize ||
+          oldItem.mainMaxSize !== newItem.mainMaxSize ||
+          oldItem.mainMinSize !== newItem.mainMinSize) {
+        return true;
+      }
+    }
+  }
+
+  return false;
+}
+
 exports.FlexboxHighlighter = FlexboxHighlighter;
--- a/devtools/shared/css/generated/properties-db.js
+++ b/devtools/shared/css/generated/properties-db.js
@@ -5925,17 +5925,19 @@ exports.CSS_PROPERTIES = {
       "unset"
     ]
   },
   "font-stretch": {
     "isInherited": true,
     "subproperties": [
       "font-stretch"
     ],
-    "supports": [],
+    "supports": [
+      8
+    ],
     "values": [
       "condensed",
       "expanded",
       "extra-condensed",
       "extra-expanded",
       "inherit",
       "initial",
       "normal",
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -162,20 +162,16 @@ DOMInterfaces = {
 'Coordinates': {
     'headerFile': 'nsGeoPosition.h'
 },
 
 'Crypto' : {
     'headerFile': 'Crypto.h'
 },
 
-'CSS': {
-    'concrete': False,
-},
-
 'CSS2Properties': {
     'nativeType': 'nsDOMCSSDeclaration'
 },
 
 'CSSConditionRule': {
     'concrete': False,
     'nativeType': 'mozilla::css::ConditionRule',
     'headerFile': 'mozilla/css/GroupRule.h',
--- a/dom/indexedDB/test/browser.ini
+++ b/dom/indexedDB/test/browser.ini
@@ -9,16 +9,18 @@ support-files =
   browserHelpers.js
   browser_permissionsPrompt.html
   browser_permissionsSharedWorker.html
   browser_permissionsSharedWorker.js
   browser_permissionsWorker.html
   browser_permissionsWorker.js
   bug839193.js
   bug839193.xul
+  page_private_idb.html
 
 [browser_forgetThisSite.js]
 [browser_permissionsPromptAllow.js]
 [browser_permissionsPromptDeny.js]
 [browser_permissionsPromptWorker.js]
 [browser_perwindow_privateBrowsing.js]
 skip-if = os == 'linux' && debug # bug 1394671
+[browser_private_idb.js]
 [browser_bug839193.js]
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/browser_private_idb.js
@@ -0,0 +1,157 @@
+async function idbCheckFunc() {
+  let factory;
+  try {
+    // in a worker, this resolves directly.
+    factory = indexedDB;
+  } catch (ex) {
+    // in a frame-script, we need to pierce "content"
+    // eslint-disable-next-line mozilla/no-cpows-in-tests
+    factory = content.indexedDB;
+  }
+  try {
+    console.log("opening db");
+    const req = factory.open("db", 1);
+    const result = await new Promise((resolve, reject) => {
+      req.onerror = () => { resolve("error"); };
+      // we expect the db to not exist and for created to resolve first
+      req.onupgradeneeded = () => { resolve("created"); };
+      // ...so this will lose the race
+      req.onsuccess = (event) => {
+        resolve("already-exists");
+      };
+    });
+    const db = req.result;
+    console.log("db req completed:", result);
+    if (result !== "error") {
+      db.close();
+      console.log("deleting database");
+      await new Promise((resolve, reject) => {
+        const delReq = factory.deleteDatabase("db");
+        delReq.onerror = reject;
+        delReq.onsuccess = resolve;
+      });
+      console.log("deleted database");
+    }
+    return result;
+  } catch (ex) {
+    console.error("received error:", ex);
+    return "exception";
+  }
+}
+
+async function workerDriverFunc() {
+  const resultPromise = idbCheckFunc();
+  /* eslint-env worker */
+  // (SharedWorker)
+  if (!("postMessage" in self)) {
+    addEventListener("connect", function(evt) {
+      const port = evt.ports[0];
+      resultPromise.then((result) => {
+        console.log("worker test completed, postMessage-ing result:", result);
+        port.postMessage({ idbResult: result });
+      });
+    });
+  }
+  const result = await resultPromise;
+  // (DedicatedWorker)
+  if ("postMessage" in self) {
+    console.log("worker test completed, postMessage-ing result:", result);
+    postMessage({ idbResult: result });
+  }
+}
+
+const workerScript = `
+${idbCheckFunc.toSource()}
+(${workerDriverFunc.toSource()})();
+`;
+const workerScriptBlob = new Blob([workerScript]);
+
+/**
+ * This function is deployed via ContextTask.spawn and operates in a tab
+ * frame script context.  Its job is to create the worker that will run the
+ * idbCheckFunc and return the result to us.
+ */
+async function workerCheckDeployer({ srcBlob, workerType }) {
+  let worker, port;
+  // eslint-disable-next-line mozilla/no-cpows-in-tests
+  const url = content.URL.createObjectURL(srcBlob);
+  if (workerType === "dedicated") {
+    worker = new content.Worker(url);
+    port = worker;
+  } else if (workerType === "shared") {
+    worker = new content.SharedWorker(url);
+    port = worker.port;
+    port.start();
+  } else {
+    throw new Error("bad worker type!");
+  }
+
+  const result = await new Promise((resolve, reject) => {
+    port.addEventListener("message", function(evt) {
+      resolve(evt.data.idbResult);
+    }, { once: true });
+    worker.addEventListener("error", function(evt) {
+      console.error("worker problem:", evt);
+      reject(evt);
+    });
+  });
+  console.log("worker completed test with result:", result);
+
+  return result;
+}
+
+function checkTabWindowIDB(tab) {
+  return ContentTask.spawn(tab.linkedBrowser, null, idbCheckFunc);
+}
+
+async function checkTabDedicatedWorkerIDB(tab) {
+  return ContentTask.spawn(
+    tab.linkedBrowser,
+    {
+      srcBlob: workerScriptBlob,
+      workerType: "dedicated"
+    },
+    workerCheckDeployer);
+}
+
+async function checkTabSharedWorkerIDB(tab) {
+  return ContentTask.spawn(
+    tab.linkedBrowser,
+    {
+      srcBlob: workerScriptBlob,
+      workerType: "shared"
+    },
+    workerCheckDeployer);
+}
+
+add_task(async function() {
+  const pageUrl =
+    "http://example.com/browser/dom/indexedDB/test/page_private_idb.html";
+
+  let normalWin = await BrowserTestUtils.openNewBrowserWindow();
+  let privateWin =
+    await BrowserTestUtils.openNewBrowserWindow({private: true});
+
+  let normalTab =
+    await BrowserTestUtils.openNewForegroundTab(normalWin.gBrowser, pageUrl);
+  let privateTab =
+    await BrowserTestUtils.openNewForegroundTab(privateWin.gBrowser, pageUrl);
+
+  is(await checkTabWindowIDB(normalTab), "created",
+     "IndexedDB works in a non-private-browsing page.");
+  is(await checkTabWindowIDB(privateTab), "error",
+     "IndexedDB does not work in a private-browsing page.");
+
+  is(await checkTabDedicatedWorkerIDB(normalTab), "created",
+     "IndexedDB works in a non-private-browsing Worker.");
+  is(await checkTabDedicatedWorkerIDB(privateTab), "error",
+     "IndexedDB does not work in a private-browsing Worker.");
+
+  is(await checkTabSharedWorkerIDB(normalTab), "created",
+     "IndexedDB works in a non-private-browsing SharedWorker.");
+  is(await checkTabSharedWorkerIDB(privateTab), "error",
+     "IndexedDB does not work in a private-browsing SharedWorker.");
+
+  await BrowserTestUtils.closeWindow(normalWin);
+  await BrowserTestUtils.closeWindow(privateWin);
+});
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/page_private_idb.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<html>
+<head>
+  <meta charset="utf-8">
+</head>
+<body>
+  All the interesting stuff happens in ContentTask.spawn() calls.
+</body>
+</html>
--- a/dom/interfaces/base/nsIServiceWorkerManager.idl
+++ b/dom/interfaces/base/nsIServiceWorkerManager.idl
@@ -113,29 +113,16 @@ interface nsIServiceWorkerManagerListene
 
   void onUnregister(in nsIServiceWorkerRegistrationInfo aInfo);
 };
 
 [scriptable, builtinclass, uuid(7404c8e8-4d47-4449-8ed1-47d1261d4e33)]
 interface nsIServiceWorkerManager : nsISupports
 {
   /**
-   * Registers a ServiceWorker with script loaded from `aScriptURI` to act as
-   * the ServiceWorker for aScope.  Requires a valid entry settings object on
-   * the stack. This means you must call this from content code 'within'
-   * a window.
-   *
-   * Returns a Promise.
-   */
-  nsISupports register(in mozIDOMWindow aWindow,
-                       in nsIURI aScope,
-                       in nsIURI aScriptURI,
-                       in uint16_t aUpdateViaCache);
-
-  /**
    * Unregister an existing ServiceWorker registration for `aScope`.
    * It keeps aCallback alive until the operation is concluded.
    */
   void unregister(in nsIPrincipal aPrincipal,
                   in nsIServiceWorkerUnregisterCallback aCallback,
                   in DOMString aScope);
 
   nsIServiceWorkerRegistrationInfo getRegistrationByPrincipal(in nsIPrincipal aPrincipal,
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -3434,30 +3434,27 @@ ContentChild::RecvGetFilesResponse(const
     child->Finished(succeeded ? NS_OK : NS_ERROR_OUT_OF_MEMORY);
   }
 
   mGetFilesPendingRequests.Remove(aUUID);
   return IPC_OK();
 }
 
 /* static */ void
-ContentChild::FatalErrorIfNotUsingGPUProcess(const char* const aProtocolName,
-                                             const char* const aErrorMsg,
+ContentChild::FatalErrorIfNotUsingGPUProcess(const char* const aErrorMsg,
                                              base::ProcessId aOtherPid)
 {
   // If we're communicating with the same process or the UI process then we
   // want to crash normally. Otherwise we want to just warn as the other end
   // must be the GPU process and it crashing shouldn't be fatal for us.
   if (aOtherPid == base::GetCurrentProcId() ||
       (GetSingleton() && GetSingleton()->OtherPid() == aOtherPid)) {
-    mozilla::ipc::FatalError(aProtocolName, aErrorMsg, false);
+    mozilla::ipc::FatalError(aErrorMsg, false);
   } else {
-    nsAutoCString formattedMessage("IPDL error [");
-    formattedMessage.AppendASCII(aProtocolName);
-    formattedMessage.AppendLiteral(R"(]: ")");
+    nsAutoCString formattedMessage("IPDL error: \"");
     formattedMessage.AppendASCII(aErrorMsg);
     formattedMessage.AppendLiteral(R"(".)");
     NS_WARNING(formattedMessage.get());
   }
 }
 
 PURLClassifierChild*
 ContentChild::AllocPURLClassifierChild(const Principal& aPrincipal,
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -678,18 +678,17 @@ public:
   }
 
   /**
    * Helper function for protocols that use the GPU process when available.
    * Overrides FatalError to just be a warning when communicating with the
    * GPU process since we don't want to crash the content process when the
    * GPU process crashes.
    */
-  static void FatalErrorIfNotUsingGPUProcess(const char* const aProtocolName,
-                                             const char* const aErrorMsg,
+  static void FatalErrorIfNotUsingGPUProcess(const char* const aErrorMsg,
                                              base::ProcessId aOtherPid);
 
   // This method is used by FileCreatorHelper for the creation of a BlobImpl.
   void
   FileCreationRequest(nsID& aUUID, FileCreatorHelper* aHelper,
                       const nsAString& aFullPath, const nsAString& aType,
                       const nsAString& aName,
                       const Optional<int64_t>& aLastModified,
--- a/dom/mathml/nsMathMLElement.cpp
+++ b/dom/mathml/nsMathMLElement.cpp
@@ -691,22 +691,22 @@ nsMathMLElement::MapMathMLAttributesInto
   if (value) {
     WarnDeprecated(nsGkAtoms::fontstyle_->GetUTF16String(),
                    nsGkAtoms::mathvariant_->GetUTF16String(),
                    aData->Document());
     if (value->Type() == nsAttrValue::eString &&
         !aData->PropertyIsSet(eCSSProperty_font_style)) {
       nsAutoString str(value->GetStringValue());
       str.CompressWhitespace();
+      // FIXME(emilio): This should use FontSlantStyle or what not. Or even
+      // better, it looks deprecated since forever, we should just kill it.
       if (str.EqualsASCII("normal")) {
-        aData->SetKeywordValue(eCSSProperty_font_style,
-                               NS_STYLE_FONT_STYLE_NORMAL);
+        aData->SetKeywordValue(eCSSProperty_font_style, NS_FONT_STYLE_NORMAL);
       } else if (str.EqualsASCII("italic")) {
-        aData->SetKeywordValue(eCSSProperty_font_style,
-                               NS_STYLE_FONT_STYLE_ITALIC);
+        aData->SetKeywordValue(eCSSProperty_font_style, NS_FONT_STYLE_ITALIC);
       }
     }
   }
 
   // fontweight
   //
   // "Specified the font weight for the token. Deprecated in favor of
   // mathvariant."
--- a/dom/media/ipc/VideoDecoderManagerChild.cpp
+++ b/dom/media/ipc/VideoDecoderManagerChild.cpp
@@ -274,15 +274,15 @@ VideoDecoderManagerChild::DeallocateSurf
         if (ref->CanSend()) {
           ref->SendDeallocateSurfaceDescriptorGPUVideo(sd);
         }
       }),
     NS_DISPATCH_NORMAL);
 }
 
 void
-VideoDecoderManagerChild::HandleFatalError(const char* aName, const char* aMsg) const
+VideoDecoderManagerChild::HandleFatalError(const char* aMsg) const
 {
-  dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aName, aMsg, OtherPid());
+  dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aMsg, OtherPid());
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/media/ipc/VideoDecoderManagerChild.h
+++ b/dom/media/ipc/VideoDecoderManagerChild.h
@@ -63,17 +63,17 @@ public:
   bool CanSend();
 
 protected:
   void InitIPDL();
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
   void DeallocPVideoDecoderManagerChild() override;
 
-  void HandleFatalError(const char* aName, const char* aMsg) const override;
+  void HandleFatalError(const char* aMsg) const override;
 
   PVideoDecoderChild* AllocPVideoDecoderChild(const VideoInfo& aVideoInfo,
                                               const float& aFramerate,
                                               const layers::TextureFactoryIdentifier& aIdentifier,
                                               bool* aSuccess,
                                               nsCString* aBlacklistedD3D11Driver,
                                               nsCString* aBlacklistedD3D9Driver,
                                               nsCString* aErrorDescription) override;
--- a/dom/security/nsContentSecurityManager.cpp
+++ b/dom/security/nsContentSecurityManager.cpp
@@ -800,16 +800,17 @@ nsContentSecurityManager::CheckChannel(n
       (securityMode == nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED)) {
     rv = DoSOPChecks(uri, loadInfo, aChannel);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   if ((securityMode == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS) ||
       (securityMode == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL)) {
     if (NS_HasBeenCrossOrigin(aChannel)) {
+      NS_ENSURE_FALSE(loadInfo->GetDontFollowRedirects(), NS_ERROR_DOM_BAD_URI);
       loadInfo->MaybeIncreaseTainting(LoadTainting::Opaque);
     }
     // Please note that DoCheckLoadURIChecks should only be enforced for
     // cross origin requests. If the flag SEC_REQUIRE_CORS_DATA_INHERITS is set
     // within the loadInfo, then then CheckLoadURIWithPrincipal is performed
     // within nsCorsListenerProxy
     rv = DoCheckLoadURIChecks(uri, loadInfo);
     NS_ENSURE_SUCCESS(rv, rv);
--- a/dom/serviceworkers/ServiceWorkerContainer.cpp
+++ b/dom/serviceworkers/ServiceWorkerContainer.cpp
@@ -1,28 +1,31 @@
 /* -*- 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 "ServiceWorkerContainer.h"
 
+#include "nsContentPolicyUtils.h"
+#include "nsContentSecurityManager.h"
 #include "nsContentUtils.h"
 #include "nsIDocument.h"
 #include "nsIServiceWorkerManager.h"
 #include "nsIScriptError.h"
 #include "nsIURL.h"
 #include "nsNetUtil.h"
 #include "nsPIDOMWindow.h"
 #include "mozilla/Services.h"
 
 #include "nsCycleCollectionParticipant.h"
 #include "nsServiceManagerUtils.h"
 
+#include "mozilla/LoadInfo.h"
 #include "mozilla/dom/DOMPrefs.h"
 #include "mozilla/dom/Navigator.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ServiceWorker.h"
 #include "mozilla/dom/ServiceWorkerContainerBinding.h"
 
 #include "ServiceWorker.h"
 #include "ServiceWorkerContainerImpl.h"
@@ -76,24 +79,23 @@ ServiceWorkerContainer::ServiceWorkerCon
   Maybe<ServiceWorkerDescriptor> controller = aGlobal->GetController();
   if (controller.isSome()) {
     mControllerWorker = aGlobal->GetOrCreateServiceWorker(controller.ref());
   }
 }
 
 ServiceWorkerContainer::~ServiceWorkerContainer()
 {
-  mReadyPromiseHolder.DisconnectIfExists();
 }
 
 void
 ServiceWorkerContainer::DisconnectFromOwner()
 {
   mControllerWorker = nullptr;
-  mReadyPromiseHolder.DisconnectIfExists();
+  mReadyPromise = nullptr;
   DOMEventTargetHelper::DisconnectFromOwner();
 }
 
 void
 ServiceWorkerContainer::ControllerChanged(ErrorResult& aRv)
 {
   nsCOMPtr<nsIGlobalObject> go = GetParentObject();
   if (!go) {
@@ -105,78 +107,113 @@ ServiceWorkerContainer::ControllerChange
 }
 
 JSObject*
 ServiceWorkerContainer::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return ServiceWorkerContainerBinding::Wrap(aCx, this, aGivenProto);
 }
 
-static nsresult
-CheckForSlashEscapedCharsInPath(nsIURI* aURI)
-{
-  MOZ_ASSERT(aURI);
+namespace {
 
-  // A URL that can't be downcast to a standard URL is an invalid URL and should
-  // be treated as such and fail with SecurityError.
-  nsCOMPtr<nsIURL> url(do_QueryInterface(aURI));
-  if (NS_WARN_IF(!url)) {
-    return NS_ERROR_DOM_SECURITY_ERR;
+already_AddRefed<nsIURI>
+GetBaseURIFromGlobal(nsIGlobalObject* aGlobal, ErrorResult& aRv)
+{
+  // It would be nice not to require a window here, but right
+  // now we don't have a great way to get the base URL just
+  // from the nsIGlobalObject.
+  nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal);
+  if (!window) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return nullptr;
+  }
+
+  nsIDocument* doc = window->GetExtantDoc();
+  if (!doc) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return nullptr;
+  }
+
+  nsCOMPtr<nsIURI> baseURI = doc->GetDocBaseURI();
+  if (!baseURI) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return nullptr;
   }
 
-  nsAutoCString path;
-  nsresult rv = url->GetFilePath(path);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
+  return baseURI.forget();
+}
+
+// This function implements parts of the step 3 of the following algorithm:
+// https://w3c.github.io/webappsec/specs/powerfulfeatures/#settings-secure
+static bool
+IsFromAuthenticatedOrigin(nsIDocument* aDoc)
+{
+  MOZ_ASSERT(aDoc);
+  nsCOMPtr<nsIDocument> doc(aDoc);
+  nsCOMPtr<nsIContentSecurityManager> csm = do_GetService(NS_CONTENTSECURITYMANAGER_CONTRACTID);
+  if (NS_WARN_IF(!csm)) {
+    return false;
   }
 
-  ToLowerCase(path);
-  if (path.Find("%2f") != kNotFound ||
-      path.Find("%5c") != kNotFound) {
-    return NS_ERROR_DOM_TYPE_ERR;
+  while (doc && !nsContentUtils::IsChromeDoc(doc)) {
+    bool trustworthyOrigin = false;
+
+    // The origin of the document may be different from the document URI
+    // itself.  Check the principal, not the document URI itself.
+    nsCOMPtr<nsIPrincipal> documentPrincipal = doc->NodePrincipal();
+
+    // The check for IsChromeDoc() above should mean we never see a system
+    // principal inside the loop.
+    MOZ_ASSERT(!nsContentUtils::IsSystemPrincipal(documentPrincipal));
+
+    csm->IsOriginPotentiallyTrustworthy(documentPrincipal, &trustworthyOrigin);
+    if (!trustworthyOrigin) {
+      return false;
+    }
+
+    doc = doc->GetParentDocument();
   }
+  return true;
+}
 
-  return NS_OK;
-}
+} // anonymous namespace
 
 already_AddRefed<Promise>
 ServiceWorkerContainer::Register(const nsAString& aScriptURL,
                                  const RegistrationOptions& aOptions,
                                  ErrorResult& aRv)
 {
-  nsCOMPtr<nsISupports> promise;
-
-  nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
-  if (!swm) {
-    aRv.Throw(NS_ERROR_FAILURE);
+  // Note, we can't use GetGlobalIfValid() from the start here.  If we
+  // hit a storage failure we want to log a message with the final
+  // scope string we put together below.
+  nsIGlobalObject* global = GetParentObject();
+  if (!global) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return nullptr;
   }
 
-  nsCOMPtr<nsIURI> baseURI;
-  nsCOMPtr<nsPIDOMWindowInner> window = GetOwner();
-  if (window) {
-    baseURI = window->GetDocBaseURI();
-  } else {
+  Maybe<ClientInfo> clientInfo = global->GetClientInfo();
+  if (clientInfo.isNothing()) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return nullptr;
   }
 
+  nsCOMPtr<nsIURI> baseURI = GetBaseURIFromGlobal(global, aRv);
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+
   nsresult rv;
   nsCOMPtr<nsIURI> scriptURI;
   rv = NS_NewURI(getter_AddRefs(scriptURI), aScriptURL, nullptr, baseURI);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     aRv.ThrowTypeError<MSG_INVALID_URL>(aScriptURL);
     return nullptr;
   }
 
-  aRv = CheckForSlashEscapedCharsInPath(scriptURI);
-  if (NS_WARN_IF(aRv.Failed())) {
-    return nullptr;
-  }
-
   // In ServiceWorkerContainer.register() the scope argument is parsed against
   // different base URLs depending on whether it was passed or not.
   nsCOMPtr<nsIURI> scopeURI;
 
   // Step 4. If none passed, parse against script's URL
   if (!aOptions.mScope.WasPassed()) {
     NS_NAMED_LITERAL_STRING(defaultScope, "./");
     rv = NS_NewURI(getter_AddRefs(scopeURI), defaultScope,
@@ -195,48 +232,173 @@ ServiceWorkerContainer::Register(const n
     if (NS_WARN_IF(NS_FAILED(rv))) {
       nsIURI* uri = baseURI ? baseURI : scriptURI;
       nsAutoCString spec;
       uri->GetSpec(spec);
       NS_ConvertUTF8toUTF16 wSpec(spec);
       aRv.ThrowTypeError<MSG_INVALID_SCOPE>(aOptions.mScope.Value(), wSpec);
       return nullptr;
     }
+  }
 
-    aRv = CheckForSlashEscapedCharsInPath(scopeURI);
-    if (NS_WARN_IF(aRv.Failed())) {
-      return nullptr;
-    }
+  // Strip the any ref from both the script and scope URLs.
+  nsCOMPtr<nsIURI> cloneWithoutRef;
+  aRv = scriptURI->CloneIgnoringRef(getter_AddRefs(cloneWithoutRef));
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+  scriptURI = cloneWithoutRef.forget();
+
+  aRv = scopeURI->CloneIgnoringRef(getter_AddRefs(cloneWithoutRef));
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+  scopeURI = cloneWithoutRef.forget();
+
+  aRv = ServiceWorkerScopeAndScriptAreValid(clientInfo.ref(),
+                                            scopeURI,
+                                            scriptURI);
+  if (aRv.Failed()) {
+    return nullptr;
   }
 
-  // The spec says that the "client" passed to Register() must be the global
-  // where the ServiceWorkerContainer was retrieved from.
-  aRv = swm->Register(GetOwner(), scopeURI, scriptURI,
-                      static_cast<uint16_t>(aOptions.mUpdateViaCache),
-                      getter_AddRefs(promise));
-  if (NS_WARN_IF(aRv.Failed())) {
+  nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(global);
+  if (!window) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return nullptr;
+  }
+
+  nsIDocument* doc = window->GetExtantDoc();
+  if (!doc) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return nullptr;
+  }
+
+  // Next, implement a lame version of [SecureContext] with an
+  // exception based on a pref or devtools option.
+  // TODO: This logic should be moved to a webidl [Func]. See bug 1455078.
+  nsCOMPtr<nsPIDOMWindowOuter> outerWindow = window->GetOuterWindow();
+  bool serviceWorkersTestingEnabled =
+    outerWindow->GetServiceWorkersTestingEnabled();
+
+  bool authenticatedOrigin;
+  if (DOMPrefs::ServiceWorkersTestingEnabled() ||
+      serviceWorkersTestingEnabled) {
+    authenticatedOrigin = true;
+  } else {
+    authenticatedOrigin = IsFromAuthenticatedOrigin(doc);
+  }
+
+  if (!authenticatedOrigin) {
+    NS_WARNING("ServiceWorker registration from insecure websites is not allowed.");
+    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return nullptr;
   }
 
-  RefPtr<Promise> ret = static_cast<Promise*>(promise.get());
-  MOZ_ASSERT(ret);
-  return ret.forget();
+  // The next section of code executes an NS_CheckContentLoadPolicy()
+  // check.  This is necessary to enforce the CSP of the calling client.
+  // Currently this requires an nsIDocument.  Once bug 965637 lands we
+  // should try to move this into ServiceWorkerScopeAndScriptAreValid()
+  // using the ClientInfo instead of doing a window-specific check here.
+  // See bug 1455077 for further investigation.
+  nsCOMPtr<nsILoadInfo> secCheckLoadInfo =
+    new LoadInfo(doc->NodePrincipal(), // loading principal
+                 doc->NodePrincipal(), // triggering principal
+                 doc,                  // loading node
+                 nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK,
+                 nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER);
+
+  // Check content policy.
+  int16_t decision = nsIContentPolicy::ACCEPT;
+  rv = NS_CheckContentLoadPolicy(scriptURI,
+                                 secCheckLoadInfo,
+                                 NS_LITERAL_CSTRING("application/javascript"),
+                                 &decision);
+  if (NS_FAILED(rv)) {
+    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
+    return nullptr;
+
+  }
+  if (NS_WARN_IF(decision != nsIContentPolicy::ACCEPT)) {
+    aRv.Throw(NS_ERROR_CONTENT_BLOCKED);
+    return nullptr;
+  }
+
+  // Get the string representation for both the script and scope since
+  // we sanitized them above.
+  nsCString cleanedScopeURL;
+  aRv = scopeURI->GetSpec(cleanedScopeURL);
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+
+  nsCString cleanedScriptURL;
+  aRv = scriptURI->GetSpec(cleanedScriptURL);
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+
+  // Verify that the global is valid and has permission to store
+  // data.  We perform this late so that we can report the final
+  // scope URL in any error message.
+  Unused << GetGlobalIfValid(aRv, [&](nsIDocument* aDoc) {
+    NS_ConvertUTF8toUTF16 reportScope(cleanedScopeURL);
+    const char16_t* param[] = { reportScope.get() };
+    nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
+                                    NS_LITERAL_CSTRING("Service Workers"),
+                                    aDoc, nsContentUtils::eDOM_PROPERTIES,
+                                    "ServiceWorkerRegisterStorageError",
+                                    param, 1);
+  });
+
+  window->NoteCalledRegisterForServiceWorkerScope(cleanedScopeURL);
+
+  RefPtr<Promise> outer = Promise::Create(global, aRv);
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+
+  RefPtr<ServiceWorkerContainer> self = this;
+
+  mInner->Register(clientInfo.ref(), cleanedScopeURL, cleanedScriptURL,
+                   aOptions.mUpdateViaCache)->Then(
+    global->EventTargetFor(TaskCategory::Other), __func__,
+    [self, outer] (const ServiceWorkerRegistrationDescriptor& aDesc) {
+      ErrorResult rv;
+      nsIGlobalObject* global = self->GetGlobalIfValid(rv);
+      if (rv.Failed()) {
+        outer->MaybeReject(rv);
+        return;
+      }
+      RefPtr<ServiceWorkerRegistration> reg =
+        global->GetOrCreateServiceWorkerRegistration(aDesc);
+      outer->MaybeResolve(reg);
+    }, [self, outer] (ErrorResult&& aRv) {
+      outer->MaybeReject(aRv);
+    });
+
+  return outer.forget();
 }
 
 already_AddRefed<ServiceWorker>
 ServiceWorkerContainer::GetController()
 {
   RefPtr<ServiceWorker> ref = mControllerWorker;
   return ref.forget();
 }
 
 already_AddRefed<Promise>
 ServiceWorkerContainer::GetRegistrations(ErrorResult& aRv)
 {
-  nsIGlobalObject* global = GetGlobalIfValid(aRv);
+  nsIGlobalObject* global = GetGlobalIfValid(aRv, [](nsIDocument* aDoc) {
+    nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
+                                    NS_LITERAL_CSTRING("Service Workers"), aDoc,
+                                    nsContentUtils::eDOM_PROPERTIES,
+                                    "ServiceWorkerGetRegistrationStorageError");
+  });
   if (aRv.Failed()) {
     return nullptr;
   }
 
   Maybe<ClientInfo> clientInfo = global->GetClientInfo();
   if (clientInfo.isNothing()) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return nullptr;
@@ -262,98 +424,82 @@ ServiceWorkerContainer::GetRegistrations
       for (auto& desc : aDescList) {
         RefPtr<ServiceWorkerRegistration> reg =
           global->GetOrCreateServiceWorkerRegistration(desc);
         if (reg) {
           regList.AppendElement(Move(reg));
         }
       }
       outer->MaybeResolve(regList);
-    }, [self, outer] (nsresult aRv) {
+    }, [self, outer] (ErrorResult&& aRv) {
       outer->MaybeReject(aRv);
     });
 
   return outer.forget();
 }
 
 already_AddRefed<Promise>
 ServiceWorkerContainer::GetRegistration(const nsAString& aURL,
                                         ErrorResult& aRv)
 {
-  nsIGlobalObject* global = GetGlobalIfValid(aRv);
+  nsIGlobalObject* global = GetGlobalIfValid(aRv, [](nsIDocument* aDoc) {
+    nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
+                                    NS_LITERAL_CSTRING("Service Workers"), aDoc,
+                                    nsContentUtils::eDOM_PROPERTIES,
+                                    "ServiceWorkerGetRegistrationStorageError");
+  });
   if (aRv.Failed()) {
     return nullptr;
   }
 
-  nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(global);
-  if (!window) {
-    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return nullptr;
-  }
-
-  // It would be nice not to require a window here, but right
-  // now we don't have a great way to get the base URL just
-  // from the nsIGlobalObject.
-  Maybe<ClientInfo> clientInfo = window->GetClientInfo();
+  Maybe<ClientInfo> clientInfo = global->GetClientInfo();
   if (clientInfo.isNothing()) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return nullptr;
   }
 
-  nsIDocument* doc = window->GetExtantDoc();
-  if (!doc) {
-    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return nullptr;
-  }
-
-  nsCOMPtr<nsIURI> baseURI = doc->GetDocBaseURI();
-  if (!baseURI) {
-    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+  nsCOMPtr<nsIURI> baseURI = GetBaseURIFromGlobal(global, aRv);
+  if (aRv.Failed()) {
     return nullptr;
   }
 
   nsCOMPtr<nsIURI> uri;
   aRv = NS_NewURI(getter_AddRefs(uri), aURL, nullptr, baseURI);
   if (aRv.Failed()) {
     return nullptr;
   }
 
   nsCString spec;
   aRv = uri->GetSpec(spec);
   if (aRv.Failed()) {
     return nullptr;
   }
 
-  RefPtr<Promise> outer = Promise::Create(window->AsGlobal(), aRv);
+  RefPtr<Promise> outer = Promise::Create(global, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
 
   RefPtr<ServiceWorkerContainer> self = this;
 
   mInner->GetRegistration(clientInfo.ref(), spec)->Then(
-    window->EventTargetFor(TaskCategory::Other), __func__,
+    global->EventTargetFor(TaskCategory::Other), __func__,
     [self, outer] (const ServiceWorkerRegistrationDescriptor& aDescriptor) {
       ErrorResult rv;
       nsIGlobalObject* global = self->GetGlobalIfValid(rv);
       if (rv.Failed()) {
         outer->MaybeReject(rv);
         return;
       }
       RefPtr<ServiceWorkerRegistration> reg =
         global->GetOrCreateServiceWorkerRegistration(aDescriptor);
       outer->MaybeResolve(reg);
-    }, [self, outer] (nsresult aRv) {
-      ErrorResult rv;
-      Unused << self->GetGlobalIfValid(rv);
-      if (rv.Failed()) {
-        outer->MaybeReject(rv);
-        return;
-      }
-      if (NS_SUCCEEDED(aRv)) {
+    }, [self, outer] (ErrorResult&& aRv) {
+      Unused << self->GetGlobalIfValid(aRv);
+      if (!aRv.Failed()) {
         outer->MaybeResolveWithUndefined();
         return;
       }
       outer->MaybeReject(aRv);
     });
 
   return outer.forget();
 }
@@ -383,31 +529,29 @@ ServiceWorkerContainer::GetReady(ErrorRe
   }
 
   RefPtr<ServiceWorkerContainer> self = this;
   RefPtr<Promise> outer = mReadyPromise;
 
   mInner->GetReady(clientInfo.ref())->Then(
     global->EventTargetFor(TaskCategory::Other), __func__,
     [self, outer] (const ServiceWorkerRegistrationDescriptor& aDescriptor) {
-      self->mReadyPromiseHolder.Complete();
       ErrorResult rv;
       nsIGlobalObject* global = self->GetGlobalIfValid(rv);
       if (rv.Failed()) {
         outer->MaybeReject(rv);
         return;
       }
       RefPtr<ServiceWorkerRegistration> reg =
         global->GetOrCreateServiceWorkerRegistration(aDescriptor);
       NS_ENSURE_TRUE_VOID(reg);
       outer->MaybeResolve(reg);
-    }, [self, outer] (nsresult aRv) {
-      self->mReadyPromiseHolder.Complete();
+    }, [self, outer] (ErrorResult&& aRv) {
       outer->MaybeReject(aRv);
-    })->Track(mReadyPromiseHolder);
+    });
 
   return mReadyPromise;
 }
 
 // Testing only.
 void
 ServiceWorkerContainer::GetScopeForUrl(const nsAString& aUrl,
                                        nsString& aScope,
@@ -431,41 +575,51 @@ ServiceWorkerContainer::GetScopeForUrl(c
     return;
   }
 
   aRv = swm->GetScopeForUrl(doc->NodePrincipal(),
                             aUrl, aScope);
 }
 
 nsIGlobalObject*
-ServiceWorkerContainer::GetGlobalIfValid(ErrorResult& aRv) const
+ServiceWorkerContainer::GetGlobalIfValid(ErrorResult& aRv,
+                                         const std::function<void(nsIDocument*)>&& aStorageFailureCB) const
 {
   // For now we require a window since ServiceWorkerContainer is
   // not exposed on worker globals yet.  The main thing we need
   // to fix here to support that is the storage access check via
   // the nsIGlobalObject.
   nsPIDOMWindowInner* window = GetOwner();
   if (NS_WARN_IF(!window)) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return nullptr;
   }
 
+  nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
+  if (NS_WARN_IF(!doc)) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return nullptr;
+  }
+
   // Don't allow a service worker to access service worker registrations
   // from a window with storage disabled.  If these windows can access
   // the registration it increases the chance they can bypass the storage
   // block via postMessage(), etc.
   auto storageAllowed = nsContentUtils::StorageAllowedForWindow(window);
   if (NS_WARN_IF(storageAllowed != nsContentUtils::StorageAccess::eAllow)) {
-    nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
-    nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
-                                    NS_LITERAL_CSTRING("Service Workers"), doc,
-                                    nsContentUtils::eDOM_PROPERTIES,
-                                    "ServiceWorkerGetRegistrationStorageError");
+    if (aStorageFailureCB) {
+      aStorageFailureCB(doc);
+    }
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return nullptr;
   }
-  MOZ_ASSERT(!nsContentUtils::IsSystemPrincipal(window->GetExtantDoc()->NodePrincipal()));
+
+  // Don't allow service workers when the document is chrome.
+  if (NS_WARN_IF(nsContentUtils::IsSystemPrincipal(doc->NodePrincipal()))) {
+    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
+    return nullptr;
+  }
 
   return window->AsGlobal();
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/serviceworkers/ServiceWorkerContainer.h
+++ b/dom/serviceworkers/ServiceWorkerContainer.h
@@ -22,18 +22,20 @@ class ServiceWorker;
 // Lightweight serviceWorker APIs collection.
 class ServiceWorkerContainer final : public DOMEventTargetHelper
 {
 public:
   class Inner
   {
   public:
     virtual RefPtr<ServiceWorkerRegistrationPromise>
-    Register(const nsAString& aScriptURL,
-             const RegistrationOptions& aOptions) = 0;
+    Register(const ClientInfo& aClientInfo,
+             const nsACString& aScopeURL,
+             const nsACString& aScriptURL,
+             ServiceWorkerUpdateViaCache aUpdateViaCache) const = 0;
 
     virtual RefPtr<ServiceWorkerRegistrationPromise>
     GetRegistration(const ClientInfo& aClientInfo,
                     const nsACString& aURL) const = 0;
 
     virtual RefPtr<ServiceWorkerRegistrationListPromise>
     GetRegistrations(const ClientInfo& aClientInfo) const = 0;
 
@@ -89,18 +91,26 @@ public:
   ControllerChanged(ErrorResult& aRv);
 
 private:
   ServiceWorkerContainer(nsIGlobalObject* aGlobal,
                          already_AddRefed<ServiceWorkerContainer::Inner> aInner);
 
   ~ServiceWorkerContainer();
 
+  // Utility method to get the global if its present and if certain
+  // additional validaty checks pass.  One of these additional checks
+  // verifies the global can access storage.  Since storage access can
+  // vary based on user settings we want to often provide some error
+  // message if the storage check fails.  This method takes an optional
+  // callback that can be used to report the storage failure to the
+  // devtools console.
   nsIGlobalObject*
-  GetGlobalIfValid(ErrorResult& aRv) const;
+  GetGlobalIfValid(ErrorResult& aRv,
+                   const std::function<void(nsIDocument*)>&& aStorageFailureCB = nullptr) const;
 
   RefPtr<Inner> mInner;
 
   // This only changes when a worker hijacks everything in its scope by calling
   // claim.
   RefPtr<ServiceWorker> mControllerWorker;
 
   RefPtr<Promise> mReadyPromise;
--- a/dom/serviceworkers/ServiceWorkerContainerImpl.cpp
+++ b/dom/serviceworkers/ServiceWorkerContainerImpl.cpp
@@ -5,21 +5,28 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ServiceWorkerContainerImpl.h"
 
 namespace mozilla {
 namespace dom {
 
 RefPtr<ServiceWorkerRegistrationPromise>
-ServiceWorkerContainerImpl::Register(const nsAString& aScriptURL,
-                                     const RegistrationOptions& aOptions)
+ServiceWorkerContainerImpl::Register(const ClientInfo& aClientInfo,
+                                     const nsACString& aScopeURL,
+                                     const nsACString& aScriptURL,
+                                     ServiceWorkerUpdateViaCache aUpdateViaCache) const
 {
-  // TODO
-  return nullptr;
+  RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+  if (NS_WARN_IF(!swm)) {
+    return ServiceWorkerRegistrationPromise::CreateAndReject(NS_ERROR_DOM_INVALID_STATE_ERR,
+                                                             __func__);
+  }
+
+  return swm->Register(aClientInfo, aScopeURL, aScriptURL, aUpdateViaCache);
 }
 
 RefPtr<ServiceWorkerRegistrationPromise>
 ServiceWorkerContainerImpl::GetRegistration(const ClientInfo& aClientInfo,
                                             const nsACString& aURL) const
 {
   RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
   if (NS_WARN_IF(!swm)) {
--- a/dom/serviceworkers/ServiceWorkerContainerImpl.h
+++ b/dom/serviceworkers/ServiceWorkerContainerImpl.h
@@ -16,18 +16,20 @@ namespace dom {
 class ServiceWorkerContainerImpl final : public ServiceWorkerContainer::Inner
 {
   ~ServiceWorkerContainerImpl() = default;
 
 public:
   ServiceWorkerContainerImpl() = default;
 
   RefPtr<ServiceWorkerRegistrationPromise>
-  Register(const nsAString& aScriptURL,
-           const RegistrationOptions& aOptions) override;
+  Register(const ClientInfo& aClientInfo,
+           const nsACString& aScopeURL,
+           const nsACString& aScriptURL,
+           ServiceWorkerUpdateViaCache aUpdateViaCache) const override;
 
   RefPtr<ServiceWorkerRegistrationPromise>
   GetRegistration(const ClientInfo& aClientInfo,
                   const nsACString& aURL) const override;
 
   RefPtr<ServiceWorkerRegistrationListPromise>
   GetRegistrations(const ClientInfo& aClientInfo) const override;
 
--- a/dom/serviceworkers/ServiceWorkerManager.cpp
+++ b/dom/serviceworkers/ServiceWorkerManager.cpp
@@ -58,18 +58,16 @@
 #include "mozilla/dom/WorkerScope.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/PBackgroundChild.h"
 #include "mozilla/ipc/PBackgroundSharedTypes.h"
 #include "mozilla/dom/ScriptLoader.h"
 #include "mozilla/Unused.h"
 #include "mozilla/EnumSet.h"
 
-#include "nsContentPolicyUtils.h"
-#include "nsContentSecurityManager.h"
 #include "nsContentUtils.h"
 #include "nsNetUtil.h"
 #include "nsProxyRelease.h"
 #include "nsQueryObject.h"
 #include "nsTArray.h"
 
 #include "ServiceWorker.h"
 #include "ServiceWorkerContainer.h"
@@ -422,66 +420,51 @@ ServiceWorkerManager::MaybeStartShutdown
   RefPtr<TeardownRunnable> runnable = new TeardownRunnable(mActor);
   nsresult rv = NS_DispatchToMainThread(runnable);
   Unused << NS_WARN_IF(NS_FAILED(rv));
   mActor = nullptr;
 }
 
 class ServiceWorkerResolveWindowPromiseOnRegisterCallback final : public ServiceWorkerJob::Callback
 {
-  // The promise "returned" by the call to Update up to
-  // navigator.serviceWorker.register().
-  PromiseWindowProxy mPromise;
+  RefPtr<ServiceWorkerRegistrationPromise::Private> mPromise;
 
   ~ServiceWorkerResolveWindowPromiseOnRegisterCallback()
   {}
 
   virtual void
   JobFinished(ServiceWorkerJob* aJob, ErrorResult& aStatus) override
   {
     MOZ_ASSERT(NS_IsMainThread());
     MOZ_ASSERT(aJob);
-    RefPtr<Promise> promise = mPromise.Get();
-    if (!promise) {
-      return;
-    }
 
     if (aStatus.Failed()) {
-      promise->MaybeReject(aStatus);
-      return;
-    }
-
-    nsCOMPtr<nsPIDOMWindowInner> window = mPromise.GetWindow();
-    if (!window) {
+      mPromise->Reject(Move(aStatus), __func__);
       return;
     }
 
     MOZ_ASSERT(aJob->GetType() == ServiceWorkerJob::Type::Register);
     RefPtr<ServiceWorkerRegisterJob> registerJob =
       static_cast<ServiceWorkerRegisterJob*>(aJob);
     RefPtr<ServiceWorkerRegistrationInfo> reg = registerJob->GetRegistration();
 
-    RefPtr<ServiceWorkerRegistration> swr =
-      window->AsGlobal()->GetOrCreateServiceWorkerRegistration(reg->Descriptor());
-
-    nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
-      "ServiceWorkerResolveWindowPromiseOnRegisterCallback::JobFinished",
-      [promise = Move(promise), swr = Move(swr)] () {
-        promise->MaybeResolve(swr);
-      });
-    MOZ_ALWAYS_SUCCEEDS(
-      window->EventTargetFor(TaskCategory::Other)->Dispatch(r.forget()));
+    mPromise->Resolve(reg->Descriptor(), __func__);
   }
 
 public:
-  ServiceWorkerResolveWindowPromiseOnRegisterCallback(nsPIDOMWindowInner* aWindow,
-                                                      Promise* aPromise)
-    : mPromise(aWindow, aPromise)
+  ServiceWorkerResolveWindowPromiseOnRegisterCallback()
+    : mPromise(new ServiceWorkerRegistrationPromise::Private(__func__))
   {}
 
+  RefPtr<ServiceWorkerRegistrationPromise>
+  Promise() const
+  {
+    return mPromise;
+  }
+
   NS_INLINE_DECL_REFCOUNTING(ServiceWorkerResolveWindowPromiseOnRegisterCallback, override)
 };
 
 namespace {
 
 class PropagateSoftUpdateRunnable final : public Runnable
 {
 public:
@@ -745,209 +728,67 @@ private:
     }
   }
 
   RefPtr<GenericPromise::Private> mPromise;
 };
 
 } // namespace
 
-// This function implements parts of the step 3 of the following algorithm:
-// https://w3c.github.io/webappsec/specs/powerfulfeatures/#settings-secure
-static bool
-IsFromAuthenticatedOrigin(nsIDocument* aDoc)
+RefPtr<ServiceWorkerRegistrationPromise>
+ServiceWorkerManager::Register(const ClientInfo& aClientInfo,
+                               const nsACString& aScopeURL,
+                               const nsACString& aScriptURL,
+                               ServiceWorkerUpdateViaCache aUpdateViaCache)
 {
-  MOZ_ASSERT(aDoc);
-  nsCOMPtr<nsIDocument> doc(aDoc);
-  nsCOMPtr<nsIContentSecurityManager> csm = do_GetService(NS_CONTENTSECURITYMANAGER_CONTRACTID);
-  if (NS_WARN_IF(!csm)) {
-    return false;
-  }
-
-  while (doc && !nsContentUtils::IsChromeDoc(doc)) {
-    bool trustworthyOrigin = false;
-
-    // The origin of the document may be different from the document URI
-    // itself.  Check the principal, not the document URI itself.
-    nsCOMPtr<nsIPrincipal> documentPrincipal = doc->NodePrincipal();
-
-    // The check for IsChromeDoc() above should mean we never see a system
-    // principal inside the loop.
-    MOZ_ASSERT(!nsContentUtils::IsSystemPrincipal(documentPrincipal));
-
-    csm->IsOriginPotentiallyTrustworthy(documentPrincipal, &trustworthyOrigin);
-    if (!trustworthyOrigin) {
-      return false;
-    }
-
-    doc = doc->GetParentDocument();
-  }
-  return true;
-}
-
-// If we return an error code here, the ServiceWorkerContainer will
-// automatically reject the Promise.
-NS_IMETHODIMP
-ServiceWorkerManager::Register(mozIDOMWindow* aWindow,
-                               nsIURI* aScopeURI,
-                               nsIURI* aScriptURI,
-                               uint16_t aUpdateViaCache,
-                               nsISupports** aPromise)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  if (NS_WARN_IF(!aWindow)) {
-    return NS_ERROR_DOM_INVALID_STATE_ERR;
-  }
-
-  auto* window = nsPIDOMWindowInner::From(aWindow);
-
-  nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
-  MOZ_ASSERT(doc);
-
-  // Don't allow a service worker to be registered if storage is restricted
-  // for the window.
-  auto storageAllowed = nsContentUtils::StorageAllowedForWindow(window);
-  if (storageAllowed != nsContentUtils::StorageAccess::eAllow) {
-    NS_ConvertUTF8toUTF16 reportScope(aScopeURI->GetSpecOrDefault());
-    const char16_t* param[] = { reportScope.get() };
-    nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
-                                    NS_LITERAL_CSTRING("Service Workers"), doc,
-                                    nsContentUtils::eDOM_PROPERTIES,
-                                    "ServiceWorkerRegisterStorageError", param,
-                                    1);
-    return NS_ERROR_DOM_SECURITY_ERR;
-  }
-
-  // Don't allow service workers to register when the *document* is chrome.
-  if (NS_WARN_IF(nsContentUtils::IsSystemPrincipal(doc->NodePrincipal()))) {
-    return NS_ERROR_DOM_SECURITY_ERR;
+  nsCOMPtr<nsIURI> scopeURI;
+  nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScopeURL, nullptr, nullptr);
+  if (NS_FAILED(rv)) {
+    return ServiceWorkerRegistrationPromise::CreateAndReject(rv, __func__);
   }
 
-  nsCOMPtr<nsPIDOMWindowOuter> outerWindow = window->GetOuterWindow();
-  bool serviceWorkersTestingEnabled =
-    outerWindow->GetServiceWorkersTestingEnabled();
-
-  bool authenticatedOrigin;
-  if (DOMPrefs::ServiceWorkersTestingEnabled() ||
-      serviceWorkersTestingEnabled) {
-    authenticatedOrigin = true;
-  } else {
-    authenticatedOrigin = IsFromAuthenticatedOrigin(doc);
-  }
-
-  if (!authenticatedOrigin) {
-    NS_WARNING("ServiceWorker registration from insecure websites is not allowed.");
-    return NS_ERROR_DOM_SECURITY_ERR;
-  }
-
-  // Data URLs are not allowed.
-  nsCOMPtr<nsIPrincipal> documentPrincipal = doc->NodePrincipal();
-
-  nsresult rv = documentPrincipal->CheckMayLoad(aScriptURI, true /* report */,
-                                                false /* allowIfInheritsPrincipal */);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return NS_ERROR_DOM_SECURITY_ERR;
+  nsCOMPtr<nsIURI> scriptURI;
+  rv = NS_NewURI(getter_AddRefs(scriptURI), aScriptURL, nullptr, nullptr);
+  if (NS_FAILED(rv)) {
+    return ServiceWorkerRegistrationPromise::CreateAndReject(rv, __func__);
   }
 
-  nsCOMPtr<nsILoadInfo> secCheckLoadInfo =
-    new LoadInfo(documentPrincipal, // loading principal
-                 documentPrincipal, // triggering principal
-                 doc,
-                 nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK,
-                 nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER);
-
-  // Check content policy.
-  int16_t decision = nsIContentPolicy::ACCEPT;
-  rv = NS_CheckContentLoadPolicy(aScriptURI,
-                                 secCheckLoadInfo,
-                                 EmptyCString(),
-                                 &decision);
-  NS_ENSURE_SUCCESS(rv, rv);
-  if (NS_WARN_IF(decision != nsIContentPolicy::ACCEPT)) {
-    return NS_ERROR_CONTENT_BLOCKED;
-  }
-
-
-  rv = documentPrincipal->CheckMayLoad(aScopeURI, true /* report */,
-                                       false /* allowIfInheritsPrinciple */);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return NS_ERROR_DOM_SECURITY_ERR;
+  rv = ServiceWorkerScopeAndScriptAreValid(aClientInfo, scopeURI, scriptURI);
+  if (NS_FAILED(rv)) {
+    return ServiceWorkerRegistrationPromise::CreateAndReject(rv, __func__);
   }
 
-  // The IsOriginPotentiallyTrustworthy() check allows file:// and possibly other
-  // URI schemes.  We need to explicitly only allows http and https schemes.
-  // Note, we just use the aScriptURI here for the check since its already
-  // been verified as same origin with the document principal.  This also
-  // is a good block against accidentally allowing blob: script URIs which
-  // might inherit the origin.
-  bool isHttp = false;
-  bool isHttps = false;
-  aScriptURI->SchemeIs("http", &isHttp);
-  aScriptURI->SchemeIs("https", &isHttps);
-  if (NS_WARN_IF(!isHttp && !isHttps)) {
-    return NS_ERROR_DOM_SECURITY_ERR;
-  }
-
-  nsCString cleanedScope;
-  rv = aScopeURI->GetSpecIgnoringRef(cleanedScope);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return NS_ERROR_FAILURE;
-  }
-
-  nsAutoCString spec;
-  rv = aScriptURI->GetSpecIgnoringRef(spec);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  ErrorResult result;
-  RefPtr<Promise> promise = Promise::Create(window->AsGlobal(), result);
-  if (result.Failed()) {
-    return result.StealNSResult();
-  }
+  // If the previous validation step passed then we must have a principal.
+  nsCOMPtr<nsIPrincipal> principal = aClientInfo.GetPrincipal();
 
   nsAutoCString scopeKey;
-  rv = PrincipalToScopeKey(documentPrincipal, scopeKey);
+  rv = PrincipalToScopeKey(principal, scopeKey);
   if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
+    return ServiceWorkerRegistrationPromise::CreateAndReject(rv, __func__);
   }
 
-  window->NoteCalledRegisterForServiceWorkerScope(cleanedScope);
-
   RefPtr<ServiceWorkerJobQueue> queue = GetOrCreateJobQueue(scopeKey,
-                                                            cleanedScope);
+                                                            aScopeURL);
 
   RefPtr<ServiceWorkerResolveWindowPromiseOnRegisterCallback> cb =
-    new ServiceWorkerResolveWindowPromiseOnRegisterCallback(window, promise);
-
-  nsCOMPtr<nsILoadGroup> docLoadGroup = doc->GetDocumentLoadGroup();
-  RefPtr<WorkerLoadInfo::InterfaceRequestor> ir =
-    new WorkerLoadInfo::InterfaceRequestor(documentPrincipal, docLoadGroup);
-  ir->MaybeAddTabChild(docLoadGroup);
-
-  // Create a load group that is separate from, yet related to, the document's load group.
-  // This allows checks for interfaces like nsILoadContext to yield the values used by the
-  // the document, yet will not cancel the update job if the document's load group is cancelled.
+    new ServiceWorkerResolveWindowPromiseOnRegisterCallback();
+
   nsCOMPtr<nsILoadGroup> loadGroup = do_CreateInstance(NS_LOADGROUP_CONTRACTID);
-  MOZ_ALWAYS_SUCCEEDS(loadGroup->SetNotificationCallbacks(ir));
-
   RefPtr<ServiceWorkerRegisterJob> job = new ServiceWorkerRegisterJob(
-    documentPrincipal, cleanedScope, spec, loadGroup,
+    principal, aScopeURL, aScriptURL, loadGroup,
     static_cast<ServiceWorkerUpdateViaCache>(aUpdateViaCache)
   );
 
   job->AppendResultCallback(cb);
   queue->ScheduleJob(job);
 
   MOZ_ASSERT(NS_IsMainThread());
   Telemetry::Accumulate(Telemetry::SERVICE_WORKER_REGISTRATIONS, 1);
 
-  promise.forget(aPromise);
-  return NS_OK;
+  return cb->Promise();
 }
 
 /*
  * Implements the async aspects of the getRegistrations algorithm.
  */
 class GetRegistrationsRunnable final : public Runnable
 {
   const ClientInfo mClientInfo;
--- a/dom/serviceworkers/ServiceWorkerManager.h
+++ b/dom/serviceworkers/ServiceWorkerManager.h
@@ -189,16 +189,21 @@ public:
 
   void
   PropagateRemoveAll();
 
   void
   RemoveAll();
 
   RefPtr<ServiceWorkerRegistrationPromise>
+  Register(const ClientInfo& aClientInfo, const nsACString& aScopeURL,
+           const nsACString& aScriptURL,
+           ServiceWorkerUpdateViaCache aUpdateViaCache);
+
+  RefPtr<ServiceWorkerRegistrationPromise>
   GetRegistration(const ClientInfo& aClientInfo, const nsACString& aURL) const;
 
   RefPtr<ServiceWorkerRegistrationListPromise>
   GetRegistrations(const ClientInfo& aClientInfo) const;
 
   already_AddRefed<ServiceWorkerRegistrationInfo>
   GetRegistration(nsIPrincipal* aPrincipal, const nsACString& aScope) const;
 
--- a/dom/serviceworkers/ServiceWorkerUtils.cpp
+++ b/dom/serviceworkers/ServiceWorkerUtils.cpp
@@ -35,10 +35,82 @@ ServiceWorkerParentInterceptEnabled()
 bool
 ServiceWorkerRegistrationDataIsValid(const ServiceWorkerRegistrationData& aData)
 {
   return !aData.scope().IsEmpty() &&
          !aData.currentWorkerURL().IsEmpty() &&
          !aData.cacheName().IsEmpty();
 }
 
+namespace {
+
+nsresult
+CheckForSlashEscapedCharsInPath(nsIURI* aURI)
+{
+  MOZ_ASSERT(aURI);
+
+  // A URL that can't be downcast to a standard URL is an invalid URL and should
+  // be treated as such and fail with SecurityError.
+  nsCOMPtr<nsIURL> url(do_QueryInterface(aURI));
+  if (NS_WARN_IF(!url)) {
+    return NS_ERROR_DOM_SECURITY_ERR;
+  }
+
+  nsAutoCString path;
+  nsresult rv = url->GetFilePath(path);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  ToLowerCase(path);
+  if (path.Find("%2f") != kNotFound ||
+      path.Find("%5c") != kNotFound) {
+    return NS_ERROR_DOM_TYPE_ERR;
+  }
+
+  return NS_OK;
+}
+
+} // anonymous namespace
+
+nsresult
+ServiceWorkerScopeAndScriptAreValid(const ClientInfo& aClientInfo,
+                                    nsIURI* aScopeURI,
+                                    nsIURI* aScriptURI)
+{
+  MOZ_DIAGNOSTIC_ASSERT(aScopeURI);
+  MOZ_DIAGNOSTIC_ASSERT(aScriptURI);
+
+  nsCOMPtr<nsIPrincipal> principal = aClientInfo.GetPrincipal();
+  NS_ENSURE_TRUE(principal, NS_ERROR_DOM_INVALID_STATE_ERR);
+
+  bool isHttp = false;
+  bool isHttps = false;
+  Unused << aScriptURI->SchemeIs("http", &isHttp);
+  Unused << aScriptURI->SchemeIs("https", &isHttps);
+  NS_ENSURE_TRUE(isHttp || isHttps, NS_ERROR_DOM_SECURITY_ERR);
+
+  nsresult rv = CheckForSlashEscapedCharsInPath(aScopeURI);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = CheckForSlashEscapedCharsInPath(aScriptURI);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoCString ref;
+  Unused << aScopeURI->GetRef(ref);
+  NS_ENSURE_TRUE(ref.IsEmpty(), NS_ERROR_DOM_SECURITY_ERR);
+
+  Unused << aScriptURI->GetRef(ref);
+  NS_ENSURE_TRUE(ref.IsEmpty(), NS_ERROR_DOM_SECURITY_ERR);
+
+  rv = principal->CheckMayLoad(aScopeURI, true /* report */,
+                               false /* allowIfInheritsPrincipal */);
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SECURITY_ERR);
+
+  rv = principal->CheckMayLoad(aScriptURI, true /* report */,
+                               false /* allowIfInheritsPrincipal */);
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SECURITY_ERR);
+
+  return NS_OK;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/serviceworkers/ServiceWorkerUtils.h
+++ b/dom/serviceworkers/ServiceWorkerUtils.h
@@ -6,29 +6,41 @@
 #ifndef _mozilla_dom_ServiceWorkerUtils_h
 #define _mozilla_dom_ServiceWorkerUtils_h
 
 #include "mozilla/MozPromise.h"
 #include "mozilla/dom/ServiceWorkerRegistrationDescriptor.h"
 #include "nsTArray.h"
 
 namespace mozilla {
+
+class ErrorResult;
+
 namespace dom {
 
 class ServiceWorkerRegistrationData;
 class ServiceWorkerRegistrationDescriptor;
 
-typedef MozPromise<ServiceWorkerRegistrationDescriptor, nsresult, false>
+// Note: These are exclusive promise types.  Only one Then() or ChainTo()
+//       call is allowed.  This is necessary since ErrorResult cannot
+//       be copied.
+
+typedef MozPromise<ServiceWorkerRegistrationDescriptor, ErrorResult, true>
         ServiceWorkerRegistrationPromise;
 
-typedef MozPromise<nsTArray<ServiceWorkerRegistrationDescriptor>, nsresult, false>
+typedef MozPromise<nsTArray<ServiceWorkerRegistrationDescriptor>, ErrorResult, true>
         ServiceWorkerRegistrationListPromise;
 
 bool
 ServiceWorkerParentInterceptEnabled();
 
 bool
 ServiceWorkerRegistrationDataIsValid(const ServiceWorkerRegistrationData& aData);
 
+nsresult
+ServiceWorkerScopeAndScriptAreValid(const ClientInfo& aClientInfo,
+                                    nsIURI* aScopeURI,
+                                    nsIURI* aScriptURI);
+
 } // namespace dom
 } // namespace mozilla
 
 #endif // _mozilla_dom_ServiceWorkerUtils_h
--- a/dom/smil/test/db_smilCSSFromTo.js
+++ b/dom/smil/test/db_smilCSSFromTo.js
@@ -283,23 +283,31 @@ var gFromToBundles = [
   new TestcaseBundle(gPropList.font_stretch, [
     new AnimTestcaseFromTo("normal", "wider", {},
                            "need support for animating between " +
                            "relative 'font-stretch' values"),
     new AnimTestcaseFromTo("narrower", "ultra-condensed", {},
                            "need support for animating between " +
                            "relative 'font-stretch' values"),
     new AnimTestcaseFromTo("ultra-condensed", "condensed",
-                           { midComp: "extra-condensed" }),
+                           { fromComp: "50%",
+                             midComp: "62.5%",
+                             toComp: "75%"}),
     new AnimTestcaseFromTo("semi-condensed", "semi-expanded",
-                           { midComp: "normal" }),
+                           { fromComp: "87.5%",
+                             midComp: "100%",
+                             toComp: "112.5%"}),
     new AnimTestcaseFromTo("expanded", "ultra-expanded",
-                           { midComp: "extra-expanded" }),
+                           { fromComp: "125%",
+                             midComp: "162.5%",
+                             toComp: "200%"}),
     new AnimTestcaseFromTo("ultra-expanded", "inherit",
-                           { midComp: "expanded", toComp: "normal" }),
+                           { fromComp: "200%",
+                             midComp: "150%",
+                             toComp: "100%"}),
   ]),
   new TestcaseBundle(gPropList.font_style, [
     new AnimTestcaseFromTo("italic", "inherit", { toComp: "normal" }),
     new AnimTestcaseFromTo("normal", "italic"),
     new AnimTestcaseFromTo("italic", "oblique"),
     new AnimTestcaseFromTo("oblique", "normal"),
   ]),
   new TestcaseBundle(gPropList.font_variant, [
--- a/dom/smil/test/test_smilCSSFontStretchRelative.xhtml
+++ b/dom/smil/test/test_smilCSSFontStretchRelative.xhtml
@@ -22,28 +22,28 @@
 */
 
 SimpleTest.waitForExplicitFinish();
 
 const gPropertyName="font-stretch";
 
 // List of non-relative font-stretch values, from smallest to largest
 const gFontStretchValues = [
-  "ultra-condensed",
-  "extra-condensed",
-  "condensed",
-  "semi-condensed",
-  "normal",
-  "semi-expanded",
-  "expanded",
-  "extra-expanded",
-  "ultra-expanded"
+  ["ultra-condensed", "50%"],
+  ["extra-condensed", "62.5%"],
+  ["condensed", "75%"],
+  ["semi-condensed", "87.5%"],
+  ["normal", "100%"],
+  ["semi-expanded", "112.5%"],
+  ["expanded", "125%"],
+  ["extra-expanded", "150%"],
+  ["ultra-expanded", "200%"],
 ];
 
-function testFontStretchValue(baseValue, narrowerStep, widerStep)
+function testFontStretchValue([baseValue, computedValue], [narrowerStep, computedNarrowerStep], [widerStep, computedWiderStep])
 {
   var svg = SMILUtil.getSVGRoot();
   var gElem = document.createElementNS(SVG_NS, "g");
   gElem.setAttribute("style", "font-stretch: " + baseValue);
   svg.appendChild(gElem);
 
   var textElem = document.createElementNS(SVG_NS, "text");
   gElem.appendChild(textElem);
@@ -56,26 +56,26 @@ function testFontStretchValue(baseValue,
   textElem.appendChild(animElem);
 
   // CHECK EFFECT OF 'narrower'
   // NOTE: Using is() instead of todo_is() for ultra-condensed, since
   // 'narrower' has no effect on that value.
   var myIs = (baseValue == "ultra-condensed" ? is : todo_is);
   animElem.setAttribute("to", "narrower");
   SMILUtil.getSVGRoot().setCurrentTime(1.0); // Force a resample
-  myIs(SMILUtil.getComputedStyleSimple(textElem, gPropertyName), narrowerStep,
+  myIs(SMILUtil.getComputedStyleSimple(textElem, gPropertyName), computedNarrowerStep,
        "checking effect of 'narrower' on inherited value '" + baseValue + "'");
 
   // CHECK EFFECT OF 'wider'
   // NOTE: using is() instead of todo_is() for ultra-expanded, since
   // 'wider' has no effect on that value.
   myIs = (baseValue == "ultra-expanded" ? is : todo_is);
   animElem.setAttribute("to", "wider");
   SMILUtil.getSVGRoot().setCurrentTime(1.0); // Force a resample
-  myIs(SMILUtil.getComputedStyleSimple(textElem, gPropertyName), widerStep,
+  myIs(SMILUtil.getComputedStyleSimple(textElem, gPropertyName), computedWiderStep,
           "checking effect of 'wider' on inherited value '" + baseValue + "'");
 
   // Removing animation should clear animated effects
   textElem.removeChild(animElem);
   svg.removeChild(gElem);
 }
 
 function main()
--- a/dom/webidl/CSS.webidl
+++ b/dom/webidl/CSS.webidl
@@ -1,24 +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
  * http://dev.w3.org/csswg/css3-conditional/
+ * http://dev.w3.org/csswg/cssom/#the-css.escape%28%29-method
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
-interface CSS {
+namespace CSS {
   [Throws]
-  static boolean supports(DOMString property, DOMString value);
+  boolean supports(DOMString property, DOMString value);
 
   [Throws]
-  static boolean supports(DOMString conditionText);
+  boolean supports(DOMString conditionText);
 };
 
 // http://dev.w3.org/csswg/cssom/#the-css.escape%28%29-method
-partial interface CSS {
-  static DOMString escape(DOMString ident);
+partial namespace CSS {
+  DOMString escape(DOMString ident);
 };
--- a/dom/webidl/MediaList.webidl
+++ b/dom/webidl/MediaList.webidl
@@ -2,16 +2,23 @@
 /* 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/. */
 
 // http://dev.w3.org/csswg/cssom/#the-medialist-interface
 
 [ArrayClass]
 interface MediaList {
+  // Bug 824857: no support for stringifier attributes yet.
+  //   [TreatNullAs=EmptyString]
+  // stringifier attribute DOMString        mediaText;
+
+  // Bug 824857 should remove this.
+  stringifier;
+
   [TreatNullAs=EmptyString]
            attribute DOMString        mediaText;
 
   readonly attribute unsigned long    length;
   getter DOMString?  item(unsigned long index);
   [Throws]
   void               deleteMedium(DOMString oldMedium);
   [Throws]
--- a/dom/webidl/StyleSheet.webidl
+++ b/dom/webidl/StyleSheet.webidl
@@ -15,17 +15,17 @@ interface StyleSheet {
   // Spec says "Node", but it can go null when the node gets a new
   // sheet.  That's also why it's not [Constant]
   [Pure]
   readonly attribute Node? ownerNode;
   [Pure]
   readonly attribute StyleSheet? parentStyleSheet;
   [Pure]
   readonly attribute DOMString? title;
-  [Constant]
+  [Constant, PutForwards=mediaText]
   readonly attribute MediaList media;
   [Pure]
   attribute boolean disabled;
   // The source map URL for this style sheet.  The source map URL can
   // be found in one of two ways.
   //
   // If a SourceMap or X-SourceMap response header is seen, this is
   // the value.  If both are seen, SourceMap is preferred.  Because
--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -322,17 +322,17 @@ EditorBase::Init(nsIDocument& aDocument,
   // Make sure that the editor will be destroyed properly
   mDidPreDestroy = false;
   // Make sure that the ediotr will be created properly
   mDidPostCreate = false;
 
   return NS_OK;
 }
 
-NS_IMETHODIMP
+nsresult
 EditorBase::PostCreate()
 {
   // Synchronize some stuff for the flags.  SetFlags() will initialize
   // something by the flag difference.  This is first time of that, so, all
   // initializations must be run.  For such reason, we need to invert mFlags
   // value first.
   mFlags = ~mFlags;
   nsresult rv = SetFlags(~mFlags);
@@ -564,18 +564,17 @@ EditorBase::SetFlags(uint32_t aFlags)
     // If we're initializing, we shouldn't do anything now.
     // SetFlags() will be called by PostCreate(),
     // we should synchronize some stuff for the flags at that time.
     return NS_OK;
   }
 
   // The flag change may cause the spellchecker state change
   if (CanEnableSpellCheck() != spellcheckerWasEnabled) {
-    nsresult rv = SyncRealTimeSpell();
-    NS_ENSURE_SUCCESS(rv, rv);
+    SyncRealTimeSpell();
   }
 
   // If this is called from PostCreate(), it will update the IME state if it's
   // necessary.
   if (!mDidPostCreate) {
     return NS_OK;
   }
 
@@ -1321,17 +1320,17 @@ EditorBase::GetInlineSpellChecker(bool a
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   NS_IF_ADDREF(*aInlineSpellChecker = mInlineSpellChecker);
 
   return NS_OK;
 }
 
-NS_IMETHODIMP
+void
 EditorBase::SyncRealTimeSpell()
 {
   bool enable = GetDesiredSpellCheckState();
 
   // Initializes mInlineSpellChecker
   nsCOMPtr<nsIInlineSpellChecker> spellChecker;
   GetInlineSpellChecker(enable, getter_AddRefs(spellChecker));
 
@@ -1341,26 +1340,25 @@ EditorBase::SyncRealTimeSpell()
       mSpellCheckerDictionaryUpdated = true;
     }
 
     // We might have a mInlineSpellChecker even if there are no dictionaries
     // available since we don't destroy the mInlineSpellChecker when the last
     // dictionariy is removed, but in that case spellChecker is null
     mInlineSpellChecker->SetEnableRealTimeSpell(enable && spellChecker);
   }
-
-  return NS_OK;
 }
 
 NS_IMETHODIMP
 EditorBase::SetSpellcheckUserOverride(bool enable)
 {
   mSpellcheckCheckboxState = enable ? eTriTrue : eTriFalse;
 
-  return SyncRealTimeSpell();
+  SyncRealTimeSpell();
+  return NS_OK;
 }
 
 template<typename PT, typename CT>
 already_AddRefed<Element>
 EditorBase::CreateNodeWithTransaction(
               nsAtom& aTagName,
               const EditorDOMPointBase<PT, CT>& aPointToInsert)
 {
@@ -2265,26 +2263,16 @@ EditorBase::OutputToString(const nsAStri
                            uint32_t aFlags,
                            nsAString& aOutputString)
 {
   // these should be implemented by derived classes.
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
-EditorBase::OutputToStream(nsIOutputStream* aOutputStream,
-                           const nsAString& aFormatType,
-                           const nsACString& aCharsetOverride,
-                           uint32_t aFlags)
-{
-  // these should be implemented by derived classes.
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-NS_IMETHODIMP
 EditorBase::DumpContentTree()
 {
 #ifdef DEBUG
   if (mRootElement) {
     mRootElement->List(stdout);
   }
 #endif
   return NS_OK;
--- a/editor/libeditor/EditorBase.h
+++ b/editor/libeditor/EditorBase.h
@@ -981,16 +981,22 @@ protected:
    *                            has parent node.  So, it's always safe to
    *                            call SetAncestorLimit() with this node.
    */
   virtual void InitializeSelectionAncestorLimit(Selection& aSelection,
                                                 nsIContent& aAncestorLimit);
 
 public:
   /**
+   * PostCreate should be called after Init, and is the time that the editor
+   * tells its documentStateObservers that the document has been created.
+   */
+  nsresult PostCreate();
+
+  /**
    * All editor operations which alter the doc should be prefaced
    * with a call to StartOperation, naming the action and direction.
    */
   virtual nsresult StartOperation(EditAction opID,
                                   nsIEditor::EDirection aDirection);
 
   /**
    * All editor operations which alter the doc should be followed
@@ -1812,16 +1818,22 @@ public:
 
   /**
    * HideCaret() hides caret with nsCaret::AddForceHide() or may show carent
    * with nsCaret::RemoveForceHide().  This does NOT set visibility of
    * nsCaret.  Therefore, this is stateless.
    */
   void HideCaret(bool aHide);
 
+  /** Resyncs spellchecking state (enabled/disabled).  This should be called
+    * when anything that affects spellchecking state changes, such as the
+    * spellcheck attribute value.
+    */
+  void SyncRealTimeSpell();
+
 private:
   nsCOMPtr<nsISelectionController> mSelectionController;
   nsCOMPtr<nsIDocument> mDocument;
 
 protected:
   enum Tristate
   {
     eTriUnset,
--- a/editor/libeditor/TextEditor.cpp
+++ b/editor/libeditor/TextEditor.cpp
@@ -1593,17 +1593,17 @@ TextEditor::CanCopy(bool* aCanCopy)
 NS_IMETHODIMP
 TextEditor::CanDelete(bool* aCanDelete)
 {
   NS_ENSURE_ARG_POINTER(aCanDelete);
   *aCanDelete = IsModifiable() && CanCutOrCopy(ePasswordFieldAllowed);
   return NS_OK;
 }
 
-// Shared between OutputToString and OutputToStream
+// Used by OutputToString
 already_AddRefed<nsIDocumentEncoder>
 TextEditor::GetAndInitDocEncoder(const nsAString& aFormatType,
                                  uint32_t aFlags,
                                  const nsACString& aCharset)
 {
   nsCOMPtr<nsIDocumentEncoder> docEncoder;
   if (!mCachedDocumentEncoder ||
       !mCachedDocumentEncoderType.Equals(aFormatType)) {
@@ -1709,46 +1709,16 @@ TextEditor::OutputToString(const nsAStri
   if (NS_WARN_IF(!encoder)) {
     return NS_ERROR_FAILURE;
   }
 
   return encoder->EncodeToString(aOutputString);
 }
 
 NS_IMETHODIMP
-TextEditor::OutputToStream(nsIOutputStream* aOutputStream,
-                           const nsAString& aFormatType,
-                           const nsACString& aCharset,
-                           uint32_t aFlags)
-{
-  nsresult rv;
-
-  // special-case for empty document when requesting plain text,
-  // to account for the bogus text node.
-  // XXX Should there be a similar test in OutputToString?
-  if (aFormatType.EqualsLiteral("text/plain")) {
-    bool docEmpty;
-    rv = GetDocumentIsEmpty(&docEmpty);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (docEmpty) {
-      return NS_OK; // Output nothing.
-    }
-  }
-
-  nsCOMPtr<nsIDocumentEncoder> encoder =
-    GetAndInitDocEncoder(aFormatType, aFlags, aCharset);
-  if (NS_WARN_IF(!encoder)) {
-    return NS_ERROR_FAILURE;
-  }
-
-  return encoder->EncodeToStream(aOutputStream);
-}
-
-NS_IMETHODIMP
 TextEditor::InsertTextWithQuotations(const nsAString& aStringToInsert)
 {
   nsresult rv = InsertTextAsAction(aStringToInsert);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
--- a/editor/libeditor/TextEditor.h
+++ b/editor/libeditor/TextEditor.h
@@ -75,21 +75,16 @@ public:
   NS_IMETHOD PasteTransferable(nsITransferable* aTransferable) override;
   NS_IMETHOD CanPasteTransferable(nsITransferable* aTransferable,
                                   bool* aCanPaste) override;
 
   NS_IMETHOD OutputToString(const nsAString& aFormatType,
                             uint32_t aFlags,
                             nsAString& aOutputString) override;
 
-  NS_IMETHOD OutputToStream(nsIOutputStream* aOutputStream,
-                            const nsAString& aFormatType,
-                            const nsACString& aCharsetOverride,
-                            uint32_t aFlags) override;
-
   // Overrides of EditorBase
   virtual nsresult RemoveAttributeOrEquivalent(
                      Element* aElement,
                      nsAtom* aAttribute,
                      bool aSuppressTransaction) override;
   virtual nsresult SetAttributeOrEquivalent(Element* aElement,
                                             nsAtom* aAttribute,
                                             const nsAString& aValue,
--- a/editor/nsIEditor.idl
+++ b/editor/nsIEditor.idl
@@ -52,22 +52,16 @@ interface nsIEditor  : nsISupports
                                 in AString sourceAttrName,
                                 in AString sourceAttrValue,
                                 in boolean aSuppressTransaction);
   void removeAttributeOrEquivalent(in nsIDOMElement element,
                                    in DOMString sourceAttrName,
                                    in boolean aSuppressTransaction);
 
   /**
-   * postCreate should be called after Init, and is the time that the editor
-   * tells its documentStateObservers that the document has been created.
-   */
-  void postCreate();
-
-  /**
    * preDestroy is called before the editor goes away, and gives the editor a
    * chance to tell its documentStateObservers that the document is going away.
    * @param aDestroyingFrames set to true when the frames being edited
    * are being destroyed (so there is no need to modify any nsISelections,
    * nor is it safe to do so)
    */
   void preDestroy(in boolean aDestroyingFrames);
 
@@ -246,22 +240,16 @@ interface nsIEditor  : nsISupports
     * you during this call.
     * @param  autoCreate  If true, this will create a spell checker object
     *                     if one does not exist yet for this editor. If false
     *                     and the object has not been created, this function
     *                     WILL RETURN NULL.
     */
   nsIInlineSpellChecker getInlineSpellChecker(in boolean autoCreate);
 
-  /** Resyncs spellchecking state (enabled/disabled).  This should be called
-    * when anything that affects spellchecking state changes, such as the
-    * spellcheck attribute value.
-    */
-  void syncRealTimeSpell();
-
   /** Called when the user manually overrides the spellchecking state for this
     * editor.
     * @param  enable  The new state of spellchecking in this editor, as
     *                 requested by the user.
     */
   void setSpellcheckUserOverride(in boolean enable);
 
   /* ------------ Clipboard methods -------------- */
@@ -470,21 +458,16 @@ interface nsIEditor  : nsISupports
 /* ------------ Output methods -------------- */
 
   /**
    * Output methods:
    * aFormatType is a mime type, like text/plain.
    */
   AString outputToString(in AString formatType,
                          in unsigned long flags);
-  void outputToStream(in nsIOutputStream aStream,
-                      in AString formatType,
-                      in ACString charsetOverride,
-                      in unsigned long flags);
-
 
   /* ------------ Various listeners methods --------------
    * nsIEditor holds strong references to the editor observers, action listeners
    * and document state listeners.
    */
 
   /** add an EditorObserver to the editors list of observers. */
   void addEditorObserver(in nsIEditorObserver observer);
--- a/gfx/2d/ScaledFontDWrite.cpp
+++ b/gfx/2d/ScaledFontDWrite.cpp
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ScaledFontDWrite.h"
 #include "UnscaledFontDWrite.h"
 #include "PathD2D.h"
 #include "gfxFont.h"
 #include "Logging.h"
+#include "mozilla/FontPropertyTypes.h"
 #include "mozilla/webrender/WebRenderTypes.h"
 
 #include "dwrite_3.h"
 
 // Currently, we build with WINVER=0x601 (Win7), which means newer
 // declarations in dwrite_3.h will not be visible. Also, we don't
 // yet have the Fall Creators Update SDK available on build machines,
 // so even with updated WINVER, some of the interfaces we need would
@@ -88,41 +89,47 @@ DoGrayscale(IDWriteFontFace *aDWFace, Fl
       }
     }
     aDWFace->ReleaseFontTable(tableContext);
   }
   return true;
 }
 
 static inline DWRITE_FONT_STRETCH
-DWriteFontStretchFromStretch(uint16_t aStretch)
+DWriteFontStretchFromStretch(FontStretch aStretch)
 {
-    switch (aStretch) {
-        case NS_FONT_STRETCH_ULTRA_CONDENSED:
-            return DWRITE_FONT_STRETCH_ULTRA_CONDENSED;
-        case NS_FONT_STRETCH_EXTRA_CONDENSED:
-            return DWRITE_FONT_STRETCH_EXTRA_CONDENSED;
-        case NS_FONT_STRETCH_CONDENSED:
-            return DWRITE_FONT_STRETCH_CONDENSED;
-        case NS_FONT_STRETCH_SEMI_CONDENSED:
-            return DWRITE_FONT_STRETCH_SEMI_CONDENSED;
-        case NS_FONT_STRETCH_NORMAL:
-            return DWRITE_FONT_STRETCH_NORMAL;
-        case NS_FONT_STRETCH_SEMI_EXPANDED:
-            return DWRITE_FONT_STRETCH_SEMI_EXPANDED;
-        case NS_FONT_STRETCH_EXPANDED:
-            return DWRITE_FONT_STRETCH_EXPANDED;
-        case NS_FONT_STRETCH_EXTRA_EXPANDED:
-            return DWRITE_FONT_STRETCH_EXTRA_EXPANDED;
-        case NS_FONT_STRETCH_ULTRA_EXPANDED:
-            return DWRITE_FONT_STRETCH_ULTRA_EXPANDED;
-        default:
-            return DWRITE_FONT_STRETCH_UNDEFINED;
+    if (aStretch == FontStretch::UltraCondensed()) {
+        return DWRITE_FONT_STRETCH_ULTRA_CONDENSED;
+    }
+    if (aStretch == FontStretch::ExtraCondensed()) {
+        return DWRITE_FONT_STRETCH_EXTRA_CONDENSED;
+    }
+    if (aStretch == FontStretch::Condensed()) {
+        return DWRITE_FONT_STRETCH_CONDENSED;
+    }
+    if (aStretch == FontStretch::SemiCondensed()) {
+        return DWRITE_FONT_STRETCH_SEMI_CONDENSED;
+    }
+    if (aStretch == FontStretch::Normal()) {
+        return DWRITE_FONT_STRETCH_NORMAL;
     }
-}
+    if (aStretch == FontStretch::SemiExpanded()) {
+        return DWRITE_FONT_STRETCH_SEMI_EXPANDED;
+    }
+    if (aStretch == FontStretch::Expanded()) {
+        return DWRITE_FONT_STRETCH_EXPANDED;
+    }
+    if (aStretch == FontStretch::ExtraExpanded()) {
+        return DWRITE_FONT_STRETCH_EXTRA_EXPANDED;
+    }
+    if (aStretch == FontStretch::UltraExpanded()) {
+        return DWRITE_FONT_STRETCH_ULTRA_EXPANDED;
+    }
+    return DWRITE_FONT_STRETCH_UNDEFINED;
+ }
 
 ScaledFontDWrite::ScaledFontDWrite(IDWriteFontFace *aFontFace,
                                    const RefPtr<UnscaledFont>& aUnscaledFont,
                                    Float aSize,
                                    bool aUseEmbeddedBitmap,
                                    bool aForceGDIMode,
                                    IDWriteRenderingParams* aParams,
                                    Float aGamma,
@@ -134,17 +141,18 @@ ScaledFontDWrite::ScaledFontDWrite(IDWri
     , mForceGDIMode(aForceGDIMode)
     , mParams(aParams)
     , mGamma(aGamma)
     , mContrast(aContrast)
 {
   if (aStyle) {
     mStyle = SkFontStyle(aStyle->weight.ToIntRounded(),
                          DWriteFontStretchFromStretch(aStyle->stretch),
-                         aStyle->style == NS_FONT_STYLE_NORMAL ?
+                         // FIXME(jwatt): also use kOblique_Slant
+                         aStyle->style ==  FontSlantStyle::Normal()?
                          SkFontStyle::kUpright_Slant : SkFontStyle::kItalic_Slant);
   }
 }
 
 already_AddRefed<Path>
 ScaledFontDWrite::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget)
 {
   if (aTarget->GetBackendType() != BackendType::DIRECT2D && aTarget->GetBackendType() != BackendType::DIRECT2D1_1) {
--- a/gfx/ipc/GPUParent.cpp
+++ b/gfx/ipc/GPUParent.cpp
@@ -27,17 +27,16 @@
 #include "mozilla/layers/APZUtils.h"    // for apz::InitializeGlobalState
 #include "mozilla/layers/CompositorBridgeParent.h"
 #include "mozilla/layers/CompositorManagerParent.h"
 #include "mozilla/layers/CompositorThread.h"
 #include "mozilla/layers/ImageBridgeParent.h"
 #include "mozilla/layers/LayerTreeOwnerTracker.h"
 #include "mozilla/layers/UiCompositorControllerParent.h"
 #include "mozilla/layers/MemoryReportingMLGPU.h"
-#include "mozilla/layers/SharedSurfacesParent.h"
 #include "mozilla/webrender/RenderThread.h"
 #include "mozilla/webrender/WebRenderAPI.h"
 #include "mozilla/HangDetails.h"
 #include "nsDebugImpl.h"
 #include "nsIGfxInfo.h"
 #include "nsThreadManager.h"
 #include "prenv.h"
 #include "ProcessUtils.h"
@@ -257,17 +256,16 @@ GPUParent::RecvInit(nsTArray<GfxPrefSett
   }
 #endif
 
   // Make sure to do this *after* we update gfxVars above.
   if (gfxVars::UseWebRender()) {
     wr::WebRenderAPI::InitExternalLogHandler();
 
     wr::RenderThread::Start();
-    SharedSurfacesParent::Initialize();
   }
 
   VRManager::ManagerInit();
   // Send a message to the UI process that we're done.
   GPUDeviceData data;
   RecvGetDeviceStatus(&data);
   Unused << SendInitComplete(data);
 
@@ -489,17 +487,16 @@ GPUParent::ActorDestroy(ActorDestroyReas
     mVsyncBridge = nullptr;
   }
   dom::VideoDecoderManagerParent::ShutdownVideoBridge();
   CompositorThreadHolder::Shutdown();
   VRListenerThreadHolder::Shutdown();
   // There is a case that RenderThread exists when gfxVars::UseWebRender() is false.
   // This could happen when WebRender was fallbacked to compositor.
   if (wr::RenderThread::Get()) {
-    SharedSurfacesParent::Shutdown();
     wr::RenderThread::ShutDown();
 
     wr::WebRenderAPI::ShutdownExternalLogHandler();
   }
   Factory::ShutDown();
 #if defined(XP_WIN)
   DeviceManagerDx::Shutdown();
 #endif
--- a/gfx/ipc/VsyncBridgeChild.cpp
+++ b/gfx/ipc/VsyncBridgeChild.cpp
@@ -146,15 +146,15 @@ VsyncBridgeChild::DeallocPVsyncBridgeChi
 
 void
 VsyncBridgeChild::ProcessingError(Result aCode, const char* aReason)
 {
   MOZ_RELEASE_ASSERT(aCode == MsgDropped, "Processing error in VsyncBridgeChild");
 }
 
 void
-VsyncBridgeChild::HandleFatalError(const char* aName, const char* aMsg) const
+VsyncBridgeChild::HandleFatalError(const char* aMsg) const
 {
-  dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aName, aMsg, OtherPid());
+  dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aMsg, OtherPid());
 }
 
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/ipc/VsyncBridgeChild.h
+++ b/gfx/ipc/VsyncBridgeChild.h
@@ -28,17 +28,17 @@ public:
   void Close();
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
   void DeallocPVsyncBridgeChild() override;
   void ProcessingError(Result aCode, const char* aReason) override;
 
   void NotifyVsync(TimeStamp aTimeStamp, const layers::LayersId& aLayersId);
 
-  virtual void HandleFatalError(const char* aName, const char* aMsg) const override;
+  virtual void HandleFatalError(const char* aMsg) const override;
 
 private:
   VsyncBridgeChild(RefPtr<VsyncIOThreadHolder>, const uint64_t& aProcessToken);
   ~VsyncBridgeChild();
 
   void Open(Endpoint<PVsyncBridgeChild>&& aEndpoint);
 
   void NotifyVsyncImpl(TimeStamp aTimeStamp, const layers::LayersId& aLayersId);
--- a/gfx/layers/SourceSurfaceSharedData.h
+++ b/gfx/layers/SourceSurfaceSharedData.h
@@ -35,16 +35,17 @@ class SourceSurfaceSharedDataWrapper fin
 {
   typedef mozilla::ipc::SharedMemoryBasic SharedMemoryBasic;
 
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceSharedDataWrapper, override)
 
   SourceSurfaceSharedDataWrapper()
     : mStride(0)
+    , mConsumers(0)
     , mFormat(SurfaceFormat::UNKNOWN)
   { }
 
   bool Init(const IntSize& aSize,
             int32_t aStride,
             SurfaceFormat aFormat,
             const SharedMemoryBasic::Handle& aHandle,
             base::ProcessId aCreatorPid);
@@ -67,26 +68,16 @@ public:
     return static_cast<uint8_t*>(mBuf->memory());
   }
 
   bool OnHeap() const override
   {
     return false;
   }
 
-  bool Map(MapType, MappedSurface *aMappedSurface) override
-  {
-    aMappedSurface->mData = GetData();
-    aMappedSurface->mStride = mStride;
-    return true;
-  }
-
-  void Unmap() override
-  { }
-
   bool AddConsumer()
   {
     return ++mConsumers == 1;
   }
 
   bool RemoveConsumer()
   {
     MOZ_ASSERT(mConsumers > 0);
--- a/gfx/layers/apz/test/mochitest/mochitest.ini
+++ b/gfx/layers/apz/test/mochitest/mochitest.ini
@@ -47,17 +47,17 @@
     helper_touch_action.html
     helper_touch_action_regions.html
     helper_touch_action_complex.html
   tags = apz
 [test_bug982141.html]
   skip-if = webrender # bug 1424752
 [test_bug1151663.html]
 [test_bug1151667.html]
-  skip-if = (os == 'android') # wheel events not supported on mobile
+  skip-if = (os == 'android') || webrender # wheel events not supported on mobile, bug 1424752 for webrender
 [test_bug1253683.html]
   skip-if = (os == 'android') # wheel events not supported on mobile
 [test_bug1277814.html]
   skip-if = (os == 'android') # wheel events not supported on mobile
 [test_bug1304689.html]
 [test_bug1304689-2.html]
 [test_frame_reconstruction.html]
   skip-if = webrender # bug 1424752
--- a/gfx/layers/ipc/CompositorManagerChild.cpp
+++ b/gfx/layers/ipc/CompositorManagerChild.cpp
@@ -239,19 +239,19 @@ CompositorManagerChild::AllocPCompositor
 bool
 CompositorManagerChild::DeallocPCompositorBridgeChild(PCompositorBridgeChild* aActor)
 {
   static_cast<CompositorBridgeChild*>(aActor)->Release();
   return true;
 }
 
 void
-CompositorManagerChild::HandleFatalError(const char* aName, const char* aMsg) const
+CompositorManagerChild::HandleFatalError(const char* aMsg) const
 {
-  dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aName, aMsg, OtherPid());
+  dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aMsg, OtherPid());
 }
 
 void
 CompositorManagerChild::ProcessingError(Result aCode, const char* aReason)
 {
   if (aCode != MsgDropped) {
     gfxDevCrash(gfx::LogReason::ProcessingError) << "Processing error in CompositorBridgeChild: " << int(aCode);
   }
--- a/gfx/layers/ipc/CompositorManagerChild.h
+++ b/gfx/layers/ipc/CompositorManagerChild.h
@@ -82,17 +82,17 @@ public:
     uint64_t id = GetNextResourceId();
     MOZ_RELEASE_ASSERT(id != 0);
     id |= (static_cast<uint64_t>(mNamespace) << 32);
     return wr::ToExternalImageId(id);
   }
 
   void ActorDestroy(ActorDestroyReason aReason) override;
 
-  void HandleFatalError(const char* aName, const char* aMsg) const override;
+  void HandleFatalError(const char* aMsg) const override;
 
   void ProcessingError(Result aCode, const char* aReason) override;
 
   PCompositorBridgeChild* AllocPCompositorBridgeChild(const CompositorBridgeOptions& aOptions) override;
 
   bool DeallocPCompositorBridgeChild(PCompositorBridgeChild* aActor) override;
 
   bool ShouldContinueFromReplyTimeout() override;
--- a/gfx/layers/ipc/ImageBridgeChild.cpp
+++ b/gfx/layers/ipc/ImageBridgeChild.cpp
@@ -1114,19 +1114,19 @@ ImageBridgeChild::ReleaseCompositable(co
 bool
 ImageBridgeChild::CanSend() const
 {
   MOZ_ASSERT(InImageBridgeChildThread());
   return mCanSend;
 }
 
 void
-ImageBridgeChild::HandleFatalError(const char* aName, const char* aMsg) const
+ImageBridgeChild::HandleFatalError(const char* aMsg) const
 {
-  dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aName, aMsg, OtherPid());
+  dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aMsg, OtherPid());
 }
 
 wr::MaybeExternalImageId
 ImageBridgeChild::GetNextExternalImageId()
 {
   static uint32_t sNextID = 1;
   ++sNextID;
   MOZ_RELEASE_ASSERT(sNextID != UINT32_MAX);
--- a/gfx/layers/ipc/ImageBridgeChild.h
+++ b/gfx/layers/ipc/ImageBridgeChild.h
@@ -343,17 +343,17 @@ public:
 
   virtual void UpdateFwdTransactionId() override { ++mFwdTransactionId; }
   virtual uint64_t GetFwdTransactionId() override { return mFwdTransactionId; }
 
   bool InForwarderThread() override {
     return InImageBridgeChildThread();
   }
 
-  virtual void HandleFatalError(const char* aName, const char* aMsg) const override;
+  virtual void HandleFatalError(const char* aMsg) const override;
 
   virtual wr::MaybeExternalImageId GetNextExternalImageId() override;
 
 protected:
   explicit ImageBridgeChild(uint32_t aNamespace);
   bool DispatchAllocShmemInternal(size_t aSize,
                                   SharedMemory::SharedMemoryType aType,
                                   Shmem* aShmem,
--- a/gfx/layers/ipc/SharedSurfacesParent.cpp
+++ b/gfx/layers/ipc/SharedSurfacesParent.cpp
@@ -1,15 +1,16 @@
 /* -*- 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 "SharedSurfacesParent.h"
+#include "mozilla/DebugOnly.h"
 #include "mozilla/layers/SourceSurfaceSharedData.h"
 #include "mozilla/layers/CompositorThread.h"
 #include "mozilla/webrender/RenderSharedSurfaceTextureHost.h"
 #include "mozilla/webrender/RenderThread.h"
 
 namespace mozilla {
 namespace layers {
 
@@ -18,31 +19,39 @@ using namespace mozilla::gfx;
 StaticAutoPtr<SharedSurfacesParent> SharedSurfacesParent::sInstance;
 
 SharedSurfacesParent::SharedSurfacesParent()
 {
 }
 
 SharedSurfacesParent::~SharedSurfacesParent()
 {
+  for (auto i = mSurfaces.Iter(); !i.Done(); i.Next()) {
+    // There may be lingering consumers of the surfaces that didn't get shutdown
+    // yet but since we are here, we know the render thread is finished and we
+    // can unregister everything.
+    wr::RenderThread::Get()->UnregisterExternalImageDuringShutdown(i.Key());
+  }
 }
 
 /* static */ void
 SharedSurfacesParent::Initialize()
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (!sInstance) {
     sInstance = new SharedSurfacesParent();
   }
 }
 
 /* static */ void
 SharedSurfacesParent::Shutdown()
 {
-  MOZ_ASSERT(NS_IsMainThread());
+  // The main thread should blocked on waiting for the render thread to
+  // complete so this should be safe to release off the main thread.
+  MOZ_ASSERT(wr::RenderThread::IsInRenderThread());
   sInstance = nullptr;
 }
 
 /* static */ already_AddRefed<DataSourceSurface>
 SharedSurfacesParent::Get(const wr::ExternalImageId& aId)
 {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   if (!sInstance) {
@@ -83,16 +92,17 @@ SharedSurfacesParent::Release(const wr::
   uint64_t id = wr::AsUint64(aId);
   RefPtr<SourceSurfaceSharedDataWrapper> surface;
   sInstance->mSurfaces.Get(wr::AsUint64(aId), getter_AddRefs(surface));
   if (!surface) {
     return false;
   }
 
   if (surface->RemoveConsumer()) {
+    wr::RenderThread::Get()->UnregisterExternalImage(id);
     sInstance->mSurfaces.Remove(id);
   }
 
   return true;
 }
 
 /* static */ void
 SharedSurfacesParent::AddSameProcess(const wr::ExternalImageId& aId,
@@ -119,49 +129,51 @@ SharedSurfacesParent::AddSameProcess(con
       }
 
       MOZ_ASSERT(!sInstance->mSurfaces.Contains(id));
 
       RefPtr<wr::RenderSharedSurfaceTextureHost> texture =
         new wr::RenderSharedSurfaceTextureHost(surface);
       wr::RenderThread::Get()->RegisterExternalImage(id, texture.forget());
 
+      surface->AddConsumer();
       sInstance->mSurfaces.Put(id, surface);
     });
 
   CompositorThreadHolder::Loop()->PostTask(task.forget());
 }
 
 /* static */ void
 SharedSurfacesParent::RemoveSameProcess(const wr::ExternalImageId& aId)
 {
   MOZ_ASSERT(XRE_IsParentProcess());
   MOZ_ASSERT(NS_IsMainThread());
 
   const wr::ExternalImageId id(aId);
   RefPtr<Runnable> task = NS_NewRunnableFunction(
     "layers::SharedSurfacesParent::RemoveSameProcess",
     [id]() -> void {
-      Remove(id);
+      Release(id);
     });
 
   CompositorThreadHolder::Loop()->PostTask(task.forget());
 }
 
 /* static */ void
 SharedSurfacesParent::DestroyProcess(base::ProcessId aPid)
 {
   if (!sInstance) {
     return;
   }
 
   // Note that the destruction of a parent may not be cheap if it still has a
   // lot of surfaces still bound that require unmapping.
   for (auto i = sInstance->mSurfaces.Iter(); !i.Done(); i.Next()) {
-    if (i.Data()->GetCreatorPid() == aPid) {
+    SourceSurfaceSharedDataWrapper* surface = i.Data();
+    if (surface->GetCreatorPid() == aPid && surface->RemoveConsumer()) {
       wr::RenderThread::Get()->UnregisterExternalImage(i.Key());
       i.Remove();
     }
   }
 }
 
 /* static */ void
 SharedSurfacesParent::Add(const wr::ExternalImageId& aId,
@@ -185,16 +197,17 @@ SharedSurfacesParent::Add(const wr::Exte
 
   uint64_t id = wr::AsUint64(aId);
   MOZ_ASSERT(!sInstance->mSurfaces.Contains(id));
 
   RefPtr<wr::RenderSharedSurfaceTextureHost> texture =
     new wr::RenderSharedSurfaceTextureHost(surface);
   wr::RenderThread::Get()->RegisterExternalImage(id, texture.forget());
 
+  surface->AddConsumer();
   sInstance->mSurfaces.Put(id, surface.forget());
 }
 
 /* static */ void
 SharedSurfacesParent::Remove(const wr::ExternalImageId& aId)
 {
   //MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   DebugOnly<bool> rv = Release(aId);
--- a/gfx/layers/ipc/UiCompositorControllerChild.cpp
+++ b/gfx/layers/ipc/UiCompositorControllerChild.cpp
@@ -252,19 +252,19 @@ UiCompositorControllerChild::DeallocPUiC
 
 void
 UiCompositorControllerChild::ProcessingError(Result aCode, const char* aReason)
 {
   MOZ_RELEASE_ASSERT(aCode == MsgDropped, "Processing error in UiCompositorControllerChild");
 }
 
 void
-UiCompositorControllerChild::HandleFatalError(const char* aName, const char* aMsg) const
+UiCompositorControllerChild::HandleFatalError(const char* aMsg) const
 {
-  dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aName, aMsg, OtherPid());
+  dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aMsg, OtherPid());
 }
 
 mozilla::ipc::IPCResult
 UiCompositorControllerChild::RecvToolbarAnimatorMessageFromCompositor(const int32_t& aMessage)
 {
 #if defined(MOZ_WIDGET_ANDROID)
   if (mWidget) {
     mWidget->RecvToolbarAnimatorMessageFromCompositor(aMessage);
--- a/gfx/layers/ipc/UiCompositorControllerChild.h
+++ b/gfx/layers/ipc/UiCompositorControllerChild.h
@@ -46,17 +46,17 @@ public:
   void SetBaseWidget(nsBaseWidget* aWidget);
   bool AllocPixelBuffer(const int32_t aSize, Shmem* aMem);
   bool DeallocPixelBuffer(Shmem& aMem);
 
 protected:
   void ActorDestroy(ActorDestroyReason aWhy) override;
   void DeallocPUiCompositorControllerChild() override;
   void ProcessingError(Result aCode, const char* aReason) override;
-  virtual void HandleFatalError(const char* aName, const char* aMsg) const override;
+  virtual void HandleFatalError(const char* aMsg) const override;
   mozilla::ipc::IPCResult RecvToolbarAnimatorMessageFromCompositor(const int32_t& aMessage) override;
   mozilla::ipc::IPCResult RecvRootFrameMetrics(const ScreenPoint& aScrollOffset, const CSSToScreenScale& aZoom) override;
   mozilla::ipc::IPCResult RecvScreenPixels(ipc::Shmem&& aMem, const ScreenIntSize& aSize) override;
 private:
   explicit UiCompositorControllerChild(const uint64_t& aProcessToken);
   ~UiCompositorControllerChild();
   void OpenForSameProcess();
   void OpenForGPUProcess(Endpoint<PUiCompositorControllerChild>&& aEndpoint);
--- a/gfx/layers/wr/AsyncImagePipelineManager.cpp
+++ b/gfx/layers/wr/AsyncImagePipelineManager.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "AsyncImagePipelineManager.h"
 
 #include "CompositableHost.h"
 #include "gfxEnv.h"
 #include "mozilla/gfx/gfxVars.h"
 #include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/SharedSurfacesParent.h"
 #include "mozilla/layers/WebRenderImageHost.h"
 #include "mozilla/layers/WebRenderTextureHost.h"
 #include "mozilla/webrender/WebRenderAPI.h"
 #include "mozilla/webrender/WebRenderTypes.h"
 
 namespace mozilla {
 namespace layers {
 
@@ -378,30 +379,57 @@ AsyncImagePipelineManager::HoldExternalI
   if (!holder) {
     return;
   }
   // Hold WebRenderTextureHost until end of its usage on RenderThread
   holder->mTextureHosts.push(ForwardingTextureHost(aEpoch, aTexture));
 }
 
 void
+AsyncImagePipelineManager::HoldExternalImage(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch, const wr::ExternalImageId& aImageId)
+{
+  if (mDestroyed) {
+    SharedSurfacesParent::Release(aImageId);
+    return;
+  }
+
+  PipelineTexturesHolder* holder = mPipelineTexturesHolders.Get(wr::AsUint64(aPipelineId));
+  MOZ_ASSERT(holder);
+  if (!holder) {
+    SharedSurfacesParent::Release(aImageId);
+    return;
+  }
+
+  holder->mExternalImages.push(ForwardingExternalImage(aEpoch, aImageId));
+}
+
+void
 AsyncImagePipelineManager::PipelineRendered(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch)
 {
   if (mDestroyed) {
     return;
   }
   if (auto entry = mPipelineTexturesHolders.Lookup(wr::AsUint64(aPipelineId))) {
     PipelineTexturesHolder* holder = entry.Data();
     // Release TextureHosts based on Epoch
     while (!holder->mTextureHosts.empty()) {
       if (aEpoch <= holder->mTextureHosts.front().mEpoch) {
         break;
       }
       holder->mTextureHosts.pop();
     }
+    while (!holder->mExternalImages.empty()) {
+      if (aEpoch <= holder->mExternalImages.front().mEpoch) {
+        break;
+      }
+      DebugOnly<bool> released =
+        SharedSurfacesParent::Release(holder->mExternalImages.front().mImageId);
+      MOZ_ASSERT(released);
+      holder->mExternalImages.pop();
+    }
   }
 }
 
 void
 AsyncImagePipelineManager::PipelineRemoved(const wr::PipelineId& aPipelineId)
 {
   if (mDestroyed) {
     return;
--- a/gfx/layers/wr/AsyncImagePipelineManager.h
+++ b/gfx/layers/wr/AsyncImagePipelineManager.h
@@ -43,16 +43,17 @@ protected:
 
 public:
   void Destroy();
 
   void AddPipeline(const wr::PipelineId& aPipelineId);
   void RemovePipeline(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch);
 
   void HoldExternalImage(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch, WebRenderTextureHost* aTexture);
+  void HoldExternalImage(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch, const wr::ExternalImageId& aImageId);
   void PipelineRendered(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch);
   void PipelineRemoved(const wr::PipelineId& aPipelineId);
 
   TimeStamp GetCompositionTime() const {
     return mCompositionTime;
   }
   void SetCompositionTime(TimeStamp aTimeStamp) {
     mCompositionTime = aTimeStamp;
@@ -112,19 +113,29 @@ private:
     ForwardingTextureHost(const wr::Epoch& aEpoch, TextureHost* aTexture)
       : mEpoch(aEpoch)
       , mTexture(aTexture)
     {}
     wr::Epoch mEpoch;
     CompositableTextureHostRef mTexture;
   };
 
+  struct ForwardingExternalImage {
+    ForwardingExternalImage(const wr::Epoch& aEpoch, const wr::ExternalImageId& aImageId)
+      : mEpoch(aEpoch)
+      , mImageId(aImageId)
+    {}
+    wr::Epoch mEpoch;
+    wr::ExternalImageId mImageId;
+  };
+
   struct PipelineTexturesHolder {
     // Holds forwarding WebRenderTextureHosts.
     std::queue<ForwardingTextureHost> mTextureHosts;
+    std::queue<ForwardingExternalImage> mExternalImages;
     Maybe<wr::Epoch> mDestroyedEpoch;
   };
 
   struct AsyncImagePipeline {
     AsyncImagePipeline();
     void Update(const LayoutDeviceRect& aScBounds,
                 const gfx::Matrix4x4& aScTransform,
                 const gfx::MaybeIntSize& aScaleToSize,
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -379,18 +379,26 @@ WebRenderBridgeParent::AddExternalImage(
                                         wr::TransactionBuilder& aResources)
 {
   Range<wr::ImageKey> keys(&aKey, 1);
   // Check if key is obsoleted.
   if (keys[0].mNamespace != mIdNamespace) {
     return true;
   }
 
-  RefPtr<DataSourceSurface> dSurf = SharedSurfacesParent::Get(aExtId);
+  RefPtr<DataSourceSurface> dSurf = SharedSurfacesParent::Acquire(aExtId);
   if (dSurf) {
+    bool inserted = mSharedSurfaceIds.EnsureInserted(wr::AsUint64(aExtId));
+    if (!inserted) {
+      // We already have a mapping for this image, so decrement the ownership
+      // counter just increased unnecessarily. This can happen when an image is
+      // slow to decode and we need to invalidate it by updating its image key.
+      SharedSurfacesParent::Release(aExtId);
+    }
+
     if (!gfxEnv::EnableWebRenderRecording()) {
       wr::ImageDescriptor descriptor(dSurf->GetSize(), dSurf->Stride(),
                                      dSurf->GetFormat());
       aResources.AddExternalImage(aKey, descriptor, aExtId,
                                   wr::WrExternalImageBufferType::ExternalBuffer,
                                   0);
       return true;
     }
@@ -938,23 +946,29 @@ WebRenderBridgeParent::AddExternalImageI
 
 void
 WebRenderBridgeParent::RemoveExternalImageId(const ExternalImageId& aImageId)
 {
   if (mDestroyed) {
     return;
   }
 
-  WebRenderImageHost* wrHost = mExternalImageIds.Get(wr::AsUint64(aImageId)).get();
+  uint64_t imageId = wr::AsUint64(aImageId);
+  if (mSharedSurfaceIds.EnsureRemoved(imageId)) {
+    mAsyncImageManager->HoldExternalImage(mPipelineId, mWrEpoch, aImageId);
+    return;
+  }
+
+  WebRenderImageHost* wrHost = mExternalImageIds.Get(imageId).get();
   if (!wrHost) {
     return;
   }
 
   wrHost->ClearWrBridge();
-  mExternalImageIds.Remove(wr::AsUint64(aImageId));
+  mExternalImageIds.Remove(imageId);
 
   return;
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvSetLayerObserverEpoch(const uint64_t& aLayerObserverEpoch)
 {
   if (mDestroyed) {
@@ -1427,16 +1441,21 @@ WebRenderBridgeParent::ClearResources()
   mExternalImageIds.Clear();
   for (auto iter = mAsyncCompositables.Iter(); !iter.Done(); iter.Next()) {
     wr::PipelineId pipelineId = wr::AsPipelineId(iter.Key());
     RefPtr<WebRenderImageHost> host = iter.Data();
     host->ClearWrBridge();
     mAsyncImageManager->RemoveAsyncImagePipeline(pipelineId, txn);
   }
   mAsyncCompositables.Clear();
+  for (auto iter = mSharedSurfaceIds.Iter(); !iter.Done(); iter.Next()) {
+    wr::ExternalImageId id = wr::ToExternalImageId(iter.Get()->GetKey());
+    mAsyncImageManager->HoldExternalImage(mPipelineId, mWrEpoch, id);
+  }
+  mSharedSurfaceIds.Clear();
 
   mAsyncImageManager->RemovePipeline(mPipelineId, wrEpoch);
   txn.RemovePipeline(mPipelineId);
 
   mApi->SendTransaction(txn);
 
   for (std::unordered_set<uint64_t>::iterator iter = mActiveAnimations.begin(); iter != mActiveAnimations.end(); iter++) {
     mAnimStorage->ClearById(*iter);
--- a/gfx/layers/wr/WebRenderBridgeParent.h
+++ b/gfx/layers/wr/WebRenderBridgeParent.h
@@ -246,16 +246,17 @@ private:
   RefPtr<AsyncImagePipelineManager> mAsyncImageManager;
   RefPtr<CompositorVsyncScheduler> mCompositorScheduler;
   RefPtr<CompositorAnimationStorage> mAnimStorage;
   // mActiveAnimations is used to avoid leaking animations when WebRenderBridgeParent is
   // destroyed abnormally and Tab move between different windows.
   std::unordered_set<uint64_t> mActiveAnimations;
   nsDataHashtable<nsUint64HashKey, RefPtr<WebRenderImageHost>> mAsyncCompositables;
   nsDataHashtable<nsUint64HashKey, RefPtr<WebRenderImageHost>> mExternalImageIds;
+  nsTHashtable<nsUint64HashKey> mSharedSurfaceIds;
 
   TimeStamp mPreviousFrameTimeStamp;
   // These fields keep track of the latest layer observer epoch values in the child and the
   // parent. mChildLayerObserverEpoch is the latest epoch value received from the child.
   // mParentLayerObserverEpoch is the latest epoch value that we have told TabParent about
   // (via ObserveLayerUpdate).
   uint64_t mChildLayerObserverEpoch;
   uint64_t mParentLayerObserverEpoch;
--- a/gfx/layers/wr/WebRenderCommandBuilder.cpp
+++ b/gfx/layers/wr/WebRenderCommandBuilder.cpp
@@ -1067,20 +1067,17 @@ WebRenderCommandBuilder::DoGroupingForDi
   g.ConstructGroups(this, aBuilder, aResources, &group, aList, aSc);
   mScrollingHelper.EndList(aSc);
 }
 
 void
 WebRenderCommandBuilder::Destroy()
 {
   mLastCanvasDatas.Clear();
-  RemoveUnusedAndResetWebRenderUserData();
-  // UserDatas should only be in the used state during a call to WebRenderCommandBuilder::BuildWebRenderCommands
-  // The should always be false upon return from BuildWebRenderCommands().
-  MOZ_RELEASE_ASSERT(mWebRenderUserDatas.Count() == 0);
+  ClearCachedResources();
 }
 
 void
 WebRenderCommandBuilder::EmptyTransaction()
 {
   // We need to update canvases that might have changed.
   for (auto iter = mLastCanvasDatas.Iter(); !iter.Done(); iter.Next()) {
     RefPtr<WebRenderCanvasData> canvasData = iter.Get()->GetKey();
@@ -1827,20 +1824,20 @@ WebRenderCommandBuilder::RemoveUnusedAnd
 
     data->SetUsed(false);
   }
 }
 
 void
 WebRenderCommandBuilder::ClearCachedResources()
 {
-  for (auto iter = mWebRenderUserDatas.Iter(); !iter.Done(); iter.Next()) {
-    WebRenderUserData* data = iter.Get()->GetKey();
-    data->ClearCachedResources();
-  }
+  RemoveUnusedAndResetWebRenderUserData();
+  // UserDatas should only be in the used state during a call to WebRenderCommandBuilder::BuildWebRenderCommands
+  // The should always be false upon return from BuildWebRenderCommands().
+  MOZ_RELEASE_ASSERT(mWebRenderUserDatas.Count() == 0);
 }
 
 
 
 WebRenderGroupData::WebRenderGroupData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem)
   : WebRenderUserData(aWRManager, aItem)
 {
   MOZ_COUNT_CTOR(WebRenderGroupData);
--- a/gfx/layers/wr/WebRenderUserData.cpp
+++ b/gfx/layers/wr/WebRenderUserData.cpp
@@ -62,59 +62,41 @@ WebRenderUserData::WrBridge() const
 WebRenderImageData::WebRenderImageData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem)
   : WebRenderUserData(aWRManager, aItem)
   , mOwnsKey(false)
 {
 }
 
 WebRenderImageData::~WebRenderImageData()
 {
-  DoClearCachedResources();
+  ClearImageKey();
+
+  if (mExternalImageId) {
+    WrBridge()->DeallocExternalImageId(mExternalImageId.ref());
+  }
+
+  if (mPipelineId) {
+    WrBridge()->RemovePipelineIdForCompositable(mPipelineId.ref());
+  }
 }
 
 void
 WebRenderImageData::ClearImageKey()
 {
   if (mKey) {
     // If we don't own the key, then the owner is responsible for discarding the
     // key when appropriate.
     if (mOwnsKey) {
       mWRManager->AddImageKeyForDiscard(mKey.value());
     }
     mKey.reset();
   }
   mOwnsKey = false;
 }
 
-void
-WebRenderImageData::ClearCachedResources()
-{
-  DoClearCachedResources();
-}
-
-void
-WebRenderImageData::DoClearCachedResources()
-{
-  ClearImageKey();
-
-  if (mExternalImageId) {
-    WrBridge()->DeallocExternalImageId(mExternalImageId.ref());
-    mExternalImageId.reset();
-  }
-
-  if (mPipelineId) {
-    WrBridge()->RemovePipelineIdForCompositable(mPipelineId.ref());
-    mPipelineId.reset();
-  }
-
-  if (mImageClient) {
-    mImageClient = nullptr;
-  }
-}
-
 Maybe<wr::ImageKey>
 WebRenderImageData::UpdateImageKey(ImageContainer* aContainer,
                                    wr::IpcResourceUpdateQueue& aResources,
                                    bool aFallback)
 {
   MOZ_ASSERT(aContainer);
 
   if (mContainer != aContainer) {
@@ -270,24 +252,16 @@ WebRenderFallbackData::WebRenderFallback
   , mInvalid(false)
 {
 }
 
 WebRenderFallbackData::~WebRenderFallbackData()
 {
 }
 
-void
-WebRenderFallbackData::ClearCachedResources()
-{
-  WebRenderImageData::ClearCachedResources();
-  mBasicLayerManager = nullptr;
-  mInvalid = true;
-}
-
 nsDisplayItemGeometry*
 WebRenderFallbackData::GetGeometry()
 {
   return mGeometry.get();
 }
 
 void
 WebRenderFallbackData::SetGeometry(nsAutoPtr<nsDisplayItemGeometry> aGeometry)
@@ -315,28 +289,16 @@ WebRenderAnimationData::~WebRenderAnimat
 
 WebRenderCanvasData::WebRenderCanvasData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem)
   : WebRenderUserData(aWRManager, aItem)
 {
 }
 
 WebRenderCanvasData::~WebRenderCanvasData()
 {
-  DoClearCachedResources();
-}
-
-void
-WebRenderCanvasData::ClearCachedResources()
-{
-  DoClearCachedResources();
-}
-
-void
-WebRenderCanvasData::DoClearCachedResources()
-{
   if (mCanvasRenderer) {
     mCanvasRenderer->ClearCachedResources();
   }
 }
 
 void
 WebRenderCanvasData::ClearCanvasRenderer()
 {
--- a/gfx/layers/wr/WebRenderUserData.h
+++ b/gfx/layers/wr/WebRenderUserData.h
@@ -57,17 +57,16 @@ public:
   };
 
   virtual UserDataType GetType() = 0;
   bool IsUsed() { return mUsed; }
   void SetUsed(bool aUsed) { mUsed = aUsed; }
   nsIFrame* GetFrame() { return mFrame; }
   uint32_t GetDisplayItemKey() { return mDisplayItemKey; }
   void RemoveFromTable();
-  virtual void ClearCachedResources() {};
   virtual nsDisplayItemGeometry* GetGeometry() { return nullptr; }
 protected:
   virtual ~WebRenderUserData();
 
   WebRenderBridgeChild* WrBridge() const;
 
   RefPtr<WebRenderLayerManager> mWRManager;
   nsIFrame* mFrame;
@@ -121,27 +120,25 @@ public:
                                          const LayoutDeviceRect& aSCBounds,
                                          const gfx::Matrix4x4& aSCTransform,
                                          const gfx::MaybeIntSize& aScaleToSize,
                                          const wr::ImageRendering& aFilter,
                                          const wr::MixBlendMode& aMixBlendMode,
                                          bool aIsBackfaceVisible);
 
   void CreateImageClientIfNeeded();
-  void ClearCachedResources() override;
 
   bool IsAsync()
   {
     return mPipelineId.isSome();
   }
 
 protected:
   void ClearImageKey();
   void CreateExternalImageIfNeeded();
-  void DoClearCachedResources();
 
   wr::MaybeExternalImageId mExternalImageId;
   Maybe<wr::ImageKey> mKey;
   RefPtr<ImageClient> mImageClient;
   Maybe<wr::PipelineId> mPipelineId;
   RefPtr<ImageContainer> mContainer;
   bool mOwnsKey;
 };
@@ -150,17 +147,16 @@ class WebRenderFallbackData : public Web
 {
 public:
   explicit WebRenderFallbackData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem);
   virtual ~WebRenderFallbackData();
 
   virtual WebRenderFallbackData* AsFallbackData() override { return this; }
   virtual UserDataType GetType() override { return UserDataType::eFallback; }
   static UserDataType Type() { return UserDataType::eFallback; }
-  void ClearCachedResources() override;
   nsDisplayItemGeometry* GetGeometry() override;
   void SetGeometry(nsAutoPtr<nsDisplayItemGeometry> aGeometry);
   nsRect GetBounds() { return mBounds; }
   void SetBounds(const nsRect& aRect) { mBounds = aRect; }
   void SetInvalid(bool aInvalid) { mInvalid = aInvalid; }
   void SetScale(gfx::Size aScale) { mScale = aScale; }
   gfx::Size GetScale() { return mScale; }
   bool IsInvalid() { return mInvalid; }
@@ -195,19 +191,17 @@ public:
 
   virtual WebRenderCanvasData* AsCanvasData() override { return this; }
   virtual UserDataType GetType() override { return UserDataType::eCanvas; }
   static UserDataType Type() { return UserDataType::eCanvas; }
 
   void ClearCanvasRenderer();
   WebRenderCanvasRendererAsync* GetCanvasRenderer();
   WebRenderCanvasRendererAsync* CreateCanvasRenderer();
-  void ClearCachedResources() override;
 protected:
-  void DoClearCachedResources();
 
   UniquePtr<WebRenderCanvasRendererAsync> mCanvasRenderer;
 };
 
 extern void DestroyWebRenderUserDataTable(WebRenderUserDataTable* aTable);
 
 struct WebRenderUserDataProperty {
   NS_DECLARE_FRAME_PROPERTY_WITH_DTOR(Key, WebRenderUserDataTable, DestroyWebRenderUserDataTable)
--- a/gfx/src/FontPropertyTypes.h
+++ b/gfx/src/FontPropertyTypes.h
@@ -6,45 +6,47 @@
 /* font specific types shared by both thebes and layout */
 
 #ifndef GFX_FONT_PROPERTY_TYPES_H
 #define GFX_FONT_PROPERTY_TYPES_H
 
 #include <cstdint>
 #include <cmath>
 #include "mozilla/Assertions.h"
+#include "nsStringFwd.h"
 
 /*
  * This file is separate from gfxFont.h so that layout can include it
  * without bringing in gfxFont.h and everything it includes.
  */
 
 namespace mozilla {
 
 /**
  * Generic template for font property type classes that use a fixed-point
  * internal representation.
  * Template parameters:
- *   T - the integer type to use as the internal representation (e.g. uint16_t)
+ *   InternalType - the integer type to use as the internal representation (e.g.
+ *                  uint16_t)
  *       * NOTE that T must NOT be plain /int/, as that would result in
  *         ambiguity between constructors from /int/ and /T/, which mean
  *         different things.
  *   FractionBits - number of bits to use for the fractional part
  *   Min, Max - [inclusive] limits to the range of values that may be stored
  * Values are constructed from and exposed as floating-point, but stored
  * internally as fixed point, so there will be a quantization effect on
  * fractional values, depending on the number of fractional bits used.
  * Using (16-bit) fixed-point types rather than floats for these style
- * attributes reduces the memory footprint of gfxFontEntry and gfxFontStyle;
+ * attributes reduces the memory footprint of gfxFontEntry and gfxFontSlantStyle;
  * it will also tend to reduce the number of distinct font instances that
  * get created, particularly when styles are animated or set to arbitrary
  * values (e.g. by sliders in the UI), which should reduce pressure on
  * graphics resources and improve cache hit rates.
  */
-template<class T,unsigned FractionBits,int Min,int Max>
+template<class InternalType, unsigned FractionBits, int Min, int Max>
 class FontPropertyValue
 {
 public:
   // Ugh. We need a default constructor to allow this type to be used in the
   // union in nsCSSValue. Furthermore we need the default and copy
   // constructors to be "trivial" (i.e. the compiler implemented defaults that
   // do no initialization).
   // Annoyingly we can't make the default implementations constexpr (at least
@@ -83,62 +85,61 @@ public:
   // The difference between two values, returned as a raw floating-point number
   // (which might not be a valid property value in its own right).
   float operator-(const FontPropertyValue& aOther) const
   {
     return (mValue - aOther.mValue) * kInverseScale;
   }
 
   /// Return the raw internal representation, for purposes of hashing.
-  T ForHash() const
+  InternalType ForHash() const
   {
     return mValue;
   }
 
+  static constexpr const float kMin = float(Min);
+  static constexpr const float kMax = float(Max);
+
 protected:
-  typedef T internal_type;
-
   // Construct from a floating-point or integer value, checking that it is
   // within the allowed range and converting to fixed-point representation.
   explicit FontPropertyValue(float aValue)
     : mValue(std::round(aValue * kScale))
   {
     MOZ_ASSERT(aValue >= kMin && aValue <= kMax);
   }
   explicit FontPropertyValue(int aValue)
     : mValue(aValue << kFractionBits)
   {
     MOZ_ASSERT(aValue >= Min && aValue <= Max);
   }
 
   // Construct directly from a fixed-point value of type T, with no check;
   // note that there may be special "flag" values that are outside the normal
   // min/max range (e.g. for font-style:italic, distinct from oblique angle).
-  explicit FontPropertyValue(T aValue)
+  explicit FontPropertyValue(InternalType aValue)
     : mValue(aValue)
   {
   }
 
   // This is protected as it may not be the most appropriate accessor for a
   // given instance to expose. It's up to each individual property to provide
   // public accessors that forward to this as required.
   float ToFloat() const { return mValue * kInverseScale; }
   int ToIntRounded() const { return (mValue + kPointFive) >> FractionBits; }
 
   static constexpr float kScale = float(1u << FractionBits);
   static constexpr float kInverseScale = 1.0f / kScale;
-  static constexpr float kMin = float(Min);
-  static constexpr float kMax = float(Max);
   static const unsigned kFractionBits = FractionBits;
 
   // Constant representing 0.5 in the internal representation (note this
   // assumes that kFractionBits is greater than zero!)
-  static const T kPointFive = 1u << (kFractionBits - 1);
+  static const InternalType kPointFive = 1u << (kFractionBits - 1);
 
-  T mValue;
+  InternalType mValue;
 };
 
 /**
  * font-weight: range 1..1000, fractional values permitted; keywords
  * 'normal', 'bold' aliased to 400, 700 respectively; relative keywords
  * 'lighter', 'bolder' (not currently handled here).
  *
  * We use an unsigned 10.6 fixed-point value (range 0.0 - 1023.984375)
@@ -181,26 +182,28 @@ public:
 
   bool IsNormal() const { return mValue == kNormal; }
   bool IsBold() const { return mValue >= kBoldThreshold; }
 
   float ToFloat() const { return FontPropertyValue::ToFloat(); }
   int ToIntRounded() const { return FontPropertyValue::ToIntRounded(); }
 
 private:
-  explicit FontWeight(internal_type aValue)
+  typedef uint16_t InternalType;
+
+  explicit FontWeight(InternalType aValue)
     : FontPropertyValue(aValue)
   {
   }
 
-  static const internal_type kNormal        = 400u << kFractionBits;
-  static const internal_type kBold          = 700u << kFractionBits;
-  static const internal_type kBoldThreshold = 600u << kFractionBits;
-  static const internal_type kThin          = 100u << kFractionBits;
-  static const internal_type kExtraBold     = 900u << kFractionBits;
+  static const InternalType kNormal        = 400u << kFractionBits;
+  static const InternalType kBold          = 700u << kFractionBits;
+  static const InternalType kBoldThreshold = 600u << kFractionBits;
+  static const InternalType kThin          = 100u << kFractionBits;
+  static const InternalType kExtraBold     = 900u << kFractionBits;
 };
 
 /**
  * font-stretch is represented as a percentage relative to 'normal'.
  *
  * css-fonts says the value must be >= 0%, and normal is 100%. Keywords
  * from ultra-condensed to ultra-expanded are aliased to percentages
  * from 50% to 200%; values outside that range are unlikely to be common,
@@ -260,77 +263,92 @@ public:
   {
     return FontStretch(kUltraExpanded);
   }
 
   bool IsNormal() const { return mValue == kNormal; }
   float Percentage() const { return ToFloat(); }
 
 private:
-  static const internal_type kUltraCondensed =  50u << kFractionBits;
-  static const internal_type kExtraCondensed = (62u << kFractionBits) + kPointFive;
-  static const internal_type kCondensed      =  75u << kFractionBits;
-  static const internal_type kSemiCondensed  = (87u << kFractionBits) + kPointFive;
-  static const internal_type kNormal         = 100u << kFractionBits;
-  static const internal_type kSemiExpanded  = (112u << kFractionBits) + kPointFive;
-  static const internal_type kExpanded       = 125u << kFractionBits;
-  static const internal_type kExtraExpanded  = 150u << kFractionBits;
-  static const internal_type kUltraExpanded  = 200u << kFractionBits;
+  typedef uint16_t InternalType;
+
+  explicit FontStretch(InternalType aValue)
+    : FontPropertyValue(aValue)
+  {
+  }
+
+  static const InternalType kUltraCondensed =  50u << kFractionBits;
+  static const InternalType kExtraCondensed = (62u << kFractionBits) + kPointFive;
+  static const InternalType kCondensed      =  75u << kFractionBits;
+  static const InternalType kSemiCondensed  = (87u << kFractionBits) + kPointFive;
+  static const InternalType kNormal         = 100u << kFractionBits;
+  static const InternalType kSemiExpanded  = (112u << kFractionBits) + kPointFive;
+  static const InternalType kExpanded       = 125u << kFractionBits;
+  static const InternalType kExtraExpanded  = 150u << kFractionBits;
+  static const InternalType kUltraExpanded  = 200u << kFractionBits;
 };
 
 /**
  * font-style: normal | italic | oblique <angle>?
  * values of <angle> below -90 or above 90 not permitted
  * - Use a signed 8.8 fixed-point value
  *   (representable range -128.0 - 127.99609375)
  * - Define min value (-128.0) as meaning 'normal'
  * - Define max value (127.99609375) as 'italic'
  * - Other values represent 'oblique <angle>'
  * - Note that 'oblique 0deg' is distinct from 'normal' (should it be?)
  */
-class FontStyle final : public FontPropertyValue<int16_t,8,-90,90>
+class FontSlantStyle final : public FontPropertyValue<int16_t,8,-90,90>
 {
+  typedef int16_t InternalType;
 public:
+  const static constexpr float kDefaultAngle = 14.0;
+
   // See comment in FontPropertyValue regarding requirement for a trivial
   // default constructor.
-  FontStyle() = default;
+  FontSlantStyle() = default;
 
-  static FontStyle Normal()
+  static FontSlantStyle Normal()
   {
-    return FontStyle(kNormal);
+    return FontSlantStyle(kNormal);
   }
 
-  static FontStyle Italic()
+  static FontSlantStyle Italic()
   {
-    return FontStyle(kItalic);
+    return FontSlantStyle(kItalic);
   }
 
-  static FontStyle Oblique(float aAngle = 14.0f)
+  static FontSlantStyle Oblique(float aAngle = kDefaultAngle)
   {
-    return FontStyle(aAngle);
+    return FontSlantStyle(aAngle);
   }
 
   bool IsNormal() const { return mValue == kNormal; }
   bool IsItalic() const { return mValue == kItalic; }
   bool IsOblique() const { return mValue != kItalic && mValue != kNormal; }
 
   float ObliqueAngle() const
   {
     // It's not meaningful to get the oblique angle from a style that is
     // actually 'normal' or 'italic'.
-    MOZ_ASSERT(!IsItalic() && !IsNormal());
+    MOZ_ASSERT(IsOblique());
     return ToFloat();
   }
 
 private:
-  explicit FontStyle(float aAngle)
+  explicit FontSlantStyle(InternalType aConstant)
+    : FontPropertyValue(aConstant)
+  {
+  }
+
+  explicit FontSlantStyle(float aAngle)
     : FontPropertyValue(aAngle)
   {
   }
 
-  static const int16_t kNormal = INT16_MIN;
-  static const int16_t kItalic = INT16_MAX;
+  static const InternalType kNormal = INT16_MIN;
+  static const InternalType kItalic = INT16_MAX;
 };
 
 } // namespace mozilla
 
 #endif // GFX_FONT_PROPERTY_TYPES_H
 
--- a/gfx/src/nsFont.h
+++ b/gfx/src/nsFont.h
@@ -40,24 +40,23 @@ const uint8_t kGenericFont_moz_fixed    
 const uint8_t kGenericFont_serif        = 0x02;
 const uint8_t kGenericFont_sans_serif   = 0x04;
 const uint8_t kGenericFont_monospace    = 0x08;
 const uint8_t kGenericFont_cursive      = 0x10;
 const uint8_t kGenericFont_fantasy      = 0x20;
 
 // Font structure.
 struct nsFont {
+  typedef mozilla::FontStretch FontStretch;
+  typedef mozilla::FontSlantStyle FontSlantStyle;
   typedef mozilla::FontWeight FontWeight;
 
   // list of font families, either named or generic
   mozilla::FontFamilyList fontlist;
 
-  // The style of font (normal, italic, oblique; see gfxFontConstants.h)
-  uint8_t style = NS_FONT_STYLE_NORMAL;
-
   // Force this font to not be considered a 'generic' font, even if
   // the name is the same as a CSS generic font family.
   bool systemFont = false;
 
   // Variant subproperties
   uint8_t variantCaps = NS_FONT_VARIANT_CAPS_NORMAL;
   uint8_t variantNumeric = NS_FONT_VARIANT_NUMERIC_NORMAL;
   uint8_t variantPosition = NS_FONT_VARIANT_POSITION_NORMAL;
@@ -75,22 +74,19 @@ struct nsFont {
 
   // Smoothing - controls subpixel-antialiasing (currently OSX only)
   uint8_t smoothing = NS_FONT_SMOOTHING_AUTO;
 
   // The estimated background color behind the text. Enables a special
   // rendering mode when NS_GET_A(.) > 0. Only used for text in the chrome.
   nscolor fontSmoothingBackgroundColor = NS_RGBA(0,0,0,0);
 
-  // The weight of the font; see gfxFontConstants.h.
+  FontSlantStyle style = FontSlantStyle::Normal();
   FontWeight weight = FontWeight::Normal();
-
-  // The stretch of the font (the sum of various NS_FONT_STRETCH_*
-  // constants; see gfxFontConstants.h).
-  int16_t stretch = NS_FONT_STRETCH_NORMAL;
+  FontStretch stretch = FontStretch::Normal();
 
   // Kerning
   uint8_t kerning = NS_FONT_KERNING_AUTO;
 
   // Whether automatic optical sizing should be applied to variation fonts
   // that include an 'opsz' axis
   uint8_t opticalSizing = NS_FONT_OPTICAL_SIZING_AUTO;
 
--- a/gfx/thebes/gfxDWriteCommon.h
+++ b/gfx/thebes/gfxDWriteCommon.h
@@ -3,79 +3,86 @@
  * 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 GFX_DWRITECOMMON_H
 #define GFX_DWRITECOMMON_H
 
 // Mozilla includes
 #include "mozilla/MemoryReporting.h"
+#include "mozilla/FontPropertyTypes.h"
 #include "nscore.h"
 #include "nsIServiceManager.h"
 #include "nsCOMPtr.h"
 #include "cairo-features.h"
 #include "gfxFontConstants.h"
 #include "nsTArray.h"
 #include "gfxWindowsPlatform.h"
 #include "nsIUUIDGenerator.h"
 
 #include <windows.h>
 #include <dwrite.h>
 
 static inline DWRITE_FONT_STRETCH
-DWriteFontStretchFromStretch(uint16_t aStretch)
+DWriteFontStretchFromStretch(mozilla::FontStretch aStretch)
 {
-    switch (aStretch) {
-        case NS_FONT_STRETCH_ULTRA_CONDENSED:
-            return DWRITE_FONT_STRETCH_ULTRA_CONDENSED;
-        case NS_FONT_STRETCH_EXTRA_CONDENSED:
-            return DWRITE_FONT_STRETCH_EXTRA_CONDENSED;
-        case NS_FONT_STRETCH_CONDENSED:
-            return DWRITE_FONT_STRETCH_CONDENSED;
-        case NS_FONT_STRETCH_SEMI_CONDENSED:
-            return DWRITE_FONT_STRETCH_SEMI_CONDENSED;
-        case NS_FONT_STRETCH_NORMAL:
-            return DWRITE_FONT_STRETCH_NORMAL;
-        case NS_FONT_STRETCH_SEMI_EXPANDED:
-            return DWRITE_FONT_STRETCH_SEMI_EXPANDED;
-        case NS_FONT_STRETCH_EXPANDED:
-            return DWRITE_FONT_STRETCH_EXPANDED;
-        case NS_FONT_STRETCH_EXTRA_EXPANDED:
-            return DWRITE_FONT_STRETCH_EXTRA_EXPANDED;
-        case NS_FONT_STRETCH_ULTRA_EXPANDED:
-            return DWRITE_FONT_STRETCH_ULTRA_EXPANDED;
-        default:
-            return DWRITE_FONT_STRETCH_UNDEFINED;
+    if (aStretch == mozilla::FontStretch::UltraCondensed()) {
+        return DWRITE_FONT_STRETCH_ULTRA_CONDENSED;
+    }
+    if (aStretch == mozilla::FontStretch::ExtraCondensed()) {
+        return DWRITE_FONT_STRETCH_EXTRA_CONDENSED;
+    }
+    if (aStretch == mozilla::FontStretch::Condensed()) {
+        return DWRITE_FONT_STRETCH_CONDENSED;
+    }
+    if (aStretch == mozilla::FontStretch::SemiCondensed()) {
+        return DWRITE_FONT_STRETCH_SEMI_CONDENSED;
     }
+    if (aStretch == mozilla::FontStretch::Normal()) {
+        return DWRITE_FONT_STRETCH_NORMAL;
+    }
+    if (aStretch == mozilla::FontStretch::SemiExpanded()) {
+        return DWRITE_FONT_STRETCH_SEMI_EXPANDED;
+    }
+    if (aStretch == mozilla::FontStretch::Expanded()) {
+        return DWRITE_FONT_STRETCH_EXPANDED;
+    }
+    if (aStretch == mozilla::FontStretch::ExtraExpanded()) {
+        return DWRITE_FONT_STRETCH_EXTRA_EXPANDED;
+    }
+    if (aStretch == mozilla::FontStretch::UltraExpanded()) {
+        return DWRITE_FONT_STRETCH_ULTRA_EXPANDED;
+    }
+    return DWRITE_FONT_STRETCH_UNDEFINED;
 }
 
-static inline uint16_t
+static inline mozilla::FontStretch
 FontStretchFromDWriteStretch(DWRITE_FONT_STRETCH aStretch)
 {
     switch (aStretch) {
         case DWRITE_FONT_STRETCH_ULTRA_CONDENSED:
-            return NS_FONT_STRETCH_ULTRA_CONDENSED;
+            return mozilla::FontStretch::UltraCondensed();
         case DWRITE_FONT_STRETCH_EXTRA_CONDENSED:
-            return NS_FONT_STRETCH_EXTRA_CONDENSED;
+            return mozilla::FontStretch::ExtraCondensed();
         case DWRITE_FONT_STRETCH_CONDENSED:
-            return NS_FONT_STRETCH_CONDENSED;
+            return mozilla::FontStretch::Condensed();
         case DWRITE_FONT_STRETCH_SEMI_CONDENSED:
-            return NS_FONT_STRETCH_SEMI_CONDENSED;
+            return mozilla::FontStretch::SemiCondensed();
         case DWRITE_FONT_STRETCH_NORMAL:
-            return NS_FONT_STRETCH_NORMAL;
+            return mozilla::FontStretch::Normal();
         case DWRITE_FONT_STRETCH_SEMI_EXPANDED:
-            return NS_FONT_STRETCH_SEMI_EXPANDED;
+            return mozilla::FontStretch::SemiExpanded();
         case DWRITE_FONT_STRETCH_EXPANDED:
-            return NS_FONT_STRETCH_EXPANDED;
+            return mozilla::FontStretch::Expanded();
         case DWRITE_FONT_STRETCH_EXTRA_EXPANDED:
-            return NS_FONT_STRETCH_EXTRA_EXPANDED;
+            return mozilla::FontStretch::ExtraExpanded();
         case DWRITE_FONT_STRETCH_ULTRA_EXPANDED:
-            return NS_FONT_STRETCH_ULTRA_EXPANDED;
+            return mozilla::FontStretch::UltraExpanded();
         default:
-            return NS_FONT_STRETCH_NORMAL;
+            return mozilla::FontStretch::Normal();
     }
 }
 
 class gfxDWriteFontFileLoader : public IDWriteFontFileLoader
 {
 public:
     gfxDWriteFontFileLoader()
     {
--- a/gfx/thebes/gfxDWriteFontList.cpp
+++ b/gfx/thebes/gfxDWriteFontList.cpp
@@ -403,17 +403,17 @@ gfxDWriteFontEntry::CopyFontTable(uint32
     gfxDWriteFontList *pFontList = gfxDWriteFontList::PlatformFontList();
     const uint32_t tagBE = NativeEndian::swapToBigEndian(aTableTag);
 
     // Don't use GDI table loading for symbol fonts or for
     // italic fonts in Arabic-script system locales because of
     // potential cmap discrepancies, see bug 629386.
     // Ditto for Hebrew, bug 837498.
     if (mFont && pFontList->UseGDIFontTableAccess() &&
-        !(mStyle && UsingArabicOrHebrewScriptSystemLocale()) &&
+        !(!IsUpright() && UsingArabicOrHebrewScriptSystemLocale()) &&
         !mFont->IsSymbolFont())
     {
         LOGFONTW logfont = { 0 };
         if (InitLogFont(mFont, &logfont)) {
             AutoDC dc;
             AutoSelectFont font(dc.GetDC(), &logfont);
             if (font.IsValid()) {
                 uint32_t tableSize =
@@ -953,18 +953,18 @@ gfxDWriteFontList::GetDefaultFontForPlat
     }
 
     return nullptr;
 }
 
 gfxFontEntry *
 gfxDWriteFontList::LookupLocalFont(const nsAString& aFontName,
                                    FontWeight aWeight,
-                                   uint16_t aStretch,
-                                   uint8_t aStyle)
+                                   FontStretch aStretch,
+                                   FontSlantStyle aStyle)
 {
     gfxFontEntry *lookup;
 
     lookup = LookupInFaceNameLists(aFontName);
     if (!lookup) {
         return nullptr;
     }
 
@@ -977,18 +977,18 @@ gfxDWriteFontList::LookupLocalFont(const
                                aStyle);
     fe->SetForceGDIClassic(dwriteLookup->GetForceGDIClassic());
     return fe;
 }
 
 gfxFontEntry *
 gfxDWriteFontList::MakePlatformFont(const nsAString& aFontName,
                                     FontWeight aWeight,
-                                    uint16_t aStretch,
-                                    uint8_t aStyle,
+                                    FontStretch aStretch,
+                                    FontSlantStyle aStyle,
                                     const uint8_t* aFontData,
                                     uint32_t aLength)
 {
     RefPtr<IDWriteFontFileStream> fontFileStream;
     RefPtr<IDWriteFontFile> fontFile;
     HRESULT hr =
       gfxDWriteFontFileLoader::CreateCustomFontFile(aFontData, aLength,
                                                     getter_AddRefs(fontFile),
--- a/gfx/thebes/gfxDWriteFontList.h
+++ b/gfx/thebes/gfxDWriteFontList.h
@@ -29,30 +29,31 @@
 #include "cairo-win32.h"
 
 #include "gfxPlatformFontList.h"
 #include "gfxPlatform.h"
 #include <algorithm>
 
 #include "mozilla/gfx/UnscaledFontDWrite.h"
 
-
 /**
  * gfxDWriteFontFamily is a class that describes one of the fonts on the
  * users system.  It holds each gfxDWriteFontEntry (maps more directly to
  * a font face) which holds font type, charset info and character map info.
  */
 class gfxDWriteFontEntry;
 
 /**
  * \brief Class representing directwrite font family.
  */
 class gfxDWriteFontFamily : public gfxFontFamily
 {
 public:
+    typedef mozilla::FontStretch FontStretch;
+    typedef mozilla::FontSlantStyle FontSlantStyle;
     typedef mozilla::FontWeight FontWeight;
 
     /**
      * Constructs a new DWriteFont Family.
      *
      * \param aName Name identifying the family
      * \param aFamily IDWriteFontFamily object representing the directwrite
      * family object.
@@ -110,19 +111,19 @@ public:
                        IDWriteFont *aFont,
                        bool aIsSystemFont = false)
       : gfxFontEntry(aFaceName), mFont(aFont), mFontFile(nullptr),
         mIsSystemFont(aIsSystemFont), mForceGDIClassic(false),
         mHasVariations(false), mHasVariationsInitialized(false)
     {
         DWRITE_FONT_STYLE dwriteStyle = aFont->GetStyle();
         mStyle = (dwriteStyle == DWRITE_FONT_STYLE_ITALIC ?
-                  NS_FONT_STYLE_ITALIC :
+                  FontSlantStyle::Italic() :
                   (dwriteStyle == DWRITE_FONT_STYLE_OBLIQUE ?
-                   NS_FONT_STYLE_OBLIQUE : NS_FONT_STYLE_NORMAL));
+                   FontSlantStyle::Oblique() : FontSlantStyle::Normal()));
         mStretch = FontStretchFromDWriteStretch(aFont->GetStretch());
         int weight = NS_ROUNDUP(aFont->GetWeight() - 50, 100);
 
         weight = mozilla::Clamp(weight, 100, 900);
         mWeight = FontWeight(weight);
 
         mIsCJK = UNINITIALIZED_VALUE;
     }
@@ -134,20 +135,20 @@ public:
      *
      * \param aFaceName The name of the corresponding font face.
      * \param aFont DirectWrite font object
      * \param aWeight Weight of the font
      * \param aStretch Stretch of the font
      * \param aStyle italic or oblique of font
      */
     gfxDWriteFontEntry(const nsAString& aFaceName,
-                              IDWriteFont *aFont,
-                              FontWeight aWeight,
-                              uint16_t aStretch,
-                              uint8_t aStyle)
+                       IDWriteFont *aFont,
+                       FontWeight aWeight,
+                       FontStretch aStretch,
+                       FontSlantStyle aStyle)
       : gfxFontEntry(aFaceName), mFont(aFont), mFontFile(nullptr),
         mIsSystemFont(false), mForceGDIClassic(false),
         mHasVariations(false), mHasVariationsInitialized(false)
     {
         mWeight = aWeight;
         mStretch = aStretch;
         mStyle = aStyle;
         mIsLocalUserFont = true;
@@ -163,18 +164,18 @@ public:
      * \param aWeight Weight of the font
      * \param aStretch Stretch of the font
      * \param aStyle italic or oblique of font
      */
     gfxDWriteFontEntry(const nsAString& aFaceName,
                               IDWriteFontFile *aFontFile,
                               IDWriteFontFileStream *aFontFileStream,
                               FontWeight aWeight,
-                              uint16_t aStretch,
-                              uint8_t aStyle)
+                              FontStretch aStretch,
+                              FontSlantStyle aStyle)
       : gfxFontEntry(aFaceName), mFont(nullptr),
         mFontFile(aFontFile), mFontFileStream(aFontFileStream),
         mIsSystemFont(false), mForceGDIClassic(false),
         mHasVariations(false), mHasVariationsInitialized(false)
     {
         mWeight = aWeight;
         mStretch = aStretch;
         mStyle = aStyle;
@@ -400,23 +401,23 @@ public:
 
     // initialize font lists
     virtual nsresult InitFontListForPlatform() override;
 
     gfxFontFamily* CreateFontFamily(const nsAString& aName) const override;
 
     virtual gfxFontEntry* LookupLocalFont(const nsAString& aFontName,
                                           FontWeight aWeight,
-                                          uint16_t aStretch,
-                                          uint8_t aStyle);
+                                          FontStretch aStretch,
+                                          FontSlantStyle aStyle);
 
     virtual gfxFontEntry* MakePlatformFont(const nsAString& aFontName,
                                            FontWeight aWeight,
-                                           uint16_t aStretch,
-                                           uint8_t aStyle,
+                                           FontStretch aStretch,
+                                           FontSlantStyle aStyle,
                                            const uint8_t* aFontData,
                                            uint32_t aLength);
     
     bool GetStandardFamilyName(const nsAString& aFontName,
                                  nsAString& aFamilyName);
 
     IDWriteGdiInterop *GetGDIInterop() { return mGDIInterop; }
     bool UseGDIFontTableAccess() { return mGDIFontTableAccess; }
--- a/gfx/thebes/gfxFT2FontList.cpp
+++ b/gfx/thebes/gfxFT2FontList.cpp
@@ -253,18 +253,18 @@ FT2FontEntry::CreateFontInstance(const g
     cairo_scaled_font_destroy(scaledFont);
     return font;
 }
 
 /* static */
 FT2FontEntry*
 FT2FontEntry::CreateFontEntry(const nsAString& aFontName,
                               FontWeight aWeight,
-                              uint16_t aStretch,
-                              uint8_t aStyle,
+                              FontStretch aStretch,
+                              FontSlantStyle aStyle,
                               const uint8_t* aFontData,
                               uint32_t aLength)
 {
     // Ownership of aFontData is passed in here; the fontEntry must
     // retain it as long as the FT_Face needs it, and ensure it is
     // eventually deleted.
     FT_Face face = Factory::NewFTFaceFromData(nullptr, aFontData, aLength, 0);
     if (!face) {
@@ -326,18 +326,19 @@ FT2FontEntry*
 FT2FontEntry::CreateFontEntry(const FontListEntry& aFLE)
 {
     FT2FontEntry *fe = new FT2FontEntry(aFLE.faceName());
     fe->mFilename = aFLE.filepath();
     fe->mFTFontIndex = aFLE.index();
     // The weight transported across IPC is a float, so we need to explicitly
     // convert it back to a FontWeight.
     fe->mWeight = FontWeight(aFLE.weight());
-    fe->mStretch = aFLE.stretch();
-    fe->mStyle = (aFLE.italic() ? NS_FONT_STYLE_ITALIC : NS_FONT_STYLE_NORMAL);
+    fe->mStretch = FontStretch(float(aFLE.stretch()));
+    fe->mStyle = aFLE.italic()
+      ? FontSlantStyle::Italic() : FontSlantStyle::Normal();
     return fe;
 }
 
 // Helpers to extract font entry properties from an FT_Face
 static bool
 FTFaceIsItalic(FT_Face aFace)
 {
     return !!(aFace->style_flags & FT_STYLE_FLAG_ITALIC);
@@ -386,17 +387,17 @@ FT2FontEntry*
 FT2FontEntry::CreateFontEntry(FT_Face aFace,
                               const char* aFilename, uint8_t aIndex,
                               const nsAString& aName,
                               const uint8_t* aFontData,
                               uint32_t aLength)
 {
     FT2FontEntry *fe = new FT2FontEntry(aName);
     fe->mStyle = (FTFaceIsItalic(aFace) ?
-                  NS_FONT_STYLE_ITALIC : NS_FONT_STYLE_NORMAL);
+                  FontSlantStyle::Italic() : FontSlantStyle::Normal());
     fe->mWeight = FTFaceGetWeight(aFace);
     fe->mFilename = aFilename;
     fe->mFTFontIndex = aIndex;
 
     if (aFontData) {
         fe->mFTFace = aFace;
         int flags = gfxPlatform::GetPlatform()->FontHintingEnabled() ?
                     FT_LOAD_DEFAULT :
@@ -661,21 +662,24 @@ FT2FontFamily::AddFacesToFontList(Infall
         const FT2FontEntry *fe =
             static_cast<const FT2FontEntry*>(mAvailableFonts[i].get());
         if (!fe) {
             continue;
         }
 
         // We convert the weight to a float purely for transport across IPC.
         // Ideally we'd avoid doing that.
-        aFontList->AppendElement(FontListEntry(Name(), fe->Name(),
+        aFontList->AppendElement(FontListEntry(Name(),
+                                               fe->Name(),
                                                fe->mFilename,
                                                fe->Weight().ToFloat(),
-                                               fe->Stretch(),
-                                               fe->mStyle,
+                                               fe->Stretch().Percentage(),
+                                               fe->mStyle.IsItalic()
+                                                ? NS_FONT_STYLE_ITALIC
+                                                : NS_FONT_STYLE_NORMAL,
                                                fe->mFTFontIndex));
     }
 }
 
 /*
  * Startup cache support for the font list:
  * We store the list of families and faces, with their style attributes and the
  * corresponding font files, in the startup cache.
@@ -988,17 +992,18 @@ AppendToFaceList(nsCString& aFaceList,
     aFaceList.Append(NS_ConvertUTF16toUTF8(aFontEntry->Name()));
     aFaceList.Append(',');
     aFaceList.AppendInt(aFontEntry->mFTFontIndex);
     aFaceList.Append(',');
     aFaceList.Append(aFontEntry->IsItalic() ? '1' : '0');
     aFaceList.Append(',');
     aFaceList.AppendFloat(aFontEntry->Weight().ToFloat());
     aFaceList.Append(',');
-    aFaceList.AppendInt(aFontEntry->Stretch());
+    // FIXME(emilio): Probably the stretch should be converted to float.
+    aFaceList.AppendInt(int32_t(aFontEntry->Stretch().Percentage()));
     aFaceList.Append(',');
 }
 
 void
 FT2FontEntry::CheckForBrokenFont(gfxFontFamily *aFamily)
 {
     // note if the family is in the "bad underline" blacklist
     if (aFamily->IsBadUnderlineFamily()) {
@@ -1145,21 +1150,21 @@ gfxFT2FontList::AddFaceToList(const nsCS
         fe->mStandardFace = (aStdFile == kStandard);
         family->AddFontEntry(fe);
 
         fe->CheckForBrokenFont(family);
 
         AppendToFaceList(aFaceList, name, fe);
         if (LOG_ENABLED()) {
             LOG(("(fontinit) added (%s) to family (%s)"
-                 " with style: %s weight: %g stretch: %d",
+                 " with style: %s weight: %g stretch: %g%%",
                  NS_ConvertUTF16toUTF8(fe->Name()).get(),
                  NS_ConvertUTF16toUTF8(family->Name()).get(),
                  fe->IsItalic() ? "italic" : "normal",
-                 fe->Weight().ToFloat(), fe->Stretch()));
+                 fe->Weight().ToFloat(), fe->Stretch().Percentage()));
         }
     }
 }
 
 void
 gfxFT2FontList::AppendFacesFromOmnijarEntry(nsZipArchive* aArchive,
                                             const nsCString& aEntryName,
                                             FontNameCache *aCache,
@@ -1460,18 +1465,18 @@ gfxFT2FontList::InitFontListForPlatform(
 }
 
 // called for each family name, based on the assumption that the
 // first part of the full name is the family name
 
 gfxFontEntry*
 gfxFT2FontList::LookupLocalFont(const nsAString& aFontName,
                                 FontWeight aWeight,
-                                uint16_t aStretch,
-                                uint8_t aStyle)
+                                FontStretch aStretch,
+                                FontSlantStyle aStyle)
 {
     // walk over list of names
     FT2FontEntry* fontEntry = nullptr;
     nsString fullName(aFontName);
 
     for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
         // Check family name, based on the assumption that the
         // first part of the full name is the family name
@@ -1542,18 +1547,18 @@ gfxFT2FontList::GetDefaultFontForPlatfor
 #endif
     /* TODO: what about Qt or other platforms that may use this? */
     return ff;
 }
 
 gfxFontEntry*
 gfxFT2FontList::MakePlatformFont(const nsAString& aFontName,
                                  FontWeight aWeight,
-                                 uint16_t aStretch,
-                                 uint8_t aStyle,
+                                 FontStretch aStretch,
+                                 FontSlantStyle aStyle,
                                  const uint8_t* aFontData,
                                  uint32_t aLength)
 {
     // The FT2 font needs the font data to persist, so we do NOT free it here
     // but instead pass ownership to the font entry.
     // Deallocation will happen later, when the font face is destroyed.
     return FT2FontEntry::CreateFontEntry(aFontName, aWeight, aStretch,
                                          aStyle, aFontData, aLength);
--- a/gfx/thebes/gfxFT2FontList.h
+++ b/gfx/thebes/gfxFT2FontList.h
@@ -39,18 +39,18 @@ public:
     const nsString& GetName() const {
         return Name();
     }
 
     // create a font entry for a downloaded font
     static FT2FontEntry* 
     CreateFontEntry(const nsAString& aFontName,
                     FontWeight aWeight,
-                    uint16_t aStretch,
-                    uint8_t aStyle,
+                    FontStretch aStretch,
+                    FontSlantStyle aStyle,
                     const uint8_t* aFontData,
                     uint32_t aLength);
 
     // create a font entry representing an installed font, identified by
     // a FontListEntry; the freetype and cairo faces will not be instantiated
     // until actually needed
     static FT2FontEntry*
     CreateFontEntry(const FontListEntry& aFLE);
@@ -118,23 +118,23 @@ public:
 class gfxFT2FontList : public gfxPlatformFontList
 {
 public:
     gfxFT2FontList();
     virtual ~gfxFT2FontList();
 
     virtual gfxFontEntry* LookupLocalFont(const nsAString& aFontName,
                                           FontWeight aWeight,
-                                          uint16_t aStretch,
-                                          uint8_t aStyle) override;
+                                          FontStretch aStretch,
+                                          FontSlantStyle aStyle) override;
 
     virtual gfxFontEntry* MakePlatformFont(const nsAString& aFontName,
                                            FontWeight aWeight,
-                                           uint16_t aStretch,
-                                           uint8_t aStyle,
+                                           FontStretch aStretch,
+                                           FontSlantStyle aStyle,
                                            const uint8_t* aFontData,
                                            uint32_t aLength) override;
 
     void GetSystemFontList(InfallibleTArray<FontListEntry>* retValue);
 
     static gfxFT2FontList* PlatformFontList() {
         return static_cast<gfxFT2FontList*>(gfxPlatformFontList::PlatformFontList());
     }
--- a/gfx/thebes/gfxFcPlatformFontList.cpp
+++ b/gfx/thebes/gfxFcPlatformFontList.cpp
@@ -191,63 +191,64 @@ MapFcWeight(int aFcWeight)
   if (aFcWeight <= FC_WEIGHT_BLACK) {
     return FontWeight(900);
   }
 
   // including FC_WEIGHT_EXTRABLACK
   return FontWeight(901);
 }
 
-static int16_t
+// TODO(emilio, jfkthame): I think this can now be more fine-grained.
+static FontStretch
 MapFcWidth(int aFcWidth)
 {
     if (aFcWidth <= (FC_WIDTH_ULTRACONDENSED + FC_WIDTH_EXTRACONDENSED) / 2) {
-        return NS_FONT_STRETCH_ULTRA_CONDENSED;
+        return FontStretch::UltraCondensed();
     }
     if (aFcWidth <= (FC_WIDTH_EXTRACONDENSED + FC_WIDTH_CONDENSED) / 2) {
-        return NS_FONT_STRETCH_EXTRA_CONDENSED;
+        return FontStretch::ExtraCondensed();
     }
     if (aFcWidth <= (FC_WIDTH_CONDENSED + FC_WIDTH_SEMICONDENSED) / 2) {
-        return NS_FONT_STRETCH_CONDENSED;
+        return FontStretch::Condensed();
     }
     if (aFcWidth <= (FC_WIDTH_SEMICONDENSED + FC_WIDTH_NORMAL) / 2) {
-        return NS_FONT_STRETCH_SEMI_CONDENSED;
+        return FontStretch::SemiCondensed();
     }
     if (aFcWidth <= (FC_WIDTH_NORMAL + FC_WIDTH_SEMIEXPANDED) / 2) {
-        return NS_FONT_STRETCH_NORMAL;
+        return FontStretch::Normal();
     }
     if (aFcWidth <= (FC_WIDTH_SEMIEXPANDED + FC_WIDTH_EXPANDED) / 2) {
-        return NS_FONT_STRETCH_SEMI_EXPANDED;
+        return FontStretch::SemiExpanded();
     }
     if (aFcWidth <= (FC_WIDTH_EXPANDED + FC_WIDTH_EXTRAEXPANDED) / 2) {
-        return NS_FONT_STRETCH_EXPANDED;
+        return FontStretch::Expanded();
     }
     if (aFcWidth <= (FC_WIDTH_EXTRAEXPANDED + FC_WIDTH_ULTRAEXPANDED) / 2) {
-        return NS_FONT_STRETCH_EXTRA_EXPANDED;
+        return FontStretch::ExtraExpanded();
     }
-    return NS_FONT_STRETCH_ULTRA_EXPANDED;
+    return FontStretch::UltraExpanded();
 }
 
 gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsAString& aFaceName,
                                                FcPattern* aFontPattern,
                                                bool aIgnoreFcCharmap)
         : gfxFontEntry(aFaceName), mFontPattern(aFontPattern),
           mFTFace(nullptr), mFTFaceInitialized(false),
           mIgnoreFcCharmap(aIgnoreFcCharmap),
           mAspect(0.0), mFontData(nullptr), mLength(0)
 {
     // italic
     int slant;
     if (FcPatternGetInteger(aFontPattern, FC_SLANT, 0, &slant) != FcResultMatch) {
         slant = FC_SLANT_ROMAN;
     }
     if (slant == FC_SLANT_OBLIQUE) {
-        mStyle = NS_FONT_STYLE_OBLIQUE;
+        mStyle = FontSlantStyle::Oblique();
     } else if (slant > 0) {
-        mStyle = NS_FONT_STYLE_ITALIC;
+        mStyle = FontSlantStyle::Italic();
     }
 
     // weight
     int weight;
     if (FcPatternGetInteger(aFontPattern, FC_WEIGHT, 0, &weight) != FcResultMatch) {
         weight = FC_WEIGHT_REGULAR;
     }
     mWeight = MapFcWeight(weight);
@@ -307,18 +308,18 @@ CreateFaceForPattern(FcPattern* aPattern
     if (FcPatternGetInteger(aPattern, FC_INDEX, 0, &index) != FcResultMatch) {
         index = 0; // default to 0 if not found in pattern
     }
     return Factory::NewFTFace(nullptr, ToCharPtr(filename), index);
 }
 
 gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsAString& aFaceName,
                                                FontWeight aWeight,
-                                               uint16_t aStretch,
-                                               uint8_t aStyle,
+                                               FontStretch aStretch,
+                                               FontSlantStyle aStyle,
                                                const uint8_t *aData,
                                                uint32_t aLength,
                                                FT_Face aFace)
     : gfxFontEntry(aFaceName),
       mFTFace(aFace), mFTFaceInitialized(true),
       mIgnoreFcCharmap(true),
       mAspect(0.0), mFontData(aData), mLength(aLength)
 {
@@ -330,18 +331,18 @@ gfxFontconfigFontEntry::gfxFontconfigFon
     mFontPattern = CreatePatternForFace(mFTFace);
 
     mUserFontData = new FTUserFontData(mFTFace, mFontData);
 }
 
 gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsAString& aFaceName,
                                                FcPattern* aFontPattern,
                                                FontWeight aWeight,
-                                               uint16_t aStretch,
-                                               uint8_t aStyle)
+                                               FontStretch aStretch,
+                                               FontSlantStyle aStyle)
         : gfxFontEntry(aFaceName), mFontPattern(aFontPattern),
           mFTFace(nullptr), mFTFaceInitialized(false),
           mAspect(0.0), mFontData(nullptr), mLength(0)
 {
     mWeight = aWeight;
     mStyle = aStyle;
     mStretch = aStretch;
     mIsLocalUserFont = true;
@@ -749,17 +750,17 @@ gfxFontconfigFontEntry::CreateScaledFont
                                          bool aNeedsBold)
 {
     if (aNeedsBold) {
         FcPatternAddBool(aRenderPattern, FC_EMBOLDEN, FcTrue);
     }
 
     // will synthetic oblique be applied using a transform?
     bool needsOblique = IsUpright() &&
-                        aStyle->style != NS_FONT_STYLE_NORMAL &&
+                        aStyle->style != FontSlantStyle::Normal() &&
                         aStyle->allowSyntheticStyle;
 
     if (needsOblique) {
         // disable embedded bitmaps (mimics behavior in 90-synthetic.conf)
         FcPatternDel(aRenderPattern, FC_EMBEDDED_BITMAP);
         FcPatternAddBool(aRenderPattern, FC_EMBEDDED_BITMAP, FcFalse);
     }
 
@@ -1197,23 +1198,24 @@ gfxFontconfigFontFamily::FindStyleVariat
         AddFontEntry(fontEntry);
 
         if (fontEntry->IsNormalStyle()) {
             numRegularFaces++;
         }
 
         if (LOG_FONTLIST_ENABLED()) {
             LOG_FONTLIST(("(fontlist) added (%s) to family (%s)"
-                 " with style: %s weight: %g stretch: %d"
+                 " with style: %s weight: %g stretch: %g%%"
                  " psname: %s fullname: %s",
                  NS_ConvertUTF16toUTF8(fontEntry->Name()).get(),
                  NS_ConvertUTF16toUTF8(Name()).get(),
                  (fontEntry->IsItalic()) ?
                   "italic" : (fontEntry->IsOblique() ? "oblique" : "normal"),
-                 fontEntry->Weight().ToFloat(), fontEntry->Stretch(),
+                 fontEntry->Weight().ToFloat(),
+                 fontEntry->Stretch().Percentage(),
                  NS_ConvertUTF16toUTF8(psname).get(),
                  NS_ConvertUTF16toUTF8(fullname).get()));
         }
     }
 
     // somewhat arbitrary, but define a family with two or more regular
     // faces as a family for which intra-family fallback should be used
     if (numRegularFaces > 1) {
@@ -1853,18 +1855,18 @@ gfxFcPlatformFontList::GetDefaultFontFor
         return (*prefFonts)[0];
     }
     return nullptr;
 }
 
 gfxFontEntry*
 gfxFcPlatformFontList::LookupLocalFont(const nsAString& aFontName,
                                        FontWeight aWeight,
-                                       uint16_t aStretch,
-                                       uint8_t aStyle)
+                                       FontStretch aStretch,
+                                       FontSlantStyle aStyle)
 {
     nsAutoString keyName(aFontName);
     ToLowerCase(keyName);
 
     // if name is not in the global list, done
     FcPattern* fontPattern = mLocalNames.Get(keyName);
     if (!fontPattern) {
         return nullptr;
@@ -1872,18 +1874,18 @@ gfxFcPlatformFontList::LookupLocalFont(c
 
     return new gfxFontconfigFontEntry(aFontName, fontPattern,
                                       aWeight, aStretch, aStyle);
 }
 
 gfxFontEntry*
 gfxFcPlatformFontList::MakePlatformFont(const nsAString& aFontName,
                                         FontWeight aWeight,
-                                        uint16_t aStretch,
-                                        uint8_t aStyle,
+                                        FontStretch aStretch,
+                                        FontSlantStyle aStyle,
                                         const uint8_t* aFontData,
                                         uint32_t aLength)
 {
     FT_Face face = Factory::NewFTFaceFromData(nullptr, aFontData, aLength, 0);
     if (!face) {
         free((void*)aFontData);
         return nullptr;
     }
--- a/gfx/thebes/gfxFcPlatformFontList.h
+++ b/gfx/thebes/gfxFcPlatformFontList.h
@@ -90,28 +90,28 @@ public:
     explicit gfxFontconfigFontEntry(const nsAString& aFaceName,
                                     FcPattern* aFontPattern,
                                     bool aIgnoreFcCharmap);
 
     // used for data fonts where the fontentry takes ownership
     // of the font data and the FT_Face
     explicit gfxFontconfigFontEntry(const nsAString& aFaceName,
                                     FontWeight aWeight,
-                                    uint16_t aStretch,
-                                    uint8_t aStyle,
+                                    FontStretch aStretch,
+                                    FontSlantStyle aStyle,
                                     const uint8_t *aData,
                                     uint32_t aLength,
                                     FT_Face aFace);
 
     // used for @font-face local system fonts with explicit patterns
     explicit gfxFontconfigFontEntry(const nsAString& aFaceName,
                                     FcPattern* aFontPattern,
                                     FontWeight aWeight,
-                                    uint16_t aStretch,
-                                    uint8_t aStyle);
+                                    FontStretch aStretch,
+                                    FontSlantStyle aStyle);
 
     gfxFontEntry* Clone() const override;
 
     FcPattern* GetPattern() { return mFontPattern; }
 
     nsresult ReadCMAP(FontInfoData *aFontInfoData = nullptr) override;
     bool TestCharacterMap(uint32_t aCh) override;
 
@@ -285,23 +285,24 @@ public:
                      nsTArray<nsString>& aListOfFonts) override;
 
     void ReadSystemFontList(
         InfallibleTArray<mozilla::dom::SystemFontListEntry>* retValue);
 
     gfxFontEntry*
     LookupLocalFont(const nsAString& aFontName,
                     FontWeight aWeight,
-                    uint16_t aStretch, uint8_t aStyle) override;
+                    FontStretch aStretch,
+                    FontSlantStyle aStyle) override;
 
     gfxFontEntry*
     MakePlatformFont(const nsAString& aFontName,
                      FontWeight aWeight,
-                     uint16_t aStretch,
-                     uint8_t aStyle,
+                     FontStretch aStretch,
+                     FontSlantStyle aStyle,
                      const uint8_t* aFontData,
                      uint32_t aLength) override;
 
     bool FindAndAddFamilies(const nsAString& aFamily,
                             nsTArray<gfxFontFamily*>* aOutput,
                             FindFamiliesFlags aFlags,
                             gfxFontStyle* aStyle = nullptr,
                             gfxFloat aDevToCssSize = 1.0) override;
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -2670,17 +2670,17 @@ gfxFont::Measure(const gfxTextRun *aText
         if (isRTL) {
             metrics.mBoundingBox -= gfxPoint(x, 0);
         }
     }
 
     // If the font may be rendered with a fake-italic effect, we need to allow
     // for the top-right of the glyphs being skewed to the right, and the
     // bottom-left being skewed further left.
-    if (mStyle.style != NS_FONT_STYLE_NORMAL &&
+    if (mStyle.style != FontSlantStyle::Normal() &&
         mFontEntry->IsUpright() &&
         mStyle.allowSyntheticStyle) {
         gfxFloat extendLeftEdge =
             ceil(OBLIQUE_SKEW_FACTOR * metrics.mBoundingBox.YMost());
         gfxFloat extendRightEdge =
             ceil(OBLIQUE_SKEW_FACTOR * -metrics.mBoundingBox.Y());
         metrics.mBoundingBox.SetWidth(metrics.mBoundingBox.Width() + extendLeftEdge + extendRightEdge);
         metrics.mBoundingBox.MoveByX(-extendLeftEdge);
@@ -4107,30 +4107,30 @@ gfxFont::RemoveGlyphChangeObserver(Glyph
 #define DEFAULT_PIXEL_FONT_SIZE 16.0f
 
 gfxFontStyle::gfxFontStyle() :
     language(nsGkAtoms::x_western),
     size(DEFAULT_PIXEL_FONT_SIZE), sizeAdjust(-1.0f), baselineOffset(0.0f),
     languageOverride(NO_FONT_LANGUAGE_OVERRIDE),
     fontSmoothingBackgroundColor(NS_RGBA(0, 0, 0, 0)),
     weight(FontWeight::Normal()),
-    stretch(NS_FONT_STRETCH_NORMAL),
-    style(NS_FONT_STYLE_NORMAL),
+    stretch(FontStretch::Normal()),
+    style(FontSlantStyle::Normal()),
     variantCaps(NS_FONT_VARIANT_CAPS_NORMAL),
     variantSubSuper(NS_FONT_VARIANT_POSITION_NORMAL),
     systemFont(true), printerFont(false), useGrayscaleAntialiasing(false),
     allowSyntheticWeight(true), allowSyntheticStyle(true),
     noFallbackVariantFeatures(true),
     explicitLanguage(false)
 {
 }
 
-gfxFontStyle::gfxFontStyle(uint8_t aStyle,
+gfxFontStyle::gfxFontStyle(FontSlantStyle aStyle,
                            FontWeight aWeight,
-                           uint16_t aStretch,
+                           FontStretch aStretch,
                            gfxFloat aSize,
                            nsAtom *aLanguage, bool aExplicitLanguage,
                            float aSizeAdjust, bool aSystemFont,
                            bool aPrinterFont,
                            bool aAllowWeightSynthesis,
                            bool aAllowStyleSynthesis,
                            uint32_t aLanguageOverride):
     language(aLanguage),
@@ -4168,16 +4168,30 @@ gfxFontStyle::gfxFontStyle(uint8_t aStyl
     }
 
     if (!language) {
         NS_WARNING("null language");
         language = nsGkAtoms::x_western;
     }
 }
 
+PLDHashNumber
+gfxFontStyle::Hash() const
+{
+    return mozilla::HashGeneric(systemFont, style.ForHash(),
+                                stretch.ForHash(), weight.ForHash(),
+                                size, sizeAdjust,
+                                nsRefPtrHashKey<nsAtom>::HashKey(language));
+    /* XXX
+    return (style + (systemFont << 7) + (weight.ForHash() << 8) +
+            uint32_t(size*1000) + int32_t(sizeAdjust*1000)) ^
+            nsRefPtrHashKey<nsAtom>::HashKey(language);
+    */
+}
+
 void
 gfxFontStyle::AdjustForSubSuperscript(int32_t aAppUnitsPerDevPixel)
 {
     NS_PRECONDITION(variantSubSuper != NS_FONT_VARIANT_POSITION_NORMAL &&
                     baselineOffset == 0,
                     "can't adjust this style for sub/superscript");
 
     // calculate the baseline offset (before changing the size)
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -71,20 +71,22 @@ class gfxMathTable;
 
 struct gfxTextRunDrawCallbacks;
 
 namespace mozilla {
 class SVGContextPaint;
 } // namespace mozilla
 
 struct gfxFontStyle {
+    typedef mozilla::FontStretch FontStretch;
+    typedef mozilla::FontSlantStyle FontSlantStyle;
     typedef mozilla::FontWeight FontWeight;
 
     gfxFontStyle();
-    gfxFontStyle(uint8_t aStyle, FontWeight aWeight, uint16_t aStretch,
+    gfxFontStyle(FontSlantStyle aStyle, FontWeight aWeight, FontStretch aStretch,
                  gfxFloat aSize, nsAtom *aLanguage, bool aExplicitLanguage,
                  float aSizeAdjust, bool aSystemFont,
                  bool aPrinterFont,
                  bool aWeightSynthesis, bool aStyleSynthesis,
                  uint32_t aLanguageOverride);
 
     // the language (may be an internal langGroup code rather than an actual
     // language code) specified in the document or element's lang property,
@@ -137,21 +139,21 @@ struct gfxFontStyle {
 
     // The estimated background color behind the text. Enables a special
     // rendering mode when NS_GET_A(.) > 0. Only used for text in the chrome.
     nscolor fontSmoothingBackgroundColor;
 
     // The weight of the font: 100, 200, ... 900.
     FontWeight weight;
 
-    // The stretch of the font (NS_FONT_STRETCH_*, see gfxFontConstants.h).
-    uint8_t stretch;
+    // The stretch of the font
+    FontStretch stretch;
 
-    // The style of font (normal, italic, oblique)
-    uint8_t style;
+    // The style of font
+    FontSlantStyle style;
 
     // caps variant (small-caps, petite-caps, etc.)
     uint8_t variantCaps;
 
     // sub/superscript variant
     uint8_t variantSubSuper;
 
     // Say that this font is a system font and therefore does not
@@ -180,21 +182,17 @@ struct gfxFontStyle {
     // Return the final adjusted font size for the given aspect ratio.
     // Not meant to be called when sizeAdjust = -1.0.
     gfxFloat GetAdjustedSize(gfxFloat aspect) const {
         NS_ASSERTION(sizeAdjust >= 0.0, "Not meant to be called when sizeAdjust = -1.0");
         gfxFloat adjustedSize = std::max(NS_round(size*(sizeAdjust/aspect)), 1.0);
         return std::min(adjustedSize, FONT_MAX_SIZE);
     }
 
-    PLDHashNumber Hash() const {
-        return (style + (systemFont << 7) + (weight.ForHash() << 8) +
-            uint32_t(size*1000) + int32_t(sizeAdjust*1000)) ^
-            nsRefPtrHashKey<nsAtom>::HashKey(language);
-    }
+    PLDHashNumber Hash() const;
 
     // Adjust this style to simulate sub/superscript (as requested in the
     // variantSubSuper field) using size and baselineOffset instead.
     void AdjustForSubSuperscript(int32_t aAppUnitsPerDevPixel);
 
     bool Equals(const gfxFontStyle& other) const {
         return
             (*reinterpret_cast<const uint64_t*>(&size) ==
@@ -1448,16 +1446,18 @@ class gfxFont {
 protected:
     typedef mozilla::gfx::DrawTarget DrawTarget;
     typedef mozilla::unicode::Script Script;
     typedef mozilla::SVGContextPaint SVGContextPaint;
 
     typedef gfxFontShaper::RoundingFlags RoundingFlags;
 
 public:
+    typedef mozilla::FontSlantStyle FontSlantStyle;
+
     nsrefcnt AddRef(void) {
         NS_PRECONDITION(int32_t(mRefCnt) >= 0, "illegal refcnt");
         if (mExpirationState.IsTracked()) {
             gfxFontCache::GetCache()->RemoveObject(this);
         }
         ++mRefCnt;
         NS_LOG_ADDREF(this, mRefCnt, "gfxFont", sizeof(*this));
         return mRefCnt;
@@ -1808,17 +1808,17 @@ public:
     virtual bool SetupCairoFont(DrawTarget* aDrawTarget) = 0;
 
     virtual bool AllowSubpixelAA() { return true; }
 
     bool IsSyntheticBold() { return mApplySyntheticBold; }
 
     bool IsSyntheticOblique() {
         return mFontEntry->IsUpright() &&
-               mStyle.style != NS_FONT_STYLE_NORMAL &&
+               mStyle.style != FontSlantStyle::Normal() &&
                mStyle.allowSyntheticStyle;
     }
 
     // Amount by which synthetic bold "fattens" the glyphs:
     // For size S up to a threshold size T, we use (0.25 + 3S / 4T),
     // so that the result ranges from 0.25 to 1.0; thereafter,
     // simply use (S / T).
     gfxFloat GetSyntheticBoldOffset() {
--- a/gfx/thebes/gfxFontEntry.cpp
+++ b/gfx/thebes/gfxFontEntry.cpp
@@ -53,17 +53,17 @@ gfxCharacterMap::NotifyReleased()
     gfxPlatformFontList *fontlist = gfxPlatformFontList::PlatformFontList();
     if (mShared) {
         fontlist->RemoveCmap(this);
     }
     delete this;
 }
 
 gfxFontEntry::gfxFontEntry() :
-    mStyle(NS_FONT_STYLE_NORMAL), mFixedPitch(false),
+    mFixedPitch(false),
     mIsBadUnderlineFont(false),
     mIsUserFontContainer(false),
     mIsDataUserFont(false),
     mIsLocalUserFont(false),
     mStandardFace(false),
     mIgnoreGDEF(false),
     mIgnoreGSUB(false),
     mSVGInitialized(false),
@@ -75,33 +75,35 @@ gfxFontEntry::gfxFontEntry() :
     mGraphiteSpaceContextualsInitialized(false),
     mHasGraphiteSpaceContextuals(false),
     mSpaceGlyphIsInvisible(false),
     mSpaceGlyphIsInvisibleInitialized(false),
     mCheckedForGraphiteTables(false),
     mHasCmapTable(false),
     mGrFaceInitialized(false),
     mCheckedForColorGlyph(false),
-    mWeight(500), mStretch(NS_FONT_STRETCH_NORMAL),
+    mWeight(500),
+    mStretch(FontStretch::Normal()),
+    mStyle(FontSlantStyle::Normal()),
     mUVSOffset(0), mUVSData(nullptr),
     mLanguageOverride(NO_FONT_LANGUAGE_OVERRIDE),
     mCOLR(nullptr),
     mCPAL(nullptr),
     mUnitsPerEm(0),
     mHBFace(nullptr),
     mGrFace(nullptr),
     mGrFaceRefCnt(0),
     mComputedSizeOfUserFont(0)
 {
     memset(&mDefaultSubSpaceFeatures, 0, sizeof(mDefaultSubSpaceFeatures));
     memset(&mNonDefaultSubSpaceFeatures, 0, sizeof(mNonDefaultSubSpaceFeatures));
 }
 
 gfxFontEntry::gfxFontEntry(const nsAString& aName, bool aIsStandardFace) :
-    mName(aName), mStyle(NS_FONT_STYLE_NORMAL), mFixedPitch(false),
+    mName(aName), mFixedPitch(false),
     mIsBadUnderlineFont(false),
     mIsUserFontContainer(false),
     mIsDataUserFont(false),
     mIsLocalUserFont(false), mStandardFace(aIsStandardFace),
     mIgnoreGDEF(false),
     mIgnoreGSUB(false),
     mSVGInitialized(false),
     mHasSpaceFeaturesInitialized(false),
@@ -112,17 +114,19 @@ gfxFontEntry::gfxFontEntry(const nsAStri
     mGraphiteSpaceContextualsInitialized(false),
     mHasGraphiteSpaceContextuals(false),
     mSpaceGlyphIsInvisible(false),
     mSpaceGlyphIsInvisibleInitialized(false),
     mCheckedForGraphiteTables(false),
     mHasCmapTable(false),
     mGrFaceInitialized(false),
     mCheckedForColorGlyph(false),
-    mWeight(500), mStretch(NS_FONT_STRETCH_NORMAL),
+    mWeight(500),
+    mStretch(FontStretch::Normal()),
+    mStyle(FontSlantStyle::Normal()),
     mUVSOffset(0), mUVSData(nullptr),
     mLanguageOverride(NO_FONT_LANGUAGE_OVERRIDE),
     mCOLR(nullptr),
     mCPAL(nullptr),
     mUnitsPerEm(0),
     mHBFace(nullptr),
     mGrFace(nullptr),
     mGrFaceRefCnt(0),
@@ -1173,49 +1177,49 @@ gfxFontFamily::FindFontForStyle(const gf
     }
     return nullptr;
 }
 
 #define STYLE_SHIFT 2 // number of bits to contain style distance
 
 // style distance ==> [0,2]
 static inline uint32_t
-StyleDistance(uint32_t aFontStyle, uint32_t aTargetStyle)
+StyleDistance(FontSlantStyle aFontStyle, FontSlantStyle aTargetStyle)
 {
     if (aFontStyle == aTargetStyle) {
         return 0; // styles match exactly ==> 0
     }
-    if (aFontStyle == NS_FONT_STYLE_NORMAL ||
-        aTargetStyle == NS_FONT_STYLE_NORMAL) {
+    if (aFontStyle == FontSlantStyle::Normal() ||
+        aTargetStyle == FontSlantStyle::Normal()) {
         return 2; // one is normal (but not the other) ==> 2
     }
     return 1; // neither is normal; must be italic vs oblique ==> 1
 }
 
-#define REVERSE_STRETCH_DISTANCE 200
+#define REVERSE_STRETCH_DISTANCE 200.0f
 
 // stretch distance ==> [0,350]
 static inline uint32_t
-StretchDistance(int32_t aFontStretch, int32_t aTargetStretch)
+StretchDistance(FontStretch aFontStretch, FontStretch aTargetStretch)
 {
-    int32_t distance = 0;
+    float distance = 0.0f;
     if (aTargetStretch != aFontStretch) {
         // stretch values are in the range 50 .. 200
         // if aTargetStretch is >100, we prefer larger values;
         // if <=100, prefer smaller
-        if (aTargetStretch > 100) {
+        if (aTargetStretch > FontStretch::Normal()) {
             distance = (aFontStretch - aTargetStretch);
         } else {
             distance = (aTargetStretch - aFontStretch);
         }
         // if the computed "distance" here is negative, it means that
         // aFontEntry lies in the "non-preferred" direction from aTargetStretch,
         // so we treat that as larger than any preferred-direction distance
         // (max possible is 150) by adding an extra 200 to the absolute value
-        if (distance < 0) {
+        if (distance < 0.0f) {
             distance = -distance + REVERSE_STRETCH_DISTANCE;
         }
     }
     return uint32_t(distance);
 }
 
 // CSS currently limits font weights to multiples of 100 but the weight
 // matching code below does not assume this.
@@ -1330,17 +1334,17 @@ gfxFontFamily::FindAllFontsForStyle(cons
     // We can then pick the required entry based on whether the request is for
     // bold or non-bold, italic or non-italic, without running the more complex
     // matching algorithm used for larger families with many weights and/or widths.
 
     if (mIsSimpleFamily) {
         // Family has no more than the "standard" 4 faces, at fixed indexes;
         // calculate which one we want.
         // Note that we cannot simply return it as not all 4 faces are necessarily present.
-        bool wantItalic = (aFontStyle.style != NS_FONT_STYLE_NORMAL);
+        bool wantItalic = !aFontStyle.style.IsNormal();
         uint8_t faceIndex = (wantItalic ? kItalicMask : 0) |
                             (wantBold ? kBoldMask : 0);
 
         // if the desired style is available, return it directly
         fe = mAvailableFonts[faceIndex];
         if (fe) {
             // no need to set aNeedsSyntheticBold here as we matched the boldness request
             aFontEntryList.AppendElement(fe);
@@ -1430,17 +1434,17 @@ gfxFontFamily::CheckForSimpleFamily()
                 // if none then the family is unusable anyway
     }
 
     if (count == 1) {
         mIsSimpleFamily = true;
         return;
     }
 
-    uint16_t firstStretch = mAvailableFonts[0]->Stretch();
+    FontStretch firstStretch = mAvailableFonts[0]->Stretch();
 
     gfxFontEntry *faces[4] = { 0 };
     for (uint8_t i = 0; i < count; ++i) {
         gfxFontEntry *fe = mAvailableFonts[i];
         if (fe->Stretch() != firstStretch || fe->IsOblique()) {
             // simple families don't have varying font-stretch or oblique
             return;
         }
@@ -1493,17 +1497,17 @@ void gfxFontFamily::LocalizedName(nsAStr
 
 // metric for how close a given font matches a style
 static int32_t
 CalcStyleMatch(gfxFontEntry *aFontEntry, const gfxFontStyle *aStyle)
 {
     int32_t rank = 0;
     if (aStyle) {
          // italics
-         bool wantUpright = (aStyle->style == NS_FONT_STYLE_NORMAL);
+         bool wantUpright = aStyle->style.IsNormal();
          if (aFontEntry->IsUpright() == wantUpright) {
              rank += 10;
          }
 
         // measure of closeness of weight to the desired value
         rank += 9 - Abs((aFontEntry->Weight() - aStyle->weight) / 100.0f);
     } else {
         // if no font to match, prefer non-bold, non-italic fonts
--- a/gfx/thebes/gfxFontEntry.h
+++ b/gfx/thebes/gfxFontEntry.h
@@ -105,19 +105,21 @@ private:
 struct gfxFontFeatureInfo {
     uint32_t mTag;
     uint32_t mScript;
     uint32_t mLangSys;
 };
 
 class gfxFontEntry {
 public:
-    typedef mozilla::FontWeight FontWeight;
     typedef mozilla::gfx::DrawTarget DrawTarget;
     typedef mozilla::unicode::Script Script;
+    typedef mozilla::FontWeight FontWeight;
+    typedef mozilla::FontSlantStyle FontSlantStyle;
+    typedef mozilla::FontStretch FontStretch;
 
     // Used by stylo
     NS_INLINE_DECL_THREADSAFE_REFCOUNTING(gfxFontEntry)
 
     explicit gfxFontEntry(const nsAString& aName, bool aIsStandardFace = false);
 
     // Create a new entry that refers to the same font as this, but without
     // additional state that may have been set up (such as family name).
@@ -137,39 +139,39 @@ public:
     // they are intended only for the font-inspection API, not for
     // perf-critical layout/drawing work.
 
     // The "real" name of the face, if available from the font resource;
     // returns Name() if nothing better is available.
     virtual nsString RealFaceName();
 
     FontWeight Weight() const { return mWeight; }
-    uint16_t Stretch() const { return mStretch; }
+    FontStretch Stretch() const { return mStretch; }
 
     bool IsUserFont() const { return mIsDataUserFont || mIsLocalUserFont; }
     bool IsLocalUserFont() const { return mIsLocalUserFont; }
     bool IsFixedPitch() const { return mFixedPitch; }
-    bool IsItalic() const { return mStyle == NS_FONT_STYLE_ITALIC; }
-    bool IsOblique() const { return mStyle == NS_FONT_STYLE_OBLIQUE; }
-    bool IsUpright() const { return mStyle == NS_FONT_STYLE_NORMAL; }
+    bool IsItalic() const { return mStyle.IsItalic(); }
+    bool IsOblique() const { return mStyle.IsOblique(); }
+    bool IsUpright() const { return mStyle.IsNormal(); }
     bool IsBold() const { return mWeight.IsBold(); } // bold == weights 600 and above
     bool IgnoreGDEF() const { return mIgnoreGDEF; }
     bool IgnoreGSUB() const { return mIgnoreGSUB; }
 
     // Return whether the face corresponds to "normal" CSS style properties:
     //    font-style: normal;
     //    font-weight: normal;
     //    font-stretch: normal;
     // If this is false, we might want to fall back to a different face and
     // possibly apply synthetic styling.
     bool IsNormalStyle() const
     {
         return IsUpright() &&
                Weight() == FontWeight::Normal() &&
-               Stretch() == NS_FONT_STRETCH_NORMAL;
+               Stretch().IsNormal();
     }
 
     // whether a feature is supported by the font (limited to a small set
     // of features for which some form of fallback needs to be implemented)
     virtual bool SupportsOpenTypeFeature(Script aScript, uint32_t aFeatureTag);
     bool SupportsGraphiteFeature(uint32_t aFeatureTag);
 
     // returns a set containing all input glyph ids for a given feature
@@ -369,17 +371,16 @@ public:
     }
 
     // Get the font's list of features (if any) for DevTools support.
     void GetFeatureInfo(nsTArray<gfxFontFeatureInfo>& aFeatureInfo);
 
     nsString         mName;
     nsString         mFamilyName;
 
-    uint8_t          mStyle       : 2; // italic/oblique
     bool             mFixedPitch  : 1;
     bool             mIsBadUnderlineFont : 1;
     bool             mIsUserFontContainer : 1; // userfont entry
     bool             mIsDataUserFont : 1;      // platform font entry (data)
     bool             mIsLocalUserFont : 1;     // platform font entry (local)
     bool             mStandardFace : 1;
     bool             mIgnoreGDEF  : 1;
     bool             mIgnoreGSUB  : 1;
@@ -399,18 +400,19 @@ public:
     bool             mGrFaceInitialized : 1;
     bool             mCheckedForColorGlyph : 1;
 
     // bitvector of substitution space features per script, one each
     // for default and non-default features
     uint32_t         mDefaultSubSpaceFeatures[(int(Script::NUM_SCRIPT_CODES) + 31) / 32];
     uint32_t         mNonDefaultSubSpaceFeatures[(int(Script::NUM_SCRIPT_CODES) + 31) / 32];
 
-    FontWeight       mWeight;
-    uint16_t         mStretch;
+    FontWeight mWeight;
+    FontStretch mStretch;
+    FontSlantStyle mStyle;
 
     RefPtr<gfxCharacterMap> mCharacterMap;
     uint32_t         mUVSOffset;
     mozilla::UniquePtr<uint8_t[]> mUVSData;
     mozilla::UniquePtr<gfxUserFontData> mUserFontData;
     mozilla::UniquePtr<gfxSVGGlyphs> mSVGGlyphs;
     // list of gfxFonts that are using SVG glyphs
     nsTArray<gfxFont*> mFontsUsingSVGGlyphs;
--- a/gfx/thebes/gfxGDIFontList.cpp
+++ b/gfx/thebes/gfxGDIFontList.cpp
@@ -110,19 +110,19 @@ FontTypeToOutPrecision(uint8_t fontType)
 /***************************************************************
  *
  * GDIFontEntry
  *
  */
 
 GDIFontEntry::GDIFontEntry(const nsAString& aFaceName,
                            gfxWindowsFontType aFontType,
-                           uint8_t aStyle,
+                           FontSlantStyle aStyle,
                            FontWeight aWeight,
-                           uint16_t aStretch,
+                           FontStretch aStretch,
                            gfxUserFontData *aUserFontData)
     : gfxFontEntry(aFaceName),
       mFontType(aFontType),
       mForceGDI(false),
       mUnicodeRanges()
 {
     mUserFontData.reset(aUserFontData);
     mStyle = aStyle;
@@ -298,17 +298,17 @@ GDIFontEntry::TestCharacterMap(uint32_t 
 
     if (mCharacterMap->mBuildOnTheFly) {
         if (aCh > 0xFFFF)
             return false;
 
         // previous code was using the group style
         gfxFontStyle fakeStyle;
         if (!IsUpright()) {
-            fakeStyle.style = NS_FONT_STYLE_ITALIC;
+            fakeStyle.style = FontSlantStyle::Italic();
         }
         fakeStyle.weight = mWeight;
 
         RefPtr<gfxFont> tempFont = FindOrMakeFont(&fakeStyle, false);
         if (!tempFont || !tempFont->Valid())
             return false;
         gfxGDIFont *font = static_cast<gfxGDIFont*>(tempFont.get());
 
@@ -389,19 +389,19 @@ GDIFontEntry::InitLogFont(const nsAStrin
     int len = std::min<int>(aName.Length(), LF_FACESIZE - 1);
     memcpy(&mLogFont.lfFaceName, aName.BeginReading(), len * sizeof(char16_t));
     mLogFont.lfFaceName[len] = '\0';
 }
 
 GDIFontEntry* 
 GDIFontEntry::CreateFontEntry(const nsAString& aName,
                               gfxWindowsFontType aFontType,
-                              uint8_t aStyle,
+                              FontSlantStyle aStyle,
                               FontWeight aWeight,
-                              uint16_t aStretch,
+                              FontStretch aStretch,
                               gfxUserFontData* aUserFontData)
 {
     // jtdfix - need to set charset, unicode ranges, pitch/family
 
     GDIFontEntry *fe = new GDIFontEntry(aName, aFontType, aStyle,
                                         aWeight, aStretch, aUserFontData);
 
     return fe;
@@ -475,21 +475,22 @@ GDIFontFamily::FamilyAddStylesProc(const
             ff->mCharset.set(metrics.tmCharSet);
             return 1; 
         }
     }
 
     // We can't set the hasItalicFace flag correctly here,
     // because we might not have seen the family's italic face(s) yet.
     // So we'll set that flag for all members after loading all the faces.
-    uint8_t italicStyle = (logFont.lfItalic == 0xFF ?
-                           NS_FONT_STYLE_ITALIC : NS_FONT_STYLE_NORMAL);
+    auto italicStyle = (logFont.lfItalic == 0xFF ?
+                           FontSlantStyle::Italic() : FontSlantStyle::Normal());
     fe = GDIFontEntry::CreateFontEntry(nsDependentString(lpelfe->elfFullName),
                                        feType, italicStyle,
-                                       FontWeight(int32_t(logFont.lfWeight)), 0,
+                                       FontWeight(int32_t(logFont.lfWeight)),
+                                       FontStretch::Normal(),
                                        nullptr);
     if (!fe)
         return 1;
 
     ff->AddFontEntry(fe);
 
     if (nmetrics->ntmFontSig.fsUsb[0] != 0x00000000 &&
         nmetrics->ntmFontSig.fsUsb[1] != 0x00000000 &&
@@ -502,22 +503,25 @@ GDIFontFamily::FamilyAddStylesProc(const
             DWORD range = nmetrics->ntmFontSig.fsUsb[i];
             for (uint32_t k = 0; k < 32; ++k) {
                 fe->mUnicodeRanges.set(x++, (range & (1 << k)) != 0);
             }
         }
     }
 
     if (LOG_FONTLIST_ENABLED()) {
+        nsAutoCString stretchString;
         LOG_FONTLIST(("(fontlist) added (%s) to family (%s)"
-             " with style: %s weight: %d stretch: %d",
-             NS_ConvertUTF16toUTF8(fe->Name()).get(), 
-             NS_ConvertUTF16toUTF8(ff->Name()).get(), 
+             " with style: %s weight: %d stretch: %g%%",
+             NS_ConvertUTF16toUTF8(fe->Name()).get(),
+             NS_ConvertUTF16toUTF8(ff->Name()).get(),
              (logFont.lfItalic == 0xff) ? "italic" : "normal",
-             logFont.lfWeight, fe->Stretch()));
+             logFont.lfWeight,
+             fe->Stretch().Percentage(),
+             stretchString.get()));
     }
     return 1;
 }
 
 void
 GDIFontFamily::FindStyleVariations(FontInfoData *aFontInfoData)
 {
     if (mHasStyles)
@@ -706,18 +710,18 @@ gfxGDIFontList::EnumFontFamExProc(ENUMLO
     }
 
     return 1;
 }
 
 gfxFontEntry* 
 gfxGDIFontList::LookupLocalFont(const nsAString& aFontName,
                                 FontWeight aWeight,
-                                uint16_t aStretch,
-                                uint8_t aStyle)
+                                FontStretch aStretch,
+                                FontSlantStyle aStyle)
 {
     gfxFontEntry *lookup;
 
     lookup = LookupInFaceNameLists(aFontName);
     if (!lookup) {
         return nullptr;
     }
 
@@ -798,18 +802,18 @@ FixupSymbolEncodedFont(uint8_t* aFontDat
         }
     }
     return false;
 }
 
 gfxFontEntry*
 gfxGDIFontList::MakePlatformFont(const nsAString& aFontName,
                                  FontWeight aWeight,
-                                 uint16_t aStretch,
-                                 uint8_t aStyle,
+                                 FontStretch aStretch,
+                                 FontSlantStyle aStyle,
                                  const uint8_t* aFontData,
                                  uint32_t aLength)
 {
     // MakePlatformFont is responsible for deleting the font data with free
     // so we set up a stack object to ensure it is freed even if we take an
     // early exit
     struct FontDataDeleter {
         explicit FontDataDeleter(const uint8_t* aFontData)
--- a/gfx/thebes/gfxGDIFontList.h
+++ b/gfx/thebes/gfxGDIFontList.h
@@ -103,16 +103,18 @@ enum gfxWindowsFontType {
 };
 
 // A single member of a font family (i.e. a single face, such as Times Italic)
 // represented as a LOGFONT that will resolve to the correct face.
 // This replaces FontEntry from gfxWindowsFonts.h/cpp.
 class GDIFontEntry : public gfxFontEntry
 {
 public:
+    typedef mozilla::FontStretch FontStretch;
+    typedef mozilla::FontSlantStyle FontSlantStyle;
     typedef mozilla::FontWeight FontWeight;
 
     LPLOGFONTW GetLogFont() { return &mLogFont; }
 
     nsresult ReadCMAP(FontInfoData *aFontInfoData = nullptr) override;
 
     void FillLogFont(LOGFONTW *aLogFont,
                      LONG aWeight,
@@ -163,37 +165,37 @@ public:
     virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                                         FontListSizes* aSizes) const;
 
     gfxFontEntry* Clone() const override;
 
     // create a font entry for a font with a given name
     static GDIFontEntry* CreateFontEntry(const nsAString& aName,
                                          gfxWindowsFontType aFontType,
-                                         uint8_t aStyle,
+                                         FontSlantStyle aStyle,
                                          FontWeight aWeight,
-                                         uint16_t aStretch,
+                                         FontStretch aStretch,
                                          gfxUserFontData* aUserFontData);
 
     // create a font entry for a font referenced by its fullname
     static GDIFontEntry* LoadLocalFont(const nsAString& aFontName,
                                        FontWeight aWeight,
-                                       uint16_t aStretch,
-                                       uint8_t aStyle);
+                                       FontStretch aStretch,
+                                       FontSlantStyle aStyle);
 
     gfxWindowsFontType mFontType;
     bool mForceGDI;
 
     gfxSparseBitSet mUnicodeRanges;
 
 protected:
     friend class gfxGDIFont;
 
     GDIFontEntry(const nsAString& aFaceName, gfxWindowsFontType aFontType,
-                 uint8_t aStyle, FontWeight aWeight, uint16_t aStretch,
+                 FontSlantStyle aStyle, FontWeight aWeight, FontStretch aStretch,
                  gfxUserFontData *aUserFontData);
 
     void InitLogFont(const nsAString& aName, gfxWindowsFontType aFontType);
 
     virtual gfxFont* CreateFontInstance(const gfxFontStyle *aFontStyle,
                                         bool aNeedsBold) override;
 
     virtual nsresult CopyFontTable(uint32_t aTableTag,
@@ -315,16 +317,18 @@ protected:
 private:
     static int CALLBACK FamilyAddStylesProc(const ENUMLOGFONTEXW *lpelfe,
                                             const NEWTEXTMETRICEXW *nmetrics,
                                             DWORD fontType, LPARAM data);
 };
 
 class gfxGDIFontList : public gfxPlatformFontList {
 public:
+    typedef mozilla::FontStretch FontStretch;
+    typedef mozilla::FontSlantStyle FontSlantStyle;
     typedef mozilla::FontWeight FontWeight;
 
     static gfxGDIFontList* PlatformFontList() {
         return static_cast<gfxGDIFontList*>(sPlatformFontList);
     }
 
     // initialize font lists
     virtual nsresult InitFontListForPlatform() override;
@@ -334,23 +338,23 @@ public:
     bool FindAndAddFamilies(const nsAString& aFamily,
                             nsTArray<gfxFontFamily*>* aOutput,
                             FindFamiliesFlags aFlags,
                             gfxFontStyle* aStyle = nullptr,
                             gfxFloat aDevToCssSize = 1.0) override;
 
     virtual gfxFontEntry* LookupLocalFont(const nsAString& aFontName,
                                           FontWeight aWeight,
-                                          uint16_t aStretch,
-                                          uint8_t aStyle);
+                                          FontStretch aStretch,
+                                          FontSlantStyle aStyle);
 
     virtual gfxFontEntry* MakePlatformFont(const nsAString& aFontName,
                                            FontWeight aWeight,
-                                           uint16_t aStretch,
-                                           uint8_t aStyle,
+                                           FontStretch aStretch,
+                                           FontSlantStyle aStyle,
                                            const uint8_t* aFontData,
                                            uint32_t aLength);
 
     virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                                         FontListSizes* aSizes) const;
     virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                                         FontListSizes* aSizes) const;
 
--- a/gfx/thebes/gfxMacPlatformFontList.h
+++ b/gfx/thebes/gfxMacPlatformFontList.h
@@ -32,17 +32,17 @@ public:
     friend class gfxMacPlatformFontList;
 
     MacOSFontEntry(const nsAString& aPostscriptName, FontWeight aWeight,
                    bool aIsStandardFace = false,
                    double aSizeHint = 0.0);
 
     // for use with data fonts
     MacOSFontEntry(const nsAString& aPostscriptName, CGFontRef aFontRef,
-                   FontWeight aWeight, uint16_t aStretch, uint8_t aStyle,
+                   FontWeight aWeight, FontStretch aStretch, FontSlantStyle aStyle,
                    bool aIsDataUserFont, bool aIsLocal);
 
     virtual ~MacOSFontEntry() {
         if (mTrakTable) {
             hb_blob_destroy(mTrakTable);
         }
         ::CGFontRelease(mFontRef);
     }
@@ -127,23 +127,23 @@ public:
     gfxFontFamily* CreateFontFamily(const nsAString& aName) const override;
 
     static int32_t AppleWeightToCSSWeight(int32_t aAppleWeight);
 
     bool GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName) override;
 
     gfxFontEntry* LookupLocalFont(const nsAString& aFontName,
                                   FontWeight aWeight,
-                                  uint16_t aStretch,
-                                  uint8_t aStyle) override;
+                                  FontStretch aStretch,
+                                  FontSlantStyle aStyle) override;
 
     gfxFontEntry* MakePlatformFont(const nsAString& aFontName,
                                    FontWeight aWeight,
-                                   uint16_t aStretch,
-                                   uint8_t aStyle,
+                                   FontStretch aStretch,
+                                   FontSlantStyle aStyle,
                                    const uint8_t* aFontData,
                                    uint32_t aLength) override;
 
     bool FindAndAddFamilies(const nsAString& aFamily,
                             nsTArray<gfxFontFamily*>* aOutput,
                             FindFamiliesFlags aFlags,
                             gfxFontStyle* aStyle = nullptr,
                             gfxFloat aDevToCssSize = 1.0) override;
--- a/gfx/thebes/gfxMacPlatformFontList.mm
+++ b/gfx/thebes/gfxMacPlatformFontList.mm
@@ -384,18 +384,18 @@ MacOSFontEntry::MacOSFontEntry(const nsA
       mTrakSizeTable(nullptr)
 {
     mWeight = aWeight;
 }
 
 MacOSFontEntry::MacOSFontEntry(const nsAString& aPostscriptName,
                                CGFontRef aFontRef,
                                FontWeight aWeight,
-                               uint16_t aStretch,
-                               uint8_t aStyle,
+                               FontStretch aStretch,
+                               FontSlantStyle aStyle,
                                bool aIsDataUserFont,
                                bool aIsLocalUserFont)
     : gfxFontEntry(aPostscriptName, false),
       mFontRef(NULL),
       mSizeHint(0.0),
       mFontRefInitialized(false),
       mRequiresAAT(false),
       mIsCFF(false),
@@ -907,40 +907,40 @@ gfxMacFontFamily::FindStyleVariations(Fo
             new MacOSFontEntry(postscriptFontName, FontWeight(cssWeight),
                                isStandardFace, mSizeHint);
         if (!fontEntry) {
             break;
         }
 
         // set additional properties based on the traits reported by Cocoa
         if (macTraits & (NSCondensedFontMask | NSNarrowFontMask | NSCompressedFontMask)) {
-            fontEntry->mStretch = NS_FONT_STRETCH_CONDENSED;
+            fontEntry->mStretch = FontStretch::Condensed();
         } else if (macTraits & NSExpandedFontMask) {
-            fontEntry->mStretch = NS_FONT_STRETCH_EXPANDED;
+            fontEntry->mStretch = FontStretch::Expanded();
         }
         // Cocoa fails to set the Italic traits bit for HelveticaLightItalic,
         // at least (see bug 611855), so check for style name endings as well
         if ((macTraits & NSItalicFontMask) ||
             [facename hasSuffix:@"Italic"] ||
             [facename hasSuffix:@"Oblique"])
         {
-            fontEntry->mStyle = NS_FONT_STYLE_ITALIC;
+            fontEntry->mStyle = FontSlantStyle::Italic();
         }
         if (macTraits & NSFixedPitchFontMask) {
             fontEntry->mFixedPitch = true;
         }
 
         if (LOG_FONTLIST_ENABLED()) {
             LOG_FONTLIST(("(fontlist) added (%s) to family (%s)"
-                 " with style: %s weight: %d stretch: %d"
+                 " with style: %s weight: %d stretch: %g%%"
                  " (apple-weight: %d macTraits: %8.8x)",
-                 NS_ConvertUTF16toUTF8(fontEntry->Name()).get(), 
-                 NS_ConvertUTF16toUTF8(Name()).get(), 
+                 NS_ConvertUTF16toUTF8(fontEntry->Name()).get(),
+                 NS_ConvertUTF16toUTF8(Name()).get(),
                  fontEntry->IsItalic() ? "italic" : "normal",
-                 cssWeight, fontEntry->Stretch(),
+                 cssWeight, fontEntry->Stretch().Percentage(),
                  appKitWeight, macTraits));
         }
 
         // insert into font entry array of family
         AddFontEntry(fontEntry);
     }
 
     SortAvailableFonts();
@@ -1529,18 +1529,18 @@ gfxMacPlatformFontList::AppleWeightToCSS
     else if (aAppleWeight > kAppleMaxWeight)
         aAppleWeight = kAppleMaxWeight;
     return gAppleWeightToCSSWeight[aAppleWeight];
 }
 
 gfxFontEntry*
 gfxMacPlatformFontList::LookupLocalFont(const nsAString& aFontName,
                                         FontWeight aWeight,
-                                        uint16_t aStretch,
-                                        uint8_t aStyle)
+                                        FontStretch aStretch,
+                                        FontSlantStyle aStyle)
 {
     nsAutoreleasePool localPool;
 
     NSString *faceName = GetNSStringForString(aFontName);
     MacOSFontEntry *newFontEntry;
 
     // lookup face based on postscript or full name
     CGFontRef fontRef = ::CGFontCreateWithFontName(CFStringRef(faceName));
@@ -1562,18 +1562,18 @@ gfxMacPlatformFontList::LookupLocalFont(
 static void ReleaseData(void *info, const void *data, size_t size)
 {
     free((void*)data);
 }
 
 gfxFontEntry*
 gfxMacPlatformFontList::MakePlatformFont(const nsAString& aFontName,
                                          FontWeight aWeight,
-                                         uint16_t aStretch,
-                                         uint8_t aStyle,
+                                         FontStretch aStretch,
+                                         FontSlantStyle aStyle,
                                          const uint8_t* aFontData,
                                          uint32_t aLength)
 {
     NS_ASSERTION(aFontData, "MakePlatformFont called with null data");
 
     MOZ_ASSERT(aWeight >= FontWeight(100) && aWeight <= FontWeight(900),
                "bogus font weight value!");
 
@@ -1698,23 +1698,23 @@ gfxMacPlatformFontList::LookupSystemFont
     NS_ASSERTION(systemFontName, "system font name not set");
 
     if (systemFontName) {
         aSystemFontName.AssignASCII(systemFontName);
     }
 
     NSFontSymbolicTraits traits = [[font fontDescriptor] symbolicTraits];
     aFontStyle.style =
-        (traits & NSFontItalicTrait) ?  NS_FONT_STYLE_ITALIC : NS_FONT_STYLE_NORMAL;
+        (traits & NSFontItalicTrait) ?  FontSlantStyle::Italic() : FontSlantStyle::Normal();
     aFontStyle.weight =
         (traits & NSFontBoldTrait) ? FontWeight::Bold() : FontWeight::Normal();
     aFontStyle.stretch =
         (traits & NSFontExpandedTrait) ?
-            NS_FONT_STRETCH_EXPANDED : (traits & NSFontCondensedTrait) ?
-                NS_FONT_STRETCH_CONDENSED : NS_FONT_STRETCH_NORMAL;
+            FontStretch::Expanded() : (traits & NSFontCondensedTrait) ?
+                FontStretch::Condensed() : FontStretch::Normal();
     // convert size from css pixels to device pixels
     aFontStyle.size = [font pointSize] * aDevPixPerCSSPixel;
     aFontStyle.systemFont = true;
 }
 
 // used to load system-wide font info on off-main thread
 class MacFontInfo : public FontInfoData {
 public:
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -3,17 +3,16 @@
  * 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/FontPropertyTypes.h"
 #include "mozilla/layers/CompositorManagerChild.h"
 #include "mozilla/layers/CompositorThread.h"
 #include "mozilla/layers/ImageBridgeChild.h"
 #include "mozilla/layers/ISurfaceAllocator.h"     // for GfxMemoryImageReporter
-#include "mozilla/layers/SharedSurfacesParent.h"
 #include "mozilla/webrender/RenderThread.h"
 #include "mozilla/webrender/WebRenderAPI.h"
 #include "mozilla/webrender/webrender_ffi.h"
 #include "mozilla/layers/PaintThread.h"
 #include "mozilla/gfx/gfxVars.h"
 #include "mozilla/gfx/GPUProcessManager.h"
 #include "mozilla/gfx/GraphicsMessages.h"
 #include "mozilla/ClearOnShutdown.h"
@@ -1026,17 +1025,16 @@ gfxPlatform::InitLayersIPC()
 
   if (XRE_IsContentProcess()) {
     if (gfxVars::UseOMTP()) {
       layers::PaintThread::Start();
     }
   } else if (XRE_IsParentProcess()) {
     if (gfxVars::UseWebRender()) {
       wr::RenderThread::Start();
-      layers::SharedSurfacesParent::Initialize();
     }
 
     layers::CompositorThreadHolder::Start();
     gfx::VRListenerThreadHolder::Start();
   }
 }
 
 /* static */ void
@@ -1063,17 +1061,16 @@ gfxPlatform::ShutdownLayersIPC()
         layers::CompositorManagerChild::Shutdown();
         layers::ImageBridgeChild::ShutDown();
         // This has to happen after shutting down the child protocols.
         layers::CompositorThreadHolder::Shutdown();
         gfx::VRListenerThreadHolder::Shutdown();
         // There is a case that RenderThread exists when gfxVars::UseWebRender() is false.
         // This could happen when WebRender was fallbacked to compositor.
         if (wr::RenderThread::Get()) {
-          layers::SharedSurfacesParent::Shutdown();
           wr::RenderThread::ShutDown();
 
           Preferences::UnregisterCallback(WebRenderDebugPrefChangeCallback, WR_DEBUG_PREF);
         }
 
     } else {
       // TODO: There are other kind of processes and we should make sure gfx
       // stuff is either not created there or shut down properly.
@@ -1752,30 +1749,30 @@ gfxPlatform::IsFontFormatSupported(uint3
 
     // no format hint set, need to look at data
     return true;
 }
 
 gfxFontEntry*
 gfxPlatform::LookupLocalFont(const nsAString& aFontName,
                              FontWeight aWeight,
-                             uint16_t aStretch,
-                             uint8_t aStyle)
+                             FontStretch aStretch,
+                             FontSlantStyle aStyle)
 {
     return gfxPlatformFontList::PlatformFontList()->LookupLocalFont(aFontName,
                                                                     aWeight,
                                                                     aStretch,
                                                                     aStyle);
 }
 
 gfxFontEntry*
 gfxPlatform::MakePlatformFont(const nsAString& aFontName,
                               FontWeight aWeight,
-                              uint16_t aStretch,
-                              uint8_t aStyle,
+                              FontStretch aStretch,
+                              FontSlantStyle aStyle,
                               const uint8_t* aFontData,
                               uint32_t aLength)
 {
     return gfxPlatformFontList::PlatformFontList()->MakePlatformFont(aFontName,
                                                                      aWeight,
                                                                      aStretch,
                                                                      aStyle,
                                                                      aFontData,
--- a/gfx/thebes/gfxPlatform.h
+++ b/gfx/thebes/gfxPlatform.h
@@ -154,16 +154,18 @@ struct BackendPrefsData
   uint32_t mContentBitmask = 0;
   mozilla::gfx::BackendType mContentDefault = mozilla::gfx::BackendType::NONE;
 };
 
 class gfxPlatform {
     friend class SRGBOverrideObserver;
 
 public:
+    typedef mozilla::FontStretch FontStretch;
+    typedef mozilla::FontSlantStyle FontSlantStyle;
     typedef mozilla::FontWeight FontWeight;
     typedef mozilla::gfx::Color Color;
     typedef mozilla::gfx::DataSourceSurface DataSourceSurface;
     typedef mozilla::gfx::DrawTarget DrawTarget;
     typedef mozilla::gfx::IntSize IntSize;
     typedef mozilla::gfx::SourceSurface SourceSurface;
     typedef mozilla::unicode::Script Script;
 
@@ -389,31 +391,31 @@ public:
     /**
      * Look up a local platform font using the full font face name.
      * (Needed to support @font-face src local().)
      * Ownership of the returned gfxFontEntry is passed to the caller,
      * who must either AddRef() or delete.
      */
     virtual gfxFontEntry* LookupLocalFont(const nsAString& aFontName,
                                           FontWeight aWeight,
-                                          uint16_t aStretch,
-                                          uint8_t aStyle);
+                                          FontStretch aStretch,
+                                          FontSlantStyle aStyle);
 
     /**
      * Activate a platform font.  (Needed to support @font-face src url().)
      * aFontData is a NS_Malloc'ed block that must be freed by this function
      * (or responsibility passed on) when it is no longer needed; the caller
      * will NOT free it.
      * Ownership of the returned gfxFontEntry is passed to the caller,
      * who must either AddRef() or delete.
      */
     virtual gfxFontEntry* MakePlatformFont(const nsAString& aFontName,
                                            FontWeight aWeight,
-                                           uint16_t aStretch,
-                                           uint8_t aStyle,
+                                           FontStretch aStretch,
+                                           FontSlantStyle aStyle,
                                            const uint8_t* aFontData,
                                            uint32_t aLength);
 
     /**
      * Whether to allow downloadable fonts via @font-face rules
      */
     bool DownloadableFontsEnabled();
 
--- a/gfx/thebes/gfxPlatformFontList.h
+++ b/gfx/thebes/gfxPlatformFontList.h
@@ -93,16 +93,18 @@ struct FontListSizes {
 
 class gfxUserFontSet;
 
 class gfxPlatformFontList : public gfxFontInfoLoader
 {
     friend class InitOtherFamilyNamesRunnable;
 
 public:
+    typedef mozilla::FontStretch FontStretch;
+    typedef mozilla::FontSlantStyle FontSlantStyle;
     typedef mozilla::FontWeight FontWeight;
     typedef mozilla::unicode::Script Script;
 
     static gfxPlatformFontList* PlatformFontList() {
         return sPlatformFontList;
     }
 
     static nsresult Init() {
@@ -184,25 +186,25 @@ public:
     // pure virtual functions, to be provided by concrete subclasses
 
     // get the system default font family
     gfxFontFamily* GetDefaultFont(const gfxFontStyle* aStyle);
 
     // look up a font by name on the host platform
     virtual gfxFontEntry* LookupLocalFont(const nsAString& aFontName,
                                           FontWeight aWeight,
-                                          uint16_t aStretch,
-                                          uint8_t aStyle) = 0;
+                                          FontStretch aStretch,
+                                          FontSlantStyle aStyle) = 0;
 
     // create a new platform font from downloaded data (@font-face)
     // this method is responsible to ensure aFontData is free()'d
     virtual gfxFontEntry* MakePlatformFont(const nsAString& aFontName,
                                            FontWeight aWeight,
-                                           uint16_t aStretch,
-                                           uint8_t aStyle,
+                                           FontStretch aStretch,
+                                           FontSlantStyle aStyle,
                                            const uint8_t* aFontData,
                                            uint32_t aLength) = 0;
 
     // get the standard family name on the platform for a given font name
     // (platforms may override, eg Mac)
     virtual bool GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName);
 
     // get the default font name which is available on the system from
--- a/gfx/thebes/gfxPlatformGtk.cpp
+++ b/gfx/thebes/gfxPlatformGtk.cpp
@@ -271,29 +271,29 @@ gfxPlatformGtk::CreateFontGroup(const Fo
 {
     return new gfxFontGroup(aFontFamilyList, aStyle, aTextPerf,
                             aUserFontSet, aDevToCssSize);
 }
 
 gfxFontEntry*
 gfxPlatformGtk::LookupLocalFont(const nsAString& aFontName,
                                 FontWeight aWeight,
-                                uint16_t aStretch,
-                                uint8_t aStyle)
+                                FontStretch aStretch,
+                                FontSlantStyle aStyle)
 {
     gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
     return pfl->LookupLocalFont(aFontName, aWeight, aStretch,
                                 aStyle);
 }
 
 gfxFontEntry*
 gfxPlatformGtk::MakePlatformFont(const nsAString& aFontName,
                                  FontWeight aWeight,
-                                 uint16_t aStretch,
-                                 uint8_t aStyle,
+                                 FontStretch aStretch,
+                                 FontSlantStyle aStyle,
                                  const uint8_t* aFontData,
                                  uint32_t aLength)
 {
     gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
     return pfl->MakePlatformFont(aFontName, aWeight, aStretch,
                                  aStyle, aFontData, aLength);
 }
 
--- a/gfx/thebes/gfxPlatformGtk.h
+++ b/gfx/thebes/gfxPlatformGtk.h
@@ -62,27 +62,27 @@ public:
                     gfxFloat aDevToCssSize) override;
 
     /**
      * Look up a local platform font using the full font face name (needed to
      * support @font-face src local() )
      */
     virtual gfxFontEntry* LookupLocalFont(const nsAString& aFontName,
                                           FontWeight aWeight,
-                                          uint16_t aStretch,
-                                          uint8_t aStyle) override;
+                                          FontStretch aStretch,
+                                          FontSlantStyle aStyle) override;
 
     /**
      * Activate a platform font (needed to support @font-face src url() )
      *
      */
     virtual gfxFontEntry* MakePlatformFont(const nsAString& aFontName,
                                            FontWeight aWeight,
-                                           uint16_t aStretch,
-                                           uint8_t aStyle,
+                                           FontStretch aStretch,
+                                           FontSlantStyle aStyle,
                                            const uint8_t* aFontData,
                                            uint32_t aLength) override;
 
     /**
      * Calls XFlush if xrender is enabled.
      */
     virtual void FlushContentDrawing() override;
 
--- a/gfx/thebes/gfxTextRun.cpp
+++ b/gfx/thebes/gfxTextRun.cpp
@@ -14,16 +14,17 @@
 
 #include "gfxContext.h"
 #include "gfxFontConstants.h"
 #include "gfxFontMissingGlyphs.h"
 #include "gfxScriptItemizer.h"
 #include "nsUnicodeProperties.h"
 #include "nsUnicodeRange.h"
 #include "nsStyleConsts.h"
+#include "nsStyleUtil.h"
 #include "mozilla/Likely.h"
 #include "gfx2DGlue.h"
 #include "mozilla/gfx/Logging.h"        // for gfxCriticalError
 #include "mozilla/UniquePtr.h"
 #include "TextDrawTarget.h"
 
 #ifdef XP_WIN
 #include "gfxWindowsPlatform.h"
@@ -1756,21 +1757,25 @@ gfxTextRun::Dump(FILE* aOutput) {
     const GlyphRun* glyphRuns = GetGlyphRuns(&numGlyphRuns);
     for (uint32_t i = 0; i < numGlyphRuns; ++i) {
         if (i > 0) {
             fputc(',', aOutput);
         }
         gfxFont* font = glyphRuns[i].mFont;
         const gfxFontStyle* style = font->GetStyle();
         NS_ConvertUTF16toUTF8 fontName(font->GetName());
+        nsAutoString styleString;
+        nsStyleUtil::AppendFontSlantStyle(style->style, styleString);
         nsAutoCString lang;
         style->language->ToUTF8String(lang);
-        fprintf(aOutput, "%d: %s %f/%g/%d/%s", glyphRuns[i].mCharacterOffset,
+        fprintf(aOutput, "%d: %s %f/%g/%s/%s", glyphRuns[i].mCharacterOffset,
                 fontName.get(), style->size,
-                style->weight.ToFloat(), style->style, lang.get());
+                style->weight.ToFloat(),
+                NS_ConvertUTF16toUTF8(styleString).get(),
+                lang.get());
     }
     fputc(']', aOutput);
 }
 #endif
 
 gfxFontGroup::gfxFontGroup(const FontFamilyList& aFontFamilyList,
                            const gfxFontStyle *aStyle,
                            gfxTextPerfMetrics* aTextPerf,
@@ -2404,31 +2409,32 @@ gfxFontGroup::InitTextRun(DrawTarget* aD
         if (sizeof(T) == sizeof(uint8_t) && !transformedString) {
 
             if (MOZ_UNLIKELY(MOZ_LOG_TEST(log, LogLevel::Warning))) {
                 nsAutoCString lang;
                 mStyle.language->ToUTF8String(lang);
                 nsAutoString families;
                 mFamilyList.ToString(families);
                 nsAutoCString str((const char*)aString, aLength);
+                nsAutoString styleString;
+                nsStyleUtil::AppendFontSlantStyle(mStyle.style, styleString);
                 MOZ_LOG(log, LogLevel::Warning,\
                        ("(%s) fontgroup: [%s] default: %s lang: %s script: %d "
-                        "len %d weight: %g width: %d style: %s size: %6.2f %zu-byte "
+                        "len %d weight: %g stretch: %g%% style: %s size: %6.2f %zu-byte "
                         "TEXTRUN [%s] ENDTEXTRUN\n",
                         (mStyle.systemFont ? "textrunui" : "textrun"),
                         NS_ConvertUTF16toUTF8(families).get(),
                         (mFamilyList.GetDefaultFontType() == eFamily_serif ?
                          "serif" :
                          (mFamilyList.GetDefaultFontType() == eFamily_sans_serif ?
                           "sans-serif" : "none")),
                         lang.get(), static_cast<int>(Script::LATIN), aLength,
-                        mStyle.weight.ToFloat(), uint32_t(mStyle.stretch),
-                        (mStyle.style & NS_FONT_STYLE_ITALIC ? "italic" :
-                        (mStyle.style & NS_FONT_STYLE_OBLIQUE ? "oblique" :
-                                                                "normal")),
+                        mStyle.weight.ToFloat(),
+                        mStyle.stretch.Percentage(),
+                        NS_ConvertUTF16toUTF8(styleString).get(),
                         mStyle.size,
                         sizeof(T),
                         str.get()));
             }
 
             // the text is still purely 8-bit; bypass the script-run itemizer
             // and treat it as a single Latin run
             InitScriptRun(aDrawTarget, aTextRun, aString,
@@ -2451,32 +2457,33 @@ gfxFontGroup::InitTextRun(DrawTarget* aD
             Script runScript = Script::LATIN;
             while (scriptRuns.Next(runStart, runLimit, runScript)) {
 
                 if (MOZ_UNLIKELY(MOZ_LOG_TEST(log, LogLevel::Warning))) {
                     nsAutoCString lang;
                     mStyle.language->ToUTF8String(lang);
                     nsAutoString families;
                     mFamilyList.ToString(families);
+                    nsAutoString styleString;
+                    nsStyleUtil::AppendFontSlantStyle(mStyle.style, styleString);
                     uint32_t runLen = runLimit - runStart;
                     MOZ_LOG(log, LogLevel::Warning,\
                            ("(%s) fontgroup: [%s] default: %s lang: %s script: %d "
-                            "len %d weight: %g width: %d style: %s size: %6.2f "
+                            "len %d weight: %g stretch: %g%% style: %s size: %6.2f "
                             "%zu-byte TEXTRUN [%s] ENDTEXTRUN\n",
                             (mStyle.systemFont ? "textrunui" : "textrun"),
                             NS_ConvertUTF16toUTF8(families).get(),
                             (mFamilyList.GetDefaultFontType() == eFamily_serif ?
                              "serif" :
                              (mFamilyList.GetDefaultFontType() == eFamily_sans_serif ?
                               "sans-serif" : "none")),
                             lang.get(), static_cast<int>(runScript), runLen,
-                            mStyle.weight.ToFloat(), uint32_t(mStyle.stretch),
-                            (mStyle.style & NS_FONT_STYLE_ITALIC ? "italic" :
-                            (mStyle.style & NS_FONT_STYLE_OBLIQUE ? "oblique" :
-                                                                    "normal")),
+                            mStyle.weight.ToFloat(),
+                            mStyle.stretch.Percentage(),
+                            NS_ConvertUTF16toUTF8(styleString).get(),
                             mStyle.size,
                             sizeof(T),
                             NS_ConvertUTF16toUTF8(textPtr + runStart, runLen).get()));
                 }
 
                 InitScriptRun(aDrawTarget, aTextRun, textPtr + runStart,
                               runStart, runLimit - runStart, runScript, aMFR);
             }
--- a/gfx/thebes/gfxUserFontSet.cpp
+++ b/gfx/thebes/gfxUserFontSet.cpp
@@ -101,18 +101,18 @@ private:
     size_t       mLength;
     const size_t mLimit;
     off_t        mOff;
 };
 
 gfxUserFontEntry::gfxUserFontEntry(gfxUserFontSet* aFontSet,
              const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
              FontWeight aWeight,
-             uint32_t aStretch,
-             uint8_t aStyle,
+             FontStretch aStretch,
+             FontSlantStyle aStyle,
              const nsTArray<gfxFontFeature>& aFeatureSettings,
              const nsTArray<gfxFontVariation>& aVariationSettings,
              uint32_t aLanguageOverride,
              gfxCharacterMap* aUnicodeRanges,
              uint8_t aFontDisplay)
     : gfxFontEntry(NS_LITERAL_STRING("userfont")),
       mUserFontLoadState(STATUS_NOT_LOADED),
       mFontDataLoadingState(NOT_LOADING),
@@ -141,18 +141,18 @@ gfxUserFontEntry::~gfxUserFontEntry()
     // traversal, since PostTraversalTask objects can hold raw pointers to
     // gfxUserFontEntry objects.
     MOZ_ASSERT(!ServoStyleSet::IsInServoTraversal());
 }
 
 bool
 gfxUserFontEntry::Matches(const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
                           FontWeight aWeight,
-                          uint32_t aStretch,
-                          uint8_t aStyle,
+                          FontStretch aStretch,
+                          FontSlantStyle aStyle,
                           const nsTArray<gfxFontFeature>& aFeatureSettings,
                           const nsTArray<gfxFontVariation>& aVariationSettings,
                           uint32_t aLanguageOverride,
                           gfxCharacterMap* aUnicodeRanges,
                           uint8_t aFontDisplay)
 {
     return mWeight == aWeight &&
            mStretch == aStretch &&
@@ -932,18 +932,18 @@ gfxUserFontSet::~gfxUserFontSet()
     }
 }
 
 already_AddRefed<gfxUserFontEntry>
 gfxUserFontSet::FindOrCreateUserFontEntry(
                                const nsAString& aFamilyName,
                                const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
                                FontWeight aWeight,
-                               uint32_t aStretch,
-                               uint8_t aStyle,
+                               FontStretch aStretch,
+                               FontSlantStyle aStyle,
                                const nsTArray<gfxFontFeature>& aFeatureSettings,
                                const nsTArray<gfxFontVariation>& aVariationSettings,
                                uint32_t aLanguageOverride,
                                gfxCharacterMap* aUnicodeRanges,
                                uint8_t aFontDisplay)
 {
     RefPtr<gfxUserFontEntry> entry;
 
@@ -972,18 +972,18 @@ gfxUserFontSet::FindOrCreateUserFontEntr
 
     return entry.forget();
 }
 
 already_AddRefed<gfxUserFontEntry>
 gfxUserFontSet::CreateUserFontEntry(
                                const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
                                FontWeight aWeight,
-                               uint32_t aStretch,
-                               uint8_t aStyle,
+                               FontStretch aStretch,
+                               FontSlantStyle aStyle,
                                const nsTArray<gfxFontFeature>& aFeatureSettings,
                                const nsTArray<gfxFontVariation>& aVariationSettings,
                                uint32_t aLanguageOverride,
                                gfxCharacterMap* aUnicodeRanges,
                                uint8_t aFontDisplay)
 {
 
     RefPtr<gfxUserFontEntry> userFontEntry =
@@ -993,18 +993,18 @@ gfxUserFontSet::CreateUserFontEntry(
     return userFontEntry.forget();
 }
 
 gfxUserFontEntry*
 gfxUserFontSet::FindExistingUserFontEntry(
                                gfxUserFontFamily* aFamily,
                                const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
                                FontWeight aWeight,
-                               uint32_t aStretch,
-                               uint8_t aStyle,
+                               FontStretch aStretch,
+                               FontSlantStyle aStyle,
                                const nsTArray<gfxFontFeature>& aFeatureSettings,
                                const nsTArray<gfxFontVariation>& aVariationSettings,
                                uint32_t aLanguageOverride,
                                gfxCharacterMap* aUnicodeRanges,
                                uint8_t aFontDisplay)
 {
     MOZ_ASSERT(aWeight.ToFloat() != 0.0f,
                "aWeight must not be 0; use FontWeight::Normal() instead");
@@ -1036,21 +1036,22 @@ void
 gfxUserFontSet::AddUserFontEntry(const nsAString& aFamilyName,
                                  gfxUserFontEntry* aUserFontEntry)
 {
     gfxUserFontFamily* family = GetFamily(aFamilyName);
     family->AddFontEntry(aUserFontEntry);
 
     if (LOG_ENABLED()) {
         LOG(("userfonts (%p) added to \"%s\" (%p) style: %s weight: %g "
-             "stretch: %d display: %d",
+             "stretch: %g%% display: %d",
              this, NS_ConvertUTF16toUTF8(aFamilyName).get(), aUserFontEntry,
              (aUserFontEntry->IsItalic() ? "italic" :
               (aUserFontEntry->IsOblique() ? "oblique" : "normal")),
-             aUserFontEntry->Weight().ToFloat(), aUserFontEntry->Stretch(),
+             aUserFontEntry->Weight().ToFloat(),
+             aUserFontEntry->Stretch().Percentage(),
              aUserFontEntry->GetFontDisplay()));
     }
 }
 
 void
 gfxUserFontSet::IncrementGeneration(bool aIsRebuild)
 {
     // add one, increment again if zero
--- a/gfx/thebes/gfxUserFontSet.h
+++ b/gfx/thebes/gfxUserFontSet.h
@@ -180,16 +180,18 @@ public:
 class gfxUserFontEntry;
 class gfxOTSContext;
 
 class gfxUserFontSet {
     friend class gfxUserFontEntry;
     friend class gfxOTSContext;
 
 public:
+    typedef mozilla::FontStretch FontStretch;
+    typedef mozilla::FontSlantStyle FontSlantStyle;
     typedef mozilla::FontWeight FontWeight;
 
     NS_INLINE_DECL_THREADSAFE_REFCOUNTING(gfxUserFontSet)
 
     gfxUserFontSet();
 
     enum {
         // no flags ==> no hint set
@@ -220,39 +222,39 @@ public:
 
         // mask of all unused bits, update when adding new formats
         FLAG_FORMAT_NOT_USED       = ~((1 << 12)-1)
     };
 
 
     // creates a font face without adding it to a particular family
     // weight - [100, 900] (multiples of 100)
-    // stretch = [NS_FONT_STRETCH_ULTRA_CONDENSED, NS_FONT_STRETCH_ULTRA_EXPANDED]
+    // stretch = [FontStretch::UltraCondensed(), FontStretch::UltraExpanded()]
     // italic style = constants in gfxFontConstants.h, e.g. NS_FONT_STYLE_NORMAL
     // language override = result of calling nsRuleNode::ParseFontLanguageOverride
     // TODO: support for unicode ranges not yet implemented
     virtual already_AddRefed<gfxUserFontEntry> CreateUserFontEntry(
                               const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
                               FontWeight aWeight,
-                              uint32_t aStretch,
-                              uint8_t aStyle,
+                              FontStretch aStretch,
+                              FontSlantStyle aStyle,
                               const nsTArray<gfxFontFeature>& aFeatureSettings,
                               const nsTArray<gfxFontVariation>& aVariationSettings,
                               uint32_t aLanguageOverride,
                               gfxCharacterMap* aUnicodeRanges,
                               uint8_t aFontDisplay) = 0;
 
     // creates a font face for the specified family, or returns an existing
     // matching entry on the family if there is one
     already_AddRefed<gfxUserFontEntry> FindOrCreateUserFontEntry(
                                const nsAString& aFamilyName,
                                const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
                                FontWeight aWeight,
-                               uint32_t aStretch,
-                               uint8_t aStyle,
+                               FontStretch aStretch,
+                               FontSlantStyle aStyle,
                                const nsTArray<gfxFontFeature>& aFeatureSettings,
                                const nsTArray<gfxFontVariation>& aVariationSettings,
                                uint32_t aLanguageOverride,
                                gfxCharacterMap* aUnicodeRanges,
                                uint8_t aFontDisplay);
 
     // add in a font face for which we have the gfxUserFontEntry already
     void AddUserFontEntry(const nsAString& aFamilyName,
@@ -402,19 +404,20 @@ public:
                 PLDHashNumber principalHash =
                     aKey->mPrincipal ? aKey->mPrincipal->Hash() : 0;
                 return mozilla::HashGeneric(principalHash + int(aKey->mPrivate),
                                             aKey->mURI->Hash(),
                                             HashFeatures(aKey->mFontEntry->mFeatureSettings),
                                             HashVariations(aKey->mFontEntry->mVariationSettings),
                                             mozilla::HashString(aKey->mFontEntry->mFamilyName),
                                             aKey->mFontEntry->mWeight.ForHash(),
-                                            (aKey->mFontEntry->mStyle |
-                                             (aKey->mFontEntry->mStretch << 11) ) ^
-                                             aKey->mFontEntry->mLanguageOverride);
+                                            // XXX Is this right?
+                                            aKey->mFontEntry->mStyle.ForHash(),
+                                            aKey->mFontEntry->mStretch.ForHash(),
+                                            aKey->mFontEntry->mLanguageOverride);
             }
 
             enum { ALLOW_MEMMOVE = false };
 
             gfxFontSrcURI* GetURI() const { return mURI; }
             gfxFontSrcPrincipal* GetPrincipal() const { return mPrincipal; }
             gfxFontEntry* GetFontEntry() const { return mFontEntry; }
             bool IsPrivate() const { return mPrivate; }
@@ -495,18 +498,18 @@ protected:
     // helper method for performing the actual userfont set rebuild
     virtual void DoRebuildUserFontSet() = 0;
 
     // helper method for FindOrCreateUserFontEntry
     gfxUserFontEntry* FindExistingUserFontEntry(
                                    gfxUserFontFamily* aFamily,
                                    const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
                                    FontWeight aWeight,
-                                   uint32_t aStretch,
-                                   uint8_t aStyle,
+                                   FontStretch aStretch,
+                                   FontSlantStyle aStyle,
                                    const nsTArray<gfxFontFeature>& aFeatureSettings,
                                    const nsTArray<gfxFontVariation>& aVariationSettings,
                                    uint32_t aLanguageOverride,
                                    gfxCharacterMap* aUnicodeRanges,
                                    uint8_t aFontDisplay);
 
     // creates a new gfxUserFontFamily in mFontFamilies, or returns an existing
     // family if there is one
@@ -534,44 +537,46 @@ protected:
 class gfxUserFontEntry : public gfxFontEntry {
     friend class mozilla::PostTraversalTask;
     friend class gfxUserFontSet;
     friend class nsUserFontSet;
     friend class nsFontFaceLoader;
     friend class gfxOTSContext;
 
 public:
+    typedef mozilla::FontStretch FontStretch;
+    typedef mozilla::FontSlantStyle FontSlantStyle;
     typedef mozilla::FontWeight FontWeight;
 
     enum UserFontLoadState {
         STATUS_NOT_LOADED = 0,
         STATUS_LOAD_PENDING,
         STATUS_LOADING,
         STATUS_LOADED,
         STATUS_FAILED
     };
 
     gfxUserFontEntry(gfxUserFontSet* aFontSet,
                      const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
                      FontWeight aWeight,
-                     uint32_t aStretch,
-                     uint8_t aStyle,
+                     FontStretch aStretch,
+                     FontSlantStyle aStyle,
                      const nsTArray<gfxFontFeature>& aFeatureSettings,
                      const nsTArray<gfxFontVariation>& aVariationSettings,
                      uint32_t aLanguageOverride,
                      gfxCharacterMap* aUnicodeRanges,
                      uint8_t aFontDisplay);
 
     virtual ~gfxUserFontEntry();
 
     // Return whether the entry matches the given list of attributes
     bool Matches(const nsTArray<gfxFontFaceSrc>& aFontFaceSrcList,
                  FontWeight aWeight,
-                 uint32_t aStretch,
-                 uint8_t aStyle,
+                 FontStretch aStretch,
+                 FontSlantStyle aStyle,
                  const nsTArray<gfxFontFeature>& aFeatureSettings,
                  const nsTArray<gfxFontVariation>& aVariationSettings,
                  uint32_t aLanguageOverride,
                  gfxCharacterMap* aUnicodeRanges,
                  uint8_t aFontDisplay);
 
     gfxFont* CreateFontInstance(const gfxFontStyle* aFontStyle,
                                 bool aNeedsBold) override;
--- a/gfx/vr/ipc/VRManagerChild.cpp
+++ b/gfx/vr/ipc/VRManagerChild.cpp
@@ -590,19 +590,19 @@ VRManagerChild::RemoveListener(dom::VREv
 
   mListeners.RemoveElement(aObserver);
   if (mListeners.IsEmpty()) {
     Unused << SendSetHaveEventListener(false);
   }
 }
 
 void
-VRManagerChild::HandleFatalError(const char* aName, const char* aMsg) const
+VRManagerChild::HandleFatalError(const char* aMsg) const
 {
-  dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aName, aMsg, OtherPid());
+  dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aMsg, OtherPid());
 }
 
 void
 VRManagerChild::AddPromise(const uint32_t& aID, dom::Promise* aPromise)
 {
   MOZ_ASSERT(!mGamepadPromiseList.Get(aID, nullptr));
   mGamepadPromiseList.Put(aID, aPromise);
 }
--- a/gfx/vr/ipc/VRManagerChild.h
+++ b/gfx/vr/ipc/VRManagerChild.h
@@ -73,17 +73,17 @@ public:
   void UpdateDisplayInfo(nsTArray<VRDisplayInfo>& aDisplayUpdates);
   void FireDOMVRDisplayMountedEvent(uint32_t aDisplayID);
   void FireDOMVRDisplayUnmountedEvent(uint32_t aDisplayID);
   void FireDOMVRDisplayConnectEvent(uint32_t aDisplayID);
   void FireDOMVRDisplayDisconnectEvent(uint32_t aDisplayID);
   void FireDOMVRDisplayPresentChangeEvent(uint32_t aDisplayID);
   void FireDOMVRDisplayConnectEventsForLoad(dom::VREventObserver* aObserver);
 
-  virtual void HandleFatalError(const char* aName, const char* aMsg) const override;
+  virtual void HandleFatalError(const char* aMsg) const override;
 
 protected:
   explicit VRManagerChild();
   ~VRManagerChild();
   void Destroy();
   static void DeferredDestroy(RefPtr<VRManagerChild> aVRManagerChild);
 
   virtual PVRLayerChild* AllocPVRLayerChild(const uint32_t& aDisplayID,
--- a/gfx/webrender_bindings/RenderThread.cpp
+++ b/gfx/webrender_bindings/RenderThread.cpp
@@ -6,16 +6,17 @@
 
 #include "base/task.h"
 #include "GeckoProfiler.h"
 #include "RenderThread.h"
 #include "nsThreadUtils.h"
 #include "mtransport/runnable_utils.h"
 #include "mozilla/layers/CompositorThread.h"
 #include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/layers/SharedSurfacesParent.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/webrender/RendererOGL.h"
 #include "mozilla/webrender/RenderTextureHost.h"
 #include "mozilla/widget/CompositorWidget.h"
 
 #ifdef XP_WIN
 #include "mozilla/widget/WinCompositorWindowThread.h"
 #endif
@@ -62,16 +63,17 @@ RenderThread::Start()
     delete thread;
     return;
   }
 
   sRenderThread = new RenderThread(thread);
 #ifdef XP_WIN
   widget::WinCompositorWindowThread::Start();
 #endif
+  layers::SharedSurfacesParent::Initialize();
 }
 
 // static
 void
 RenderThread::ShutDown()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(sRenderThread);
@@ -95,16 +97,20 @@ RenderThread::ShutDown()
 #endif
 }
 
 void
 RenderThread::ShutDownTask(layers::SynchronousTask* aTask)
 {
   layers::AutoCompleteTask complete(aTask);
   MOZ_ASSERT(IsInRenderThread());
+
+  // Releasing on the render thread will allow us to avoid dispatching to remove
+  // remaining textures from the texture map.
+  layers::SharedSurfacesParent::Shutdown();
 }
 
 // static
 MessageLoop*
 RenderThread::Loop()
 {
   return sRenderThread ? sRenderThread->mThread->message_loop() : nullptr;
 }
@@ -444,16 +450,26 @@ RenderThread::UnregisterExternalImage(ui
       this, &RenderThread::DeferredRenderTextureHostDestroy, Move(texture)
     ));
   } else {
     mRenderTextures.Remove(aExternalImageId);
   }
 }
 
 void
+RenderThread::UnregisterExternalImageDuringShutdown(uint64_t aExternalImageId)
+{
+  MOZ_ASSERT(IsInRenderThread());
+  MutexAutoLock lock(mRenderTextureMapLock);
+  MOZ_ASSERT(mHasShutdown);
+  MOZ_ASSERT(mRenderTextures.GetWeak(aExternalImageId));
+  mRenderTextures.Remove(aExternalImageId);
+}
+
+void
 RenderThread::DeferredRenderTextureHostDestroy(RefPtr<RenderTextureHost>)
 {
   // Do nothing. Just decrease the ref-count of RenderTextureHost.
 }
 
 RenderTextureHost*
 RenderThread::GetRenderTexture(wr::WrExternalImageId aExternalImageId)
 {
--- a/gfx/webrender_bindings/RenderThread.h
+++ b/gfx/webrender_bindings/RenderThread.h
@@ -134,16 +134,19 @@ public:
 
   /// Can be called from any thread.
   void RegisterExternalImage(uint64_t aExternalImageId, already_AddRefed<RenderTextureHost> aTexture);
 
   /// Can be called from any thread.
   void UnregisterExternalImage(uint64_t aExternalImageId);
 
   /// Can only be called from the render thread.
+  void UnregisterExternalImageDuringShutdown(uint64_t aExternalImageId);
+
+  /// Can only be called from the render thread.
   RenderTextureHost* GetRenderTexture(WrExternalImageId aExternalImageId);
 
   /// Can be called from any thread.
   bool IsDestroyed(wr::WindowId aWindowId);
   /// Can be called from any thread.
   void SetDestroyed(wr::WindowId aWindowId);
   /// Can be called from any thread.
   bool TooManyPendingFrames(wr::WindowId aWindowId);
--- a/ipc/chromium/src/base/pickle.cc
+++ b/ipc/chromium/src/base/pickle.cc
@@ -446,16 +446,17 @@ bool Pickle::ReadBytesInto(PickleIterato
   if (!buffers_.ReadBytes(iter->iter_, reinterpret_cast<char*>(data), length)) {
     return false;
   }
 
   return iter->iter_.AdvanceAcrossSegments(buffers_, AlignInt(length) - length);
 }
 
 #ifdef MOZ_PICKLE_SENTINEL_CHECKING
+MOZ_NEVER_INLINE
 bool Pickle::ReadSentinel(PickleIterator* iter, uint32_t sentinel) const {
   uint32_t found;
   if (!ReadUInt32(iter, &found)) {
     return false;
   }
   return found == sentinel;
 }
 
--- a/ipc/chromium/src/base/shared_memory.h
+++ b/ipc/chromium/src/base/shared_memory.h
@@ -108,24 +108,16 @@ class SharedMemory {
   //   return ok;
   // Note that the memory is unmapped by calling this method, regardless of the
   // return value.
   bool GiveToProcess(ProcessId target_pid,
                      SharedMemoryHandle* new_handle) {
     return ShareToProcessCommon(target_pid, new_handle, true);
   }
 
-#ifdef OS_POSIX
-  // If named POSIX shm is being used, append the prefix (including
-  // the leading '/') that would be used by a process with the given
-  // pid to the given string and return true.  If not, return false.
-  // (This is public so that the Linux sandboxing code can use it.)
-  static bool AppendPosixShmPrefix(std::string* str, pid_t pid);
-#endif
-
  private:
   bool ShareToProcessCommon(ProcessId target_pid,
                             SharedMemoryHandle* new_handle,
                             bool close_self);
 
 #if defined(OS_WIN)
   HANDLE             mapped_file_;
 #elif defined(OS_POSIX)
--- a/ipc/chromium/src/base/shared_memory_posix.cc
+++ b/ipc/chromium/src/base/shared_memory_posix.cc
@@ -7,25 +7,21 @@
 #include "base/shared_memory.h"
 
 #include <errno.h>
 #include <fcntl.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
 #include <unistd.h>
 
-#ifdef ANDROID
-#include <linux/ashmem.h>
-#endif
-
-#include "base/eintr_wrapper.h"
+#include "base/file_util.h"
 #include "base/logging.h"
+#include "base/platform_thread.h"
 #include "base/string_util.h"
-#include "mozilla/Atomics.h"
-#include "prenv.h"
+#include "mozilla/UniquePtr.h"
 
 namespace base {
 
 SharedMemory::SharedMemory()
     : mapped_file_(-1),
       memory_(NULL),
       read_only_(false),
       max_size_(0) {
@@ -48,102 +44,69 @@ bool SharedMemory::IsHandleValid(const S
   return handle.fd >= 0;
 }
 
 // static
 SharedMemoryHandle SharedMemory::NULLHandle() {
   return SharedMemoryHandle();
 }
 
-// static
-bool SharedMemory::AppendPosixShmPrefix(std::string* str, pid_t pid)
-{
-#if defined(ANDROID) || defined(SHM_ANON)
-  return false;
-#else
-  *str += '/';
-#ifdef OS_LINUX
-  // The Snap package environment doesn't provide a private /dev/shm
-  // (it's used for communication with services like PulseAudio);
-  // instead AppArmor is used to restrict access to it.  Anything with
-  // this prefix is allowed:
-  static const char* const kSnap = PR_GetEnv("SNAP_NAME");
-  if (kSnap) {
-    StringAppendF(str, "snap.%s.", kSnap);
+namespace {
+
+// A class to handle auto-closing of FILE*'s.
+class ScopedFILEClose {
+ public:
+  inline void operator()(FILE* x) const {
+    if (x) {
+      fclose(x);
+    }
   }
-#endif // OS_LINUX
-  // Hopefully the "implementation defined" name length limit is long
-  // enough for this.
-  StringAppendF(str, "org.mozilla.ipc.%d.", static_cast<int>(pid));
-  return true;
-#endif // !ANDROID && !SHM_ANON
+};
+
+typedef mozilla::UniquePtr<FILE, ScopedFILEClose> ScopedFILE;
+
 }
 
 bool SharedMemory::Create(size_t size) {
   read_only_ = false;
 
   DCHECK(size > 0);
   DCHECK(mapped_file_ == -1);
 
-  int fd;
-  bool needs_truncate = true;
+  ScopedFILE file_closer;
+  FILE *fp;
+
+  FilePath path;
+  fp = file_util::CreateAndOpenTemporaryShmemFile(&path);
 
-#ifdef ANDROID
-  // Android has its own shared memory facility:
-  fd = open("/" ASHMEM_NAME_DEF, O_RDWR, 0600);
-  if (fd < 0) {
-    CHROMIUM_LOG(WARNING) << "failed to open shm: " << strerror(errno);
-    return false;
-  }
-  if (ioctl(fd, ASHMEM_SET_SIZE, size) != 0) {
-    CHROMIUM_LOG(WARNING) << "failed to set shm size: " << strerror(errno);
-    close(fd);
+  // Deleting the file prevents anyone else from mapping it in
+  // (making it private), and prevents the need for cleanup (once
+  // the last fd is closed, it is truly freed).
+  file_util::Delete(path);
+
+  if (fp == NULL)
     return false;
-  }
-  needs_truncate = false;
-#elif defined(SHM_ANON)
-  // FreeBSD (or any other Unix that might decide to implement this
-  // nice, simple API):
-  fd = shm_open(SHM_ANON, O_RDWR, 0600);
-#else
-  // Generic Unix: shm_open + shm_unlink
-  do {
-    // The names don't need to be unique, but it saves time if they
-    // usually are.
-    static mozilla::Atomic<size_t> sNameCounter;
-    std::string name;
-    CHECK(AppendPosixShmPrefix(&name, getpid()));
-    StringAppendF(&name, "%zu", sNameCounter++);
-    // O_EXCL means the names being predictable shouldn't be a problem.
-    fd = HANDLE_EINTR(shm_open(name.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600));
-    if (fd >= 0) {
-      if (shm_unlink(name.c_str()) != 0) {
-        // This shouldn't happen, but if it does: assume the file is
-        // in fact leaked, and bail out now while it's still 0-length.
-        DLOG(FATAL) << "failed to unlink shm: " << strerror(errno);
-        return false;
-      }
-    }
-  } while (fd < 0 && errno == EEXIST);
-#endif
+  file_closer.reset(fp);  // close when we go out of scope
 
-  if (fd < 0) {
-    CHROMIUM_LOG(WARNING) << "failed to open shm: " << strerror(errno);
+  // Set the file size.
+  //
+  // According to POSIX, (f)truncate can be used to extend files;
+  // previously this required the XSI option but as of the 2008
+  // edition it's required for everything.  (Linux documents that this
+  // may fail on non-"native" filesystems like FAT, but /dev/shm
+  // should always be tmpfs.)
+  if (ftruncate(fileno(fp), size) != 0)
     return false;
-  }
+  // This probably isn't needed.
+  if (fseeko(fp, size, SEEK_SET) != 0)
+    return false;
 
-  if (needs_truncate) {
-    if (HANDLE_EINTR(ftruncate(fd, static_cast<off_t>(size))) != 0) {
-      CHROMIUM_LOG(WARNING) << "failed to set shm size: " << strerror(errno);
-      close(fd);
-      return false;
-    }
-  }
+  mapped_file_ = dup(fileno(fp));
+  DCHECK(mapped_file_ >= 0);
 
-  mapped_file_ = fd;
   max_size_ = size;
   return true;
 }
 
 bool SharedMemory::Map(size_t bytes) {
   if (mapped_file_ == -1)
     return false;
 
--- a/ipc/glue/FileDescriptor.cpp
+++ b/ipc/glue/FileDescriptor.cpp
@@ -244,16 +244,15 @@ IPDLParamTraits<FileDescriptor>::Read(co
 {
   FileDescriptor::PickleType pfd;
   if (!ReadIPDLParam(aMsg, aIter, aActor, &pfd)) {
     return false;
   }
 
   *aResult = FileDescriptor(FileDescriptor::IPDLPrivate(), pfd);
   if (!aResult->IsValid()) {
-    printf_stderr("IPDL protocol Error: [%s] Received an invalid file descriptor\n",
-                  aActor->ProtocolName());
+    printf_stderr("IPDL protocol Error: Received an invalid file descriptor\n");
   }
   return true;
 }
 
 } // namespace ipc
 } // namespace mozilla
--- a/ipc/glue/IPDLParamTraits.h
+++ b/ipc/glue/IPDLParamTraits.h
@@ -50,27 +50,27 @@ struct IPDLParamTraits
 // rather than ParamTraits.
 //
 // NOTE: WriteIPDLParam takes a universal reference, so that it can support
 // whatever reference type is supported by the underlying IPDLParamTraits::Write
 // implementation. See the comment on IPDLParamTraits<nsTArray<T>>::Write for
 // more information.
 //
 template<typename P>
-static inline void
+static MOZ_NEVER_INLINE void
 WriteIPDLParam(IPC::Message* aMsg,
                IProtocol* aActor,
                P&& aParam)
 {
   IPDLParamTraits<typename IPC::ParamTraitsSelector<P>::Type>
     ::Write(aMsg, aActor, Forward<P>(aParam));
 }
 
 template<typename P>
-static inline bool
+static MOZ_NEVER_INLINE bool
 ReadIPDLParam(const IPC::Message* aMsg,
               PickleIterator* aIter,
               IProtocol* aActor,
               P* aResult)
 {
   return IPDLParamTraits<typename IPC::ParamTraitsSelector<P>::Type>
     ::Read(aMsg, aIter, aActor, aResult);
 }
--- a/ipc/glue/MessageChannel.cpp
+++ b/ipc/glue/MessageChannel.cpp
@@ -1163,17 +1163,19 @@ void
 MessageChannel::OnMessageReceivedFromLink(Message&& aMsg)
 {
     AssertLinkThread();
     mMonitor->AssertCurrentThreadOwns();
 
     if (MaybeInterceptSpecialIOMessage(aMsg))
         return;
 
+#ifdef EARLY_BETA_OR_EARLIER
     mListener->OnChannelReceivedMessage(aMsg);
+#endif
 
     // Regardless of the Interrupt stack, if we're awaiting a sync reply,
     // we know that it needs to be immediately handled to unblock us.
     if (aMsg.is_sync() && aMsg.is_reply()) {
         IPC_LOG("Received reply seqno=%d xid=%d", aMsg.seqno(), aMsg.transaction_id());
 
         if (aMsg.seqno() == mTimedOutMessageSeqno) {
             // Drop the message, but allow future sync messages to be sent.
--- a/ipc/glue/ProtocolUtils.cpp
+++ b/ipc/glue/ProtocolUtils.cpp
@@ -55,17 +55,17 @@ MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLAT
 #endif
 
 namespace ipc {
 
 IPCResult
 IPCResult::Fail(NotNull<IProtocol*> actor, const char* where, const char* why)
 {
   // Calls top-level protocol to handle the error.
-  nsPrintfCString errorMsg("%s::%s %s\n", actor->ProtocolName(), where, why);
+  nsPrintfCString errorMsg("%s %s\n", where, why);
   actor->GetIPCChannel()->Listener()->ProcessingError(
     HasResultCodes::MsgProcessingError, errorMsg.get());
   return IPCResult(false);
 }
 
 class ChannelOpened : public IPC::Message
 {
 public:
@@ -269,32 +269,28 @@ ProtocolErrorBreakpoint(const char* aMsg
 {
     // Bugs that generate these error messages can be tough to
     // reproduce.  Log always in the hope that someone finds the error
     // message.
     printf_stderr("IPDL protocol error: %s\n", aMsg);
 }
 
 void
-FatalError(const char* aProtocolName, const char* aMsg, bool aIsParent)
+FatalError(const char* aMsg, bool aIsParent)
 {
   ProtocolErrorBreakpoint(aMsg);
 
-  nsAutoCString formattedMessage("IPDL error [");
-  formattedMessage.AppendASCII(aProtocolName);
-  formattedMessage.AppendLiteral("]: \"");
+  nsAutoCString formattedMessage("IPDL error: \"");
   formattedMessage.AppendASCII(aMsg);
   if (aIsParent) {
     // We're going to crash the parent process because at this time
     // there's no other really nice way of getting a minidump out of
     // this process if we're off the main thread.
     formattedMessage.AppendLiteral("\". Intentionally crashing.");
     NS_ERROR(formattedMessage.get());
-    CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("IPCFatalErrorProtocol"),
-                                       nsDependentCString(aProtocolName));
     CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("IPCFatalErrorMsg"),
                                        nsDependentCString(aMsg));
     AnnotateSystemError();
     MOZ_CRASH("IPC FatalError in the parent process!");
   } else {
     formattedMessage.AppendLiteral("\". abort()ing as a result.");
     MOZ_CRASH_UNSAFE_OOL(formattedMessage.get());
   }
@@ -393,90 +389,102 @@ IProtocol::ReadActor(const IPC::Message*
         MismatchedActorTypeError(aActorDescription);
         return Nothing();
     }
 
     return Some(listener);
 }
 
 int32_t
-IProtocol::Register(IProtocol* aRouted)
+IProtocol::ManagedState::Register(IProtocol* aRouted)
 {
-  return Manager()->Register(aRouted);
+  return mProtocol->Manager()->Register(aRouted);
 }
 
 int32_t
-IProtocol::RegisterID(IProtocol* aRouted, int32_t aId)
+IProtocol::ManagedState::RegisterID(IProtocol* aRouted, int32_t aId)
 {
-  return Manager()->RegisterID(aRouted, aId);
+  return mProtocol->Manager()->RegisterID(aRouted, aId);
 }
 
 IProtocol*
-IProtocol::Lookup(int32_t aId)
+IProtocol::ManagedState::Lookup(int32_t aId)
 {
-  return Manager()->Lookup(aId);
+  return mProtocol->Manager()->Lookup(aId);
 }
 
 void
-IProtocol::Unregister(int32_t aId)
+IProtocol::ManagedState::Unregister(int32_t aId)
 {
-  if (mId == aId) {
-    mId = kFreedActorId;
+  if (mProtocol->mId == aId) {
+    mProtocol->mId = kFreedActorId;
   }
-  Manager()->Unregister(aId);
+  mProtocol->Manager()->Unregister(aId);
+}
+
+Shmem::SharedMemory*
+IProtocol::ManagedState::CreateSharedMemory(size_t aSize,
+                                            SharedMemory::SharedMemoryType aType,
+                                            bool aUnsafe,
+                                            int32_t* aId)
+{
+  return mProtocol->Manager()->CreateSharedMemory(aSize, aType, aUnsafe, aId);
 }
 
 Shmem::SharedMemory*
-IProtocol::CreateSharedMemory(size_t aSize,
-                              SharedMemory::SharedMemoryType aType,
-                              bool aUnsafe,
-                              int32_t* aId)
+IProtocol::ManagedState::LookupSharedMemory(int32_t aId)
 {
-  return Manager()->CreateSharedMemory(aSize, aType, aUnsafe, aId);
+  return mProtocol->Manager()->LookupSharedMemory(aId);
 }
 
-Shmem::SharedMemory*
-IProtocol::LookupSharedMemory(int32_t aId)
+bool
+IProtocol::ManagedState::IsTrackingSharedMemory(Shmem::SharedMemory* aSegment)
 {
-  return Manager()->LookupSharedMemory(aId);
+  return mProtocol->Manager()->IsTrackingSharedMemory(aSegment);
 }
 
 bool
-IProtocol::IsTrackingSharedMemory(Shmem::SharedMemory* aSegment)
+IProtocol::ManagedState::DestroySharedMemory(Shmem& aShmem)
 {
-  return Manager()->IsTrackingSharedMemory(aSegment);
+  return mProtocol->Manager()->DestroySharedMemory(aShmem);
 }
 
-bool
-IProtocol::DestroySharedMemory(Shmem& aShmem)
+const MessageChannel*
+IProtocol::ManagedState::GetIPCChannel() const
 {
-  return Manager()->DestroySharedMemory(aShmem);
+  return mChannel;
+}
+
+MessageChannel*
+IProtocol::ManagedState::GetIPCChannel()
+{
+  return mChannel;
 }
 
 ProcessId
 IProtocol::OtherPid() const
 {
   return Manager()->OtherPid();
 }
 
 void
 IProtocol::FatalError(const char* const aErrorMsg) const
 {
-  HandleFatalError(ProtocolName(), aErrorMsg);
+  HandleFatalError(aErrorMsg);
 }
 
 void
-IProtocol::HandleFatalError(const char* aProtocolName, const char* aErrorMsg) const
+IProtocol::HandleFatalError(const char* aErrorMsg) const
 {
   if (IProtocol* manager = Manager()) {
-    manager->HandleFatalError(aProtocolName, aErrorMsg);
+    manager->HandleFatalError(aErrorMsg);
     return;
   }
 
-  mozilla::ipc::FatalError(aProtocolName, aErrorMsg, mSide == ParentSide);
+  mozilla::ipc::FatalError(aErrorMsg, mSide == ParentSide);
 }
 
 bool
 IProtocol::AllocShmem(size_t aSize,
                       Shmem::SharedMemory::SharedMemoryType aType,
                       Shmem* aOutMem)
 {
   Shmem::id_t id;
@@ -525,71 +533,106 @@ IProtocol::DeallocShmem(Shmem& aMem)
 void
 IProtocol::SetManager(IProtocol* aManager)
 {
   MOZ_RELEASE_ASSERT(!mManager || mManager == aManager);
   mManager = aManager;
 }
 
 void
+IProtocol::SetManagerAndRegister(IProtocol* aManager)
+{
+  // Set the manager prior to registering so registering properly inherits
+  // the manager's event target.
+  SetManager(aManager);
+
+  aManager->Register(this);
+
+  mState->SetIPCChannel(aManager->GetIPCChannel());
+}
+
+void
+IProtocol::SetManagerAndRegister(IProtocol* aManager, int32_t aId)
+{
+  // Set the manager prior to registering so registering properly inherits
+  // the manager's event target.
+  SetManager(aManager);
+
+  aManager->RegisterID(this, aId);
+
+  mState->SetIPCChannel(aManager->GetIPCChannel());
+}
+
+void
 IProtocol::SetEventTargetForActor(IProtocol* aActor, nsIEventTarget* aEventTarget)
 {
   // Make sure we have a manager for the internal method to access.
   aActor->SetManager(this);
-  SetEventTargetForActorInternal(aActor, aEventTarget);
+  mState->SetEventTargetForActor(aActor, aEventTarget);
 }
 
 void
 IProtocol::ReplaceEventTargetForActor(IProtocol* aActor,
                                       nsIEventTarget* aEventTarget)
 {
   // Ensure the actor has been registered.
   MOZ_ASSERT(aActor->Manager());
-  ReplaceEventTargetForActorInternal(aActor, aEventTarget);
-}
-
-void
-IProtocol::SetEventTargetForActorInternal(IProtocol* aActor,
-                                          nsIEventTarget* aEventTarget)
-{
-  Manager()->SetEventTargetForActorInternal(aActor, aEventTarget);
-}
-
-void
-IProtocol::ReplaceEventTargetForActorInternal(IProtocol* aActor,
-                                              nsIEventTarget* aEventTarget)
-{
-  Manager()->ReplaceEventTargetForActorInternal(aActor, aEventTarget);
+  mState->ReplaceEventTargetForActor(aActor, aEventTarget);
 }
 
 nsIEventTarget*
 IProtocol::GetActorEventTarget()
 {
-  // We should only call this function when this actor has been registered and
-  // is not unregistered yet.
-  MOZ_RELEASE_ASSERT(mId != kNullActorId && mId != kFreedActorId);
-  RefPtr<nsIEventTarget> target = Manager()->GetActorEventTargetInternal(this);
-  return target;
+  return mState->GetActorEventTarget();
 }
 
 already_AddRefed<nsIEventTarget>
-IProtocol::GetActorEventTargetInternal(IProtocol* aActor)
+IProtocol::GetActorEventTarget(IProtocol* aActor)
 {
-  return Manager()->GetActorEventTargetInternal(aActor);
+  return mState->GetActorEventTarget(aActor);
+}
+
+nsIEventTarget*
+IProtocol::ManagedState::GetActorEventTarget()
+{
+  // We should only call this function when this actor has been registered and
+  // is not unregistered yet.
+  MOZ_RELEASE_ASSERT(mProtocol->mId != kNullActorId && mProtocol->mId != kFreedActorId);
+  RefPtr<nsIEventTarget> target = GetActorEventTarget(mProtocol);
+  return target;
 }
 
-IToplevelProtocol::IToplevelProtocol(ProtocolId aProtoId, Side aSide)
- : IProtocol(aSide),
+void
+IProtocol::ManagedState::SetEventTargetForActor(IProtocol* aActor,
+                                                        nsIEventTarget* aEventTarget)
+{
+  // Go directly through the state so we don't try to redundantly (and
+  // wrongly) call SetManager() on aActor.
+  mProtocol->Manager()->mState->SetEventTargetForActor(aActor, aEventTarget);
+}
+
+void
+IProtocol::ManagedState::ReplaceEventTargetForActor(IProtocol* aActor,
+                                                            nsIEventTarget* aEventTarget)
+{
+  mProtocol->Manager()->ReplaceEventTargetForActor(aActor, aEventTarget);
+}
+
+already_AddRefed<nsIEventTarget>
+IProtocol::ManagedState::GetActorEventTarget(IProtocol* aActor)
+{
+  return mProtocol->Manager()->GetActorEventTarget(aActor);
+}
+
+IToplevelProtocol::IToplevelProtocol(const char* aName, ProtocolId aProtoId, Side aSide)
+ : IProtocol(aSide, MakeUnique<ToplevelState>(aName, this, aSide)),
    mMonitor("mozilla.ipc.IToplevelProtocol.mMonitor"),
    mProtocolId(aProtoId),
    mOtherPid(mozilla::ipc::kInvalidProcessId),
-   mOtherPidState(ProcessIdState::eUnstarted),
-   mLastRouteId(aSide == ParentSide ? kFreedActorId : kNullActorId),
-   mLastShmemId(aSide == ParentSide ? kFreedActorId : kNullActorId),
-   mEventTargetMutex("ProtocolEventTargetMutex")
+   mOtherPidState(ProcessIdState::eUnstarted)
 {
 }
 
 IToplevelProtocol::~IToplevelProtocol()
 {
   if (mTrans) {
     RefPtr<DeleteTask<Transport>> task = new DeleteTask<Transport>(mTrans.release());
     XRE_GetIOMessageLoop()->PostTask(task.forget());
@@ -690,159 +733,173 @@ IToplevelProtocol::SetReplyTimeoutMs(int
 
 bool
 IToplevelProtocol::IsOnCxxStack() const
 {
   return GetIPCChannel()->IsOnCxxStack();
 }
 
 int32_t
-IToplevelProtocol::Register(IProtocol* aRouted)
+IToplevelProtocol::ToplevelState::Register(IProtocol* aRouted)
 {
   if (aRouted->Id() != kNullActorId && aRouted->Id() != kFreedActorId) {
     // If there's already an ID, just return that.
     return aRouted->Id();
   }
-  int32_t id = GetSide() == ParentSide ? ++mLastRouteId : --mLastRouteId;
+  int32_t id = mProtocol->GetSide() == ParentSide ? ++mLastRouteId : --mLastRouteId;
   mActorMap.AddWithID(aRouted, id);
   aRouted->SetId(id);
 
   // Inherit our event target from our manager.
   if (IProtocol* manager = aRouted->Manager()) {
     MutexAutoLock lock(mEventTargetMutex);
     if (nsCOMPtr<nsIEventTarget> target = mEventTargetMap.Lookup(manager->Id())) {
       mEventTargetMap.AddWithID(target, id);
     }
   }
 
   return id;
 }
 
 int32_t
-IToplevelProtocol::RegisterID(IProtocol* aRouted,
-                              int32_t aId)
+IToplevelProtocol::ToplevelState::RegisterID(IProtocol* aRouted,
+                                     int32_t aId)
 {
   mActorMap.AddWithID(aRouted, aId);
   aRouted->SetId(aId);
   return aId;
 }
 
 IProtocol*
-IToplevelProtocol::Lookup(int32_t aId)
+IToplevelProtocol::ToplevelState::Lookup(int32_t aId)
 {
   return mActorMap.Lookup(aId);
 }
 
 void
-IToplevelProtocol::Unregister(int32_t aId)
+IToplevelProtocol::ToplevelState::Unregister(int32_t aId)
 {
   mActorMap.Remove(aId);
 
   MutexAutoLock lock(mEventTargetMutex);
   mEventTargetMap.RemoveIfPresent(aId);
 }
 
+IToplevelProtocol::ToplevelState::ToplevelState(const char* aName,
+                                                IToplevelProtocol* aProtocol,
+                                                Side aSide)
+  : ProtocolState()
+  , mProtocol(aProtocol)
+  , mLastRouteId(aSide == ParentSide ? kFreedActorId : kNullActorId)
+  , mLastShmemId(aSide == ParentSide ? kFreedActorId : kNullActorId)
+  , mEventTargetMutex("ProtocolEventTargetMutex")
+  , mChannel(aName, aProtocol)
+{
+}
+
 Shmem::SharedMemory*
-IToplevelProtocol::CreateSharedMemory(size_t aSize,
-                                      Shmem::SharedMemory::SharedMemoryType aType,
-                                      bool aUnsafe,
-                                      Shmem::id_t* aId)
+IToplevelProtocol::ToplevelState::CreateSharedMemory(size_t aSize,
+                                                     Shmem::SharedMemory::SharedMemoryType aType,
+                                                     bool aUnsafe,
+                                                     Shmem::id_t* aId)
 {
+  // XXX the mProtocol uses here should go away!
   RefPtr<Shmem::SharedMemory> segment(
     Shmem::Alloc(Shmem::PrivateIPDLCaller(), aSize, aType, aUnsafe));
   if (!segment) {
     return nullptr;
   }
-  int32_t id = GetSide() == ParentSide ? ++mLastShmemId : --mLastShmemId;
+  int32_t id = mProtocol->GetSide() == ParentSide ? ++mLastShmemId : --mLastShmemId;
   Shmem shmem(
     Shmem::PrivateIPDLCaller(),
     segment.get(),
     id);
 
   base::ProcessId pid =
 #ifdef ANDROID
     // We use OtherPidMaybeInvalid() because on Android this method is actually
     // called on an unconnected protocol, but Android's shared memory
     // implementation doesn't actually use the PID.
-    OtherPidMaybeInvalid();
+    mProtocol->OtherPidMaybeInvalid();
 #else
-    OtherPid();
+    mProtocol->OtherPid();
 #endif
 
   Message* descriptor = shmem.ShareTo(
     Shmem::PrivateIPDLCaller(), pid, MSG_ROUTING_CONTROL);
   if (!descriptor) {
     return nullptr;
   }
-  Unused << GetIPCChannel()->Send(descriptor);
+  Unused << mProtocol->GetIPCChannel()->Send(descriptor);
 
   *aId = shmem.Id(Shmem::PrivateIPDLCaller());
   Shmem::SharedMemory* rawSegment = segment.get();
   mShmemMap.AddWithID(segment.forget().take(), *aId);
   return rawSegment;
 }
 
 Shmem::SharedMemory*
-IToplevelProtocol::LookupSharedMemory(Shmem::id_t aId)
+IToplevelProtocol::ToplevelState::LookupSharedMemory(Shmem::id_t aId)
 {
   return mShmemMap.Lookup(aId);
 }
 
 bool
-IToplevelProtocol::IsTrackingSharedMemory(Shmem::SharedMemory* segment)
+IToplevelProtocol::ToplevelState::IsTrackingSharedMemory(Shmem::SharedMemory* segment)
 {
   return mShmemMap.HasData(segment);
 }
 
 bool
-IToplevelProtocol::DestroySharedMemory(Shmem& shmem)
+IToplevelProtocol::ToplevelState::DestroySharedMemory(Shmem& shmem)
 {
   Shmem::id_t aId = shmem.Id(Shmem::PrivateIPDLCaller());
   Shmem::SharedMemory* segment = LookupSharedMemory(aId);
   if (!segment) {
     return false;
   }
 
   Message* descriptor = shmem.UnshareFrom(
     Shmem::PrivateIPDLCaller(), MSG_ROUTING_CONTROL);
 
   mShmemMap.Remove(aId);
   Shmem::Dealloc(Shmem::PrivateIPDLCaller(), segment);
 
-  if (!GetIPCChannel()->CanSend()) {
+  MessageChannel* channel = mProtocol->GetIPCChannel();
+  if (!channel->CanSend()) {
     delete descriptor;
     return true;
   }
 
-  return descriptor && GetIPCChannel()->Send(descriptor);
+  return descriptor && channel->Send(descriptor);
 }
 
 void
-IToplevelProtocol::DeallocShmems()
+IToplevelProtocol::ToplevelState::DeallocShmems()
 {
   for (IDMap<SharedMemory*>::const_iterator cit = mShmemMap.begin(); cit != mShmemMap.end(); ++cit) {
     Shmem::Dealloc(Shmem::PrivateIPDLCaller(), cit->second);
   }
   mShmemMap.Clear();
 }
 
 bool
-IToplevelProtocol::ShmemCreated(const Message& aMsg)
+IToplevelProtocol::ToplevelState::ShmemCreated(const Message& aMsg)
 {
   Shmem::id_t id;
   RefPtr<Shmem::SharedMemory> rawmem(Shmem::OpenExisting(Shmem::PrivateIPDLCaller(), aMsg, &id, true));
   if (!rawmem) {
     return false;
   }
   mShmemMap.AddWithID(rawmem.forget().take(), id);
   return true;
 }
 
 bool
-IToplevelProtocol::ShmemDestroyed(const Message& aMsg)
+IToplevelProtocol::ToplevelState::ShmemDestroyed(const Message& aMsg)
 {
   Shmem::id_t id;
   PickleIterator iter = PickleIterator(aMsg);
   if (!IPC::ReadParam(&aMsg, &iter, &id)) {
     return false;
   }
   aMsg.EndRead(iter);
 
@@ -850,17 +907,17 @@ IToplevelProtocol::ShmemDestroyed(const 
   if (rawmem) {
     mShmemMap.Remove(id);
     Shmem::Dealloc(Shmem::PrivateIPDLCaller(), rawmem);
   }
   return true;
 }
 
 already_AddRefed<nsIEventTarget>
-IToplevelProtocol::GetMessageEventTarget(const Message& aMsg)
+IToplevelProtocol::ToplevelState::GetMessageEventTarget(const Message& aMsg)
 {
   int32_t route = aMsg.routing_id();
 
   Maybe<MutexAutoLock> lock;
   lock.emplace(mEventTargetMutex);
 
   nsCOMPtr<nsIEventTarget> target = mEventTargetMap.Lookup(route);
 
@@ -871,59 +928,53 @@ IToplevelProtocol::GetMessageEventTarget
       return nullptr;
     }
 
     // Normally a new actor inherits its event target from its manager. If the
     // manager has no event target, we give the subclass a chance to make a new
     // one.
     if (!target) {
       MutexAutoUnlock unlock(mEventTargetMutex);
-      target = GetConstructedEventTarget(aMsg);
+      target = mProtocol->GetConstructedEventTarget(aMsg);
     }
 
     mEventTargetMap.AddWithID(target, handle.mId);
   } else if (!target) {
     // We don't need the lock after this point.
     lock.reset();
 
-    target = GetSpecificMessageEventTarget(aMsg);
+    target = mProtocol->GetSpecificMessageEventTarget(aMsg);
   }
 
   return target.forget();
 }
 
 already_AddRefed<nsIEventTarget>
-IToplevelProtocol::GetActorEventTargetInternal(IProtocol* aActor)
+IToplevelProtocol::ToplevelState::GetActorEventTarget(IProtocol* aActor)
 {
   MOZ_RELEASE_ASSERT(aActor->Id() != kNullActorId && aActor->Id() != kFreedActorId);
 
   MutexAutoLock lock(mEventTargetMutex);
   nsCOMPtr<nsIEventTarget> target = mEventTargetMap.Lookup(aActor->Id());
   return target.forget();
 }
 
-already_AddRefed<nsIEventTarget>
-IToplevelProtocol::GetActorEventTarget(IProtocol* aActor)
-{
-  return GetActorEventTargetInternal(aActor);
-}
-
 nsIEventTarget*
-IToplevelProtocol::GetActorEventTarget()
+IToplevelProtocol::ToplevelState::GetActorEventTarget()
 {
   // The EventTarget of a ToplevelProtocol shall never be set.
   return nullptr;
 }
 
 void
-IToplevelProtocol::SetEventTargetForActorInternal(IProtocol* aActor,
-                                                  nsIEventTarget* aEventTarget)
+IToplevelProtocol::ToplevelState::SetEventTargetForActor(IProtocol* aActor,
+                                                 nsIEventTarget* aEventTarget)
 {
   // The EventTarget of a ToplevelProtocol shall never be set.
-  MOZ_RELEASE_ASSERT(aActor != this);
+  MOZ_RELEASE_ASSERT(aActor != mProtocol);
 
   // We should only call this function on actors that haven't been used for IPC
   // code yet. Otherwise we'll be posting stuff to the wrong event target before
   // we're called.
   MOZ_RELEASE_ASSERT(aActor->Id() == kNullActorId || aActor->Id() == kFreedActorId);
 
   // Register the actor early. When it's registered again, it will keep the same
   // ID.
@@ -940,25 +991,37 @@ IToplevelProtocol::SetEventTargetForActo
   if (replace) {
     mEventTargetMap.ReplaceWithID(aEventTarget, id);
   } else {
     mEventTargetMap.AddWithID(aEventTarget, id);
   }
 }
 
 void
-IToplevelProtocol::ReplaceEventTargetForActorInternal(
+IToplevelProtocol::ToplevelState::ReplaceEventTargetForActor(
   IProtocol* aActor,
   nsIEventTarget* aEventTarget)
 {
   // The EventTarget of a ToplevelProtocol shall never be set.
-  MOZ_RELEASE_ASSERT(aActor != this);
+  MOZ_RELEASE_ASSERT(aActor != mProtocol);
 
   int32_t id = aActor->Id();
   // The ID of the actor should have existed.
   MOZ_RELEASE_ASSERT(id!= kNullActorId && id!= kFreedActorId);
 
   MutexAutoLock lock(mEventTargetMutex);
   mEventTargetMap.ReplaceWithID(aEventTarget, id);
 }
 
+const MessageChannel*
+IToplevelProtocol::ToplevelState::GetIPCChannel() const
+{
+  return &mChannel;
+}
+
+MessageChannel*
+IToplevelProtocol::ToplevelState::GetIPCChannel()
+{
+  return &mChannel;
+}
+
 } // namespace ipc
 } // namespace mozilla
--- a/ipc/glue/ProtocolUtils.h
+++ b/ipc/glue/ProtocolUtils.h
@@ -15,16 +15,17 @@
 
 #include "prenv.h"
 
 #include "IPCMessageStart.h"
 #include "mozilla/AlreadyAddRefed.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/ipc/ByteBuf.h"
 #include "mozilla/ipc/FileDescriptor.h"
+#include "mozilla/ipc/MessageChannel.h"
 #include "mozilla/ipc/Shmem.h"
 #include "mozilla/ipc/Transport.h"
 #include "mozilla/ipc/MessageLink.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/MozPromise.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/NotNull.h"
@@ -138,55 +139,176 @@ public:
     enum ActorDestroyReason {
         FailedConstructor,
         Deletion,
         AncestorDeletion,
         NormalShutdown,
         AbnormalShutdown
     };
 
+    // A lot of the functionality of IProtocol only differs between toplevel
+    // protocols (IToplevelProtocol) and managed protocols (everything else).
+    // If we put such functionality in IProtocol via virtual methods, that
+    // means that *every* protocol inherits that functionality through said
+    // virtual methods, then every protocol needs a (largely redundant)
+    // entry in its vtable.  That redundancy adds up quickly with several
+    // hundred protocols.
+    //
+    // This class (and its two subclasses) ensure that we don't have a bunch
+    // of redundant entries in protocol vtables: we have a single vtable per
+    // subclass, and then each protocol has its own instance of one of the
+    // subclasses.  This setup makes things a bit slower, but the space
+    // savings are worth it.
+    class ProtocolState
+    {
+    public:
+        ProtocolState() : mChannel(nullptr) {}
+        virtual ~ProtocolState() = default;
+
+        // Shared memory functions.
+        virtual Shmem::SharedMemory* CreateSharedMemory(
+            size_t, SharedMemory::SharedMemoryType, bool, int32_t*) = 0;
+        virtual Shmem::SharedMemory* LookupSharedMemory(int32_t) = 0;
+        virtual bool IsTrackingSharedMemory(Shmem::SharedMemory*) = 0;
+        virtual bool DestroySharedMemory(Shmem&) = 0;
+
+        // Protocol management functions.
+        virtual int32_t Register(IProtocol*) = 0;
+        virtual int32_t RegisterID(IProtocol*, int32_t) = 0;
+        virtual IProtocol* Lookup(int32_t) = 0;
+        virtual void Unregister(int32_t) = 0;
+
+        // Returns the event target set by SetEventTargetForActor() if available.
+        virtual nsIEventTarget* GetActorEventTarget() = 0;
+
+        virtual void SetEventTargetForActor(IProtocol* aActor, nsIEventTarget* aEventTarget) = 0;
+        virtual void ReplaceEventTargetForActor(IProtocol* aActor, nsIEventTarget* aEventTarget) = 0;
+
+        virtual already_AddRefed<nsIEventTarget>
+        GetActorEventTarget(IProtocol* aActor) = 0;
+
+        virtual const MessageChannel* GetIPCChannel() const = 0;
+        virtual MessageChannel* GetIPCChannel() = 0;
+
+        // XXX we have this weird setup where ProtocolState has an mChannel
+        // member, but it (probably?) only gets set for protocols that have
+        // a manager.  That is, for toplevel protocols, this member is dead
+        // weight and should be removed, since toplevel protocols maintain
+        // their own channel.
+        void SetIPCChannel(MessageChannel* aChannel) { mChannel = aChannel; }
+
+    protected:
+        MessageChannel* mChannel;
+    };
+
+    // Managed protocols just forward all of their operations to the topmost
+    // managing protocol.
+    class ManagedState final : public ProtocolState
+    {
+    public:
+        explicit ManagedState(IProtocol* aProtocol)
+            : ProtocolState()
+            , mProtocol(aProtocol)
+        {}
+
+        Shmem::SharedMemory* CreateSharedMemory(
+            size_t, SharedMemory::SharedMemoryType, bool, int32_t*) override;
+        Shmem::SharedMemory* LookupSharedMemory(int32_t) override;
+        bool IsTrackingSharedMemory(Shmem::SharedMemory*) override;
+        bool DestroySharedMemory(Shmem&) override;
+
+        int32_t Register(IProtocol*) override;
+        int32_t RegisterID(IProtocol*, int32_t) override;
+        IProtocol* Lookup(int32_t) override;
+        void Unregister(int32_t) override;
+
+        nsIEventTarget* GetActorEventTarget() override;
+        void SetEventTargetForActor(IProtocol* aActor, nsIEventTarget* aEventTarget) override;
+        void ReplaceEventTargetForActor(IProtocol* aActor, nsIEventTarget* aEventTarget) override;
+        already_AddRefed<nsIEventTarget> GetActorEventTarget(IProtocol* aActor) override;
+
+        const MessageChannel* GetIPCChannel() const override;
+        MessageChannel* GetIPCChannel() override;
+
+    private:
+        IProtocol* const mProtocol;
+    };
+
     typedef base::ProcessId ProcessId;
     typedef IPC::Message Message;
     typedef IPC::MessageInfo MessageInfo;
 
-    IProtocol(Side aSide) : mId(0), mSide(aSide), mManager(nullptr), mChannel(nullptr) {}
+    explicit IProtocol(Side aSide)
+        : IProtocol(aSide, MakeUnique<ManagedState>(this))
+    {}
 
-    virtual int32_t Register(IProtocol*);
-    virtual int32_t RegisterID(IProtocol*, int32_t);
-    virtual IProtocol* Lookup(int32_t);
-    virtual void Unregister(int32_t);
+    int32_t Register(IProtocol* aRouted)
+    {
+        return mState->Register(aRouted);
+    }
+    int32_t RegisterID(IProtocol* aRouted, int32_t aId)
+    {
+        return mState->RegisterID(aRouted, aId);
+    }
+    IProtocol* Lookup(int32_t aId)
+    {
+        return mState->Lookup(aId);
+    }
+    void Unregister(int32_t aId)
+    {
+        return mState->Unregister(aId);
+    }
+
     virtual void RemoveManagee(int32_t, IProtocol*) = 0;
 
-    virtual Shmem::SharedMemory* CreateSharedMemory(
-        size_t, SharedMemory::SharedMemoryType, bool, int32_t*);
-    virtual Shmem::SharedMemory* LookupSharedMemory(int32_t);
-    virtual bool IsTrackingSharedMemory(Shmem::SharedMemory*);
-    virtual bool DestroySharedMemory(Shmem&);
+    Shmem::SharedMemory* CreateSharedMemory(
+        size_t aSize, SharedMemory::SharedMemoryType aType, bool aUnsafe, int32_t* aId)
+    {
+        return mState->CreateSharedMemory(aSize, aType, aUnsafe, aId);
+    }
+    Shmem::SharedMemory* LookupSharedMemory(int32_t aId)
+    {
+        return mState->LookupSharedMemory(aId);
+    }
+    bool IsTrackingSharedMemory(Shmem::SharedMemory* aSegment)
+    {
+        return mState->IsTrackingSharedMemory(aSegment);
+    }
+    bool DestroySharedMemory(Shmem& aShmem)
+    {
+        return mState->DestroySharedMemory(aShmem);
+    }
+
+    MessageChannel* GetIPCChannel()
+    {
+        return mState->GetIPCChannel();
+    }
+    const MessageChannel* GetIPCChannel() const
+    {
+        return mState->GetIPCChannel();
+    }
 
     // XXX odd ducks, acknowledged
     virtual ProcessId OtherPid() const;
     Side GetSide() const { return mSide; }
 
-    virtual const char* ProtocolName() const = 0;
     void FatalError(const char* const aErrorMsg) const;
-    virtual void HandleFatalError(const char* aProtocolName, const char* aErrorMsg) const;
+    virtual void HandleFatalError(const char* aErrorMsg) const;
 
     Maybe<IProtocol*> ReadActor(const IPC::Message* aMessage, PickleIterator* aIter, bool aNullable,
                                 const char* aActorDescription, int32_t aProtocolTypeId);
 
     virtual Result OnMessageReceived(const Message& aMessage) = 0;
     virtual Result OnMessageReceived(const Message& aMessage, Message *& aReply) = 0;
     virtual Result OnCallReceived(const Message& aMessage, Message *& aReply) = 0;
 
     virtual int32_t GetProtocolTypeId() = 0;
 
     int32_t Id() const { return mId; }
     IProtocol* Manager() const { return mManager; }
-    virtual const MessageChannel* GetIPCChannel() const { return mChannel; }
-    virtual MessageChannel* GetIPCChannel() { return mChannel; }
 
     bool AllocShmem(size_t aSize, Shmem::SharedMemory::SharedMemoryType aType, Shmem* aOutMem);
     bool AllocUnsafeShmem(size_t aSize, Shmem::SharedMemory::SharedMemoryType aType, Shmem* aOutMem);
     bool DeallocShmem(Shmem& aMem);
 
     // Sets an event target to which all messages for aActor will be
     // dispatched. This method must be called before right before the SendPFoo
     // message for aActor is sent. And SendPFoo *must* be called if
@@ -196,43 +318,49 @@ public:
     void SetEventTargetForActor(IProtocol* aActor, nsIEventTarget* aEventTarget);
 
     // Replace the event target for the messages of aActor. There must not be
     // any messages of aActor in the task queue, or we might run into some
     // unexpected behavior.
     void ReplaceEventTargetForActor(IProtocol* aActor,
                                     nsIEventTarget* aEventTarget);
 
-    // Returns the event target set by SetEventTargetForActor() if available.
-    virtual nsIEventTarget* GetActorEventTarget();
+    nsIEventTarget* GetActorEventTarget();
+    already_AddRefed<nsIEventTarget> GetActorEventTarget(IProtocol* aActor);
 
 protected:
+    IProtocol(Side aSide, UniquePtr<ProtocolState> aState)
+        : mId(0)
+        , mSide(aSide)
+        , mManager(nullptr)
+        , mState(Move(aState))
+    {}
+
     friend class IToplevelProtocol;
 
     void SetId(int32_t aId) { mId = aId; }
     void ResetManager() { mManager = nullptr; }
+    // We have separate functions because the accessibility code manually
+    // calls SetManager.
     void SetManager(IProtocol* aManager);
-    void SetIPCChannel(MessageChannel* aChannel) { mChannel = aChannel; }
 
-    virtual void SetEventTargetForActorInternal(IProtocol* aActor, nsIEventTarget* aEventTarget);
-    virtual void ReplaceEventTargetForActorInternal(
-      IProtocol* aActor,
-      nsIEventTarget* aEventTarget);
-
-    virtual already_AddRefed<nsIEventTarget>
-    GetActorEventTargetInternal(IProtocol* aActor);
+    // Sets the manager for the protocol and registers the protocol with
+    // its manager, setting up channels for the protocol as well.  Not
+    // for use outside of IPDL.
+    void SetManagerAndRegister(IProtocol* aManager);
+    void SetManagerAndRegister(IProtocol* aManager, int32_t aId);
 
     static const int32_t kNullActorId = 0;
     static const int32_t kFreedActorId = 1;
 
 private:
     int32_t mId;
     Side mSide;
     IProtocol* mManager;
-    MessageChannel* mChannel;
+    UniquePtr<ProtocolState> mState;
 };
 
 typedef IPCMessageStart ProtocolId;
 
 #define IPC_OK() mozilla::ipc::IPCResult::Ok()
 #define IPC_FAIL(actor, why) mozilla::ipc::IPCResult::Fail(WrapNotNull(actor), __func__, (why))
 #define IPC_FAIL_NO_REASON(actor) mozilla::ipc::IPCResult::Fail(WrapNotNull(actor), __func__)
 
@@ -261,27 +389,73 @@ class Endpoint;
  * IToplevelProtocol tracks all top-level protocol actors created from
  * this protocol actor.
  */
 class IToplevelProtocol : public IProtocol
 {
     template<class PFooSide> friend class Endpoint;
 
 protected:
-    explicit IToplevelProtocol(ProtocolId aProtoId, Side aSide);
+    explicit IToplevelProtocol(const char* aName, ProtocolId aProtoId,
+                               Side aSide);
     ~IToplevelProtocol();
 
 public:
     enum ProcessIdState {
         eUnstarted,
         ePending,
         eReady,
         eError
     };
 
+    class ToplevelState final : public ProtocolState
+    {
+    public:
+        ToplevelState(const char* aName, IToplevelProtocol* aProtocol, Side aSide);
+
+        Shmem::SharedMemory* CreateSharedMemory(
+            size_t, SharedMemory::SharedMemoryType, bool, int32_t*) override;
+        Shmem::SharedMemory* LookupSharedMemory(int32_t) override;
+        bool IsTrackingSharedMemory(Shmem::SharedMemory*) override;
+        bool DestroySharedMemory(Shmem&) override;
+
+        void DeallocShmems();
+
+        bool ShmemCreated(const Message& aMsg);
+        bool ShmemDestroyed(const Message& aMsg);
+
+        int32_t Register(IProtocol*) override;
+        int32_t RegisterID(IProtocol*, int32_t) override;
+        IProtocol* Lookup(int32_t) override;
+        void Unregister(int32_t) override;
+
+        nsIEventTarget* GetActorEventTarget() override;
+        void SetEventTargetForActor(IProtocol* aActor, nsIEventTarget* aEventTarget) override;
+        void ReplaceEventTargetForActor(IProtocol* aActor, nsIEventTarget* aEventTarget) override;
+        already_AddRefed<nsIEventTarget> GetActorEventTarget(IProtocol* aActor) override;
+
+        virtual already_AddRefed<nsIEventTarget>
+        GetMessageEventTarget(const Message& aMsg);
+
+        const MessageChannel* GetIPCChannel() const override;
+        MessageChannel* GetIPCChannel() override;
+
+    private:
+        IToplevelProtocol* const mProtocol;
+        IDMap<IProtocol*> mActorMap;
+        int32_t mLastRouteId;
+        IDMap<Shmem::SharedMemory*> mShmemMap;
+        Shmem::id_t mLastShmemId;
+
+        Mutex mEventTargetMutex;
+        IDMap<nsCOMPtr<nsIEventTarget>> mEventTargetMap;
+
+        MessageChannel mChannel;
+    };
+
     using SchedulerGroupSet = nsILabelableRunnable::SchedulerGroupSet;
 
     void SetTransport(UniquePtr<Transport> aTrans)
     {
         mTrans = Move(aTrans);
     }
 
     Transport* GetTransport() const { return mTrans.get(); }
@@ -315,31 +489,20 @@ public:
     bool OpenWithAsyncPid(mozilla::ipc::Transport* aTransport,
                           MessageLoop* aThread = nullptr,
                           mozilla::ipc::Side aSide = mozilla::ipc::UnknownSide);
 
     void Close();
 
     void SetReplyTimeoutMs(int32_t aTimeoutMs);
 
-    virtual int32_t Register(IProtocol*) override;
-    virtual int32_t RegisterID(IProtocol*, int32_t) override;
-    virtual IProtocol* Lookup(int32_t) override;
-    virtual void Unregister(int32_t) override;
+    void DeallocShmems() { DowncastState()->DeallocShmems(); }
 
-    virtual Shmem::SharedMemory* CreateSharedMemory(
-        size_t, SharedMemory::SharedMemoryType, bool, int32_t*) override;
-    virtual Shmem::SharedMemory* LookupSharedMemory(int32_t) override;
-    virtual bool IsTrackingSharedMemory(Shmem::SharedMemory*) override;
-    virtual bool DestroySharedMemory(Shmem&) override;
-
-    void DeallocShmems();
-
-    bool ShmemCreated(const Message& aMsg);
-    bool ShmemDestroyed(const Message& aMsg);
+    bool ShmemCreated(const Message& aMsg) { return DowncastState()->ShmemCreated(aMsg); }
+    bool ShmemDestroyed(const Message& aMsg) { return DowncastState()->ShmemDestroyed(aMsg); }
 
     virtual bool ShouldContinueFromReplyTimeout() {
         return false;
     }
 
     // WARNING: This function is called with the MessageChannel monitor held.
     virtual void IntentionalCrash() {
         MOZ_CRASH("Intentional IPDL crash");
@@ -404,68 +567,59 @@ public:
     // when it's difficult to determine an EventTarget ahead of time. See the
     // comment in nsILabelableRunnable.h for more information.
     virtual bool
     GetMessageSchedulerGroups(const Message& aMsg, SchedulerGroupSet& aGroups)
     {
         return false;
     }
 
-    virtual already_AddRefed<nsIEventTarget>
-    GetMessageEventTarget(const Message& aMsg);
-
-    already_AddRefed<nsIEventTarget>
-    GetActorEventTarget(IProtocol* aActor);
-
-    virtual nsIEventTarget*
-    GetActorEventTarget() override;
-
+    // This method is only used for collecting telemetry bits in various places,
+    // and we shouldn't pay the overhead of having it in protocol vtables when
+    // it's not being used.
+#ifdef EARLY_BETA_OR_EARLIER
     virtual void OnChannelReceivedMessage(const Message& aMsg) {}
+#endif
 
     bool IsMainThreadProtocol() const { return mIsMainThreadProtocol; }
     void SetIsMainThreadProtocol() { mIsMainThreadProtocol = NS_IsMainThread(); }
 
+    already_AddRefed<nsIEventTarget>
+    GetMessageEventTarget(const Message& aMsg)
+    {
+        return DowncastState()->GetMessageEventTarget(aMsg);
+    }
+
 protected:
+    ToplevelState* DowncastState() const
+    {
+        return static_cast<ToplevelState*>(mState.get());
+    }
+
     // Override this method in top-level protocols to change the event target
     // for a new actor (and its sub-actors).
     virtual already_AddRefed<nsIEventTarget>
     GetConstructedEventTarget(const Message& aMsg) { return nullptr; }
 
     // Override this method in top-level protocols to change the event target
     // for specific messages.
     virtual already_AddRefed<nsIEventTarget>
     GetSpecificMessageEventTarget(const Message& aMsg) { return nullptr; }
 
-    virtual void SetEventTargetForActorInternal(IProtocol* aActor,
-                                                nsIEventTarget* aEventTarget) override;
-    virtual void ReplaceEventTargetForActorInternal(
-      IProtocol* aActor,
-      nsIEventTarget* aEventTarget) override;
-
-    virtual already_AddRefed<nsIEventTarget>
-    GetActorEventTargetInternal(IProtocol* aActor) override;
-
     // This monitor protects mOtherPid and mOtherPidState. All other fields
     // should only be accessed on the worker thread.
     mutable mozilla::Monitor mMonitor;
   private:
     base::ProcessId OtherPidMaybeInvalid() const;
 
     ProtocolId mProtocolId;
     UniquePtr<Transport> mTrans;
     base::ProcessId mOtherPid;
     ProcessIdState mOtherPidState;
-    IDMap<IProtocol*> mActorMap;
-    int32_t mLastRouteId;
-    IDMap<Shmem::SharedMemory*> mShmemMap;
-    Shmem::id_t mLastShmemId;
     bool mIsMainThreadProtocol;
-
-    Mutex mEventTargetMutex;
-    IDMap<nsCOMPtr<nsIEventTarget>> mEventTargetMap;
 };
 
 class IShmemAllocator
 {
 public:
   virtual bool AllocShmem(size_t aSize,
                           mozilla::ipc::SharedMemory::SharedMemoryType aShmType,
                           mozilla::ipc::Shmem* aShmem) = 0;
@@ -524,17 +678,17 @@ LogMessageForProtocol(const char* aTopLe
 
 MOZ_NEVER_INLINE void
 ProtocolErrorBreakpoint(const char* aMsg);
 
 // The code generator calls this function for errors which come from the
 // methods of protocols.  Doing this saves codesize by making the error
 // cases significantly smaller.
 MOZ_NEVER_INLINE void
-FatalError(const char* aProtocolName, const char* aMsg, bool aIsParent);
+FatalError(const char* aMsg, bool aIsParent);
 
 // The code generator calls this function for errors which are not
 // protocol-specific: errors in generated struct methods or errors in
 // transition functions, for instance.  Doing this saves codesize by
 // by making the error cases significantly smaller.
 MOZ_NEVER_INLINE void
 LogicError(const char* aMsg);
 
--- a/ipc/ipdl/ipdl/lower.py
+++ b/ipc/ipdl/ipdl/lower.py
@@ -3235,23 +3235,20 @@ class _GenerateProtocolActorCode(ipdl.as
             + [ Whitespace.NL ]
         ))
 
         self.cls.addstmt(Label.PUBLIC)
         # Actor()
         ctor = ConstructorDefn(ConstructorDecl(self.clsname))
         side = ExprVar('mozilla::ipc::' + self.side.title() + 'Side')
         if ptype.isToplevel():
+            name = ExprLiteral.String(_actorName(p.name, self.side))
             ctor.memberinits = [
                 ExprMemberInit(ExprVar('mozilla::ipc::IToplevelProtocol'),
-                               [_protocolId(ptype), side]),
-                ExprMemberInit(p.channelVar(), [
-                    ExprLiteral.String(_actorName(p.name, self.side)),
-                    ExprCall(ExprVar('ALLOW_THIS_IN_INITIALIZER_LIST'),
-                             [ ExprVar.THIS ]) ]),
+                               [name, _protocolId(ptype), side]),
                 ExprMemberInit(p.stateVar(),
                                [ p.startState() ])
             ]
         else:
             ctor.memberinits = [
                 ExprMemberInit(ExprVar('mozilla::ipc::IProtocol'), [side]),
                 ExprMemberInit(p.stateVar(),
                                [ p.deadState() ])
@@ -3479,38 +3476,31 @@ class _GenerateProtocolActorCode(ipdl.as
         if (ptype.isToplevel() and ptype.isInterrupt()):
 
             processnative = MethodDefn(
                 MethodDecl('ProcessNativeEventsInInterruptCall', ret=Type.VOID))
 
             processnative.addstmts([
                     CppDirective('ifdef', 'OS_WIN'),
                     StmtExpr(ExprCall(
-                            ExprSelect(p.channelVar(), '.',
-                                       'ProcessNativeEventsInInterruptCall'))),
+                        ExprSelect(ExprCall(ExprSelect(ExprCall(ExprVar('DowncastState')),
+                                                       '->',
+                                                       'GetIPCChannel')),
+                                   '->',
+                                   'ProcessNativeEventsInInterruptCall'))),
                     CppDirective('else'),
                     _fatalError('This method is Windows-only'),
                     CppDirective('endif'),
                     ])
 
             self.cls.addstmts([ processnative, Whitespace.NL ])
 
         ## private methods
         self.cls.addstmt(Label.PRIVATE)
 
-        ## ProtocolName()
-        actorname = _actorName(p.name, self.side)
-        protocolname = MethodDefn(MethodDecl(
-            'ProtocolName', params=[],
-            const=1, methodspec=MethodSpec.OVERRIDE, ret=Type('char', const=1, ptr=1)))
-        protocolname.addstmts([
-            StmtReturn(ExprLiteral.String(actorname))
-        ])
-        self.cls.addstmts([ protocolname, Whitespace.NL ])
-
         ## DestroySubtree(bool normal)
         whyvar = ExprVar('why')
         subtreewhyvar = ExprVar('subtreewhy')
         kidsvar = ExprVar('kids')
         ivar = ExprVar('i')
         itervar = ExprVar('iter')
         ithkid = ExprIndex(kidsvar, ivar)
 
@@ -3624,19 +3614,16 @@ class _GenerateProtocolActorCode(ipdl.as
         # we're toplevel
         self.cls.addstmts([ deallocsubtree, Whitespace.NL ])
 
         if ptype.isToplevel():
             deallocself = MethodDefn(MethodDecl(deallocselfvar.name, methodspec=MethodSpec.VIRTUAL))
             self.cls.addstmts([ deallocself, Whitespace.NL ])
 
         ## private members
-        if ptype.isToplevel():
-            self.cls.addstmt(StmtDecl(Decl(p.channelType(), 'mChannel')))
-
         self.cls.addstmt(StmtDecl(Decl(Type('State'), p.stateVar().name)))
 
         for managed in ptype.manages:
             self.cls.addstmts([
                 StmtDecl(Decl(
                     p.managedVarType(managed, self.side),
                     p.managedVar(managed, self.side).name)) ])
 
@@ -3653,32 +3640,16 @@ class _GenerateProtocolActorCode(ipdl.as
         sourcevar = ExprVar('aSource')
         ivar = ExprVar('i')
         kidsvar = ExprVar('kids')
         ithkid = ExprIndex(kidsvar, ivar)
 
         methods = []
 
         if p.decl.type.isToplevel():
-            getchannel = MethodDefn(MethodDecl(
-                p.getChannelMethod().name,
-                ret=Type('MessageChannel', ptr=1),
-                methodspec=MethodSpec.OVERRIDE))
-            getchannel.addstmt(StmtReturn(ExprAddrOf(p.channelVar())))
-
-            getchannelconst = MethodDefn(MethodDecl(
-                p.getChannelMethod().name,
-                ret=Type('MessageChannel', ptr=1, const=1),
-                methodspec=MethodSpec.OVERRIDE, const=1))
-            getchannelconst.addstmt(StmtReturn(ExprAddrOf(p.channelVar())))
-
-            methods += [ getchannel,
-                         getchannelconst ]
-
-        if p.decl.type.isToplevel():
             tmpvar = ExprVar('tmp')
 
             # "private" message that passes shmem mappings from one process
             # to the other
             if p.subtreeUsesShmem():
                 self.asyncSwitch.addcase(
                     CaseLabel('SHMEM_CREATED_MESSAGE_TYPE'),
                     self.genShmemCreatedHandler())
@@ -3912,29 +3883,25 @@ class _GenerateProtocolActorCode(ipdl.as
 
     def ctorPrologue(self, md, errfn=ExprLiteral.NULL, idexpr=None):
         actordecl = md.actorDecl()
         actorvar = actordecl.var()
         actorproto = actordecl.ipdltype.protocol
         actortype = ipdl.type.ActorType(actorproto)
 
         if idexpr is None:
-            registerexpr = ExprCall(self.protocol.registerMethod(),
-                                    args=[ actorvar ])
+            setManagerArgs = [ExprVar.THIS]
         else:
-            registerexpr = ExprCall(self.protocol.registerIDMethod(),
-                                    args=[ actorvar, idexpr ])
+            setManagerArgs = [ExprVar.THIS, idexpr]
+        setmanager = ExprCall(ExprSelect(actorvar, '->', 'SetManagerAndRegister'),
+                              args=setManagerArgs)
 
         return [
             self.failIfNullActor(actorvar, errfn, msg="Error constructing actor %s" % actortype.name() + self.side.capitalize()),
-            # set manager in prior to register to inherit EventTarget from manager.
-            StmtExpr(ExprCall(ExprSelect(actorvar, '->', 'SetManager'), args=[ExprVar.THIS])),
-            StmtExpr(registerexpr),
-            StmtExpr(ExprCall(ExprSelect(actorvar, '->', 'SetIPCChannel'),
-                              args=[self.protocol.callGetChannel()])),
+            StmtExpr(setmanager),
             StmtExpr(_callInsertManagedActor(
                 self.protocol.managedVar(md.decl.type.constructedType(),
                                          self.side),
                 actorvar)),
             StmtExpr(ExprAssn(_actorState(actorvar),
                               _startState(actorproto, fq=1)))
         ]
 
--- a/ipc/ipdl/test/cxx/TestActorPunning.cpp
+++ b/ipc/ipdl/test/cxx/TestActorPunning.cpp
@@ -24,22 +24,18 @@ TestActorPunningParent::RecvPun(PTestAct
     fail("shouldn't have received this message in the first place");
     return IPC_OK();
 }
 
 // By default, fatal errors kill the parent process, but this makes it
 // hard to test, so instead we use the previous behavior and kill the
 // child process.
 void
-TestActorPunningParent::HandleFatalError(const char* aProtocolName, const char* aErrorMsg) const
+TestActorPunningParent::HandleFatalError(const char* aErrorMsg) const
 {
-  if (!!strcmp(aProtocolName, "PTestActorPunningParent")) {
-    fail("wrong protocol hit a fatal error");
-  }
-
   if (!!strcmp(aErrorMsg, "Error deserializing 'PTestActorPunningSubParent'")) {
     fail("wrong fatal error");
   }
 
   ipc::ScopedProcessHandle otherProcessHandle;
   if (!base::OpenProcessHandle(OtherPid(), &otherProcessHandle.rwget())) {
     fail("couldn't open child process");
   } else {
--- a/ipc/ipdl/test/cxx/TestActorPunning.h
+++ b/ipc/ipdl/test/cxx/TestActorPunning.h
@@ -35,17 +35,17 @@ protected:
     virtual void ActorDestroy(ActorDestroyReason why) override
     {
         if (NormalShutdown == why)
             fail("should have died from error!");
         passed("ok");
         QuitParent();
     }
 
-    virtual void HandleFatalError(const char* aProtocolName, const char* aErrorMsg) const override;
+    virtual void HandleFatalError(const char* aErrorMsg) const override;
 };
 
 class TestActorPunningPunnedParent :
     public PTestActorPunningPunnedParent
 {
 public:
     TestActorPunningPunnedParent() {}
     virtual ~TestActorPunningPunnedParent() {}
--- a/ipc/ipdl/test/cxx/TestBadActor.cpp
+++ b/ipc/ipdl/test/cxx/TestBadActor.cpp
@@ -18,22 +18,18 @@ TestBadActorParent::Main()
 
   Unused << child->Call__delete__(child);
 }
 
 // By default, fatal errors kill the parent process, but this makes it
 // hard to test, so instead we use the previous behavior and kill the
 // child process.
 void
-TestBadActorParent::HandleFatalError(const char* aProtocolName, const char* aErrorMsg) const
+TestBadActorParent::HandleFatalError(const char* aErrorMsg) const
 {
-  if (!!strcmp(aProtocolName, "PTestBadActorSubParent")) {
-    fail("wrong protocol hit a fatal error");
-  }
-
   if (!!strcmp(aErrorMsg, "incoming message racing with actor deletion")) {
     fail("wrong fatal error");
   }
 
   ipc::ScopedProcessHandle otherProcessHandle;
   if (!base::OpenProcessHandle(OtherPid(), &otherProcessHandle.rwget())) {
     fail("couldn't open child process");
   } else {
--- a/ipc/ipdl/test/cxx/TestBadActor.h
+++ b/ipc/ipdl/test/cxx/TestBadActor.h
@@ -28,17 +28,17 @@ protected:
   virtual void ActorDestroy(ActorDestroyReason why) override
   {
     if (AbnormalShutdown != why)
       fail("unexpected destruction");
     passed("ok");
     QuitParent();
   }
 
-  virtual void HandleFatalError(const char* aProtocolName, const char* aErrorMsg) const override;
+  virtual void HandleFatalError(const char* aErrorMsg) const override;
 
   virtual PTestBadActorSubParent*
   AllocPTestBadActorSubParent() override;
 
   virtual bool
   DeallocPTestBadActorSubParent(PTestBadActorSubParent* actor) override {
     delete actor;
     return true;
--- a/js/public/MemoryMetrics.h
+++ b/js/public/MemoryMetrics.h
@@ -499,16 +499,59 @@ struct NotableScriptSourceInfo : public 
     }
 
     char* filename_;
 
   private:
     NotableScriptSourceInfo(const NotableScriptSourceInfo& info) = delete;
 };
 
+struct HelperThreadStats
+{
+#define FOR_EACH_SIZE(macro) \
+    macro(_, MallocHeap, stateData) \
+    macro(_, MallocHeap, parseTask) \
+    macro(_, MallocHeap, ionBuilder)
+
+    explicit HelperThreadStats()
+      : FOR_EACH_SIZE(ZERO_SIZE)
+        idleThreadCount(0),
+        activeThreadCount(0)
+    { }
+
+    FOR_EACH_SIZE(DECL_SIZE)
+
+    unsigned idleThreadCount;
+    unsigned activeThreadCount;
+
+#undef FOR_EACH_SIZE
+};
+
+/**
+ * Measurements that not associated with any individual runtime.
+ */
+struct GlobalStats
+{
+#define FOR_EACH_SIZE(macro) \
+    macro(_, MallocHeap, tracelogger)
+
+    explicit GlobalStats(mozilla::MallocSizeOf mallocSizeOf)
+      : FOR_EACH_SIZE(ZERO_SIZE)
+        mallocSizeOf_(mallocSizeOf)
+    { }
+
+    FOR_EACH_SIZE(DECL_SIZE)
+
+    HelperThreadStats helperThread;
+
+    mozilla::MallocSizeOf mallocSizeOf_;
+
+#undef FOR_EACH_SIZE
+};
+
 /**
  * These measurements relate directly to the JSRuntime, and not to zones,
  * compartments, and realms within it.
  */
 struct RuntimeSizes
 {
 #define FOR_EACH_SIZE(macro) \
     macro(_, MallocHeap, object) \
@@ -518,17 +561,18 @@ struct RuntimeSizes
     macro(_, MallocHeap, temporary) \
     macro(_, MallocHeap, interpreterStack) \
     macro(_, MallocHeap, mathCache) \
     macro(_, MallocHeap, sharedImmutableStringsCache) \
     macro(_, MallocHeap, sharedIntlData) \
     macro(_, MallocHeap, uncompressedSourceCache) \
     macro(_, MallocHeap, scriptData) \
     macro(_, MallocHeap, tracelogger) \
-    macro(_, MallocHeap, wasmRuntime)
+    macro(_, MallocHeap, wasmRuntime) \
+    macro(_, MallocHeap, jitLazyLink)
 
     RuntimeSizes()
       : FOR_EACH_SIZE(ZERO_SIZE)
         scriptSourceInfo(),
         code(),
         gc(),
         notableScriptSources()
     {
@@ -949,16 +993,19 @@ class ObjectPrivateVisitor
     GetISupportsFun getISupports_;
 
     explicit ObjectPrivateVisitor(GetISupportsFun getISupports)
       : getISupports_(getISupports)
     {}
 };
 
 extern JS_PUBLIC_API(bool)
+CollectGlobalStats(GlobalStats* gStats);
+
+extern JS_PUBLIC_API(bool)
 CollectRuntimeStats(JSContext* cx, RuntimeStats* rtStats, ObjectPrivateVisitor* opv, bool anonymize);
 
 extern JS_PUBLIC_API(size_t)
 SystemCompartmentCount(JSContext* cx);
 
 extern JS_PUBLIC_API(size_t)
 UserCompartmentCount(JSContext* cx);
 
@@ -968,19 +1015,16 @@ PeakSizeOfTemporary(const JSContext* cx)
 extern JS_PUBLIC_API(bool)
 AddSizeOfTab(JSContext* cx, JS::HandleObject obj, mozilla::MallocSizeOf mallocSizeOf,
              ObjectPrivateVisitor* opv, TabSizes* sizes);
 
 extern JS_PUBLIC_API(bool)
 AddServoSizeOf(JSContext* cx, mozilla::MallocSizeOf mallocSizeOf,
                ObjectPrivateVisitor* opv, ServoSizes* sizes);
 
-extern JS_PUBLIC_API(void)
-CollectTraceLoggerStateStats(RuntimeStats* rtStats);
-
 } // namespace JS
 
 #undef DECL_SIZE
 #undef ZERO_SIZE
 #undef COPY_OTHER_SIZE
 #undef ADD_OTHER_SIZE
 #undef SUB_OTHER_SIZE
 #undef ADD_SIZE_TO_N
--- a/js/src/ds/Fifo.h
+++ b/js/src/ds/Fifo.h
@@ -80,16 +80,51 @@ class Fifo
         return front_.length() + rear_.length();
     }
 
     bool empty() const {
         MOZ_ASSERT_IF(rear_.length() > 0, front_.length() > 0); // Invariant 4.
         return front_.empty();
     }
 
+    // Iterator from oldest to yongest element.
+    struct ConstIterator
+    {
+        const Fifo& self_;
+        size_t idx_;
+
+        ConstIterator(const Fifo& self, size_t idx)
+            : self_(self), idx_(idx)
+        { }
+
+        ConstIterator& operator++() {
+            ++idx_;
+            return *this;
+        }
+
+        const T& operator*() const {
+            // Iterate front in reverse, then rear.
+            size_t split = self_.front_.length();
+            return (idx_ < split) ? self_.front_[(split - 1) - idx_]
+                                  : self_.rear_[idx_ - split];
+        }
+
+        bool operator!=(const ConstIterator& other) const {
+            return (&self_ != &other.self_) || (idx_ != other.idx_);
+        }
+    };
+
+    ConstIterator begin() const {
+        return ConstIterator(*this, 0);
+    }
+
+    ConstIterator end() const {
+        return ConstIterator(*this, length());
+    }
+
     // Push an element to the back of the queue. This method can take either a
     // |const T&| or a |T&&|.
     template <typename U>
     MOZ_MUST_USE bool pushBack(U&& u) {
         if (!rear_.append(mozilla::Forward<U>(u)))
             return false;
         fixup();
         return true;
@@ -137,13 +172,21 @@ class Fifo
     // Clear all elements for which the given predicate returns 'true'. Return
     // the number of elements removed.
     template <class Pred>
     size_t eraseIf(Pred pred) {
         size_t erased = EraseIf(front_, pred);
         erased += EraseIf(rear_, pred);
         return erased;
     }
+
+    size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
+        return front_.sizeOfExcludingThis(mallocSizeOf) +
+               rear_.sizeOfExcludingThis(mallocSizeOf);
+    }
+    size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
+        return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf);
+    }
 };
 
 } // namespace js
 
 #endif /* js_Fifo_h */
--- a/js/src/gc/Allocator.cpp
+++ b/js/src/gc/Allocator.cpp
@@ -607,16 +607,18 @@ GCRuntime::getOrAllocChunk(AutoLockGCBgA
         lock.tryToStartBackgroundAllocation();
 
     return chunk;
 }
 
 void
 GCRuntime::recycleChunk(Chunk* chunk, const AutoLockGC& lock)
 {
+    AlwaysPoison(&chunk->trailer, JS_FREED_CHUNK_PATTERN, sizeof(ChunkTrailer),
+                 MemCheckKind::MakeNoAccess);
     emptyChunks(lock).push(chunk);
 }
 
 Chunk*
 GCRuntime::pickChunk(AutoLockGCBgAlloc& lock)
 {