Merge inbound to mozilla-central. a=merge
authorCosmin Sabou <csabou@mozilla.com>
Fri, 20 Apr 2018 01:29:10 +0300
changeset 414591 5d73549d363f2a52854ff43fabac9c6fd05b90b0
parent 414524 5c983ad5a421edc1d56179d437c7397191ef47d5 (current diff)
parent 414590 5893581067a7fbcaf376783af27145a51207b84f (diff)
child 414592 ea3555cf12afff38370e7a697db81f181e15dbf6
child 414608 77aa2a89aa37ea92194f79518895febc737ce341
child 414704 afbc50ccf0d05fddfae15ae0e5f678a5c39b1d03
push id33871
push usercsabou@mozilla.com
push dateThu, 19 Apr 2018 22:30:08 +0000
treeherdermozilla-central@5d73549d363f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone61.0a1
first release with
nightly linux32
5d73549d363f / 61.0a1 / 20180419224145 / files
nightly linux64
5d73549d363f / 61.0a1 / 20180419224145 / files
nightly mac
5d73549d363f / 61.0a1 / 20180419224145 / files
nightly win32
5d73549d363f / 61.0a1 / 20180419224145 / files
nightly win64
5d73549d363f / 61.0a1 / 20180419224145 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
browser/components/preferences/in-content/tests/siteData/head.js
browser/modules/Feeds.jsm
dom/base/nsIDOMDOMRequest.idl
netwerk/cookie/nsCookieService.cpp
testing/mozharness/mozharness/mozilla/testing/verify_tools.py
testing/web-platform/meta/css/css-fonts/variations/font-weight-interpolation.html.ini
testing/web-platform/meta/css/css-fonts/variations/font-weight-lighter-bolder.html.ini
testing/web-platform/meta/css/css-fonts/variations/font-weight-parsing.html.ini
toolkit/modules/offlineAppCache.jsm
--- a/accessible/base/TextAttrs.cpp
+++ b/accessible/base/TextAttrs.cpp
@@ -650,19 +650,20 @@ TextAttrsMgr::FontWeightTextAttr::
     return FontWeight::Bold();
   }
 
   // On Windows, font->GetStyle()->weight will give the same weight as
   // fontEntry->Weight(), the weight of the first font in the font group,
   // which may not be the weight of the font face used to render the
   // characters. On Mac, font->GetStyle()->weight will just give the same
   // number as getComputedStyle(). fontEntry->Weight() will give the weight
-  // of the font face used.
+  // range supported by the font face used, so we clamp the weight that was
+  // requested by style to what is actually supported by the font.
   gfxFontEntry *fontEntry = font->GetFontEntry();
-  return fontEntry->Weight();
+  return fontEntry->Weight().Clamp(font->GetStyle()->weight);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // AutoGeneratedTextAttr
 ////////////////////////////////////////////////////////////////////////////////
 TextAttrsMgr::AutoGeneratedTextAttr::
   AutoGeneratedTextAttr(HyperTextAccessible* aHyperTextAcc,
                         Accessible* aAccessible) :
--- a/browser/app/blocklist.xml
+++ b/browser/app/blocklist.xml
@@ -1,10 +1,10 @@
 <?xml version='1.0' encoding='UTF-8'?>
-<blocklist lastupdate="1523286321447" xmlns="http://www.mozilla.org/2006/addons-blocklist">
+<blocklist lastupdate="1523897202689" 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"/>
@@ -2234,16 +2234,20 @@
     <emItem blockID="baf7f735-d6b6-410a-8cc8-25c60f7c57e2" id="adbeaver@adbeaver.org">
       <prefs/>
       <versionRange minVersion="0" maxVersion="*" severity="1"/>
     </emItem>
     <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>
   </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/components/preferences/in-content/tests/siteData/browser_siteData2.js
+++ b/browser/components/preferences/in-content/tests/siteData/browser_siteData2.js
@@ -205,24 +205,26 @@ add_task(async function() {
   assertSitesListed(doc, fakeHosts.slice(2));
 
   await mockSiteDataManager.unregister();
   BrowserTestUtils.removeTab(gBrowser.selectedTab);
 
   function removeSelectedSite(hosts) {
     frameDoc = win.gSubDialog._topDialog._frame.contentDocument;
     let removeBtn = frameDoc.getElementById("removeSelected");
+    is(removeBtn.disabled, true, "Should start with disabled removeSelected button");
     let sitesList = frameDoc.getElementById("sitesList");
     hosts.forEach(host => {
       let site = sitesList.querySelector(`richlistitem[host="${host}"]`);
       if (site) {
         site.click();
+        let currentSelectedIndex = sitesList.selectedIndex;
         is(removeBtn.disabled, false, "Should enable the removeSelected button");
         removeBtn.doCommand();
-        is(removeBtn.disabled, true, "Should disable the removeSelected button");
+        is(sitesList.selectedIndex, currentSelectedIndex);
       } else {
         ok(false, `Should not select and remove inexistent site of ${host}`);
       }
     });
   }
 });
 
 // Test searching and then removing only visible sites
--- a/browser/components/preferences/in-content/tests/siteData/browser_siteData_multi_select.js
+++ b/browser/components/preferences/in-content/tests/siteData/browser_siteData_multi_select.js
@@ -45,34 +45,34 @@ add_task(async function() {
   await openPreferencesViaOpenPreferencesAPI("privacy", { leaveOpen: true });
   await updatePromise;
   await openSiteDataSettingsDialog();
 
   let doc = gBrowser.selectedBrowser.contentDocument;
 
   // Test the initial state
   assertSitesListed(doc, fakeHosts);
+  let win = gBrowser.selectedBrowser.contentWindow;
+  let frameDoc = win.gSubDialog._topDialog._frame.contentDocument;
+  let removeBtn = frameDoc.getElementById("removeSelected");
+  is(removeBtn.disabled, true, "Should start with disabled removeSelected button");
 
   let removeDialogOpenPromise = BrowserTestUtils.promiseAlertDialogOpen("accept", REMOVE_DIALOG_URL);
   let settingsDialogClosePromise = promiseSettingsDialogClose();
 
-  let win = gBrowser.selectedBrowser.contentWindow;
-  let frameDoc = win.gSubDialog._topDialog._frame.contentDocument;
-
   // Select some sites to remove.
   let sitesList = frameDoc.getElementById("sitesList");
   fakeHosts.slice(0, 2).forEach(host => {
     let site = sitesList.querySelector(`richlistitem[host="${host}"]`);
     sitesList.addItemToSelection(site);
   });
 
-  let removeBtn = frameDoc.getElementById("removeSelected");
   is(removeBtn.disabled, false, "Should enable the removeSelected button");
   removeBtn.doCommand();
-  is(removeBtn.disabled, true, "Should disable the removeSelected button");
+  is(sitesList.selectedIndex, 0, "Should select next item");
 
   let saveBtn = frameDoc.getElementById("save");
   assertSitesListed(doc, fakeHosts.slice(2));
   saveBtn.doCommand();
 
   await removeDialogOpenPromise;
   await settingsDialogClosePromise;
   await openSiteDataSettingsDialog();
--- a/browser/components/preferences/in-content/tests/siteData/head.js
+++ b/browser/components/preferences/in-content/tests/siteData/head.js
@@ -126,26 +126,24 @@ function promiseSettingsDialogClose() {
         resolve();
       }
     }, { once: true });
   });
 }
 
 function assertSitesListed(doc, hosts) {
   let frameDoc = content.gSubDialog._topDialog._frame.contentDocument;
-  let removeBtn = frameDoc.getElementById("removeSelected");
   let removeAllBtn = frameDoc.getElementById("removeAll");
   let sitesList = frameDoc.getElementById("sitesList");
   let totalSitesNumber = sitesList.getElementsByTagName("richlistitem").length;
   is(totalSitesNumber, hosts.length, "Should list the right sites number");
   hosts.forEach(host => {
     let site = sitesList.querySelector(`richlistitem[host="${host}"]`);
     ok(site, `Should list the site of ${host}`);
   });
-  is(removeBtn.disabled, true, "Should disable the removeSelected button");
   is(removeAllBtn.disabled, false, "Should enable the removeAllBtn button");
 }
 
 const mockSiteDataManager = {
 
   _SiteDataManager: null,
   _originalQMS: null,
   _originalRemoveQuotaUsage: null,
--- a/browser/components/preferences/siteDataSettings.js
+++ b/browser/components/preferences/siteDataSettings.js
@@ -254,18 +254,29 @@ let gSiteDataSettings = {
   },
 
   onCommandSearch() {
     this._buildSitesList(this._sites);
     this._list.clearSelection();
   },
 
   onClickRemoveSelected() {
+    let lastIndex = this._list.selectedItems.length - 1;
+    let lastSelectedItem = this._list.selectedItems[lastIndex];
+    let lastSelectedItemPosition = this._list.getIndexOfItem(lastSelectedItem);
+    let nextSelectedItem = this._list.getItemAtIndex(lastSelectedItemPosition + 1);
+
     this._removeSiteItems(this._list.selectedItems);
     this._list.clearSelection();
+
+    if (nextSelectedItem) {
+      this._list.selectedItem = nextSelectedItem;
+    } else {
+      this._list.selectedIndex = this._list.itemCount - 1;
+    }
   },
 
   onClickRemoveAll() {
     let siteItems = this._list.getElementsByTagName("richlistitem");
     if (siteItems.length > 0) {
       this._removeSiteItems(siteItems);
     }
   },
--- a/browser/modules/Feeds.jsm
+++ b/browser/modules/Feeds.jsm
@@ -69,17 +69,17 @@ var Feeds = {
     var type = aLink.type.toLowerCase().replace(/^\s+|\s*(?:;.*)?$/g, "");
     if (!aIsFeed) {
       aIsFeed = (type == "application/rss+xml" ||
                  type == "application/atom+xml");
     }
 
     if (aIsFeed) {
       try {
-        let href = BrowserUtils.makeURI(aLink.href, aLink.ownerDocument.characterSet);
+        let href = Services.io.newURI(aLink.href, aLink.ownerDocument.characterSet);
         BrowserUtils.urlSecurityCheck(href, aPrincipal,
                                       Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL);
         return type || "application/rss+xml";
       } catch (ex) {
       }
     }
 
     return null;
--- a/config/check_macroassembler_style.py
+++ b/config/check_macroassembler_style.py
@@ -22,19 +22,16 @@
 
 from __future__ import print_function
 
 import difflib
 import os
 import re
 import sys
 
-from mozversioncontrol import get_repository_from_env
-
-
 architecture_independent = set(['generic'])
 all_unsupported_architectures_names = set(['mips32', 'mips64', 'mips_shared'])
 all_architecture_names = set(['x86', 'x64', 'arm', 'arm64'])
 all_shared_architecture_names = set(['x86_shared', 'arm', 'arm64'])
 
 reBeforeArg = "(?<=[(,\s])"
 reArgType = "(?P<type>[\w\s:*&]+)"
 reArgName = "(?P<name>\s\w+)"
@@ -260,29 +257,30 @@ def generate_file_content(signatures):
 
 def check_style():
     # We read from the header file the signature of each function.
     decls = dict()      # type: dict(signature => ['x86', 'x64'])
 
     # We infer from each file the signature of each MacroAssembler function.
     defs = dict()       # type: dict(signature => ['x86', 'x64'])
 
-    with get_repository_from_env() as repo:
-        # Select the appropriate files.
-        for filename in repo.get_files_in_working_directory():
-            if not filename.startswith('js/src/jit/'):
-                continue
+    root_dir = os.path.join('js', 'src', 'jit')
+    for dirpath, dirnames, filenames in os.walk(root_dir):
+        for filename in filenames:
             if 'MacroAssembler' not in filename:
                 continue
 
-            filename = os.path.join(repo.path, filename)
+            filepath = os.path.join(dirpath, filename).replace('\\', '/')
 
-            if filename.endswith('MacroAssembler.h'):
-                decls = append_signatures(decls, get_macroassembler_declaration(filename))
-            defs = append_signatures(defs, get_macroassembler_definitions(filename))
+            if filepath.endswith('MacroAssembler.h'):
+                decls = append_signatures(decls, get_macroassembler_declaration(filepath))
+            defs = append_signatures(defs, get_macroassembler_definitions(filepath))
+
+    if not decls or not defs:
+        raise Exception("Did not find any definitions or declarations")
 
     # Compare declarations and definitions output.
     difflines = difflib.unified_diff(generate_file_content(decls),
                                      generate_file_content(defs),
                                      fromfile='check_macroassembler_style.py declared syntax',
                                      tofile='check_macroassembler_style.py found definitions')
     ok = True
     for diffline in difflines:
--- a/devtools/client/inspector/test/browser_inspector_highlighter-cssshape_04.js
+++ b/devtools/client/inspector/test/browser_inspector_highlighter-cssshape_04.js
@@ -179,36 +179,37 @@ async function testPolygonRemovePoint(co
   await teardown({selector, property, ...config});
 }
 
 async function testCircleMoveCenter(config) {
   const {inspector, highlighters, testActor, helper} = config;
   const selector = "#circle";
   const property = "clip-path";
 
+  let onShapeChangeApplied = highlighters.once("shapes-highlighter-changes-applied");
   await setup({selector, property, ...config});
 
   let cx = parseFloat(await testActor.getHighlighterNodeAttribute(
     "shapes-ellipse", "cx", highlighters.highlighters[HIGHLIGHTER_TYPE]));
   let cy = parseFloat(await testActor.getHighlighterNodeAttribute(
     "shapes-ellipse", "cy", highlighters.highlighters[HIGHLIGHTER_TYPE]));
   let quads = await testActor.getAllAdjustedQuads(selector);
   let { width, height } = quads.border[0].bounds;
   let cxPixel = width * cx / 100;
   let cyPixel = height * cy / 100;
   let dx = width / 10;
   let dy = height / 10;
 
-  let onShapeChangeApplied = highlighters.once("shapes-highlighter-changes-applied");
   info("Moving circle center");
   let { mouse } = helper;
   await mouse.down(cxPixel, cyPixel, selector);
   await mouse.move(cxPixel + dx, cyPixel + dy, selector);
   await mouse.up(cxPixel + dx, cyPixel + dy, selector);
   await testActor.reflow();
+  info("Waiting for shape changes to apply");
   await onShapeChangeApplied;
 
   let definition = await getComputedPropertyValue(selector, property, inspector);
   ok(definition.includes(`at ${cx + 10}% ${cy + 10}%`),
      "Circle center successfully moved");
 
   await teardown({selector, property, ...config});
 }
@@ -287,35 +288,41 @@ async function testInsetMoveEdges(config
   let bottom = top + elemHeight * height / 100;
   let xCenter = (left + right) / 2;
   let yCenter = (top + bottom) / 2;
   let dx = elemWidth / 10;
   let dy = elemHeight / 10;
   let { mouse } = helper;
 
   info("Moving inset top");
+  let onShapeChangeApplied = highlighters.once("shapes-highlighter-changes-applied");
   await mouse.down(xCenter, top, selector);
   await mouse.move(xCenter, top + dy, selector);
   await mouse.up(xCenter, top + dy, selector);
   await testActor.reflow();
+  await onShapeChangeApplied;
 
   info("Moving inset bottom");
+  onShapeChangeApplied = highlighters.once("shapes-highlighter-changes-applied");
   await mouse.down(xCenter, bottom, selector);
   await mouse.move(xCenter, bottom + dy, selector);
   await mouse.up(xCenter, bottom + dy, selector);
   await testActor.reflow();
+  await onShapeChangeApplied;
 
   info("Moving inset left");
+  onShapeChangeApplied = highlighters.once("shapes-highlighter-changes-applied");
   await mouse.down(left, yCenter, selector);
   await mouse.move(left + dx, yCenter, selector);
   await mouse.up(left + dx, yCenter, selector);
   await testActor.reflow();
+  await onShapeChangeApplied;
 
   info("Moving inset right");
-  let onShapeChangeApplied = highlighters.once("shapes-highlighter-changes-applied");
+  onShapeChangeApplied = highlighters.once("shapes-highlighter-changes-applied");
   await mouse.down(right, yCenter, selector);
   await mouse.move(right + dx, yCenter, selector);
   await mouse.up(right + dx, yCenter, selector);
   await testActor.reflow();
   await onShapeChangeApplied;
 
   let definition = await getComputedPropertyValue(selector, property, inspector);
   ok(definition.includes(
--- a/dom/base/DOMRequest.cpp
+++ b/dom/base/DOMRequest.cpp
@@ -61,60 +61,27 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(DOMRequest,
                                                DOMEventTargetHelper)
   // Don't need NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER because
   // DOMEventTargetHelper does it for us.
   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResult)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMRequest)
-  NS_INTERFACE_MAP_ENTRY(nsIDOMDOMRequest)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(DOMRequest, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(DOMRequest, DOMEventTargetHelper)
 
 /* virtual */ JSObject*
 DOMRequest::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return DOMRequestBinding::Wrap(aCx, this, aGivenProto);
 }
 
-NS_IMETHODIMP
-DOMRequest::GetReadyState(nsAString& aReadyState)
-{
-  DOMRequestReadyState readyState = ReadyState();
-  switch (readyState) {
-    case DOMRequestReadyState::Pending:
-      aReadyState.AssignLiteral("pending");
-      break;
-    case DOMRequestReadyState::Done:
-      aReadyState.AssignLiteral("done");
-      break;
-    default:
-      MOZ_CRASH("Unrecognized readyState.");
-  }
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-DOMRequest::GetResult(JS::MutableHandle<JS::Value> aResult)
-{
-  GetResult(nullptr, aResult);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-DOMRequest::GetError(nsISupports** aError)
-{
-  NS_IF_ADDREF(*aError = static_cast<Exception*>(GetError()));
-  return NS_OK;
-}
-
 void
 DOMRequest::FireSuccess(JS::Handle<JS::Value> aResult)
 {
   NS_ASSERTION(!mDone, "mDone shouldn't have been set to true already!");
   NS_ASSERTION(!mError, "mError shouldn't have been set!");
   NS_ASSERTION(mResult.isUndefined(), "mResult shouldn't have been set!");
 
   mDone = true;
@@ -232,42 +199,43 @@ DOMRequest::Then(JSContext* aCx, AnyCall
   global = js::GetGlobalForObjectCrossCompartment(global);
   mPromise->Then(aCx, global, aResolveCallback, aRejectCallback, aRetval, aRv);
 }
 
 NS_IMPL_ISUPPORTS(DOMRequestService, nsIDOMRequestService)
 
 NS_IMETHODIMP
 DOMRequestService::CreateRequest(mozIDOMWindow* aWindow,
-                                 nsIDOMDOMRequest** aRequest)
+                                 DOMRequest** aRequest)
 {
   MOZ_ASSERT(NS_IsMainThread());
   NS_ENSURE_STATE(aWindow);
   auto* win = nsPIDOMWindowInner::From(aWindow);
-  NS_ADDREF(*aRequest = new DOMRequest(win));
+  RefPtr<DOMRequest> req = new DOMRequest(win);
+  req.forget(aRequest);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-DOMRequestService::FireSuccess(nsIDOMDOMRequest* aRequest,
+DOMRequestService::FireSuccess(DOMRequest* aRequest,
                                JS::Handle<JS::Value> aResult)
 {
   NS_ENSURE_STATE(aRequest);
-  static_cast<DOMRequest*>(aRequest)->FireSuccess(aResult);
+  aRequest->FireSuccess(aResult);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-DOMRequestService::FireError(nsIDOMDOMRequest* aRequest,
+DOMRequestService::FireError(DOMRequest* aRequest,
                              const nsAString& aError)
 {
   NS_ENSURE_STATE(aRequest);
-  static_cast<DOMRequest*>(aRequest)->FireError(aError);
+  aRequest->FireError(aError);
 
   return NS_OK;
 }
 
 class FireSuccessAsyncTask : public mozilla::Runnable
 {
 
   FireSuccessAsyncTask(DOMRequest* aRequest, const JS::Value& aResult)
@@ -321,25 +289,24 @@ public:
     return NS_OK;
   }
 private:
   RefPtr<DOMRequest> mReq;
   nsString mError;
 };
 
 NS_IMETHODIMP
-DOMRequestService::FireSuccessAsync(nsIDOMDOMRequest* aRequest,
+DOMRequestService::FireSuccessAsync(DOMRequest* aRequest,
                                     JS::Handle<JS::Value> aResult)
 {
   NS_ENSURE_STATE(aRequest);
-  return FireSuccessAsyncTask::Dispatch(static_cast<DOMRequest*>(aRequest), aResult);
+  return FireSuccessAsyncTask::Dispatch(aRequest, aResult);
 }
 
 NS_IMETHODIMP
-DOMRequestService::FireErrorAsync(nsIDOMDOMRequest* aRequest,
+DOMRequestService::FireErrorAsync(DOMRequest* aRequest,
                                   const nsAString& aError)
 {
   NS_ENSURE_STATE(aRequest);
-  nsCOMPtr<nsIRunnable> asyncTask =
-    new FireErrorAsyncTask(static_cast<DOMRequest*>(aRequest), aError);
+  nsCOMPtr<nsIRunnable> asyncTask = new FireErrorAsyncTask(aRequest, aError);
   MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(asyncTask));
   return NS_OK;
 }
--- a/dom/base/DOMRequest.h
+++ b/dom/base/DOMRequest.h
@@ -2,45 +2,43 @@
 /* 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/. */
 
 #ifndef mozilla_dom_domrequest_h__
 #define mozilla_dom_domrequest_h__
 
-#include "nsIDOMDOMRequest.h"
+#include "nsIDOMRequestService.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/dom/DOMException.h"
 #include "mozilla/dom/DOMRequestBinding.h"
 
 #include "nsCOMPtr.h"
 
 namespace mozilla {
 
 class ErrorResult;
 
 namespace dom {
 
 class AnyCallback;
 class Promise;
 
-class DOMRequest : public DOMEventTargetHelper,
-                   public nsIDOMDOMRequest
+class DOMRequest : public DOMEventTargetHelper
 {
 protected:
   JS::Heap<JS::Value> mResult;
   RefPtr<DOMException> mError;
   RefPtr<Promise> mPromise;
   bool mDone;
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
-  NS_DECL_NSIDOMDOMREQUEST
 
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(DOMRequest,
                                                          DOMEventTargetHelper)
 
   // WrapperCache
   nsPIDOMWindowInner* GetParentObject() const
   {
     return GetOwner();
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -11,18 +11,18 @@ with Files("**"):
     BUG_COMPONENT = ("Core", "DOM")
 
 TEST_DIRS += ['test']
 
 XPIDL_SOURCES += [
     'mozIDOMWindow.idl',
     'nsIContentPolicy.idl',
     'nsIDocumentEncoder.idl',
-    'nsIDOMDOMRequest.idl',
     'nsIDOMParser.idl',
+    'nsIDOMRequestService.idl',
     'nsIDroppedLinkHandler.idl',
     'nsIFrameLoaderOwner.idl',
     'nsIImageLoadingContent.idl',
     'nsIMessageManager.idl',
     'nsIObjectLoadingContent.idl',
     'nsIPerformanceMetrics.idl',
     'nsIRemoteWindowContext.idl',
     'nsIScriptChannel.idl',
rename from dom/base/nsIDOMDOMRequest.idl
rename to dom/base/nsIDOMRequestService.idl
--- a/dom/base/nsIDOMDOMRequest.idl
+++ b/dom/base/nsIDOMRequestService.idl
@@ -1,32 +1,21 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=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 "nsIDOMEventTarget.idl"
+#include "nsISupports.idl"
 
 interface mozIDOMWindow;
-interface nsICursorContinueCallback;
-
-[shim(DOMRequest), uuid(e39da69e-2232-4e49-9856-b8a4a6210336)]
-interface nsIDOMDOMRequest : nsIDOMEventTarget
-{
-  readonly attribute DOMString readyState; // "pending" or "done"
-
-  readonly attribute jsval result;
-
-  // DOMException
-  readonly attribute nsISupports error;
-};
+webidl DOMRequest;
 
 [scriptable, builtinclass, uuid(9a57e5de-ce93-45fa-8145-755722834f7c)]
 interface nsIDOMRequestService : nsISupports
 {
-  nsIDOMDOMRequest createRequest(in mozIDOMWindow window);
+  DOMRequest createRequest(in mozIDOMWindow window);
 
-  void fireSuccess(in nsIDOMDOMRequest request, in jsval result);
-  void fireError(in nsIDOMDOMRequest request, in DOMString error);
-  void fireSuccessAsync(in nsIDOMDOMRequest request, in jsval result);
-  void fireErrorAsync(in nsIDOMDOMRequest request, in DOMString error);
+  void fireSuccess(in DOMRequest request, in jsval result);
+  void fireError(in DOMRequest request, in DOMString error);
+  void fireSuccessAsync(in DOMRequest request, in jsval result);
+  void fireErrorAsync(in DOMRequest request, in DOMString error);
 };
--- a/dom/browser-element/nsIBrowserElementAPI.idl
+++ b/dom/browser-element/nsIBrowserElementAPI.idl
@@ -1,18 +1,17 @@
 /* -*- Mode: IDL; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=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 "nsISupports.idl"
 
-interface nsIDOMDOMRequest;
-
+webidl DOMRequest;
 webidl FrameLoader;
 
 [scriptable, function, uuid(00d0e19d-bd67-491f-8e85-b9905224d3bb)]
 interface nsIBrowserElementNextPaintListener : nsISupports
 {
   void recvNextPaint();
 };
 
@@ -58,34 +57,34 @@ interface nsIBrowserElementAPI : nsISupp
                       [const, array, size_is(count)] in float aRotationAngles,
                       [const, array, size_is(count)] in float aForces,
                       in uint32_t count,
                       in long aModifiers);
   void goBack();
   void goForward();
   void reload(in boolean hardReload);
   void stop();
-  nsIDOMDOMRequest download(in DOMString url,
+  DOMRequest download(in DOMString url,
                             [optional] in jsval options);
-  nsIDOMDOMRequest purgeHistory();
-  nsIDOMDOMRequest getScreenshot(in uint32_t width,
+  DOMRequest purgeHistory();
+  DOMRequest getScreenshot(in uint32_t width,
                                  in uint32_t height,
                                  [optional] in DOMString mimeType);
   void zoom(in float zoom);
-  nsIDOMDOMRequest getCanGoBack();
-  nsIDOMDOMRequest getCanGoForward();
-  nsIDOMDOMRequest getContentDimensions();
+  DOMRequest getCanGoBack();
+  DOMRequest getCanGoForward();
+  DOMRequest getContentDimensions();
 
   void findAll(in DOMString searchString, in long caseSensitivity);
   void findNext(in long direction);
   void clearMatch();
 
   void addNextPaintListener(in nsIBrowserElementNextPaintListener listener);
   void removeNextPaintListener(in nsIBrowserElementNextPaintListener listener);
 
-  nsIDOMDOMRequest executeScript(in DOMString script, in jsval options);
+  DOMRequest executeScript(in DOMString script, in jsval options);
 
   /**
    * Returns an object that represents a Web Manifest:
    * http://w3c.github.io/manifest/
    */
-  nsIDOMDOMRequest getWebManifest();
+  DOMRequest getWebManifest();
 };
--- a/dom/html/nsBrowserElement.cpp
+++ b/dom/html/nsBrowserElement.cpp
@@ -10,17 +10,16 @@
 #include "mozilla/Services.h"
 #include "mozilla/dom/BrowserElementBinding.h"
 #include "mozilla/dom/DOMRequest.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/ToJSValue.h"
 
 #include "nsComponentManagerUtils.h"
 #include "nsFrameLoader.h"
-#include "nsIDOMDOMRequest.h"
 #include "nsIDOMElement.h"
 #include "nsIMozBrowserFrame.h"
 #include "nsINode.h"
 #include "nsIPrincipal.h"
 
 using namespace mozilla::dom;
 
 namespace mozilla {
@@ -178,17 +177,17 @@ nsBrowserElement::Stop(ErrorResult& aRv)
 
 already_AddRefed<DOMRequest>
 nsBrowserElement::Download(const nsAString& aUrl,
                            const BrowserElementDownloadOptions& aOptions,
                            ErrorResult& aRv)
 {
   NS_ENSURE_TRUE(IsBrowserElementOrThrow(aRv), nullptr);
 
-  nsCOMPtr<nsIDOMDOMRequest> req;
+  RefPtr<DOMRequest> req;
   nsCOMPtr<nsIXPConnectWrappedJS> wrappedObj = do_QueryInterface(mBrowserElementAPI);
   MOZ_ASSERT(wrappedObj, "Failed to get wrapped JS from XPCOM component.");
   AutoJSAPI jsapi;
   if (!jsapi.Init(wrappedObj->GetJSObject())) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
   JSContext* cx = jsapi.cx();
@@ -200,57 +199,57 @@ nsBrowserElement::Download(const nsAStri
   }
   nsresult rv = mBrowserElementAPI->Download(aUrl, options, getter_AddRefs(req));
 
   if (NS_WARN_IF(NS_FAILED(rv))) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return nullptr;
   }
 
-  return req.forget().downcast<DOMRequest>();
+  return req.forget();
 }
 
 already_AddRefed<DOMRequest>
 nsBrowserElement::PurgeHistory(ErrorResult& aRv)
 {
   NS_ENSURE_TRUE(IsBrowserElementOrThrow(aRv), nullptr);
 
-  nsCOMPtr<nsIDOMDOMRequest> req;
+  RefPtr<DOMRequest> req;
   nsresult rv = mBrowserElementAPI->PurgeHistory(getter_AddRefs(req));
 
   if (NS_WARN_IF(NS_FAILED(rv))) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return nullptr;
   }
 
-  return req.forget().downcast<DOMRequest>();
+  return req.forget();
 }
 
 already_AddRefed<DOMRequest>
 nsBrowserElement::GetScreenshot(uint32_t aWidth,
                                 uint32_t aHeight,
                                 const nsAString& aMimeType,
                                 ErrorResult& aRv)
 {
   NS_ENSURE_TRUE(IsBrowserElementOrThrow(aRv), nullptr);
 
-  nsCOMPtr<nsIDOMDOMRequest> req;
+  RefPtr<DOMRequest> req;
   nsresult rv = mBrowserElementAPI->GetScreenshot(aWidth, aHeight, aMimeType,
                                                   getter_AddRefs(req));
 
   if (NS_WARN_IF(NS_FAILED(rv))) {
     if (rv == NS_ERROR_INVALID_ARG) {
       aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
     } else {
       aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     }
     return nullptr;
   }
 
-  return req.forget().downcast<DOMRequest>();
+  return req.forget();
 }
 
 void
 nsBrowserElement::Zoom(float aZoom, ErrorResult& aRv)
 {
   NS_ENSURE_TRUE_VOID(IsBrowserElementOrThrow(aRv));
 
   nsresult rv = mBrowserElementAPI->Zoom(aZoom);
@@ -260,57 +259,57 @@ nsBrowserElement::Zoom(float aZoom, Erro
   }
 }
 
 already_AddRefed<DOMRequest>
 nsBrowserElement::GetCanGoBack(ErrorResult& aRv)
 {
   NS_ENSURE_TRUE(IsBrowserElementOrThrow(aRv), nullptr);
 
-  nsCOMPtr<nsIDOMDOMRequest> req;
+  RefPtr<DOMRequest> req;
   nsresult rv = mBrowserElementAPI->GetCanGoBack(getter_AddRefs(req));
 
   if (NS_WARN_IF(NS_FAILED(rv))) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return nullptr;
   }
 
-  return req.forget().downcast<DOMRequest>();
+  return req.forget();
 }
 
 already_AddRefed<DOMRequest>
 nsBrowserElement::GetCanGoForward(ErrorResult& aRv)
 {
   NS_ENSURE_TRUE(IsBrowserElementOrThrow(aRv), nullptr);
 
-  nsCOMPtr<nsIDOMDOMRequest> req;
+  RefPtr<DOMRequest> req;
   nsresult rv = mBrowserElementAPI->GetCanGoForward(getter_AddRefs(req));
 
   if (NS_WARN_IF(NS_FAILED(rv))) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return nullptr;
   }
 
-  return req.forget().downcast<DOMRequest>();
+  return req.forget();
 }
 
 already_AddRefed<DOMRequest>
 nsBrowserElement::GetContentDimensions(ErrorResult& aRv)
 {
   NS_ENSURE_TRUE(IsBrowserElementOrThrow(aRv), nullptr);
 
-  nsCOMPtr<nsIDOMDOMRequest> req;
+  RefPtr<DOMRequest> req;
   nsresult rv = mBrowserElementAPI->GetContentDimensions(getter_AddRefs(req));
 
   if (NS_WARN_IF(NS_FAILED(rv))) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return nullptr;
   }
 
-  return req.forget().downcast<DOMRequest>();
+  return req.forget();
 }
 
 void
 nsBrowserElement::FindAll(const nsAString& aSearchString,
                           BrowserFindCaseSensitivity aCaseSensitivity,
                           ErrorResult& aRv)
 {
   NS_ENSURE_TRUE_VOID(IsBrowserElementOrThrow(aRv));
@@ -397,17 +396,17 @@ nsBrowserElement::RemoveNextPaintListene
 
 already_AddRefed<DOMRequest>
 nsBrowserElement::ExecuteScript(const nsAString& aScript,
                                 const BrowserElementExecuteScriptOptions& aOptions,
                                 ErrorResult& aRv)
 {
   NS_ENSURE_TRUE(IsBrowserElementOrThrow(aRv), nullptr);
 
-  nsCOMPtr<nsIDOMDOMRequest> req;
+  RefPtr<DOMRequest> req;
   nsCOMPtr<nsIXPConnectWrappedJS> wrappedObj = do_QueryInterface(mBrowserElementAPI);
   MOZ_ASSERT(wrappedObj, "Failed to get wrapped JS from XPCOM component.");
   AutoJSAPI jsapi;
   if (!jsapi.Init(wrappedObj->GetJSObject())) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
   JSContext* cx = jsapi.cx();
@@ -424,30 +423,30 @@ nsBrowserElement::ExecuteScript(const ns
     if (rv == NS_ERROR_INVALID_ARG) {
       aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
     } else {
       aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     }
     return nullptr;
   }
 
-  return req.forget().downcast<DOMRequest>();
+  return req.forget();
 }
 
 already_AddRefed<DOMRequest>
 nsBrowserElement::GetWebManifest(ErrorResult& aRv)
 {
   NS_ENSURE_TRUE(IsBrowserElementOrThrow(aRv), nullptr);
 
-  nsCOMPtr<nsIDOMDOMRequest> req;
+  RefPtr<DOMRequest> req;
   nsresult rv = mBrowserElementAPI->GetWebManifest(getter_AddRefs(req));
 
   if (NS_WARN_IF(NS_FAILED(rv))) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return nullptr;
   }
 
-  return req.forget().downcast<DOMRequest>();
+  return req.forget();
 }
 
 
 
 } // namespace mozilla
--- a/dom/imptests/moz.build
+++ b/dom/imptests/moz.build
@@ -3,23 +3,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/.
 
 with Files("**"):
     BUG_COMPONENT = ("Core", "DOM")
 
 with Files("html/webgl/**"):
-    BUG_COMPONENT = ("Core", "Canvas 2D")
+    BUG_COMPONENT = ("Core", "Canvas: 2D")
 
 with Files("html/typedarrays/**"):
-    BUG_COMPONENT = ("Core", "Javascript: Standard Library")
+    BUG_COMPONENT = ("Core", "JavaScript: Standard Library")
 
 with Files("html/js/**"):
-    BUG_COMPONENT = ("Core", "Javascript: Standard Library")
+    BUG_COMPONENT = ("Core", "JavaScript: Standard Library")
 
 with Files("html/html/**"):
     BUG_COMPONENT = ("Core", "DOM: Core & HTML")
 
 MOCHITEST_MANIFESTS += [
     'html/mochitest.ini',
     'webapps/mochitest.ini',
 ]
--- a/dom/indexedDB/IDBRequest.cpp
+++ b/dom/indexedDB/IDBRequest.cpp
@@ -18,18 +18,18 @@
 #include "mozilla/ContentEvents.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/Move.h"
 #include "mozilla/dom/DOMException.h"
 #include "mozilla/dom/ErrorEventBinding.h"
 #include "mozilla/dom/IDBOpenDBRequestBinding.h"
 #include "mozilla/dom/ScriptSettings.h"
-#include "mozilla/dom/WorkerHolder.h"
 #include "mozilla/dom/WorkerPrivate.h"
+#include "mozilla/dom/WorkerRef.h"
 #include "nsCOMPtr.h"
 #include "nsContentUtils.h"
 #include "nsIScriptContext.h"
 #include "nsJSUtils.h"
 #include "nsPIDOMWindow.h"
 #include "nsString.h"
 #include "ReportInternalError.h"
 
@@ -408,64 +408,16 @@ void
 IDBRequest::GetEventTargetParent(EventChainPreVisitor& aVisitor)
 {
   AssertIsOnOwningThread();
 
   aVisitor.mCanHandle = true;
   aVisitor.SetParentTarget(mTransaction, false);
 }
 
-class IDBOpenDBRequest::WorkerHolder final
-  : public mozilla::dom::WorkerHolder
-{
-  WorkerPrivate* mWorkerPrivate;
-#ifdef DEBUG
-  // This is only here so that assertions work in the destructor even if
-  // NoteAddWorkerHolderFailed was called.
-  WorkerPrivate* mWorkerPrivateDEBUG;
-#endif
-
-public:
-  explicit
-  WorkerHolder(WorkerPrivate* aWorkerPrivate)
-    : mozilla::dom::WorkerHolder("IDBOpenDBRequest::WorkerHolder")
-    , mWorkerPrivate(aWorkerPrivate)
-#ifdef DEBUG
-    , mWorkerPrivateDEBUG(aWorkerPrivate)
-#endif
-  {
-    MOZ_ASSERT(aWorkerPrivate);
-    aWorkerPrivate->AssertIsOnWorkerThread();
-
-    MOZ_COUNT_CTOR(IDBOpenDBRequest::WorkerHolder);
-  }
-
-  ~WorkerHolder()
-  {
-#ifdef DEBUG
-    mWorkerPrivateDEBUG->AssertIsOnWorkerThread();
-#endif
-
-    MOZ_COUNT_DTOR(IDBOpenDBRequest::WorkerHolder);
-  }
-
-  void
-  NoteAddWorkerHolderFailed()
-  {
-    MOZ_ASSERT(mWorkerPrivate);
-    mWorkerPrivate->AssertIsOnWorkerThread();
-
-    mWorkerPrivate = nullptr;
-  }
-
-private:
-  virtual bool
-  Notify(WorkerStatus aStatus) override;
-};
-
 IDBOpenDBRequest::IDBOpenDBRequest(IDBFactory* aFactory,
                                    nsPIDOMWindowInner* aOwner,
                                    bool aFileHandleDisabled)
   : IDBRequest(aOwner)
   , mFactory(aFactory)
   , mFileHandleDisabled(aFileHandleDisabled)
   , mIncreasedActiveDatabaseCount(false)
 {
@@ -525,23 +477,21 @@ IDBOpenDBRequest::CreateForJS(JSContext*
   request->SetScriptOwner(aScriptOwner);
 
   if (!NS_IsMainThread()) {
     WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
     MOZ_ASSERT(workerPrivate);
 
     workerPrivate->AssertIsOnWorkerThread();
 
-    nsAutoPtr<WorkerHolder> workerHolder(new WorkerHolder(workerPrivate));
-    if (NS_WARN_IF(!workerHolder->HoldWorker(workerPrivate, Canceling))) {
-      workerHolder->NoteAddWorkerHolderFailed();
+    request->mWorkerRef =
+      StrongWorkerRef::Create(workerPrivate, "IDBOpenDBRequest");
+    if (NS_WARN_IF(!request->mWorkerRef)) {
       return nullptr;
     }
-
-    request->mWorkerHolder = Move(workerHolder);
   }
 
   request->IncreaseActiveDatabaseCount();
 
   return request.forget();
 }
 
 void
@@ -581,24 +531,23 @@ IDBOpenDBRequest::DispatchNonTransaction
     NS_WARNING("Failed to dispatch event!");
   }
 }
 
 void
 IDBOpenDBRequest::NoteComplete()
 {
   AssertIsOnOwningThread();
-  MOZ_ASSERT_IF(!NS_IsMainThread(), mWorkerHolder);
+  MOZ_ASSERT_IF(!NS_IsMainThread(), mWorkerRef);
 
   // Normally, we decrease the number of active IDBOpenRequests here.
   MaybeDecreaseActiveDatabaseCount();
 
-  // If we have a WorkerHolder installed on the worker then nulling this out
-  // will uninstall it from the worker.
-  mWorkerHolder = nullptr;
+  // If we have a WorkerRef, then nulling this out will release the worker.
+  mWorkerRef = nullptr;
 }
 
 void
 IDBOpenDBRequest::IncreaseActiveDatabaseCount()
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(!mIncreasedActiveDatabaseCount);
 
@@ -656,24 +605,10 @@ IDBOpenDBRequest::PostHandleEvent(EventC
 JSObject*
 IDBOpenDBRequest::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   AssertIsOnOwningThread();
 
   return IDBOpenDBRequestBinding::Wrap(aCx, this, aGivenProto);
 }
 
-bool
-IDBOpenDBRequest::
-WorkerHolder::Notify(WorkerStatus aStatus)
-{
-  MOZ_ASSERT(mWorkerPrivate);
-  mWorkerPrivate->AssertIsOnWorkerThread();
-  MOZ_ASSERT(aStatus > Running);
-
-  // There's nothing we can really do here at the moment...
-  NS_WARNING("Worker closing but IndexedDB is waiting to open a database!");
-
-  return true;
-}
-
 } // namespace dom
 } // namespace mozilla
--- a/dom/indexedDB/IDBRequest.h
+++ b/dom/indexedDB/IDBRequest.h
@@ -30,16 +30,17 @@ class DOMException;
 class IDBCursor;
 class IDBDatabase;
 class IDBFactory;
 class IDBIndex;
 class IDBObjectStore;
 class IDBTransaction;
 template <typename> struct Nullable;
 class OwningIDBObjectStoreOrIDBIndexOrIDBCursor;
+class StrongWorkerRef;
 
 class IDBRequest
   : public IDBWrapperCache
 {
 protected:
   // mSourceAsObjectStore and mSourceAsIndex are exclusive and one must always
   // be set. mSourceAsCursor is sometimes set also.
   RefPtr<IDBObjectStore> mSourceAsObjectStore;
@@ -210,22 +211,20 @@ public:
 protected:
   ResultCallback()
   { }
 };
 
 class IDBOpenDBRequest final
   : public IDBRequest
 {
-  class WorkerHolder;
-
   // Only touched on the owning thread.
   RefPtr<IDBFactory> mFactory;
 
-  nsAutoPtr<WorkerHolder> mWorkerHolder;
+  RefPtr<StrongWorkerRef> mWorkerRef;
 
   const bool mFileHandleDisabled;
   bool mIncreasedActiveDatabaseCount;
 
 public:
   static already_AddRefed<IDBOpenDBRequest>
   CreateForWindow(JSContext* aCx,
                   IDBFactory* aFactory,
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -112,17 +112,18 @@ namespace mozilla {
 namespace dom {
 
 // Used on Android/B2G to pass the list of fonts on the device
 // to the child process
 struct FontListEntry {
     nsString  familyName;
     nsString  faceName;
     nsCString filepath;
-    float     weight;
+    float     minWeight;
+    float     maxWeight;
     int16_t   stretch;
     uint8_t   italic;
     uint8_t   index;
 };
 
 // Used on Mac OS X to pass the list of font families (not faces)
 // from chrome to content processes.
 // The entryType field distinguishes several types of font family
--- a/dom/media/tests/mochitest/test_getUserMedia_basicScreenshare.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_basicScreenshare.html
@@ -99,25 +99,34 @@
       SpecialPowers.wrap(document).onfullscreenerror = () => reject(new Error("fullscreenerror"));
 
       // Note that going fullscreen requires the tab (and window) to be in the
       // foreground and having focus.
       SpecialPowers.wrap(canvas).requestFullscreen();
     });
 
     info("Testing screenshare without constraints");
-    let stream = await getUserMedia({
-      video: {mediaSource: "screen"},
-      fake: false,
-    });
+    let stream = await getUserMedia({video: {mediaSource: "screen"}});
+    let settings = stream.getTracks()[0].getSettings();
+    ok(settings.width <= 8192,
+       `Width setting ${settings.width} should be set after gUM (or 0 per bug 1453247)`);
+    ok(settings.height <= 8192,
+       `Height setting ${settings.height} should be set after gUM (or 0 per bug 1453247)`);
     draw(helper.red, helper.blue,
          helper.green, helper.grey);
     let playback = new LocalMediaStreamPlayback(testVideo, stream);
     playback.startMedia();
     await playback.verifyPlaying();
+    settings = stream.getTracks()[0].getSettings();
+    is(settings.width, testVideo.videoWidth,
+       "Width setting should match video width");
+    is(settings.height, testVideo.videoHeight,
+       "Height setting should match video height");
+    let screenWidth = testVideo.videoWidth;
+    let screenHeight = testVideo.videoHeight;
     await verifyScreenshare(testVideo, helper,
                             helper.red, helper.blue,
                             helper.green, helper.grey);
     for (let track of stream.getTracks()) {
       track.stop();
     }
     playback.detachFromMediaElement();
 
@@ -133,40 +142,75 @@
           min: '10',
           max: '100'
         },
         frameRate: {
           min: '10',
           max: '15'
         },
       },
-      fake: false,
     });
+    settings = stream.getTracks()[0].getSettings();
+    ok(settings.width == 0 || (settings.width >= 10 && settings.width <= 100),
+       `Width setting ${settings.width} should be correct after gUM (or 0 per bug 1453247)`);
+    ok(settings.height == 0 || (settings.height >= 10 && settings.height <= 100),
+       `Height setting ${settings.height} should be correct after gUM (or 0 per bug 1453247)`);
     draw(helper.green, helper.red,
          helper.grey, helper.blue);
     playback = new LocalMediaStreamPlayback(testVideo, stream);
     playback.startMedia();
     await playback.verifyPlaying();
+    settings = stream.getTracks()[0].getSettings();
+    ok(settings.width >= 10 && settings.width <= 100,
+       `Width setting ${settings.width} should be within constraints`);
+    ok(settings.height >= 10 && settings.height <= 100,
+       `Height setting ${settings.height} should be within constraints`);
+    is(settings.width, testVideo.videoWidth,
+       "Width setting should match video width");
+    is(settings.height, testVideo.videoHeight,
+       "Height setting should match video height");
+    let expectedHeight = (screenHeight * settings.width) / screenWidth;
+    ok(Math.abs(expectedHeight - settings.height) <= 1,
+       "Aspect ratio after constrained gUM should be close enough");
     await verifyScreenshare(testVideo, helper,
                             helper.green, helper.red,
                             helper.grey, helper.blue);
 
     info("Testing modifying screenshare with applyConstraints");
-    await Promise.all([
-      testVideo.srcObject.getVideoTracks()[0].applyConstraints({
-        mediaSource: 'screen',
-        width: 200,
-        height: 200,
-        frameRate: {
-          min: '5',
-          max: '10'
-        }
-      }),
-      haveEvent(testVideo, "resize", wait(5000, new Error("Timeout"))),
-    ]);
+    let resize = haveEvent(testVideo, "resize", wait(5000, new Error("Timeout")));
+    await testVideo.srcObject.getVideoTracks()[0].applyConstraints({
+      mediaSource: 'screen',
+      width: 200,
+      height: 200,
+      frameRate: {
+        min: '5',
+        max: '10'
+      }
+    });
+    // getSettings() should report correct size as soon as applyConstraints()
+    // resolves - bug 1453259. Until fixed, check that we at least report
+    // something sane.
+    let newSettings = stream.getTracks()[0].getSettings();
+    ok(newSettings.width > settings.width && newSettings.width < screenWidth,
+       `Width setting ${newSettings.width} should have increased after applyConstraints`);
+    ok(newSettings.height > settings.height && newSettings.height < screenHeight,
+       `Height setting ${newSettings.height} should have increased after applyConstraints`);
+    await resize;
+    settings = stream.getTracks()[0].getSettings();
+    ok(settings.width > 100 && settings.width < screenWidth,
+       `Width setting ${settings.width} should have increased after first frame after applyConstraints`);
+    ok(settings.height > 100 && settings.height < screenHeight,
+       `Height setting ${settings.height} should have increased after first frame after applyConstraints`);
+    is(settings.width, testVideo.videoWidth,
+       "Width setting should match video width");
+    is(settings.height, testVideo.videoHeight,
+       "Height setting should match video height");
+    expectedHeight = (screenHeight * settings.width) / screenWidth;
+    ok(Math.abs(expectedHeight - settings.height) <= 1,
+       "Aspect ratio after applying constraints should be close enough");
     draw(helper.grey, helper.green,
          helper.blue, helper.red);
     await playback.verifyPlaying(); // still playing
     await verifyScreenshare(testVideo, helper,
                             helper.grey, helper.green,
                             helper.blue, helper.red);
     await playback.deprecatedStopStreamInMediaPlayback();
     playback.detachFromMediaElement();
--- a/dom/smil/test/db_smilCSSFromTo.js
+++ b/dom/smil/test/db_smilCSSFromTo.js
@@ -307,17 +307,17 @@ var gFromToBundles = [
     new AnimTestcaseFromTo("small-caps", "normal"),
   ]),
   new TestcaseBundle(gPropList.font_weight, [
     new AnimTestcaseFromTo("100", "900", { midComp: "500" }),
     new AnimTestcaseFromTo("700", "100", { midComp: "400" }),
     new AnimTestcaseFromTo("inherit", "200",
                            { fromComp: "400", midComp: "300" }),
     new AnimTestcaseFromTo("normal", "bold",
-                           { fromComp: "400", midComp: "600", toComp: "700" }),
+                           { fromComp: "400", midComp: "550", toComp: "700" }),
     new AnimTestcaseFromTo("lighter", "bolder", {},
                            "need support for animating between " +
                            "relative 'font-weight' values"),
   ]),
   // NOTE: Mozilla doesn't currently support "glyph-orientation-horizontal" or
   // "glyph-orientation-vertical", but I'm testing them here in case we ever
   // add support for them, because they're explicitly not animatable in the SVG
   // spec.
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -213,17 +213,17 @@ with Files("NativeOSFileInternals.webidl
 
 with Files("Net*"):
     BUG_COMPONENT = ("Core", "Networking")
 
 with Files("OfflineAudio*"):
     BUG_COMPONENT = ("Core", "Web Audio")
 
 with Files("OffscreenCanvas.webidl"):
-    BUG_COMPONENT = ("Core", "Canvas 2D")
+    BUG_COMPONENT = ("Core", "Canvas: 2D")
 
 with Files("OscillatorNode.webidl"):
     BUG_COMPONENT = ("Core", "Web Audio")
 
 with Files("PannerNode.webidl"):
     BUG_COMPONENT = ("Core", "Web Audio")
 
 with Files("Peer*"):
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -1548,16 +1548,17 @@ struct Config {
 class GFX2D_API Factory
 {
   using char_type = filesystem::Path::value_type;
 public:
   static void Init(const Config& aConfig);
   static void ShutDown();
 
   static bool HasSSE2();
+  static bool HasSSE4();
 
   /**
    * Returns false if any of the following are true:
    *
    *   - the width/height of |sz| are less than or equal to zero
    *   - the width/height of |sz| are greater than |limit|
    *   - the number of bytes that need to be allocated for the surface is too
    *     big to fit in an int32_t, or bigger than |allocLimit|, if specifed
--- a/gfx/2d/BaseRect.h
+++ b/gfx/2d/BaseRect.h
@@ -119,17 +119,17 @@ struct BaseRect {
   // of *this and aRect.
   MOZ_MUST_USE Sub Intersect(const Sub& aRect) const
   {
     Sub result;
     result.x = std::max<T>(x, aRect.x);
     result.y = std::max<T>(y, aRect.y);
     result.width = std::min<T>(x - result.x + width, aRect.x - result.x + aRect.width);
     result.height = std::min<T>(y - result.y + height, aRect.y - result.y + aRect.height);
-    if (result.width < 0 || result.height < 0) {
+    if (result.width <= 0 || result.height <= 0) {
       result.SizeTo(0, 0);
     }
     return result;
   }
   // Sets *this to be the rectangle containing the intersection of the points
   // (including edges) of *this and aRect. If there are no points in that
   // intersection, sets *this to be an empty rectangle with x/y set to the std::max
   // of the x/y of *this and aRect.
--- a/gfx/2d/Factory.cpp
+++ b/gfx/2d/Factory.cpp
@@ -82,17 +82,18 @@ GetGFX2DLog()
 
 // The following code was largely taken from xpcom/glue/SSE.cpp and
 // made a little simpler.
 enum CPUIDRegister { eax = 0, ebx = 1, ecx = 2, edx = 3 };
 
 #ifdef HAVE_CPUID_H
 
 #if !(defined(__SSE2__) || defined(_M_X64) || \
-     (defined(_M_IX86_FP) && _M_IX86_FP >= 2))
+     (defined(_M_IX86_FP) && _M_IX86_FP >= 2)) || \
+    !defined(__SSE4__)
 // cpuid.h is available on gcc 4.3 and higher on i386 and x86_64
 #include <cpuid.h>
 
 static inline bool
 HasCPUIDBit(unsigned int level, CPUIDRegister reg, unsigned int bit)
 {
   unsigned int regs[4];
   return __get_cpuid(level, &regs[0], &regs[1], &regs[2], &regs[3]) &&
@@ -277,16 +278,39 @@ Factory::HasSSE2()
     sDetectionState = HasCPUIDBit(1u, edx, (1u<<26)) ? HAS_SSE2 : NO_SSE2;
   }
   return sDetectionState == HAS_SSE2;
 #else
   return false;
 #endif
 }
 
+bool
+Factory::HasSSE4()
+{
+#if defined(__SSE4__)
+  // gcc with -msse2 (default on OSX and x86-64)
+  // cl.exe with -arch:SSE2 (default on x64 compiler)
+  return true;
+#elif defined(HAVE_CPU_DETECTION)
+  static enum {
+    UNINITIALIZED,
+    NO_SSE4,
+    HAS_SSE4
+  } sDetectionState = UNINITIALIZED;
+
+  if (sDetectionState == UNINITIALIZED) {
+    sDetectionState = HasCPUIDBit(1u, ecx, (1u << 19)) ? HAS_SSE4 : NO_SSE4;
+  }
+  return sDetectionState == HAS_SSE4;
+#else
+  return false;
+#endif
+}
+
 // If the size is "reasonable", we want gfxCriticalError to assert, so
 // this is the option set up for it.
 inline int LoggerOptionsBasedOnSize(const IntSize& aSize)
 {
   return CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize));
 }
 
 bool
--- a/gfx/src/FontPropertyTypes.h
+++ b/gfx/src/FontPropertyTypes.h
@@ -3,19 +3,23 @@
  * 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/. */
 
 /* font specific types shared by both thebes and layout */
 
 #ifndef GFX_FONT_PROPERTY_TYPES_H
 #define GFX_FONT_PROPERTY_TYPES_H
 
+#include <algorithm>
 #include <cstdint>
 #include <cmath>
+#include <utility>
+
 #include "mozilla/Assertions.h"
+#include "nsString.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 {
 
@@ -325,12 +329,85 @@ private:
     : FontPropertyValue(aAngle)
   {
   }
 
   static const int16_t kNormal = INT16_MIN;
   static const int16_t kItalic = INT16_MAX;
 };
 
+
+/**
+ * Convenience type to hold a <min, max> pair representing a range of values.
+ *
+ * The min and max are both inclusive, so when min == max the range represents
+ * a single value (not an empty range).
+ */
+template<class T>
+class FontPropertyRange
+{
+public:
+  /**
+   * Construct a range from given minimum and maximum values (inclusive).
+   */
+  FontPropertyRange(T aMin, T aMax)
+    : mValues(aMin, aMax)
+  {
+    MOZ_ASSERT(aMin <= aMax);
+  }
+
+  /**
+   * Construct a range representing a single value (min==max).
+   */
+  explicit FontPropertyRange(T aValue)
+    : mValues(aValue, aValue)
+  {
+  }
+
+  T Min() const { return mValues.first; }
+  T Max() const { return mValues.second; }
+
+  /**
+   * Clamp the given value to this range.
+   *
+   * (We can't use mozilla::Clamp here because it only accepts integral types.)
+   */
+  T Clamp(T aValue) const
+  {
+    return aValue <= Min() ? Min() : (aValue >= Max() ? Max() : aValue);
+  }
+
+  /**
+   * Return whether the range consists of a single unique value.
+   */
+  bool IsSingle() const
+  {
+    return Min() == Max();
+  }
+
+  bool operator==(const FontPropertyRange& aOther) const
+  {
+    return mValues == aOther.mValues;
+  }
+  bool operator!=(const FontPropertyRange& aOther) const
+  {
+    return mValues != aOther.mValues;
+  }
+
+  void ToString(nsACString& aOutString, const char* aDelim = "..") const
+  {
+    aOutString.AppendPrintf("%g", Min().ToFloat());
+    if (!IsSingle()) {
+      aOutString.AppendPrintf("%s%g", aDelim, Max().ToFloat());
+    }
+  }
+
+private:
+  std::pair<T,T> mValues;
+};
+
+typedef FontPropertyRange<mozilla::FontWeight>  WeightRange;
+typedef FontPropertyRange<mozilla::FontStretch> StretchRange;
+typedef FontPropertyRange<mozilla::FontStyle>   StyleRange;
+
 } // namespace mozilla
 
 #endif // GFX_FONT_PROPERTY_TYPES_H
-
--- a/gfx/src/nsRect.h
+++ b/gfx/src/nsRect.h
@@ -8,22 +8,26 @@
 #ifndef NSRECT_H
 #define NSRECT_H
 
 #include <stdio.h>                      // for FILE
 #include <stdint.h>                     // for int32_t, int64_t
 #include <algorithm>                    // for min/max
 #include "mozilla/Likely.h"             // for MOZ_UNLIKELY
 #include "mozilla/gfx/Rect.h"
+#include "mozilla/gfx/2D.h"
 #include "nsCoord.h"                    // for nscoord, etc
 #include "nsISupportsImpl.h"            // for MOZ_COUNT_CTOR, etc
 #include "nsPoint.h"                    // for nsIntPoint, nsPoint
 #include "nsMargin.h"                   // for nsIntMargin, nsMargin
 #include "nsSize.h"                     // for IntSize, nsSize
 #include "nscore.h"                     // for NS_BUILD_REFCNT_LOGGING
+#if !defined(ANDROID) && !defined(MOZ_ASAN) && (defined(__SSE2__) || defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP >= 2))
+#include "smmintrin.h"
+#endif
 
 typedef mozilla::gfx::IntRect nsIntRect;
 
 struct nsRect :
   public mozilla::gfx::BaseRect<nscoord, nsRect, nsPoint, nsSize, nsMargin> {
   typedef mozilla::gfx::BaseRect<nscoord, nsRect, nsPoint, nsSize, nsMargin> Super;
 
   static void VERIFY_COORD(nscoord aValue) { ::VERIFY_COORD(aValue); }
@@ -115,16 +119,89 @@ struct nsRect :
   MOZ_MUST_USE nsRect UnsafeUnion(const nsRect& aRect) const
   {
     return Super::Union(aRect);
   }
   void UnionRect(const nsRect& aRect1, const nsRect& aRect2)
   {
     *this = aRect1.Union(aRect2);
   }
+
+#if defined(_MSC_VER) && !defined(__clang__)
+  // Only MSVC supports inlining intrinsics for archs you're not compiling for.
+  MOZ_MUST_USE nsRect Intersect(const nsRect& aRect) const
+  {
+    nsRect result;
+    if (mozilla::gfx::Factory::HasSSE4()) {
+      __m128i rect1 = _mm_loadu_si128((__m128i*)&aRect); // x1, y1, w1, h1
+      __m128i rect2 = _mm_loadu_si128((__m128i*)this); // x2, y2, w2, h2
+
+      __m128i resultRect = _mm_max_epi32(rect1, rect2); // xr, yr, zz, zz
+
+
+      // result.width = std::min<int32_t>(x - result.x + width, aRect.x - result.x + aRect.width);
+      // result.height = std::min<int32_t>(y - result.y + height, aRect.y - result.y + aRect.height);
+      __m128i widthheight = _mm_min_epi32(_mm_add_epi32(_mm_sub_epi32(rect1, resultRect), _mm_srli_si128(rect1, 8)),
+                                          _mm_add_epi32(_mm_sub_epi32(rect2, resultRect), _mm_srli_si128(rect2, 8))); // w, h, zz, zz
+      widthheight = _mm_slli_si128(widthheight, 8); // 00, 00, wr, hr
+
+      resultRect = _mm_blend_epi16(resultRect, widthheight, 0xF0); // xr, yr, wr, hr
+
+      if ((_mm_movemask_ps(_mm_castsi128_ps(_mm_cmpgt_epi32(resultRect, _mm_setzero_si128()))) & 0xC) != 0xC) {
+        // It's potentially more efficient to store all 0s. But the non SSE4 code leaves x/y intact
+        // so let's do the same here.
+        resultRect = _mm_and_si128(resultRect, _mm_set_epi32(0, 0, 0xFFFFFFFF, 0xFFFFFFFF));
+      }
+
+      _mm_storeu_si128((__m128i*)&result, resultRect);
+
+      return result;
+    }
+
+    result.x = std::max<int32_t>(x, aRect.x);
+    result.y = std::max<int32_t>(y, aRect.y);
+    result.width = std::min<int32_t>(x - result.x + width, aRect.x - result.x + aRect.width);
+    result.height = std::min<int32_t>(y - result.y + height, aRect.y - result.y + aRect.height);
+    if (result.width <= 0 || result.height <= 0) {
+      result.SizeTo(0, 0);
+    }
+    return result;
+  }
+
+  bool IntersectRect(const nsRect& aRect1, const nsRect& aRect2)
+  {
+    if (mozilla::gfx::Factory::HasSSE4()) {
+      __m128i rect1 = _mm_loadu_si128((__m128i*)&aRect1); // x1, y1, w1, h1
+      __m128i rect2 = _mm_loadu_si128((__m128i*)&aRect2); // x2, y2, w2, h2
+
+      __m128i resultRect = _mm_max_epi32(rect1, rect2); // xr, yr, zz, zz
+      // result.width = std::min<int32_t>(x - result.x + width, aRect.x - result.x + aRect.width);
+      // result.height = std::min<int32_t>(y - result.y + height, aRect.y - result.y + aRect.height);
+      __m128i widthheight = _mm_min_epi32(_mm_add_epi32(_mm_sub_epi32(rect1, resultRect), _mm_srli_si128(rect1, 8)),
+                                          _mm_add_epi32(_mm_sub_epi32(rect2, resultRect), _mm_srli_si128(rect2, 8))); // w, h, zz, zz
+      widthheight = _mm_slli_si128(widthheight, 8); // 00, 00, wr, hr
+
+      resultRect = _mm_blend_epi16(resultRect, widthheight, 0xF0); // xr, yr, wr, hr
+
+      if ((_mm_movemask_ps(_mm_castsi128_ps(_mm_cmpgt_epi32(resultRect, _mm_setzero_si128()))) & 0xC) != 0xC) {
+        // It's potentially more efficient to store all 0s. But the non SSE4 code leaves x/y intact
+        // so let's do the same here.
+        resultRect = _mm_and_si128(resultRect, _mm_set_epi32(0, 0, 0xFFFFFFFF, 0xFFFFFFFF));
+        _mm_storeu_si128((__m128i*)this, resultRect);
+        return false;
+      }
+
+      _mm_storeu_si128((__m128i*)this, resultRect);
+
+      return true;
+    }
+    *static_cast<nsRect*>(this) = aRect1.Intersect(aRect2);
+    return !IsEmpty();
+  }
+#endif
 #endif
 
   void SaturatingUnionRect(const nsRect& aRect1, const nsRect& aRect2)
   {
     *this = aRect1.SaturatingUnion(aRect2);
   }
   void SaturatingUnionRectEdges(const nsRect& aRect1, const nsRect& aRect2)
   {
@@ -215,41 +292,124 @@ nsRect::ScaleToOtherAppUnitsRoundIn(int3
 }
 
 // scale the rect but round to preserve centers
 inline mozilla::gfx::IntRect
 nsRect::ScaleToNearestPixels(float aXScale, float aYScale,
                              nscoord aAppUnitsPerPixel) const
 {
   mozilla::gfx::IntRect rect;
+  // ASAN builds appear not to respect changes to the SSE rounding mode.
+  // Android x86 builds have bindgen issues.
+#if !defined(ANDROID) && !defined(MOZ_ASAN) && (defined(__SSE2__) || defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP >= 2))
+  __m128 appUnitsPacked = _mm_set_ps(aAppUnitsPerPixel, aAppUnitsPerPixel, aAppUnitsPerPixel, aAppUnitsPerPixel);
+  __m128 scalesPacked = _mm_set_ps(aYScale, aXScale, aYScale, aXScale);
+  __m128 biasesPacked = _mm_set_ps(0.5f, 0.5f, 0.5f, 0.5f);
+
+  // See Floor section.
+  _MM_SET_ROUNDING_MODE(_MM_ROUND_DOWN);
+
+  __m128i rectPacked = _mm_loadu_si128((__m128i*)this);
+  __m128i widthHeight = _mm_slli_si128(rectPacked, 8);
+
+  rectPacked = _mm_add_epi32(rectPacked, widthHeight); // X, Y, XMost(), YMost()
+
+  __m128 rectFloat = _mm_cvtepi32_ps(rectPacked);
+
+  // Scale, i.e. ([ x y xmost ymost ] / aAppUnitsPerPixel) * [ aXScale aYScale aXScale aYScale ]
+  rectFloat = _mm_div_ps(_mm_mul_ps(rectFloat, scalesPacked), appUnitsPacked);
+
+  // Floor
+  // Executed with bias and roundmode down, since round-nearest rounds 0.5 downward half the time.
+  rectFloat = _mm_add_ps(rectFloat, biasesPacked);
+  rectPacked = _mm_cvtps_epi32(rectFloat);
+
+  widthHeight = _mm_slli_si128(rectPacked, 8);
+  rectPacked = _mm_sub_epi32(rectPacked, widthHeight); // X, Y, Width, Height
+
+  // Avoid negative width/height due to overflow.
+  __m128i mask = _mm_or_si128(_mm_cmpgt_epi32(rectPacked, _mm_setzero_si128()),
+                              _mm_set_epi32(0, 0, 0xFFFFFFFF, 0xFFFFFFFF));
+  // Mask will now contain [ 0xFFFFFFFF 0xFFFFFFFF (width <= 0 ? 0 : 0xFFFFFFFF) (height <= 0 ? 0 : 0xFFFFFFFF) ]
+  rectPacked = _mm_and_si128(rectPacked, mask);
+
+  _mm_storeu_si128((__m128i*)&rect, rectPacked);
+
+  _MM_SET_ROUNDING_MODE(_MM_ROUND_NEAREST);
+#else
   rect.SetNonEmptyBox(NSToIntRoundUp(NSAppUnitsToDoublePixels(x,
                                      aAppUnitsPerPixel) * aXScale),
                       NSToIntRoundUp(NSAppUnitsToDoublePixels(y,
                                      aAppUnitsPerPixel) * aYScale),
                       NSToIntRoundUp(NSAppUnitsToDoublePixels(XMost(),
                                      aAppUnitsPerPixel) * aXScale),
                       NSToIntRoundUp(NSAppUnitsToDoublePixels(YMost(),
                                      aAppUnitsPerPixel) * aYScale));
+#endif
   return rect;
 }
 
 // scale the rect but round to smallest containing rect
 inline mozilla::gfx::IntRect
 nsRect::ScaleToOutsidePixels(float aXScale, float aYScale,
                              nscoord aAppUnitsPerPixel) const
 {
   mozilla::gfx::IntRect rect;
+  // ASAN builds appear not to respect changes to the SSE rounding mode.
+  // Android x86 builds have bindgen issues.
+#if !defined(ANDROID) && !defined(MOZ_ASAN) && (defined(__SSE2__) || defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP >= 2))
+  __m128 appUnitsPacked = _mm_set_ps(aAppUnitsPerPixel, aAppUnitsPerPixel, aAppUnitsPerPixel, aAppUnitsPerPixel);
+  __m128 scalesPacked = _mm_set_ps(aYScale, aXScale, aYScale, aXScale);
+
+  // This is a bit of a rough approximation, i.e. any value under x + (1 - FLT_EPSILON) will
+  // be rounded to x instead of x + 1, however this inaccuracy is much smaller than
+  // 1/256th (which would indicate any significant coverage of the pixel) and reduces
+  // the amount of cycles considerably.
+  __m128 biasesPacked = _mm_set_ps(1 - FLT_EPSILON, 1 - FLT_EPSILON, 0, 0);
+
+  // See Floor section.
+  _MM_SET_ROUNDING_MODE(_MM_ROUND_DOWN);
+
+  __m128i rectPacked = _mm_loadu_si128((__m128i*)this); // x, y, w, h
+  __m128i widthHeight = _mm_slli_si128(rectPacked, 8); // 0, 0, x, y
+
+  rectPacked = _mm_add_epi32(rectPacked, widthHeight); // X, Y, XMost(), YMost()
+
+  __m128 rectFloat = _mm_cvtepi32_ps(rectPacked);
+
+  // Scale i.e. ([ x y xmost ymost ] / aAppUnitsPerPixel) * [ aXScale aYScale aXScale aYScale ]
+  rectFloat = _mm_mul_ps(_mm_div_ps(rectFloat, appUnitsPacked), scalesPacked);
+
+  // Floor
+  // Executed with bias and roundmode down, since round-nearest rounds 0.5 downward half the time.
+  rectFloat = _mm_add_ps(rectFloat, biasesPacked);
+  rectPacked = _mm_cvtps_epi32(rectFloat); // r.x, r.y, r.XMost(), r.YMost()
+
+  widthHeight = _mm_slli_si128(rectPacked, 8); // 0, 0, r.x, r.y
+  rectPacked = _mm_sub_epi32(rectPacked, widthHeight); // r.x, r.y, r.w, r.h
+
+  // Avoid negative width/height due to overflow.
+  __m128i mask = _mm_or_si128(_mm_cmpgt_epi32(rectPacked, _mm_setzero_si128()),
+                              _mm_set_epi32(0, 0, 0xFFFFFFFF, 0xFFFFFFFF));
+  // Mask will now contain [ 0xFFFFFFFF 0xFFFFFFFF (width <= 0 ? 0 : 0xFFFFFFFF) (height <= 0 ? 0 : 0xFFFFFFFF) ]
+  rectPacked = _mm_and_si128(rectPacked, mask);
+
+  _mm_storeu_si128((__m128i*)&rect, rectPacked);
+
+  _MM_SET_ROUNDING_MODE(_MM_ROUND_NEAREST);
+#else
   rect.SetNonEmptyBox(NSToIntFloor(NSAppUnitsToFloatPixels(x,
                                    float(aAppUnitsPerPixel)) * aXScale),
                       NSToIntFloor(NSAppUnitsToFloatPixels(y,
                                    float(aAppUnitsPerPixel)) * aYScale),
                       NSToIntCeil(NSAppUnitsToFloatPixels(XMost(),
                                    float(aAppUnitsPerPixel)) * aXScale),
                       NSToIntCeil(NSAppUnitsToFloatPixels(YMost(),
                                    float(aAppUnitsPerPixel)) * aYScale));
+#endif
   return rect;
 }
 
 // scale the rect but round to largest contained rect
 inline mozilla::gfx::IntRect
 nsRect::ScaleToInsidePixels(float aXScale, float aYScale,
                             nscoord aAppUnitsPerPixel) const
 {
--- a/gfx/tests/gtest/TestRect.cpp
+++ b/gfx/tests/gtest/TestRect.cpp
@@ -537,16 +537,17 @@ TestIntersectionLogical(nscoord x1, nsco
                         bool isNonEmpty)
 {
   TestIntersectionLogicalHelper(x1, y1, w1, h1, x2, y2, w2, h2, xR, yR, wR, hR, isNonEmpty);
   TestIntersectionLogicalHelper(x2, y2, w2, h2, x1, y1, w1, h1, xR, yR, wR, hR, isNonEmpty);
 }
 
 TEST(Gfx, Logical)
 {
+  PR_Sleep(PR_SecondsToInterval(30));
   TestIntersectionLogical(578, 0, 2650, 1152, 1036, 0, 2312, 1, 1036, 0, 2192, 1, true);
   TestIntersectionLogical(0, 0, 1000, 1000, 500, 500, 1000, 1000, 500, 500, 500, 500, true);
   TestIntersectionLogical(100, 200, 300, 400, 50, 250, 100, 100, 100, 250, 50, 100, true);
   TestIntersectionLogical(0, 100, 200, 300, 300, 100, 100, 300, 300, 100, 0, 0, false);
 }
 
 TEST(Gfx, nsRect) {
   TestConstructors<nsRect>();
--- a/gfx/thebes/gfxDWriteFontList.cpp
+++ b/gfx/thebes/gfxDWriteFontList.cpp
@@ -185,16 +185,19 @@ gfxDWriteFontFamily::FindStyleVariations
         // <em> and <i> should be rendered as italic in the default style.
         if (fullID.EqualsLiteral("Meiryo Italic") ||
             fullID.EqualsLiteral("Meiryo Bold Italic")) {
             continue;
         }
 
         gfxDWriteFontEntry *fe = new gfxDWriteFontEntry(fullID, font, mIsSystemFontFamily);
         fe->SetForceGDIClassic(mForceGDIClassic);
+
+        fe->SetupVariationRanges();
+
         AddFontEntry(fe);
 
         // postscript/fullname if needed
         nsAutoString psname, fullname;
         if (fontInfoShouldHaveFaceNames) {
             aFontInfoData->GetFaceNames(fe->Name(), fullname, psname);
             if (!fullname.IsEmpty()) {
                 fp->AddFullname(fe, fullname);
@@ -214,23 +217,26 @@ gfxDWriteFontFamily::FindStyleVariations
             if (FAILED(hr)) {
                 skipFaceNames = true;
             } else if (fullname.Length() > 0) {
                 fp->AddFullname(fe, fullname);
             }
         }
 
         if (LOG_FONTLIST_ENABLED()) {
+            nsAutoCString weightString;
+            fe->Weight().ToString(weightString);
             LOG_FONTLIST(("(fontlist) added (%s) to family (%s)"
-                 " with style: %s weight: %g stretch: %d psname: %s fullname: %s",
+                 " with style: %s weight: %s stretch: %d psname: %s fullname: %s",
                  NS_ConvertUTF16toUTF8(fe->Name()).get(),
                  NS_ConvertUTF16toUTF8(Name()).get(),
                  (fe->IsItalic()) ?
                   "italic" : (fe->IsOblique() ? "oblique" : "normal"),
-                 fe->Weight().ToFloat(), fe->Stretch(),
+                 weightString.get(),
+                 fe->Stretch(),
                  NS_ConvertUTF16toUTF8(psname).get(),
                  NS_ConvertUTF16toUTF8(fullname).get()));
         }
     }
 
     // assume that if no error, all postscript/fullnames were initialized
     if (!skipFaceNames) {
         mFaceNamesInitialized = true;
@@ -664,46 +670,46 @@ gfxDWriteFontEntry::CreateFontInstance(c
     DWRITE_FONT_SIMULATIONS sims =
         aNeedsBold ? DWRITE_FONT_SIMULATIONS_BOLD : DWRITE_FONT_SIMULATIONS_NONE;
     if (HasVariations() && !aFontStyle->variationSettings.IsEmpty()) {
         // If we need to apply variations, we can't use the cached mUnscaledFont
         // or mUnscaledFontBold here.
         // XXX todo: consider caching a small number of variation instances?
         RefPtr<IDWriteFontFace> fontFace;
         nsresult rv = CreateFontFace(getter_AddRefs(fontFace),
-                                     &aFontStyle->variationSettings,
+                                     aFontStyle,
                                      sims);
         if (NS_FAILED(rv)) {
             return nullptr;
         }
         RefPtr<UnscaledFontDWrite> unscaledFont =
             new UnscaledFontDWrite(fontFace, mIsSystemFont ? mFont : nullptr, sims);
-        return new gfxDWriteFont(unscaledFont, this, aFontStyle, aNeedsBold);
+        return new gfxDWriteFont(unscaledFont, this, nullptr, aNeedsBold);
     }
 
     ThreadSafeWeakPtr<UnscaledFontDWrite>& unscaledFontPtr =
         aNeedsBold ? mUnscaledFontBold : mUnscaledFont;
     RefPtr<UnscaledFontDWrite> unscaledFont(unscaledFontPtr);
     if (!unscaledFont) {
         RefPtr<IDWriteFontFace> fontFace;
-        nsresult rv = CreateFontFace(getter_AddRefs(fontFace), nullptr, sims);
+        nsresult rv = CreateFontFace(getter_AddRefs(fontFace), aFontStyle, sims);
         if (NS_FAILED(rv)) {
             return nullptr;
         }
         unscaledFont =
             new UnscaledFontDWrite(fontFace,
                                    mIsSystemFont ? mFont : nullptr, sims);
         unscaledFontPtr = unscaledFont;
     }
     return new gfxDWriteFont(unscaledFont, this, aFontStyle, aNeedsBold);
 }
 
 nsresult
 gfxDWriteFontEntry::CreateFontFace(IDWriteFontFace **aFontFace,
-                                   const nsTArray<gfxFontVariation>* aVariations,
+                                   const gfxFontStyle* aFontStyle,
                                    DWRITE_FONT_SIMULATIONS aSimulations)
 {
     // Convert an OpenType font tag from our uint32_t representation
     // (as constructed by TRUETYPE_TAG(...)) to the order DWrite wants.
     auto makeDWriteAxisTag = [](uint32_t aTag) {
         return DWRITE_MAKE_FONT_AXIS_TAG((aTag >> 24) & 0xff,
                                          (aTag >> 16) & 0xff,
                                          (aTag >> 8) & 0xff,
@@ -761,46 +767,40 @@ gfxDWriteFontEntry::CreateFontFace(IDWri
 
     // Do we need to modify DWrite simulations from what mFontFace has?
     bool needSimulations =
         (aSimulations & DWRITE_FONT_SIMULATIONS_BOLD) &&
         !(mFontFace->GetSimulations() & DWRITE_FONT_SIMULATIONS_BOLD);
 
     // If the IDWriteFontFace5 interface is available, we can go via
     // IDWriteFontResource to create a new modified face.
-    if (mFontFace5 && (aVariations && !aVariations->IsEmpty() ||
+    if (mFontFace5 && ((aFontStyle && !aFontStyle->variationSettings.IsEmpty()) ||
+                       !Weight().IsSingle() ||
                        needSimulations)) {
         RefPtr<IDWriteFontResource> resource;
         HRESULT hr = mFontFace5->GetFontResource(getter_AddRefs(resource));
         MOZ_ASSERT(SUCCEEDED(hr));
         AutoTArray<DWRITE_FONT_AXIS_VALUE, 4> fontAxisValues;
-        if (aVariations) {
-            // Merge mVariationSettings and *aVariations if both present
-            const nsTArray<gfxFontVariation>* vars;
-            AutoTArray<gfxFontVariation,4> mergedSettings;
-            if (!aVariations) {
-                vars = &mVariationSettings;
-            } else  {
-                if (mVariationSettings.IsEmpty()) {
-                    vars = aVariations;
-                } else {
-                    gfxFontUtils::MergeVariations(mVariationSettings,
-                                                  *aVariations,
-                                                  &mergedSettings);
-                    vars = &mergedSettings;
-                }
-            }
-            for (const auto& v : *vars) {
+
+        // Get the variation settings needed to instantiate the fontEntry
+        // for a particular fontStyle.
+        AutoTArray<gfxFontVariation,4> vars;
+        GetVariationsForStyle(vars, *aFontStyle);
+
+        // Copy variation settings to DWrite's type.
+        if (!vars.IsEmpty()) {
+            for (const auto& v : vars) {
                 DWRITE_FONT_AXIS_VALUE axisValue = {
                     makeDWriteAxisTag(v.mTag),
                     v.mValue
                 };
                 fontAxisValues.AppendElement(axisValue);
             }
         }
+
         IDWriteFontFace5* ff5;
         resource->CreateFontFace(aSimulations,
                                  fontAxisValues.Elements(),
                                  fontAxisValues.Length(),
                                  &ff5);
         if (ff5) {
             *aFontFace = ff5;
         }
@@ -1146,23 +1146,26 @@ gfxDWriteFontList::InitFontListForPlatfo
             // add faces to Gill Sans MT
             for (i = 0; i < faces.Length(); i++) {
                 // change the entry's family name to match its adoptive family
                 faces[i]->mFamilyName = gillSansMTFamily->Name();
                 gillSansMTFamily->AddFontEntry(faces[i]);
 
                 if (LOG_FONTLIST_ENABLED()) {
                     gfxFontEntry *fe = faces[i];
+                    nsAutoCString weightString;
+                    fe->Weight().ToString(weightString);
                     LOG_FONTLIST(("(fontlist) moved (%s) to family (%s)"
-                         " with style: %s weight: %g stretch: %d",
+                         " with style: %s weight: %s stretch: %d",
                          NS_ConvertUTF16toUTF8(fe->Name()).get(),
                          NS_ConvertUTF16toUTF8(gillSansMTFamily->Name()).get(),
                          (fe->IsItalic()) ?
                           "italic" : (fe->IsOblique() ? "oblique" : "normal"),
-                         fe->Weight().ToFloat(), fe->Stretch()));
+                         weightString.get(),
+                         fe->Stretch()));
                 }
             }
 
             // remove Gills Sans
             mFontFamilies.Remove(nameGillSans);
         }
     }
 
--- a/gfx/thebes/gfxDWriteFontList.h
+++ b/gfx/thebes/gfxDWriteFontList.h
@@ -117,17 +117,17 @@ public:
         mStyle = (dwriteStyle == DWRITE_FONT_STYLE_ITALIC ?
                   NS_FONT_STYLE_ITALIC :
                   (dwriteStyle == DWRITE_FONT_STYLE_OBLIQUE ?
                    NS_FONT_STYLE_OBLIQUE : NS_FONT_STYLE_NORMAL));
         mStretch = FontStretchFromDWriteStretch(aFont->GetStretch());
         int weight = NS_ROUNDUP(aFont->GetWeight() - 50, 100);
 
         weight = mozilla::Clamp(weight, 100, 900);
-        mWeight = FontWeight(weight);
+        mWeightRange = WeightRange(FontWeight(weight));
 
         mIsCJK = UNINITIALIZED_VALUE;
     }
 
     /**
      * Constructs a font entry using a font. But with custom font values.
      * This is used for creating correct font entries for @font-face with local
      * font source.
@@ -142,17 +142,17 @@ public:
                               IDWriteFont *aFont,
                               FontWeight aWeight,
                               uint16_t aStretch,
                               uint8_t aStyle)
       : gfxFontEntry(aFaceName), mFont(aFont), mFontFile(nullptr),
         mIsSystemFont(false), mForceGDIClassic(false),
         mHasVariations(false), mHasVariationsInitialized(false)
     {
-        mWeight = aWeight;
+        mWeightRange = WeightRange(aWeight);
         mStretch = aStretch;
         mStyle = aStyle;
         mIsLocalUserFont = true;
         mIsCJK = UNINITIALIZED_VALUE;
     }
 
     /**
      * Constructs a font entry using a font file.
@@ -170,17 +170,17 @@ public:
                               FontWeight aWeight,
                               uint16_t aStretch,
                               uint8_t aStyle)
       : gfxFontEntry(aFaceName), mFont(nullptr),
         mFontFile(aFontFile), mFontFileStream(aFontFileStream),
         mIsSystemFont(false), mForceGDIClassic(false),
         mHasVariations(false), mHasVariationsInitialized(false)
     {
-        mWeight = aWeight;
+        mWeightRange = WeightRange(aWeight);
         mStretch = aStretch;
         mStyle = aStyle;
         mIsDataUserFont = true;
         mIsCJK = UNINITIALIZED_VALUE;
     }
 
     gfxFontEntry* Clone() const override;
 
@@ -211,17 +211,17 @@ protected:
     virtual nsresult CopyFontTable(uint32_t aTableTag,
                                    nsTArray<uint8_t>& aBuffer) override;
 
     virtual gfxFont *CreateFontInstance(const gfxFontStyle *aFontStyle,
                                         bool aNeedsBold);
     
     nsresult CreateFontFace(
         IDWriteFontFace **aFontFace,
-        const nsTArray<gfxFontVariation>* aVariations = nullptr,
+        const gfxFontStyle* aFontStyle = nullptr,
         DWRITE_FONT_SIMULATIONS aSimulations = DWRITE_FONT_SIMULATIONS_NONE);
 
     static bool InitLogFont(IDWriteFont *aFont, LOGFONTW *aLogFont);
 
     /**
      * A fontentry only needs to have either of these. If it has both only
      * the IDWriteFont will be used.
      */
--- a/gfx/thebes/gfxDWriteFonts.cpp
+++ b/gfx/thebes/gfxDWriteFonts.cpp
@@ -152,17 +152,18 @@ gfxDWriteFont::GetFakeMetricsForArialBla
 
     return true;
 }
 
 void
 gfxDWriteFont::ComputeMetrics(AntialiasOption anAAOption)
 {
     DWRITE_FONT_METRICS fontMetrics;
-    if (!(mFontEntry->Weight() == FontWeight(900) &&
+    if (!(mFontEntry->Weight().Min() == FontWeight(900) &&
+          mFontEntry->Weight().Max() == FontWeight(900) &&
           !mFontEntry->IsUserFont() &&
           mFontEntry->Name().EqualsLiteral("Arial Black") &&
           GetFakeMetricsForArialBlack(&fontMetrics)))
     {
         mFontFace->GetMetrics(&fontMetrics);
     }
 
     if (mStyle.sizeAdjust >= 0.0) {
--- a/gfx/thebes/gfxFT2FontBase.cpp
+++ b/gfx/thebes/gfxFT2FontBase.cpp
@@ -225,32 +225,23 @@ gfxFT2FontBase::InitMetrics()
         mMetrics.strikeoutOffset = 0.25 * emHeight;
         mMetrics.strikeoutSize = underlineSize;
 
         SanitizeMetrics(&mMetrics, false);
         return;
     }
 
     if ((!mFontEntry->mVariationSettings.IsEmpty() ||
-         !mStyle.variationSettings.IsEmpty()) &&
+         !mStyle.variationSettings.IsEmpty() ||
+         !mFontEntry->Weight().IsSingle()) &&
          (face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS)) {
         // Resolve variations from entry (descriptor) and style (property)
-        const nsTArray<gfxFontVariation>* settings;
-        AutoTArray<gfxFontVariation,8> mergedSettings;
-        if (mFontEntry->mVariationSettings.IsEmpty()) {
-            settings = &mStyle.variationSettings;
-        } else if (mStyle.variationSettings.IsEmpty()) {
-            settings = &mFontEntry->mVariationSettings;
-        } else {
-            gfxFontUtils::MergeVariations(mFontEntry->mVariationSettings,
-                                          mStyle.variationSettings,
-                                          &mergedSettings);
-            settings = &mergedSettings;
-        }
-        SetupVarCoords(face, *settings, &mCoords);
+        AutoTArray<gfxFontVariation,8> settings;
+        mFontEntry->GetVariationsForStyle(settings, mStyle);
+        SetupVarCoords(face, settings, &mCoords);
         if (!mCoords.IsEmpty()) {
 #if MOZ_TREE_FREETYPE
             FT_Set_Var_Design_Coordinates(face, mCoords.Length(), mCoords.Elements());
 #else
             typedef FT_Error (*SetCoordsFunc)(FT_Face, FT_UInt, FT_Fixed*);
             static SetCoordsFunc setCoords;
             static bool firstTime = true;
             if (firstTime) {
--- a/gfx/thebes/gfxFT2FontList.cpp
+++ b/gfx/thebes/gfxFT2FontList.cpp
@@ -219,17 +219,17 @@ FT2FontEntry::~FT2FontEntry()
 
 gfxFontEntry*
 FT2FontEntry::Clone() const
 {
     MOZ_ASSERT(!IsUserFont(), "we can only clone installed fonts!");
     FT2FontEntry* fe = new FT2FontEntry(Name());
     fe->mFilename = mFilename;
     fe->mFTFontIndex = mFTFontIndex;
-    fe->mWeight = mWeight;
+    fe->mWeightRange = mWeightRange;
     fe->mStretch = mStretch;
     fe->mStyle = mStyle;
     return fe;
 }
 
 gfxFont*
 FT2FontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold)
 {
@@ -278,17 +278,17 @@ FT2FontEntry::CreateFontEntry(const nsAS
     }
     // Create our FT2FontEntry, which inherits the name of the userfont entry
     // as it's not guaranteed that the face has valid names (bug 737315)
     FT2FontEntry* fe =
         FT2FontEntry::CreateFontEntry(face, nullptr, 0, aFontName,
                                       aFontData, aLength);
     if (fe) {
         fe->mStyle = aStyle;
-        fe->mWeight = aWeight;
+        fe->mWeightRange = WeightRange(aWeight);
         fe->mStretch = aStretch;
         fe->mIsDataUserFont = true;
     }
     return fe;
 }
 
 class FTUserFontData {
 public:
@@ -325,17 +325,18 @@ FTFontDestroyFunc(void *data)
 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->mWeightRange = WeightRange(FontWeight(aFLE.minWeight()),
+                                   FontWeight(aFLE.maxWeight()));
     fe->mStretch = aFLE.stretch();
     fe->mStyle = (aFLE.italic() ? NS_FONT_STYLE_ITALIC : NS_FONT_STYLE_NORMAL);
     return fe;
 }
 
 // Helpers to extract font entry properties from an FT_Face
 static bool
 FTFaceIsItalic(FT_Face aFace)
@@ -387,17 +388,17 @@ FT2FontEntry::CreateFontEntry(FT_Face aF
                               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);
-    fe->mWeight = FTFaceGetWeight(aFace);
+    fe->mWeightRange = WeightRange(FTFaceGetWeight(aFace));
     fe->mFilename = aFilename;
     fe->mFTFontIndex = aIndex;
 
     if (aFontData) {
         fe->mFTFace = aFace;
         int flags = gfxPlatform::GetPlatform()->FontHintingEnabled() ?
                     FT_LOAD_DEFAULT :
                     (FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING);
@@ -454,38 +455,27 @@ FT2FontEntry::CairoFontFace(const gfxFon
                                       userFontData, FTFontDestroyFunc);
         mFTFace = face.forget();
     }
 
     // If variations are present, we will not use our cached mFontFace
     // but always create a new cairo_font_face_t because its FT_Face will
     // have custom variation coordinates applied.
     if ((!mVariationSettings.IsEmpty() ||
-        (aStyle && !aStyle->variationSettings.IsEmpty())) &&
+        (aStyle && !aStyle->variationSettings.IsEmpty()) ||
+        !Weight().IsSingle()) &&
         (mFTFace->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS)) {
         int flags = gfxPlatform::GetPlatform()->FontHintingEnabled() ?
                     FT_LOAD_DEFAULT :
                     (FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING);
         // Resolve variations from entry (descriptor) and style (property)
-        const nsTArray<gfxFontVariation>* settings;
-        AutoTArray<gfxFontVariation,8> mergedSettings;
-        if (aStyle) {
-            if (mVariationSettings.IsEmpty()) {
-                settings = &aStyle->variationSettings;
-            } else {
-                gfxFontUtils::MergeVariations(mVariationSettings,
-                                              aStyle->variationSettings,
-                                              &mergedSettings);
-                settings = &mergedSettings;
-            }
-        } else {
-            settings = &mVariationSettings;
-        }
+        AutoTArray<gfxFontVariation,8> settings;
+        GetVariationsForStyle(settings, aStyle ? *aStyle : gfxFontStyle());
         AutoTArray<FT_Fixed,8> coords;
-        gfxFT2FontBase::SetupVarCoords(mFTFace, *settings, &coords);
+        gfxFT2FontBase::SetupVarCoords(mFTFace, settings, &coords);
         // Create a separate FT_Face because we need to apply custom
         // variation settings to it.
         FT_Face ftFace;
         if (!mFilename.IsEmpty()) {
             ftFace = Factory::NewFTFace(nullptr, mFilename.get(), mFTFontIndex);
         } else {
             auto ufd = reinterpret_cast<FTUserFontData*>(
                 cairo_font_face_get_user_data(mFontFace, &sFTUserFontDataKey));
@@ -663,17 +653,18 @@ FT2FontFamily::AddFacesToFontList(Infall
         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(),
                                                fe->mFilename,
-                                               fe->Weight().ToFloat(),
+                                               fe->Weight().Min().ToFloat(),
+                                               fe->Weight().Max().ToFloat(),
                                                fe->Stretch(),
                                                fe->mStyle,
                                                fe->mFTFontIndex));
     }
 }
 
 /*
  * Startup cache support for the font list:
@@ -958,25 +949,33 @@ gfxFT2FontList::AppendFacesFromCachedFac
         if (!(end = strchr(beginning, ','))) {
             break;
         }
         bool italic = (*beginning != '0');
         beginning = end + 1;
         if (!(end = strchr(beginning, ','))) {
             break;
         }
-        uint32_t weight = strtoul(beginning, nullptr, 10);
+        char* limit;
+        float minWeight = strtof(beginning, &limit);
+        float maxWeight;
+        if (*limit == ':' && limit + 1 < end) {
+            maxWeight = strtof(limit + 1, nullptr);
+        } else {
+            maxWeight = minWeight;
+        }
         beginning = end + 1;
         if (!(end = strchr(beginning, ','))) {
             break;
         }
         uint32_t stretch = strtoul(beginning, nullptr, 10);
 
         FontListEntry fle(familyName, faceName, aFileName,
-                          weight, stretch, italic, index);
+                          minWeight, maxWeight,
+                          stretch, italic, index);
         AppendFaceFromFontListEntry(fle, aStdFile);
 
         beginning = end + 1;
         end = strchr(beginning, ',');
     }
 }
 
 static void
@@ -986,17 +985,19 @@ AppendToFaceList(nsCString& aFaceList,
     aFaceList.Append(NS_ConvertUTF16toUTF8(aFamilyName));
     aFaceList.Append(',');
     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.AppendFloat(aFontEntry->Weight().Min().ToFloat());
+    aFaceList.Append(':');
+    aFaceList.AppendFloat(aFontEntry->Weight().Max().ToFloat());
     aFaceList.Append(',');
     aFaceList.AppendInt(aFontEntry->Stretch());
     aFaceList.Append(',');
 }
 
 void
 FT2FontEntry::CheckForBrokenFont(gfxFontFamily *aFamily)
 {
@@ -1144,22 +1145,25 @@ gfxFT2FontList::AddFaceToList(const nsCS
         }
         fe->mStandardFace = (aStdFile == kStandard);
         family->AddFontEntry(fe);
 
         fe->CheckForBrokenFont(family);
 
         AppendToFaceList(aFaceList, name, fe);
         if (LOG_ENABLED()) {
+            nsAutoCString weightString;
+            fe->Weight().ToString(weightString);
             LOG(("(fontinit) added (%s) to family (%s)"
-                 " with style: %s weight: %g stretch: %d",
+                 " with style: %s weight: %s stretch: %d",
                  NS_ConvertUTF16toUTF8(fe->Name()).get(),
                  NS_ConvertUTF16toUTF8(family->Name()).get(),
                  fe->IsItalic() ? "italic" : "normal",
-                 fe->Weight().ToFloat(), fe->Stretch()));
+                 weightString.get(),
+                 fe->Stretch()));
         }
     }
 }
 
 void
 gfxFT2FontList::AppendFacesFromOmnijarEntry(nsZipArchive* aArchive,
                                             const nsCString& aEntryName,
                                             FontNameCache *aCache,
@@ -1517,17 +1521,17 @@ searchDone:
 
     FT2FontEntry* fe =
         FT2FontEntry::CreateFontEntry(fontEntry->mFTFace,
                                       fontEntry->mFilename.get(),
                                       fontEntry->mFTFontIndex,
                                       fontEntry->Name(), nullptr);
     if (fe) {
         fe->mStyle = aStyle;
-        fe->mWeight = aWeight;
+        fe->mWeightRange = WeightRange(aWeight);
         fe->mStretch = aStretch;
         fe->mIsLocalUserFont = true;
     }
 
     return fe;
 }
 
 gfxFontFamily*
--- a/gfx/thebes/gfxFcPlatformFontList.cpp
+++ b/gfx/thebes/gfxFcPlatformFontList.cpp
@@ -245,17 +245,17 @@ gfxFontconfigFontEntry::gfxFontconfigFon
         mStyle = NS_FONT_STYLE_ITALIC;
     }
 
     // weight
     int weight;
     if (FcPatternGetInteger(aFontPattern, FC_WEIGHT, 0, &weight) != FcResultMatch) {
         weight = FC_WEIGHT_REGULAR;
     }
-    mWeight = MapFcWeight(weight);
+    mWeightRange = WeightRange(MapFcWeight(weight));
 
     // width
     int width;
     if (FcPatternGetInteger(aFontPattern, FC_WIDTH, 0, &width) != FcResultMatch) {
         width = FC_WIDTH_NORMAL;
     }
     mStretch = MapFcWidth(width);
 }
@@ -317,17 +317,17 @@ gfxFontconfigFontEntry::gfxFontconfigFon
                                                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)
 {
-    mWeight = aWeight;
+    mWeightRange = WeightRange(aWeight);
     mStyle = aStyle;
     mStretch = aStretch;
     mIsDataUserFont = true;
 
     mFontPattern = CreatePatternForFace(mFTFace);
 
     mUserFontData = new FTUserFontData(mFTFace, mFontData);
 }
@@ -336,17 +336,17 @@ gfxFontconfigFontEntry::gfxFontconfigFon
                                                FcPattern* aFontPattern,
                                                FontWeight aWeight,
                                                uint16_t aStretch,
                                                uint8_t aStyle)
         : gfxFontEntry(aFaceName), mFontPattern(aFontPattern),
           mFTFace(nullptr), mFTFaceInitialized(false),
           mAspect(0.0), mFontData(nullptr), mLength(0)
 {
-    mWeight = aWeight;
+    mWeightRange = WeightRange(aWeight);
     mStyle = aStyle;
     mStretch = aStretch;
     mIsLocalUserFont = true;
 
     // The proper setting of mIgnoreFcCharmap is tricky for fonts loaded
     // via src:local()...
     // If the local font happens to come from the application fontset,
     // we want to set it to true so that color/svg fonts will work even
@@ -759,32 +759,24 @@ gfxFontconfigFontEntry::CreateScaledFont
 
     if (needsOblique) {
         // disable embedded bitmaps (mimics behavior in 90-synthetic.conf)
         FcPatternDel(aRenderPattern, FC_EMBEDDED_BITMAP);
         FcPatternAddBool(aRenderPattern, FC_EMBEDDED_BITMAP, FcFalse);
     }
 
     AutoTArray<FT_Fixed,8> coords;
-    if (!aStyle->variationSettings.IsEmpty() || !mVariationSettings.IsEmpty()) {
+    if (!aStyle->variationSettings.IsEmpty() ||
+        !mVariationSettings.IsEmpty() ||
+        !Weight().IsSingle()) {
         FT_Face ftFace = GetFTFace();
         if (ftFace) {
-            const nsTArray<gfxFontVariation>* settings;
-            AutoTArray<gfxFontVariation,8> mergedSettings;
-            if (mVariationSettings.IsEmpty()) {
-                settings = &aStyle->variationSettings;
-            } else if (aStyle->variationSettings.IsEmpty()) {
-                settings = &mVariationSettings;
-            } else {
-                gfxFontUtils::MergeVariations(mVariationSettings,
-                                              aStyle->variationSettings,
-                                              &mergedSettings);
-                settings = &mergedSettings;
-            }
-            gfxFT2FontBase::SetupVarCoords(ftFace, *settings, &coords);
+            AutoTArray<gfxFontVariation,8> settings;
+            GetVariationsForStyle(settings, *aStyle);
+            gfxFT2FontBase::SetupVarCoords(ftFace, settings, &coords);
         }
     }
 
     cairo_font_face_t *face =
         cairo_ft_font_face_create_for_pattern(aRenderPattern,
                                               coords.Elements(),
                                               coords.Length());
 
@@ -1189,31 +1181,37 @@ gfxFontconfigFontFamily::FindStyleVariat
 
         // figure out the psname/fullname and choose which to use as the facename
         nsAutoString psname, fullname;
         GetFaceNames(face, mName, psname, fullname);
         const nsAutoString& faceName = !psname.IsEmpty() ? psname : fullname;
 
         gfxFontconfigFontEntry *fontEntry =
             new gfxFontconfigFontEntry(faceName, face, mContainsAppFonts);
+
+        fontEntry->SetupVariationRanges();
+
         AddFontEntry(fontEntry);
 
         if (fontEntry->IsNormalStyle()) {
             numRegularFaces++;
         }
 
         if (LOG_FONTLIST_ENABLED()) {
+            nsAutoCString weightString;
+            fontEntry->Weight().ToString(weightString);
             LOG_FONTLIST(("(fontlist) added (%s) to family (%s)"
-                 " with style: %s weight: %g stretch: %d"
+                 " with style: %s weight: %s stretch: %d"
                  " 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(),
+                 weightString.get(),
+                 fontEntry->Stretch(),
                  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) {
@@ -1308,17 +1306,18 @@ gfxFontconfigFontFamily::FindAllFontsFor
             static_cast<gfxFontconfigFontEntry*>(aFontEntryList[i]);
         double dist = SizeDistance(entry, aFontStyle,
                                    mForceScalable || aIgnoreSizeTolerance);
         // If the entry is scalable or has a style that does not match
         // the group of unscalable fonts, then start a new group.
         if (dist < 0.0 ||
             !bestEntry ||
             bestEntry->Stretch() != entry->Stretch() ||
-            bestEntry->Weight() != entry->Weight() ||
+            bestEntry->Weight().Min() != entry->Weight().Min() ||
+            bestEntry->Weight().Max() != entry->Weight().Max() ||
             bestEntry->mStyle != entry->mStyle) {
             // If the best entry in this group is still outside the tolerance,
             // then skip the entire group.
             if (bestDist >= kRejectDistance) {
                 skipped++;
             }
             // Remove any compacted entries from the previous group.
             if (skipped) {
@@ -2173,37 +2172,36 @@ gfxFcPlatformFontList::GetFTLibrary()
         // has been called on each FT_Face, at least until this bug is fixed:
         // https://bugs.freedesktop.org/show_bug.cgi?id=18857
         //
         // Cairo keeps it's own FT_Library object for creating FT_Face
         // instances, so use that. There's no simple API for accessing this
         // so use the hacky method below of making a font and extracting
         // the library pointer from that.
 
-        bool needsBold;
-        gfxFontStyle style;
-        gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
-        gfxFontFamily* family = pfl->GetDefaultFont(&style);
-        NS_ASSERTION(family, "couldn't find a default font family");
-        gfxFontEntry* fe = family->FindFontForStyle(style, needsBold, true);
-        if (!fe) {
-            return nullptr;
-        }
-        RefPtr<gfxFont> font = fe->FindOrMakeFont(&style, false);
-        if (!font) {
-            return nullptr;
-        }
+        FcPattern* pat =
+            FcPatternBuild(0, FC_FAMILY, FcTypeString, "serif", (char*)0);
+        cairo_font_face_t* face =
+            cairo_ft_font_face_create_for_pattern(pat, nullptr, 0);
+        FcPatternDestroy(pat);
 
-        gfxFT2FontBase* ft2Font = reinterpret_cast<gfxFT2FontBase*>(font.get());
-        gfxFT2LockedFace face(ft2Font);
-        if (!face.get()) {
-            return nullptr;
-        }
+        cairo_matrix_t identity;
+        cairo_matrix_init_identity(&identity);
+        cairo_font_options_t* options = cairo_font_options_create();
+        cairo_scaled_font_t* sf =
+            cairo_scaled_font_create(face, &identity, &identity, options);
+        cairo_font_options_destroy(options);
+        cairo_font_face_destroy(face);
 
-        sCairoFTLibrary = face.get()->glyph->library;
+        FT_Face ft = cairo_ft_scaled_font_lock_face(sf);
+
+        sCairoFTLibrary = ft->glyph->library;
+
+        cairo_ft_scaled_font_unlock_face(sf);
+        cairo_scaled_font_destroy(sf);
     }
 
     return sCairoFTLibrary;
 }
 
 gfxPlatformFontList::PrefFontList*
 gfxFcPlatformFontList::FindGenericFamilies(const nsAString& aGeneric,
                                            nsAtom* aLanguage)
--- a/gfx/thebes/gfxFontEntry.cpp
+++ b/gfx/thebes/gfxFontEntry.cpp
@@ -75,17 +75,18 @@ 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),
+    mWeightRange(FontWeight(500)),
+    mStretch(NS_FONT_STRETCH_NORMAL),
     mUVSOffset(0), mUVSData(nullptr),
     mLanguageOverride(NO_FONT_LANGUAGE_OVERRIDE),
     mCOLR(nullptr),
     mCPAL(nullptr),
     mUnitsPerEm(0),
     mHBFace(nullptr),
     mGrFace(nullptr),
     mGrFaceRefCnt(0),
@@ -112,17 +113,18 @@ 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),
+    mWeightRange(FontWeight(500)),
+    mStretch(NS_FONT_STRETCH_NORMAL),
     mUVSOffset(0), mUVSData(nullptr),
     mLanguageOverride(NO_FONT_LANGUAGE_OVERRIDE),
     mCOLR(nullptr),
     mCPAL(nullptr),
     mUnitsPerEm(0),
     mHBFace(nullptr),
     mGrFace(nullptr),
     mGrFaceRefCnt(0),
@@ -1023,16 +1025,96 @@ gfxFontEntry::GetColorLayersInfo(uint32_
     return gfxFontUtils::GetColorGlyphLayers(mCOLR,
                                              mCPAL,
                                              aGlyphId,
                                              aDefaultColor,
                                              aLayerGlyphs,
                                              aLayerColors);
 }
 
+void
+gfxFontEntry::SetupVariationRanges()
+{
+    if (!HasVariations() || IsUserFont()) {
+        return;
+    }
+    AutoTArray<gfxFontVariationAxis,4> axes;
+    GetVariationAxes(axes);
+    for (const auto& axis : axes) {
+        switch (axis.mTag) {
+        case HB_TAG('w','g','h','t'):
+            // If the axis range looks like it doesn't fit the CSS font-weight
+            // scale, we don't hook up the high-level property. Setting 'wght'
+            // with font-variation-settings will still work.
+            // Strictly speaking, the min value should be checked against 1.0,
+            // not 0.0, but we'll allow font makers that amount of leeway, as
+            // in practice a number of fonts seem to use 0..1000.
+            if (axis.mMinValue >= 0.0f && axis.mMaxValue <= 1000.0 &&
+                // If axis.mMaxValue is less than the default weight we already
+                // set up, assume the axis has a non-standard range (like Skia)
+                // and don't try to map it.
+                Weight().Min() <= FontWeight(axis.mMaxValue)) {
+                mStandardFace = FontWeight(axis.mDefaultValue) == Weight().Min();
+                mWeightRange =
+                    WeightRange(FontWeight(std::max(1.0f, axis.mMinValue)),
+                                FontWeight(axis.mMaxValue));
+            }
+            break;
+        // XXX todo:
+        // case HB_TAG('w','d','t','h'):
+        // case HB_TAG('s','l','n','t'):
+        // case HB_TAG('i','t','a','l'):
+        default:
+            continue;
+        }
+    }
+}
+
+void
+gfxFontEntry::GetVariationsForStyle(nsTArray<gfxFontVariation>& aResult,
+                                    const gfxFontStyle& aStyle)
+{
+    // Resolve high-level CSS properties from the requested style
+    // (font-{style,weight,stretch}) to the appropriate variations.
+    if (!Weight().IsSingle()) {
+        float clampedWeight = Weight().Clamp(aStyle.weight).ToFloat();
+        aResult.AppendElement(gfxFontVariation{HB_TAG('w','g','h','t'),
+                                               clampedWeight});
+    }
+
+    // XXX todo: 'wdth', 'slnt', 'ital'
+
+    auto replaceOrAppend = [&aResult](const gfxFontVariation& aSetting) {
+        struct TagEquals {
+            bool Equals(const gfxFontVariation& aIter, uint32_t aTag) const {
+                return aIter.mTag == aTag;
+            }
+        };
+        auto index = aResult.IndexOf(aSetting.mTag, 0, TagEquals());
+        if (index == aResult.NoIndex) {
+            aResult.AppendElement(aSetting);
+        } else {
+            aResult[index].mValue = aSetting.mValue;
+        }
+    };
+
+    // The low-level font-variation-settings descriptor from @font-face,
+    // if present, takes precedence over automatic variation settings
+    // from high-level properties.
+    for (const auto& v : mVariationSettings) {
+        replaceOrAppend(v);
+    }
+
+    // And the low-level font-variation-settings property takes precedence
+    // over the descriptor.
+    for (const auto& v : aStyle.variationSettings) {
+        replaceOrAppend(v);
+    }
+}
+
 size_t
 gfxFontEntry::FontTableHashEntry::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
 {
     size_t n = 0;
     if (mBlob) {
         n += aMallocSizeOf(mBlob);
     }
     if (mSharedBlobData) {
@@ -1231,43 +1313,45 @@ StretchDistance(int32_t aFontStretch, in
 // weights are farther away than lighter weights. If the target is 5 and the
 // font weight 995, the distance would be 1590 for the same reason.
 
 #define REVERSE_WEIGHT_DISTANCE 600
 #define WEIGHT_SHIFT             11 // number of bits to contain weight distance
 
 // weight distance ==> [0,1598]
 static inline uint32_t
-WeightDistance(FontWeight aFontWeight, FontWeight aTargetWeight)
+WeightDistance(const gfxFontEntry* aFontEntry, FontWeight aTargetWeight)
 {
     // Compute a measure of the "distance" between the requested
     // weight and the given fontEntry
 
     float distance = 0.0f, addedDistance = 0.0f;
-    if (aTargetWeight != aFontWeight) {
+    FontWeight minWeight = aFontEntry->Weight().Min();
+    FontWeight maxWeight = aFontEntry->Weight().Max();
+    if (aTargetWeight < minWeight || aTargetWeight > maxWeight) {
         if (aTargetWeight > FontWeight(500)) {
-            distance = aFontWeight - aTargetWeight;
+            distance = minWeight - aTargetWeight;
         } else if (aTargetWeight < FontWeight(400)) {
-            distance = aTargetWeight - aFontWeight;
+            distance = aTargetWeight - maxWeight;
         } else {
             // special case - target is between 400 and 500
 
             // font weights between 400 and 500 are close
-            if (aFontWeight >= FontWeight(400) &&
-                aFontWeight <= FontWeight(500)) {
-                if (aFontWeight < aTargetWeight) {
-                    distance = FontWeight(500) - aFontWeight;
+            if (maxWeight >= FontWeight(400) &&
+                minWeight <= FontWeight(500)) {
+                if (maxWeight < aTargetWeight) {
+                    distance = FontWeight(500) - maxWeight;
                 } else {
-                    distance = aFontWeight - aTargetWeight;
+                    distance = minWeight - aTargetWeight;
                 }
             } else {
                 // font weights outside use rule for target weights < 400 with
                 // added distance to separate from font weights in
                 // the [400..500] range
-                distance = aTargetWeight - aFontWeight;
+                distance = aTargetWeight - maxWeight;
                 addedDistance = 100;
             }
         }
         if (distance < 0.0f) {
             distance = -distance + REVERSE_WEIGHT_DISTANCE;
         }
         distance += addedDistance;
     }
@@ -1279,18 +1363,17 @@ WeightDistance(FontWeight aFontWeight, F
 static inline uint32_t
 WeightStyleStretchDistance(gfxFontEntry* aFontEntry,
                            const gfxFontStyle& aTargetStyle)
 {
     // weight/style/stretch priority: stretch >> style >> weight
     uint32_t stretchDist =
         StretchDistance(aFontEntry->mStretch, aTargetStyle.stretch);
     uint32_t styleDist = StyleDistance(aFontEntry->mStyle, aTargetStyle.style);
-    uint32_t weightDist =
-        WeightDistance(aFontEntry->Weight(), aTargetStyle.weight);
+    uint32_t weightDist = WeightDistance(aFontEntry, aTargetStyle.weight);
 
     NS_ASSERTION(weightDist < (1 << WEIGHT_SHIFT), "weight value out of bounds");
     NS_ASSERTION(styleDist < (1 << STYLE_SHIFT), "slope value out of bounds");
 
     return (stretchDist << (STYLE_SHIFT + WEIGHT_SHIFT)) |
            (styleDist << WEIGHT_SHIFT) |
            weightDist;
 }
@@ -1439,18 +1522,21 @@ gfxFontFamily::CheckForSimpleFamily()
 
     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;
         }
+        if (fe->Weight().Min() != fe->Weight().Max()) {
+            return; // family with variation fonts is not considered "simple"
+        }
         uint8_t faceIndex = (fe->IsItalic() ? kItalicMask : 0) |
-                            (fe->Weight() >= FontWeight(600) ? kBoldMask : 0);
+                            (fe->IsBold() ? kBoldMask : 0);
         if (faces[faceIndex]) {
             return; // two faces resolve to the same slot; family isn't "simple"
         }
         faces[faceIndex] = fe;
     }
 
     // we have successfully slotted the available faces into the standard
     // 4-face framework
@@ -1487,43 +1573,51 @@ gfxFontFamily::ContainsFace(gfxFontEntry
 
 void gfxFontFamily::LocalizedName(nsAString& aLocalizedName)
 {
     // just return the primary name; subclasses should override
     aLocalizedName = mName;
 }
 
 // metric for how close a given font matches a style
-static int32_t
+static float
 CalcStyleMatch(gfxFontEntry *aFontEntry, const gfxFontStyle *aStyle)
 {
-    int32_t rank = 0;
+    float rank = 0;
     if (aStyle) {
-         // italics
-         bool wantUpright = (aStyle->style == NS_FONT_STYLE_NORMAL);
-         if (aFontEntry->IsUpright() == wantUpright) {
-             rank += 10;
-         }
+        // TODO: stretch
+
+        // italics
+        bool wantUpright = (aStyle->style == NS_FONT_STYLE_NORMAL);
+        if (aFontEntry->IsUpright() == wantUpright) {
+            rank += 5000.0f;
+        }
 
         // measure of closeness of weight to the desired value
-        rank += 9 - Abs((aFontEntry->Weight() - aStyle->weight) / 100.0f);
+        if (aFontEntry->Weight().Min() > aStyle->weight) {
+            rank += aFontEntry->Weight().Min() - aStyle->weight;
+        } else if (aFontEntry->Weight().Max() < aStyle->weight) {
+            rank += aStyle->weight - aFontEntry->Weight().Max();
+        } else {
+            rank += 2000.0f; // the font supports the exact weight wanted
+        }
     } else {
         // if no font to match, prefer non-bold, non-italic fonts
         if (aFontEntry->IsUpright()) {
-            rank += 3;
+            rank += 2000.0f;
         }
         if (!aFontEntry->IsBold()) {
-            rank += 2;
+            rank += 1000.0f;
         }
     }
 
     return rank;
 }
 
-#define RANK_MATCHED_CMAP   20
+#define RANK_MATCHED_CMAP   10000.0f
 
 void
 gfxFontFamily::FindFontForChar(GlobalFontMatch *aMatchData)
 {
     if (mFamilyCharacterMapInitialized && !TestCharacterMap(aMatchData->mCh)) {
         // none of the faces in the family support the required char,
         // so bail out immediately
         return;
@@ -1531,17 +1625,17 @@ gfxFontFamily::FindFontForChar(GlobalFon
 
     bool needsBold;
     gfxFontEntry *fe =
         FindFontForStyle(aMatchData->mStyle ? *aMatchData->mStyle
                                             : gfxFontStyle(),
                          needsBold, true);
 
     if (fe && !fe->SkipDuringSystemFallback()) {
-        int32_t rank = 0;
+        float rank = 0;
 
         if (fe->HasCharacter(aMatchData->mCh)) {
             rank += RANK_MATCHED_CMAP;
             aMatchData->mCount++;
 
             LogModule* log = gfxPlatform::GetLog(eGfxLog_textrun);
 
             if (MOZ_UNLIKELY(MOZ_LOG_TEST(log, LogLevel::Debug))) {
@@ -1591,17 +1685,17 @@ gfxFontFamily::FindFontForChar(GlobalFon
 
 void
 gfxFontFamily::SearchAllFontsForChar(GlobalFontMatch *aMatchData)
 {
     uint32_t i, numFonts = mAvailableFonts.Length();
     for (i = 0; i < numFonts; i++) {
         gfxFontEntry *fe = mAvailableFonts[i];
         if (fe && fe->HasCharacter(aMatchData->mCh)) {
-            int32_t rank = RANK_MATCHED_CMAP;
+            float rank = RANK_MATCHED_CMAP;
             rank += CalcStyleMatch(fe, aMatchData->mStyle);
             if (rank > aMatchData->mMatchRank
                 || (rank == aMatchData->mMatchRank &&
                     Compare(fe->Name(), aMatchData->mBestMatch->Name()) > 0))
             {
                 aMatchData->mBestMatch = fe;
                 aMatchData->mMatchedFamily = this;
                 aMatchData->mMatchRank = rank;
--- a/gfx/thebes/gfxFontEntry.h
+++ b/gfx/thebes/gfxFontEntry.h
@@ -106,16 +106,17 @@ struct gfxFontFeatureInfo {
     uint32_t mTag;
     uint32_t mScript;
     uint32_t mLangSys;
 };
 
 class gfxFontEntry {
 public:
     typedef mozilla::FontWeight FontWeight;
+    typedef mozilla::WeightRange WeightRange;
     typedef mozilla::gfx::DrawTarget DrawTarget;
     typedef mozilla::unicode::Script Script;
 
     // Used by stylo
     NS_INLINE_DECL_THREADSAFE_REFCOUNTING(gfxFontEntry)
 
     explicit gfxFontEntry(const nsAString& aName, bool aIsStandardFace = false);
 
@@ -136,39 +137,40 @@ public:
     // will (usually, except on Linux) load and parse the 'name' table;
     // 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; }
+    WeightRange Weight() const { return mWeightRange; }
     uint16_t 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 IsBold() const { return mWeight.IsBold(); } // bold == weights 600 and above
+    bool IsBold() const { return Weight().Max().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() &&
+               Weight().Min() <= FontWeight::Normal() &&
+               Weight().Max() >= FontWeight::Normal() &&
                Stretch() == NS_FONT_STRETCH_NORMAL;
     }
 
     // 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);
 
@@ -363,16 +365,26 @@ public:
     }
     virtual void GetVariationAxes(nsTArray<gfxFontVariationAxis>& aVariationAxes)
     {
     }
     virtual void GetVariationInstances(nsTArray<gfxFontVariationInstance>& aInstances)
     {
     }
 
+    // Set up the entry's weight/stretch/style ranges according to axes found
+    // by GetVariationAxes (for installed fonts; do NOT call this for user
+    // fonts, where the ranges are provided by @font-face descriptors).
+    void SetupVariationRanges();
+
+    // Get variation axis settings that should be used to implement a particular
+    // font style using this resource.
+    void GetVariationsForStyle(nsTArray<gfxFontVariation>& aResult,
+                               const gfxFontStyle& aStyle);
+
     // 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;
@@ -399,17 +411,17 @@ 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;
+    WeightRange      mWeightRange;
     uint16_t         mStretch;
 
     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
@@ -605,24 +617,24 @@ private:
 };
 
 
 // used when iterating over all fonts looking for a match for a given character
 struct GlobalFontMatch {
     GlobalFontMatch(const uint32_t aCharacter,
                     const gfxFontStyle *aStyle) :
         mCh(aCharacter), mStyle(aStyle),
-        mMatchRank(0), mCount(0), mCmapsTested(0)
+        mMatchRank(0.0f), mCount(0), mCmapsTested(0)
         {
 
         }
 
     const uint32_t         mCh;          // codepoint to be matched
     const gfxFontStyle*    mStyle;       // style to match
-    int32_t                mMatchRank;   // metric indicating closest match
+    float                  mMatchRank;   // metric indicating closest match
     RefPtr<gfxFontEntry> mBestMatch;   // current best match
     RefPtr<gfxFontFamily> mMatchedFamily; // the family it belongs to
     uint32_t               mCount;       // number of fonts matched
     uint32_t               mCmapsTested; // number of cmaps tested
 };
 
 class gfxFontFamily {
 public:
--- a/gfx/thebes/gfxFontUtils.cpp
+++ b/gfx/thebes/gfxFontUtils.cpp
@@ -1939,34 +1939,16 @@ gfxFontUtils::GetVariationInstances(gfxF
             value.mAxis = axes[j].axisTag;
             value.mValue = int32_t(coords[j]) / 65536.0;
             instance.mValues.AppendElement(value);
         }
         aInstances.AppendElement(instance);
     }
 }
 
-void
-gfxFontUtils::MergeVariations(const nsTArray<gfxFontVariation>& aEntrySettings,
-                              const nsTArray<gfxFontVariation>& aStyleSettings,
-                              nsTArray<gfxFontVariation>* aMerged)
-{
-    MOZ_ASSERT(!aEntrySettings.IsEmpty() &&
-               !aStyleSettings.IsEmpty() &&
-               aMerged->IsEmpty());
-    // Settings from the CSS style will take precedence over those from the
-    // font entry (i.e. from the @font-face descriptor).
-    aMerged->AppendElements(aStyleSettings);
-    for (auto& setting : aEntrySettings) {
-        if (!aMerged->Contains(setting.mTag, VariationTagComparator())) {
-            aMerged->AppendElement(setting);
-        }
-    }
-}
-
 #ifdef XP_WIN
 
 /* static */
 bool
 gfxFontUtils::IsCffFont(const uint8_t* aFontData)
 {
     // this is only called after aFontData has passed basic validation,
     // so we know there is enough data present to allow us to read the version!
--- a/gfx/thebes/gfxFontUtils.h
+++ b/gfx/thebes/gfxFontUtils.h
@@ -1005,32 +1005,16 @@ public:
     // platforms where the native font APIs don't provide the info we want
     // in a convenient form.
     // (Not used on platforms -- currently, freetype -- where the font APIs
     // expose variation instance details directly.)
     static void
     GetVariationInstances(gfxFontEntry* aFontEntry,
                           nsTArray<gfxFontVariationInstance>& aInstances);
 
-    // Merge a list of font-variation-settings from a font entry and a list
-    // from a gfxFontStyle, to get a combined collection of settings that can
-    // be used to instantiate a font.
-    static void
-    MergeVariations(const nsTArray<gfxFontVariation>& aEntrySettings,
-                    const nsTArray<gfxFontVariation>& aStyleSettings,
-                    nsTArray<gfxFontVariation>* aMerged);
-
-    // Helper used by MergeVariations, and other code that wants to check
-    // whether an array of variation settings includes a particular tag.
-    struct VariationTagComparator {
-        bool Equals(const gfxFontVariation& aVariation, uint32_t aTag) const {
-            return aVariation.mTag == aTag;
-        }
-    };
-
 protected:
     friend struct MacCharsetMappingComparator;
 
     static nsresult
     ReadNames(const char *aNameData, uint32_t aDataLen, uint32_t aNameID,
               int32_t aLangID, int32_t aPlatformID, nsTArray<nsString>& aNames);
 
     // convert opentype name-table platform/encoding/language values to an
--- a/gfx/thebes/gfxGDIFont.cpp
+++ b/gfx/thebes/gfxGDIFont.cpp
@@ -455,17 +455,20 @@ gfxGDIFont::FillLogFont(LOGFONTW& aLogFo
             // choice of actual face used (bug 724231)
             weight = 0;
         } else {
             // avoid GDI synthetic bold which occurs when weight
             // specified is >= font data weight + 200
             weight = mNeedsBold ? 700 : 200;
         }
     } else {
-        weight = mNeedsBold ? 700 : fe->Weight().ToIntRounded();
+        // GDI doesn't support variation fonts, so for system fonts we know
+        // that the entry has only a single weight, not a range.
+        MOZ_ASSERT(fe->Weight().IsSingle());
+        weight = mNeedsBold ? 700 : fe->Weight().Min().ToIntRounded();
     }
 
     fe->FillLogFont(&aLogFont, weight, aSize);
 }
 
 uint32_t
 gfxGDIFont::GetGlyph(uint32_t aUnicode, uint32_t aVarSelector)
 {
--- a/gfx/thebes/gfxGDIFontList.cpp
+++ b/gfx/thebes/gfxGDIFontList.cpp
@@ -121,30 +121,32 @@ GDIFontEntry::GDIFontEntry(const nsAStri
                            gfxUserFontData *aUserFontData)
     : gfxFontEntry(aFaceName),
       mFontType(aFontType),
       mForceGDI(false),
       mUnicodeRanges()
 {
     mUserFontData.reset(aUserFontData);
     mStyle = aStyle;
-    mWeight = aWeight;
+    mWeightRange = WeightRange(aWeight);
     mStretch = aStretch;
     if (IsType1())
         mForceGDI = true;
     mIsDataUserFont = aUserFontData != nullptr;
 
     InitLogFont(aFaceName, aFontType);
 }
 
 gfxFontEntry*
 GDIFontEntry::Clone() const
 {
     MOZ_ASSERT(!IsUserFont(), "we can only clone installed fonts!");
-    return new GDIFontEntry(Name(), mFontType, mStyle, mWeight, mStretch,
+    // GDI fonts don't support variations, so we don't need to worry about
+    // cloning the weight as a range.
+    return new GDIFontEntry(Name(), mFontType, mStyle, Weight().Min(), mStretch,
                             nullptr);
 }
 
 nsresult
 GDIFontEntry::ReadCMAP(FontInfoData *aFontInfoData)
 {
     AUTO_PROFILER_LABEL("GDIFontEntry::ReadCMAP", OTHER);
 
@@ -300,17 +302,17 @@ GDIFontEntry::TestCharacterMap(uint32_t 
         if (aCh > 0xFFFF)
             return false;
 
         // previous code was using the group style
         gfxFontStyle fakeStyle;
         if (!IsUpright()) {
             fakeStyle.style = NS_FONT_STYLE_ITALIC;
         }
-        fakeStyle.weight = mWeight;
+        fakeStyle.weight = Weight().Min();
 
         RefPtr<gfxFont> tempFont = FindOrMakeFont(&fakeStyle, false);
         if (!tempFont || !tempFont->Valid())
             return false;
         gfxGDIFont *font = static_cast<gfxGDIFont*>(tempFont.get());
 
         HDC dc = GetDC((HWND)nullptr);
         SetGraphicsMode(dc, GM_ADVANCED);
@@ -379,17 +381,17 @@ GDIFontEntry::InitLogFont(const nsAStrin
     mLogFont.lfClipPrecision  = CLIP_TURNOFF_FONTASSOCIATION;
     mLogFont.lfQuality        = DEFAULT_QUALITY;
     mLogFont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
     // always force lfItalic if we want it.  Font selection code will
     // do its best to give us an italic font entry, but if no face exists
     // it may give us a regular one based on weight.  Windows should
     // do fake italic for us in that case.
     mLogFont.lfItalic         = !IsUpright();
-    mLogFont.lfWeight         = int(mWeight.ToFloat());
+    mLogFont.lfWeight         = Weight().Min().ToIntRounded();
 
     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,
@@ -462,17 +464,17 @@ GDIFontFamily::FamilyAddStylesProc(const
             // otherwise if the new type is worse, skip it
             return 1;
         }
     }
 
     for (uint32_t i = 0; i < ff->mAvailableFonts.Length(); ++i) {
         fe = static_cast<GDIFontEntry*>(ff->mAvailableFonts[i].get());
         // check if we already know about this face
-        if (fe->mWeight == FontWeight(int32_t(logFont.lfWeight)) &&
+        if (fe->Weight().Min() == FontWeight(int32_t(logFont.lfWeight)) &&
             fe->IsItalic() == (logFont.lfItalic == 0xFF)) {
             // update the charset bit here since this could be different
             // XXX Can we still do this now that we store mCharset
             // on the font family rather than the font entry?
             ff->mCharset.set(metrics.tmCharSet);
             return 1; 
         }
     }
@@ -724,25 +726,25 @@ gfxGDIFontList::LookupLocalFont(const ns
     bool isCFF = false; // jtdfix -- need to determine this
     
     // use the face name from the lookup font entry, which will be the localized
     // face name which GDI mapping tables use (e.g. with the system locale set to
     // Dutch, a fullname of 'Arial Bold' will find a font entry with the face name
     // 'Arial Vet' which can be used as a key in GDI font lookups).
     GDIFontEntry *fe = GDIFontEntry::CreateFontEntry(lookup->Name(), 
         gfxWindowsFontType(isCFF ? GFX_FONT_TYPE_PS_OPENTYPE : GFX_FONT_TYPE_TRUETYPE) /*type*/, 
-        lookup->mStyle, lookup->mWeight, aStretch, nullptr);
+        lookup->mStyle, lookup->Weight().Min(), aStretch, nullptr);
 
     if (!fe)
         return nullptr;
 
     fe->mIsLocalUserFont = true;
 
     // make the new font entry match the userfont entry style characteristics
-    fe->mWeight = aWeight;
+    fe->mWeightRange = WeightRange(aWeight);
     fe->mStyle = aStyle;
 
     return fe;
 }
 
 // If aFontData contains only a MS/Symbol cmap subtable, not MS/Unicode,
 // we modify the subtable header to mark it as Unicode instead, because
 // otherwise GDI will refuse to load the font.
--- a/gfx/thebes/gfxMacFont.cpp
+++ b/gfx/thebes/gfxMacFont.cpp
@@ -32,43 +32,33 @@ gfxMacFont::gfxMacFont(const RefPtr<Unsc
       mCTFont(nullptr),
       mFontFace(nullptr),
       mFontSmoothingBackgroundColor(aFontStyle->fontSmoothingBackgroundColor),
       mVariationFont(aFontEntry->HasVariations())
 {
     mApplySyntheticBold = aNeedsBold;
 
     if (mVariationFont && (!aFontStyle->variationSettings.IsEmpty() ||
-                           !aFontEntry->mVariationSettings.IsEmpty())) {
+                           !aFontEntry->mVariationSettings.IsEmpty() ||
+                           !aFontEntry->Weight().IsSingle())) {
         CGFontRef baseFont = aUnscaledFont->GetFont();
         if (!baseFont) {
             mIsValid = false;
             return;
         }
 
-        // Probably one of the lists of variations, either from the @font-face
-        // descriptor or from the property, will be empty. So skip merging them
-        // unless really necessary.
-        const nsTArray<gfxFontVariation>* vars;
-        AutoTArray<gfxFontVariation,4> mergedSettings;
-        if (aFontStyle->variationSettings.IsEmpty()) {
-            vars = &aFontEntry->mVariationSettings;
-        } else if (aFontEntry->mVariationSettings.IsEmpty()) {
-            vars = &aFontStyle->variationSettings;
-        } else {
-            gfxFontUtils::MergeVariations(aFontEntry->mVariationSettings,
-                                          aFontStyle->variationSettings,
-                                          &mergedSettings);
-            vars = &mergedSettings;
-        }
+        // Get the variation settings needed to instantiate the fontEntry
+        // for a particular fontStyle.
+        AutoTArray<gfxFontVariation,4> vars;
+        aFontEntry->GetVariationsForStyle(vars, *aFontStyle);
 
         mCGFont =
             UnscaledFontMac::CreateCGFontWithVariations(baseFont,
-                                                        vars->Length(),
-                                                        vars->Elements());
+                                                        vars.Length(),
+                                                        vars.Elements());
         if (!mCGFont) {
           ::CFRetain(baseFont);
           mCGFont = baseFont;
         }
     } else {
         mCGFont = aUnscaledFont->GetFont();
         if (!mCGFont) {
             mIsValid = false;
--- a/gfx/thebes/gfxMacPlatformFontList.mm
+++ b/gfx/thebes/gfxMacPlatformFontList.mm
@@ -378,17 +378,17 @@ MacOSFontEntry::MacOSFontEntry(const nsA
       mHasVariationsInitialized(false),
       mHasAATSmallCaps(false),
       mHasAATSmallCapsInitialized(false),
       mCheckedForTracking(false),
       mTrakTable(nullptr),
       mTrakValues(nullptr),
       mTrakSizeTable(nullptr)
 {
-    mWeight = aWeight;
+    mWeightRange = WeightRange(aWeight);
 }
 
 MacOSFontEntry::MacOSFontEntry(const nsAString& aPostscriptName,
                                CGFontRef aFontRef,
                                FontWeight aWeight,
                                uint16_t aStretch,
                                uint8_t aStyle,
                                bool aIsDataUserFont,
@@ -408,35 +408,36 @@ MacOSFontEntry::MacOSFontEntry(const nsA
       mTrakTable(nullptr),
       mTrakValues(nullptr),
       mTrakSizeTable(nullptr)
 {
     mFontRef = aFontRef;
     mFontRefInitialized = true;
     ::CFRetain(mFontRef);
 
-    mWeight = aWeight;
+    mWeightRange = WeightRange(aWeight);
     mStretch = aStretch;
     mFixedPitch = false; // xxx - do we need this for downloaded fonts?
     mStyle = aStyle;
 
     NS_ASSERTION(!(aIsDataUserFont && aIsLocalUserFont),
                  "userfont is either a data font or a local font");
     mIsDataUserFont = aIsDataUserFont;
     mIsLocalUserFont = aIsLocalUserFont;
 }
 
 gfxFontEntry*
 MacOSFontEntry::Clone() const
 {
     MOZ_ASSERT(!IsUserFont(), "we can only clone installed fonts!");
     MacOSFontEntry* fe =
-        new MacOSFontEntry(Name(), mWeight, mStandardFace, mSizeHint);
+        new MacOSFontEntry(Name(), Weight().Min(), mStandardFace, mSizeHint);
     fe->mStyle = mStyle;
     fe->mStretch = mStretch;
+    fe->mWeightRange = mWeightRange;
     fe->mFixedPitch = mFixedPitch;
     return fe;
 }
 
 CGFontRef
 MacOSFontEntry::GetFontRef()
 {
     if (!mFontRefInitialized) {
@@ -923,24 +924,29 @@ gfxMacFontFamily::FindStyleVariations(Fo
             [facename hasSuffix:@"Oblique"])
         {
             fontEntry->mStyle = NS_FONT_STYLE_ITALIC;
         }
         if (macTraits & NSFixedPitchFontMask) {
             fontEntry->mFixedPitch = true;
         }
 
+        fontEntry->SetupVariationRanges();
+
         if (LOG_FONTLIST_ENABLED()) {
+            nsAutoCString weightString;
+            fontEntry->Weight().ToString(weightString);
             LOG_FONTLIST(("(fontlist) added (%s) to family (%s)"
-                 " with style: %s weight: %d stretch: %d"
+                 " with style: %s weight: %s stretch: %d"
                  " (apple-weight: %d macTraits: %8.8x)",
                  NS_ConvertUTF16toUTF8(fontEntry->Name()).get(), 
                  NS_ConvertUTF16toUTF8(Name()).get(), 
                  fontEntry->IsItalic() ? "italic" : "normal",
-                 cssWeight, fontEntry->Stretch(),
+                 weightString.get(),
+                 fontEntry->Stretch(),
                  appKitWeight, macTraits));
         }
 
         // insert into font entry array of family
         AddFontEntry(fontEntry);
     }
 
     SortAvailableFonts();
@@ -1277,19 +1283,20 @@ gfxMacPlatformFontList::InitSingleFaceLi
 
         // add only if doesn't exist already
         if (!mFontFamilies.GetWeak(key)) {
             RefPtr<gfxFontFamily> familyEntry =
                 new gfxSingleFaceMacFontFamily(familyName);
             // We need a separate font entry, because its family name will
             // differ from the one we found in the main list.
             MacOSFontEntry* fontEntry =
-                new MacOSFontEntry(fe->Name(), fe->mWeight, true,
+                new MacOSFontEntry(fe->Name(), fe->Weight().Min(), true,
                                    static_cast<const MacOSFontEntry*>(fe)->
                                        mSizeHint);
+            fontEntry->mWeightRange = fe->mWeightRange;
             familyEntry->AddFontEntry(fontEntry);
             familyEntry->SetHasStyles(true);
             mFontFamilies.Put(key, familyEntry);
             LOG_FONTLIST(("(fontlist-singleface) added new family: %s, key: %s\n",
                           NS_ConvertUTF16toUTF8(familyName).get(),
                           NS_ConvertUTF16toUTF8(key).get()));
         }
     }
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -770,16 +770,20 @@ gfxPlatform::Init()
 #  endif
 #endif
 
     InitLayersIPC();
 
     gPlatform->PopulateScreenInfo();
     gPlatform->ComputeTileSize();
 
+#ifdef MOZ_ENABLE_FREETYPE
+    Factory::SetFTLibrary(gPlatform->GetFTLibrary());
+#endif
+
     nsresult rv;
     rv = gfxPlatformFontList::Init();
     if (NS_FAILED(rv)) {
         MOZ_CRASH("Could not initialize gfxPlatformFontList");
     }
 
     gPlatform->mScreenReferenceSurface =
         gPlatform->CreateOffscreenSurface(IntSize(1, 1),
@@ -801,20 +805,16 @@ gfxPlatform::Init()
       }
     }
 
     rv = gfxFontCache::Init();
     if (NS_FAILED(rv)) {
         MOZ_CRASH("Could not initialize gfxFontCache");
     }
 
-#ifdef MOZ_ENABLE_FREETYPE
-    Factory::SetFTLibrary(gPlatform->GetFTLibrary());
-#endif
-
     /* Create and register our CMS Override observer. */
     gPlatform->mSRGBOverrideObserver = new SRGBOverrideObserver();
     Preferences::AddWeakObserver(gPlatform->mSRGBOverrideObserver, GFX_PREF_CMS_FORCE_SRGB);
 
     gPlatform->mFontPrefsObserver = new FontPrefsObserver();
     Preferences::AddStrongObservers(gPlatform->mFontPrefsObserver, kObservedPrefs);
 
     GLContext::PlatformStartup();
--- a/gfx/thebes/gfxUserFontSet.cpp
+++ b/gfx/thebes/gfxUserFontSet.cpp
@@ -121,17 +121,17 @@ gfxUserFontEntry::gfxUserFontEntry(gfxUs
       mLoader(nullptr),
       mFontSet(aFontSet)
 {
     MOZ_ASSERT(aWeight.ToFloat() != 0.0f,
                "aWeight must not be 0; use FontWeight::Normal() instead");
     mIsUserFontContainer = true;
     mSrcList = aFontFaceSrcList;
     mSrcIndex = 0;
-    mWeight = aWeight;
+    mWeightRange = WeightRange(aWeight);
     mStretch = aStretch;
     mStyle = aStyle;
     mFeatureSettings.AppendElements(aFeatureSettings);
     mVariationSettings.AppendElements(aVariationSettings);
     mLanguageOverride = aLanguageOverride;
     mCharacterMap = aUnicodeRanges;
 }
 
@@ -149,17 +149,18 @@ gfxUserFontEntry::Matches(const nsTArray
                           uint32_t aStretch,
                           uint8_t aStyle,
                           const nsTArray<gfxFontFeature>& aFeatureSettings,
                           const nsTArray<gfxFontVariation>& aVariationSettings,
                           uint32_t aLanguageOverride,
                           gfxCharacterMap* aUnicodeRanges,
                           uint8_t aFontDisplay)
 {
-    return mWeight == aWeight &&
+    return Weight().Min() == aWeight &&
+           Weight().Max() == aWeight &&
            mStretch == aStretch &&
            mStyle == aStyle &&
            mFeatureSettings == aFeatureSettings &&
            mVariationSettings == aVariationSettings &&
            mLanguageOverride == aLanguageOverride &&
            mSrcList == aFontFaceSrcList &&
            mFontDisplay == aFontDisplay &&
            ((!aUnicodeRanges && !mCharacterMap) ||
@@ -511,17 +512,17 @@ gfxUserFontEntry::DoLoadNextSrc(bool aFo
         // src local ==> lookup and load immediately
 
         if (currSrc.mSourceType == gfxFontFaceSrc::eSourceType_Local) {
             // Don't look up local fonts if the font whitelist is being used.
             gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
             gfxFontEntry* fe = pfl && pfl->IsFontFamilyWhitelistActive() ?
                 nullptr :
                 gfxPlatform::GetPlatform()->LookupLocalFont(currSrc.mLocalName,
-                                                            mWeight,
+                                                            Weight().Min(),
                                                             mStretch,
                                                             mStyle);
             nsTArray<gfxUserFontSet*> fontSets;
             GetUserFontSets(fontSets);
             for (gfxUserFontSet* fontSet : fontSets) {
                 // We need to note on each gfxUserFontSet that contains the user
                 // font entry that we used a local() rule.
                 fontSet->SetLocalRulesUsed();
@@ -766,17 +767,17 @@ gfxUserFontEntry::LoadPlatformFont(const
         // don't allow us to retrieve or measure it directly.
         // The *OnAlloc function will also tell DMD about this block, as the
         // OS font code may hold on to it for an extended period.
         computedSize = UserFontMallocSizeOfOnAlloc(saneData);
 
         // Here ownership of saneData is passed to the platform,
         // which will delete it when no longer required
         fe = gfxPlatform::GetPlatform()->MakePlatformFont(mName,
-                                                          mWeight,
+                                                          Weight().Min(),
                                                           mStretch,
                                                           mStyle,
                                                           saneData,
                                                           saneLen);
         if (!fe) {
             mFontSet->LogMessage(this, "not usable by platform");
         }
     }
@@ -1035,22 +1036,25 @@ gfxUserFontSet::FindExistingUserFontEntr
 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 "
+        nsAutoCString weightString;
+        aUserFontEntry->Weight().ToString(weightString);
+        LOG(("userfonts (%p) added to \"%s\" (%p) style: %s weight: %s "
              "stretch: %d display: %d",
              this, NS_ConvertUTF16toUTF8(aFamilyName).get(), aUserFontEntry,
              (aUserFontEntry->IsItalic() ? "italic" :
               (aUserFontEntry->IsOblique() ? "oblique" : "normal")),
-             aUserFontEntry->Weight().ToFloat(), aUserFontEntry->Stretch(),
+             weightString.get(),
+             aUserFontEntry->Stretch(),
              aUserFontEntry->GetFontDisplay()));
     }
 }
 
 void
 gfxUserFontSet::IncrementGeneration(bool aIsRebuild)
 {
     // add one, increment again if zero
@@ -1177,17 +1181,17 @@ gfxUserFontSet::UserFontCache::Entry::Ke
         }
     }
 
     if (mPrivate != aKey->mPrivate) {
         return false;
     }
 
     if (mFontEntry->mStyle            != fe->mStyle     ||
-        mFontEntry->mWeight           != fe->mWeight          ||
+        mFontEntry->Weight()          != fe->Weight()         ||
         mFontEntry->mStretch          != fe->mStretch         ||
         mFontEntry->mFeatureSettings  != fe->mFeatureSettings ||
         mFontEntry->mVariationSettings != fe->mVariationSettings ||
         mFontEntry->mLanguageOverride != fe->mLanguageOverride ||
         mFontEntry->mFamilyName       != fe->mFamilyName) {
         return false;
     }
 
--- a/gfx/thebes/gfxUserFontSet.h
+++ b/gfx/thebes/gfxUserFontSet.h
@@ -401,17 +401,18 @@ public:
             static PLDHashNumber HashKey(const KeyTypePointer aKey) {
                 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->Weight().Min().ForHash(),
+                                            aKey->mFontEntry->Weight().Max().ForHash(),
                                             (aKey->mFontEntry->mStyle |
                                              (aKey->mFontEntry->mStretch << 11) ) ^
                                              aKey->mFontEntry->mLanguageOverride);
             }
 
             enum { ALLOW_MEMMOVE = false };
 
             gfxFontSrcURI* GetURI() const { return mURI; }
--- a/js/public/Utility.h
+++ b/js/public/Utility.h
@@ -58,17 +58,17 @@ namespace js {
  * below), and also used to characterize threads in the thread scheduler (see
  * js/src/vm/HelperThreads.cpp).
  *
  * Please update oom::FirstThreadTypeToTest and oom::LastThreadTypeToTest when
  * adding new thread types.
  */
 enum ThreadType {
     THREAD_TYPE_NONE = 0,       // 0
-    THREAD_TYPE_COOPERATING,    // 1
+    THREAD_TYPE_MAIN,           // 1
     THREAD_TYPE_WASM,           // 2
     THREAD_TYPE_ION,            // 3
     THREAD_TYPE_PARSE,          // 4
     THREAD_TYPE_COMPRESS,       // 5
     THREAD_TYPE_GCHELPER,       // 6
     THREAD_TYPE_GCPARALLEL,     // 7
     THREAD_TYPE_PROMISE_TASK,   // 8
     THREAD_TYPE_ION_FREE,       // 9
@@ -87,17 +87,17 @@ namespace oom {
  *
  * Getter/Setter functions to encapsulate mozilla::ThreadLocal, implementation
  * is in jsutil.cpp.
  */
 # if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
 
 // Define the range of threads tested by simulated OOM testing and the
 // like. Testing worker threads is not supported.
-const ThreadType FirstThreadTypeToTest = THREAD_TYPE_COOPERATING;
+const ThreadType FirstThreadTypeToTest = THREAD_TYPE_MAIN;
 const ThreadType LastThreadTypeToTest = THREAD_TYPE_WASM_TIER2;
 
 extern bool InitThreadType(void);
 extern void SetThreadType(ThreadType);
 extern JS_FRIEND_API(uint32_t) GetThreadType(void);
 
 # else
 
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -48,17 +48,17 @@ ifdef MOZ_MSAN
 JITTEST_SANITIZER_ENV=MSAN_SYMBOLIZER_PATH='$(LLVM_SYMBOLIZER)'
 endif
 endif
 
 check-style::
 	(cd $(topsrcdir) && $(PYTHON) $(topsrcdir)/config/check_spidermonkey_style.py);
 
 check-masm::
-	(cd $(srcdir) && $(PYTHON) $(topsrcdir)/config/check_macroassembler_style.py);
+	(cd $(topsrcdir) && $(PYTHON) $(topsrcdir)/config/check_macroassembler_style.py);
 
 check-js-msg::
 	(cd $(topsrcdir) && $(PYTHON) $(topsrcdir)/config/check_js_msg_encoding.py);
 
 check-opcode::
 	(cd $(topsrcdir) && $(PYTHON) $(topsrcdir)/config/check_js_opcode.py);
 
 check-jit-test::
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -1537,17 +1537,17 @@ OOMThreadTypes(JSContext* cx, unsigned a
     CallArgs args = CallArgsFromVp(argc, vp);
     args.rval().setInt32(js::THREAD_TYPE_MAX);
     return true;
 }
 
 static bool
 CheckCanSimulateOOM(JSContext* cx)
 {
-    if (js::oom::GetThreadType() != js::THREAD_TYPE_COOPERATING) {
+    if (js::oom::GetThreadType() != js::THREAD_TYPE_MAIN) {
         JS_ReportErrorASCII(cx, "Simulated OOM failure is only supported on the main thread");
         return false;
     }
 
     return true;
 }
 
 static bool
@@ -1574,17 +1574,17 @@ SetupOOMFailure(JSContext* cx, bool fail
     if (!JS::ToInt32(cx, args.get(0), &count))
         return false;
 
     if (count <= 0) {
         JS_ReportErrorASCII(cx, "OOM cutoff should be positive");
         return false;
     }
 
-    uint32_t targetThread = js::THREAD_TYPE_COOPERATING;
+    uint32_t targetThread = js::THREAD_TYPE_MAIN;
     if (args.length() > 1 && !ToUint32(cx, args[1], &targetThread))
         return false;
 
     if (targetThread == js::THREAD_TYPE_NONE || targetThread >= js::THREAD_TYPE_MAX) {
         JS_ReportErrorASCII(cx, "Invalid thread type specified");
         return false;
     }
 
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -3563,17 +3563,17 @@ BytecodeEmitter::maybeSetSourceMap()
 
     return true;
 }
 
 void
 BytecodeEmitter::tellDebuggerAboutCompiledScript(JSContext* cx)
 {
     // Note: when parsing off thread the resulting scripts need to be handed to
-    // the debugger after rejoining to the active thread.
+    // the debugger after rejoining to the main thread.
     if (cx->helperThread())
         return;
 
     // Lazy scripts are never top level (despite always being invoked with a
     // nullptr parent), and so the hook should never be fired.
     if (emitterMode != LazyFunction && !parent)
         Debugger::onNewScript(cx, script);
 }
--- a/js/src/gc/Allocator.cpp
+++ b/js/src/gc/Allocator.cpp
@@ -370,28 +370,28 @@ GCRuntime::refillFreeListFromAnyThread(J
         return refillFreeListFromMainThread(cx, thingKind);
 
     return refillFreeListFromHelperThread(cx, thingKind);
 }
 
 /* static */ TenuredCell*
 GCRuntime::refillFreeListFromMainThread(JSContext* cx, AllocKind thingKind)
 {
-    // It should not be possible to allocate on the active thread while we are
+    // It should not be possible to allocate on the main thread while we are
     // inside a GC.
     Zone *zone = cx->zone();
     MOZ_ASSERT(!JS::CurrentThreadIsHeapBusy(), "allocating while under GC");
 
     return cx->arenas()->allocateFromArena(zone, thingKind, ShouldCheckThresholds::CheckThresholds);
 }
 
 /* static */ TenuredCell*
 GCRuntime::refillFreeListFromHelperThread(JSContext* cx, AllocKind thingKind)
 {
-    // A GC may be happening on the active thread, but zones used by off thread
+    // A GC may be happening on the main thread, but zones used by off thread
     // tasks are never collected.
     Zone* zone = cx->zone();
     MOZ_ASSERT(!zone->wasGCStarted());
 
     return cx->arenas()->allocateFromArena(zone, thingKind, ShouldCheckThresholds::CheckThresholds);
 }
 
 /* static */ TenuredCell*
--- a/js/src/gc/Cell.h
+++ b/js/src/gc/Cell.h
@@ -378,25 +378,25 @@ TenuredCell::readBarrier(TenuredCell* th
     // at the moment this can happen e.g. when rekeying tables containing
     // read-barriered GC things after a moving GC.
     //
     // TODO: Fix this and assert we're not collecting if we're on the active
     // thread.
 
     JS::shadow::Zone* shadowZone = thing->shadowZoneFromAnyThread();
     if (shadowZone->needsIncrementalBarrier()) {
-        // Barriers are only enabled on the active thread and are disabled while collecting.
+        // Barriers are only enabled on the main thread and are disabled while collecting.
         MOZ_ASSERT(!RuntimeFromMainThreadIsHeapMajorCollecting(shadowZone));
         Cell* tmp = thing;
         TraceManuallyBarrieredGenericPointerEdge(shadowZone->barrierTracer(), &tmp, "read barrier");
         MOZ_ASSERT(tmp == thing);
     }
 
     if (thing->isMarkedGray()) {
-        // There shouldn't be anything marked grey unless we're on the active thread.
+        // There shouldn't be anything marked grey unless we're on the main thread.
         MOZ_ASSERT(CurrentThreadCanAccessRuntime(thing->runtimeFromAnyThread()));
         if (!RuntimeFromMainThreadIsHeapMajorCollecting(shadowZone))
             JS::UnmarkGrayGCThingRecursively(JS::GCCellPtr(thing, thing->getTraceKind()));
     }
 }
 
 void
 AssertSafeToSkipBarrier(TenuredCell* thing);
--- a/js/src/gc/FreeOp.h
+++ b/js/src/gc/FreeOp.h
@@ -40,17 +40,17 @@ class FreeOp : public JSFreeOp
     explicit FreeOp(JSRuntime* maybeRuntime);
     ~FreeOp();
 
     bool onMainThread() const {
         return runtime_ != nullptr;
     }
 
     bool maybeOnHelperThread() const {
-        // Sometimes background finalization happens on the active thread so
+        // Sometimes background finalization happens on the main thread so
         // runtime_ being null doesn't always mean we are off thread.
         return !runtime_;
     }
 
     bool isDefaultFreeOp() const;
 
     void free_(void* p) {
         js_free(p);
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -425,31 +425,31 @@ FOR_EACH_ALLOCKIND(EXPAND_THINGS_PER_ARE
 
 struct js::gc::FinalizePhase
 {
     gcstats::PhaseKind statsPhase;
     AllocKinds kinds;
 };
 
 /*
- * Finalization order for objects swept incrementally on the active thread.
+ * Finalization order for objects swept incrementally on the main thread.
  */
 static const FinalizePhase ForegroundObjectFinalizePhase = {
     gcstats::PhaseKind::SWEEP_OBJECT, {
         AllocKind::OBJECT0,
         AllocKind::OBJECT2,
         AllocKind::OBJECT4,
         AllocKind::OBJECT8,
         AllocKind::OBJECT12,
         AllocKind::OBJECT16
     }
 };
 
 /*
- * Finalization order for GC things swept incrementally on the active thread.
+ * Finalization order for GC things swept incrementally on the main thread.
  */
 static const FinalizePhase ForegroundNonObjectFinalizePhase = {
     gcstats::PhaseKind::SWEEP_SCRIPT, {
         AllocKind::SCRIPT,
         AllocKind::JITCODE
     }
 };
 
@@ -2435,17 +2435,17 @@ ShouldRelocateZone(size_t arenaCount, si
 
     return (relocCount * 100.0) / arenaCount >= MIN_ZONE_RECLAIM_PERCENT;
 }
 
 bool
 ArenaLists::relocateArenas(Zone* zone, Arena*& relocatedListOut, JS::gcreason::Reason reason,
                            SliceBudget& sliceBudget, gcstats::Statistics& stats)
 {
-    // This is only called from the active thread while we are doing a GC, so
+    // This is only called from the main thread while we are doing a GC, so
     // there is no need to lock.
     MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime_));
     MOZ_ASSERT(runtime_->gc.isHeapCompacting());
     MOZ_ASSERT(!runtime_->gc.isBackgroundSweeping());
 
     // Clear all the free lists.
     clearFreeLists();
 
@@ -3300,17 +3300,17 @@ Nursery::requestMinorGC(JS::gcreason::Re
     // See comment in requestMajorGC.
     runtime()->mainContextFromOwnThread()->requestInterrupt(JSContext::RequestInterruptCanWait);
 }
 
 bool
 GCRuntime::triggerGC(JS::gcreason::Reason reason)
 {
     /*
-     * Don't trigger GCs if this is being called off the active thread from
+     * Don't trigger GCs if this is being called off the main thread from
      * onTooMuchMalloc().
      */
     if (!CurrentThreadCanAccessRuntime(rt))
         return false;
 
     /* GC is already running. */
     if (JS::CurrentThreadIsHeapCollecting())
         return false;
@@ -3555,17 +3555,17 @@ GCRuntime::sweepBackgroundThings(ZoneLis
                 if (arenas)
                     ArenaLists::backgroundFinalize(&fop, arenas, &emptyArenas);
             }
         }
 
         AutoLockGC lock(rt);
 
         // Release any arenas that are now empty, dropping and reaquiring the GC
-        // lock every so often to avoid blocking the active thread from
+        // lock every so often to avoid blocking the main thread from
         // allocating chunks.
         static const size_t LockReleasePeriod = 32;
         size_t releaseCount = 0;
         Arena* next;
         for (Arena* arena = emptyArenas; arena; arena = next) {
             next = arena->next;
             rt->gc.releaseArena(arena, lock);
             releaseCount++;
@@ -3725,17 +3725,17 @@ GCHelperState::waitBackgroundSweepEnd()
         waitForBackgroundThread(lock);
     if (!rt->gc.isIncrementalGCInProgress())
         rt->gc.assertBackgroundSweepingFinished();
 }
 
 void
 GCHelperState::doSweep(AutoLockGC& lock)
 {
-    // The active thread may call queueZonesForBackgroundSweep() while this is
+    // The main thread may call queueZonesForBackgroundSweep() while this is
     // running so we must check there is no more work to do before exiting.
 
     do {
         while (!rt->gc.backgroundSweepZones.ref().isEmpty()) {
             AutoSetThreadIsSweeping threadIsSweeping;
 
             ZoneList zones;
             zones.transferFrom(rt->gc.backgroundSweepZones.ref());
@@ -4199,17 +4199,17 @@ ShouldCollectZone(Zone* zone, JS::gcreas
     // thread. In either case we don't have information about which atoms are
     // roots, so we must skip collecting atoms.
     //
     // Note that only affects the first slice of an incremental GC since root
     // marking is completed before we return to the mutator.
     //
     // Off-thread parsing is inhibited after the start of GC which prevents
     // races between creating atoms during parsing and sweeping atoms on the
-    // active thread.
+    // main thread.
     //
     // Otherwise, we always schedule a GC in the atoms zone so that atoms which
     // the other collected zones are using are marked, and we can update the
     // set of atoms in use by the other collected zones at the end of the GC.
     if (zone->isAtomsZone())
         return TlsContext.get()->canCollectAtoms();
 
     return zone->canCollect();
@@ -7432,17 +7432,17 @@ GCRuntime::gcCycle(bool nonincrementalBy
     AutoTraceSession session(rt, JS::HeapState::MajorCollecting);
 
     majorGCTriggerReason = JS::gcreason::NO_REASON;
 
     number++;
     if (!isIncrementalGCInProgress())
         incMajorGcNumber();
 
-    // It's ok if threads other than the active thread have suppressGC set, as
+    // It's ok if threads other than the main thread have suppressGC set, as
     // they are operating on zones which will not be collected from here.
     MOZ_ASSERT(!rt->mainContextFromOwnThread()->suppressGC);
 
     // Assert if this is a GC unsafe region.
     rt->mainContextFromOwnThread()->verifyIsSafeToGC();
 
     {
         gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::WAIT_BACKGROUND_THREAD);
@@ -9050,17 +9050,17 @@ js::gc::detail::CellIsMarkedGrayIfKnown(
     // following cases:
     //
     // 1) When OOM has caused us to clear the gcGrayBitsValid_ flag.
     //
     // 2) When we are in an incremental GC and examine a cell that is in a zone
     // that is not being collected. Gray targets of CCWs that are marked black
     // by a barrier will eventually be marked black in the next GC slice.
     //
-    // 3) When we are not on the runtime's active thread. Helper threads might
+    // 3) When we are not on the runtime's main thread. Helper threads might
     // call this while parsing, and they are not allowed to inspect the
     // runtime's incremental state. The objects being operated on are not able
     // to be collected and will not be marked any color.
 
     if (!CanCheckGrayBits(cell))
         return false;
 
     auto tc = &cell->asTenured();
--- a/js/src/gc/GCHelperState.h
+++ b/js/src/gc/GCHelperState.h
@@ -18,30 +18,30 @@ namespace gc {
 class ArenaLists;
 } /* namespace gc */
 
 /*
  * Helper state for use when JS helper threads sweep and allocate GC thing kinds
  * that can be swept and allocated off thread.
  *
  * In non-threadsafe builds, all actual sweeping and allocation is performed
- * on the active thread, but GCHelperState encapsulates this from clients as
+ * on the main thread, but GCHelperState encapsulates this from clients as
  * much as possible.
  */
 class GCHelperState
 {
     enum State {
         IDLE,
         SWEEPING
     };
 
     // Associated runtime.
     JSRuntime* const rt;
 
-    // Condvar for notifying the active thread when work has finished. This is
+    // Condvar for notifying the main thread when work has finished. This is
     // associated with the runtime's GC lock --- the worker thread state
     // condvars can't be used here due to lock ordering issues.
     ConditionVariable done;
 
     // Activity for the helper to do, protected by the GC lock.
     MainThreadOrGCTaskData<State> state_;
 
     // Whether work is being performed on some thread.
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -100,17 +100,17 @@ class ChunkPool
         operator Chunk*() const { return get(); }
         Chunk* operator->() const { return get(); }
       private:
         Chunk* current_;
     };
 };
 
 // Performs extra allocation off thread so that when memory is required on the
-// active thread it will already be available and waiting.
+// main thread it will already be available and waiting.
 class BackgroundAllocTask : public GCParallelTask
 {
     // Guarded by the GC lock.
     GCLockData<ChunkPool&> chunkPool_;
 
     const bool enabled_;
 
   public:
@@ -931,17 +931,17 @@ class GCRuntime
 
     /* Always preserve JIT code during GCs, for testing. */
     MainThreadData<bool> alwaysPreserveCode;
 
 #ifdef DEBUG
     MainThreadData<bool> arenasEmptyAtShutdown;
 #endif
 
-    /* Synchronize GC heap access among GC helper threads and active threads. */
+    /* Synchronize GC heap access among GC helper threads and the main thread. */
     friend class js::AutoLockGC;
     friend class js::AutoLockGCBgAlloc;
     js::Mutex lock;
 
     BackgroundAllocTask allocTask;
     BackgroundDecommitTask decommitTask;
 
     js::GCHelperState helperState;
--- a/js/src/gc/ObjectKind-inl.h
+++ b/js/src/gc/ObjectKind-inl.h
@@ -18,17 +18,17 @@ namespace js {
 namespace gc {
 
 static inline bool
 CanBeFinalizedInBackground(AllocKind kind, const Class* clasp)
 {
     MOZ_ASSERT(IsObjectAllocKind(kind));
     /* If the class has no finalizer or a finalizer that is safe to call on
      * a different thread, we change the alloc kind. For example,
-     * AllocKind::OBJECT0 calls the finalizer on the active thread,
+     * AllocKind::OBJECT0 calls the finalizer on the main thread,
      * AllocKind::OBJECT0_BACKGROUND calls the finalizer on the gcHelperThread.
      * IsBackgroundFinalized is called to prevent recursively incrementing
      * the alloc kind; kind may already be a background finalize kind.
      */
     return (!IsBackgroundFinalized(kind) &&
             (!clasp->hasFinalize() || (clasp->flags & JSCLASS_BACKGROUND_FINALIZE)));
 }
 
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -247,18 +247,18 @@ Zone::discardJitCode(FreeOp* fop, bool d
      */
     if (discardBaselineCode) {
         jitZone()->optimizedStubSpace()->freeAllAfterMinorGC(this);
         jitZone()->purgeIonCacheIRStubInfo();
     }
 
     /*
      * Free all control flow graphs that are cached on BaselineScripts.
-     * Assuming this happens on the active thread and all control flow
-     * graph reads happen on the active thread, this is safe.
+     * Assuming this happens on the main thread and all control flow
+     * graph reads happen on the main thread, this is safe.
      */
     jitZone()->cfgSpace()->lifoAlloc().freeAll();
 }
 
 #ifdef JSGC_HASH_TABLE_CHECKS
 void
 JS::Zone::checkUniqueIdTableAfterMovingGC()
 {
--- a/js/src/jit/CompileWrappers.h
+++ b/js/src/jit/CompileWrappers.h
@@ -11,17 +11,17 @@
 
 namespace js {
 namespace jit {
 
 class JitRuntime;
 
 // During Ion compilation we need access to various bits of the current
 // compartment, runtime and so forth. However, since compilation can run off
-// thread while the active thread is mutating the VM, this access needs
+// thread while the main thread is mutating the VM, this access needs
 // to be restricted. The classes below give the compiler an interface to access
 // all necessary information in a threadsafe fashion.
 
 class CompileRuntime
 {
     JSRuntime* runtime();
 
   public:
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -381,39 +381,39 @@ JitRuntime::debugTrapHandler(JSContext* 
     }
     return debugTrapHandler_;
 }
 
 JitRuntime::IonBuilderList&
 JitRuntime::ionLazyLinkList(JSRuntime* rt)
 {
     MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt),
-               "Should only be mutated by the active thread.");
+               "Should only be mutated by the main thread.");
     return ionLazyLinkList_.ref();
 }
 
 void
 JitRuntime::ionLazyLinkListRemove(JSRuntime* rt, jit::IonBuilder* builder)
 {
     MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt),
-               "Should only be mutated by the active thread.");
+               "Should only be mutated by the main thread.");
     MOZ_ASSERT(rt == builder->script()->runtimeFromMainThread());
     MOZ_ASSERT(ionLazyLinkListSize_ > 0);
 
     builder->removeFrom(ionLazyLinkList(rt));
     ionLazyLinkListSize_--;
 
     MOZ_ASSERT(ionLazyLinkList(rt).isEmpty() == (ionLazyLinkListSize_ == 0));
 }
 
 void
 JitRuntime::ionLazyLinkListAdd(JSRuntime* rt, jit::IonBuilder* builder)
 {
     MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt),
-               "Should only be mutated by the active thread.");
+               "Should only be mutated by the main thread.");
     MOZ_ASSERT(rt == builder->script()->runtimeFromMainThread());
     ionLazyLinkList(rt).insertFront(builder);
     ionLazyLinkListSize_++;
 }
 
 uint8_t*
 JSContext::allocateOsrTempData(size_t size)
 {
@@ -2305,18 +2305,18 @@ CanIonCompileOrInlineScript(JSScript* sc
 static bool
 ScriptIsTooLarge(JSContext* cx, JSScript* script)
 {
     if (!JitOptions.limitScriptSize)
         return false;
 
     uint32_t numLocalsAndArgs = NumLocalsAndArgs(script);
 
-    if (script->length() > MAX_ACTIVE_THREAD_SCRIPT_SIZE ||
-        numLocalsAndArgs > MAX_ACTIVE_THREAD_LOCALS_AND_ARGS)
+    if (script->length() > MAX_MAIN_THREAD_SCRIPT_SIZE ||
+        numLocalsAndArgs > MAX_MAIN_THREAD_LOCALS_AND_ARGS)
     {
         if (!OffThreadCompilationAvailable(cx)) {
             JitSpew(JitSpew_IonAbort, "Script too large (%zu bytes) (%u locals/args)",
                     script->length(), numLocalsAndArgs);
             TrackIonAbort(cx, script, script->code(), "too large");
             return true;
         }
     }
@@ -2442,17 +2442,17 @@ Compile(JSContext* cx, HandleScript scri
 
 } // namespace jit
 } // namespace js
 
 bool
 jit::OffThreadCompilationAvailable(JSContext* cx)
 {
     // Even if off thread compilation is enabled, compilation must still occur
-    // on the active thread in some cases.
+    // on the main thread in some cases.
     //
     // Require cpuCount > 1 so that Ion compilation jobs and active-thread
     // execution are not competing for the same resources.
     return cx->runtime()->canUseOffthreadIonCompilation()
         && HelperThreadState().cpuCount > 1
         && CanUseExtraThreads();
 }
 
--- a/js/src/jit/Ion.h
+++ b/js/src/jit/Ion.h
@@ -58,17 +58,17 @@ class JitContext
     JitContext(JSContext* cx, TempAllocator* temp);
     JitContext(CompileRuntime* rt, CompileCompartment* comp, TempAllocator* temp);
     JitContext(CompileRuntime* rt, TempAllocator* temp);
     explicit JitContext(CompileRuntime* rt);
     explicit JitContext(TempAllocator* temp);
     JitContext();
     ~JitContext();
 
-    // Running context when executing on the active thread. Not available during
+    // Running context when executing on the main thread. Not available during
     // compilation.
     JSContext* cx;
 
     // Allocator for temporary memory during compilation.
     TempAllocator* temp;
 
     // Wrappers with information about the current runtime/compartment for use
     // during compilation.
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -7319,17 +7319,17 @@ IonBuilder::ensureDefiniteType(MDefiniti
     current->add(replace);
     return replace;
 }
 
 static size_t
 NumFixedSlots(JSObject* object)
 {
     // Note: we can't use object->numFixedSlots() here, as this will read the
-    // shape and can race with the active thread if we are building off thread.
+    // shape and can race with the main thread if we are building off thread.
     // The allocation kind and object class (which goes through the type) can
     // be read freely, however.
     gc::AllocKind kind = object->asTenured().getAllocKind();
     return gc::GetGCKindSlots(kind, object->getClass());
 }
 
 static bool
 IsUninitializedGlobalLexicalSlot(JSObject* obj, PropertyName* name)
@@ -13539,17 +13539,17 @@ IonBuilder::setPropTryReferenceTypedObje
     *emitted = true;
     return resumeAfter(store);
 }
 
 JSObject*
 IonBuilder::checkNurseryObject(JSObject* obj)
 {
     // If we try to use any nursery pointers during compilation, make sure that
-    // the active thread will cancel this compilation before performing a minor
+    // the main thread will cancel this compilation before performing a minor
     // GC. All constants used during compilation should either go through this
     // function or should come from a type set (which has a similar barrier).
     if (obj && IsInsideNursery(obj)) {
         compartment->zone()->setMinorGCShouldCancelIonCompilations();
         IonBuilder* builder = this;
         while (builder) {
             builder->setNotSafeForMinorGC();
             builder = builder->callerBuilder_;
--- a/js/src/jit/IonOptimizationLevels.cpp
+++ b/js/src/jit/IonOptimizationLevels.cpp
@@ -88,27 +88,27 @@ OptimizationInfo::compilerWarmUpThreshol
     uint32_t warmUpThreshold = JitOptions.forcedDefaultIonWarmUpThreshold
         .valueOr(compilerWarmUpThreshold_);
 
     if (JitOptions.isSmallFunction(script)) {
         warmUpThreshold = JitOptions.forcedDefaultIonSmallFunctionWarmUpThreshold
             .valueOr(compilerSmallFunctionWarmUpThreshold_);
     }
 
-    // If the script is too large to compile on the active thread, we can still
+    // If the script is too large to compile on the main thread, we can still
     // compile it off thread. In these cases, increase the warm-up counter
     // threshold to improve the compilation's type information and hopefully
     // avoid later recompilation.
 
-    if (script->length() > MAX_ACTIVE_THREAD_SCRIPT_SIZE)
-        warmUpThreshold *= (script->length() / (double) MAX_ACTIVE_THREAD_SCRIPT_SIZE);
+    if (script->length() > MAX_MAIN_THREAD_SCRIPT_SIZE)
+        warmUpThreshold *= (script->length() / double(MAX_MAIN_THREAD_SCRIPT_SIZE));
 
     uint32_t numLocalsAndArgs = NumLocalsAndArgs(script);
-    if (numLocalsAndArgs > MAX_ACTIVE_THREAD_LOCALS_AND_ARGS)
-        warmUpThreshold *= (numLocalsAndArgs / (double) MAX_ACTIVE_THREAD_LOCALS_AND_ARGS);
+    if (numLocalsAndArgs > MAX_MAIN_THREAD_LOCALS_AND_ARGS)
+        warmUpThreshold *= (numLocalsAndArgs / double(MAX_MAIN_THREAD_LOCALS_AND_ARGS));
 
     if (!pc || JitOptions.eagerCompilation)
         return warmUpThreshold;
 
     // It's more efficient to enter outer loops, rather than inner loops, via OSR.
     // To accomplish this, we use a slightly higher threshold for inner loops.
     // Note that the loop depth is always > 0 so we will prefer non-OSR over OSR.
     uint32_t loopDepth = LoopEntryDepthHint(pc);
--- a/js/src/jit/IonOptimizationLevels.h
+++ b/js/src/jit/IonOptimizationLevels.h
@@ -92,17 +92,17 @@ class OptimizationInfo
     // Toggles whether sink is used.
     bool sink_;
 
     // Describes which register allocator to use.
     IonRegisterAllocator registerAllocator_;
 
     // The maximum total bytecode size of an inline call site. We use a lower
     // value if off-thread compilation is not available, to avoid stalling the
-    // active thread.
+    // main thread.
     uint32_t inlineMaxBytecodePerCallSiteHelperThread_;
     uint32_t inlineMaxBytecodePerCallSiteMainThread_;
 
     // The maximum value we allow for baselineScript->inlinedBytecodeLength_
     // when inlining.
     uint16_t inlineMaxCalleeInlinedBytecodeLength_;
 
     // The maximum bytecode length we'll inline in a single compilation.
--- a/js/src/jit/JitCompartment.h
+++ b/js/src/jit/JitCompartment.h
@@ -605,17 +605,17 @@ class JitCompartment
         if (stubs_[RegExpTester])
             return true;
         stubs_[RegExpTester] = generateRegExpTesterStub(cx);
         return stubs_[RegExpTester];
     }
 
     // Perform the necessary read barriers on stubs and SIMD template object
     // described by the bitmasks passed in. This function can only be called
-    // from the active thread.
+    // from the main thread.
     //
     // The stub and template object pointers must still be valid by the time
     // these methods are called. This is arranged by cancelling off-thread Ion
     // compilation at the start of GC and at the start of sweeping.
     void performStubReadBarriers(uint32_t stubsToBarrier) const;
     void performSIMDTemplateReadBarriers(uint32_t simdTemplatesToBarrier) const;
 
     size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
--- a/js/src/jit/JitOptions.h
+++ b/js/src/jit/JitOptions.h
@@ -11,19 +11,19 @@
 
 #include "jit/IonTypes.h"
 #include "js/TypeDecls.h"
 
 namespace js {
 namespace jit {
 
 // Longer scripts can only be compiled off thread, as these compilations
-// can be expensive and stall the active thread for too long.
-static const uint32_t MAX_ACTIVE_THREAD_SCRIPT_SIZE = 2 * 1000;
-static const uint32_t MAX_ACTIVE_THREAD_LOCALS_AND_ARGS = 256;
+// can be expensive and stall the main thread for too long.
+static const uint32_t MAX_MAIN_THREAD_SCRIPT_SIZE = 2 * 1000;
+static const uint32_t MAX_MAIN_THREAD_LOCALS_AND_ARGS = 256;
 
 // Possible register allocators which may be used.
 enum IonRegisterAllocator {
     RegisterAllocator_Backtracking,
     RegisterAllocator_Testbed,
     RegisterAllocator_Stupid
 };
 
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -949,22 +949,22 @@ MakeUnknownTypeSet(TempAllocator& tempAl
 }
 
 #ifdef DEBUG
 
 bool
 jit::IonCompilationCanUseNurseryPointers()
 {
     // If we are doing backend compilation, which could occur on a helper
-    // thread but might actually be on the active thread, check the flag set on
+    // thread but might actually be on the main thread, check the flag set on
     // the JSContext by AutoEnterIonCompilation.
     if (CurrentThreadIsIonCompiling())
         return !CurrentThreadIsIonCompilingSafeForMinorGC();
 
-    // Otherwise, we must be on the active thread during MIR construction. The
+    // Otherwise, we must be on the main thread during MIR construction. The
     // store buffer must have been notified that minor GCs must cancel pending
     // or in progress Ion compilations.
     JSRuntime* rt = TlsContext.get()->zone()->runtimeFromMainThread();
     return rt->gc.storeBuffer().cancelIonCompilations();
 }
 
 #endif // DEBUG
 
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -4180,17 +4180,17 @@ class MInitElemGetterSetter
 
   public:
     INSTRUCTION_HEADER(InitElemGetterSetter)
     TRIVIAL_NEW_WRAPPERS
     NAMED_OPERANDS((0, object), (1, idValue), (2, value))
 };
 
 // WrappedFunction wraps a JSFunction so it can safely be used off-thread.
-// In particular, a function's flags can be modified on the active thread as
+// In particular, a function's flags can be modified on the main thread as
 // functions are relazified and delazified, so we must be careful not to access
 // these flags off-thread.
 class WrappedFunction : public TempObject
 {
     CompilerFunction fun_;
     uint16_t nargs_;
     bool isNative_ : 1;
     bool isNativeWithJitEntry_ : 1;
@@ -8867,17 +8867,17 @@ class MClassConstructor : public MNullar
         return AliasSet::None();
     }
 };
 
 struct LambdaFunctionInfo
 {
     // The functions used in lambdas are the canonical original function in
     // the script, and are immutable except for delazification. Record this
-    // information while still on the active thread to avoid races.
+    // information while still on the main thread to avoid races.
   private:
     CompilerFunction fun_;
 
   public:
     uint16_t flags;
     uint16_t nargs;
     gc::Cell* scriptOrLazyScript;
     bool singletonType;
--- a/js/src/jit/MIRGenerator.h
+++ b/js/src/jit/MIRGenerator.h
@@ -114,17 +114,17 @@ class MIRGenerator
 
     bool safeForMinorGC() const {
         return safeForMinorGC_;
     }
     void setNotSafeForMinorGC() {
         safeForMinorGC_ = false;
     }
 
-    // Whether the active thread is trying to cancel this build.
+    // Whether the main thread is trying to cancel this build.
     bool shouldCancel(const char* why) {
         return cancelBuild_;
     }
     void cancel() {
         cancelBuild_ = true;
     }
 
     bool compilingWasm() const {
--- a/js/src/jsapi-tests/testHashTable.cpp
+++ b/js/src/jsapi-tests/testHashTable.cpp
@@ -376,17 +376,17 @@ LookupWithDefaultUntilResize() {
 
     return true;
 }
 
 BEGIN_TEST(testHashMapLookupWithDefaultOOM)
 {
     uint32_t timeToFail;
     for (timeToFail = 1; timeToFail < 1000; timeToFail++) {
-        js::oom::SimulateOOMAfter(timeToFail, js::THREAD_TYPE_COOPERATING, false);
+        js::oom::SimulateOOMAfter(timeToFail, js::THREAD_TYPE_MAIN, false);
         LookupWithDefaultUntilResize();
     }
 
     js::oom::ResetSimulatedOOM();
     return true;
 }
 
 END_TEST(testHashMapLookupWithDefaultOOM)
--- a/js/src/jsapi-tests/testOOM.cpp
+++ b/js/src/jsapi-tests/testOOM.cpp
@@ -31,17 +31,17 @@ END_TEST(testOOM)
 #ifdef DEBUG  // js::oom functions are only available in debug builds.
 
 const uint32_t maxAllocsPerTest = 100;
 
 #define START_OOM_TEST(name)                                                  \
     testName = name;                                                          \
     printf("Test %s: started\n", testName);                                   \
     for (oomAfter = 1; oomAfter < maxAllocsPerTest; ++oomAfter) {             \
-    js::oom::SimulateOOMAfter(oomAfter, js::THREAD_TYPE_COOPERATING, true)
+    js::oom::SimulateOOMAfter(oomAfter, js::THREAD_TYPE_MAIN, true)
 
 #define OOM_TEST_FINISHED                                                     \
     {                                                                         \
         printf("Test %s: finished with %" PRIu64 " allocations\n",            \
                testName, oomAfter - 1);                                       \
         break;                                                                \
     }
 
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -868,33 +868,34 @@ JS_PUBLIC_API(JSObject*)
 JS_TransplantObject(JSContext* cx, HandleObject origobj, HandleObject target)
 {
     AssertHeapIsIdle();
     MOZ_ASSERT(origobj != target);
     MOZ_ASSERT(!origobj->is<CrossCompartmentWrapperObject>());
     MOZ_ASSERT(!target->is<CrossCompartmentWrapperObject>());
     MOZ_ASSERT(origobj->getClass() == target->getClass());
     ReleaseAssertObjectHasNoWrappers(cx, target);
+    MOZ_ASSERT(JS::CellIsNotGray(target));
 
     RootedValue origv(cx, ObjectValue(*origobj));
     RootedObject newIdentity(cx);
 
     // Don't allow a compacting GC to observe any intermediate state.
     AutoDisableCompactingGC nocgc(cx);
 
     AutoDisableProxyCheck adpc;
 
     JSCompartment* destination = target->compartment();
 
     if (origobj->compartment() == destination) {
         // If the original object is in the same compartment as the
         // destination, then we know that we won't find a wrapper in the
         // destination's cross compartment map and that the same
         // object will continue to work.
-        AutoCompartment ac(cx, origobj);
+        AutoCompartmentUnchecked ac(cx, origobj->compartment());
         if (!JSObject::swap(cx, origobj, target))
             MOZ_CRASH();
         newIdentity = origobj;
     } else if (WrapperMap::Ptr p = destination->lookupWrapper(origv)) {
         // There might already be a wrapper for the original object in
         // the new compartment. If there is, we use its identity and swap
         // in the contents of |target|.
         newIdentity = &p->value().get().toObject();
@@ -918,28 +919,29 @@ JS_TransplantObject(JSContext* cx, Handl
     // `newIdentity == origobj`, because this process also clears out any
     // cached wrapper state.
     if (!RemapAllWrappersForObject(cx, origobj, newIdentity))
         MOZ_CRASH();
 
     // Lastly, update the original object to point to the new one.
     if (origobj->compartment() != destination) {
         RootedObject newIdentityWrapper(cx, newIdentity);
-        AutoCompartment ac(cx, origobj);
+        AutoCompartmentUnchecked ac(cx, origobj->compartment());
         if (!JS_WrapObject(cx, &newIdentityWrapper))
             MOZ_CRASH();
         MOZ_ASSERT(Wrapper::wrappedObject(newIdentityWrapper) == newIdentity);
         if (!JSObject::swap(cx, origobj, newIdentityWrapper))
             MOZ_CRASH();
         if (!origobj->compartment()->putWrapper(cx, CrossCompartmentKey(newIdentity), origv))
             MOZ_CRASH();
     }
 
     // The new identity object might be one of several things. Return it to avoid
     // ambiguity.
+    MOZ_ASSERT(JS::CellIsNotGray(newIdentity));
     return newIdentity;
 }
 
 /*
  * Recompute all cross-compartment wrappers for an object, resetting state.
  * Gecko uses this to clear Xray wrappers when doing a navigation that reuses
  * the inner window and global object.
  */
@@ -4224,23 +4226,23 @@ CanDoOffThread(JSContext* cx, const Read
 {
     static const size_t TINY_LENGTH = 5 * 1000;
     static const size_t HUGE_SRC_LENGTH = 100 * 1000;
     static const size_t HUGE_BC_LENGTH = 367 * 1000;
 
     // These are heuristics which the caller may choose to ignore (e.g., for
     // testing purposes).
     if (!options.forceAsync) {
-        // Compiling off the active thread inolves creating a new Zone and other
+        // Compiling off the main thread inolves creating a new Zone and other
         // significant overheads.  Don't bother if the script is tiny.
         if (length < TINY_LENGTH)
             return false;
 
         // If the parsing task would have to wait for GC to complete, it'll probably
-        // be faster to just start it synchronously on the active thread unless the
+        // be faster to just start it synchronously on the main thread unless the
         // script is huge.
         if (OffThreadParsingMustWaitForGC(cx->runtime())) {
             if (what == OffThread::Compile && length < HUGE_SRC_LENGTH)
                 return false;
             if (what == OffThread::Decode && length < HUGE_BC_LENGTH)
                 return false;
         }
     }
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -3913,17 +3913,17 @@ CanDecodeOffThread(JSContext* cx, const 
 
 /*
  * Off thread compilation control flow.
  *
  * After successfully triggering an off thread compile of a script, the
  * callback will eventually be invoked with the specified data and a token
  * for the compilation. The callback will be invoked while off thread,
  * so must ensure that its operations are thread safe. Afterwards, one of the
- * following functions must be invoked on the runtime's active thread:
+ * following functions must be invoked on the runtime's main thread:
  *
  * - FinishOffThreadScript, to get the result script (or nullptr on failure).
  * - CancelOffThreadScript, to free the resources without creating a script.
  *
  * The characters passed in to CompileOffThread must remain live until the
  * callback is invoked, and the resulting script will be rooted until the call
  * to FinishOffThreadScript.
  */
@@ -6040,17 +6040,17 @@ extern JS_PUBLIC_API(TranscodeResult)
 DecodeScript(JSContext* cx, const TranscodeRange& range, JS::MutableHandleScript scriptp);
 
 extern JS_PUBLIC_API(TranscodeResult)
 DecodeInterpretedFunction(JSContext* cx, TranscodeBuffer& buffer, JS::MutableHandleFunction funp,
                           size_t cursorIndex = 0);
 
 // Register an encoder on the given script source, such that all functions can
 // be encoded as they are parsed. This strategy is used to avoid blocking the
-// active thread in a non-interruptible way.
+// main thread in a non-interruptible way.
 //
 // The |script| argument of |StartIncrementalEncoding| and
 // |FinishIncrementalEncoding| should be the top-level script returned either as
 // an out-param of any of the |Compile| functions, or the result of
 // |FinishOffThreadScript|.
 //
 // The |buffer| argument of |FinishIncrementalEncoding| is used for appending
 // the encoded bytecode into the buffer. If any of these functions failed, the
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -1738,18 +1738,20 @@ js::ToUint16Slow(JSContext* cx, const Ha
         return false;
     }
     *out = ToUint16(d);
     return true;
 }
 
 // ES2017 draft 7.1.17 ToIndex
 bool
-js::ToIndex(JSContext* cx, JS::HandleValue v, const unsigned errorNumber, uint64_t* index)
+js::ToIndexSlow(JSContext* cx, JS::HandleValue v, const unsigned errorNumber, uint64_t* index)
 {
+    MOZ_ASSERT_IF(v.isInt32(), v.toInt32() < 0);
+
     // Step 1.
     if (v.isUndefined()) {
         *index = 0;
         return true;
     }
 
     // Step 2.a.
     double integerIndex;
--- a/js/src/jsnum.h
+++ b/js/src/jsnum.h
@@ -266,18 +266,31 @@ ToInteger(JSContext* cx, HandleValue v, 
 
 /* ES2017 draft 7.1.17 ToIndex
  *
  * Return true and set |*index| to the integer value if |v| is a valid
  * integer index value. Otherwise report a RangeError and return false.
  *
  * The returned index will always be in the range 0 <= *index <= 2^53-1.
  */
-MOZ_MUST_USE bool
-ToIndex(JSContext* cx, JS::HandleValue v, const unsigned errorNumber, uint64_t* index);
+extern MOZ_MUST_USE bool
+ToIndexSlow(JSContext* cx, JS::HandleValue v, const unsigned errorNumber, uint64_t* index);
+
+static MOZ_MUST_USE inline bool
+ToIndex(JSContext* cx, JS::HandleValue v, const unsigned errorNumber, uint64_t* index)
+{
+    if (v.isInt32()) {
+        int32_t i = v.toInt32();
+        if (i >= 0) {
+            *index = uint64_t(i);
+            return true;
+        }
+    }
+    return ToIndexSlow(cx, v, errorNumber, index);
+}
 
 static MOZ_MUST_USE inline bool
 ToIndex(JSContext* cx, JS::HandleValue v, uint64_t* index)
 {
     return ToIndex(cx, v, JSMSG_BAD_INDEX, index);
 }
 
 MOZ_MUST_USE inline bool
--- a/js/src/jsutil.cpp
+++ b/js/src/jsutil.cpp
@@ -62,17 +62,17 @@ SetThreadType(ThreadType type) {
 uint32_t
 GetThreadType(void) {
     return threadType.get();
 }
 
 static inline bool
 IsHelperThreadType(uint32_t thread)
 {
-    return thread != THREAD_TYPE_NONE && thread != THREAD_TYPE_COOPERATING;
+    return thread != THREAD_TYPE_NONE && thread != THREAD_TYPE_MAIN;
 }
 
 void
 SimulateOOMAfter(uint64_t allocations, uint32_t thread, bool always)
 {
     Maybe<AutoLockHelperThreadState> lock;
     if (IsHelperThreadType(targetThread) || IsHelperThreadType(thread)) {
         lock.emplace();
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -6220,36 +6220,48 @@ EnsureGrayRoot(JSContext* cx, unsigned a
     return true;
 }
 
 static MarkBitObservers*
 EnsureMarkBitObservers(JSContext* cx)
 {
     ShellContext* sc = GetShellContext(cx);
     if (!sc->markObservers) {
-        sc->markObservers.reset(cx->new_<MarkBitObservers>(cx->runtime(),
-                                                         NonshrinkingGCObjectVector()));
+        auto* observers =
+            cx->new_<MarkBitObservers>(cx->runtime(), NonshrinkingGCObjectVector());
+        if (!observers)
+            return nullptr;
+        sc->markObservers.reset(observers);
     }
     return sc->markObservers.get();
 }
 
 static bool
 ClearMarkObservers(JSContext* cx, unsigned argc, Value* vp)
 {
+    CallArgs args = CallArgsFromVp(argc, vp);
+
     auto markObservers = EnsureMarkBitObservers(cx);
+    if (!markObservers)
+        return false;
+
     markObservers->get().clear();
+
+    args.rval().setUndefined();
     return true;
 }
 
 static bool
 AddMarkObservers(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     auto markObservers = EnsureMarkBitObservers(cx);
+    if (!markObservers)
+        return false;
 
     if (!args.get(0).isObject()) {
         JS_ReportErrorASCII(cx, "argument must be an Array of objects");
         return false;
     }
 
     // WeakCaches are not swept during a minor GC. To prevent nursery-allocated
     // contents from having the mark bits be deceptively black until the second
@@ -7094,16 +7106,44 @@ JS_FN_HELP("parseBin", BinParse, 1, 0,
 "Take jobs from the shell's job queue in FIFO order and run them until the\n"
 "queue is empty.\n"),
 
     JS_FN_HELP("setPromiseRejectionTrackerCallback", SetPromiseRejectionTrackerCallback, 1, 0,
 "setPromiseRejectionTrackerCallback()",
 "Sets the callback to be invoked whenever a Promise rejection is unhandled\n"
 "or a previously-unhandled rejection becomes handled."),
 
+    JS_FN_HELP("dumpScopeChain", DumpScopeChain, 1, 0,
+"dumpScopeChain(obj)",
+"  Prints the scope chain of an interpreted function or a module."),
+
+    JS_FN_HELP("grayRoot", EnsureGrayRoot, 0, 0,
+"grayRoot()",
+"  Create a gray root Array, if needed, for the current compartment, and\n"
+"  return it."),
+
+    JS_FN_HELP("addMarkObservers", AddMarkObservers, 1, 0,
+"addMarkObservers(array_of_objects)",
+"  Register an array of objects whose mark bits will be tested by calls to\n"
+"  getMarks. The objects will be in calling compartment. Objects from\n"
+"  multiple compartments may be monitored by calling this function in\n"
+"  different compartments."),
+
+    JS_FN_HELP("clearMarkObservers", ClearMarkObservers, 1, 0,
+"clearMarkObservers()",
+"  Clear out the list of objects whose mark bits will be tested.\n"),
+
+    JS_FN_HELP("getMarks", GetMarks, 0, 0,
+"getMarks()",
+"  Return an array of strings representing the current state of the mark\n"
+"  bits ('gray' or 'black', or 'dead' if the object has been collected)\n"
+"  for the objects registered via addMarkObservers. Note that some of the\n"
+"  objects tested may be from different compartments than the one in which\n"
+"  this function runs."),
+
     JS_FN_HELP("bindToAsyncStack", BindToAsyncStack, 2, 0,
 "bindToAsyncStack(fn, { stack, cause, explicit })",
 "  Returns a new function that calls 'fn' with no arguments, passing\n"
 "  'undefined' as the 'this' value, and supplies an async stack for the\n"
 "  call as described by the second argument, an object with the following\n"
 "  properties (which are not optional, unless specified otherwise):\n"
 "\n"
 "  stack:    A SavedFrame object, like that returned by 'saveStack'. Stacks\n"
@@ -7185,44 +7225,16 @@ TestAssertRecoveredOnBailout,
 "  performed, and which, presumably, only |hook| knows how to find.\n"),
 
     JS_FN_HELP("trackedOpts", ReflectTrackedOptimizations, 1, 0,
 "trackedOpts(fun)",
 "  Returns an object describing the tracked optimizations of |fun|, if\n"
 "  any. If |fun| is not a scripted function or has not been compiled by\n"
 "  Ion, null is returned."),
 
-    JS_FN_HELP("dumpScopeChain", DumpScopeChain, 1, 0,
-"dumpScopeChain(obj)",
-"  Prints the scope chain of an interpreted function or a module."),
-
-    JS_FN_HELP("grayRoot", EnsureGrayRoot, 0, 0,
-"grayRoot()",
-"  Create a gray root Array, if needed, for the current compartment, and\n"
-"  return it."),
-
-    JS_FN_HELP("addMarkObservers", AddMarkObservers, 1, 0,
-"addMarkObservers(array_of_objects)",
-"  Register an array of objects whose mark bits will be tested by calls to\n"
-"  getMarks. The objects will be in calling compartment. Objects from\n"
-"  multiple compartments may be monitored by calling this function in\n"
-"  different compartments."),
-
-    JS_FN_HELP("clearMarkObservers", ClearMarkObservers, 1, 0,
-"clearMarkObservers()",
-"  Clear out the list of objects whose mark bits will be tested.\n"),
-
-    JS_FN_HELP("getMarks", GetMarks, 0, 0,
-"getMarks()",
-"  Return an array of strings representing the current state of the mark\n"
-"  bits ('gray' or 'black', or 'dead' if the object has been collected)\n"
-"  for the objects registered via addMarkObservers. Note that some of the\n"
-"  objects tested may be from different compartments than the one in which\n"
-"  this function runs."),
-
     JS_FN_HELP("crash", Crash, 0, 0,
 "crash([message, [{disable_minidump:true}]])",
 "  Crashes the process with a MOZ_CRASH, optionally providing a message.\n"
 "  An options object may be passed as the second argument. If the key\n"
 "  'suppress_minidump' is set to true, then a minidump will not be\n"
 "  generated by the crash (which only has an effect if the breakpad\n"
 "  dumping library is loaded.)"),
 
--- a/js/src/vm/ErrorReporting.cpp
+++ b/js/src/vm/ErrorReporting.cpp
@@ -54,17 +54,17 @@ js::CompileError::throwError(JSContext* 
     // uncaught exception report.
     ErrorToException(cx, this, nullptr, nullptr);
 }
 
 bool
 js::ReportCompileWarning(JSContext* cx, ErrorMetadata&& metadata, UniquePtr<JSErrorNotes> notes,
                          unsigned flags, unsigned errorNumber, va_list args)
 {
-    // On the active thread, report the error immediately. When compiling off
+    // On the main thread, report the error immediately. When compiling off
     // thread, save the error so that the thread finishing the parse can report
     // it later.
     CompileError tempErr;
     CompileError* err = &tempErr;
     if (cx->helperThread() && !cx->addPendingCompileError(&err))
         return false;
 
     err->notes = Move(notes);
@@ -90,17 +90,17 @@ js::ReportCompileWarning(JSContext* cx, 
 
     return true;
 }
 
 void
 js::ReportCompileError(JSContext* cx, ErrorMetadata&& metadata, UniquePtr<JSErrorNotes> notes,
                        unsigned flags, unsigned errorNumber, va_list args)
 {
-    // On the active thread, report the error immediately. When compiling off
+    // On the main thread, report the error immediately. When compiling off
     // thread, save the error so that the thread finishing the parse can report
     // it later.
     CompileError tempErr;
     CompileError* err = &tempErr;
     if (cx->helperThread() && !cx->addPendingCompileError(&err))
         return;
 
     err->notes = Move(notes);
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -594,17 +594,17 @@ js::CancelOffThreadParses(JSRuntime* rt)
                     inProgress = true;
             }
             if (!inProgress)
                 break;
         }
         HelperThreadState().wait(lock, GlobalHelperThreadState::CONSUMER);
     }
 
-    // Clean up any parse tasks which haven't been finished by the active thread.
+    // Clean up any parse tasks which haven't been finished by the main thread.
     GlobalHelperThreadState::ParseTaskVector& finished = HelperThreadState().parseFinishedList(lock);
     while (true) {
         bool found = false;
         for (size_t i = 0; i < finished.length(); i++) {
             ParseTask* task = finished[i];
             if (task->runtimeMatches(rt)) {
                 found = true;
                 AutoUnlockHelperThreadState unlock(lock);
@@ -1755,17 +1755,17 @@ HelperThread::handleWasmWorkload(AutoLoc
     currentTask.emplace(HelperThreadState().wasmWorklist(locked, mode).popCopyFront());
 
     wasm::CompileTask* task = wasmTask();
     {
         AutoUnlockHelperThreadState unlock(locked);
         wasm::ExecuteCompileTaskFromHelperThread(task);
     }
 
-    // No active thread should be waiting on the CONSUMER mutex.
+    // No main thread should be waiting on the CONSUMER mutex.
     currentTask.reset();
 }
 
 void
 HelperThread::handleWasmTier2GeneratorWorkload(AutoLockHelperThreadState& locked)
 {
     MOZ_ASSERT(HelperThreadState().canStartWasmTier2Generator(locked));
     MOZ_ASSERT(idle());
@@ -1798,17 +1798,17 @@ HelperThread::handlePromiseHelperTaskWor
     currentTask.emplace(task);
 
     {
         AutoUnlockHelperThreadState unlock(locked);
         task->execute();
         task->dispatchResolveAndDestroy();
     }
 
-    // No active thread should be waiting on the CONSUMER mutex.
+    // No main thread should be waiting on the CONSUMER mutex.
     currentTask.reset();
 }
 
 void
 HelperThread::handleIonWorkload(AutoLockHelperThreadState& locked)
 {
     MOZ_ASSERT(HelperThreadState().canStartIonCompile(locked));
     MOZ_ASSERT(idle());
@@ -1846,17 +1846,17 @@ HelperThread::handleIonWorkload(AutoLock
     // This must happen before the current task is reset. DestroyContext
     // cancels in progress Ion compilations before destroying its target
     // context, and after we reset the current task we are no longer considered
     // to be Ion compiling.
     rt->mainContextFromAnyThread()->requestInterrupt(JSContext::RequestInterruptCanWait);
 
     currentTask.reset();
 
-    // Notify the active thread in case it is waiting for the compilation to finish.
+    // Notify the main thread in case it is waiting for the compilation to finish.
     HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked);
 }
 
 void
 HelperThread::handleIonFreeWorkload(AutoLockHelperThreadState& locked)
 {
     MOZ_ASSERT(idle());
     MOZ_ASSERT(HelperThreadState().canStartIonFreeTask(locked));
@@ -1948,17 +1948,17 @@ HelperThread::handleParseWorkload(AutoLo
     {
         AutoEnterOOMUnsafeRegion oomUnsafe;
         if (!HelperThreadState().parseFinishedList(locked).append(task))
             oomUnsafe.crash("handleParseWorkload");
     }
 
     currentTask.reset();
 
-    // Notify the active thread in case it is waiting for the parse/emit to finish.
+    // Notify the main thread in case it is waiting for the parse/emit to finish.
     HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked);
 }
 
 void
 HelperThread::handleCompressionWorkload(AutoLockHelperThreadState& locked)
 {
     MOZ_ASSERT(HelperThreadState().canStartCompressionTask(locked));
     MOZ_ASSERT(idle());
@@ -1983,17 +1983,17 @@ HelperThread::handleCompressionWorkload(
     {
         AutoEnterOOMUnsafeRegion oomUnsafe;
         if (!HelperThreadState().compressionFinishedList(locked).append(Move(task)))
             oomUnsafe.crash("handleCompressionWorkload");
     }
 
     currentTask.reset();
 
-    // Notify the active thread in case it is waiting for the compression to finish.
+    // Notify the main thread in case it is waiting for the compression to finish.
     HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, locked);
 }
 
 bool
 js::EnqueueOffThreadCompression(JSContext* cx, UniquePtr<SourceCompressionTask> task)
 {
     AutoLockHelperThreadState lock;
 
--- a/js/src/vm/JSContext.cpp
+++ b/js/src/vm/JSContext.cpp
@@ -127,17 +127,17 @@ JSContext::init(ContextKind kind)
 JSContext*
 js::NewContext(uint32_t maxBytes, uint32_t maxNurseryBytes, JSRuntime* parentRuntime)
 {
     AutoNoteSingleThreadedRegion anstr;
 
     MOZ_RELEASE_ASSERT(!TlsContext.get());
 
 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
-    js::oom::SetThreadType(!parentRuntime ? js::THREAD_TYPE_COOPERATING
+    js::oom::SetThreadType(!parentRuntime ? js::THREAD_TYPE_MAIN
                                           : js::THREAD_TYPE_WORKER);
 #endif
 
     JSRuntime* runtime = js_new<JSRuntime>(parentRuntime);
     if (!runtime)
         return nullptr;
 
     JSContext* cx = js_new<JSContext>(runtime, JS::ContextOptions());
--- a/js/src/vm/JSContext.h
+++ b/js/src/vm/JSContext.h
@@ -459,22 +459,22 @@ struct JSContext : public JS::RootingCon
     js::ThreadData<bool> ionCompiling;
 
     // Whether this thread is actively Ion compiling in a context where a minor
     // GC could happen simultaneously. If this is true, this thread cannot use
     // any pointers into the nursery.
     js::ThreadData<bool> ionCompilingSafeForMinorGC;
 
     // Whether this thread is currently performing GC.  This thread could be the
-    // active thread or a helper thread while the active thread is running the
+    // main thread or a helper thread while the main thread is running the
     // collector.
     js::ThreadData<bool> performingGC;
 
     // Whether this thread is currently sweeping GC things.  This thread could
-    // be the active thread or a helper thread while the active thread is running
+    // be the main thread or a helper thread while the main thread is running
     // the mutator.  This is used to assert that destruction of GCPtr only
     // happens when we are sweeping.
     js::ThreadData<bool> gcSweeping;
 
     // Whether this thread is performing work in the background for a runtime's
     // GCHelperState.
     js::ThreadData<bool> gcHelperStateThread;
 
@@ -536,17 +536,17 @@ struct JSContext : public JS::RootingCon
     js::ThreadData<unsigned> generationalDisabled;
 
     // Some code cannot tolerate compacting GC so it can be disabled temporarily
     // with AutoDisableCompactingGC which uses this counter.
     js::ThreadData<unsigned> compactingDisabledCount;
 
     // Count of AutoKeepAtoms instances on the current thread's stack. When any
     // instances exist, atoms in the runtime will not be collected. Threads
-    // parsing off the active thread do not increment this value, but the presence
+    // parsing off the main thread do not increment this value, but the presence
     // of any such threads also inhibits collection of atoms. We don't scan the
     // stacks of exclusive threads, so we need to avoid collecting their
     // objects in another way. The only GC thing pointers they have are to
     // their exclusive compartment (which is not collected) or to the atoms
     // compartment. Therefore, we avoid collecting the atoms compartment when
     // exclusive threads are running.
     js::ThreadData<unsigned> keepAtoms;
 
--- a/js/src/vm/JSObject-inl.h
+++ b/js/src/vm/JSObject-inl.h
@@ -90,17 +90,17 @@ JSObject::ensureShape(JSContext* cx)
 inline void
 JSObject::finalize(js::FreeOp* fop)
 {
     js::probes::FinalizeObject(this);
 
 #ifdef DEBUG
     MOZ_ASSERT(isTenured());
     if (!IsBackgroundFinalized(asTenured().getAllocKind())) {
-        /* Assert we're on the active thread. */
+        /* Assert we're on the main thread. */
         MOZ_ASSERT(CurrentThreadCanAccessZone(zone()));
     }
 #endif
 
     const js::Class* clasp = getClass();
     js::NativeObject* nobj = nullptr;
     if (clasp->isNative())
         nobj = &as<js::NativeObject>();
--- a/js/src/vm/JSObject.cpp
+++ b/js/src/vm/JSObject.cpp
@@ -4111,17 +4111,17 @@ JSObject::debugCheckNewObject(ObjectGrou
         MOZ_ASSERT(clasp == &UnboxedPlainObject::class_);
 
     if (!ClassCanHaveFixedData(clasp)) {
         MOZ_ASSERT(shape);
         MOZ_ASSERT(gc::GetGCKindSlots(allocKind, clasp) == shape->numFixedSlots());
     }
 
     // Classes with a finalizer must specify whether instances will be finalized
-    // on the active thread or in the background, except proxies whose behaviour
+    // on the main thread or in the background, except proxies whose behaviour
     // depends on the target object.
     static const uint32_t FinalizeMask = JSCLASS_FOREGROUND_FINALIZE | JSCLASS_BACKGROUND_FINALIZE;
     uint32_t flags = clasp->flags;
     uint32_t finalizeFlags = flags & FinalizeMask;
     if (clasp->hasFinalize() && !clasp->isProxy()) {
         MOZ_ASSERT(finalizeFlags == JSCLASS_FOREGROUND_FINALIZE ||
                    finalizeFlags == JSCLASS_BACKGROUND_FINALIZE);
         MOZ_ASSERT((finalizeFlags == JSCLASS_BACKGROUND_FINALIZE) == IsBackgroundFinalized(allocKind));
--- a/js/src/vm/JSScript.h
+++ b/js/src/vm/JSScript.h
@@ -388,18 +388,18 @@ class ScriptSource
             return chars_;
         }
     };
 
   private:
     uint32_t refs;
 
     // Note: while ScriptSources may be compressed off thread, they are only
-    // modified by the active thread, and all members are always safe to access
-    // on the active thread.
+    // modified by the main thread, and all members are always safe to access
+    // on the main thread.
 
     // Indicate which field in the |data| union is active.
 
     struct Missing { };
 
     struct Uncompressed
     {
         SharedImmutableTwoByteString string;
--- a/js/src/vm/ObjectGroup-inl.h
+++ b/js/src/vm/ObjectGroup-inl.h
@@ -10,17 +10,17 @@
 #include "vm/ObjectGroup.h"
 
 namespace js {
 
 inline bool
 ObjectGroup::needsSweep()
 {
     // Note: this can be called off thread during compacting GCs, in which case
-    // nothing will be running on the active thread.
+    // nothing will be running on the main thread.
     MOZ_ASSERT(!TlsContext.get()->inUnsafeCallWithABI);
     return generation() != zoneFromAnyThread()->types.generation;
 }
 
 inline void
 ObjectGroup::maybeSweep(AutoClearTypeInferenceStateOnOOM* oom)
 {
     if (needsSweep())
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -286,17 +286,17 @@ JSRuntime::destroyRuntime()
             FinishGC(cx);
 
         /* Free source hook early, as its destructor may want to delete roots. */
         sourceHook = nullptr;
 
         /*
          * Cancel any pending, in progress or completed Ion compilations and
          * parse tasks. Waiting for wasm and compression tasks is done
-         * synchronously (on the active thread or during parse tasks), so no
+         * synchronously (on the main thread or during parse tasks), so no
          * explicit canceling is needed for these.
          */
         CancelOffThreadIonCompile(this);
         CancelOffThreadParses(this);
         CancelOffThreadCompressions(this);
 
         /* Remove persistent GC roots. */
         gc.finishRoots();
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -457,29 +457,29 @@ struct JSRuntime : public js::MallocProv
     mozilla::LinkedList<js::Debugger>& debuggerList() { return debuggerList_.ref(); }
 
   private:
     /*
      * Lock taken when using per-runtime or per-zone data that could otherwise
      * be accessed simultaneously by multiple threads.
      *
      * Locking this only occurs if there is actually a thread other than the
-     * active thread which could access such data.
+     * main thread which could access such data.
      */
     js::Mutex exclusiveAccessLock;
 #ifdef DEBUG
     bool activeThreadHasExclusiveAccess;
 #endif
 
     /*
      * Lock used to protect the script data table, which can be used by
      * off-thread parsing.
      *
      * Locking this only occurs if there is actually a thread other than the
-     * active thread which could access this.
+     * main thread which could access this.
      */
     js::Mutex scriptDataLock;
 #ifdef DEBUG
     bool activeThreadHasScriptDataAccess;
 #endif
 
     // Number of zones which may be operated on by helper threads.
     mozilla::Atomic<size_t> numActiveHelperThreadZones;
--- a/js/src/vm/SymbolType.cpp
+++ b/js/src/vm/SymbolType.cpp
@@ -39,17 +39,17 @@ Symbol::new_(JSContext* cx, JS::SymbolCo
     JSAtom* atom = nullptr;
     if (description) {
         atom = AtomizeString(cx, description);
         if (!atom)
             return nullptr;
     }
 
     // Lock to allocate. If symbol allocation becomes a bottleneck, this can
-    // probably be replaced with an assertion that we're on the active thread.
+    // probably be replaced with an assertion that we're on the main thread.
     AutoLockForExclusiveAccess lock(cx);
     Symbol* sym;
     {
         AutoAtomsCompartment ac(cx, lock);
         sym = newInternal(cx, code, cx->compartment()->randomHashCode(), atom, lock);
     }
     if (sym)
         cx->markAtom(sym);
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -1016,40 +1016,40 @@ TypeSet::intersectSets(TemporaryTypeSet*
 // Compiler constraints
 /////////////////////////////////////////////////////////////////////
 
 // Compiler constraints overview
 //
 // Constraints generated during Ion compilation capture assumptions made about
 // heap properties that will trigger invalidation of the resulting Ion code if
 // the constraint is violated. Constraints can only be attached to type sets on
-// the active thread, so to allow compilation to occur almost entirely off thread
+// the main thread, so to allow compilation to occur almost entirely off thread
 // the generation is split into two phases.
 //
 // During compilation, CompilerConstraint values are constructed in a list,
 // recording the heap property type set which was read from and its expected
 // contents, along with the assumption made about those contents.
 //
-// At the end of compilation, when linking the result on the active thread, the
+// At the end of compilation, when linking the result on the main thread, the
 // list of compiler constraints are read and converted to type constraints and
 // attached to the type sets. If the property type sets have changed so that the
 // assumptions no longer hold then the compilation is aborted and its result
 // discarded.
 
 // Superclass of all constraints generated during Ion compilation. These may
 // be allocated off thread, using the current JIT context's allocator.
 class CompilerConstraint
 {
   public:
     // Property being queried by the compiler.
     HeapTypeSetKey property;
 
     // Contents of the property at the point when the query was performed. This
     // may differ from the actual property types later in compilation as the
-    // active thread performs side effects.
+    // main thread performs side effects.
     TemporaryTypeSet* expected;
 
     CompilerConstraint(LifoAlloc* alloc, const HeapTypeSetKey& property)
       : property(property),
         expected(property.maybeTypes() ? property.maybeTypes()->clone(alloc) : nullptr)
     {}
 
     // Generate the type constraint recording the assumption made by this
@@ -1309,17 +1309,17 @@ TypeSet::ObjectKey::property(jsid id)
     return property;
 }
 
 void
 TypeSet::ObjectKey::ensureTrackedProperty(JSContext* cx, jsid id)
 {
     // If we are accessing a lazily defined property which actually exists in
     // the VM and has not been instantiated yet, instantiate it now if we are
-    // on the active thread and able to do so.
+    // on the main thread and able to do so.
     if (!JSID_IS_VOID(id) && !JSID_IS_EMPTY(id)) {
         MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
         if (isSingleton()) {
             JSObject* obj = singleton();
             if (obj->isNative() && obj->as<NativeObject>().containsPure(id))
                 EnsureTrackPropertyTypes(cx, obj, id);
         }
     }
@@ -1507,17 +1507,17 @@ js::FinishCompilation(JSContext* cx, Han
 
     *isValidOut = true;
     return true;
 }
 
 static void
 CheckDefinitePropertiesTypeSet(JSContext* cx, TemporaryTypeSet* frozen, StackTypeSet* actual)
 {
-    // The definite properties analysis happens on the active thread, so no new
+    // The definite properties analysis happens on the main thread, so no new
     // types can have been added to actual. The analysis may have updated the
     // contents of |frozen| though with new speculative types, and these need
     // to be reflected in |actual| for AddClearDefiniteFunctionUsesInScript
     // to work.
     if (!frozen->isSubset(actual)) {
         TypeSet::TypeList list;
         frozen->enumerateTypes(&list);
 
@@ -4333,17 +4333,17 @@ ObjectGroup::sweep(AutoClearTypeInferenc
 
     Maybe<AutoClearTypeInferenceStateOnOOM> fallbackOOM;
     EnsureHasAutoClearTypeInferenceStateOnOOM(oom, zone(), fallbackOOM);
 
     AutoTouchingGrayThings tgt;
 
     if (maybeUnboxedLayout()) {
         // Remove unboxed layouts that are about to be finalized from the
-        // compartment wide list while we are still on the active thread.
+        // compartment wide list while we are still on the main thread.
         ObjectGroup* group = this;
         if (IsAboutToBeFinalizedUnbarriered(&group))
             unboxedLayout().detachFromCompartment();
 
         if (unboxedLayout().newScript())
             unboxedLayout().newScript()->sweep();
 
         // Discard constructor code to avoid holding onto ExecutablePools.
--- a/js/src/vm/TypeInference.h
+++ b/js/src/vm/TypeInference.h
@@ -1293,20 +1293,20 @@ FinishCompilation(JSContext* cx, HandleS
 
 // Update the actual types in any scripts queried by constraints with any
 // speculative types added during the definite properties analysis.
 void
 FinishDefinitePropertiesAnalysis(JSContext* cx, CompilerConstraintList* constraints);
 
 // Representation of a heap type property which may or may not be instantiated.
 // Heap properties for singleton types are instantiated lazily as they are used
-// by the compiler, but this is only done on the active thread. If we are
+// by the compiler, but this is only done on the main thread. If we are
 // compiling off thread and use a property which has not yet been instantiated,
 // it will be treated as empty and non-configured and will be instantiated when
-// rejoining to the active thread. If it is in fact not empty, the compilation
+// rejoining to the main thread. If it is in fact not empty, the compilation
 // will fail; to avoid this, we try to instantiate singleton property types
 // during generation of baseline caches.
 class HeapTypeSetKey
 {
     friend class TypeSet::ObjectKey;
 
     // Object and property being accessed.
     TypeSet::ObjectKey* object_;
--- a/js/src/wasm/WasmSignalHandlers.cpp
+++ b/js/src/wasm/WasmSignalHandlers.cpp
@@ -1031,17 +1031,17 @@ HandleMachException(JSContext* cx, const
 
     if (request.body.exception != EXC_BAD_ACCESS &&
         request.body.exception != EXC_BAD_INSTRUCTION)
     {
         return false;
     }
 
     // The faulting thread is suspended so we can access cx fields that can
-    // normally only be accessed by the cx's active thread.
+    // normally only be accessed by the cx's main thread.
     AutoNoteSingleThreadedRegion anstr;
 
     const CodeSegment* codeSegment = LookupCodeSegment(pc);
     if (!codeSegment || !codeSegment->isModule())
         return false;
 
     const ModuleSegment* moduleSegment = codeSegment->asModule();
 
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -1281,16 +1281,19 @@ PresShell::Destroy()
   }
 
   if (mAccessibleCaretEventHub) {
     mAccessibleCaretEventHub->Terminate();
     mAccessibleCaretEventHub = nullptr;
   }
 
   // release our pref style sheet, if we have one still
+  //
+  // FIXME(emilio): Why do we need to do this? The stylist is getting nixed with
+  // us anyway.
   RemovePreferenceStyles();
 
   mIsDestroying = true;
 
   // We can't release all the event content in
   // mCurrentEventContentStack here since there might be code on the
   // stack that will release the event content too. Double release
   // bad!
@@ -1498,27 +1501,31 @@ PresShell::UpdatePreferenceStyles()
   if (mPrefStyleSheet == newPrefSheet) {
     return;
   }
 
   mStyleSet->BeginUpdate();
 
   RemovePreferenceStyles();
 
-  mStyleSet->AppendStyleSheet(SheetType::User, newPrefSheet->AsServo());
+  // NOTE(emilio): This sheet is added as an agent sheet, because we don't want
+  // it to be modifiable from devtools and similar, see bugs 1239336 and
+  // 1436782. I think it conceptually should be a user sheet, and could be
+  // without too much trouble I'd think.
+  mStyleSet->AppendStyleSheet(SheetType::Agent, newPrefSheet->AsServo());
   mPrefStyleSheet = newPrefSheet;
 
   mStyleSet->EndUpdate();
 }
 
 void
 PresShell::RemovePreferenceStyles()
 {
   if (mPrefStyleSheet) {
-    mStyleSet->RemoveStyleSheet(SheetType::User, mPrefStyleSheet->AsServo());
+    mStyleSet->RemoveStyleSheet(SheetType::Agent, mPrefStyleSheet->AsServo());
     mPrefStyleSheet = nullptr;
   }
 }
 
 void
 PresShell::AddUserSheet(StyleSheet* aSheet)
 {
   // Make sure this does what nsDocumentViewer::CreateStyleSet does wrt
--- a/layout/generic/WritingModes.h
+++ b/layout/generic/WritingModes.h
@@ -1925,17 +1925,17 @@ public:
     mBStart = std::max(aRect1.mBStart, aRect2.mBStart);
     mBSize = bEnd - mBStart;
 
     if (mISize < 0 || mBSize < 0) {
       mISize = 0;
       mBSize = 0;
     }
 
-    MOZ_ASSERT(rectDebug.IsEqualEdges(nsRect(mIStart, mBStart, mISize, mBSize)));
+    MOZ_ASSERT((rectDebug.IsEmpty() && (mISize == 0 || mBSize == 0)) || rectDebug.IsEqualEdges(nsRect(mIStart, mBStart, mISize, mBSize)));
     return mISize > 0 && mBSize > 0;
   }
 
 private:
   LogicalRect() = delete;
 
 #ifdef DEBUG
   WritingMode GetWritingMode() const { return mWritingMode; }
--- a/layout/reftests/async-scrolling/reftest.list
+++ b/layout/reftests/async-scrolling/reftest.list
@@ -3,17 +3,17 @@ skip-if(!asyncPan) == bg-fixed-cover-1.h
 skip-if(!asyncPan) == bg-fixed-cover-2.html bg-fixed-cover-2-ref.html
 skip-if(!asyncPan) == bg-fixed-cover-3.html bg-fixed-cover-3-ref.html
 skip-if(!asyncPan) == bg-fixed-child.html bg-fixed-child-ref.html
 skip-if(!asyncPan) == bg-fixed-child-clip-1.html bg-fixed-child-clip-ref.html
 skip-if(!asyncPan) == bg-fixed-child-clip-2.html bg-fixed-child-clip-ref.html
 fuzzy(1,246) fuzzy-if(skiaContent,2,170) fuzzy-if(browserIsRemote&&d2d,59,187) skip-if(!asyncPan) == bg-fixed-child-mask.html bg-fixed-child-mask-ref.html
 skip-if(!asyncPan) == bg-fixed-in-opacity.html bg-fixed-in-opacity-ref.html
 # Passing the test below without WebRender would require implementing CSS filters in the Gecko compositor.
-fails-if(!webrender) skip-if(!asyncPan) == bg-fixed-in-css-filter.html bg-fixed-in-css-filter-ref.html
+fails-if(!webrender) skip-if(!asyncPan) fuzzy-if(webrender&&gtkWidget,0-1,0-59) == bg-fixed-in-css-filter.html bg-fixed-in-css-filter-ref.html # bug 1454794 for webrender fuzziness
 skip-if(!asyncPan) == bg-fixed-child-no-culling-1.html bg-fixed-child-no-culling-1-ref.html
 skip-if(!asyncPan) == bg-fixed-child-no-culling-2.html bg-fixed-child-no-culling-2-ref.html
 skip-if(!asyncPan) == bg-fixed-child-no-culling-3.html bg-fixed-child-no-culling-3-ref.html
 fuzzy-if(Android,2,4000) fuzzy-if(browserIsRemote&&cocoaWidget,2,179524) fuzzy-if(browserIsRemote&&winWidget,1,74590) fuzzy-if(gtkWidget&&layersGPUAccelerated,1,3528) skip-if(!asyncPan) == bg-fixed-transformed-image.html bg-fixed-transformed-image-ref.html
 skip-if(!asyncPan) == element-1.html element-1-ref.html
 pref(layers.force-active,true) skip-if(!asyncPan) == iframe-1.html iframe-1-ref.html
 skip-if(!asyncPan) == nested-1.html nested-1-ref.html
 skip-if(!asyncPan) == nested-2.html nested-2-ref.html
--- a/layout/style/FontFaceSet.cpp
+++ b/layout/style/FontFaceSet.cpp
@@ -953,44 +953,54 @@ FontFaceSet::FindOrCreateUserFontEntryFr
     // usable font, so there is no point in processing it further.
     return nullptr;
   }
 
   return FindOrCreateUserFontEntryFromFontFace(fontfamily, aFontFace,
                                                SheetType::Doc);
 }
 
+static FontWeight
+GetWeightForDescriptor(const nsCSSValue& aVal)
+{
+  switch (aVal.GetUnit()) {
+    case eCSSUnit_FontWeight:
+      return aVal.GetFontWeight();
+    case eCSSUnit_Enumerated:
+      return FontWeight(aVal.GetIntValue());
+    case eCSSUnit_Normal:
+    case eCSSUnit_Null:
+      return FontWeight::Normal();
+    case eCSSUnit_Pair:
+      // TODO(jfkthame): Handle optional second value of the font descriptor.
+      return GetWeightForDescriptor(aVal.GetPairValue().mXValue);
+    default:
+      MOZ_ASSERT_UNREACHABLE("Unknown font-weight descriptor value");
+      return FontWeight::Normal();
+  }
+}
+
 /* static */ already_AddRefed<gfxUserFontEntry>
 FontFaceSet::FindOrCreateUserFontEntryFromFontFace(const nsAString& aFamilyName,
                                                    FontFace* aFontFace,
                                                    SheetType aSheetType)
 {
   FontFaceSet* set = aFontFace->GetPrimaryFontFaceSet();
 
   nsCSSValue val;
   nsCSSUnit unit;
 
-  FontWeight weight = FontWeight::Normal();
   uint32_t stretch = NS_STYLE_FONT_STRETCH_NORMAL;
   uint8_t italicStyle = NS_STYLE_FONT_STYLE_NORMAL;
   uint32_t languageOverride = NO_FONT_LANGUAGE_OVERRIDE;
   uint8_t fontDisplay = NS_FONT_DISPLAY_AUTO;
 
   // set up weight
   aFontFace->GetDesc(eCSSFontDesc_Weight, val);
-  unit = val.GetUnit();
-  if (unit == eCSSUnit_FontWeight) {
-    weight = val.GetFontWeight();
-  } else if (unit == eCSSUnit_Enumerated) {
-    weight = FontWeight(val.GetIntValue());
-  } else if (unit == eCSSUnit_Normal) {
-    weight = FontWeight::Normal();
-  } else {
-    MOZ_ASSERT(unit == eCSSUnit_Null, "@font-face weight has unexpected unit");
-  }
+  FontWeight weight = GetWeightForDescriptor(val);
 
   // set up stretch
   aFontFace->GetDesc(eCSSFontDesc_Stretch, val);
   unit = val.GetUnit();
   if (unit == eCSSUnit_Enumerated) {
     stretch = val.GetIntValue();
   } else if (unit == eCSSUnit_Normal) {
     stretch = NS_STYLE_FONT_STRETCH_NORMAL;
@@ -1232,23 +1242,25 @@ FontFaceSet::LogMessage(gfxUserFontEntry
   if (!console) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   nsAutoCString familyName;
   nsAutoCString fontURI;
   aUserFontEntry->GetFamilyNameAndURIForLogging(familyName, fontURI);
 
+  nsAutoCString weightString;
+  aUserFontEntry->Weight().ToString(weightString);
   nsPrintfCString message
        ("downloadable font: %s "
-        "(font-family: \"%s\" style:%s weight:%g stretch:%s src index:%d)",
+        "(font-family: \"%s\" style:%s weight:%s stretch:%s src index:%d)",
         aMessage,
         familyName.get(),
         aUserFontEntry->IsItalic() ? "italic" : "normal",
-        aUserFontEntry->Weight().ToFloat(),
+        weightString.get(),
         nsCSSProps::ValueToKeyword(aUserFontEntry->Stretch(),
                                    nsCSSProps::kFontStretchKTable).get(),
         aUserFontEntry->GetSrcIndex());
 
   if (NS_FAILED(aStatus)) {
     message.AppendLiteral(": ");
     switch (aStatus) {
     case NS_ERROR_DOM_BAD_URI:
--- a/layout/style/test/descriptor_database.js
+++ b/layout/style/test/descriptor_database.js
@@ -24,18 +24,23 @@ var gCSSFontFaceDescriptors = {
 	},
 	"font-style": {
 		domProp: "fontStyle",
 		values: [ "normal", "italic", "oblique" ],
 		invalid_values: []
 	},
 	"font-weight": {
 		domProp: "fontWeight",
-		values: [ "normal", "400", "bold", "100", "200", "300", "500", "600", "700", "800", "900" ],
-		invalid_values: [ "107", "399", "401", "699", "710", "bolder", "lighter" ]
+		values: [
+			"normal", "400", "bold", "100", "200", "300", "500", "600",
+			"700", "800", "900", "107", "399", "401", "699", "710",
+			"calc(1001)", "calc(100 + 1)", "calc(1)", "100.6", "99",
+			"700 900", "300.4 500.4", "calc(200.4) calc(400.4)",
+		],
+		invalid_values: [ "bolder", "lighter", "1001", "0", "0 100", "100 1001" ]
 	},
 	"src": {
 		domProp: null,
 		values: [
 			"url(404.ttf)",
 			"url(\"404.eot\")",
 			"url(\'404.otf\')",
 			"url(404.ttf) format(\"truetype\")",
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -3738,18 +3738,21 @@ var gCSSProperties = {
   "font-weight": {
     domProp: "fontWeight",
     inherited: true,
     type: CSS_TYPE_LONGHAND,
     applies_to_first_letter: true,
     applies_to_first_line: true,
     applies_to_placeholder: true,
     initial_values: [ "normal", "400" ],
-    other_values: [ "bold", "100", "200", "300", "500", "600", "700", "800", "900", "bolder", "lighter" ],
-    invalid_values: [ "0", "100.0", "107", "399", "401", "699", "710", "1000" ]
+    other_values: [ "bold", "100", "200", "300", "500", "600", "700", "800",
+                    "900", "bolder", "lighter", "10.5", "calc(10 + 10)",
+                    "calc(10 - 99)", "100.0", "107", "399", "401", "699",
+                    "710", "1000" ],
+    invalid_values: [ "0", "1001", "calc(10%)" ]
   },
   "height": {
     domProp: "height",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
     /* FIXME: test zero, and test calc clamping */
     initial_values: [ " auto",
       // these four keywords compute to the initial value when the
--- a/layout/style/test/test_transitions_per_property.html
+++ b/layout/style/test/test_transitions_per_property.html
@@ -2029,46 +2029,46 @@ function test_font_weight(prop) {
   is(prop, "font-weight", "only designed for one property");
 
   div.style.setProperty("transition-property", "none", "");
   div.style.setProperty(prop, "normal", "");
   is(cs.getPropertyValue(prop), "400",
      "font-weight property " + prop + ": computed value before transition");
   div.style.setProperty("transition-property", prop, "");
   div.style.setProperty(prop, "900", "");
-  is(cs.getPropertyValue(prop), "500",
+  is(cs.getPropertyValue(prop), "525",
      "font-weight property " + prop + ": interpolation of font-weights");
   check_distance(prop, "400", "500", "800");
 
   div.style.setProperty("transition-property", "none", "");
   div.style.setProperty(prop, "900", "");
   is(cs.getPropertyValue(prop), "900",
      "font-weight property " + prop + ": computed value before transition");
   div.style.setProperty("transition-property", prop, "");
   div.style.setProperty(prop, "100", "");
   is(cs.getPropertyValue(prop), "700",
      "font-weight property " + prop + ": interpolation of font-weights");
   check_distance(prop, "900", "700", "100");
 
   div.style.setProperty("transition-timing-function", FUNC_NEGATIVE, "");
   div.style.setProperty("transition-property", "none", "");
-  div.style.setProperty(prop, "100", "");
-  is(cs.getPropertyValue(prop), "100",
+  div.style.setProperty(prop, "1", "");
+  is(cs.getPropertyValue(prop), "1",
      "font-weight property " + prop + ": flush before clamping test");
   div.style.setProperty("transition-property", prop, "");
-  div.style.setProperty(prop, "900", "");
-  is(cs.getPropertyValue(prop), "100",
+  div.style.setProperty(prop, "1000", "");
+  is(cs.getPropertyValue(prop), "1",
      "font-weight property " + prop + ": clamping of values");
   div.style.setProperty("transition-property", "none", "");
-  div.style.setProperty(prop, "900", "");
-  is(cs.getPropertyValue(prop), "900",
+  div.style.setProperty(prop, "1000", "");
+  is(cs.getPropertyValue(prop), "1000",
      "font-weight property " + prop + ": flush before clamping test");
   div.style.setProperty("transition-property", prop, "");
-  div.style.setProperty(prop, "100", "");
-  is(cs.getPropertyValue(prop), "900",
+  div.style.setProperty(prop, "1", "");
+  is(cs.getPropertyValue(prop), "1000",
      "font-weight property " + prop + ": clamping of values");
   div.style.setProperty("transition-timing-function", "linear", "");
 }
 
 function test_grid_gap(prop) {
   test_length_transition(prop);
   test_length_clamped(prop);
   test_percent_transition(prop);
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -2452,17 +2452,17 @@ pref("security.directory",              
 pref("security.dialog_enable_delay", 1000);
 pref("security.notification_enable_delay", 500);
 
 pref("security.csp.enable", true);
 pref("security.csp.experimentalEnabled", false);
 pref("security.csp.enableStrictDynamic", true);
 
 #if defined(DEBUG) && !defined(ANDROID)
-pref("csp.content_privileged_about_uris_without_csp", "blank,cache,certerror,checkerboard,credits,home,logo,neterror,newtab,printpreview,rights,srcdoc,studies");
+pref("csp.content_privileged_about_uris_without_csp", "blank,cache,certerror,checkerboard,credits,home,logo,neterror,newtab,printpreview,srcdoc,studies");
 #endif
 
 #ifdef NIGHTLY_BUILD
 pref("security.csp.enable_violation_events", true);
 #else
 pref("security.csp.enable_violation_events", false);
 #endif
 
--- a/netwerk/base/nsNetUtil.cpp
+++ b/netwerk/base/nsNetUtil.cpp
@@ -2139,16 +2139,25 @@ bool NS_IsSameSiteForeign(nsIChannel* aC
 {
   if (!aChannel) {
     return false;
   }
   nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
   if (!loadInfo) {
     return false;
   }
+
+  // Do not treat loads triggered by web extensions as foreign
+  nsCOMPtr<nsIURI> channelURI;
+  NS_GetFinalChannelURI(aChannel, getter_AddRefs(channelURI));
+  if (BasePrincipal::Cast(loadInfo->TriggeringPrincipal())->
+        AddonAllowsLoad(channelURI)) {
+    return false;
+  }
+
   nsCOMPtr<nsIURI> uri;
   if (loadInfo->GetExternalContentPolicyType() == nsIContentPolicy::TYPE_DOCUMENT) {
     // for loads of TYPE_DOCUMENT we query the hostURI from the triggeringPricnipal
     // which returns the URI of the document that caused the navigation.
     loadInfo->TriggeringPrincipal()->GetURI(getter_AddRefs(uri));
   }
   else {
     uri = aHostURI;
--- a/netwerk/cookie/nsCookieService.cpp
+++ b/netwerk/cookie/nsCookieService.cpp
@@ -3477,22 +3477,36 @@ nsCookieService::CanSetCookie(nsIURI*   
     return newCookie;
   }
 
   // If the new cookie is same-site but in a cross site context,
   // browser must ignore the cookie.
   if ((aCookieAttributes.sameSite != nsICookie2::SAMESITE_UNSET) &&
       aThirdPartyUtil &&
       IsSameSiteEnabled()) {
-    bool isThirdParty = false;
-    aThirdPartyUtil->IsThirdPartyChannel(aChannel, aHostURI, &isThirdParty);
-    if (isThirdParty) {
-      COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, savedCookieHeader,
-                        "failed the samesite tests");
-      return newCookie;
+
+    // Do not treat loads triggered by web extensions as foreign
+    bool addonAllowsLoad = false;
+    if (aChannel) {
+      nsCOMPtr<nsIURI> channelURI;
+      NS_GetFinalChannelURI(aChannel, getter_AddRefs(channelURI));
+      nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
+      addonAllowsLoad = loadInfo &&
+        BasePrincipal::Cast(loadInfo->TriggeringPrincipal())->
+          AddonAllowsLoad(channelURI);
+    }
+
+    if (!addonAllowsLoad) {
+      bool isThirdParty = false;
+      nsresult rv = aThirdPartyUtil->IsThirdPartyChannel(aChannel, aHostURI, &isThirdParty);
+      if (NS_FAILED(rv) || isThirdParty) {
+        COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, savedCookieHeader,
+                          "failed the samesite tests");
+        return newCookie;
+      }
     }
   }
 
   aSetCookie = true;
   return newCookie;
 }
 
 // processes a single cookie, and returns true if there are more cookies
--- a/security/manager/ssl/StaticHPKPins.h
+++ b/security/manager/ssl/StaticHPKPins.h
@@ -1158,9 +1158,9 @@ static const TransportSecurityPreload kP
   { "za.search.yahoo.com", false, true, false, -1, &kPinset_yahoo },
   { "zh.search.yahoo.com", false, true, false, -1, &kPinset_yahoo },
 };
 
 // Pinning Preload List Length = 485;
 
 static const int32_t kUnknownId = -1;
 
-static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1532341796153000);
+static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1532610325338000);
--- a/security/manager/ssl/nsSTSPreloadList.inc
+++ b/security/manager/ssl/nsSTSPreloadList.inc
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /*****************************************************************************/
 /* This is an automatically generated file. If you're not                    */
 /* nsSiteSecurityService.cpp, you shouldn't be #including it.                */
 /*****************************************************************************/
 
 #include <stdint.h>
-const PRTime gPreloadListExpirationTime = INT64_C(1534759545927000);
+const PRTime gPreloadListExpirationTime = INT64_C(1535027182216000);
 %%
 0-1.party, 1
 0.me.uk, 1
 0005pay.com, 1
 00100010.net, 1
 0010100.net, 1
 00120012.net, 1
 00130013.net, 1
new file mode 100644
--- /dev/null
+++ b/security/nss/.sancov-blacklist
@@ -0,0 +1,2 @@
+src:*/gtests/google_test/*
+src:*/gtests/ssl_gtest/*
--- a/security/nss/TAG-INFO
+++ b/security/nss/TAG-INFO
@@ -1,1 +1,1 @@
-c1a4035420c3
+3e452651e282
new file mode 100755
--- /dev/null
+++ b/security/nss/automation/taskcluster/scripts/gen_coverage_report.sh
@@ -0,0 +1,12 @@
+#!/usr/bin/env bash
+
+source $(dirname "$0")/tools.sh
+
+# Clone NSPR.
+hg_clone https://hg.mozilla.org/projects/nspr ./nspr default
+
+out=/home/worker/artifacts
+mkdir -p $out
+
+# Generate coverage report.
+cd nss && ./mach coverage --outdir=$out ssl_gtests
--- a/security/nss/coreconf/coreconf.dep
+++ b/security/nss/coreconf/coreconf.dep
@@ -5,9 +5,8 @@
 
 /*
  * A dummy header file that is a dependency for all the object files.
  * Used to force a full recompilation of NSS in Mozilla's Tinderbox
  * depend builds.  See comments in rules.mk.
  */
 
 #error "Do not include this header file."
-
--- a/security/nss/lib/softoken/sdb.c
+++ b/security/nss/lib/softoken/sdb.c
@@ -639,23 +639,28 @@ sdb_closeDBLocal(SDBPrivate *sdb_p, sqli
 
 /*
  * wrapper to sqlite3_open which also sets the busy_timeout
  */
 static int
 sdb_openDB(const char *name, sqlite3 **sqlDB, int flags)
 {
     int sqlerr;
-    /*
-     * in sqlite3 3.5.0, there is a new open call that allows us
-     * to specify read only. Most new OS's are still on 3.3.x (including
-     * NSS's internal version and the version shipped with Firefox).
-     */
+    int openFlags;
+
     *sqlDB = NULL;
-    sqlerr = sqlite3_open(name, sqlDB);
+
+    if (flags & SDB_RDONLY) {
+        openFlags = SQLITE_OPEN_READONLY;
+    } else {
+        openFlags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
+    }
+
+    /* Requires SQLite 3.5.0 or newer. */
+    sqlerr = sqlite3_open_v2(name, sqlDB, openFlags, NULL);
     if (sqlerr != SQLITE_OK) {
         return sqlerr;
     }
 
     sqlerr = sqlite3_busy_timeout(*sqlDB, SDB_SQLITE_BUSY_TIMEOUT);
     if (sqlerr != SQLITE_OK) {
         sqlite3_close(*sqlDB);
         *sqlDB = NULL;
--- a/security/nss/lib/ssl/ssl3prot.h
+++ b/security/nss/lib/ssl/ssl3prot.h
@@ -11,17 +11,17 @@
 #define __ssl3proto_h_
 
 typedef PRUint16 SSL3ProtocolVersion;
 /* version numbers are defined in sslproto.h */
 
 /* The TLS 1.3 draft version. Used to avoid negotiating
  * between incompatible pre-standard TLS 1.3 drafts.
  * TODO(ekr@rtfm.com): Remove when TLS 1.3 is published. */
-#define TLS_1_3_DRAFT_VERSION 26
+#define TLS_1_3_DRAFT_VERSION 28
 
 typedef PRUint16 ssl3CipherSuite;
 /* The cipher suites are defined in sslproto.h */
 
 #define MAX_CERT_TYPES 10
 #define MAX_MAC_LENGTH 64
 #define MAX_PADDING_LENGTH 64
 #define MAX_KEY_LENGTH 64
--- a/servo/components/style/font_face.rs
+++ b/servo/components/style/font_face.rs
@@ -4,17 +4,17 @@
 
 //! The [`@font-face`][ff] at-rule.
 //!
 //! [ff]: https://drafts.csswg.org/css-fonts/#at-font-face-rule
 
 #![deny(missing_docs)]
 
 #[cfg(feature = "gecko")]
-use computed_values::{font_stretch, font_style, font_weight};
+use computed_values::{font_stretch, font_style};
 use cssparser::{AtRuleParser, DeclarationListParser, DeclarationParser, Parser};
 use cssparser::{CowRcStr, SourceLocation};
 #[cfg(feature = "gecko")]
 use cssparser::UnicodeRange;
 use error_reporting::{ContextualParseError, ParseErrorReporter};
 use parser::{Parse, ParserContext, ParserErrorContext};
 #[cfg(feature = "gecko")]
 use properties::longhands::font_language_override;
@@ -22,17 +22,17 @@ use selectors::parser::SelectorParseErro
 use shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
 use std::fmt::{self, Write};
 use str::CssStringWriter;
 use style_traits::{Comma, CssWriter, OneOrMoreSeparated, ParseError};
 use style_traits::{StyleParseErrorKind, ToCss};
 use style_traits::values::SequenceWriter;
 use values::computed::font::FamilyName;
 #[cfg(feature = "gecko")]
-use values::specified::font::{SpecifiedFontFeatureSettings, SpecifiedFontVariationSettings};
+use values::specified::font::{AbsoluteFontWeight, SpecifiedFontFeatureSettings, SpecifiedFontVariationSettings};
 use values::specified::url::SpecifiedUrl;
 
 /// A source for a font-face rule.
 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
 #[derive(Clone, Debug, Eq, PartialEq, ToCss)]
 pub enum Source {
     /// A `url()` source.
     Url(UrlSource),
@@ -87,48 +87,31 @@ impl ToCss for UrlSource {
 pub enum FontDisplay {
     Auto,
     Block,
     Swap,
     Fallback,
     Optional,
 }
 
-/// A font-weight value for a @font-face rule.
-/// The font-weight CSS property specifies the weight or boldness of the font.
-#[cfg(feature = "gecko")]
-#[derive(Clone, Debug, Eq, PartialEq, ToCss)]
-pub enum FontWeight {
-    /// Numeric font weights for fonts that provide more than just normal and bold.
-    Weight(font_weight::T),
-    /// Normal font weight. Same as 400.
-    Normal,
-    /// Bold font weight. Same as 700.
-    Bold,
-}
+/// The font-weight descriptor:
+///
+/// https://drafts.csswg.org/css-fonts-4/#descdef-font-face-font-weight
+#[derive(Clone, Debug, PartialEq, ToCss)]
+pub struct FontWeight(pub AbsoluteFontWeight, pub Option<AbsoluteFontWeight>);
 
-#[cfg(feature = "gecko")]
 impl Parse for FontWeight {
     fn parse<'i, 't>(
-        _: &ParserContext,
+        context: &ParserContext,
         input: &mut Parser<'i, 't>,
-    ) -> Result<FontWeight, ParseError<'i>> {
-        let result = input.try(|input| {
-            let ident = input.expect_ident().map_err(|_| ())?;
-            match_ignore_ascii_case! { &ident,
-                "normal" => Ok(FontWeight::Normal),
-                "bold" => Ok(FontWeight::Bold),
-                _ => Err(())
-            }
-        });
-        result.or_else(|_| {
-            font_weight::T::from_int(input.expect_integer()?)
-                .map(FontWeight::Weight)
-                .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
-        })
+    ) -> Result<Self, ParseError<'i>> {
+        let first = AbsoluteFontWeight::parse(context, input)?;
+        let second =
+            input.try(|input| AbsoluteFontWeight::parse(context, input)).ok();
+        Ok(FontWeight(first, second))
     }
 }
 
 /// Parse the block inside a `@font-face` rule.
 ///
 /// Note that the prelude parsing code lives in the `stylesheets` module.
 pub fn parse_font_face_block<R>(
     context: &ParserContext,
--- a/servo/components/style/gecko/rules.rs
+++ b/servo/components/style/gecko/rules.rs
@@ -1,46 +1,37 @@
 /* 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/. */
 
 //! Bindings for CSS Rule objects
 
 use byteorder::{BigEndian, WriteBytesExt};
-use computed_values::{font_stretch, font_style, font_weight};
+use computed_values::{font_stretch, font_style};
 use counter_style::{self, CounterBound};
 use cssparser::UnicodeRange;
 use font_face::{FontDisplay, FontWeight, Source};
 use gecko_bindings::structs::{self, nsCSSValue};
 use gecko_bindings::sugar::ns_css_value::ToNsCssValue;
 use properties::longhands::font_language_override;
 use std::str;
 use values::computed::font::FamilyName;
 use values::generics::font::FontTag;
+use values::specified::font::AbsoluteFontWeight;
 use values::specified::font::{SpecifiedFontFeatureSettings, SpecifiedFontVariationSettings};
 
 impl<'a> ToNsCssValue for &'a FamilyName {
     fn convert(self, nscssvalue: &mut nsCSSValue) {
         nscssvalue.set_string_from_atom(&self.name)
     }
 }
 
-impl ToNsCssValue for font_weight::T {
+impl<'a> ToNsCssValue for &'a AbsoluteFontWeight {
     fn convert(self, nscssvalue: &mut nsCSSValue) {
-        nscssvalue.set_font_weight(self.0)
-    }
-}
-
-impl<'a> ToNsCssValue for &'a FontWeight {
-    fn convert(self, nscssvalue: &mut nsCSSValue) {
-        match *self {
-            FontWeight::Normal => nscssvalue.set_enum(structs::NS_FONT_WEIGHT_NORMAL as i32),
-            FontWeight::Bold => nscssvalue.set_enum(structs::NS_FONT_WEIGHT_BOLD as i32),
-            FontWeight::Weight(weight) => nscssvalue.set_font_weight(weight.0),
-        }
+        nscssvalue.set_font_weight(self.compute().0)
     }
 }
 
 impl ToNsCssValue for FontTag {
     fn convert(self, nscssvalue: &mut nsCSSValue) {
         let mut raw = [0u8; 4];
         (&mut raw[..]).write_u32::<BigEndian>(self.0).unwrap();
         nscssvalue.set_string(str::from_utf8(&raw).unwrap());
@@ -72,16 +63,38 @@ impl<'a> ToNsCssValue for &'a SpecifiedF
         nscssvalue.set_pair_list(self.0.iter().map(|entry| {
             let mut value = nsCSSValue::null();
             value.set_number(entry.value.into());
             (entry.tag.into(), value)
         }))
     }
 }
 
+impl<'a> ToNsCssValue for &'a FontWeight {
+    fn convert(self, nscssvalue: &mut nsCSSValue) {
+        let FontWeight(ref first, ref second) = *self;
+
+        let second = match *second {
+            None => {
+                nscssvalue.set_from(first);
+                return;
+            }
+            Some(ref second) => second,
+        };
+
+        let mut a = nsCSSValue::null();
+        let mut b = nsCSSValue::null();
+
+        a.set_from(first);
+        b.set_from(second);
+
+        nscssvalue.set_pair(&a, &b);
+    }
+}
+
 impl<'a> ToNsCssValue for &'a font_language_override::SpecifiedValue {
     fn convert(self, nscssvalue: &mut nsCSSValue) {
         match *self {
             font_language_override::SpecifiedValue::Normal => nscssvalue.set_normal(),
             font_language_override::SpecifiedValue::Override(ref lang) => {
                 nscssvalue.set_string(&*lang)
             },
             // This path is unreachable because the descriptor is only specified by the user.
--- a/servo/components/style/gecko_bindings/sugar/ns_css_value.rs
+++ b/servo/components/style/gecko_bindings/sugar/ns_css_value.rs
@@ -173,18 +173,18 @@ impl nsCSSValue {
     }
 
     /// Set to a local font value
     pub fn set_local_font(&mut self, s: &Atom) {
         self.set_string_from_atom_internal(s, nsCSSUnit::eCSSUnit_Local_Font);
     }
 
     /// Set to a font weight
-    pub fn set_font_weight(&mut self, w: u16) {
-        unsafe { bindings::Gecko_CSSValue_SetFontWeight(self, w as f32) }
+    pub fn set_font_weight(&mut self, w: f32) {
+        unsafe { bindings::Gecko_CSSValue_SetFontWeight(self, w) }
     }
 
     fn set_int_internal(&mut self, value: i32, unit: nsCSSUnit) {
         unsafe { bindings::Gecko_CSSValue_SetInt(self, value, unit) }
     }
 
     /// Set to an integer value
     pub fn set_integer(&mut self, value: i32) {
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -2602,19 +2602,17 @@ fn static_assert() {
 
     pub fn set_font_weight(&mut self, v: longhands::font_weight::computed_value::T) {
         unsafe { Gecko_FontWeight_SetFloat(&mut self.gecko.mFont.weight, v.0 as f32) };
     }
     ${impl_simple_copy('font_weight', 'mFont.weight')}
 
     pub fn clone_font_weight(&self) -> longhands::font_weight::computed_value::T {
         let weight: f32 = unsafe { Gecko_FontWeight_ToFloat(self.gecko.mFont.weight) };
-        debug_assert!(weight >= 0.0 &&
-                      weight <= ::std::u16::MAX as f32);
-        longhands::font_weight::computed_value::T(weight as u16)
+        longhands::font_weight::computed_value::T(weight)
     }
 
     ${impl_simple_type_with_conversion("font_synthesis", "mFont.synthesis")}
 
     pub fn set_font_size_adjust(&mut self, v: longhands::font_size_adjust::computed_value::T) {
         use properties::longhands::font_size_adjust::computed_value::T;
         match v {
             T::None => self.gecko.mFont.sizeAdjust = -1.0 as f32,
--- a/servo/components/style/properties/helpers/animated_properties.mako.rs
+++ b/servo/components/style/properties/helpers/animated_properties.mako.rs
@@ -868,42 +868,27 @@ impl ToAnimatedZero for LengthOrPercenta
     }
 }
 
 impl ToAnimatedZero for MaxLength {
     #[inline]
     fn to_animated_zero(&self) -> Result<Self, ()> { Err(()) }
 }
 
-/// <http://dev.w3.org/csswg/css-transitions/#animtype-font-weight>
-impl Animate for FontWeight {
-    #[inline]
-    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
-        let a = self.0 as f64;
-        let b = other.0 as f64;
-        const NORMAL: f64 = 400.;
-        let (this_weight, other_weight) = procedure.weights();
-        let weight = (a - NORMAL) * this_weight + (b - NORMAL) * other_weight + NORMAL;
-        let weight = (weight.max(100.).min(900.) / 100.).round() * 100.;
-        Ok(FontWeight(weight as u16))
-    }
-}
-
 impl ToAnimatedZero for FontWeight {
     #[inline]
     fn to_animated_zero(&self) -> Result<Self, ()> {
         Ok(FontWeight::normal())
     }
 }
 
 /// <https://drafts.csswg.org/css-fonts/#font-stretch-prop>
 impl Animate for FontStretch {
     #[inline]
-    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()>
-    {
+    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
         let from = f64::from(*self);
         let to = f64::from(*other);
         let normal = f64::from(FontStretch::Normal);
         let (this_weight, other_weight) = procedure.weights();
         let result = (from - normal) * this_weight + (to - normal) * other_weight + normal;
         Ok(result.into())
     }
 }
--- a/servo/components/style/properties/longhand/font.mako.rs
+++ b/servo/components/style/properties/longhand/font.mako.rs
@@ -38,24 +38,26 @@
                                 gecko_constant_prefix="NS_FONT_VARIANT_CAPS",
                                 gecko_ffi_name="mFont.variantCaps",
                                 spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-caps",
                                 custom_consts=font_variant_caps_custom_consts,
                                 flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
                                 animation_value_type="discrete",
                                 servo_restyle_damage="rebuild_and_reflow")}
 
-${helpers.predefined_type("font-weight",
-                          "FontWeight",
-                          initial_value="computed::FontWeight::normal()",
-                          initial_specified_value="specified::FontWeight::Normal",
-                          animation_value_type="ComputedValue",
-                          flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
-                          spec="https://drafts.csswg.org/css-fonts/#propdef-font-weight",
-                          servo_restyle_damage="rebuild_and_reflow")}
+${helpers.predefined_type(
+    "font-weight",
+    "FontWeight",
+    initial_value="computed::FontWeight::normal()",
+    initial_specified_value="specified::FontWeight::normal()",
+    animation_value_type="Number",
+    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
+    spec="https://drafts.csswg.org/css-fonts/#propdef-font-weight",
+    servo_restyle_damage="rebuild_and_reflow",
+)}
 
 ${helpers.predefined_type("font-size",
                           "FontSize",
                           initial_value="computed::FontSize::medium()",
                           initial_specified_value="specified::FontSize::medium()",
                           animation_value_type="NonNegativeLength",
                           allow_quirks=True,
                           flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
--- a/servo/components/style/values/computed/font.rs
+++ b/servo/components/style/values/computed/font.rs
@@ -20,30 +20,45 @@ use std::hash::{Hash, Hasher};
 #[cfg(feature = "servo")]
 use std::slice;
 use style_traits::{CssWriter, ParseError, ToCss};
 use values::CSSFloat;
 use values::animated::{ToAnimatedValue, ToAnimatedZero};
 use values::computed::{Context, Integer, NonNegativeLength, Number, ToComputedValue};
 use values::generics::font::{FeatureTagValue, FontSettings};
 use values::generics::font::{KeywordInfo as GenericKeywordInfo, VariationValue};
-use values::specified::font as specified;
+use values::specified::font::{self as specified, MIN_FONT_WEIGHT, MAX_FONT_WEIGHT};
 use values::specified::length::{FontBaseSize, NoCalcLength};
 
 pub use values::computed::Length as MozScriptMinSize;
 pub use values::specified::font::{FontSynthesis, MozScriptSizeMultiplier, XLang, XTextZoom};
 
-/// As of CSS Fonts Module Level 3, only the following values are
-/// valid: 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900
+/// A value for the font-weight property per:
+///
+/// https://drafts.csswg.org/css-fonts-4/#propdef-font-weight
 ///
-/// However, system fonts may provide other values. Pango
-/// may provide 350, 380, and 1000 (on top of the existing values), for example.
-#[derive(Clone, ComputeSquaredDistance, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToCss)]
+/// This is effectively just a `Number`.
+#[derive(Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq,
+         ToCss)]
 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
-pub struct FontWeight(pub u16);
+pub struct FontWeight(pub Number);
+
+impl ToAnimatedValue for FontWeight {
+    type AnimatedValue = Number;
+
+    #[inline]
+    fn to_animated_value(self) -> Self::AnimatedValue {
+        self.0
+    }
+
+    #[inline]
+    fn from_animated_value(animated: Self::AnimatedValue) -> Self {
+        FontWeight(animated.max(MIN_FONT_WEIGHT).min(MAX_FONT_WEIGHT))
+    }
+}
 
 #[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq,
          ToAnimatedZero, ToCss)]
 /// The computed value of font-size
 pub struct FontSize {
     /// The size.
     pub size: NonNegativeLength,
     /// If derived from a keyword, the keyword and additional transformations applied to it
@@ -52,66 +67,63 @@ pub struct FontSize {
 }
 
 /// Additional information for computed keyword-derived font sizes.
 pub type KeywordInfo = GenericKeywordInfo<NonNegativeLength>;
 
 impl FontWeight {
     /// Value for normal
     pub fn normal() -> Self {
-        FontWeight(400)
+        FontWeight(400.)
     }
 
     /// Value for bold
     pub fn bold() -> Self {
-        FontWeight(700)
-    }
-
-    /// Convert from an integer to Weight
-    pub fn from_int(n: i32) -> Result<Self, ()> {
-        if n >= 100 && n <= 900 && n % 100 == 0 {
-            Ok(FontWeight(n as u16))
-        } else {
-            Err(())
-        }
+        FontWeight(700.)
     }
 
     /// Convert from an Gecko weight
     #[cfg(feature = "gecko")]
     pub fn from_gecko_weight(weight: structs::FontWeight) -> Self {
         // we allow a wider range of weights than is parseable
         // because system fonts may provide custom values
         let weight = unsafe { bindings::Gecko_FontWeight_ToFloat(weight) };
-        FontWeight(weight as u16)
+        FontWeight(weight)
     }
 
     /// Weither this weight is bold
     pub fn is_bold(&self) -> bool {
-        self.0 > 500
+        self.0 > 500.
     }
 
-    /// Return the bolder weight
+    /// Return the bolder weight.
+    ///
+    /// See the table in:
+    /// https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values
     pub fn bolder(self) -> Self {
-        if self.0 < 400 {
-            FontWeight(400)
-        } else if self.0 < 600 {
-            FontWeight(700)
+        if self.0 < 350. {
+            FontWeight(400.)
+        } else if self.0 < 550. {
+            FontWeight(700.)
         } else {
-            FontWeight(900)
+            FontWeight(self.0.max(900.))
         }
     }
 
-    /// Returns the lighter weight
+    /// Return the lighter weight.
+    ///
+    /// See the table in:
+    /// https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values
     pub fn lighter(self) -> Self {
-        if self.0 < 600 {
-            FontWeight(100)
-        } else if self.0 < 800 {
-            FontWeight(400)
+        if self.0 < 550. {
+            FontWeight(self.0.min(100.))
+        } else if self.0 < 750. {
+            FontWeight(400.)
         } else {
-            FontWeight(700)
+            FontWeight(700.)
         }
     }
 }
 
 impl FontSize {
     /// The actual computed font size.
     pub fn size(self) -> Au {
         self.size.into()
--- a/servo/components/style/values/specified/font.rs
+++ b/servo/components/style/values/specified/font.rs
@@ -22,39 +22,53 @@ use values::computed::font::{FamilyName,
 use values::generics::font::{FeatureTagValue, FontSettings, FontTag};
 use values::generics::font::{KeywordInfo as GenericKeywordInfo, KeywordSize, VariationValue};
 use values::specified::{AllowQuirks, Integer, LengthOrPercentage, NoCalcLength, Number};
 use values::specified::length::{FontBaseSize, AU_PER_PT, AU_PER_PX};
 
 const DEFAULT_SCRIPT_MIN_SIZE_PT: u32 = 8;
 const DEFAULT_SCRIPT_SIZE_MULTIPLIER: f64 = 0.71;
 
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToCss)]
-/// A specified font-weight value
+/// The minimum font-weight value per:
+///
+/// https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values
+pub const MIN_FONT_WEIGHT: f32 = 1.;
+
+/// The maximum font-weight value per:
+///
+/// https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values
+pub const MAX_FONT_WEIGHT: f32 = 1000.;
+
+/// A specified font-weight value.
+///
+/// https://drafts.csswg.org/css-fonts-4/#propdef-font-weight
+#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss)]
 pub enum FontWeight {
-    /// Normal variant
-    Normal,
-    /// Bold variant
-    Bold,
+    /// `<font-weight-absolute>`
+    Absolute(AbsoluteFontWeight),
     /// Bolder variant
     Bolder,
     /// Lighter variant
     Lighter,
-    /// Computed weight variant
-    Weight(computed::FontWeight),
-    /// System font varaint
+    /// System font variant.
     System(SystemFont),
 }
 
 impl FontWeight {
+    /// `normal`
+    #[inline]
+    pub fn normal() -> Self {
+        FontWeight::Absolute(AbsoluteFontWeight::Normal)
+    }
+
     /// Get a specified FontWeight from a gecko keyword
     pub fn from_gecko_keyword(kw: u32) -> Self {
-        computed::FontWeight::from_int(kw as i32)
-            .map(FontWeight::Weight)
-            .expect("Found unexpected value in style struct for font-weight property")
+        debug_assert!(kw % 100 == 0);
+        debug_assert!(kw as f32 <= MAX_FONT_WEIGHT);
+        FontWeight::Absolute(AbsoluteFontWeight::Weight(Number::new(kw as f32)))
     }
 
     /// Get a specified FontWeight from a SystemFont
     pub fn system_font(f: SystemFont) -> Self {
         FontWeight::System(f)
     }
 
     /// Retreive a SystemFont from FontWeight
@@ -64,49 +78,37 @@ impl FontWeight {
         } else {
             None
         }
     }
 }
 
 impl Parse for FontWeight {
     fn parse<'i, 't>(
-        _: &ParserContext,
+        context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<FontWeight, ParseError<'i>> {
-        let result = match *input.next()? {
-            Token::Ident(ref ident) => {
-                match_ignore_ascii_case! { ident,
-                    "normal" => Ok(FontWeight::Normal),
-                    "bold" => Ok(FontWeight::Bold),
-                    "bolder" => Ok(FontWeight::Bolder),
-                    "lighter" => Ok(FontWeight::Lighter),
-                    _ => Err(()),
-                }
-            },
-            Token::Number {
-                int_value: Some(value),
-                ..
-            } => computed::FontWeight::from_int(value).map(FontWeight::Weight),
-            _ => Err(()),
-        };
+        if let Ok(absolute) = input.try(|input| AbsoluteFontWeight::parse(context, input)) {
+            return Ok(FontWeight::Absolute(absolute));
+        }
 
-        result.map_err(|_| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
+        Ok(try_match_ident_ignore_ascii_case! { input,
+            "bolder" => FontWeight::Bolder,
+            "lighter" => FontWeight::Lighter,
+        })
     }
 }
 
 impl ToComputedValue for FontWeight {
     type ComputedValue = computed::FontWeight;
 
     #[inline]
     fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
         match *self {
-            FontWeight::Weight(weight) => weight,
-            FontWeight::Normal => computed::FontWeight::normal(),
-            FontWeight::Bold => computed::FontWeight::bold(),
+            FontWeight::Absolute(ref abs) => abs.compute(),
             FontWeight::Bolder => context
                 .builder
                 .get_parent_font()
                 .clone_font_weight()
                 .bolder(),
             FontWeight::Lighter => context
                 .builder
                 .get_parent_font()
@@ -121,17 +123,74 @@ impl ToComputedValue for FontWeight {
                 .clone(),
             #[cfg(not(feature = "gecko"))]
             FontWeight::System(_) => unreachable!(),
         }
     }
 
     #[inline]
     fn from_computed_value(computed: &computed::FontWeight) -> Self {
-        FontWeight::Weight(*computed)
+        FontWeight::Absolute(AbsoluteFontWeight::Weight(
+            Number::from_computed_value(&computed.0)
+        ))
+    }
+}
+
+/// An absolute font-weight value for a @font-face rule.
+///
+/// https://drafts.csswg.org/css-fonts-4/#font-weight-absolute-values
+#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss)]
+pub enum AbsoluteFontWeight {
+    /// A `<number>`, with the additional constraints specified in:
+    ///
+    ///   https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values
+    Weight(Number),
+    /// Normal font weight. Same as 400.
+    Normal,
+    /// Bold font weight. Same as 700.
+    Bold,
+}
+
+impl AbsoluteFontWeight {
+    /// Returns the computed value for this absolute font weight.
+    pub fn compute(&self) -> computed::FontWeight {
+        match *self {
+            AbsoluteFontWeight::Weight(weight) => {
+                computed::FontWeight(
+                    weight.get().max(MIN_FONT_WEIGHT).min(MAX_FONT_WEIGHT)
+                )
+            },
+            AbsoluteFontWeight::Normal => computed::FontWeight::normal(),
+            AbsoluteFontWeight::Bold => computed::FontWeight::bold(),
+        }
+    }
+}
+
+impl Parse for AbsoluteFontWeight {
+    fn parse<'i, 't>(
+        context: &ParserContext,
+        input: &mut Parser<'i, 't>,
+    ) -> Result<Self, ParseError<'i>> {
+        if let Ok(number) = input.try(|input| Number::parse(context, input)) {
+            // We could add another AllowedNumericType value, but it doesn't
+            // seem worth it just for a single property with such a weird range,
+            // so we do the clamping here manually.
+            if !number.was_calc() &&
+                (number.get() < MIN_FONT_WEIGHT || number.get() > MAX_FONT_WEIGHT) {
+                return Err(input.new_custom_error(
+                    StyleParseErrorKind::UnspecifiedError
+                ))
+            }
+            return Ok(AbsoluteFontWeight::Weight(number))
+        }
+
+        Ok(try_match_ident_ignore_ascii_case! { input,
+            "normal" => AbsoluteFontWeight::Normal,
+            "bold" => AbsoluteFontWeight::Bold,
+        })
     }
 }
 
 #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)]
 /// A specified font-size value
 pub enum FontSize {
     /// A length; e.g. 10px.
     Length(LengthOrPercentage),
--- a/servo/components/style/values/specified/mod.rs
+++ b/servo/components/style/values/specified/mod.rs
@@ -196,17 +196,24 @@ impl Number {
     /// Returns a new number with the value `val`.
     pub fn new(val: CSSFloat) -> Self {
         Number {
             value: val,
             calc_clamping_mode: None,
         }
     }
 
+    /// Returns whether this number came from a `calc()` expression.
+    #[inline]
+    pub fn was_calc(&self) -> bool {
+        self.calc_clamping_mode.is_some()
+    }
+
     /// Returns the numeric value, clamped if needed.
+    #[inline]
     pub fn get(&self) -> f32 {
         self.calc_clamping_mode
             .map_or(self.value, |mode| mode.clamp(self.value))
     }
 
     #[allow(missing_docs)]
     pub fn parse_non_negative<'i, 't>(
         context: &ParserContext,
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -5437,20 +5437,19 @@ pub extern "C" fn Servo_ParseFontShortha
     style.set_from(match font.font_style {
         font_style::SpecifiedValue::Keyword(ref kw) => kw,
         font_style::SpecifiedValue::System(_) => return false,
     });
     stretch.set_from(match font.font_stretch {
         font_stretch::SpecifiedValue::Keyword(ref kw) => kw,
         font_stretch::SpecifiedValue::System(_) => return false,
     });
+
     match font.font_weight {
-        FontWeight::Weight(w) => weight.set_from(w),
-        FontWeight::Normal => weight.set_enum(structs::NS_FONT_WEIGHT_NORMAL as i32),
-        FontWeight::Bold => weight.set_enum(structs::NS_FONT_WEIGHT_BOLD as i32),
+        FontWeight::Absolute(w) => weight.set_font_weight(w.compute().0),
         // Resolve relative font weights against the initial of font-weight
         // (normal, which is equivalent to 400).
         FontWeight::Bolder => weight.set_enum(structs::NS_FONT_WEIGHT_BOLD as i32),
         FontWeight::Lighter => weight.set_enum(structs::NS_FONT_WEIGHT_THIN as i32),
         FontWeight::System(_) => return false,
     }
 
     true
--- a/taskcluster/ci/config.yml
+++ b/taskcluster/ci/config.yml
@@ -108,18 +108,20 @@ scriptworker:
         'scriptworker-prov-v1/signing-linux-v1':
             - 'project:releng:signing:cert:release-signing'
             - 'project:releng:signing:cert:nightly-signing'
         'scriptworker-prov-v1/depsigning':
             - 'project:releng:signing:cert:dep-signing'
         'scriptworker-prov-v1/beetmoverworker-v1':
             - 'project:releng:beetmover:bucket:release'
             - 'project:releng:beetmover:bucket:nightly'
+            - 'project:releng:beetmover:bucket:partner'
         'scriptworker-prov-v1/beetmoverworker-dev':
             - 'project:releng:beetmover:bucket:dep'
+            - 'project:releng:beetmover:bucket:dep-partner'
         'scriptworker-prov-v1/balrogworker-v1':
             - 'project:releng:balrog:server:nightly'
             - 'project:releng:balrog:server:aurora'
             - 'project:releng:balrog:server:beta'
             - 'project:releng:balrog:server:release'
             - 'project:releng:balrog:server:esr'
         'scriptworker-prov-v1/balrog-dev':
             - 'project:releng:balrog:server:dep'
--- a/taskcluster/ci/docker-image/kind.yml
+++ b/taskcluster/ci/docker-image/kind.yml
@@ -90,12 +90,16 @@ jobs:
   google-play-strings:
     symbol: I(gps)
   funsize-balrog-submitter:
     symbol: I(fbs)
   update-verify:
     symbol: I(uv)
   diffoscope:
     symbol: I(diff)
+  partner-repack:
+    symbol: I(PR)
+    parent: debian9-base
+    definition: partner-repack
   periodic-updates:
     symbol: I(file)
   firefox-snap:
     symbol: I(snap)
--- a/taskcluster/ci/release-bouncer-check/kind.yml
+++ b/taskcluster/ci/release-bouncer-check/kind.yml
@@ -17,17 +17,17 @@ transforms:
 job-defaults:
     name: bouncer-check
     description: bouncer check
     run-on-projects: []  # to make sure this never runs as part of CI
     shipping-phase: push
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
         max-run-time: 1200
-        docker-image: {in-tree: "lint"}
+        docker-image: {in-tree: "update-verify"}
     run:
         using: run-task
         sparse-profile: mozharness
     attributes:
         build_platform: linux64
         build_type: opt
     treeherder:
         symbol: Rel(BncChk)
new file mode 100644
--- /dev/null
+++ b/taskcluster/ci/release-eme-free-repack-beetmover/kind.yml
@@ -0,0 +1,30 @@
+# 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/.
+
+loader: taskgraph.loader.single_dep:loader
+
+transforms:
+   - taskgraph.transforms.name_sanity:transforms
+   - taskgraph.transforms.beetmover_repackage_partner:transforms
+   - taskgraph.transforms.release_notifications:transforms
+   - taskgraph.transforms.task:transforms
+
+kind-dependencies:
+   - release-eme-free-repack-repackage  # Mac
+   - release-eme-free-repack-repackage-signing  # Windows
+
+only-for-build-platforms:
+   - macosx64-nightly/opt
+   - win32-nightly/opt
+   - win64-nightly/opt
+
+job-template:
+   shipping-phase: promote
+   partner-bucket-scope:
+      by-project:
+         mozilla-beta: beetmover:bucket:partner
+         mozilla-release: beetmover:bucket:partner
+         default: beetmover:bucket:dep-partner
+   partner-private-path: null
+   partner-public-path: "{platform}-EME-free/{locale}"
new file mode 100644
--- /dev/null
+++ b/taskcluster/ci/release-eme-free-repack-repackage-signing/kind.yml
@@ -0,0 +1,18 @@
+# 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/.
+
+loader: taskgraph.loader.single_dep:loader
+
+transforms:
+   - taskgraph.transforms.name_sanity:transforms
+   - taskgraph.transforms.repackage_signing_partner:transforms
+   - taskgraph.transforms.release_notifications:transforms
+   - taskgraph.transforms.task:transforms
+
+kind-dependencies:
+   - release-eme-free-repack-repackage
+
+only-for-build-platforms:
+   - win32-nightly/opt
+   - win64-nightly/opt
new file mode 100644
--- /dev/null
+++ b/taskcluster/ci/release-eme-free-repack-repackage/kind.yml
@@ -0,0 +1,35 @@
+# 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/.
+
+loader: taskgraph.loader.single_dep:loader
+
+transforms:
+   - taskgraph.transforms.chunk_partners:transforms
+   - taskgraph.transforms.name_sanity:transforms
+   - taskgraph.transforms.repackage_partner:transforms
+   - taskgraph.transforms.use_toolchains:transforms
+   - taskgraph.transforms.job:transforms
+   - taskgraph.transforms.release_notifications:transforms
+   - taskgraph.transforms.task:transforms
+
+kind-dependencies:
+   - release-eme-free-repack
+   - release-eme-free-repack-signing
+   - toolchain
+
+only-for-build-platforms:
+   - macosx64-nightly/opt
+   - win32-nightly/opt
+   - win64-nightly/opt
+
+job-template:
+   mozharness:
+      config:
+         by-build-platform:
+            macosx64-.*:
+               - repackage/osx_partner.py
+            win32-.*:
+               - repackage/win32_partner.py
+            win64-.*:
+               - repackage/win64_partner.py
new file mode 100644
--- /dev/null
+++ b/taskcluster/ci/release-eme-free-repack-signing/kind.yml
@@ -0,0 +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/.
+
+loader: taskgraph.loader.build_signing:loader
+
+transforms:
+   - taskgraph.transforms.name_sanity:transforms
+   - taskgraph.transforms.partner_signing:transforms
+   - taskgraph.transforms.signing:transforms
+   - taskgraph.transforms.release_notifications:transforms
+   - taskgraph.transforms.task:transforms
+
+kind-dependencies:
+   - release-eme-free-repack
+
+only-for-build-platforms:
+   - macosx64-nightly/opt
+
+job-template:
+   shipping-product: firefox
+   shipping-phase: promote
+   depname: release-eme-free-repack
+   max-run-time: 7200
+   treeherder:
+      symbol: EME-Sign
+      platform: macosx64/opt
+      kind: test
+      tier: 1
new file mode 100644
--- /dev/null
+++ b/taskcluster/ci/release-eme-free-repack/kind.yml
@@ -0,0 +1,81 @@
+# 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/.
+
+loader: taskgraph.loader.transform:loader
+
+transforms:
+   - taskgraph.transforms.release_deps:transforms
+   - taskgraph.transforms.partner_repack:transforms
+   - taskgraph.transforms.release_notifications:transforms
+   - taskgraph.transforms.job:transforms
+   - taskgraph.transforms.task:transforms
+
+kind-dependencies:
+   - build-signing
+   - nightly-l10n-signing
+
+job-defaults:
+   name: eme-free-repack
+   description: Release Promotion eme-free repacks
+   run-on-projects: []  # to make sure this never runs as part of CI
+   shipping-product: firefox
+   shipping-phase: promote
+   worker-type: aws-provisioner-v1/gecko-{level}-b-linux
+   worker:
+      docker-image:
+         in-tree: "partner-repack"
+      chain-of-trust: true
+      max-run-time: 7200
+      env:
+         REPACK_MANIFESTS_URL:
+            by-project:
+               mozilla-beta: "git@github.com:mozilla-partners/mozilla-EME-free-manifest"
+               mozilla-release: "git@github.com:mozilla-partners/mozilla-EME-free-manifest"
+               maple: "git@github.com:mozilla-partners/mozilla-EME-free-manifest"
+               default: "git@github.com:mozilla-releng/staging-repack-manifests.git"
+   run:
+      using: mozharness
+      config:
+         - partner_repacks/release_mozilla-release_desktop.py
+      script: mozharness/scripts/desktop_partner_repacks.py
+      job-script: taskcluster/scripts/builder/repackage.sh
+      need-xvfb: false
+      tooltool-downloads: false
+
+jobs:
+   macosx64-nightly:
+      treeherder:
+         symbol: EME
+         platform: macosx64/opt
+         kind: test
+         tier: 1
+      attributes:
+         build_platform: macosx64-nightly
+         build_type: opt
+         artifact_prefix: releng/partner
+         nightly: true
+
+   win32-nightly:
+      treeherder:
+         symbol: EME
+         platform: win32/opt
+         kind: test
+         tier: 1
+      attributes:
+         build_platform: win32-nightly
+         build_type: opt
+         artifact_prefix: releng/partner
+         nightly: true
+
+   win64-nightly:
+      treeherder:
+         symbol: EME
+         platform: win64/opt
+         kind: test
+         tier: 1
+      attributes:
+         build_platform: win64-nightly
+         build_type: opt
+         artifact_prefix: releng/partner
+         nightly: true
--- a/taskcluster/ci/release-final-verify/kind.yml
+++ b/taskcluster/ci/release-final-verify/kind.yml
@@ -16,17 +16,18 @@ transforms:
 
 job-defaults:
    name: final-verify
    run-on-projects: []  # to make sure this never runs as part of CI
    worker-type: aws-provisioner-v1/gecko-{level}-b-linux
    worker:
       implementation: docker-worker
       os: linux
-      docker-image: mozillareleases/python-test-runner@sha256:0729c2e6e7bc0d6a4cbccb2e66a78e1d8e8cbb5e44105d56e3c9c610230ebd69
+      docker-image:
+         in-tree: "update-verify"
       max-run-time: 7200
       retry-exit-status: [1]
       env:
          BUILD_TOOLS_REPO:
             by-project:
                jamun: https://hg.mozilla.org/users/stage-ffxbld/tools
                maple: https://hg.mozilla.org/users/asasaki_mozilla.com/tools
                default: https://hg.mozilla.org/build/tools
new file mode 100644
--- /dev/null
+++ b/taskcluster/ci/release-partner-repack-beetmover/kind.yml
@@ -0,0 +1,33 @@
+# 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/.
+
+loader: taskgraph.loader.single_dep:loader
+
+transforms:
+   - taskgraph.transforms.name_sanity:transforms
+   - taskgraph.transforms.beetmover_repackage_partner:transforms
+   - taskgraph.transforms.release_notifications:transforms
+   - taskgraph.transforms.task:transforms
+
+kind-dependencies:
+   - release-partner-repack-chunking-dummy  # Linux
+   - release-partner-repack-repackage  # Mac
+   - release-partner-repack-repackage-signing  # Windows
+
+only-for-build-platforms:
+   - linux-nightly/opt
+   - linux64-nightly/opt
+   - macosx64-nightly/opt
+   - win32-nightly/opt
+   - win64-nightly/opt
+
+job-template:
+   shipping-phase: promote
+   partner-bucket-scope:
+      by-project:
+         mozilla-beta: beetmover:bucket:partner
+         mozilla-release: beetmover:bucket:partner
+         default: beetmover:bucket:dep-partner
+   partner-public-path: "partner-repacks/{partner}/{subpartner}/v{release_partner_build_number}/{platform}/{locale}"
+   partner-private-path: "{partner}/{version}-{build_number}/{subpartner}/{platform}/{locale}"
new file mode 100644
--- /dev/null
+++ b/taskcluster/ci/release-partner-repack-chunking-dummy/kind.yml
@@ -0,0 +1,44 @@
+# 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/.
+
+loader: taskgraph.loader.single_dep:loader
+
+transforms:
+   - taskgraph.transforms.chunk_partners:transforms
+   - taskgraph.transforms.name_sanity:transforms
+   # This transform sets build_platform to the same thing as the upstream task.
+   # We'd do it here, except single_dep doesn't pay attention to any
+   # per platform things that we set.
+   - taskgraph.transforms.copy_attributes_from_dependent_task:transforms
+   # This transform is needed because task.py doesn't allow "dependent-task" to be
+   # set, but the single_dep loader sets it (and we need it for chunk_partners,
+   # name_sanity, and copy_build_platform_from_dependent_task to work).
+   - taskgraph.transforms.strip_dependent_task:transforms
+   - taskgraph.transforms.release_deps:transforms
+   - taskgraph.transforms.release_notifications:transforms
+   - taskgraph.transforms.task:transforms
+
+kind-dependencies:
+   - release-partner-repack
+
+only-for-build-platforms:
+   - linux-nightly/opt
+   - linux64-nightly/opt
+
+job-template:
+   shipping-phase: promote
+   shipping-product: firefox
+   name: release-partner-repack-chunking-dummy
+   description: Dummy task to deal with fanning out Linux partner repacks
+   run-on-projects: []
+   worker-type: aws-provisioner-v1/gecko-{level}-b-linux
+   worker:
+      implementation: docker-worker
+      os: linux
+      docker-image: "ubuntu:16.10"
+      max-run-time: 600
+      command:
+         - /bin/bash
+         - -c
+         - echo "Dummy task"
new file mode 100644
--- /dev/null
+++ b/taskcluster/ci/release-partner-repack-repackage-signing/kind.yml
@@ -0,0 +1,18 @@
+# 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/.
+
+loader: taskgraph.loader.single_dep:loader
+
+transforms:
+   - taskgraph.transforms.name_sanity:transforms
+   - taskgraph.transforms.repackage_signing_partner:transforms
+   - taskgraph.transforms.release_notifications:transforms
+   - taskgraph.transforms.task:transforms
+
+kind-dependencies:
+   - release-partner-repack-repackage
+
+only-for-build-platforms:
+   - win32-nightly/opt
+   - win64-nightly/opt
new file mode 100644
--- /dev/null
+++ b/taskcluster/ci/release-partner-repack-repackage/kind.yml
@@ -0,0 +1,35 @@
+# 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/.
+
+loader: taskgraph.loader.single_dep:loader
+
+transforms:
+   - taskgraph.transforms.chunk_partners:transforms
+   - taskgraph.transforms.name_sanity:transforms
+   - taskgraph.transforms.repackage_partner:transforms
+   - taskgraph.transforms.use_toolchains:transforms
+   - taskgraph.transforms.job:transforms
+   - taskgraph.transforms.release_notifications:transforms
+   - taskgraph.transforms.task:transforms
+
+kind-dependencies:
+   - release-partner-repack
+   - release-partner-repack-signing
+   - toolchain
+
+only-for-build-platforms:
+   - macosx64-nightly/opt
+   - win32-nightly/opt
+   - win64-nightly/opt
+
+job-template:
+   mozharness:
+      config:
+         by-build-platform:
+            macosx64-.*:
+               - repackage/osx_partner.py
+            win32-.*:
+               - repackage/win32_partner.py
+            win64-.*:
+               - repackage/win64_partner.py
new file mode 100644
--- /dev/null
+++ b/taskcluster/ci/release-partner-repack-signing/kind.yml
@@ -0,0 +1,24 @@
+# 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/.
+
+loader: taskgraph.loader.build_signing:loader
+
+transforms:
+   - taskgraph.transforms.name_sanity:transforms
+   - taskgraph.transforms.partner_signing:transforms
+   - taskgraph.transforms.signing:transforms
+   - taskgraph.transforms.release_notifications:transforms
+   - taskgraph.transforms.task:transforms
+
+kind-dependencies:
+   - release-partner-repack
+
+only-for-build-platforms:
+   - macosx64-nightly/opt
+
+job-template:
+   shipping-product: firefox
+   shipping-phase: promote
+   depname: release-partner-repack
+   max-run-time: 7200
--- a/taskcluster/ci/release-partner-repack/kind.yml
+++ b/taskcluster/ci/release-partner-repack/kind.yml
@@ -2,138 +2,79 @@
 # 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/.
 
 loader: taskgraph.loader.transform:loader
 
 transforms:
    - taskgraph.transforms.release_deps:transforms
    - taskgraph.transforms.partner_repack:transforms
+   - taskgraph.transforms.release_notifications:transforms
    - taskgraph.transforms.job:transforms
-   - taskgraph.transforms.release_notifications:transforms
    - taskgraph.transforms.task:transforms
 
 kind-dependencies:
-   - post-beetmover-dummy
+   - build-signing
+   - nightly-l10n-signing
 
 job-defaults:
+   name: partner-repack
    description: Release Promotion partner repacks
-   worker-type: buildbot-bridge/buildbot-bridge
-   run-on-projects: []
+   run-on-projects: []  # to make sure this never runs as part of CI
    shipping-product: firefox
    shipping-phase: promote
+   worker-type: aws-provisioner-v1/gecko-{level}-b-linux
+   worker:
+      docker-image:
+         in-tree: "partner-repack"
+      chain-of-trust: true
+      max-run-time: 7200
+      env:
+         REPACK_MANIFESTS_URL:
+            by-project:
+               mozilla-beta: "git@github.com:mozilla-partners/repack-manifests.git"
+               mozilla-release: "git@github.com:mozilla-partners/repack-manifests.git"
+               maple: "git@github.com:mozilla-partners/repack-manifests.git"
+               default: "git@github.com:mozilla-releng/staging-repack-manifests.git"
    run:
-      using: buildbot
-      release-promotion: true
-      product: firefox
+      using: mozharness
+      config:
+         - partner_repacks/release_mozilla-release_desktop.py
+      script: mozharness/scripts/desktop_partner_repacks.py
+      job-script: taskcluster/scripts/builder/repackage.sh
+      need-xvfb: false
+      tooltool-downloads: false
 
 jobs:
-   firefox-linux:
-      label: firefox linux partner repacks
+   linux-nightly:
       attributes:
          build_platform: linux-nightly
-      worker:
-         properties:
-            repack_manifests_url:
-               by-project:
-                  mozilla-beta: "git@github.com:mozilla-partners/repack-manifests.git"
-                  mozilla-release: "git@github.com:mozilla-partners/repack-manifests.git"
-                  default: "git@github.com:mozilla-releng/staging-repack-manifests.git"
-      run:
-         buildername: release-{branch}-firefox-linux_partner_repacks
+         build_type: opt
+         artifact_prefix: releng/partner
+         nightly: true
 
-   firefox-linux64:
-      label: firefox linux64 partner repacks
+   linux64-nightly:
       attributes:
          build_platform: linux64-nightly
-      worker:
-         properties:
-            repack_manifests_url:
-               by-project:
-                  mozilla-beta: "git@github.com:mozilla-partners/repack-manifests.git"
-                  mozilla-release: "git@github.com:mozilla-partners/repack-manifests.git"
-                  default: "git@github.com:mozilla-releng/staging-repack-manifests.git"
-      run:
-         buildername: release-{branch}-firefox-linux64_partner_repacks
+         build_type: opt
+         artifact_prefix: releng/partner
+         nightly: true
 
-   firefox-macosx:
-      label: firefox macosx64 partner repacks
+   macosx64-nightly:
       attributes:
          build_platform: macosx64-nightly
-      worker:
-         properties:
-            repack_manifests_url:
-               by-project:
-                  mozilla-beta: "git@github.com:mozilla-partners/repack-manifests.git"
-                  mozilla-release: "git@github.com:mozilla-partners/repack-manifests.git"
-                  default: "git@github.com:mozilla-releng/staging-repack-manifests.git"
-      run:
-         buildername: release-{branch}-firefox-macosx64_partner_repacks
+         build_type: opt
+         artifact_prefix: releng/partner
+         nightly: true
 
-   firefox-win32:
-      label: firefox win32 partner repacks
+   win32-nightly:
       attributes:
          build_platform: win32-nightly
-      worker:
-         properties:
-            repack_manifests_url:
-               by-project:
-                  mozilla-beta: "git@github.com:mozilla-partners/repack-manifests.git"
-                  mozilla-release: "git@github.com:mozilla-partners/repack-manifests.git"
-                  default: "git@github.com:mozilla-releng/staging-repack-manifests.git"
-      run:
-         buildername: release-{branch}-firefox-win32_partner_repacks
+         build_type: opt
+         artifact_prefix: releng/partner
+         nightly: true
 
-   firefox-win64:
-      label: firefox win64 partner repacks
+   win64-nightly:
       attributes:
          build_platform: win64-nightly
-      worker:
-         properties:
-            repack_manifests_url:
-               by-project:
-                  mozilla-beta: "git@github.com:mozilla-partners/repack-manifests.git"
-                  mozilla-release: "git@github.com:mozilla-partners/repack-manifests.git"
-                  default: "git@github.com:mozilla-releng/staging-repack-manifests.git"
-      run:
-         buildername: release-{branch}-firefox-win64_partner_repacks
-
-   firefox-macosx-eme-free:
-      label: firefox macosx64 EME-free repacks
-      attributes:
-         build_platform: macosx64-nightly
-      worker:
-         properties:
-            repack_manifests_url:
-               by-project:
-                  mozilla-beta: "git@github.com:mozilla-partners/mozilla-EME-free-manifest"
-                  mozilla-release: "git@github.com:mozilla-partners/mozilla-EME-free-manifest"
-                  default: "git@github.com:mozilla-releng/staging-repack-manifests.git"
-      run:
-         buildername: release-{branch}-firefox-macosx64_partner_repacks
-
-   firefox-win32-eme-free:
-      label: firefox win32 EME-free repacks
-      attributes:
-         build_platform: win32-nightly
-      worker:
-         properties:
-            repack_manifests_url:
-               by-project:
-                  mozilla-beta: "git@github.com:mozilla-partners/mozilla-EME-free-manifest"
-                  mozilla-release: "git@github.com:mozilla-partners/mozilla-EME-free-manifest"
-                  default: "git@github.com:mozilla-releng/staging-repack-manifests.git"
-      run:
-         buildername: release-{branch}-firefox-win32_partner_repacks
-
-   firefox-win64-eme-free:
-      label: firefox win64 EME-free repacks
-      attributes:
-         build_platform: win64-nightly
-      worker:
-         properties:
-            repack_manifests_url:
-               by-project:
-                  mozilla-beta: "git@github.com:mozilla-partners/mozilla-EME-free-manifest"
-                  mozilla-release: "git@github.com:mozilla-partners/mozilla-EME-free-manifest"
-                  default: "git@github.com:mozilla-releng/staging-repack-manifests.git"
-      run:
-         buildername: release-{branch}-firefox-win64_partner_repacks
+         build_type: opt
+         artifact_prefix: releng/partner
+         nightly: true
--- a/taskcluster/ci/release-secondary-final-verify/kind.yml
+++ b/taskcluster/ci/release-secondary-final-verify/kind.yml
@@ -18,17 +18,18 @@ transforms:
 
 job-defaults:
    name: secondary-final-verify
    run-on-projects: []  # to make sure this never runs as part of CI
    worker-type: aws-provisioner-v1/gecko-{level}-b-linux
    worker:
       implementation: docker-worker
       os: linux
-      docker-image: mozillareleases/python-test-runner@sha256:0729c2e6e7bc0d6a4cbccb2e66a78e1d8e8cbb5e44105d56e3c9c610230ebd69
+      docker-image:
+         in-tree: "update-verify"
       max-run-time: 7200
       retry-exit-status: [1]
       env:
          BUILD_TOOLS_REPO:
             by-project:
                jamun: https://hg.mozilla.org/users/stage-ffxbld/tools
                maple: https://hg.mozilla.org/users/asasaki_mozilla.com/tools
                default: https://hg.mozilla.org/build/tools
--- a/taskcluster/ci/test/test-sets.yml
+++ b/taskcluster/ci/test/test-sets.yml
@@ -109,18 +109,16 @@ linux-qr-tests:
     - jittest
     - jsreftest
     - mochitest
     - mochitest-a11y
     - mochitest-gpu
     - mochitest-media
     - mochitest-webgl
     - reftest
-    - test-verify
-    - test-verify-wpt
     - xpcshell
 
 linux-talos-profiling:
     - talos-chrome-profiling
     - talos-damp-profiling
     - talos-dromaeojs-profiling
     - talos-g1-profiling
     - talos-g3-profiling
@@ -140,17 +138,16 @@ linux-talos-flex:
     - talos-flex
 
 windows-qr-tests:
     - crashtest
     - mochitest-gpu
     - mochitest-media
     - mochitest-webgl
     - reftest
-    - test-verify
 
 windows-qr-talos:
     - talos-chrome
     - talos-dromaeojs
     - talos-g1
     - talos-g4
     - talos-perf-reftest
     - talos-perf-reftest-singletons
new file mode 100644
--- /dev/null
+++ b/taskcluster/docker/partner-repack/Dockerfile
@@ -0,0 +1,25 @@
+# %ARG DOCKER_IMAGE_PARENT
+FROM $DOCKER_IMAGE_PARENT
+MAINTAINER Ben Hearsum <bhearsum@mozilla.com>
+
+VOLUME /builds/worker/checkouts
+VOLUME /builds/worker/workspace
+
+RUN dpkg --add-architecture amd64
+
+RUN apt-get update && \
+    apt-get install \
+      bzip2 \
+      curl \
+      git \
+      gzip \
+      openssh-client \
+      unzip \
+      zip && \
+    apt-get clean
+
+# Add wrapper scripts for xvfb for repackage.sh, and interactive sessions
+# %include taskcluster/docker/recipes/xvfb.sh
+COPY topsrcdir/taskcluster/docker/recipes/xvfb.sh /builds/worker/scripts/xvfb.sh
+
+COPY known_hosts /etc/ssh/ssh_known_hosts
new file mode 100644
--- /dev/null
+++ b/taskcluster/docker/partner-repack/known_hosts
@@ -0,0 +1,2 @@
+|1|l1+RDluaBh2vgAVjzSQP5cMsazU=|zA0AvSBB4c3SJ6OM1H0pjrTY/BQ= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==
+|1|A4q4hHD/XV6BPf9bom/6780dCwk=|gm+K1qkEPDUMm1axYNTOkbI1BQ4= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==
--- a/taskcluster/docs/kinds.rst
+++ b/taskcluster/docs/kinds.rst
@@ -349,16 +349,56 @@ Generates source for the release
 release-source-signing
 --------------------
 Signs source for the release
 
 release-partner-repack
 ----------------------
 Generates customized versions of releases for partners.
 
+release-partner-repack-chunking-dummy
+----------------------
+Chunks the partner repacks by locale.
+
+release-partner-repack-signing
+------------------------------
+Internal signing of partner repacks.
+
+release-partner-repack-repackage
+------------------------------
+Repackaging of partner repacks.
+
+release-partner-repack-repackage-signing
+------------------------------
+External signing of partner repacks.
+
+release-partner-repack-beetmover
+------------------------------
+Moves the partner repacks to S3 buckets.
+
+release-eme-free-repack
+----------------------
+Generates customized versions of releases for eme-free repacks.
+
+release-eme-free-repack-signing
+------------------------------
+Internal signing of eme-free repacks
+
+release-eme-free-repack-repackage
+------------------------------
+Repackaging of eme-free repacks.
+
+release-eme-free-repack-repackage-signing
+------------------------------
+External signing of eme-free repacks.
+
+release-eme-free-repack-beetmover
+------------------------------
+Moves the eme-free repacks to S3 buckets.
+
 repackage
 ---------
 Repackage tasks take a signed output and package them up into something suitable
 for shipping to our users. For example, on OSX we return a tarball as the signed output
 and this task would package that up as an Apple Disk Image (.dmg)
 
 repackage-l10n
 --------------
--- a/taskcluster/docs/parameters.rst
+++ b/taskcluster/docs/parameters.rst
@@ -154,16 +154,31 @@ Release Promotion
    Specify the next version for version bump tasks.
 
 ``release_type``
    The type of release being promoted. One of "beta", "devedition", "esr", "rc", or "release".
 
 ``release_eta``
    The time and date when a release is scheduled to live. This value is passed to Balrog.
 
+``release_enable_partners``
+   Boolean which controls repacking vanilla Firefox builds for partners.
+
+``release_partners``
+   List of partners to repack. A null value defaults to all.
+
+``release_partner_config``
+   Configuration for partner repacks.
+
+``release_partner_build_number``
+   The build number for partner repacks. We sometimes have multiple partner build numbers per release build number; this parameter lets us bump them independently. Defaults to 1.
+
+``release_enable_emefree``
+   Boolean which controls repacking vanilla Firefox builds into EME-free builds.
+
 Comm Push Information
 ---------------------
 
 These parameters correspond to the repository and revision of the comm-central
 repository to checkout. Their meaning is the same as the corresponding
 parameters for the gecko repository above. They are optional, but if any of
 them are specified, they must all be specified.
 
--- a/taskcluster/taskgraph/actions/release_promotion.py
+++ b/taskcluster/taskgraph/actions/release_promotion.py
@@ -10,16 +10,17 @@ import json
 import os
 
 from .registry import register_callback_action
 
 from .util import (find_decision_task, find_existing_tasks_from_previous_kinds,
                    find_hg_revision_pushlog_id)
 from taskgraph.util.taskcluster import get_artifact
 from taskgraph.util.partials import populate_release_history
+from taskgraph.util.partners import fix_partner_config
 from taskgraph.taskgraph import TaskGraph
 from taskgraph.decision import taskgraph_decision
 from taskgraph.parameters import Parameters
 from taskgraph.util.attributes import RELEASE_PROMOTION_PROJECTS
 
 RELEASE_PROMOTION_CONFIG = {
     'promote_fennec': {
         'target_tasks_method': 'promote_fennec',
@@ -51,16 +52,28 @@ RELEASE_PROMOTION_CONFIG = {
         'product': 'firefox',
         'release_type': 'rc',
     },
     'ship_firefox_rc': {
         'target_tasks_method': 'ship_firefox',
         'product': 'firefox',
         'release_type': 'rc',
     },
+    'promote_firefox_partners': {
+        'target_tasks_method': 'promote_firefox',
+        'product': 'firefox',
+        'rebuild_kinds': [
+            'release-partner-repack',
+            'release-partner-beetmover',
+            'release-partner-repack-chunking-dummy',
+            'release-partner-repackage-signing',
+            'release-partner-repackage',
+            'release-partner-signing',
+        ],
+    },
     'promote_devedition': {
         'target_tasks_method': 'promote_devedition',
         'product': 'devedition',
     },
     'push_devedition': {
         'target_tasks_method': 'push_devedition',
         'product': 'devedition',
     },
@@ -83,16 +96,19 @@ PARTIAL_UPDATES_FLAVORS = (
     'push_firefox',
     'push_firefox_rc',
     'push_devedition',
     'ship_firefox',
     'ship_firefox_rc',
     'ship_devedition',
 )
 
+PARTNER_BRANCHES = ('mozilla-beta', 'mozilla-release', 'maple', 'birch', 'jamun')
+EMEFREE_BRANCHES = ('mozilla-beta', 'mozilla-release', 'maple', 'birch', 'jamun')
+
 
 def is_release_promotion_available(parameters):
     return parameters['project'] in RELEASE_PROMOTION_PROJECTS
 
 
 @register_callback_action(
     name='release-promotion',
     title='Release Promotion',
@@ -198,21 +214,52 @@ def is_release_promotion_available(param
                     },
                     'required': [
                         'buildNumber',
                         'locales',
                     ],
                     'additionalProperties': False,
                 }
             },
-
             'release_eta': {
                 'type': 'string',
                 'default': '',
             },
+            'release_enable_partners': {
+                'type': 'boolean',
+                'default': False,
+                'description': ('Toggle for creating partner repacks'),
+            },
+            'release_partner_build_number': {
+                'type': 'integer',
+                'default': 1,
+                'minimum': 1,
+                'description': ('The partner build number. This translates to, e.g. '
+                                '`v1` in the path. We generally only have to '
+                                'bump this on off-cycle partner rebuilds.'),
+            },
+            'release_partners': {
+                'type': 'array',
+                'description': ('A list of partners to repack, or if null or empty then use '
+                                'the current full set'),
+                'items': {
+                    'type': 'string',
+                }
+            },
+            'release_partner_config': {
+                'type': 'object',
+                'description': ('Partner configuration to use for partner repacks.'),
+                'properties': {},
+                'additionalProperties': True,
+            },
+            'release_enable_emefree': {
+                'type': 'boolean',
+                'default': False,
+                'description': ('Toggle for creating EME-free repacks'),
+            },
         },
         "required": ['release_promotion_flavor', 'build_number'],
     }
 )
 def release_promotion_action(parameters, graph_config, input, task_group_id, task_id, task):
     release_promotion_flavor = input['release_promotion_flavor']
     promotion_config = RELEASE_PROMOTION_CONFIG[release_promotion_flavor]
     release_history = {}
@@ -248,16 +295,24 @@ def release_promotion_action(parameters,
         project=parameters['project']
     )
     rebuild_kinds = input.get(
         'rebuild_kinds', promotion_config.get('rebuild_kinds', [])
     )
     do_not_optimize = input.get(
         'do_not_optimize', promotion_config.get('do_not_optimize', [])
     )
+    release_enable_partners = input.get(
+        'release_enable_partners',
+        parameters['project'] in PARTNER_BRANCHES and product in ('firefox',)
+    )
+    release_enable_emefree = input.get(
+        'release_enable_emefree',
+        parameters['project'] in EMEFREE_BRANCHES and product in ('firefox',)
+    )
 
     # make parameters read-write
     parameters = dict(parameters)
     # Build previous_graph_ids from ``previous_graph_ids``, ``pushlog_id``,
     # or ``revision``.
     previous_graph_ids = input.get('previous_graph_ids')
     if not previous_graph_ids:
         revision = input.get('revision')
@@ -282,15 +337,21 @@ def release_promotion_action(parameters,
     )
     parameters['do_not_optimize'] = do_not_optimize
     parameters['target_tasks_method'] = target_tasks_method
     parameters['build_number'] = int(input['build_number'])
     parameters['next_version'] = next_version
     parameters['release_history'] = release_history
     parameters['release_type'] = promotion_config.get('release_type', '')
     parameters['release_eta'] = input.get('release_eta', '')
+    parameters['release_enable_partners'] = release_enable_partners
+    parameters['release_partners'] = input.get('release_partners')
+    if input.get('release_partner_config'):
+        parameters['release_partner_config'] = fix_partner_config(input['release_partner_config'])
+    parameters['release_enable_emefree'] = release_enable_emefree
+
     if input['version']:
         parameters['version'] = input['version']
 
     # make parameters read-only
     parameters = Parameters(**parameters)
 
     taskgraph_decision({}, parameters=parameters)
--- a/taskcluster/taskgraph/decision.py
+++ b/taskcluster/taskgraph/decision.py
@@ -193,16 +193,21 @@ def get_decision_parameters(options):
     parameters['existing_tasks'] = {}
     parameters['do_not_optimize'] = []
     parameters['build_number'] = 1
     parameters['version'] = get_version()
     parameters['app_version'] = get_app_version()
     parameters['next_version'] = None
     parameters['release_type'] = ''
     parameters['release_eta'] = ''
+    parameters['release_enable_partners'] = False
+    parameters['release_partners'] = []
+    parameters['release_partner_config'] = {}
+    parameters['release_partner_build_number'] = 1
+    parameters['release_enable_emefree'] = False
 
     # owner must be an email, but sometimes (e.g., for ffxbld) it is not, in which
     # case, fake it
     if '@' not in parameters['owner']:
         parameters['owner'] += '@noreply.mozilla.org'
 
     # use the pushdate as build_date if given, else use current time
     parameters['build_date'] = parameters['pushdate'] or int(time.time())
--- a/taskcluster/taskgraph/loader/build_signing.py
+++ b/taskcluster/taskgraph/loader/build_signing.py
@@ -3,27 +3,29 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 from __future__ import absolute_import, print_function, unicode_literals
 
 from taskgraph.loader.single_dep import loader as base_loader
 
 # XXX: This logic should rely in kind.yml. This hasn't been done in the original
 # patch because it required some heavy changes in single_dep.
-LABELS_WHICH_SHOULD_SIGN_CI_BUILDS = (
+NON_NIGHTLY_LABELS_WHICH_SHOULD_SIGN_BUILDS = (
     'build-win32/debug', 'build-win32/opt', 'build-win32/pgo',
     'build-win64/debug', 'build-win64/opt', 'build-win64/pgo',
     'build-win32-devedition/opt', 'build-win64-devedition/opt',
     'build-win64-ccov/debug',
     'release-source-linux64-source/opt',
     'release-source-linux64-fennec-source/opt',
     'release-source-linux64-devedition-source/opt',
+    'release-eme-free-repack-macosx64-nightly',
+    'release-partner-repack-macosx64-nightly',
 )
 
 
 def loader(kind, path, config, params, loaded_tasks):
     jobs = base_loader(kind, path, config, params, loaded_tasks)
 
     for job in jobs:
         dependent_task = job['dependent-task']
         if dependent_task.attributes.get('nightly') or \
-                dependent_task.label in LABELS_WHICH_SHOULD_SIGN_CI_BUILDS:
+                dependent_task.label in NON_NIGHTLY_LABELS_WHICH_SHOULD_SIGN_BUILDS:
             yield job
--- a/taskcluster/taskgraph/parameters.py
+++ b/taskcluster/taskgraph/parameters.py
@@ -55,18 +55,23 @@ PARAMETERS = {
     'message': '',
     'moz_build_date': lambda: datetime.now().strftime("%Y%m%d%H%M%S"),
     'next_version': None,
     'optimize_target_tasks': True,
     'owner': 'nobody@mozilla.com',
     'project': 'mozilla-central',
     'pushdate': lambda: int(time.time()),
     'pushlog_id': '0',
+    'release_enable_emefree': False,
+    'release_enable_partners': False,
     'release_eta': '',
     'release_history': {},
+    'release_partners': None,
+    'release_partner_config': None,
+    'release_partner_build_number': 1,
     'release_type': '',
     'target_tasks_method': 'default',
     'try_mode': None,
     'try_options': None,
     'try_task_config': None,
     'version': get_version(),
 }
 
new file mode 100644
--- /dev/null
+++ b/taskcluster/taskgraph/transforms/beetmover_repackage_partner.py
@@ -0,0 +1,304 @@
+# 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/.
+"""
+Transform the beetmover task into an actual task description.
+"""
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+from taskgraph.transforms.base import TransformSequence
+from taskgraph.transforms.beetmover import craft_release_properties
+from taskgraph.util.attributes import copy_attributes_from_dependent_job
+from taskgraph.util.partners import (
+    check_if_partners_enabled,
+    get_ftp_platform,
+    get_partner_config_by_kind,
+)
+from taskgraph.util.schema import (
+    Schema,
+    optionally_keyed_by,
+    resolve_keyed_by,
+    validate_schema,
+)
+from taskgraph.util.scriptworker import (
+    add_scope_prefix,
+    get_beetmover_bucket_scope,
+    get_worker_type_for_scope,
+)
+from taskgraph.util.taskcluster import get_artifact_prefix
+from taskgraph.transforms.task import task_description_schema
+from voluptuous import Any, Required, Optional
+
+from copy import deepcopy
+import logging
+
+logger = logging.getLogger(__name__)
+
+
+# Voluptuous uses marker objects as dictionary *keys*, but they are not
+# comparable, so we cast all of the keys back to regular strings
+task_description_schema = {str(k): v for k, v in task_description_schema.schema.iteritems()}
+
+transforms = TransformSequence()
+
+# shortcut for a string where task references are allowed
+taskref_or_string = Any(
+    basestring,
+    {Required('task-reference'): basestring})
+
+beetmover_description_schema = Schema({
+    # the dependent task (object) for this beetmover job, used to inform beetmover.
+    Required('dependent-task'): object,
+
+    # depname is used in taskref's to identify the taskID of the unsigned things
+    Required('depname', default='build'): basestring,
+
+    # unique label to describe this beetmover task, defaults to {dep.label}-beetmover
+    Optional('label'): basestring,
+
+    Required('partner-bucket-scope'): optionally_keyed_by('project', basestring),
+    Required('partner-public-path'): Any(None, basestring),
+    Required('partner-private-path'): Any(None, basestring),
+
+    Optional('extra'): object,
+    Required('shipping-phase'): task_description_schema['shipping-phase'],
+    Optional('shipping-product'): task_description_schema['shipping-product'],
+})
+
+transforms.add(check_if_partners_enabled)
+
+
+@transforms.add
+def validate(config, jobs):
+    for job in jobs:
+        label = job.get('dependent-task', object).__dict__.get('label', '?no-label?')
+        validate_schema(
+            beetmover_description_schema, job,
+            "In beetmover ({!r} kind) task for {!r}:".format(config.kind, label))
+        yield job
+
+
+@transforms.add
+def skip_for_indirect_dependencies(config, jobs):
+    for job in jobs:
+        dep_job = job['dependent-task']
+        build_platform = dep_job.attributes.get("build_platform")
+        if not build_platform:
+            raise Exception("Cannot find build platform!")
+
+        # Partner and EME free beetmover tasks have multiple upstreams defined
+        # because some platforms don't run some parts of the sign -> repack ->
+        # repack sign chain. We only want to run beetmover for the last part of
+        # that chain that runs for any given platform.
+        # For Linux, it is the eme-free/partner repack build tasks.
+        # For Mac, it is repackage.
+        # For Windows, it is repackage-signing.
+        if "win" in build_platform:
+            if "repackage" not in dep_job.label:
+                continue
+            elif "signing" not in dep_job.label:
+                continue
+        if "macosx" in build_platform:
+            if "repackage" not in dep_job.label:
+                continue
+
+        yield job
+
+
+@transforms.add
+def resolve_keys(config, jobs):
+    for job in jobs:
+        resolve_keyed_by(
+            job, 'partner-bucket-scope', item_name=job['label'], project=config.params['project']
+        )
+        yield job
+
+
+@transforms.add
+def make_task_description(config, jobs):
+    for job in jobs:
+        dep_job = job['dependent-task']
+        repack_id = dep_job.task.get('extra', {}).get('repack_id')
+        if not repack_id:
+            raise Exception("Cannot find repack id!")
+
+        attributes = dep_job.attributes
+        build_platform = attributes.get("build_platform")
+        if not build_platform:
+            raise Exception("Cannot find build platform!")
+
+        label = dep_job.label.replace("repackage-signing-", "beetmover-")
+        label = label.replace("repackage-", "beetmover-")
+        label = label.replace("chunking-dummy-", "beetmover-")
+        description = (
+            "Beetmover submission for repack_id '{repack_id}' for build '"
+            "{build_platform}/{build_type}'".format(
+                repack_id=repack_id,
+                build_platform=build_platform,
+                build_type=attributes.get('build_type')
+            )
+        )
+
+        dependencies = {}
+
+        base_label = "release-partner-repack"
+        if "eme" in config.kind:
+            base_label = "release-eme-free-repack"
+        dependencies["build"] = "{}-{}".format(base_label, build_platform)
+        if "macosx" in build_platform or "win" in build_platform:
+            dependencies["repackage"] = "{}-repackage-{}-{}".format(
+                base_label, build_platform, repack_id.replace('/', '-')
+            )
+        if "win" in build_platform:
+            dependencies["repackage-signing"] = "{}-repackage-signing-{}-{}".format(
+                base_label, build_platform, repack_id.replace('/', '-')
+            )
+
+        attributes = copy_attributes_from_dependent_job(dep_job)
+
+        task = {
+            'label': label,
+            'description': description,
+            'dependencies': dependencies,
+            'attributes': attributes,
+            'run-on-projects': dep_job.attributes.get('run_on_projects'),
+            'shipping-phase': job['shipping-phase'],
+            'shipping-product': job.get('shipping-product'),
+            'partner-private-path': job['partner-private-path'],
+            'partner-public-path': job['partner-public-path'],
+            'partner-bucket-scope': job['partner-bucket-scope'],
+            'extra': {
+                'repack_id': repack_id,
+            },
+        }
+        yield task
+
+
+def populate_scopes_and_worker_type(config, job, bucket_scope, partner_public=False):
+    action_scope = add_scope_prefix(config, 'beetmover:action:push-to-partner')
+
+    task = deepcopy(job)
+    task['scopes'] = [bucket_scope, action_scope]
+    task['worker-type'] = get_worker_type_for_scope(config, bucket_scope)
+    task['partner_public'] = partner_public
+    if partner_public:
+        task['label'] = "{}-public".format(task['label'])
+    return task
+
+
+@transforms.add
+def split_public_and_private(config, jobs):
+    public_bucket_scope = get_beetmover_bucket_scope(config)
+    partner_config = get_partner_config_by_kind(config, config.kind)
+
+    for job in jobs:
+        partner_bucket_scope = add_scope_prefix(config, job['partner-bucket-scope'])
+        partner, subpartner, _ = job['extra']['repack_id'].split('/')
+
+        # public
+        if partner_config[partner][subpartner].get('upload_to_candidates'):
+            yield populate_scopes_and_worker_type(
+                config, job, public_bucket_scope, partner_public=True
+            )
+        # private
+        yield populate_scopes_and_worker_type(
+            config, job, partner_bucket_scope, partner_public=False
+        )
+
+
+def generate_upstream_artifacts(job, build_task_ref, repackage_task_ref,
+                                repackage_signing_task_ref, platform, repack_id,
+                                partner_path):
+
+    upstream_artifacts = []
+    artifact_prefix = get_artifact_prefix(job)
+
+    if "linux" in platform:
+        upstream_artifacts.append({
+            "taskId": {"task-reference": build_task_ref},
+            "taskType": "build",
+            "paths": ["{}/{}/target.tar.bz2".format(artifact_prefix, repack_id)],
+            "locale": partner_path,
+        })
+    elif "macosx" in platform:
+        upstream_artifacts.append({
+            "taskId": {"task-reference": repackage_task_ref},
+            "taskType": "repackage",
+            "paths": ["{}/{}/target.dmg".format(artifact_prefix, repack_id)],
+            "locale": partner_path,
+        })
+    elif "win" in platform:
+        upstream_artifacts.append({
+            "taskId": {"task-reference": repackage_signing_task_ref},
+            "taskType": "repackage",
+            "paths": ["{}/{}/target.installer.exe".format(artifact_prefix, repack_id)],
+            "locale": partner_path,
+        })
+
+    if not upstream_artifacts:
+        raise Exception("Couldn't find any upstream artifacts.")
+
+    return upstream_artifacts
+
+
+@transforms.add
+def make_task_worker(config, jobs):
+    for job in jobs:
+        platform = job["attributes"]["build_platform"]
+        repack_id = job["extra"]["repack_id"]
+        partner, subpartner, locale = job['extra']['repack_id'].split('/')
+        build_task = None
+        repackage_task = None
+        repackage_signing_task = None
+
+        for dependency in job["dependencies"].keys():
+            if 'repackage-signing' in dependency:
+                repackage_signing_task = dependency
+            elif 'repackage' in dependency:
+                repackage_task = dependency
+            else:
+                build_task = "build"
+
+        build_task_ref = "<" + str(build_task) + ">"
+        repackage_task_ref = "<" + str(repackage_task) + ">"
+        repackage_signing_task_ref = "<" + str(repackage_signing_task) + ">"
+
+        # generate the partner path; we'll send this to beetmover as the "locale"
+        ftp_platform = get_ftp_platform(platform)
+        repl_dict = {
+            "build_number": config.params['build_number'],
+            "locale": locale,
+            "partner": partner,
+            "platform": ftp_platform,
+            "release_partner_build_number": config.params['release_partner_build_number'],
+            "subpartner": subpartner,
+            "version": config.params['version'],
+        }
+        partner_public = job['partner_public']
+        if partner_public:
+            partner_path_key = 'partner-public-path'
+        else:
+            partner_path_key = 'partner-private-path'
+        # Kinds can set these to None
+        if not job[partner_path_key]:
+            continue
+        partner_path = job[partner_path_key].format(**repl_dict)
+        del(job['partner_public'])
+        del(job['partner-private-path'])
+        del(job['partner-public-path'])
+        del(job['partner-bucket-scope'])
+
+        worker = {
+            'implementation': 'beetmover',
+            'release-properties': craft_release_properties(config, job),
+            'upstream-artifacts': generate_upstream_artifacts(
+                job, build_task_ref, repackage_task_ref,
+                repackage_signing_task_ref, platform, repack_id,
+                partner_path
+            ),
+            'partner-public': partner_public,
+        }
+        job["worker"] = worker
+
+        yield job
new file mode 100644
--- /dev/null
+++ b/taskcluster/taskgraph/transforms/chunk_partners.py
@@ -0,0 +1,37 @@
+# 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/.
+"""
+Chunk the partner repack tasks by subpartner and locale
+"""
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+import copy
+
+from taskgraph.transforms.base import TransformSequence
+from taskgraph.util.partners import get_partner_config_by_kind, locales_per_build_platform
+
+transforms = TransformSequence()
+
+
+@transforms.add
+def chunk_partners(config, jobs):
+    partner_configs = get_partner_config_by_kind(config, config.kind)
+
+    for job in jobs:
+        dep_job = job['dependent-task']
+        build_platform = dep_job.attributes["build_platform"]
+        for partner, partner_config in partner_configs.iteritems():
+            for sub_partner, cfg in partner_config.iteritems():
+                if build_platform not in cfg.get("platforms", []):
+                    continue
+                locales = locales_per_build_platform(build_platform, cfg.get('locales', []))
+                for locale in locales:
+                    repack_id = "{}/{}/{}".format(partner, sub_partner, locale)
+
+                    partner_job = copy.deepcopy(job)  # don't overwrite dict values here
+                    partner_job.setdefault('extra', {})
+                    partner_job['extra']['repack_id'] = repack_id
+
+                    yield partner_job
new file mode 100644
--- /dev/null
+++ b/taskcluster/taskgraph/transforms/copy_attributes_from_dependent_task.py
@@ -0,0 +1,22 @@
+# 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/.
+"""
+Transform the repackage task into an actual task description.
+"""
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+from taskgraph.transforms.base import TransformSequence
+from taskgraph.util.attributes import copy_attributes_from_dependent_job
+
+transforms = TransformSequence()
+
+
+@transforms.add
+def copy_attributes(config, jobs):
+    for job in jobs:
+        job.setdefault('attributes', {})
+        job['attributes'].update(copy_attributes_from_dependent_job(job['dependent-task']))
+
+        yield job
--- a/taskcluster/taskgraph/transforms/job/common.py
+++ b/taskcluster/taskgraph/transforms/job/common.py
@@ -46,20 +46,20 @@ def add_artifacts(config, job, taskdesc,
 
 def docker_worker_add_artifacts(config, job, taskdesc):
     """ Adds an artifact directory to the task """
     add_artifacts(config, job, taskdesc, path='/builds/worker/artifacts/')
 
 
 def generic_worker_add_artifacts(config, job, taskdesc):
     """ Adds an artifact directory to the task """
-    # This ``public/build`` is the location on disk; it doesn't necessarily
-    # mean the artifacts will be public; that is set via the ``artifact_prefix``
-    # attribute.
-    add_artifacts(config, job, taskdesc, path=r'public/build')
+    # The path is the location on disk; it doesn't necessarily
+    # mean the artifacts will be public or private; that is set via the name
+    # attribute in add_artifacts.
+    add_artifacts(config, job, taskdesc, path=get_artifact_prefix(taskdesc))
 
 
 def docker_worker_add_gecko_vcs_env_vars(config, job, taskdesc):
     """Add the GECKO_BASE_* and GECKO_HEAD_* env vars to the worker."""
     env = taskdesc['worker'].setdefault('env', {})
     env.update({
         'GECKO_BASE_REPOSITORY': config.params['base_repository'],
         'GECKO_HEAD_REF': config.params['head_rev'],
--- a/taskcluster/taskgraph/transforms/name_sanity.py
+++ b/taskcluster/taskgraph/transforms/name_sanity.py
@@ -21,16 +21,22 @@ def make_label(config, jobs):
     for job in jobs:
         dep_job = job['dependent-task']
         attr = dep_job.attributes.get
 
         if attr('locale', job.get('locale')):
             template = "{kind}-{locale}-{build_platform}/{build_type}"
         elif attr('l10n_chunk'):
             template = "{kind}-{build_platform}-{l10n_chunk}/{build_type}"
+        elif config.kind.startswith("release-eme-free") or \
+                config.kind.startswith("release-partner-repack"):
+            repack_id = job.get("extra", {}).get("repack_id", None)
+            template = "{kind}-{build_platform}"
+            if repack_id:
+                template += "-{}".format(repack_id.replace('/', '-'))
         else:
             template = "{kind}-{build_platform}/{build_type}"
         job['label'] = template.format(
             kind=config.kind,
             build_platform=attr('build_platform'),
             build_type=attr('build_type'),
             locale=attr('locale', job.get('locale', '')),  # Locale can be absent
             l10n_chunk=attr('l10n_chunk', '')  # Can be empty
--- a/taskcluster/taskgraph/transforms/partner_repack.py
+++ b/taskcluster/taskgraph/transforms/partner_repack.py
@@ -4,20 +4,60 @@
 """
 Transform the partner repack task into an actual task description.
 """
 
 from __future__ import absolute_import, print_function, unicode_literals
 
 from taskgraph.transforms.base import TransformSequence
 from taskgraph.util.schema import resolve_keyed_by
+from taskgraph.util.scriptworker import get_release_config
+from taskgraph.util.partners import check_if_partners_enabled
 
 
 transforms = TransformSequence()
 
+transforms.add(check_if_partners_enabled)
+
 
 @transforms.add
 def resolve_properties(config, tasks):
     for task in tasks:
-        for property in ("repack_manifests_url", ):
-            property = "worker.properties.{}".format(property)
+        for property in ("REPACK_MANIFESTS_URL", ):
+            property = "worker.env.{}".format(property)
             resolve_keyed_by(task, property, property, **config.params)
-            yield task
+
+        if task['worker']['env']['REPACK_MANIFESTS_URL'].startswith('git@'):
+            task.setdefault('scopes', []).append(
+                'secrets:get:project/releng/gecko/build/level-{level}/partner-github-ssh'.format(
+                    **config.params
+                )
+            )
+
+        yield task
+
+
+@transforms.add
+def make_label(config, tasks):
+    for task in tasks:
+        task['label'] = "{}-{}".format(config.kind, task['name'])
+        yield task
+
+
+@transforms.add
+def add_command_arguments(config, tasks):
+    release_config = get_release_config(config)
+    for task in tasks:
+        # add the MOZHARNESS_OPTIONS, eg version=61.0, build-number=1, platform=win64
+        task['run']['options'] = [
+            'version={}'.format(release_config['version']),
+            'build-number={}'.format(release_config['build_number']),
+            'platform={}'.format(task['attributes']['build_platform'].split('-')[0]),
+        ]
+
+        # The upstream taskIds are stored a special environment variable, because we want to use
+        # task-reference's to resolve dependencies, but the string handling of MOZHARNESS_OPTIONS
+        # blocks that. It's space-separated string of ids in the end.
+        task['worker']['env']['UPSTREAM_TASKIDS'] = {
+            'task-reference': ' '.join(['<{}>'.format(dep) for dep in task['dependencies']])
+        }
+
+        yield task
new file mode 100644
--- /dev/null
+++ b/taskcluster/taskgraph/transforms/partner_signing.py
@@ -0,0 +1,54 @@
+# 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/.
+"""
+Transform the signing task into an actual task description.
+"""
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+from taskgraph.transforms.base import TransformSequence
+from taskgraph.util.partners import (get_partner_config_by_kind, check_if_partners_enabled,
+                                     locales_per_build_platform)
+from taskgraph.util.signed_artifacts import generate_specifications_of_artifacts_to_sign
+
+transforms = TransformSequence()
+
+transforms.add(check_if_partners_enabled)
+
+
+@transforms.add
+def define_upstream_artifacts(config, jobs):
+    partner_configs = get_partner_config_by_kind(config, config.kind)
+    if not partner_configs:
+        return
+
+    for job in jobs:
+        dep_job = job['dependent-task']
+        build_platform = dep_job.attributes.get('build_platform')
+
+        repack_ids = []
+        for partner, partner_config in partner_configs.iteritems():
+            for sub_partner, cfg in partner_config.iteritems():
+                if not cfg or build_platform not in cfg["platforms"]:
+                    continue
+                for locale in locales_per_build_platform(build_platform, cfg.get('locales', [])):
+                    repack_ids.append("{}/{}/{}".format(partner, sub_partner, locale))
+
+        artifacts_specifications = generate_specifications_of_artifacts_to_sign(
+            dep_job,
+            keep_locale_template=True,
+            kind=config.kind,
+        )
+        job['upstream-artifacts'] = [{
+            'taskId': {'task-reference': '<{}>'.format(job['depname'])},
+            'taskType': 'build',
+            'paths': [
+                path_template.format(locale=repack_id)
+                for repack_id in repack_ids
+                for path_template in spec['artifacts']
+            ],
+            'formats': spec['formats'],
+        } for spec in artifacts_specifications]
+
+        yield job
new file mode 100644
--- /dev/null
+++ b/taskcluster/taskgraph/transforms/repackage_partner.py
@@ -0,0 +1,268 @@
+# 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/.
+"""
+Transform the repackage task into an actual task description.
+"""
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+import copy
+
+from taskgraph.transforms.base import TransformSequence
+from taskgraph.util.attributes import copy_attributes_from_dependent_job
+from taskgraph.util.schema import (
+    validate_schema,
+    optionally_keyed_by,
+    resolve_keyed_by,
+    Schema,
+)
+from taskgraph.util.taskcluster import get_taskcluster_artifact_prefix, get_artifact_prefix
+from taskgraph.util.partners import check_if_partners_enabled
+from taskgraph.transforms.task import task_description_schema
+from voluptuous import Any, Required, Optional
+
+transforms = TransformSequence()
+
+# Voluptuous uses marker objects as dictionary *keys*, but they are not
+# comparable, so we cast all of the keys back to regular strings
+task_description_schema = {str(k): v for k, v in task_description_schema.schema.iteritems()}
+
+
+def _by_platform(arg):
+    return optionally_keyed_by('build-platform', arg)
+
+
+# shortcut for a string where task references are allowed
+taskref_or_string = Any(
+    basestring,
+    {Required('task-reference'): basestring})
+
+packaging_description_schema = Schema({
+    # the dependant task (object) for this  job, used to inform repackaging.
+    Required('dependent-task'): object,
+
+    # depname is used in taskref's to identify the taskID of the signed things
+    Required('depname', default='build'): basestring,
+
+    # unique label to describe this repackaging task
+    Optional('label'): basestring,
+
+    # Routes specific to this task, if defined
+    Optional('routes'): [basestring],
+
+    # passed through directly to the job description
+    Optional('extra'): task_description_schema['extra'],
+
+    # Shipping product and phase
+    Optional('shipping-product'): task_description_schema['shipping-product'],
+    Optional('shipping-phase'): task_description_schema['shipping-phase'],
+
+    # All l10n jobs use mozharness
+    Required('mozharness'): {
+        # Config files passed to the mozharness script
+        Required('config'): _by_platform([basestring]),
+
+        # Additional paths to look for mozharness configs in. These should be
+        # relative to the base of the source checkout
+        Optional('config-paths'): [basestring],
+
+        # if true, perform a checkout of a comm-central based branch inside the
+        # gecko checkout
+        Required('comm-checkout', default=False): bool,
+    }
+})
+
+transforms.add(check_if_partners_enabled)
+
+
+@transforms.add
+def validate(config, jobs):
+    for job in jobs:
+        label = job.get('dependent-task', object).__dict__.get('label', '?no-label?')
+        validate_schema(
+            packaging_description_schema, job,
+            "In packaging ({!r} kind) task for {!r}:".format(config.kind, label))
+        yield job
+
+
+@transforms.add
+def copy_in_useful_magic(config, jobs):
+    """Copy attributes from upstream task to be used for keyed configuration."""
+    for job in jobs:
+        dep = job['dependent-task']
+        job['build-platform'] = dep.attributes.get("build_platform")
+        yield job
+
+
+@transforms.add
+def handle_keyed_by(config, jobs):
+    """Resolve fields that can be keyed by platform, etc."""
+    fields = [
+        "mozharness.config",
+    ]
+    for job in jobs:
+        job = copy.deepcopy(job)  # don't overwrite dict values here
+        for field in fields:
+            resolve_keyed_by(item=job, field=field, item_name="?")
+        yield job
+
+
+@transforms.add
+def make_repackage_description(config, jobs):
+    for job in jobs:
+        dep_job = job['dependent-task']
+
+        label = job.get('label',
+                        dep_job.label.replace("signing-", "repackage-"))
+        job['label'] = label
+
+        yield job
+
+
+@transforms.add
+def make_job_description(config, jobs):
+    for job in jobs:
+        dep_job = job['dependent-task']
+        attributes = copy_attributes_from_dependent_job(dep_job)
+        build_platform = attributes['build_platform']
+
+        if job['build-platform'].startswith('win'):
+            if dep_job.kind.endswith('signing'):
+                continue
+        if job['build-platform'].startswith('macosx'):
+            if dep_job.kind.endswith('repack'):
+                continue
+        dependencies = {dep_job.attributes.get('kind'): dep_job.label}
+        dependencies.update(dep_job.dependencies)
+
+        signing_task = None
+        for dependency in dependencies.keys():
+            if build_platform.startswith('macosx') and dependency.endswith('signing'):
+                signing_task = dependency
+            elif build_platform.startswith('win') and dependency.endswith('repack'):
+                signing_task = dependency
+        signing_task_ref = "<{}>".format(signing_task)
+
+        attributes['repackage_type'] = 'repackage'
+
+        level = config.params['level']
+        repack_id = job['extra']['repack_id']
+
+        run = job.get('mozharness', {})
+        run.update({
+            'using': 'mozharness',
+            'script': 'mozharness/scripts/repackage.py',
+            'job-script': 'taskcluster/scripts/builder/repackage.sh',
+            'actions': ['download_input', 'setup', 'repackage'],
+            'extra-workspace-cache-key': 'repackage',
+        })
+
+        worker = {
+            'env': _generate_task_env(build_platform, signing_task, signing_task_ref,
+                                      partner=repack_id),
+            'artifacts': _generate_task_output_files(dep_job, build_platform, partner=repack_id),
+            'chain-of-trust': True,
+            'max-run-time': 7200 if build_platform.startswith('win') else 3600,
+            'taskcluster-proxy': True if get_artifact_prefix(dep_job) else False,
+        }
+
+        worker['env'].update(REPACK_ID=repack_id)
+
+        if build_platform.startswith('win'):
+            worker_type = 'aws-provisioner-v1/gecko-%s-b-win2012' % level
+            run['use-magic-mh-args'] = False
+        else:
+            if build_platform.startswith('macosx'):
+                worker_type = 'aws-provisioner-v1/gecko-%s-b-macosx64' % level
+            else:
+                raise NotImplementedError(
+                    'Unsupported build_platform: "{}"'.format(build_platform)
+                )
+
+            run['tooltool-downloads'] = 'internal'
+            worker['docker-image'] = {"in-tree": "debian7-amd64-build"}
+
+        description = (
+            "Repackaging for repack_id '{repack_id}' for build '"
+            "{build_platform}/{build_type}'".format(
+                repack_id=job['extra']['repack_id'],
+                build_platform=attributes.get('build_platform'),
+                build_type=attributes.get('build_type')
+            )
+        )
+
+        task = {
+            'label': job['label'],
+            'description': description,
+            'worker-type': worker_type,
+            'dependencies': dependencies,
+            'attributes': attributes,
+            'scopes': ['queue:get-artifact:releng/partner/*'],
+            'run-on-projects': dep_job.attributes.get('run_on_projects'),
+            'routes': job.get('routes', []),
+            'extra': job.get('extra', {}),
+            'worker': worker,
+            'run': run,
+        }
+
+        if build_platform.startswith('macosx'):
+            task['toolchains'] = [
+                'linux64-libdmg',
+                'linux64-hfsplus',
+            ]
+        yield task
+
+
+def _generate_task_env(build_platform, signing_task, signing_task_ref, partner):
+    # Force private artifacts here, until we can populate our dependency map
+    # with actual task definitions rather than labels.
+    # (get_taskcluster_artifact_prefix requires the task definition to find
+    # the artifact_prefix attribute).
+    signed_prefix = get_taskcluster_artifact_prefix(
+        signing_task, signing_task_ref, locale=partner, force_private=True
+    )
+    signed_prefix = signed_prefix.replace('public/build', 'releng/partner')
+
+    if build_platform.startswith('macosx'):
+        return {
+            'SIGNED_INPUT': {'task-reference': '{}target.tar.gz'.format(signed_prefix)},
+        }
+    elif build_platform.startswith('win'):
+        task_env = {
+            'SIGNED_ZIP': {'task-reference': '{}target.zip'.format(signed_prefix)},
+            'SIGNED_SETUP': {'task-reference': '{}setup.exe'.format(signed_prefix)},
+        }
+
+        return task_env
+
+    raise NotImplementedError('Unsupported build_platform: "{}"'.format(build_platform))
+
+
+def _generate_task_output_files(task, build_platform, partner):
+    """We carefully generate an explicit list here, but there's an artifacts directory
+    too, courtesy of generic_worker_add_artifacts() (windows) or docker_worker_add_artifacts().
+    Any errors here are likely masked by that.
+    """
+    partner_output_path = '{}/'.format(partner)
+    artifact_prefix = get_artifact_prefix(task)
+
+    if build_platform.startswith('macosx'):
+        output_files = [{
+            'type': 'file',
+            'path': '/builds/worker/workspace/build/artifacts/{}target.dmg'
+                    .format(partner_output_path),
+            'name': '{}/{}target.dmg'.format(artifact_prefix, partner_output_path),
+        }]
+
+    elif build_platform.startswith('win'):
+        output_files = [{
+            'type': 'file',
+            'path': '{}/{}target.installer.exe'.format(artifact_prefix, partner_output_path),
+            'name': '{}/{}target.installer.exe'.format(artifact_prefix, partner_output_path),
+        }]
+
+    if output_files:
+        return output_files
+
+    raise NotImplementedError('Unsupported build_platform: "{}"'.format(build_platform))
new file mode 100644
--- /dev/null
+++ b/taskcluster/taskgraph/transforms/repackage_signing_partner.py
@@ -0,0 +1,114 @@
+# 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/.
+"""
+Transform the repackage signing task into an actual task description.
+"""
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+from taskgraph.transforms.base import TransformSequence
+from taskgraph.util.attributes import copy_attributes_from_dependent_job
+from taskgraph.util.partners import check_if_partners_enabled
+from taskgraph.util.schema import validate_schema, Schema
+from taskgraph.util.scriptworker import (
+    add_scope_prefix,
+    get_signing_cert_scope_per_platform,
+)
+from taskgraph.util.taskcluster import get_artifact_path
+from taskgraph.transforms.task import task_description_schema
+from voluptuous import Required, Optional
+
+# Voluptuous uses marker objects as dictionary *keys*, but they are not
+# comparable, so we cast all of the keys back to regular strings
+task_description_schema = {str(k): v for k, v in task_description_schema.schema.iteritems()}
+
+transforms = TransformSequence()
+
+repackage_signing_description_schema = Schema({
+    Required('dependent-task'): object,
+    Required('depname', default='repackage'): basestring,
+    Optional('label'): basestring,
+    Optional('extra'): object,
+    Optional('shipping-product'): task_description_schema['shipping-product'],
+    Optional('shipping-phase'): task_description_schema['shipping-phase'],
+})
+
+transforms.add(check_if_partners_enabled)
+
+
+@transforms.add
+def validate(config, jobs):
+    for job in jobs:
+        label = job.get('dependent-task', object).__dict__.get('label', '?no-label?')
+        validate_schema(
+            repackage_signing_description_schema, job,
+            "In repackage-signing ({!r} kind) task for {!r}:".format(config.kind, label))
+        yield job
+
+
+@transforms.add
+def make_repackage_signing_description(config, jobs):
+    for job in jobs:
+        dep_job = job['dependent-task']
+        repack_id = dep_job.task['extra']['repack_id']
+        attributes = dep_job.attributes
+
+        label = dep_job.label.replace("repackage-", "repackage-signing-")
+        description = (
+            "Signing of repackaged artifacts for partner repack id '{repack_id}' for build '"
+            "{build_platform}/{build_type}'".format(
+                repack_id=repack_id,
+                build_platform=attributes.get('build_platform'),
+                build_type=attributes.get('build_type')
+            )
+        )
+
+        dependencies = {"repackage": dep_job.label}
+
+        signing_dependencies = dep_job.dependencies
+        # This is so we get the build task etc in our dependencies to
+        # have better beetmover support.
+        dependencies.update({k: v for k, v in signing_dependencies.items()
+                             if k != 'docker-image'})
+        attributes = copy_attributes_from_dependent_job(dep_job)
+        attributes['repackage_type'] = 'repackage-signing'
+
+        build_platform = dep_job.attributes.get('build_platform')
+        is_nightly = dep_job.attributes.get('nightly')
+        signing_cert_scope = get_signing_cert_scope_per_platform(
+            build_platform, is_nightly, config
+        )
+        scopes = [signing_cert_scope]
+
+        if 'win' not in build_platform:
+            raise Exception("Repackage signing is not supported for non-Windows partner repacks.")
+
+        upstream_artifacts = [{
+            "taskId": {"task-reference": "<repackage>"},
+            "taskType": "repackage",
+            "paths": [
+                get_artifact_path(dep_job, "{}/target.installer.exe".format(repack_id)),
+            ],
+            "formats": ["sha2signcode"]
+        }]
+        scopes.append(add_scope_prefix(config, "signing:format:sha2signcode"))
+
+        task = {
+            'label': label,
+            'description': description,
+            # 'worker-type': get_worker_type_for_scope(config, signing_cert_scope),
+            'worker-type': 'scriptworker-prov-v1/signing-linux-v1',
+            'worker': {'implementation': 'scriptworker-signing',
+                       'upstream-artifacts': upstream_artifacts,
+                       'max-run-time': 3600},
+            'scopes': scopes,
+            'dependencies': dependencies,
+            'attributes': attributes,
+            'run-on-projects': dep_job.attributes.get('run_on_projects'),
+            'extra': {
+                'repack_id': repack_id,
+            }
+        }
+
+        yield task
--- a/taskcluster/taskgraph/transforms/signing.py
+++ b/taskcluster/taskgraph/transforms/signing.py
@@ -61,16 +61,19 @@ signing_description_schema = Schema({
     # below transforms for defaults of various values.
     Optional('treeherder'): task_description_schema['treeherder'],
 
     # Routes specific to this task, if defined
     Optional('routes'): [basestring],
 
     Optional('shipping-phase'): task_description_schema['shipping-phase'],
     Optional('shipping-product'): task_description_schema['shipping-product'],
+
+    # Optional control for how long a task may run (aka maxRunTime)
+    Optional('max-run-time'): int,
 })
 
 
 @transforms.add
 def set_defaults(config, jobs):
     for job in jobs:
         job.setdefault('depname', 'build')
         yield job
@@ -97,30 +100,32 @@ def make_task_description(config, jobs):
         for artifacts in job['upstream-artifacts']:
             for f in artifacts['formats']:
                 formats.add(f)  # Add each format only once
         for format in formats:
             signing_format_scopes.append(
                 add_scope_prefix(config, 'signing:format:{}'.format(format))
             )
 
-        treeherder = job.get('treeherder', {})
         is_nightly = dep_job.attributes.get('nightly', False)
-        treeherder.setdefault('symbol', _generate_treeherder_symbol(is_nightly))
+        treeherder = None
+        if 'partner' not in config.kind:
+            treeherder = job.get('treeherder', {})
+            treeherder.setdefault('symbol', _generate_treeherder_symbol(is_nightly, config.kind))
 
-        dep_th_platform = dep_job.task.get('extra', {}).get(
-            'treeherder', {}).get('machine', {}).get('platform', '')
-        build_type = dep_job.attributes.get('build_type')
-        build_platform = dep_job.attributes.get('build_platform')
-        treeherder.setdefault('platform', _generate_treeherder_platform(
-            dep_th_platform, build_platform, build_type
-        ))
+            dep_th_platform = dep_job.task.get('extra', {}).get(
+                'treeherder', {}).get('machine', {}).get('platform', '')
+            build_type = dep_job.attributes.get('build_type')
+            build_platform = dep_job.attributes.get('build_platform')
+            treeherder.setdefault('platform', _generate_treeherder_platform(
+                dep_th_platform, build_platform, build_type
+            ))
 
-        treeherder.setdefault('tier', 1 if '-ccov' not in build_platform else 2)
-        treeherder.setdefault('kind', 'build')
+            treeherder.setdefault('tier', 1 if '-ccov' not in build_platform else 2)
+            treeherder.setdefault('kind', 'build')
 
         label = job['label']
         description = (
             "Initial Signing for locale '{locale}' for build '"
             "{build_platform}/{build_type}'".format(
                 locale=attributes.get('locale', 'en-US'),
                 build_platform=attributes.get('build_platform'),
                 build_type=attributes.get('build_type')
@@ -139,33 +144,39 @@ def make_task_description(config, jobs):
         )
 
         task = {
             'label': label,
             'description': description,
             'worker-type': get_worker_type_for_scope(config, signing_cert_scope),
             'worker': {'implementation': 'scriptworker-signing',
                        'upstream-artifacts': job['upstream-artifacts'],
-                       'max-run-time': 3600},
+                       'max-run-time': job.get('max-run-time', 3600)},
             'scopes': [signing_cert_scope] + signing_format_scopes,
             'dependencies': {job['depname']: dep_job.label},
             'attributes': attributes,
             'run-on-projects': dep_job.attributes.get('run_on_projects'),
             'optimization': dep_job.optimization,
-            'treeherder': treeherder,
             'routes': job.get('routes', []),
+            'shipping-product': job.get('shipping-product'),
+            'shipping-phase': job.get('shipping-phase'),
         }
+        if treeherder:
+            task['treeherder'] = treeherder
 
         yield task
 
 
 def _generate_treeherder_platform(dep_th_platform, build_platform, build_type):
     if '-pgo' in build_platform:
         actual_build_type = 'pgo'
     elif '-ccov' in build_platform:
         actual_build_type = 'ccov'
     else:
         actual_build_type = build_type
     return '{}/{}'.format(dep_th_platform, actual_build_type)
 
 
-def _generate_treeherder_symbol(is_nightly):
-    return 'Ns' if is_nightly else 'Bs'
+def _generate_treeherder_symbol(is_nightly, kind):
+    if is_nightly:
+        return 'Ns'
+    else:
+        return 'Bs'
new file mode 100644
--- /dev/null
+++ b/taskcluster/taskgraph/transforms/strip_dependent_task.py
@@ -0,0 +1,19 @@
+# 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/.
+"""
+FIXME
+"""
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+from taskgraph.transforms.base import TransformSequence
+
+transforms = TransformSequence()
+
+
+@transforms.add
+def strip_dependent_task(config, jobs):
+    for job in jobs:
+        del job['dependent-task']
+        yield job
--- a/taskcluster/taskgraph/transforms/task.py
+++ b/taskcluster/taskgraph/transforms/task.py
@@ -371,16 +371,17 @@ task_description_schema = Schema({
         # the maximum time to run, in seconds
         Required('max-run-time'): int,
 
         # os user groups for test task workers
         Optional('os-groups'): [basestring],
 
         # optional features
         Required('chain-of-trust'): bool,
+        Optional('taskcluster-proxy'): bool,
     }, {
         Required('implementation'): 'buildbot-bridge',
 
         # see
         # https://github.com/mozilla/buildbot-bridge/blob/master/bbb/schemas/payload.yml
         Required('buildername'): basestring,
         Required('sourcestamp'): {
             'branch': basestring,
@@ -456,16 +457,18 @@ task_description_schema = Schema({
         Required('implementation'): 'beetmover',
 
         # the maximum time to run, in seconds
         Required('max-run-time', default=600): int,
 
         # locale key, if this is a locale beetmover job
         Optional('locale'): basestring,
 
+        Optional('partner-public'): bool,
+
         Required('release-properties'): {
             'app-name': basestring,
             'app-version': basestring,
             'branch': basestring,
             'build-id': basestring,
             'hash-type': basestring,
             'platform': basestring,
         },
@@ -944,16 +947,19 @@ def build_generic_worker_payload(config,
         worker['env']['SCCACHE_DISABLE'] = '1'
 
     # currently only support one feature (chain of trust) but this will likely grow
     features = {}
 
     if worker.get('chain-of-trust'):
         features['chainOfTrust'] = True
 
+    if worker.get('taskcluster-proxy'):
+        features['taskclusterProxy'] = True
+
     if features:
         task_def['payload']['features'] = features
 
     # coalesce / superseding
     if 'coalesce' in task:
         task_def['payload']['supersederUrl'] = superseder_url(config, task)
 
 
@@ -1004,16 +1010,18 @@ def build_beetmover_payload(config, task
             'hashType': release_properties['hash-type'],
             'platform': release_properties['platform'],
         },
         'upload_date': config.params['build_date'],
         'upstreamArtifacts':  worker['upstream-artifacts'],
     }
     if worker.get('locale'):
         task_def['payload']['locale'] = worker['locale']
+    if worker.get('partner-public'):
+        task_def['payload']['is_partner_repack_public'] = worker['partner-public']
     if release_config:
         task_def['payload'].update(release_config)
 
 
 @payload_builder('beetmover-cdns')
 def build_beetmover_cdns_payload(config, task, task_def):
     worker = task['worker']
     release_config = get_release_config(config)
new file mode 100644
--- /dev/null
+++ b/taskcluster/taskgraph/util/partners.py
@@ -0,0 +1,103 @@
+from __future__ import absolute_import, print_function, unicode_literals
+
+from copy import deepcopy
+import json
+import os
+
+LOCALES_FILE = os.path.join(
+    os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))),
+    'browser', 'locales', 'l10n-changesets.json'
+)
+
+
+def check_if_partners_enabled(config, tasks):
+    if (
+        config.params['release_enable_partners'] and
+        config.kind.startswith('release-partner-repack')
+    ) or (
+        config.params['release_enable_emefree'] and
+        config.kind.startswith('release-eme-free-repack')
+    ):
+        for task in tasks:
+            yield task
+
+
+def get_partner_config_by_kind(config, kind):
+    """ Retrieve partner data starting from the manifest url, which points to a repository
+    containing a default.xml that is intended to be drive the Google tool 'repo'. It
+    descends into each partner repo to lookup and parse the repack.cfg file(s).
+
+    Supports caching data by kind to avoid repeated requests, relying on the related kinds for
+    partner repacking, signing, repackage, repackage signing all having the same kind prefix.
+    """
+    partner_subset = config.params['release_partners']
+    partner_configs = config.params['release_partner_config'] or {}
+
+    # TODO eme-free should be a partner; we shouldn't care about per-kind
+    for k in partner_configs:
+        if kind.startswith(k):
+            kind_config = partner_configs[k]
+            break
+    else:
+        return {}
+    # if we're only interested in a subset of partners we remove the rest
+    if isinstance(partner_subset, (list, tuple)):
+        # TODO - should be fatal to have an unknown partner in partner_subset
+        for partner in kind_config.keys():
+            if partner not in partner_subset:
+                del(kind_config[partner])
+
+    return kind_config
+
+
+def _fix_subpartner_locales(orig_config, all_locales):
+    subpartner_config = deepcopy(orig_config)
+    # Get an ordered list of subpartner locales that is a subset of all_locales
+    subpartner_config['locales'] = sorted(list(
+        set(orig_config['locales']) & set(all_locales)
+    ))
+    return subpartner_config
+
+
+def fix_partner_config(orig_config):
+    pc = {}
+    with open(LOCALES_FILE, 'r') as fh:
+        all_locales = json.load(fh).keys()
+    # l10n-changesets.json doesn't include en-US, but the repack list does
+    if 'en-US' not in all_locales:
+        all_locales.append('en-US')
+    for kind, kind_config in orig_config.iteritems():
+        for partner, partner_config in kind_config.iteritems():
+            for subpartner, subpartner_config in partner_config.iteritems():
+                # get rid of empty subpartner configs
+                if not subpartner_config:
+                    continue
+                # Make sure our locale list is a subset of all_locales
+                pc.setdefault(kind, {}).setdefault(partner, {})[subpartner] = \
+                    _fix_subpartner_locales(subpartner_config, all_locales)
+    return pc
+
+
+# seems likely this exists elsewhere already
+def get_ftp_platform(platform):
+    if platform.startswith('win32'):
+        return 'win32'
+    elif platform.startswith('win64'):
+        return 'win64'
+    elif platform.startswith('macosx'):
+        return 'mac'
+    elif platform.startswith('linux-'):
+        return 'linux-i686'
+    elif platform.startswith('linux64'):
+        return 'linux-x86_64'
+    else:
+        raise ValueError('Unimplemented platform %s'.format(platform))
+
+
+# Ugh
+def locales_per_build_platform(build_platform, locales):
+    if build_platform.startswith('mac'):
+        exclude = ['ja']
+    else:
+        exclude = ['ja-JP-mac']
+    return [locale for locale in locales if locale not in exclude]
--- a/taskcluster/taskgraph/util/signed_artifacts.py
+++ b/taskcluster/taskgraph/util/signed_artifacts.py
@@ -4,16 +4,21 @@
 """
 Defines artifacts to sign before repackage.
 """
 
 from __future__ import absolute_import, print_function, unicode_literals
 from taskgraph.util.taskcluster import get_artifact_path
 
 
+def is_partner_kind(kind):
+    if kind and kind.startswith(('release-partner', 'release-eme-free')):
+        return True
+
+
 def generate_specifications_of_artifacts_to_sign(
     task, keep_locale_template=True, kind=None
 ):
     build_platform = task.attributes.get('build_platform')
     is_nightly = task.attributes.get('nightly')
     if kind == 'release-source-signing':
         artifacts_specifications = [{
             'artifacts': [
@@ -26,18 +31,22 @@ def generate_specifications_of_artifacts
             'artifacts': [
                 get_artifact_path(task, '{locale}/target.apk'),
             ],
             'formats': ['jar'],
         }]
     # XXX: Mars aren't signed here (on any platform) because internals will be
     # signed at after this stage of the release
     elif 'macosx' in build_platform:
+        if is_partner_kind(kind):
+            extension = 'tar.gz'
+        else:
+            extension = 'dmg'
         artifacts_specifications = [{
-            'artifacts': [get_artifact_path(task, '{locale}/target.dmg')],
+            'artifacts': [get_artifact_path(task, '{{locale}}/target.{}'.format(extension))],
             'formats': ['macapp', 'widevine'],
         }]
     elif 'win' in build_platform:
         artifacts_specifications = [{
             'artifacts': [
                 get_artifact_path(task, '{locale}/setup.exe'),
             ],
             'formats': ['sha2signcode'],
@@ -57,19 +66,33 @@ def generate_specifications_of_artifacts
             'formats': ['gpg', 'widevine'],
         }]
     else:
         raise Exception("Platform not implemented for signing")
 
     if not keep_locale_template:
         artifacts_specifications = _strip_locale_template(artifacts_specifications)
 
+    if is_partner_kind(kind):
+        artifacts_specifications = _strip_widevine_for_partners(artifacts_specifications)
+
     return artifacts_specifications
 
 
 def _strip_locale_template(artifacts_without_locales):
     for spec in artifacts_without_locales:
         for index, artifact in enumerate(spec['artifacts']):
             stripped_artifact = artifact.format(locale='')
             stripped_artifact = stripped_artifact.replace('//', '/')
             spec['artifacts'][index] = stripped_artifact
 
     return artifacts_without_locales
+
+
+def _strip_widevine_for_partners(artifacts_specifications):
+    """ Partner repacks should not resign that's previously signed for fear of breaking partial
+    updates
+    """
+    for spec in artifacts_specifications:
+        if 'widevine' in spec['formats']:
+            spec['formats'].remove('widevine')
+
+    return artifacts_specifications
--- a/taskcluster/taskgraph/util/taskcluster.py
+++ b/taskcluster/taskgraph/util/taskcluster.py
@@ -11,19 +11,22 @@ import functools
 import yaml
 import requests
 import logging
 from mozbuild.util import memoize
 from requests.packages.urllib3.util.retry import Retry
 from requests.adapters import HTTPAdapter
 from taskgraph.task import Task
 
-_TC_ARTIFACT_LOCATION = \
+_PUBLIC_TC_ARTIFACT_LOCATION = \
         'https://queue.taskcluster.net/v1/task/{task_id}/artifacts/{artifact_prefix}/{postfix}'
 
+_PRIVATE_TC_ARTIFACT_LOCATION = \
+        'http://taskcluster/queue/v1/task/{task_id}/artifacts/{artifact_prefix}/{postfix}'
+
 logger = logging.getLogger(__name__)
 
 # this is set to true for `mach taskgraph action-callback --test`
 testing = False
 
 
 @memoize
 def get_session():
@@ -172,19 +175,22 @@ def cancel_task(task_id, use_proxy=False
         logger.info('Would have cancelled {}.'.format(task_id))
     else:
         _do_request(get_task_url(task_id, use_proxy) + '/cancel', json={})
 
 
 def status_task(task_id, use_proxy=False):
     """Gets the status of a task given a task_id. In testing mode, just logs that it would
     have retrieved status."""
-    resp = _do_request(get_task_url(task_id, use_proxy) + '/status')
-    status = resp.json().get("status", {}).get('state') or 'unknown'
-    return status
+    if testing:
+        logger.info('Would have gotten status for {}.'.format(task_id))
+    else:
+        resp = _do_request(get_task_url(task_id, use_proxy) + '/status')
+        status = resp.json().get("status", {}).get('state') or 'unknown'
+        return status
 
 
 def rerun_task(task_id):
     """Reruns a task given a task_id. In testing mode, just logs that it would
     have reran."""
     if testing:
         logger.info('Would have rerun {}.'.format(task_id))
     else:
@@ -204,17 +210,21 @@ def purge_cache(provisioner_id, worker_t
     if testing:
         logger.info('Would have purged {}/{}/{}.'.format(provisioner_id, worker_type, cache_name))
     else:
         logger.info('Purging {}/{}/{}.'.format(provisioner_id, worker_type, cache_name))
         purge_cache_url = get_purge_cache_url(provisioner_id, worker_type, use_proxy)
         _do_request(purge_cache_url, json={'cacheName': cache_name})
 
 
-def get_taskcluster_artifact_prefix(task, task_id, postfix='', locale=None):
+def get_taskcluster_artifact_prefix(task, task_id, postfix='', locale=None, force_private=False):
     if locale:
         postfix = '{}/{}'.format(locale, postfix)
 
     artifact_prefix = get_artifact_prefix(task)
+    if artifact_prefix == 'public/build' and not force_private:
+        tmpl = _PUBLIC_TC_ARTIFACT_LOCATION
+    else:
+        tmpl = _PRIVATE_TC_ARTIFACT_LOCATION
 
-    return _TC_ARTIFACT_LOCATION.format(
+    return tmpl.format(
         task_id=task_id, postfix=postfix, artifact_prefix=artifact_prefix
     )
--- a/testing/config/tooltool-manifests/win32/ccov.manifest
+++ b/testing/config/tooltool-manifests/win32/ccov.manifest
@@ -1,9 +1,9 @@
 [
   {
-    "size": 532795,
-    "digest": "9da747c4bc15bae58dc9ca315f443a2f93c61d68fecdd4ff0369cfa0f933bfbc82bb8c9886afba760c83f2feb70c6c48e0118885f51184517ee9a8270c298188",
+    "size": 849392,
+    "digest": "cc85bdbb0c0e3563565c83ac731edbf3da1f9e70ab312149902611f23cb6fe42907d879f65662075a5ec356996a01fba022e513baa2f69f32cf01e34e26dfc0f",
     "algorithm": "sha512",
     "filename": "grcov-win-i686.tar.bz2",
     "unpack": false
   }
 ]
--- a/testing/moz.build
+++ b/testing/moz.build
@@ -20,20 +20,20 @@ with Files("*.mk"):
 with Files("README.txt"):
     BUG_COMPONENT = ("Testing", "General")
 
 with Files("talos/**"):
     BUG_COMPONENT = ("Testing", "Talos")
     SCHEDULES.exclusive = ['talos']
 
 with Files("talos/talos/tests/v8_7/**"):
-    BUG_COMPONENT = ("Core", "Javascript Engine")
+    BUG_COMPONENT = ("Core", "JavaScript Engine")
 
 with Files("talos/talos/tests/kraken/**"):
-    BUG_COMPONENT = ("Core", "Javascript Engine")
+    BUG_COMPONENT = ("Core", "JavaScript Engine")
 
 with Files("talos/talos/tests/a11y/**"):
     BUG_COMPONENT = ("Core", "Disability Access APIs")
 
 with Files("talos/talos/tests/webgl/**"):
     BUG_COMPONENT = ("Core", "Canvas: WebGL")
 
 with Files("talos/talos/tests/dromaeo/**"):
--- a/testing/mozharness/configs/awsy/taskcluster_windows_config.py
+++ b/testing/mozharness/configs/awsy/taskcluster_windows_config.py
@@ -6,17 +6,16 @@ external_tools_path = os.path.join(
     os.path.abspath(os.path.dirname(os.path.dirname(mozharness.__file__))),
     'external_tools',
 )
 
 config = {
     "virtualenv_path": 'venv',
     "exes": {
         'python': sys.executable,
-        'mozinstall': ['build/venv/scripts/python', 'build/venv/scripts/mozinstall-script.py'],
         'hg': os.path.join(os.environ['PROGRAMFILES'], 'Mercurial', 'hg')
     },
     "find_links": [
         "http://pypi.pub.build.mozilla.org/pub",
     ],
     "pip_index": False,
 
     "download_minidump_stackwalk": True,
--- a/testing/mozharness/configs/firefox_ui_tests/taskcluster_windows.py
+++ b/testing/mozharness/configs/firefox_ui_tests/taskcluster_windows.py
@@ -3,17 +3,16 @@
 import os
 import sys
 
 
 config = {
     "virtualenv_path": 'venv',
     "exes": {
         'python': sys.executable,
-        'mozinstall': ['build/venv/scripts/python', 'build/venv/scripts/mozinstall-script.py'],
         'hg': os.path.join(os.environ['PROGRAMFILES'], 'Mercurial', 'hg')
     },
 
     "find_links": [
         "http://pypi.pub.build.mozilla.org/pub",
     ],
     "pip_index": False,
 
--- a/testing/mozharness/configs/marionette/windows_config.py
+++ b/testing/mozharness/configs/marionette/windows_config.py
@@ -5,18 +5,16 @@ config = {
     # marionette options
     "marionette_address": "localhost:2828",
     "test_manifest": "unit-tests.ini",
 
     "virtualenv_path": 'venv',
     "exes": {
         'python': 'c:/mozilla-build/python27/python',
         'hg': 'c:/mozilla-build/hg/hg',
-        'mozinstall': ['%s/build/venv/scripts/python' % os.getcwd(),
-                       '%s/build/venv/scripts/mozinstall-script.py' % os.getcwd()],
     },
 
     "find_links": [
         "http://pypi.pvt.build.mozilla.org/pub",
         "http://pypi.pub.build.mozilla.org/pub",
     ],
     "pip_index": False,
 
--- a/testing/mozharness/configs/marionette/windows_taskcluster_config.py
+++ b/testing/mozharness/configs/marionette/windows_taskcluster_config.py
@@ -5,17 +5,16 @@ import sys
 config = {
     # marionette options
     "marionette_address": "localhost:2828",
     "test_manifest": "unit-tests.ini",
 
     "virtualenv_path": 'venv',
     "exes": {
         'python': sys.executable,
-        'mozinstall': ['build/venv/scripts/python', 'build/venv/scripts/mozinstall-script.py'],
         'hg': os.path.join(os.environ['PROGRAMFILES'], 'Mercurial', 'hg')
     },
 
     "find_links": [
         "http://pypi.pub.build.mozilla.org/pub",
     ],
     "pip_index": False,
 
--- a/testing/mozharness/configs/partner_repacks/release_mozilla-esr52_desktop.py
+++ b/testing/mozharness/configs/partner_repacks/release_mozilla-esr52_desktop.py
@@ -1,7 +1,6 @@
 config = {
     "appName": "Firefox",
     "log_name": "partner_repack",
     "repack_manifests_url": "git@github.com:mozilla-partners/mozilla-sha1-manifest",
     "repo_file": "https://raw.githubusercontent.com/mozilla/git-repo/master/repo",
-    "repo_url": "git@github.com:mozilla/git-repo.git",
 }
--- a/testing/mozharness/configs/partner_repacks/release_mozilla-release_desktop.py
+++ b/testing/mozharness/configs/partner_repacks/release_mozilla-release_desktop.py
@@ -1,7 +1,15 @@
 config = {
     "appName": "Firefox",
     "log_name": "partner_repack",
     "repack_manifests_url": "git@github.com:mozilla-partners/repack-manifests.git",
     "repo_file": "https://raw.githubusercontent.com/mozilla/git-repo/master/repo",
-    "repo_url": "git@github.com:mozilla/git-repo.git",
+    "secret_files": [
+        {
+            "filename": "/builds/partner-github-ssh",
+            "secret_name": "project/releng/gecko/build/level-%(scm-level)s/partner-github-ssh",
+            "min_scm_level": 3,
+            "mode": 0o600,
+        },
+    ],
+    "ssh_key": "/builds/partner-github-ssh",
 }
copy from testing/mozharness/configs/partner_repacks/release_mozilla-release_desktop.py
copy to testing/mozharness/configs/partner_repacks/release_mozilla-release_desktop_EME-free.py
--- a/testing/mozharness/configs/partner_repacks/release_mozilla-release_desktop.py
+++ b/testing/mozharness/configs/partner_repacks/release_mozilla-release_desktop_EME-free.py
@@ -1,7 +1,15 @@
 config = {
     "appName": "Firefox",
     "log_name": "partner_repack",
-    "repack_manifests_url": "git@github.com:mozilla-partners/repack-manifests.git",
+    "repack_manifests_url": "git@github.com:mozilla-partners/mozilla-EME-free-manifest.git",
     "repo_file": "https://raw.githubusercontent.com/mozilla/git-repo/master/repo",
-    "repo_url": "git@github.com:mozilla/git-repo.git",
+    "secret_files": [
+        {
+            "filename": "/builds/partner-github-ssh",
+            "secret_name": "project/releng/gecko/build/level-%(scm-level)s/partner-github-ssh",
+            "min_scm_level": 3,
+            "mode": 0o600,
+        },
+    ],
+    "ssh_key": "/builds/partner-github-ssh",
 }
--- a/testing/mozharness/configs/partner_repacks/staging_release_mozilla-release_desktop.py
+++ b/testing/mozharness/configs/partner_repacks/staging_release_mozilla-release_desktop.py
@@ -1,7 +1,15 @@
 config = {
     "appName": "Firefox",
     "log_name": "partner_repack",
     "repack_manifests_url": "git@github.com:mozilla-partners/repack-manifests.git",
     "repo_file": "https://raw.githubusercontent.com/mozilla/git-repo/master/repo",
-    "repo_url": "git@github.com:mozilla/git-repo.git",
+    "secret_files": [
+        {
+            "filename": "/builds/partner-github-ssh",
+            "secret_name": "project/releng/gecko/build/level-%(scm-level)s/partner-github-ssh",
+            "min_scm_level": 3,
+            "mode": 0o600,
+        },
+    ],
+    "ssh_key": "/builds/partner-github-ssh",
 }
new file mode 100644
--- /dev/null
+++ b/testing/mozharness/configs/repackage/osx_partner.py
@@ -0,0 +1,23 @@
+import os
+
+config = {
+    "input_home": "{abs_work_dir}/inputs",
+    "output_home": "{abs_work_dir}/artifacts{repack_id}",
+    "src_mozconfig": "browser/config/mozconfigs/macosx64/repack",
+
+    "repack_id": os.environ.get("REPACK_ID"),
+
+    "download_config": {
+        "target.tar.gz": os.environ.get("SIGNED_INPUT"),
+    },
+
+    "repackage_config": [[
+        "dmg",
+        "-i", "{abs_work_dir}/inputs/target.tar.gz",
+        "-o", "{output_home}/target.dmg"
+    ]],
+
+    # ToolTool
+    "tooltool_url": 'http://relengapi/tooltool/',
+    'tooltool_cache': os.environ.get('TOOLTOOL_CACHE'),
+}
new file mode 100644
--- /dev/null
+++ b/testing/mozharness/configs/repackage/win32_partner.py
@@ -0,0 +1,36 @@
+import os
+
+platform = "win32"
+
+download_config = {
+        "target.zip": os.environ.get("SIGNED_ZIP"),
+        "setup.exe": os.environ.get("SIGNED_SETUP"),
+    }
+
+repackage_config = [[
+        "installer",
+        "--package-name", "firefox",
+        "--package", "{abs_work_dir}\\inputs\\target.zip",
+        "--tag", "{abs_mozilla_dir}\\browser\\installer\\windows\\app.tag",
+        "--setupexe", "{abs_work_dir}\\inputs\\setup.exe",
+        "-o", "{output_home}\\target.installer.exe",
+        "--sfx-stub", "other-licenses/7zstub/firefox/7zSD.sfx",
+    ]]
+
+config = {
+    "input_home": "{abs_work_dir}\\inputs",
+    "output_home": "{base_work_dir}\\releng\\partner\\{repack_id}",
+
+    "repack_id": os.environ.get("REPACK_ID"),
+
+    "download_config": download_config,
+
+    "repackage_config": repackage_config,
+
+    # ToolTool
+    "tooltool_manifest_src": 'browser\\config\\tooltool-manifests\\{}\\releng.manifest'.format(platform),
+    'tooltool_url': 'https://api.pub.build.mozilla.org/tooltool/',
+    'tooltool_cache': os.environ.get('TOOLTOOL_CACHE'),
+
+    'run_configure': False,
+}
new file mode 100644
--- /dev/null
+++ b/testing/mozharness/configs/repackage/win64_partner.py
@@ -0,0 +1,36 @@
+import os
+
+platform = "win64"
+
+download_config = {
+        "target.zip": os.environ.get("SIGNED_ZIP"),
+        "setup.exe": os.environ.get("SIGNED_SETUP"),
+    }
+
+repackage_config = [[
+        "installer",
+        "--package-name", "firefox",
+        "--package", "{abs_work_dir}\\inputs\\target.zip",
+        "--tag", "{abs_mozilla_dir}\\browser\\installer\\windows\\app.tag",
+        "--setupexe", "{abs_work_dir}\\inputs\\setup.exe",
+        "-o", "{output_home}\\target.installer.exe",
+        "--sfx-stub", "other-licenses/7zstub/firefox/7zSD.sfx",
+    ]]
+
+config = {
+    "input_home": "{abs_work_dir}\\inputs",
+    "output_home": "{base_work_dir}\\releng\\partner\\{repack_id}",
+
+    "repack_id": os.environ.get("REPACK_ID"),
+
+    "download_config": download_config,
+
+    "repackage_config": repackage_config,
+
+    # ToolTool
+    "tooltool_manifest_src": 'browser\\config\\tooltool-manifests\\{}\\releng.manifest'.format(platform),
+    'tooltool_url': 'https://api.pub.build.mozilla.org/tooltool/',
+    'tooltool_cache': os.environ.get('TOOLTOOL_CACHE'),
+
+    'run_configure': False,
+}
--- a/testing/mozharness/configs/talos/windows_config.py
+++ b/testing/mozharness/configs/talos/windows_config.py
@@ -11,23 +11,18 @@ config = {
     "buildbot_json_path": "buildprops.json",
     "installer_path": "installer.exe",
     "virtualenv_path": VENV_PATH,
     "pip_index": False,
     "find_links": [
         "http://pypi.pvt.build.mozilla.org/pub",
         "http://pypi.pub.build.mozilla.org/pub",
     ],
-    "virtualenv_modules": ['pywin32', 'talos', 'mozinstall'],
     "exes": {
         'python': PYTHON,
-        'easy_install': ['%s/scripts/python' % VENV_PATH,
-                         '%s/scripts/easy_install-2.7-script.py' % VENV_PATH],
-        'mozinstall': ['%s/scripts/python' % VENV_PATH,
-                       '%s/scripts/mozinstall-script.py' % VENV_PATH],
         'hg': os.path.join(os.environ['PROGRAMFILES'], 'Mercurial', 'hg'),
         'tooltool.py': [PYTHON, os.path.join(os.environ['MOZILLABUILD'], 'tooltool.py')],
     },
     "title": socket.gethostname().split('.')[0],
     "default_actions": [
         "clobber",
         "read-buildbot-config",
         "download-and-extract",
--- a/testing/mozharness/configs/talos/windows_taskcluster_config.py
+++ b/testing/mozharness/configs/talos/windows_taskcluster_config.py
@@ -10,21 +10,18 @@ config = {
     "log_name": "talos",
     "buildbot_json_path": "buildprops.json",
     "installer_path": "installer.exe",
     "virtualenv_path": VENV_PATH,
     "pip_index": False,
     "find_links": [
         "http://pypi.pub.build.mozilla.org/pub",
     ],
-    "virtualenv_modules": ['pypiwin32', 'talos', 'mozinstall'],
     "exes": {
         'python': PYTHON,
-        'mozinstall': ['%s/scripts/python' % VENV_PATH,
-                       '%s/scripts/mozinstall-script.py' % VENV_PATH],
         'hg': os.path.join(os.environ['PROGRAMFILES'], 'Mercurial', 'hg'),
     },
     "title": socket.gethostname().split('.')[0],
     "default_actions": [
         "populate-webroot",
         "create-virtualenv",
         "install",
         "setup-mitmproxy",
--- a/testing/mozharness/configs/talos/windows_vm_config.py
+++ b/testing/mozharness/configs/talos/windows_vm_config.py
@@ -11,23 +11,18 @@ config = {
     "buildbot_json_path": "buildprops.json",
     "installer_path": "installer.exe",
     "virtualenv_path": VENV_PATH,
     "pip_index": False,
     "find_links": [
         "http://pypi.pvt.build.mozilla.org/pub",
         "http://pypi.pub.build.mozilla.org/pub",
     ],
-    "virtualenv_modules": ['pywin32', 'talos', 'mozinstall'],
     "exes": {
         'python': PYTHON,
-        'easy_install': ['%s/scripts/python' % VENV_PATH,
-                         '%s/scripts/easy_install-2.7-script.py' % VENV_PATH],
-        'mozinstall': ['%s/scripts/python' % VENV_PATH,
-                       '%s/scripts/mozinstall-script.py' % VENV_PATH],
         'hg': os.path.join(os.environ['PROGRAMFILES'], 'Mercurial', 'hg'),
     },
     "title": socket.gethostname().split('.')[0],
     "default_actions": [
         "clobber",
         "read-buildbot-config",
         "download-and-extract",
         "populate-webroot",
--- a/testing/mozharness/configs/unittests/win_taskcluster_unittest.py
+++ b/testing/mozharness/configs/unittests/win_taskcluster_unittest.py
@@ -19,17 +19,16 @@ DESKTOP_VISUALFX_THEME = {
 TASKBAR_AUTOHIDE_REG_PATH = {
     'Windows 7': 'HKCU:SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\StuckRects2',
     'Windows 10': 'HKCU:SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\StuckRects3'
 }.get('{} {}'.format(platform.system(), platform.release()))
 #####
 config = {
     "exes": {
         'python': sys.executable,
-        'mozinstall': ['build/venv/scripts/python', 'build/venv/scripts/mozinstall-script.py'],
         'hg': os.path.join(os.environ['PROGRAMFILES'], 'Mercurial', 'hg')
     },
     ###
     "installer_path": INSTALLER_PATH,
     "binary_path": BINARY_PATH,
     "xpcshell_name": XPCSHELL_NAME,
     "virtualenv_modules": ['pypiwin32'],
     "virtualenv_path": 'venv',
--- a/testing/mozharness/configs/unittests/win_unittest.py
+++ b/testing/mozharness/configs/unittests/win_unittest.py
@@ -10,18 +10,16 @@ EXE_SUFFIX = '.exe'
 DISABLE_SCREEN_SAVER = False
 ADJUST_MOUSE_AND_SCREEN = True
 #####
 config = {
     "buildbot_json_path": "buildprops.json",
     "exes": {
         'python': sys.executable,
         'hg': 'c:/mozilla-build/hg/hg',
-        'mozinstall': ['%s/build/venv/scripts/python' % os.getcwd(),
-                       '%s/build/venv/scripts/mozinstall-script.py' % os.getcwd()],
     },
     ###
     "installer_path": INSTALLER_PATH,
     "binary_path": BINARY_PATH,
     "xpcshell_name": XPCSHELL_NAME,
     "virtualenv_path": 'venv',
     "virtualenv_modules": ['pywin32'],
 
--- a/testing/mozharness/configs/web_platform_tests/prod_config_windows.py
+++ b/testing/mozharness/configs/web_platform_tests/prod_config_windows.py
@@ -18,18 +18,16 @@ config = {
         "--host-key-path=%(test_path)s/certs/web-platform.test.key",
         "--host-cert-path=%(test_path)s/certs/web-platform.test.pem",
         "--certutil-binary=%(test_install_path)s/bin/certutil",
     ],
 
     "exes": {
         'python': sys.executable,
         'hg': 'c:/mozilla-build/hg/hg',
-        'mozinstall': ['%s/build/venv/scripts/python' % os.getcwd(),
-                       '%s/build/venv/scripts/mozinstall-script.py' % os.getcwd()],
     },
 
     "find_links": [
         "http://pypi.pvt.build.mozilla.org/pub",
         "http://pypi.pub.build.mozilla.org/pub",
     ],
 
     "pip_index": False,
--- a/testing/mozharness/configs/web_platform_tests/prod_config_windows_taskcluster.py
+++ b/testing/mozharness/configs/web_platform_tests/prod_config_windows_taskcluster.py
@@ -17,17 +17,16 @@ config = {
         "--ca-cert-path=%(test_path)s/certs/cacert.pem",
         "--host-key-path=%(test_path)s/certs/web-platform.test.key",
         "--host-cert-path=%(test_path)s/certs/web-platform.test.pem",
         "--certutil-binary=%(test_install_path)s/bin/certutil",
     ],
 
     "exes": {
         'python': sys.executable,
-        'mozinstall': ['build/venv/scripts/python', 'build/venv/scripts/mozinstall-script.py'],
         'hg': os.path.join(os.environ['PROGRAMFILES'], 'Mercurial', 'hg')
     },
 
     "find_links": [
         "http://pypi.pub.build.mozilla.org/pub",
     ],
 
     "pip_index": False,
--- a/testing/mozharness/configs/web_platform_tests/test_config_windows.py
+++ b/testing/mozharness/configs/web_platform_tests/test_config_windows.py
@@ -1,33 +1,30 @@
 # ***** BEGIN LICENSE BLOCK *****
 # 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/.
 # ***** END LICENSE BLOCK *****
 
-import os
 import sys
 
 config = {
     "options": [
         "--prefs-root=%(test_path)s/prefs",
         "--processes=1",
         "--config=%(test_path)s/wptrunner.ini",
         "--ca-cert-path=%(test_path)s/certs/cacert.pem",
         "--host-key-path=%(test_path)s/certs/web-platform.test.key",
         "--host-cert-path=%(test_path)s/certs/web-platform.test.pem",
         "--certutil-binary=%(test_install_path)s/bin/certutil",
     ],
 
     "exes": {
         'python': sys.executable,
         'hg': 'c:/mozilla-build/hg/hg',
-        'mozinstall': ['%s/build/venv/scripts/python' % os.getcwd(),
-                       '%s/build/venv/scripts/mozinstall-script.py' % os.getcwd()],
     },
 
     "default_actions": [
         'clobber',
         'download-and-extract',
         'create-virtualenv',
         'pull',
         'install',
--- a/testing/mozharness/mozharness/base/python.py
+++ b/testing/mozharness/mozharness/base/python.py
@@ -237,20 +237,17 @@ class VirtualenvMixin(object):
         elif install_method == 'easy_install':
             if not module:
                 self.fatal("module parameter required with install_method='easy_install'")
             if requirements:
                 # Install pip requirements files separately, since they're
                 # not understood by easy_install.
                 self.install_module(requirements=requirements,
                                     install_method='pip')
-            # Allow easy_install to be overridden by
-            # self.config['exes']['easy_install']
-            default = 'easy_install'
-            command = self.query_exe('easy_install', default=default, return_type="list")
+            command = [self.query_python_path(), '-m', 'easy_install']
         else:
             self.fatal("install_module() doesn't understand an install_method of %s!" % install_method)
 
         trusted_hosts = set()
         for link in c.get('find_links', []):
             parsed = urlparse.urlparse(link)
 
             try:
--- a/testing/mozharness/mozharness/mozilla/secrets.py
+++ b/testing/mozharness/mozharness/mozilla/secrets.py
@@ -40,16 +40,18 @@ class SecretsMixin(object):
         secret is used as the value to be written to disk.
 
         The `filename` key in the dictionary gives the filename to which the
         secret should be written.
 
         The optional `min_scm_level` key gives a minimum SCM level at which this
         secret is required.  For lower levels, the value of the 'default` key
         is used, or no secret is written.
+
+        The optional 'mode' key allows a mode change (chmod) after the file is written
         """
         secret_files = self.config.get('secret_files', [])
 
         scm_level = self.config.get('scm_level', 1)
         subst = {
             'scm-level': scm_level,
         }
 
@@ -63,8 +65,11 @@ class SecretsMixin(object):
                     secret = sf['default']
                 else:
                     self.info("No default for secret; not writing " + filename)
                     continue
             else:
                 secret = self._fetch_secret(secret_name)
 
             open(filename, "w").write(secret)
+
+            if sf.get('mode'):
+                os.chmod(filename, sf['mode'])
--- a/testing/mozharness/mozharness/mozilla/testing/codecoverage.py
+++ b/testing/mozharness/mozharness/mozilla/testing/codecoverage.py
@@ -186,17 +186,18 @@ class CodeCoverageMixin(object):
             # Download the gcno from the build machine.
             self.download_file(self.url_to_gcno, parent_dir=self.grcov_dir)
 
             # Run grcov on the zipped .gcno and .gcda files.
             grcov_command = [
                 os.path.join(self.grcov_dir, 'grcov'),
                 '-t', 'lcov',
                 '-p', self.prefix,
-                '--ignore-dir', 'gcc',
+                '--ignore-dir', 'gcc*',
+                '--ignore-dir', 'vs2017_*',
                 os.path.join(self.grcov_dir, 'target.code-coverage-gcno.zip'), file_path_gcda
             ]
 
             if mozinfo.os == 'win':
                 grcov_command += ['--llvm']
 
             # 'grcov_output' will be a tuple, the first variable is the path to the lcov output,
             # the other is the path to the standard error output.
--- a/testing/mozharness/mozharness/mozilla/testing/talos.py
+++ b/testing/mozharness/mozharness/mozilla/testing/talos.py
@@ -619,20 +619,17 @@ class Talos(TestingMixin, MercurialScrip
                 'config',
                 'mozbase_source_requirements.txt'
             )
         self.register_virtualenv_module(
             requirements=[mozbase_requirements],
             two_pass=True,
             editable=True,
         )
-        # require pip >= 1.5 so pip will prefer .whl files to install
-        super(Talos, self).create_virtualenv(
-            modules=['pip>=1.5']
-        )
+        super(Talos, self).create_virtualenv()
         # talos in harness requires what else is
         # listed in talos requirements.txt file.
         self.install_module(
             requirements=[os.path.join(self.talos_path,
                                        'requirements.txt')]
         )
 
     def _validate_treeherder_data(self, parser):
--- a/testing/mozharness/mozharness/mozilla/testing/testbase.py
+++ b/testing/mozharness/mozharness/mozilla/testing/testbase.py
@@ -585,17 +585,17 @@ 2. running the download-and-extract acti
 """)
         if not self.is_python_package_installed("mozInstall"):
             self.fatal("""Can't call install() without mozinstall!
 Did you run with --create-virtualenv? Is mozinstall in virtualenv_modules?""")
 
     def install_app(self, app=None, target_dir=None, installer_path=None):
         """ Dependent on mozinstall """
         # install the application
-        cmd = self.query_exe("mozinstall", default=self.query_python_path("mozinstall"), return_type="list")
+        cmd = [self.query_python_path("mozinstall")]
         if app:
             cmd.extend(['--app', app])
         # Remove the below when we no longer need to support mozinstall 0.3
         self.info("Detecting whether we're running mozinstall >=1.0...")
         output = self.get_output_from_command(cmd + ['-h'])
         if '--source' in output:
             cmd.append('--source')
         # End remove
--- a/testing/mozharness/scripts/desktop_partner_repacks.py
+++ b/testing/mozharness/scripts/desktop_partner_repacks.py
@@ -13,28 +13,28 @@ import sys
 
 # load modules from parent dir
 sys.path.insert(1, os.path.dirname(sys.path[0]))
 
 from mozharness.base.script import BaseScript
 from mozharness.mozilla.buildbot import BuildbotMixin
 from mozharness.mozilla.purge import PurgeMixin
 from mozharness.mozilla.release import ReleaseMixin
+from mozharness.mozilla.secrets import SecretsMixin
 from mozharness.base.python import VirtualenvMixin
 from mozharness.base.log import FATAL
 
 
 # DesktopPartnerRepacks {{{1
 class DesktopPartnerRepacks(ReleaseMixin, BuildbotMixin, PurgeMixin,
-                            BaseScript, VirtualenvMixin):
+                            BaseScript, VirtualenvMixin, SecretsMixin):
     """Manages desktop partner repacks"""
     actions = [
                 "clobber",
-                "create-virtualenv",
-                "activate-virtualenv",
+                "get-secrets",
                 "setup",
                 "repack",
                 "summary",
               ]
     config_options = [
         [["--version", "-v"], {
           "dest": "version",
           "help": "Version of Firefox to repack",
@@ -46,89 +46,61 @@ class DesktopPartnerRepacks(ReleaseMixin
         [["--platform"], {
           "dest": "platform",
           "help": "Platform to repack (e.g. linux64, macosx64, ...)",
           }],
         [["--partner", "-p"], {
           "dest": "partner",
           "help": "Limit repackaging to partners matching this string",
           }],
-        [["--s3cfg"], {
-          "dest": "s3cfg",
-          "help": "Configuration file for uploading to S3 using s3cfg",
-          }],
-        [["--hgroot"], {
-          "dest": "hgroot",
-          "help": "Use a different hg server for retrieving files",
-          }],
-        [["--hgrepo"], {
-          "dest": "hgrepo",
-          "help": "Use a different base repo for retrieving files",
-          }],
-        [["--require-buildprops"], {
-            "action": "store_true",
-            "dest": "require_buildprops",
-            "default": False,
-            "help": "Read in config options (like partner) from the buildbot properties file."
-          }],
+        [["--taskid", "-t"], {
+            "dest": "taskIds",
+            "action": "extend",
+            "help": "taskId(s) of upstream tasks for vanilla Firefox artifacts",
+        }],
     ]
 
     def __init__(self):
         # fxbuild style:
         buildscript_kwargs = {
             'all_actions': DesktopPartnerRepacks.actions,
             'default_actions': DesktopPartnerRepacks.actions,
             'config': {
-                'buildbot_json_path': 'buildprops.json',
                 "log_name": "partner-repacks",
                 "hashType": "sha512",
-                'virtualenv_modules': [
-                    'requests==2.2.1',
-                    'PyHawk-with-a-single-extra-commit==0.1.5',
-                    'taskcluster==0.0.15',
-                    's3cmd==1.6.0',
-                ],
-                'virtualenv_path': 'venv',
                 'workdir': 'partner-repacks',
             },
         }
         #
 
         BaseScript.__init__(
             self,
             config_options=self.config_options,
             **buildscript_kwargs
         )
 
     def _pre_config_lock(self, rw_config):
-        self.read_buildbot_config()
-        if not self.buildbot_config:
-            self.warning("Skipping buildbot properties overrides")
-        else:
-            if self.config.get('require_buildprops', False) is True:
-                if not self.buildbot_config:
-                    self.fatal("Unable to load properties from file: %s" %
-                               self.config.get('buildbot_json_path'))
-            props = self.buildbot_config["properties"]
-            for prop in ['version', 'build_number', 'revision', 'repo_file',
-                         'repo_url', 'repack_manifests_url', 'partner']:
-                if props.get(prop):
-                    self.info("Overriding %s with %s" % (prop, props[prop]))
-                    self.config[prop] = props.get(prop)
+        if os.getenv('REPACK_MANIFESTS_URL'):
+            self.info('Overriding repack_manifests_url to %s' % os.getenv('REPACK_MANIFESTS_URL'))
+            self.config['repack_manifests_url'] = os.getenv('REPACK_MANIFESTS_URL')
+        if os.getenv('UPSTREAM_TASKIDS'):
+            self.info('Overriding taskIds with %s' % os.getenv('UPSTREAM_TASKIDS'))
+            self.config['taskIds'] = os.getenv('UPSTREAM_TASKIDS').split()
+        self.config['scm_level'] = os.environ.get('MOZ_SCM_LEVEL', '1')
 
         if 'version' not in self.config:
             self.fatal("Version (-v) not supplied.")
         if 'build_number' not in self.config:
             self.fatal("Build number (-n) not supplied.")
         if 'repo_file' not in self.config:
             self.fatal("repo_file not supplied.")
-        if 'repo_url' not in self.config:
-            self.fatal("repo_url not supplied.")
         if 'repack_manifests_url' not in self.config:
-            self.fatal("repack_manifests_url not supplied.")
+            self.fatal("repack_manifests_url not supplied in config or via REPACK_MANIFESTS_URL")
+        if 'taskIds' not in self.config:
+            self.fatal('Need upstream taskIds from command line or in UPSTREAM_TASKIDS')
 
     def query_abs_dirs(self):
         if self.abs_dirs:
             return self.abs_dirs
         abs_dirs = super(DesktopPartnerRepacks, self).query_abs_dirs()
         for directory in abs_dirs:
             value = abs_dirs[directory]
             abs_dirs[directory] = value
@@ -144,58 +116,57 @@ class DesktopPartnerRepacks(ReleaseMixin
 
     # Actions {{{
     def _repo_cleanup(self):
         self.rmtree(self.query_abs_dirs()['abs_repo_dir'])
         self.rmtree(self.query_abs_dirs()['abs_partners_dir'])
         self.rmtree(self.query_abs_dirs()['abs_scripts_dir'])
 
     def _repo_init(self, repo):
+        partial_env = {
+            'GIT_SSH_COMMAND': 'ssh -oIdentityFile={}'.format(self.config['ssh_key'])
+        }
         status = self.run_command([repo, "init", "--no-repo-verify",
-                                   "--repo-url", self.config['repo_url'],
                                    "-u", self.config['repack_manifests_url']],
-                                  cwd=self.query_abs_dirs()['abs_work_dir'])
+                                  cwd=self.query_abs_dirs()['abs_work_dir'],
+                                  partial_env=partial_env)
         if status:
             return status
-        return self.run_command([repo, "sync"],
-                                cwd=self.query_abs_dirs()['abs_work_dir'])
+        return self.run_command([repo, "sync", "--current-branch", "--no-tags"],
+                                cwd=self.query_abs_dirs()['abs_work_dir'],
+                                partial_env=partial_env)
 
     def setup(self):
         """setup step"""
         repo = self.download_file(self.config['repo_file'],
                                   file_name='repo',
                                   parent_dir=self.query_abs_dirs()['abs_work_dir'],
                                   error_level=FATAL)
         if not os.path.exists(repo):
             self.fatal("Unable to download repo tool.")
-        self.chmod(repo, 0755)
+        self.chmod(repo, 0o755)
         self.retry(self._repo_init,
                    args=(repo,),
                    error_level=FATAL,
                    cleanup=self._repo_cleanup(),
                    good_statuses=[0],
                    sleeptime=5)
 
     def repack(self):
         """creates the repacks"""
-        repack_cmd = [sys.executable, "partner-repacks.py",
+        repack_cmd = [sys.executable, "tc-partner-repacks.py",
                       "-v", self.config['version'],
                       "-n", str(self.config['build_number'])]
         if self.config.get('platform'):
             repack_cmd.extend(["--platform", self.config['platform']])
         if self.config.get('partner'):
             repack_cmd.extend(["--partner", self.config['partner']])
-        if self.config.get('s3cfg'):
-            repack_cmd.extend(["--s3cfg", self.config['s3cfg']])
-        if self.config.get('hgroot'):
-            repack_cmd.extend(["--hgroot", self.config['hgroot']])
-        if self.config.get('hgrepo'):
-            repack_cmd.extend(["--repo", self.config['hgrepo']])
-        if self.config.get('revision'):
-            repack_cmd.extend(["--tag", self.config["revision"]])
+        if self.config.get('taskIds'):
+            for taskId in self.config['taskIds']:
+                repack_cmd.extend(["--taskid", taskId])
 
         return self.run_command(repack_cmd,
                                 cwd=self.query_abs_dirs()['abs_scripts_dir'])
 
 
 # main {{{
 if __name__ == '__main__':
     partner_repacks = DesktopPartnerRepacks()
--- a/testing/mozharness/scripts/desktop_unittest.py
+++ b/testing/mozharness/scripts/desktop_unittest.py
@@ -314,18 +314,17 @@ class DesktopUnittest(TestingMixin, Merc
         else:
             self.abs_res_dir = abs_app_dir
         return self.abs_res_dir
 
     @PreScriptAction('create-virtualenv')
     def _pre_create_virtualenv(self, action):
         dirs = self.query_abs_dirs()
 
-        self.register_virtualenv_module(name='pip>=1.5')
-        self.register_virtualenv_module('psutil==5.4.3', method='pip')
+        self.register_virtualenv_module('psutil==5.4.3')
         self.register_virtualenv_module(name='mock')
         self.register_virtualenv_module(name='simplejson')
 
         requirements_files = [os.path.join(dirs['abs_test_install_dir'],
                               'config', 'marionette_requirements.txt')]
 
         if self._query_specified_suites('mochitest') is not None:
             # mochitest is the only thing that needs this
--- a/testing/mozharness/scripts/release/antivirus.py
+++ b/testing/mozharness/scripts/release/antivirus.py
@@ -74,17 +74,16 @@ class AntivirusScan(BaseScript, Virtuale
     CACHE_DIR = 'cache'
 
     def __init__(self):
         BaseScript.__init__(self,
                             config_options=self.config_options,
                             require_config_file=False,
                             config={
                                 "virtualenv_modules": [
-                                    "pip==1.5.5",
                                     "boto",
                                     "redo",
                                     "mar",
                                 ],
                                 "virtualenv_path": "venv",
                             },
                             all_actions=[
                                 "create-virtualenv",
--- a/testing/mozharness/scripts/release/generate-checksums.py
+++ b/testing/mozharness/scripts/release/generate-checksums.py
@@ -52,17 +52,16 @@ class ChecksumsGenerator(BaseScript, Vir
     ] + virtualenv_config_options
 
     def __init__(self):
         BaseScript.__init__(self,
                             config_options=self.config_options,
                             require_config_file=False,
                             config={
                                 "virtualenv_modules": [
-                                    "pip==1.5.5",
                                     "boto",
                                 ],
                                 "virtualenv_path": "venv",
                             },
                             all_actions=[
                                 "create-virtualenv",
                                 "collect-individual-checksums",
                                 "create-big-checksums",
--- a/testing/mozharness/scripts/release/push-candidate-to-releases.py
+++ b/testing/mozharness/scripts/release/push-candidate-to-releases.py
@@ -64,17 +64,16 @@ class ReleasePusher(BaseScript, Virtuale
     ] + virtualenv_config_options
 
     def __init__(self, aws_creds):
         BaseScript.__init__(self,
                             config_options=self.config_options,
                             require_config_file=False,
                             config={
                                     "virtualenv_modules": [
-                                        "pip==1.5.5",
                                         "boto",
                                         "redo",
                                     ],
                                     "virtualenv_path": "venv",
                                    },
                             all_actions=[
                                 "create-virtualenv",
                                 "activate-virtualenv",
--- a/testing/mozharness/scripts/repackage.py
+++ b/testing/mozharness/scripts/repackage.py
@@ -56,17 +56,24 @@ class Repackage(BaseScript):
             abs_dirs[directory] = value
 
         dirs = {}
         dirs['abs_tools_dir'] = os.path.join(abs_dirs['abs_work_dir'], 'tools')
         dirs['abs_mozilla_dir'] = os.path.join(abs_dirs['abs_work_dir'], 'src')
         locale_dir = ''
         if config.get('locale'):
             locale_dir = "{}{}".format(os.path.sep, config['locale'])
-        dirs['output_home'] = config['output_home'].format(locale=locale_dir, **abs_dirs)
+        repack_id_dir = ''
+        if config.get('repack_id'):
+            repack_id_dir = "{}{}".format(os.path.sep, config['repack_id'])
+        dirs['output_home'] = config['output_home'].format(
+            locale=locale_dir,
+            repack_id=repack_id_dir,
+            **abs_dirs
+        )
         for key in dirs.keys():
             if key not in abs_dirs:
                 abs_dirs[key] = dirs[key]
         self.abs_dirs = abs_dirs
         return self.abs_dirs
 
     def repackage(self):
         config = self.config
--- a/testing/web-platform/meta/css/css-fonts/variations/font-parse-numeric-stretch-style-weight.html.ini
+++ b/testing/web-platform/meta/css/css-fonts/variations/font-parse-numeric-stretch-style-weight.html.ini
@@ -1,13 +1,10 @@
 [font-parse-numeric-stretch-style-weight.html]
-  [Valid value 850 for font property weight used for styling.]
-    expected: FAIL
-
-  [Valid value 850.3 for font property weight used for styling.]
+  [Value 500 400 must not be accepted as weight in @font-face.]
     expected: FAIL
 
   [Valid value 51% for font property stretch used for styling.]
     expected: FAIL
 
   [Valid value 199% for font property stretch used for styling.]
     expected: FAIL
 
@@ -18,31 +15,16 @@
     expected: FAIL
 
   [Valid value 110% matches 110% for stretch in @font-face.]
     expected: FAIL
 
   [Valid value 111.5% matches 111.5% for stretch in @font-face.]
     expected: FAIL
 
-  [Valid value calc(100 + 300) for font property weight used for styling.]
-    expected: FAIL
-
-  [Valid value calc(0.2 + 205.5) for font property weight used for styling.]
-    expected: FAIL
-
-  [Valid value 100 400 matches 100 400 for weight in @font-face.]
-    expected: FAIL
-
-  [Valid value 100 101.5 matches 100 101.5 for weight in @font-face.]
-    expected: FAIL
-
-  [Valid value 999.8 999.9 matches 999.8 999.9 for weight in @font-face.]
-    expected: FAIL
-
   [Valid value 50% 200% matches 50% 200% for stretch in @font-face.]
     expected: FAIL
 
   [Valid value 0.1% 1% matches 0.1% 1% for stretch in @font-face.]
     expected: FAIL
 
   [Valid value 900% 901% matches 900% 901% for stretch in @font-face.]
     expected: FAIL
--- a/testing/web-platform/meta/css/css-fonts/variations/font-shorthand.html.ini
+++ b/testing/web-platform/meta/css/css-fonts/variations/font-shorthand.html.ini
@@ -1,51 +1,23 @@
 [font-shorthand.html]
-  [Font shorthand: Font size specified as calc()]
-    expected:
-      if debug and not stylo and e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86_64") and (bits == 64): FAIL
-      if not debug and not stylo and e10s and (os == "linux") and (version == "Ubuntu 16.04") and (processor == "x86_64") and (bits == 64): FAIL
-
-  [Font shorthand: Font weight specified as number]
-    expected: FAIL
-
-  [Font shorthand: Font weight specified as calc()]
-    expected: FAIL
-
-  [Font shorthand: Font weight specified as calc(), value smaller than 1]
-    expected: FAIL
-
-  [Font shorthand: Font weight specified as calc(), value greater than 1000]
-    expected: FAIL
 
   [Font shorthand: 'oblique' with positive angle]
     expected: FAIL
 
   [Font shorthand: 'oblique' with hegative angle]
     expected: FAIL
 
-  [Font shorthand: 'oblique' followed by valid small weight]
+  [Font shorthand: 'oblique' with angle followed by valid calc() weight]
     expected: FAIL
 
   [Font shorthand: 'oblique' with positive angle followed by valid weight]
     expected: FAIL
 
   [Font shorthand: 'oblique' with negative angle followed by valid weight]
     expected: FAIL
 
-  [Font shorthand: 'oblique' followed by valid calc() weight]
-    expected: FAIL
-
-  [Font shorthand: 'oblique' with angle followed by valid calc() weight]
-    expected: FAIL
-
-  [Font shorthand: 'oblique' followed by a to-be-clamped calc() weight]
-    expected: FAIL
-
-  [Font shorthand: calc() weight folowed by 'oblique']
-    expected: FAIL
-
   [Font shorthand: calc() weight folowed by 'oblique' and slant angle]
     expected: FAIL
 
   [Font shorthand: To-be-clamped calc() weight folowed by 'oblique' and slant angle]
     expected: FAIL
 
deleted file mode 100644
--- a/testing/web-platform/meta/css/css-fonts/variations/font-weight-interpolation.html.ini
+++ /dev/null
@@ -1,8 +0,0 @@
-[font-weight-interpolation.html]
-  expected: TIMEOUT
-  [font-weight animation]
-    expected: TIMEOUT
-
-  [font-weight transition]
-    expected: TIMEOUT
-
deleted file mode 100644
--- a/testing/web-platform/meta/css/css-fonts/variations/font-weight-lighter-bolder.html.ini
+++ /dev/null
@@ -1,40 +0,0 @@
-[font-weight-lighter-bolder.html]
-  [Test lighter font-weight for base weight 99]
-    expected: FAIL
-
-  [Test bolder font-weight for base weight 99]
-    expected: FAIL
-
-  [Test bolder font-weight for base weight 349]
-    expected: FAIL
-
-  [Test lighter font-weight for base weight 550]
-    expected: FAIL
-
-  [Test bolder font-weight for base weight 550]
-    expected: FAIL
-
-  [Test lighter font-weight for base weight 749]
-    expected: FAIL
-
-  [Test bolder font-weight for base weight 749]
-    expected: FAIL
-
-  [Test lighter font-weight for base weight 750]
-    expected: FAIL
-
-  [Test bolder font-weight for base weight 750]
-    expected: FAIL
-
-  [Test lighter font-weight for base weight 899]
-    expected: FAIL
-
-  [Test bolder font-weight for base weight 899]
-    expected: FAIL
-
-  [Test lighter font-weight for base weight 901]
-    expected: FAIL
-
-  [Test bolder font-weight for base weight 901]
-    expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/css/css-fonts/variations/font-weight-parsing.html.ini
+++ /dev/null
@@ -1,55 +0,0 @@
-[font-weight-parsing.html]
-  [@supports: Values that are not multiple of 100 should be parsed successfully]
-    expected: FAIL
-
-  [@supports: Non-integer Values should be parsed successfully]
-    expected: FAIL
-
-  [@supports: Minimum allowed value should be parsed successfully]
-    expected: FAIL
-
-  [@supports: Maximum allowed value should be parsed successfully]
-    expected: FAIL
-
-  [@supports: Simple calc value]
-    expected: FAIL
-
-  [@supports: Negative simple calc value (to be clamped)]
-    expected: FAIL
-
-  [@supports: Out-of-range simple calc value (to be clamped)]
-    expected: FAIL
-
-  [@supports: Valid calc expression]
-    expected: FAIL
-
-  [@supports: Valid calc expression with out-of-range value (to be clamped)]
-    expected: FAIL
-
-  [Computed style: Values that are not multiple of 100 should be parsed successfully]
-    expected: FAIL
-
-  [Computed style: Non-integer Values should be parsed successfully]
-    expected: FAIL
-
-  [Computed style: Minimum allowed value should be parsed successfully]
-    expected: FAIL
-
-  [Computed style: Maximum allowed value should be parsed successfully]
-    expected: FAIL
-
-  [Computed style: Simple calc value]
-    expected: FAIL
-
-  [Computed style: Negative simple calc value (to be clamped)]
-    expected: FAIL
-
-  [Computed style: Out-of-range simple calc value (to be clamped)]
-    expected: FAIL
-
-  [Computed style: Valid calc expression]
-    expected: FAIL
-
-  [Computed style: Valid calc expression with out-of-range value (to be clamped)]