Merge m-c to autoland, a=merge CLOSED TREE
authorWes Kocher <wkocher@mozilla.com>
Wed, 25 Jan 2017 17:18:56 -0800
changeset 466723 7a4dfae45c3a93edf5bfd9f787b567bcc776081f
parent 466605 5c5b0537712323cf1d9610a6fe1b127c65902182 (current diff)
parent 466497 52a34f9a6cf112377299ab32132384e2dc1f543b (diff)
child 466724 5c610ccf76de4c8faba7ac7bfafd26120597b12e
push id42980
push userbmo:jhofmann@mozilla.com
push dateThu, 26 Jan 2017 12:29:21 +0000
reviewersmerge
milestone54.0a1
Merge m-c to autoland, a=merge CLOSED TREE MozReview-Commit-ID: 86aFtkvKHHj
gfx/harfbuzz/src/hb-ot-layout-math-table.hh
taskcluster/ci/test/tests.yml
taskcluster/taskgraph/transforms/task.py
taskcluster/taskgraph/transforms/tests.py
--- a/accessible/ipc/win/PlatformChild.cpp
+++ b/accessible/ipc/win/PlatformChild.cpp
@@ -45,16 +45,23 @@ static const mozilla::mscom::ArrayData s
 PlatformChild::PlatformChild()
   : mAccTypelib(mozilla::mscom::RegisterTypelib(L"oleacc.dll",
         mozilla::mscom::RegistrationFlags::eUseSystemDirectory))
   , mMiscTypelib(mozilla::mscom::RegisterTypelib(L"Accessible.tlb"))
 {
   mozilla::mscom::InterceptorLog::Init();
   mozilla::mscom::RegisterArrayData(sPlatformChildArrayData);
 
+
+  UniquePtr<mozilla::mscom::RegisteredProxy> customProxy;
+  mozilla::mscom::EnsureMTA([&customProxy]() -> void {
+    customProxy = Move(mozilla::mscom::RegisterProxy());
+  });
+  mCustomProxy = Move(customProxy);
+
   UniquePtr<mozilla::mscom::RegisteredProxy> ia2Proxy;
   mozilla::mscom::EnsureMTA([&ia2Proxy]() -> void {
     ia2Proxy = Move(mozilla::mscom::RegisterProxy(L"ia2marshal.dll"));
   });
   mIA2Proxy = Move(ia2Proxy);
 }
 
 } // namespace a11y
--- a/accessible/ipc/win/PlatformChild.h
+++ b/accessible/ipc/win/PlatformChild.h
@@ -18,16 +18,17 @@ public:
   PlatformChild();
 
   PlatformChild(PlatformChild&) = delete;
   PlatformChild(PlatformChild&&) = delete;
   PlatformChild& operator=(PlatformChild&) = delete;
   PlatformChild& operator=(PlatformChild&&) = delete;
 
 private:
+  UniquePtr<mozilla::mscom::RegisteredProxy> mCustomProxy;
   UniquePtr<mozilla::mscom::RegisteredProxy> mIA2Proxy;
   UniquePtr<mozilla::mscom::RegisteredProxy> mAccTypelib;
   UniquePtr<mozilla::mscom::RegisteredProxy> mMiscTypelib;
 };
 
 } // namespace mozilla
 } // namespace a11y
 
--- a/accessible/windows/msaa/Platform.cpp
+++ b/accessible/windows/msaa/Platform.cpp
@@ -17,29 +17,33 @@
 #include "mozilla/mscom/Registration.h"
 #include "mozilla/StaticPtr.h"
 #include "ProxyWrappers.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 using namespace mozilla::mscom;
 
+static StaticAutoPtr<RegisteredProxy> gRegCustomProxy;
 static StaticAutoPtr<RegisteredProxy> gRegProxy;
 static StaticAutoPtr<RegisteredProxy> gRegAccTlb;
 static StaticAutoPtr<RegisteredProxy> gRegMiscTlb;
 
 void
 a11y::PlatformInit()
 {
   Compatibility::Init();
 
   nsWinUtils::MaybeStartWindowEmulation();
   ia2AccessibleText::InitTextChangeData();
   if (BrowserTabsRemoteAutostart()) {
     mscom::InterceptorLog::Init();
+    UniquePtr<RegisteredProxy> regCustomProxy(
+        mscom::RegisterProxy());
+    gRegCustomProxy = regCustomProxy.release();
     UniquePtr<RegisteredProxy> regProxy(
         mscom::RegisterProxy(L"ia2marshal.dll"));
     gRegProxy = regProxy.release();
     UniquePtr<RegisteredProxy> regAccTlb(
         mscom::RegisterTypelib(L"oleacc.dll",
                                RegistrationFlags::eUseSystemDirectory));
     gRegAccTlb = regAccTlb.release();
     UniquePtr<RegisteredProxy> regMiscTlb(
@@ -49,16 +53,17 @@ a11y::PlatformInit()
 }
 
 void
 a11y::PlatformShutdown()
 {
   ::DestroyCaret();
 
   nsWinUtils::ShutdownWindowEmulation();
+  gRegCustomProxy = nullptr;
   gRegProxy = nullptr;
   gRegAccTlb = nullptr;
   gRegMiscTlb = nullptr;
 }
 
 void
 a11y::ProxyCreated(ProxyAccessible* aProxy, uint32_t aInterfaces)
 {
--- a/addon-sdk/source/test/jetpack-package.ini
+++ b/addon-sdk/source/test/jetpack-package.ini
@@ -45,16 +45,17 @@ generated-files =
 subsuite = clipboard
 [test-collection.js]
 [test-commonjs-test-adapter.js]
 [test-content-events.js]
 [test-content-script.js]
 [test-content-sync-worker.js]
 [test-content-worker.js]
 [test-context-menu.js]
+skip-if = asan # Bug 1333359
 [test-context-menu@2.js]
 [test-cuddlefish.js]
 # Cuddlefish loader is unsupported
 skip-if = true
 [test-deprecate.js]
 [test-dev-panel.js]
 [test-diffpatcher.js]
 [test-dispatcher.js]
@@ -101,16 +102,17 @@ skip-if = true
 [test-node-os.js]
 [test-notifications.js]
 [test-object.js]
 [test-observers.js]
 [test-page-mod-debug.js]
 [test-page-mod.js]
 [test-page-worker.js]
 [test-panel.js]
+skip-if = asan # Bug 1333359
 [test-passwords-utils.js]
 [test-passwords.js]
 [test-path.js]
 [test-plain-text-console.js]
 [test-preferences-service.js]
 [test-preferences-target.js]
 [test-private-browsing.js]
 [test-promise.js]
@@ -168,12 +170,13 @@ skip-if = true
 [test-uuid.js]
 [test-weak-set.js]
 [test-window-events.js]
 [test-window-observer.js]
 [test-window-utils-private-browsing.js]
 [test-window-utils.js]
 [test-window-utils2.js]
 [test-windows-common.js]
+skip-if = asan # bug 1333357
 [test-windows.js]
 [test-xhr.js]
 [test-xpcom.js]
 [test-xul-app.js]
--- a/browser/base/content/test/newtab/browser.ini
+++ b/browser/base/content/test/newtab/browser.ini
@@ -44,10 +44,12 @@ support-files =
   searchEngine1xLogo.xml
   searchEngine2xLogo.xml
   searchEngine1x2xLogo.xml
   ../general/searchSuggestionEngine.xml
   ../general/searchSuggestionEngine.sjs
 [browser_newtab_sponsored_icon_click.js]
 skip-if = true # Bug 1314619
 [browser_newtab_undo.js]
+# temporary until determine why more intermittent on VM
+subsuite = clipboard
 [browser_newtab_unpin.js]
 [browser_newtab_update.js]
--- a/browser/base/content/test/plugins/browser.ini
+++ b/browser/base/content/test/plugins/browser.ini
@@ -51,17 +51,16 @@ skip-if = toolkit == "gtk2" || toolkit =
 [browser_CTP_crashreporting.js]
 skip-if = !crashreporter
 [browser_CTP_data_urls.js]
 [browser_CTP_drag_drop.js]
 [browser_CTP_hide_overlay.js]
 [browser_CTP_iframe.js]
 [browser_CTP_multi_allow.js]
 [browser_CTP_nonplugins.js]
-skip-if = e10s # Bug 1315042
 [browser_CTP_notificationBar.js]
 [browser_CTP_outsideScrollArea.js]
 [browser_CTP_remove_navigate.js]
 [browser_CTP_resize.js]
 [browser_CTP_zoom.js]
 [browser_blocking.js]
 [browser_iterate_hidden_plugins.js]
 [browser_plugins_added_dynamically.js]
--- a/browser/base/content/test/tabs/browser.ini
+++ b/browser/base/content/test/tabs/browser.ini
@@ -3,8 +3,9 @@ support-files =
   dummy_page.html
 
 [browser_tabSpinnerProbe.js]
 skip-if = !e10s # Tab spinner is e10s only.
 [browser_tabSwitchPrintPreview.js]
 skip-if = os == 'mac'
 [browser_navigatePinnedTab.js]
 [browser_opened_file_tab_navigated_to_web.js]
+[browser_reload_deleted_file.js]
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/tabs/browser_reload_deleted_file.js
@@ -0,0 +1,35 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+
+const uuidGenerator =
+  Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
+
+const DUMMY_FILE = "dummy_page.html";
+
+// Test for bug 1327942.
+add_task(function* () {
+  // Copy dummy page to unique file in TmpD, so that we can safely delete it.
+  let dummyPage = getChromeDir(getResolvedURI(gTestPath));
+  dummyPage.append(DUMMY_FILE);
+  let disappearingPage = Services.dirsvc.get("TmpD", Ci.nsIFile);
+  let uniqueName = uuidGenerator.generateUUID().toString();
+  dummyPage.copyTo(disappearingPage, uniqueName);
+  disappearingPage.append(uniqueName);
+
+  // Get file:// URI for new page and load in a new tab.
+  const uriString = Services.io.newFileURI(disappearingPage).spec;
+  let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, uriString);
+  registerCleanupFunction(function* () {
+    yield BrowserTestUtils.removeTab(tab);
+  });
+
+  // Delete the page, simulate a click of the reload button and check that we
+  // get a neterror page.
+  disappearingPage.remove(false);
+  document.getElementById("urlbar-reload-button").doCommand();
+  yield BrowserTestUtils.waitForErrorPage(tab.linkedBrowser);
+  yield ContentTask.spawn(tab.linkedBrowser, null, function() {
+    ok(content.document.documentURI.startsWith("about:neterror"),
+       "Check that a neterror page was loaded.");
+  });
+});
--- a/browser/base/content/test/webrtc/browser.ini
+++ b/browser/base/content/test/webrtc/browser.ini
@@ -7,14 +7,13 @@ support-files =
 
 [browser_devices_get_user_media.js]
 skip-if = (os == "linux" && debug) # linux: bug 976544
 [browser_devices_get_user_media_anim.js]
 [browser_devices_get_user_media_in_frame.js]
 [browser_devices_get_user_media_screen.js]
 skip-if = (e10s && debug) || (os == "linux" && !debug) # bug 1320754 for e10s debug, and bug 1320994 for linux opt
 [browser_devices_get_user_media_tear_off_tab.js]
-skip-if = e10s # Bug 1315042
 [browser_devices_get_user_media_unprompted_access.js]
 [browser_devices_get_user_media_unprompted_access_in_frame.js]
 [browser_devices_get_user_media_unprompted_access_tear_off_tab.js]
 skip-if = (os == "linux") # linux: bug 1331616
 [browser_webrtc_hooks.js]
--- a/browser/components/downloads/DownloadsViewUI.jsm
+++ b/browser/components/downloads/DownloadsViewUI.jsm
@@ -109,17 +109,17 @@ this.DownloadsViewUI.DownloadElementShel
    * update in order to improve performance.
    */
   _updateState() {
     this.element.setAttribute("displayName", this.displayName);
     this.element.setAttribute("image", this.image);
     this.element.setAttribute("state",
                               DownloadsCommon.stateOfDownload(this.download));
 
-    if (this.download.error &&
+    if (!this.download.succeeded && this.download.error &&
         this.download.error.becauseBlockedByReputationCheck) {
       this.element.setAttribute("verdict",
                                 this.download.error.reputationCheckVerdict);
     } else {
       this.element.removeAttribute("verdict");
     }
 
     // Since state changed, reset the time left estimation.
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -144,16 +144,23 @@
 @BINPATH@/@DLL_PREFIX@mozavcodec@DLL_SUFFIX@
 #endif
 @RESPATH@/browser/blocklist.xml
 #ifdef XP_UNIX
 #ifndef XP_MACOSX
 @RESPATH@/run-mozilla.sh
 #endif
 #endif
+#ifdef XP_WIN
+#ifdef _AMD64_
+@BINPATH@/@DLL_PREFIX@qipcap64@DLL_SUFFIX@
+#else
+@BINPATH@/@DLL_PREFIX@qipcap@DLL_SUFFIX@
+#endif
+#endif
 
 ; [Components]
 #ifdef MOZ_ARTIFACT_BUILDS
 @RESPATH@/components/prebuilt-interfaces.manifest
 @RESPATH@/components/interfaces.xpt
 @RESPATH@/browser/components/prebuilt-interfaces.manifest
 @RESPATH@/browser/components/interfaces.xpt
 #endif
--- a/browser/modules/E10SUtils.jsm
+++ b/browser/modules/E10SUtils.jsm
@@ -97,19 +97,20 @@ this.E10SUtils = {
         return aPreferredRemoteType;
       }
 
       let flags = module.getURIFlags(url);
       if (flags & Ci.nsIAboutModule.URI_MUST_LOAD_IN_CHILD) {
         return DEFAULT_REMOTE_TYPE;
       }
 
-      if (flags & Ci.nsIAboutModule.URI_CAN_LOAD_IN_CHILD &&
-          aPreferredRemoteType != NOT_REMOTE) {
-        return DEFAULT_REMOTE_TYPE;
+      // If the about page can load in parent or child, it should be safe to
+      // load in any remote type.
+      if (flags & Ci.nsIAboutModule.URI_CAN_LOAD_IN_CHILD) {
+        return aPreferredRemoteType;
       }
 
       return NOT_REMOTE;
     }
 
     if (aURL.startsWith("chrome:")) {
       let url;
       try {
--- a/devtools/client/inspector/package.json
+++ b/devtools/client/inspector/package.json
@@ -2,13 +2,13 @@
   "name": "inspector.html",
   "version": "0.0.1",
   "description": "The Firefox Inspector",
   "scripts": {
     "start": "node bin/dev-server"
   },
   "author": "",
   "dependencies": {
-    "devtools-launchpad": "0.0.24",
+    "devtools-launchpad": "0.0.29",
     "raw-loader": "^0.5.1",
     "json-loader": "^0.5.4"
   }
 }
--- a/devtools/client/inspector/webpack.config.js
+++ b/devtools/client/inspector/webpack.config.js
@@ -8,17 +8,17 @@
 
 const {toolboxConfig} = require("devtools-launchpad/index");
 
 const path = require("path");
 const webpack = require("webpack");
 
 module.exports = envConfig => {
   let webpackConfig = {
-    bail: true,
+    bail: false,
     entry: [
       path.join(__dirname, "local-toolbox.js")
     ],
     output: {
       path: path.join(__dirname, "assets/build"),
       filename: "inspector.js",
       publicPath: "/"
     },
@@ -77,30 +77,32 @@ module.exports = envConfig => {
         "method": path.join(__dirname, "../../../addon-sdk/source/lib/method"),
         "modules/libpref/init/all":
           path.join(__dirname, "../../../modules/libpref/init/all.js"),
         "sdk/system/unload": path.join(__dirname, "./webpack/system-unload-sham.js"),
         "sdk": path.join(__dirname, "../../../addon-sdk/source/lib/sdk"),
         "Services": path.join(__dirname, "../shared/shim/Services.js"),
         "toolkit/locales":
           path.join(__dirname, "../../../toolkit/locales/en-US/chrome/global"),
+        "react": "devtools/client/shared/vendor/react",
+        "react-dom": "devtools/client/shared/vendor/react-dom",
       },
     },
 
     plugins: [
       new webpack.DefinePlugin({
         "isWorker": JSON.stringify(false),
         "reportError": "console.error",
         "AppConstants": "{ DEBUG: true, DEBUG_JS_MODULES: true }",
         "loader": `{
                       lazyRequireGetter: () => {},
                       lazyGetter: () => {}
                     }`,
         "dump": "console.log",
-      }),
+      })
     ]
   };
 
   webpackConfig.externals = [
     /codemirror\//,
     {
       "promise": "var Promise",
       "devtools/server/main": "{}",
--- a/dom/cache/test/mochitest/browser.ini
+++ b/dom/cache/test/mochitest/browser.ini
@@ -1,2 +1,1 @@
 [browser_cache_pb_window.js]
-skip-if = e10s # Bug 1315042
--- a/dom/media/moz.build
+++ b/dom/media/moz.build
@@ -1,16 +1,25 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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', 'Video/Audio')
+    BUG_COMPONENT = ('Core', 'Audio/Video')
+
+with Files('test/**'):
+    BUG_COMPONENT = ('Core', 'Audio/Video: Playback')
+
+with Files('gtest/TestGMP*'):
+    BUG_COMPONENT = ('Core', 'Audio/Video: GMP')
+
+with Files('tests/**'):
+    BUG_COMPONENT = ('Core', 'WebRTC')
 
 component_signaling = ('Core', 'WebRTC: Signaling')
 with Files('IdpSandbox.jsm'):
     BUG_COMPONENT = component_signaling
 with Files('PeerConnection*'):
     BUG_COMPONENT = component_signaling
 with Files('RTC*'):
     BUG_COMPONENT = component_signaling
--- a/extensions/spellcheck/locales/en-US/hunspell/README_en_US.txt
+++ b/extensions/spellcheck/locales/en-US/hunspell/README_en_US.txt
@@ -1,11 +1,11 @@
 en_US-mozilla Hunspell Dictionary
-Generated from SCOWL Version 2016.11.20
-Tue Dec 13 14:42:45 EST 2016
+Generated from SCOWL Version 2017.01.22
+Tue Jan 24 22:59:28 EST 2017
 
 http://wordlist.sourceforge.net
 
 README file for English Hunspell dictionaries derived from SCOWL.
 
 These dictionaries are created using the speller/make-hunspell-dict
 script in SCOWL.
 
@@ -339,9 +339,9 @@ and Australian word list.  It is under t
   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   SUCH DAMAGE.
 
-Build Date: Tue Dec 13 14:42:45 EST 2016
+Build Date: Tue Jan 24 22:59:28 EST 2017
--- a/extensions/spellcheck/locales/en-US/hunspell/dictionary-sources/5-mozilla-added
+++ b/extensions/spellcheck/locales/en-US/hunspell/dictionary-sources/5-mozilla-added
@@ -5541,19 +5541,17 @@ neurosciences
 neuroscientist
 neuroscientist's
 neuroscientists
 newswires
 octopi
 oligo
 opposable
 opposer
-outlier
 outlier's
-outliers
 parallelization
 parallelization's
 parallelizations
 parallelize
 parallelized
 parallelizes
 parallelizing
 permalink
--- a/extensions/spellcheck/locales/en-US/hunspell/dictionary-sources/orig/README_en_US-custom.txt
+++ b/extensions/spellcheck/locales/en-US/hunspell/dictionary-sources/orig/README_en_US-custom.txt
@@ -1,11 +1,11 @@
 en_US-custom Hunspell Dictionary
-Generated from SCOWL Version 2016.11.20
-Tue Dec 13 14:42:42 EST 2016
+Generated from SCOWL Version 2017.01.22
+Tue Jan 24 22:59:27 EST 2017
 
 http://wordlist.sourceforge.net
 
 README file for English Hunspell dictionaries derived from SCOWL.
 
 These dictionaries are created using the speller/make-hunspell-dict
 script in SCOWL.
 
@@ -339,10 +339,10 @@ and Australian word list.  It is under t
   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   SUCH DAMAGE.
 
-Build Date: Tue Dec 13 14:42:42 EST 2016
+Build Date: Tue Jan 24 22:59:27 EST 2017
 With Input Command: ../mk-list -v1 --accents=both en_US 60
--- a/extensions/spellcheck/locales/en-US/hunspell/dictionary-sources/orig/en_US-custom.dic
+++ b/extensions/spellcheck/locales/en-US/hunspell/dictionary-sources/orig/en_US-custom.dic
@@ -1,9 +1,9 @@
-49461
+49467
 0/nm
 0th/pt
 1/n1
 1st/p
 1th/tc
 2/nm
 2nd/p
 2th/tc
@@ -2424,16 +2424,17 @@ Cynthia/M
 Cyprian/M
 Cypriot/MS
 Cyprus/M
 Cyrano/M
 Cyril/M
 Cyrillic/M
 Cyrus/M
 Czech/M
+Czechia/M
 Czechoslovak
 Czechoslovakia/M
 Czechoslovakian/SM
 Czechs
 Czerny/M
 D/M
 DA/M
 DAR
@@ -21160,16 +21161,18 @@ dysentery/M
 dysfunction/MS
 dysfunctional
 dyslectic/SM
 dyslexia/M
 dyslexic/SM
 dyspepsia/M
 dyspeptic/MS
 dysphagia
+dysphoria
+dysphoric
 dysprosium/M
 dystonia
 dz
 débridement
 débutante/SM
 décolletage/SM
 décolleté
 démodé
@@ -34669,16 +34672,17 @@ outhouse/SM
 outing/M
 outlaid
 outlandish/PY
 outlandishness/M
 outlast/DSG
 outlaw/SGMD
 outlay/SGM
 outlet/SM
+outlier/S
 outline/MGDS
 outlive/GDS
 outlook/MS
 outlying
 outmaneuver/GDS
 outmatch/GDS
 outmoded
 outnumber/DSG
@@ -45322,16 +45326,18 @@ thallium/M
 than
 thane/SM
 thank/SDG
 thankful/YP
 thankfulness/M
 thankless/PY
 thanklessness/M
 thanksgiving/SM
+that'd
+that'll
 that/M
 thatch/MDRSZG
 thatcher/M
 thatching/M
 thaw/MDGS
 the/JG
 theater/SM
 theatergoer/SM
--- a/extensions/spellcheck/locales/en-US/hunspell/en-US.dic
+++ b/extensions/spellcheck/locales/en-US/hunspell/en-US.dic
@@ -1,9 +1,9 @@
-52343
+52348
 0/nm
 0th/pt
 1/n1
 1st/p
 1th/tc
 2/nm
 2nd/p
 2th/tc
@@ -3023,16 +3023,17 @@ Cyprian/M
 Cypriot/MS
 Cyprus/M
 Cyrano/M
 Cyril/M
 Cyrille/M
 Cyrillic/M
 Cyrus/M
 Czech/M
+Czechia/M
 Czechoslovak
 Czechoslovakia/M
 Czechoslovakian/SM
 Czechs
 Czerny/M
 D'Arcy
 D/M
 DA/M
@@ -23892,16 +23893,18 @@ dysentery/M
 dysfunction/MS
 dysfunctional
 dyslectic/SM
 dyslexia/M
 dyslexic/SM
 dyspepsia/M
 dyspeptic/MS
 dysphagia
+dysphoria
+dysphoric
 dysprosium/M
 dystonia
 dz
 dbridement
 dbutante/SM
 dcolletage/SM
 dcollet
 dmod
@@ -48190,16 +48193,18 @@ thallium/M
 than
 thane/SM
 thank/SDG
 thankful/YP
 thankfulness/M
 thankless/PY
 thanklessness/M
 thanksgiving/SM
+that'd
+that'll
 that/M
 thatch/MDRSZG
 thatcher/M
 thatching/M
 thaw/MDGS
 the/JG
 theater/SM
 theatergoer/SM
--- a/gfx/harfbuzz/configure.ac
+++ b/gfx/harfbuzz/configure.ac
@@ -1,11 +1,11 @@
 AC_PREREQ([2.64])
 AC_INIT([HarfBuzz],
-        [1.4.1],
+        [1.4.2],
         [https://github.com/behdad/harfbuzz/issues/new],
         [harfbuzz],
         [http://harfbuzz.org/])
 
 AC_CONFIG_MACRO_DIR([m4])
 AC_CONFIG_SRCDIR([src/harfbuzz.pc.in])
 AC_CONFIG_HEADERS([config.h])
 
--- a/gfx/harfbuzz/src/Makefile.am
+++ b/gfx/harfbuzz/src/Makefile.am
@@ -294,16 +294,18 @@ test_would_substitute_LDADD = libharfbuz
 test_size_params_SOURCES = test-size-params.cc
 test_size_params_CPPFLAGS = $(HBCFLAGS)
 test_size_params_LDADD = libharfbuzz.la $(HBLIBS)
 
 test_buffer_serialize_SOURCES = test-buffer-serialize.cc
 test_buffer_serialize_CPPFLAGS = $(HBCFLAGS)
 test_buffer_serialize_LDADD = libharfbuzz.la $(HBLIBS)
 
+check: harfbuzz.def # For check-defs.sh
+
 dist_check_SCRIPTS = \
 	check-c-linkage-decls.sh \
 	check-defs.sh \
 	check-header-guards.sh \
 	check-includes.sh \
 	check-libstdc++.sh \
 	check-static-inits.sh \
 	check-symbols.sh \
--- a/gfx/harfbuzz/src/harfbuzz-icu.pc
+++ b/gfx/harfbuzz/src/harfbuzz-icu.pc
@@ -1,13 +1,13 @@
 prefix=/usr/local
 exec_prefix=/usr/local
 libdir=/usr/local/lib
 includedir=/usr/local/include
 
 Name: harfbuzz
 Description: HarfBuzz text shaping library ICU integration
-Version: 1.4.1
+Version: 1.4.2
 
 Requires: harfbuzz
 Requires.private: icu-uc
 Libs: -L${libdir} -lharfbuzz-icu
 Cflags: -I${includedir}/harfbuzz
--- a/gfx/harfbuzz/src/harfbuzz.pc
+++ b/gfx/harfbuzz/src/harfbuzz.pc
@@ -1,13 +1,13 @@
 prefix=/usr/local
 exec_prefix=/usr/local
 libdir=/usr/local/lib
 includedir=/usr/local/include
 
 Name: harfbuzz
 Description: HarfBuzz text shaping library
-Version: 1.4.1
+Version: 1.4.2
 
 Libs: -L${libdir} -lharfbuzz
 Libs.private:    
 Requires.private: glib-2.0 >= 2.19.1 
 Cflags: -I${includedir}/harfbuzz
--- a/gfx/harfbuzz/src/hb-common.cc
+++ b/gfx/harfbuzz/src/hb-common.cc
@@ -600,8 +600,352 @@ hb_version_string (void)
  **/
 hb_bool_t
 hb_version_atleast (unsigned int major,
 		    unsigned int minor,
 		    unsigned int micro)
 {
   return HB_VERSION_ATLEAST (major, minor, micro);
 }
+
+
+
+/* hb_feature_t and hb_variation_t */
+
+static bool
+parse_space (const char **pp, const char *end)
+{
+  while (*pp < end && ISSPACE (**pp))
+    (*pp)++;
+  return true;
+}
+
+static bool
+parse_char (const char **pp, const char *end, char c)
+{
+  parse_space (pp, end);
+
+  if (*pp == end || **pp != c)
+    return false;
+
+  (*pp)++;
+  return true;
+}
+
+static bool
+parse_uint (const char **pp, const char *end, unsigned int *pv)
+{
+  char buf[32];
+  unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp));
+  strncpy (buf, *pp, len);
+  buf[len] = '\0';
+
+  char *p = buf;
+  char *pend = p;
+  unsigned int v;
+
+  /* Intentionally use strtol instead of strtoul, such that
+   * -1 turns into "big number"... */
+  errno = 0;
+  v = strtol (p, &pend, 0);
+  if (errno || p == pend)
+    return false;
+
+  *pv = v;
+  *pp += pend - p;
+  return true;
+}
+
+static bool
+parse_float (const char **pp, const char *end, float *pv)
+{
+  char buf[32];
+  unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp));
+  strncpy (buf, *pp, len);
+  buf[len] = '\0';
+
+  char *p = buf;
+  char *pend = p;
+  float v;
+
+  errno = 0;
+  v = strtof (p, &pend);
+  if (errno || p == pend)
+    return false;
+
+  *pv = v;
+  *pp += pend - p;
+  return true;
+}
+
+static bool
+parse_bool (const char **pp, const char *end, unsigned int *pv)
+{
+  parse_space (pp, end);
+
+  const char *p = *pp;
+  while (*pp < end && ISALPHA(**pp))
+    (*pp)++;
+
+  /* CSS allows on/off as aliases 1/0. */
+  if (*pp - p == 2 || 0 == strncmp (p, "on", 2))
+    *pv = 1;
+  else if (*pp - p == 3 || 0 == strncmp (p, "off", 2))
+    *pv = 0;
+  else
+    return false;
+
+  return true;
+}
+
+/* hb_feature_t */
+
+static bool
+parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature)
+{
+  if (parse_char (pp, end, '-'))
+    feature->value = 0;
+  else {
+    parse_char (pp, end, '+');
+    feature->value = 1;
+  }
+
+  return true;
+}
+
+static bool
+parse_tag (const char **pp, const char *end, hb_tag_t *tag)
+{
+  parse_space (pp, end);
+
+  char quote = 0;
+
+  if (*pp < end && (**pp == '\'' || **pp == '"'))
+  {
+    quote = **pp;
+    (*pp)++;
+  }
+
+  const char *p = *pp;
+  while (*pp < end && ISALNUM(**pp))
+    (*pp)++;
+
+  if (p == *pp || *pp - p > 4)
+    return false;
+
+  *tag = hb_tag_from_string (p, *pp - p);
+
+  if (quote)
+  {
+    /* CSS expects exactly four bytes.  And we only allow quotations for
+     * CSS compatibility.  So, enforce the length. */
+     if (*pp - p != 4)
+       return false;
+    if (*pp == end || **pp != quote)
+      return false;
+    (*pp)++;
+  }
+
+  return true;
+}
+
+static bool
+parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature)
+{
+  parse_space (pp, end);
+
+  bool has_start;
+
+  feature->start = 0;
+  feature->end = (unsigned int) -1;
+
+  if (!parse_char (pp, end, '['))
+    return true;
+
+  has_start = parse_uint (pp, end, &feature->start);
+
+  if (parse_char (pp, end, ':')) {
+    parse_uint (pp, end, &feature->end);
+  } else {
+    if (has_start)
+      feature->end = feature->start + 1;
+  }
+
+  return parse_char (pp, end, ']');
+}
+
+static bool
+parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature)
+{
+  bool had_equal = parse_char (pp, end, '=');
+  bool had_value = parse_uint (pp, end, &feature->value) ||
+                   parse_bool (pp, end, &feature->value);
+  /* CSS doesn't use equal-sign between tag and value.
+   * If there was an equal-sign, then there *must* be a value.
+   * A value without an eqaul-sign is ok, but not required. */
+  return !had_equal || had_value;
+}
+
+static bool
+parse_one_feature (const char **pp, const char *end, hb_feature_t *feature)
+{
+  return parse_feature_value_prefix (pp, end, feature) &&
+	 parse_tag (pp, end, &feature->tag) &&
+	 parse_feature_indices (pp, end, feature) &&
+	 parse_feature_value_postfix (pp, end, feature) &&
+	 parse_space (pp, end) &&
+	 *pp == end;
+}
+
+/**
+ * hb_feature_from_string:
+ * @str: (array length=len) (element-type uint8_t): a string to parse
+ * @len: length of @str, or -1 if string is %NULL terminated
+ * @feature: (out): the #hb_feature_t to initialize with the parsed values
+ *
+ * Parses a string into a #hb_feature_t.
+ *
+ * TODO: document the syntax here.
+ *
+ * Return value:
+ * %true if @str is successfully parsed, %false otherwise.
+ *
+ * Since: 0.9.5
+ **/
+hb_bool_t
+hb_feature_from_string (const char *str, int len,
+			hb_feature_t *feature)
+{
+  hb_feature_t feat;
+
+  if (len < 0)
+    len = strlen (str);
+
+  if (likely (parse_one_feature (&str, str + len, &feat)))
+  {
+    if (feature)
+      *feature = feat;
+    return true;
+  }
+
+  if (feature)
+    memset (feature, 0, sizeof (*feature));
+  return false;
+}
+
+/**
+ * hb_feature_to_string:
+ * @feature: an #hb_feature_t to convert
+ * @buf: (array length=size) (out): output string
+ * @size: the allocated size of @buf
+ *
+ * Converts a #hb_feature_t into a %NULL-terminated string in the format
+ * understood by hb_feature_from_string(). The client in responsible for
+ * allocating big enough size for @buf, 128 bytes is more than enough.
+ *
+ * Since: 0.9.5
+ **/
+void
+hb_feature_to_string (hb_feature_t *feature,
+		      char *buf, unsigned int size)
+{
+  if (unlikely (!size)) return;
+
+  char s[128];
+  unsigned int len = 0;
+  if (feature->value == 0)
+    s[len++] = '-';
+  hb_tag_to_string (feature->tag, s + len);
+  len += 4;
+  while (len && s[len - 1] == ' ')
+    len--;
+  if (feature->start != 0 || feature->end != (unsigned int) -1)
+  {
+    s[len++] = '[';
+    if (feature->start)
+      len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start));
+    if (feature->end != feature->start + 1) {
+      s[len++] = ':';
+      if (feature->end != (unsigned int) -1)
+	len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end));
+    }
+    s[len++] = ']';
+  }
+  if (feature->value > 1)
+  {
+    s[len++] = '=';
+    len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value));
+  }
+  assert (len < ARRAY_LENGTH (s));
+  len = MIN (len, size - 1);
+  memcpy (buf, s, len);
+  buf[len] = '\0';
+}
+
+/* hb_variation_t */
+
+static bool
+parse_variation_value (const char **pp, const char *end, hb_variation_t *variation)
+{
+  parse_char (pp, end, '='); /* Optional. */
+  return parse_float (pp, end, &variation->value);
+}
+
+static bool
+parse_one_variation (const char **pp, const char *end, hb_variation_t *variation)
+{
+  return parse_tag (pp, end, &variation->tag) &&
+	 parse_variation_value (pp, end, variation) &&
+	 parse_space (pp, end) &&
+	 *pp == end;
+}
+
+/**
+ * hb_variation_from_string:
+ *
+ * Since: 1.4.2
+ */
+hb_bool_t
+hb_variation_from_string (const char *str, int len,
+			  hb_variation_t *variation)
+{
+  hb_variation_t var;
+
+  if (len < 0)
+    len = strlen (str);
+
+  if (likely (parse_one_variation (&str, str + len, &var)))
+  {
+    if (variation)
+      *variation = var;
+    return true;
+  }
+
+  if (variation)
+    memset (variation, 0, sizeof (*variation));
+  return false;
+}
+
+/**
+ * hb_variation_to_string:
+ *
+ * Since: 1.4.2
+ */
+void
+hb_variation_to_string (hb_variation_t *variation,
+			char *buf, unsigned int size)
+{
+  if (unlikely (!size)) return;
+
+  char s[128];
+  unsigned int len = 0;
+  hb_tag_to_string (variation->tag, s + len);
+  len += 4;
+  while (len && s[len - 1] == ' ')
+    len--;
+  s[len++] = '=';
+  len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", variation->value));
+
+  assert (len < ARRAY_LENGTH (s));
+  len = MIN (len, size - 1);
+  memcpy (buf, s, len);
+  buf[len] = '\0';
+}
--- a/gfx/harfbuzz/src/hb-common.h
+++ b/gfx/harfbuzz/src/hb-common.h
@@ -357,11 +357,47 @@ hb_script_get_horizontal_direction (hb_s
 typedef struct hb_user_data_key_t {
   /*< private >*/
   char unused;
 } hb_user_data_key_t;
 
 typedef void (*hb_destroy_func_t) (void *user_data);
 
 
+/* Font features and variations. */
+
+typedef struct hb_feature_t {
+  hb_tag_t      tag;
+  uint32_t      value;
+  unsigned int  start;
+  unsigned int  end;
+} hb_feature_t;
+
+HB_EXTERN hb_bool_t
+hb_feature_from_string (const char *str, int len,
+			hb_feature_t *feature);
+
+HB_EXTERN void
+hb_feature_to_string (hb_feature_t *feature,
+		      char *buf, unsigned int size);
+
+/**
+ * hb_variation_t:
+ *
+ * Since: 1.4.2
+ */
+typedef struct hb_variation_t {
+  hb_tag_t tag;
+  float    value;
+} hb_variation_t;
+
+HB_EXTERN hb_bool_t
+hb_variation_from_string (const char *str, int len,
+			  hb_variation_t *variation);
+
+HB_EXTERN void
+hb_variation_to_string (hb_variation_t *variation,
+			char *buf, unsigned int size);
+
+
 HB_END_DECLS
 
 #endif /* HB_COMMON_H */
--- a/gfx/harfbuzz/src/hb-directwrite.cc
+++ b/gfx/harfbuzz/src/hb-directwrite.cc
@@ -666,17 +666,17 @@ static hb_bool_t
   uint16_t* clusterMap = (uint16_t*) malloc (textLength * sizeof (uint16_t));
   DWRITE_SHAPING_TEXT_PROPERTIES* textProperties = (DWRITE_SHAPING_TEXT_PROPERTIES*)
     malloc (textLength * sizeof (DWRITE_SHAPING_TEXT_PROPERTIES));
 retry_getglyphs:
   uint16_t* glyphIndices = (uint16_t*) malloc (maxGlyphCount * sizeof (uint16_t));
   DWRITE_SHAPING_GLYPH_PROPERTIES* glyphProperties = (DWRITE_SHAPING_GLYPH_PROPERTIES*)
     malloc (maxGlyphCount * sizeof (DWRITE_SHAPING_GLYPH_PROPERTIES));
 
-  hr = analyzer->GetGlyphs (textString, textLength, fontFace, FALSE,
+  hr = analyzer->GetGlyphs (textString, textLength, fontFace, false,
     isRightToLeft, &runHead->mScript, localeName, NULL, &dwFeatures,
     featureRangeLengths, 1, maxGlyphCount, clusterMap, textProperties, glyphIndices,
     glyphProperties, &glyphCount);
 
   if (unlikely (hr == HRESULT_FROM_WIN32 (ERROR_INSUFFICIENT_BUFFER)))
   {
     free (glyphIndices);
     free (glyphProperties);
@@ -714,17 +714,17 @@ retry_getglyphs:
   if (fontEmSize < 0)
     fontEmSize = -fontEmSize;
   double x_mult = (double) font->x_scale / fontEmSize;
   double y_mult = (double) font->y_scale / fontEmSize;
 
   hr = analyzer->GetGlyphPlacements (textString,
     clusterMap, textProperties, textLength, glyphIndices,
     glyphProperties, glyphCount, fontFace, fontEmSize,
-    FALSE, isRightToLeft, &runHead->mScript, localeName,
+    false, isRightToLeft, &runHead->mScript, localeName,
     &dwFeatures, featureRangeLengths, 1,
     glyphAdvances, glyphOffsets);
 
   if (FAILED (hr))
   {
     FAIL ("Analyzer failed to get glyph placements.");
     return false;
   }
@@ -910,17 +910,17 @@ hb_bool_t
     features, num_features, 0);
 }
 
 /*
  * Public [experimental] API
  */
 
 hb_bool_t
-hb_shape_dwrite_experimental_width(hb_font_t          *font,
+hb_directwrite_shape_experimental_width(hb_font_t          *font,
   hb_buffer_t        *buffer,
   const hb_feature_t *features,
   unsigned int        num_features,
   float               width)
 {
   static char *shapers = "directwrite";
   hb_shape_plan_t *shape_plan = hb_shape_plan_create_cached (font->face,
     &buffer->props, features, num_features, &shapers);
--- a/gfx/harfbuzz/src/hb-directwrite.h
+++ b/gfx/harfbuzz/src/hb-directwrite.h
@@ -25,14 +25,14 @@
 #ifndef HB_DIRECTWRITE_H
 #define HB_DIRECTWRITE_H
 
 #include "hb.h"
 
 HB_BEGIN_DECLS
 
 HB_EXTERN hb_bool_t
-hb_shape_dwrite_experimental_width(hb_font_t *font, hb_buffer_t *buffer,
+hb_directwrite_shape_experimental_width(hb_font_t *font, hb_buffer_t *buffer,
   const hb_feature_t *features, unsigned int num_features, float width);
 
 HB_END_DECLS
 
 #endif /* HB_DIRECTWRITE_H */
--- a/gfx/harfbuzz/src/hb-face-private.hh
+++ b/gfx/harfbuzz/src/hb-face-private.hh
@@ -45,22 +45,26 @@ struct hb_face_t {
   ASSERT_POD ();
 
   hb_bool_t immutable;
 
   hb_reference_table_func_t  reference_table_func;
   void                      *user_data;
   hb_destroy_func_t          destroy;
 
-  unsigned int index;
-  mutable unsigned int upem;
-  mutable unsigned int num_glyphs;
+  unsigned int index;			/* Face index in a collection, zero-based. */
+  mutable unsigned int upem;		/* Units-per-EM. */
+  mutable unsigned int num_glyphs;	/* Number of glyphs. */
 
-  struct hb_shaper_data_t shaper_data;
+  struct hb_shaper_data_t shaper_data;	/* Various shaper data. */
 
+  /* Various non-shaping data. */
+  /* ... */
+
+  /* Cache */
   struct plan_node_t {
     hb_shape_plan_t *shape_plan;
     plan_node_t *next;
   } *shape_plans;
 
 
   inline hb_blob_t *reference_table (hb_tag_t tag) const
   {
--- a/gfx/harfbuzz/src/hb-face.cc
+++ b/gfx/harfbuzz/src/hb-face.cc
@@ -23,25 +23,21 @@
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Red Hat Author(s): Behdad Esfahbod
  * Google Author(s): Behdad Esfahbod
  */
 
 #include "hb-private.hh"
 
-#include "hb-ot-layout-private.hh"
-
-#include "hb-font-private.hh"
+#include "hb-face-private.hh"
 #include "hb-open-file-private.hh"
 #include "hb-ot-head-table.hh"
 #include "hb-ot-maxp-table.hh"
 
-#include <string.h>
-
 
 /*
  * hb_face_t
  */
 
 const hb_face_t _hb_face_nil = {
   HB_OBJECT_HEADER_STATIC,
 
--- a/gfx/harfbuzz/src/hb-font.cc
+++ b/gfx/harfbuzz/src/hb-font.cc
@@ -23,24 +23,17 @@
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Red Hat Author(s): Behdad Esfahbod
  * Google Author(s): Behdad Esfahbod
  */
 
 #include "hb-private.hh"
 
-#include "hb-ot-layout-private.hh"
-
 #include "hb-font-private.hh"
-#include "hb-open-file-private.hh"
-#include "hb-ot-head-table.hh"
-#include "hb-ot-maxp-table.hh"
-
-#include <string.h>
 
 
 /*
  * hb_font_funcs_t
  */
 
 static hb_bool_t
 hb_font_get_font_h_extents_nil (hb_font_t *font,
@@ -1538,40 +1531,122 @@ void
 hb_font_get_ppem (hb_font_t *font,
 		  unsigned int *x_ppem,
 		  unsigned int *y_ppem)
 {
   if (x_ppem) *x_ppem = font->x_ppem;
   if (y_ppem) *y_ppem = font->y_ppem;
 }
 
+/*
+ * Variations
+ */
 
+static void
+_hb_font_adopt_var_coords_normalized (hb_font_t *font,
+				      int *coords, /* 2.14 normalized */
+				      unsigned int coords_length)
+{
+  free (font->coords);
+
+  font->coords = coords;
+  font->num_coords = coords_length;
+}
+
+/**
+ * hb_font_set_variations:
+ *
+ * Since: 1.4.2
+ */
+void
+hb_font_set_variations (hb_font_t *font,
+			const hb_variation_t *variations,
+			unsigned int variations_length)
+{
+  if (font->immutable)
+    return;
+
+  if (!variations_length)
+  {
+    hb_font_set_var_coords_normalized (font, NULL, 0);
+    return;
+  }
+
+  unsigned int coords_length = hb_ot_var_get_axis_count (font->face);
+
+  int *normalized = coords_length ? (int *) calloc (coords_length, sizeof (int)) : NULL;
+  if (unlikely (coords_length && !normalized))
+    return;
+
+  hb_ot_var_normalize_variations (font->face,
+				  variations, variations_length,
+				  normalized, coords_length);
+  _hb_font_adopt_var_coords_normalized (font, normalized, coords_length);
+}
+
+/**
+ * hb_font_set_var_coords_design:
+ *
+ * Since: 1.4.2
+ */
+void
+hb_font_set_var_coords_design (hb_font_t *font,
+			       const float *coords,
+			       unsigned int coords_length)
+{
+  if (font->immutable)
+    return;
+
+  int *normalized = coords_length ? (int *) calloc (coords_length, sizeof (int)) : NULL;
+  if (unlikely (coords_length && !normalized))
+    return;
+
+  hb_ot_var_normalize_coords (font->face, coords_length, coords, normalized);
+  _hb_font_adopt_var_coords_normalized (font, normalized, coords_length);
+}
+
+/**
+ * hb_font_set_var_coords_normalized:
+ *
+ * Since: 1.4.2
+ */
 void
 hb_font_set_var_coords_normalized (hb_font_t *font,
-				   int *coords, /* XXX 2.14 normalized */
+				   const int *coords, /* 2.14 normalized */
 				   unsigned int coords_length)
 {
   if (font->immutable)
     return;
 
-  /* Skip tail zero entries. */
-  while (coords_length && !coords[coords_length - 1])
-    coords_length--;
-
   int *copy = coords_length ? (int *) calloc (coords_length, sizeof (coords[0])) : NULL;
   if (unlikely (coords_length && !copy))
     return;
 
-  free (font->coords);
-
   if (coords_length)
     memcpy (copy, coords, coords_length * sizeof (coords[0]));
 
-  font->coords = copy;
-  font->num_coords = coords_length;
+  _hb_font_adopt_var_coords_normalized (font, copy, coords_length);
+}
+
+/**
+ * hb_font_set_var_coords_normalized:
+ *
+ * Return value is valid as long as variation coordinates of the font
+ * are not modified.
+ *
+ * Since: 1.4.2
+ */
+const int *
+hb_font_get_var_coords_normalized (hb_font_t *font,
+				   unsigned int *length)
+{
+  if (length)
+    *length = font->num_coords;
+
+  return font->coords;
 }
 
 
 #ifndef HB_DISABLE_DEPRECATED
 
 /*
  * Deprecated get_glyph_func():
  */
--- a/gfx/harfbuzz/src/hb-font.h
+++ b/gfx/harfbuzz/src/hb-font.h
@@ -598,17 +598,30 @@ hb_font_set_ppem (hb_font_t *font,
 		  unsigned int x_ppem,
 		  unsigned int y_ppem);
 
 HB_EXTERN void
 hb_font_get_ppem (hb_font_t *font,
 		  unsigned int *x_ppem,
 		  unsigned int *y_ppem);
 
+HB_EXTERN void
+hb_font_set_variations (hb_font_t *font,
+			const hb_variation_t *variations,
+			unsigned int variations_length);
+
+HB_EXTERN void
+hb_font_set_var_coords_design (hb_font_t *font,
+			       const float *coords,
+			       unsigned int coords_length);
 
 HB_EXTERN void
 hb_font_set_var_coords_normalized (hb_font_t *font,
-				   int *coords, /* XXX 2.14 normalized */
+				   const int *coords, /* 2.14 normalized */
 				   unsigned int coords_length);
 
+HB_EXTERN const int *
+hb_font_get_var_coords_normalized (hb_font_t *font,
+				   unsigned int *length);
+
 HB_END_DECLS
 
 #endif /* HB_FONT_H */
--- a/gfx/harfbuzz/src/hb-ft.cc
+++ b/gfx/harfbuzz/src/hb-ft.cc
@@ -616,27 +616,32 @@ hb_ft_font_create (FT_Face           ft_
 		    ft_face->size->metrics.x_ppem,
 		    ft_face->size->metrics.y_ppem);
 #endif
 
 #ifdef HAVE_FT_GET_VAR_BLEND_COORDINATES
   FT_MM_Var *mm_var = NULL;
   if (!FT_Get_MM_Var (ft_face, &mm_var))
   {
-    FT_Fixed coords[mm_var->num_axis];
-    int hbCoords[mm_var->num_axis];
-    if (!FT_Get_Var_Blend_Coordinates (ft_face, mm_var->num_axis, coords))
+    FT_Fixed *ft_coords = (FT_Fixed *) calloc (mm_var->num_axis, sizeof (FT_Fixed));
+    int *coords = (int *) calloc (mm_var->num_axis, sizeof (int));
+    if (coords && ft_coords)
     {
-      for (int i = 0; i < mm_var->num_axis; ++i)
-	hbCoords[i] = coords[i] >> 2;
+      if (!FT_Get_Var_Blend_Coordinates (ft_face, mm_var->num_axis, ft_coords))
+      {
+	for (unsigned int i = 0; i < mm_var->num_axis; ++i)
+	  coords[i] = ft_coords[i] >>= 2;
 
-      hb_font_set_var_coords_normalized (font, hbCoords, mm_var->num_axis);
+	hb_font_set_var_coords_normalized (font, coords, mm_var->num_axis);
+      }
+      free (coords);
+      free (ft_coords);
     }
+    free (mm_var);
   }
-  free (mm_var);
 #endif
 
   return font;
 }
 
 /**
  * hb_ft_font_create_referenced:
  * @ft_face:
@@ -731,14 +736,28 @@ hb_ft_font_set_funcs (hb_font_t *font)
 #endif
   if (font->x_scale < 0 || font->y_scale < 0)
   {
     FT_Matrix matrix = { font->x_scale < 0 ? -1 : +1, 0,
 			  0, font->y_scale < 0 ? -1 : +1};
     FT_Set_Transform (ft_face, &matrix, NULL);
   }
 
+  unsigned int num_coords;
+  const int *coords = hb_font_get_var_coords_normalized (font, &num_coords);
+  if (num_coords)
+  {
+    FT_Fixed *ft_coords = (FT_Fixed *) calloc (num_coords, sizeof (FT_Fixed));
+    if (ft_coords)
+    {
+      for (unsigned int i = 0; i < num_coords; i++)
+	ft_coords[i] = coords[i] << 2;
+      FT_Set_Var_Blend_Coordinates (ft_face, num_coords, ft_coords);
+      free (ft_coords);
+    }
+  }
+
   ft_face->generic.data = blob;
   ft_face->generic.finalizer = (FT_Generic_Finalizer) _release_blob;
 
   _hb_ft_font_set_funcs (font, ft_face, true);
   hb_ft_font_set_load_flags (font, FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING);
 }
--- a/gfx/harfbuzz/src/hb-open-file-private.hh
+++ b/gfx/harfbuzz/src/hb-open-file-private.hh
@@ -137,17 +137,17 @@ struct TTCHeaderVersion1
     TRACE_SANITIZE (this);
     return_trace (table.sanitize (c, this));
   }
 
   protected:
   Tag		ttcTag;		/* TrueType Collection ID string: 'ttcf' */
   FixedVersion<>version;	/* Version of the TTC Header (1.0),
 				 * 0x00010000u */
-  ArrayOf<OffsetTo<OffsetTable, ULONG>, ULONG>
+  ArrayOf<LOffsetTo<OffsetTable>, ULONG>
 		table;		/* Array of offsets to the OffsetTable for each font
 				 * from the beginning of the file */
   public:
   DEFINE_SIZE_ARRAY (12, table);
 };
 
 struct TTCHeader
 {
--- a/gfx/harfbuzz/src/hb-open-type-private.hh
+++ b/gfx/harfbuzz/src/hb-open-type-private.hh
@@ -25,16 +25,17 @@
  * Red Hat Author(s): Behdad Esfahbod
  * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_OPEN_TYPE_PRIVATE_HH
 #define HB_OPEN_TYPE_PRIVATE_HH
 
 #include "hb-private.hh"
+#include "hb-face-private.hh"
 
 
 namespace OT {
 
 
 
 /*
  * Casts
@@ -824,16 +825,17 @@ struct OffsetTo : Offset<OffsetType>
   }
 
   /* Set the offset to Null */
   inline bool neuter (hb_sanitize_context_t *c) const {
     return c->try_set (this, 0);
   }
   DEFINE_SIZE_STATIC (sizeof(OffsetType));
 };
+template <typename Type> struct LOffsetTo : OffsetTo<Type, ULONG> {};
 template <typename Base, typename OffsetType, typename Type>
 static inline const Type& operator + (const Base &base, const OffsetTo<Type, OffsetType> &offset) { return offset (base); }
 template <typename Base, typename OffsetType, typename Type>
 static inline Type& operator + (Base &base, OffsetTo<Type, OffsetType> &offset) { return offset (base); }
 
 
 /*
  * Array Types
@@ -945,16 +947,17 @@ struct ArrayOf
   }
 
   public:
   LenType len;
   Type array[VAR];
   public:
   DEFINE_SIZE_ARRAY (sizeof (LenType), array);
 };
+template <typename Type> struct LArrayOf : ArrayOf<Type, ULONG> {};
 
 /* Array of Offset's */
 template <typename Type, typename OffsetType=USHORT>
 struct OffsetArrayOf : ArrayOf<OffsetTo<Type, OffsetType> > {};
 
 /* Array of offsets relative to the beginning of the array itself. */
 template <typename Type>
 struct OffsetListOf : OffsetArrayOf<Type>
@@ -1056,12 +1059,110 @@ struct SortedArrayOf : ArrayOf<Type, Len
       else
         return mid;
     }
     return -1;
   }
 };
 
 
+/* Lazy struct and blob loaders. */
+
+/* Logic is shared between hb_lazy_loader_t and hb_lazy_table_loader_t */
+template <typename T>
+struct hb_lazy_loader_t
+{
+  inline void init (hb_face_t *face_)
+  {
+    face = face_;
+    instance = NULL;
+  }
+
+  inline void fini (void)
+  {
+    if (instance && instance != &OT::Null(T))
+    {
+      instance->fini();
+      free (instance);
+    }
+  }
+
+  inline const T* get (void) const
+  {
+  retry:
+    T *p = (T *) hb_atomic_ptr_get (&instance);
+    if (unlikely (!p))
+    {
+      p = (T *) calloc (1, sizeof (T));
+      if (unlikely (!p))
+        p = const_cast<T *> (&OT::Null(T));
+      else
+	p->init (face);
+      if (unlikely (!hb_atomic_ptr_cmpexch (const_cast<T **>(&instance), NULL, p)))
+      {
+	if (p != &OT::Null(T))
+	  p->fini ();
+	goto retry;
+      }
+    }
+    return p;
+  }
+
+  inline const T* operator-> (void) const
+  {
+    return get ();
+  }
+
+  private:
+  hb_face_t *face;
+  T *instance;
+};
+
+/* Logic is shared between hb_lazy_loader_t and hb_lazy_table_loader_t */
+template <typename T>
+struct hb_lazy_table_loader_t
+{
+  inline void init (hb_face_t *face_)
+  {
+    face = face_;
+    instance = NULL;
+    blob = NULL;
+  }
+
+  inline void fini (void)
+  {
+    hb_blob_destroy (blob);
+  }
+
+  inline const T* get (void) const
+  {
+  retry:
+    T *p = (T *) hb_atomic_ptr_get (&instance);
+    if (unlikely (!p))
+    {
+      hb_blob_t *blob_ = OT::Sanitizer<T>::sanitize (face->reference_table (T::tableTag));
+      p = const_cast<T *>(OT::Sanitizer<T>::lock_instance (blob_));
+      if (!hb_atomic_ptr_cmpexch (const_cast<T **>(&instance), NULL, p))
+      {
+	hb_blob_destroy (blob_);
+	goto retry;
+      }
+      blob = blob_;
+    }
+    return p;
+  }
+
+  inline const T* operator-> (void) const
+  {
+    return get();
+  }
+
+  private:
+  hb_face_t *face;
+  T *instance;
+  mutable hb_blob_t *blob;
+};
+
+
 } /* namespace OT */
 
 
 #endif /* HB_OPEN_TYPE_PRIVATE_HH */
--- a/gfx/harfbuzz/src/hb-ot-cbdt-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-cbdt-table.hh
@@ -211,17 +211,17 @@ struct IndexSubtableRecord
       return false;
     }
     return (this+offsetToSubtable).get_image_data (gid - firstGlyphIndex,
 						   offset, length, format);
   }
 
   USHORT firstGlyphIndex;
   USHORT lastGlyphIndex;
-  OffsetTo<IndexSubtable, ULONG> offsetToSubtable;
+  LOffsetTo<IndexSubtable> offsetToSubtable;
 
   DEFINE_SIZE_STATIC(8);
 };
 
 struct IndexSubtableArray
 {
   inline bool sanitize (hb_sanitize_context_t *c, unsigned int count) const
   {
@@ -270,17 +270,17 @@ struct BitmapSizeTable
   }
 
   const IndexSubtableRecord *find_table (hb_codepoint_t glyph, const void *base) const
   {
     return (base+indexSubtableArrayOffset).find_table (glyph, numberOfIndexSubtables);
   }
 
   protected:
-  OffsetTo<IndexSubtableArray, ULONG> indexSubtableArrayOffset;
+  LOffsetTo<IndexSubtableArray> indexSubtableArrayOffset;
   ULONG indexTablesSize;
   ULONG numberOfIndexSubtables;
   ULONG colorRef;
   SBitLineMetrics horizontal;
   SBitLineMetrics vertical;
   USHORT startGlyphIndex;
   USHORT endGlyphIndex;
   BYTE ppemX;
@@ -343,18 +343,18 @@ struct CBLC
 	return sizeTables[i].find_table (glyph, this);
       }
     }
 
     return NULL;
   }
 
   protected:
-  FixedVersion<>version;
-  ArrayOf<BitmapSizeTable, ULONG> sizeTables;
+  FixedVersion<>		version;
+  LArrayOf<BitmapSizeTable>	sizeTables;
 
   public:
   DEFINE_SIZE_ARRAY(8, sizeTables);
 };
 
 /*
  * CBDT -- Color Bitmap Data Table
  */
--- a/gfx/harfbuzz/src/hb-ot-cmap-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-cmap-table.hh
@@ -375,19 +375,19 @@ struct VariationSelectorRecord
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
 		  defaultUVS.sanitize (c, base) &&
 		  nonDefaultUVS.sanitize (c, base));
   }
 
   UINT24	varSelector;	/* Variation selector. */
-  OffsetTo<DefaultUVS, ULONG>
+  LOffsetTo<DefaultUVS>
 		defaultUVS;	/* Offset to Default UVS Table. May be 0. */
-  OffsetTo<NonDefaultUVS, ULONG>
+  LOffsetTo<NonDefaultUVS>
 		nonDefaultUVS;	/* Offset to Non-Default UVS Table. May be 0. */
   public:
   DEFINE_SIZE_STATIC (11);
 };
 
 struct CmapSubtableFormat14
 {
   inline glyph_variant_t get_glyph_variant (hb_codepoint_t codepoint,
@@ -481,17 +481,17 @@ struct EncodingRecord
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
 		  subtable.sanitize (c, base));
   }
 
   USHORT	platformID;	/* Platform ID. */
   USHORT	encodingID;	/* Platform-specific encoding ID. */
-  OffsetTo<CmapSubtable, ULONG>
+  LOffsetTo<CmapSubtable>
 		subtable;	/* Byte offset from beginning of table to the subtable for this encoding. */
   public:
   DEFINE_SIZE_STATIC (8);
 };
 
 struct cmap
 {
   static const hb_tag_t tableTag	= HB_OT_TAG_cmap;
--- a/gfx/harfbuzz/src/hb-ot-font.cc
+++ b/gfx/harfbuzz/src/hb-ot-font.cc
@@ -32,35 +32,40 @@
 
 #include "hb-ot-cmap-table.hh"
 #include "hb-ot-cbdt-table.hh"
 #include "hb-ot-glyf-table.hh"
 #include "hb-ot-head-table.hh"
 #include "hb-ot-hhea-table.hh"
 #include "hb-ot-hmtx-table.hh"
 #include "hb-ot-os2-table.hh"
+#include "hb-ot-var-hvar-table.hh"
 //#include "hb-ot-post-table.hh"
 
 
 struct hb_ot_face_metrics_accelerator_t
 {
   unsigned int num_metrics;
   unsigned int num_advances;
   unsigned int default_advance;
   unsigned short ascender;
   unsigned short descender;
   unsigned short line_gap;
   bool has_font_extents;
 
-  const OT::_mtx *table;
+  const OT::hmtxvmtx *table;
   hb_blob_t *blob;
 
+  const OT::HVARVVAR *var;
+  hb_blob_t *var_blob;
+
   inline void init (hb_face_t *face,
 		    hb_tag_t _hea_tag,
 		    hb_tag_t _mtx_tag,
+		    hb_tag_t _var_tag,
 		    hb_tag_t os2_tag,
 		    unsigned int default_advance = 0)
   {
     this->default_advance = default_advance ? default_advance : face->get_upem ();
 
     bool got_font_extents = false;
     if (os2_tag)
     {
@@ -86,57 +91,60 @@ struct hb_ot_face_metrics_accelerator_t
       this->descender = _hea->descender;
       this->line_gap = _hea->lineGap;
       got_font_extents = (this->ascender | this->descender) != 0;
     }
     hb_blob_destroy (_hea_blob);
 
     this->has_font_extents = got_font_extents;
 
-    this->blob = OT::Sanitizer<OT::_mtx>::sanitize (face->reference_table (_mtx_tag));
+    this->blob = OT::Sanitizer<OT::hmtxvmtx>::sanitize (face->reference_table (_mtx_tag));
 
     /* Cap num_metrics() and num_advances() based on table length. */
     unsigned int len = hb_blob_get_length (this->blob);
     if (unlikely (this->num_advances * 4 > len))
       this->num_advances = len / 4;
     this->num_metrics = this->num_advances + (len - 4 * this->num_advances) / 2;
 
     /* We MUST set num_metrics to zero if num_advances is zero.
      * Our get_advance() depends on that. */
     if (unlikely (!this->num_advances))
     {
       this->num_metrics = this->num_advances = 0;
       hb_blob_destroy (this->blob);
       this->blob = hb_blob_get_empty ();
     }
-    this->table = OT::Sanitizer<OT::_mtx>::lock_instance (this->blob);
+    this->table = OT::Sanitizer<OT::hmtxvmtx>::lock_instance (this->blob);
+
+    this->var_blob = OT::Sanitizer<OT::HVARVVAR>::sanitize (face->reference_table (_var_tag));
+    this->var = OT::Sanitizer<OT::HVARVVAR>::lock_instance (this->var_blob);
   }
 
   inline void fini (void)
   {
     hb_blob_destroy (this->blob);
+    hb_blob_destroy (this->var_blob);
   }
 
-  inline unsigned int get_advance (hb_codepoint_t glyph) const
+  inline unsigned int get_advance (hb_codepoint_t  glyph,
+				   hb_font_t      *font) const
   {
     if (unlikely (glyph >= this->num_metrics))
     {
       /* If this->num_metrics is zero, it means we don't have the metrics table
        * for this direction: return default advance.  Otherwise, it means that the
        * glyph index is out of bound: return zero. */
       if (this->num_metrics)
 	return 0;
       else
 	return this->default_advance;
     }
 
-    if (glyph >= this->num_advances)
-      glyph = this->num_advances - 1;
-
-    return this->table->longMetric[glyph].advance;
+    return this->table->longMetric[MIN (glyph, this->num_advances - 1)].advance
+	 + this->var->get_advance_var (glyph, font->coords, font->num_coords); // TODO Optimize?!
   }
 };
 
 struct hb_ot_face_glyf_accelerator_t
 {
   bool short_offset;
   unsigned int num_glyphs;
   const OT::loca *loca;
@@ -416,79 +424,37 @@ struct hb_ot_face_cmap_accelerator_t
       case OT::GLYPH_VARIANT_FOUND:		return true;
       case OT::GLYPH_VARIANT_USE_DEFAULT:	break;
     }
 
     return get_nominal_glyph (unicode, glyph);
   }
 };
 
-template <typename T>
-struct hb_lazy_loader_t
-{
-  inline void init (hb_face_t *face_)
-  {
-    face = face_;
-    instance = NULL;
-  }
-
-  inline void fini (void)
-  {
-    if (instance && instance != &OT::Null(T))
-    {
-      instance->fini();
-      free (instance);
-    }
-  }
-
-  inline const T* operator-> (void) const
-  {
-  retry:
-    T *p = (T *) hb_atomic_ptr_get (&instance);
-    if (unlikely (!p))
-    {
-      p = (T *) calloc (1, sizeof (T));
-      if (unlikely (!p))
-        return &OT::Null(T);
-      p->init (face);
-      if (unlikely (!hb_atomic_ptr_cmpexch (const_cast<T **>(&instance), NULL, p)))
-      {
-	p->fini ();
-	goto retry;
-      }
-    }
-    return p;
-  }
-
-  private:
-  hb_face_t *face;
-  T *instance;
-};
-
 struct hb_ot_font_t
 {
   hb_ot_face_cmap_accelerator_t cmap;
   hb_ot_face_metrics_accelerator_t h_metrics;
   hb_ot_face_metrics_accelerator_t v_metrics;
-  hb_lazy_loader_t<hb_ot_face_glyf_accelerator_t> glyf;
-  hb_lazy_loader_t<hb_ot_face_cbdt_accelerator_t> cbdt;
+  OT::hb_lazy_loader_t<hb_ot_face_glyf_accelerator_t> glyf;
+  OT::hb_lazy_loader_t<hb_ot_face_cbdt_accelerator_t> cbdt;
 };
 
 
 static hb_ot_font_t *
 _hb_ot_font_create (hb_face_t *face)
 {
   hb_ot_font_t *ot_font = (hb_ot_font_t *) calloc (1, sizeof (hb_ot_font_t));
 
   if (unlikely (!ot_font))
     return NULL;
 
   ot_font->cmap.init (face);
-  ot_font->h_metrics.init (face, HB_OT_TAG_hhea, HB_OT_TAG_hmtx, HB_OT_TAG_os2);
-  ot_font->v_metrics.init (face, HB_OT_TAG_vhea, HB_OT_TAG_vmtx, HB_TAG_NONE,
+  ot_font->h_metrics.init (face, HB_OT_TAG_hhea, HB_OT_TAG_hmtx, HB_OT_TAG_HVAR, HB_OT_TAG_os2);
+  ot_font->v_metrics.init (face, HB_OT_TAG_vhea, HB_OT_TAG_vmtx, HB_OT_TAG_VVAR, HB_TAG_NONE,
 			   ot_font->h_metrics.ascender - ot_font->h_metrics.descender); /* TODO Can we do this lazily? */
   ot_font->glyf.init (face);
   ot_font->cbdt.init (face);
 
   return ot_font;
 }
 
 static void
@@ -524,46 +490,47 @@ hb_ot_get_variation_glyph (hb_font_t *fo
 			   hb_codepoint_t *glyph,
 			   void *user_data HB_UNUSED)
 {
   const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data;
   return ot_font->cmap.get_variation_glyph (unicode, variation_selector, glyph);
 }
 
 static hb_position_t
-hb_ot_get_glyph_h_advance (hb_font_t *font HB_UNUSED,
+hb_ot_get_glyph_h_advance (hb_font_t *font,
 			   void *font_data,
 			   hb_codepoint_t glyph,
 			   void *user_data HB_UNUSED)
 {
   const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data;
-  return font->em_scale_x (ot_font->h_metrics.get_advance (glyph));
+  return font->em_scale_x (ot_font->h_metrics.get_advance (glyph, font));
 }
 
 static hb_position_t
-hb_ot_get_glyph_v_advance (hb_font_t *font HB_UNUSED,
+hb_ot_get_glyph_v_advance (hb_font_t *font,
 			   void *font_data,
 			   hb_codepoint_t glyph,
 			   void *user_data HB_UNUSED)
 {
   const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data;
-  return font->em_scale_y (-(int) ot_font->v_metrics.get_advance (glyph));
+  return font->em_scale_y (-(int) ot_font->v_metrics.get_advance (glyph, font));
 }
 
 static hb_bool_t
 hb_ot_get_glyph_extents (hb_font_t *font HB_UNUSED,
 			 void *font_data,
 			 hb_codepoint_t glyph,
 			 hb_glyph_extents_t *extents,
 			 void *user_data HB_UNUSED)
 {
   const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data;
   bool ret = ot_font->glyf->get_extents (glyph, extents);
   if (!ret)
     ret = ot_font->cbdt->get_extents (glyph, extents);
+  // TODO Hook up side-bearings variations.
   extents->x_bearing = font->em_scale_x (extents->x_bearing);
   extents->y_bearing = font->em_scale_y (extents->y_bearing);
   extents->width     = font->em_scale_x (extents->width);
   extents->height    = font->em_scale_y (extents->height);
   return ret;
 }
 
 static hb_bool_t
@@ -571,29 +538,31 @@ hb_ot_get_font_h_extents (hb_font_t *fon
 			  void *font_data,
 			  hb_font_extents_t *metrics,
 			  void *user_data HB_UNUSED)
 {
   const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data;
   metrics->ascender = font->em_scale_y (ot_font->h_metrics.ascender);
   metrics->descender = font->em_scale_y (ot_font->h_metrics.descender);
   metrics->line_gap = font->em_scale_y (ot_font->h_metrics.line_gap);
+  // TODO Hook up variations.
   return ot_font->h_metrics.has_font_extents;
 }
 
 static hb_bool_t
 hb_ot_get_font_v_extents (hb_font_t *font HB_UNUSED,
 			  void *font_data,
 			  hb_font_extents_t *metrics,
 			  void *user_data HB_UNUSED)
 {
   const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data;
   metrics->ascender = font->em_scale_x (ot_font->v_metrics.ascender);
   metrics->descender = font->em_scale_x (ot_font->v_metrics.descender);
   metrics->line_gap = font->em_scale_x (ot_font->v_metrics.line_gap);
+  // TODO Hook up variations.
   return ot_font->v_metrics.has_font_extents;
 }
 
 static hb_font_funcs_t *static_ot_funcs = NULL;
 
 #ifdef HB_USE_ATEXIT
 static
 void free_static_ot_funcs (void)
--- a/gfx/harfbuzz/src/hb-ot-hmtx-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-hmtx-table.hh
@@ -45,20 +45,18 @@ namespace OT {
 struct LongMetric
 {
   UFWORD	advance; /* Advance width/height. */
   FWORD		lsb; /* Leading (left/top) side bearing. */
   public:
   DEFINE_SIZE_STATIC (4);
 };
 
-struct _mtx
+struct hmtxvmtx
 {
-  static const hb_tag_t tableTag = HB_TAG('_','m','t','x');
-
   static const hb_tag_t hmtxTag	= HB_OT_TAG_hmtx;
   static const hb_tag_t vmtxTag	= HB_OT_TAG_vmtx;
 
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     /* We don't check for anything specific here.  The users of the
      * struct do all the hard work... */
@@ -86,19 +84,19 @@ struct _mtx
 					 * run is allowed and it must be at
 					 * the end. This allows a monospaced
 					 * font to vary the side bearing
 					 * values for each glyph. */
   public:
   DEFINE_SIZE_ARRAY2 (0, longMetric, leadingBearingX);
 };
 
-struct hmtx : _mtx {
+struct hmtx : hmtxvmtx {
   static const hb_tag_t tableTag	= HB_OT_TAG_hmtx;
 };
-struct vmtx : _mtx {
+struct vmtx : hmtxvmtx {
   static const hb_tag_t tableTag	= HB_OT_TAG_vmtx;
 };
 
 } /* namespace OT */
 
 
 #endif /* HB_OT_HMTX_TABLE_HH */
--- a/gfx/harfbuzz/src/hb-ot-layout-common-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-common-private.hh
@@ -1318,28 +1318,36 @@ struct VariationStore
     if (unlikely (outer >= dataSets.len))
       return 0.;
 
     return (this+dataSets[outer]).get_delta (inner,
 					     coords, coord_count,
 					     this+regions);
   }
 
+  inline float get_delta (unsigned int index,
+			  int *coords, unsigned int coord_count) const
+  {
+    unsigned int outer = index >> 16;
+    unsigned int inner = index & 0xFFFF;
+    return get_delta (outer, inner, coords, coord_count);
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) &&
 		  format == 1 &&
 		  regions.sanitize (c, this) &&
 		  dataSets.sanitize (c, this));
   }
 
   protected:
   USHORT				format;
-  OffsetTo<VarRegionList, ULONG>	regions;
+  LOffsetTo<VarRegionList>		regions;
   OffsetArrayOf<VarData, ULONG>		dataSets;
   public:
   DEFINE_SIZE_ARRAY (8, dataSets);
 };
 
 /*
  * Feature Variations
  */
@@ -1428,18 +1436,18 @@ struct FeatureTableSubstitutionRecord
 
   inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
     return_trace (c->check_struct (this) && feature.sanitize (c, base));
   }
 
   protected:
-  USHORT			featureIndex;
-  OffsetTo<Feature, ULONG>	feature;
+  USHORT		featureIndex;
+  LOffsetTo<Feature>	feature;
   public:
   DEFINE_SIZE_STATIC (6);
 };
 
 struct FeatureTableSubstitution
 {
   inline const Feature *find_substitute (unsigned int feature_index) const
   {
@@ -1476,19 +1484,19 @@ struct FeatureVariationRecord
   inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
   {
     TRACE_SANITIZE (this);
     return_trace (conditions.sanitize (c, base) &&
 		  substitutions.sanitize (c, base));
   }
 
   protected:
-  OffsetTo<ConditionSet, ULONG>
+  LOffsetTo<ConditionSet>
 			conditions;
-  OffsetTo<FeatureTableSubstitution, ULONG>
+  LOffsetTo<FeatureTableSubstitution>
 			substitutions;
   public:
   DEFINE_SIZE_STATIC (8);
 };
 
 struct FeatureVariations
 {
   static const unsigned int NOT_FOUND_INDEX = 0xFFFFFFFFu;
@@ -1522,17 +1530,17 @@ struct FeatureVariations
     TRACE_SANITIZE (this);
     return_trace (version.sanitize (c) &&
 		  likely (version.major == 1) &&
 		  varRecords.sanitize (c, this));
   }
 
   protected:
   FixedVersion<>	version;	/* Version--0x00010000u */
-  ArrayOf<FeatureVariationRecord, ULONG>
+  LArrayOf<FeatureVariationRecord>
 			varRecords;
   public:
   DEFINE_SIZE_ARRAY (8, varRecords);
 };
 
 
 /*
  * Device Tables
--- a/gfx/harfbuzz/src/hb-ot-layout-gdef-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gdef-table.hh
@@ -290,17 +290,17 @@ struct MarkGlyphSetsFormat1
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
     return_trace (coverage.sanitize (c, this));
   }
 
   protected:
   USHORT	format;			/* Format identifier--format = 1 */
-  ArrayOf<OffsetTo<Coverage, ULONG> >
+  ArrayOf<LOffsetTo<Coverage> >
 		coverage;		/* Array of long offsets to mark set
 					 * coverage tables */
   public:
   DEFINE_SIZE_ARRAY (4, coverage);
 };
 
 struct MarkGlyphSets
 {
@@ -438,17 +438,17 @@ struct GDEF
 		markAttachClassDef;	/* Offset to class definition table for
 					 * mark attachment type--from beginning
 					 * of GDEF header (may be Null) */
   OffsetTo<MarkGlyphSets>
 		markGlyphSetsDef;	/* Offset to the table of mark set
 					 * definitions--from beginning of GDEF
 					 * header (may be NULL).  Introduced
 					 * in version 0x00010002. */
-  OffsetTo<VariationStore, ULONG>
+  LOffsetTo<VariationStore>
 		varStore;		/* Offset to the table of Item Variation
 					 * Store--from beginning of GDEF
 					 * header (may be NULL).  Introduced
 					 * in version 0x00010003. */
   public:
   DEFINE_SIZE_MIN (12);
 };
 
--- a/gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh
@@ -2308,17 +2308,17 @@ struct GSUBGPOS
   FixedVersion<>version;	/* Version of the GSUB/GPOS table--initially set
 				 * to 0x00010000u */
   OffsetTo<ScriptList>
 		scriptList;  	/* ScriptList table */
   OffsetTo<FeatureList>
 		featureList; 	/* FeatureList table */
   OffsetTo<LookupList>
 		lookupList; 	/* LookupList table */
-  OffsetTo<FeatureVariations, ULONG>
+  LOffsetTo<FeatureVariations>
 		featureVars;	/* Offset to Feature Variations
 				   table--from beginning of table
 				 * (may be NULL).  Introduced
 				 * in version 0x00010001. */
   public:
   DEFINE_SIZE_MIN (10);
 };
 
--- a/gfx/harfbuzz/src/hb-ot-layout-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-private.hh
@@ -29,16 +29,17 @@
 #ifndef HB_OT_LAYOUT_PRIVATE_HH
 #define HB_OT_LAYOUT_PRIVATE_HH
 
 #include "hb-private.hh"
 
 #include "hb-font-private.hh"
 #include "hb-buffer-private.hh"
 #include "hb-set-private.hh"
+#include "hb-open-type-private.hh"
 
 
 /* Private API corresponding to hb-ot-layout.h: */
 
 HB_INTERNAL hb_bool_t
 hb_ot_layout_table_find_feature (hb_face_t    *face,
 				 hb_tag_t      table_tag,
 				 hb_tag_t      feature_tag,
@@ -120,16 +121,18 @@ hb_ot_layout_position_finish_offsets (hb
  * hb_ot_layout_t
  */
 
 namespace OT {
   struct GDEF;
   struct GSUB;
   struct GPOS;
   struct MATH;
+  struct fvar;
+  struct avar;
 }
 
 struct hb_ot_layout_lookup_accelerator_t
 {
   template <typename TLookup>
   inline void init (const TLookup &lookup)
   {
     digest.init ();
@@ -148,22 +151,25 @@ struct hb_ot_layout_lookup_accelerator_t
   hb_set_digest_t digest;
 };
 
 struct hb_ot_layout_t
 {
   hb_blob_t *gdef_blob;
   hb_blob_t *gsub_blob;
   hb_blob_t *gpos_blob;
-  hb_blob_t *math_blob;
 
   const struct OT::GDEF *gdef;
   const struct OT::GSUB *gsub;
   const struct OT::GPOS *gpos;
-  const struct OT::MATH *math;
+
+  /* TODO Move the following out of this struct. */
+  OT::hb_lazy_table_loader_t<struct OT::MATH> math;
+  OT::hb_lazy_table_loader_t<struct OT::fvar> fvar;
+  OT::hb_lazy_table_loader_t<struct OT::avar> avar;
 
   unsigned int gsub_lookup_count;
   unsigned int gpos_lookup_count;
 
   hb_ot_layout_lookup_accelerator_t *gsub_accels;
   hb_ot_layout_lookup_accelerator_t *gpos_accels;
 };
 
--- a/gfx/harfbuzz/src/hb-ot-layout.cc
+++ b/gfx/harfbuzz/src/hb-ot-layout.cc
@@ -29,23 +29,20 @@
  */
 
 #include "hb-open-type-private.hh"
 #include "hb-ot-layout-private.hh"
 
 #include "hb-ot-layout-gdef-table.hh"
 #include "hb-ot-layout-gsub-table.hh"
 #include "hb-ot-layout-gpos-table.hh"
-#include "hb-ot-layout-jstf-table.hh"
+#include "hb-ot-layout-jstf-table.hh" // Just so we compile it; unused otherwise.
 
 #include "hb-ot-map-private.hh"
 
-#include <stdlib.h>
-#include <string.h>
-
 
 HB_SHAPER_DATA_ENSURE_DECLARE(ot, face)
 
 hb_ot_layout_t *
 _hb_ot_layout_create (hb_face_t *face)
 {
   hb_ot_layout_t *layout = (hb_ot_layout_t *) calloc (1, sizeof (hb_ot_layout_t));
   if (unlikely (!layout))
@@ -55,19 +52,19 @@ hb_ot_layout_t *
   layout->gdef = OT::Sanitizer<OT::GDEF>::lock_instance (layout->gdef_blob);
 
   layout->gsub_blob = OT::Sanitizer<OT::GSUB>::sanitize (face->reference_table (HB_OT_TAG_GSUB));
   layout->gsub = OT::Sanitizer<OT::GSUB>::lock_instance (layout->gsub_blob);
 
   layout->gpos_blob = OT::Sanitizer<OT::GPOS>::sanitize (face->reference_table (HB_OT_TAG_GPOS));
   layout->gpos = OT::Sanitizer<OT::GPOS>::lock_instance (layout->gpos_blob);
 
-  /* The MATH table is rarely used, so only try and load it in _get_math. */
-  layout->math_blob = NULL;
-  layout->math = NULL;
+  layout->math.init (face);
+  layout->fvar.init (face);
+  layout->avar.init (face);
 
   {
     /*
      * The ugly business of blacklisting individual fonts' tables happen here!
      * See this thread for why we finally had to bend in and do this:
      * https://lists.freedesktop.org/archives/harfbuzz/2016-February/005489.html
      */
     unsigned int gdef_len = hb_blob_get_length (layout->gdef_blob);
@@ -176,17 +173,20 @@ void
     layout->gpos_accels[i].fini ();
 
   free (layout->gsub_accels);
   free (layout->gpos_accels);
 
   hb_blob_destroy (layout->gdef_blob);
   hb_blob_destroy (layout->gsub_blob);
   hb_blob_destroy (layout->gpos_blob);
-  hb_blob_destroy (layout->math_blob);
+
+  layout->math.fini ();
+  layout->fvar.fini ();
+  layout->avar.fini ();
 
   free (layout);
 }
 
 static inline const OT::GDEF&
 _get_gdef (hb_face_t *face)
 {
   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GDEF);
rename from gfx/harfbuzz/src/hb-ot-layout-math-table.hh
rename to gfx/harfbuzz/src/hb-ot-math-table.hh
--- a/gfx/harfbuzz/src/hb-ot-layout-math-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-math-table.hh
@@ -19,18 +19,18 @@
  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Igalia Author(s): Frédéric Wang
  */
 
-#ifndef HB_OT_LAYOUT_MATH_TABLE_HH
-#define HB_OT_LAYOUT_MATH_TABLE_HH
+#ifndef HB_OT_MATH_TABLE_HH
+#define HB_OT_MATH_TABLE_HH
 
 #include "hb-open-type-private.hh"
 #include "hb-ot-layout-common-private.hh"
 #include "hb-ot-math.h"
 
 namespace OT {
 
 
@@ -711,12 +711,12 @@ struct MATH
   OffsetTo<MathConstants> mathConstants;/* MathConstants table */
   OffsetTo<MathGlyphInfo> mathGlyphInfo;/* MathGlyphInfo table */
   OffsetTo<MathVariants>  mathVariants;	/* MathVariants table */
 
   public:
   DEFINE_SIZE_STATIC (10);
 };
 
-} /* mathspace OT */
+} /* namespace OT */
 
 
-#endif /* HB_OT_LAYOUT_MATH_TABLE_HH */
+#endif /* HB_OT_MATH_TABLE_HH */
--- a/gfx/harfbuzz/src/hb-ot-math.cc
+++ b/gfx/harfbuzz/src/hb-ot-math.cc
@@ -21,58 +21,40 @@
  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Igalia Author(s): Frédéric Wang
  */
 
 #include "hb-open-type-private.hh"
 
-#include "hb-ot-layout-math-table.hh"
+#include "hb-ot-math-table.hh"
 
 HB_SHAPER_DATA_ENSURE_DECLARE(ot, face)
 
 static inline const OT::MATH&
 _get_math (hb_face_t *face)
 {
   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::MATH);
-
   hb_ot_layout_t * layout = hb_ot_layout_from_face (face);
-
-retry:
-  const OT::MATH *math = (const OT::MATH *) hb_atomic_ptr_get (&layout->math);
-
-  if (unlikely (!math))
-  {
-    hb_blob_t *blob = OT::Sanitizer<OT::MATH>::sanitize (face->reference_table (HB_OT_TAG_MATH));
-    math = OT::Sanitizer<OT::MATH>::lock_instance (blob);
-    if (!hb_atomic_ptr_cmpexch (&layout->math, NULL, math))
-    {
-      hb_blob_destroy (blob);
-      goto retry;
-    }
-    layout->math_blob = blob;
-  }
-
-  return *math;
+  return *(layout->math.get ());
 }
 
 /*
  * OT::MATH
  */
 
 /**
  * hb_ot_math_has_data:
  * @face: #hb_face_t to test
  *
  * This function allows to verify the presence of an OpenType MATH table on the
- * face. If so, such a table will be loaded into memory and sanitized. You can
- * then safely call other functions for math layout and shaping.
+ * face.
  *
- * Return value: #TRUE if face has a MATH table and #FALSE otherwise
+ * Return value: true if face has a MATH table, false otherwise
  *
  * Since: 1.3.3
  **/
 hb_bool_t
 hb_ot_math_has_data (hb_face_t *face)
 {
   return &_get_math (face) != &OT::Null(OT::MATH);
 }
@@ -131,20 +113,20 @@ hb_ot_math_get_glyph_top_accent_attachme
 					    hb_codepoint_t glyph)
 {
   const OT::MATH &math = _get_math (font->face);
   return math.get_math_glyph_info().get_top_accent_attachment (glyph, font);
 }
 
 /**
  * hb_ot_math_is_glyph_extended_shape:
- * @font: a #hb_font_t to test
+ * @face: a #hb_face_t to test
  * @glyph: a glyph index to test
  *
- * Return value: #TRUE if the glyph is an extended shape and #FALSE otherwise
+ * Return value: true if the glyph is an extended shape, false otherwise
  *
  * Since: 1.3.3
  **/
 hb_bool_t
 hb_ot_math_is_glyph_extended_shape (hb_face_t *face,
 				    hb_codepoint_t glyph)
 {
   const OT::MATH &math = _get_math (face);
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-hangul.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-hangul.cc
@@ -27,17 +27,17 @@
 #include "hb-ot-shape-complex-private.hh"
 
 
 /* Hangul shaper */
 
 
 /* Same order as the feature array below */
 enum {
-  NONE,
+  _JMO,
 
   LJMO,
   VJMO,
   TJMO,
 
   FIRST_HANGUL_FEATURE = LJMO,
   HANGUL_FEATURE_COUNT = TJMO + 1
 };
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-use.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-use.cc
@@ -567,38 +567,16 @@ decompose_use (const hb_ot_shape_normali
   switch (ab)
   {
     /* Chakma:
      * Special case where the Unicode decomp gives matras in the wrong order
      * for cluster validation.
      */
     case 0x1112Eu : *a = 0x11127u; *b= 0x11131u; return true;
     case 0x1112Fu : *a = 0x11127u; *b= 0x11132u; return true;
-
-    /*
-     * Decompose split matras that don't have Unicode decompositions.
-     */
-
-    /* Limbu */
-    case 0x1925u  : *a = 0x1920u; *b= 0x1923u; return true;
-    case 0x1926u  : *a = 0x1920u; *b= 0x1924u; return true;
-
-    /* Balinese */
-    case 0x1B3Cu  : *a = 0x1B42u; *b= 0x1B3Cu; return true;
-
-#if 0
-    /* Lepcha */
-    case 0x1C29u  : *a = no decomp, -> LEFT; return true;
-
-    /* Javanese */
-    case 0xA9C0u  : *a = no decomp, -> RIGHT; return true;
-
-    /* Sharada */
-    case 0x111BFu  : *a = no decomp, -> ABOVE; return true;
-#endif
   }
 
   return (bool) c->unicode->decompose (ab, a, b);
 }
 
 static bool
 compose_use (const hb_ot_shape_normalize_context_t *c,
 	     hb_codepoint_t  a,
--- a/gfx/harfbuzz/src/hb-ot-shape.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape.cc
@@ -357,16 +357,28 @@ static inline void
 hb_ot_shape_setup_masks_fraction (hb_ot_shape_context_t *c)
 {
   if (!(c->buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII) ||
       !c->plan->has_frac)
     return;
 
   hb_buffer_t *buffer = c->buffer;
 
+  hb_mask_t pre_mask, post_mask;
+  if (HB_DIRECTION_IS_FORWARD (buffer->props.direction))
+  {
+    pre_mask = c->plan->numr_mask | c->plan->frac_mask;
+    post_mask = c->plan->frac_mask | c->plan->dnom_mask;
+  }
+  else
+  {
+    pre_mask = c->plan->frac_mask | c->plan->dnom_mask;
+    post_mask = c->plan->numr_mask | c->plan->frac_mask;
+  }
+
   /* TODO look in pre/post context text also. */
   unsigned int count = buffer->len;
   hb_glyph_info_t *info = buffer->info;
   for (unsigned int i = 0; i < count; i++)
   {
     if (info[i].codepoint == 0x2044u) /* FRACTION SLASH */
     {
       unsigned int start = i, end = i + 1;
@@ -375,20 +387,20 @@ hb_ot_shape_setup_masks_fraction (hb_ot_
 	     HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER)
         start--;
       while (end < count &&
 	     _hb_glyph_info_get_general_category (&info[end]) ==
 	     HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER)
         end++;
 
       for (unsigned int j = start; j < i; j++)
-        info[j].mask |= c->plan->numr_mask | c->plan->frac_mask;
+        info[j].mask |= pre_mask;
       info[i].mask |= c->plan->frac_mask;
       for (unsigned int j = i + 1; j < end; j++)
-        info[j].mask |= c->plan->frac_mask | c->plan->dnom_mask;
+        info[j].mask |= post_mask;
 
       i = end - 1;
     }
   }
 }
 
 static inline void
 hb_ot_shape_initialize_masks (hb_ot_shape_context_t *c)
--- a/gfx/harfbuzz/src/hb-ot-tag.cc
+++ b/gfx/harfbuzz/src/hb-ot-tag.cc
@@ -23,19 +23,16 @@
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Red Hat Author(s): Behdad Esfahbod
  * Google Author(s): Behdad Esfahbod, Roozbeh Pournader
  */
 
 #include "hb-private.hh"
 
-#include <string.h>
-
-
 
 /* hb_script_t */
 
 static hb_tag_t
 hb_ot_old_tag_from_script (hb_script_t script)
 {
   /* This seems to be accurate as of end of 2012. */
 
@@ -196,16 +193,17 @@ static const LangTag ot_languages[] = {
   {"aii",	HB_TAG('S','W','A',' ')},	/* Swadaya Aramaic */
   {"aio",	HB_TAG('A','I','O',' ')},	/* Aiton */
   {"aiw",	HB_TAG('A','R','I',' ')},	/* Aari */
   {"ak",	HB_TAG('T','W','I',' ')},	/* Akan [macrolanguage] */
   {"aka",	HB_TAG('A','K','A',' ')},	/* Akan */
   {"alt",	HB_TAG('A','L','T',' ')},	/* [Southern] Altai */
   {"am",	HB_TAG('A','M','H',' ')},	/* Amharic */
   {"amf",	HB_TAG('H','B','N',' ')},	/* Hammer-Banna */
+  {"amw",	HB_TAG('S','Y','R',' ')},	/* Western Neo-Aramaic */
   {"an",	HB_TAG('A','R','G',' ')},	/* Aragonese */
   {"ang",	HB_TAG('A','N','G',' ')},	/* Old English (ca. 450-1100) */
   {"ar",	HB_TAG('A','R','A',' ')},	/* Arabic [macrolanguage] */
   {"arb",	HB_TAG('A','R','A',' ')},	/* Standard Arabic */
   {"arn",	HB_TAG('M','A','P',' ')},	/* Mapudungun */
   {"ary",	HB_TAG('M','O','R',' ')},	/* Moroccan Arabic */
   {"as",	HB_TAG('A','S','M',' ')},	/* Assamese */
   {"ast",	HB_TAG('A','S','T',' ')},	/* Asturian/Asturleonese/Bable/Leonese */
@@ -234,16 +232,17 @@ static const LangTag ot_languages[] = {
   {"ber",	HB_TAG('B','E','R',' ')},	/* Berber [family] */
   {"bfq",	HB_TAG('B','A','D',' ')},	/* Badaga */
   {"bft",	HB_TAG('B','L','T',' ')},	/* Balti */
   {"bfu",	HB_TAG('L','A','H',' ')},	/* Lahuli */
   {"bfy",	HB_TAG('B','A','G',' ')},	/* Baghelkhandi */
   {"bg",	HB_TAG('B','G','R',' ')},	/* Bulgarian */
   {"bgc",	HB_TAG('B','G','C',' ')},	/* Haryanvi */
   {"bgq",	HB_TAG('B','G','Q',' ')},	/* Bagri */
+  {"bgr",	HB_TAG('Q','I','N',' ')},	/* Bawm Chin */
   {"bhb",	HB_TAG('B','H','I',' ')},	/* Bhili */
   {"bhk",	HB_TAG('B','I','K',' ')},	/* Albay Bicolano (retired code) */
   {"bho",	HB_TAG('B','H','O',' ')},	/* Bhojpuri */
   {"bi",	HB_TAG('B','I','S',' ')},	/* Bislama */
   {"bik",	HB_TAG('B','I','K',' ')},	/* Bikol [macrolanguage] */
   {"bin",	HB_TAG('E','D','O',' ')},	/* Bini */
   {"bjj",	HB_TAG('B','J','J',' ')},	/* Kanauji */
   {"bjt",	HB_TAG('B','L','N',' ')},	/* Balanta-Ganja */
@@ -265,47 +264,63 @@ static const LangTag ot_languages[] = {
   {"bto",	HB_TAG('B','I','K',' ')},	/* Rinconada Bikol */
   {"bts",	HB_TAG('B','T','S',' ')},	/* Batak Simalungun */
   {"bug",	HB_TAG('B','U','G',' ')},	/* Buginese */
   {"bxr",	HB_TAG('R','B','U',' ')},	/* Russian Buriat */
   {"byn",	HB_TAG('B','I','L',' ')},	/* Bilen */
   {"ca",	HB_TAG('C','A','T',' ')},	/* Catalan */
   {"cak",	HB_TAG('C','A','K',' ')},	/* Kaqchikel */
   {"cbk",	HB_TAG('C','B','K',' ')},	/* Chavacano */
+  {"cbl",	HB_TAG('Q','I','N',' ')},	/* Bualkhaw Chin */
   {"ce",	HB_TAG('C','H','E',' ')},	/* Chechen */
   {"ceb",	HB_TAG('C','E','B',' ')},	/* Cebuano */
+  {"cfm",	HB_TAG('H','A','L',' ')},	/* Halam/Falam Chin */
   {"cgg",	HB_TAG('C','G','G',' ')},	/* Chiga */
   {"ch",	HB_TAG('C','H','A',' ')},	/* Chamorro */
   {"chk",	HB_TAG('C','H','K','0')},	/* Chuukese */
   {"cho",	HB_TAG('C','H','O',' ')},	/* Choctaw */
   {"chp",	HB_TAG('C','H','P',' ')},	/* Chipewyan */
   {"chr",	HB_TAG('C','H','R',' ')},	/* Cherokee */
   {"chy",	HB_TAG('C','H','Y',' ')},	/* Cheyenne */
+  {"cja",	HB_TAG('C','J','A',' ')},	/* Western Cham */
+  {"cjm",	HB_TAG('C','J','M',' ')},	/* Eastern Cham */
+  {"cka",	HB_TAG('Q','I','N',' ')},	/* Khumi Awa Chin */
   {"ckb",	HB_TAG('K','U','R',' ')},	/* Central Kurdish (Sorani) */
   {"ckt",	HB_TAG('C','H','K',' ')},	/* Chukchi */
+  {"cld",	HB_TAG('S','Y','R',' ')},	/* Chaldean Neo-Aramaic */
+  {"cmr",	HB_TAG('Q','I','N',' ')},	/* Mro-Khimi Chin */
+  {"cnb",	HB_TAG('Q','I','N',' ')},	/* Chinbon Chin */
+  {"cnh",	HB_TAG('Q','I','N',' ')},	/* Hakha Chin */
+  {"cnk",	HB_TAG('Q','I','N',' ')},	/* Khumi Chin */
+  {"cnw",	HB_TAG('Q','I','N',' ')},	/* Ngawn Chin */
   {"cop",	HB_TAG('C','O','P',' ')},	/* Coptic */
   {"cpp",	HB_TAG('C','P','P',' ')},	/* Creoles */
   {"cr",	HB_TAG('C','R','E',' ')},	/* Cree */
   {"cre",	HB_TAG('Y','C','R',' ')},	/* Y-Cree */
   {"crh",	HB_TAG('C','R','T',' ')},	/* Crimean Tatar */
   {"crj",	HB_TAG('E','C','R',' ')},	/* [Southern] East Cree */
   {"crk",	HB_TAG('W','C','R',' ')},	/* West-Cree */
   {"crl",	HB_TAG('E','C','R',' ')},	/* [Northern] East Cree */
   {"crm",	HB_TAG('M','C','R',' ')},	/* Moose Cree */
   {"crx",	HB_TAG('C','R','R',' ')},	/* Carrier */
   {"cs",	HB_TAG('C','S','Y',' ')},	/* Czech */
   {"csb",	HB_TAG('C','S','B',' ')},	/* Kashubian */
+  {"csh",	HB_TAG('Q','I','N',' ')},	/* Asho Chin */
+  {"csy",	HB_TAG('Q','I','N',' ')},	/* Siyin Chin */
+  {"ctd",	HB_TAG('Q','I','N',' ')},	/* Tedim Chin */
   {"ctg",	HB_TAG('C','T','G',' ')},	/* Chittagonian */
   {"cts",	HB_TAG('B','I','K',' ')},	/* Northern Catanduanes Bikol */
   {"cu",	HB_TAG('C','S','L',' ')},	/* Church Slavic */
   {"cuk",	HB_TAG('C','U','K',' ')},	/* San Blas Kuna */
   {"cv",	HB_TAG('C','H','U',' ')},	/* Chuvash */
   {"cwd",	HB_TAG('D','C','R',' ')},	/* Woods Cree */
   {"cy",	HB_TAG('W','E','L',' ')},	/* Welsh */
+  {"czt",	HB_TAG('Q','I','N',' ')},	/* Zotung Chin */
   {"da",	HB_TAG('D','A','N',' ')},	/* Danish */
+  {"dao",	HB_TAG('Q','I','N',' ')},	/* Daai Chin */
   {"dap",	HB_TAG('N','I','S',' ')},	/* Nisi (India) */
   {"dar",	HB_TAG('D','A','R',' ')},	/* Dargwa */
   {"dax",	HB_TAG('D','A','X',' ')},	/* Dayi */
   {"de",	HB_TAG('D','E','U',' ')},	/* German */
   {"dgo",	HB_TAG('D','G','O',' ')},	/* Dogri */
   {"dhd",	HB_TAG('M','A','W',' ')},	/* Dhundari */
   {"dhg",	HB_TAG('D','H','G',' ')},	/* Dhangu */
   {"din",	HB_TAG('D','N','K',' ')},	/* Dinka [macrolanguage] */
@@ -338,17 +353,17 @@ static const LangTag ot_languages[] = {
   {"evn",	HB_TAG('E','V','K',' ')},	/* Evenki */
   {"fa",	HB_TAG('F','A','R',' ')},	/* Persian [macrolanguage] */
   {"fan",	HB_TAG('F','A','N','0')},	/* Fang */
   {"fat",	HB_TAG('F','A','T',' ')},	/* Fanti */
   {"ff",	HB_TAG('F','U','L',' ')},	/* Fulah [macrolanguage] */
   {"fi",	HB_TAG('F','I','N',' ')},	/* Finnish */
   {"fil",	HB_TAG('P','I','L',' ')},	/* Filipino */
   {"fj",	HB_TAG('F','J','I',' ')},	/* Fijian */
-  {"flm",	HB_TAG('H','A','L',' ')},	/* Halam */
+  {"flm",	HB_TAG('H','A','L',' ')},	/* Halam/Falam Chin [retired ISO639 code] */
   {"fo",	HB_TAG('F','O','S',' ')},	/* Faroese */
   {"fon",	HB_TAG('F','O','N',' ')},	/* Fon */
   {"fr",	HB_TAG('F','R','A',' ')},	/* French */
   {"frc",	HB_TAG('F','R','C',' ')},	/* Cajun French */
   {"frp",	HB_TAG('F','R','P',' ')},	/* Arpitan/Francoprovençal */
   {"fuf",	HB_TAG('F','T','A',' ')},	/* Futa */
   {"fur",	HB_TAG('F','R','L',' ')},	/* Friulian */
   {"fuv",	HB_TAG('F','U','V',' ')},	/* Nigerian Fulfulde */
@@ -385,16 +400,17 @@ static const LangTag ot_languages[] = {
   {"ha",	HB_TAG('H','A','U',' ')},	/* Hausa */
   {"har",	HB_TAG('H','R','I',' ')},	/* Harari */
   {"haw",	HB_TAG('H','A','W',' ')},	/* Hawaiian */
   {"hay",	HB_TAG('H','A','Y',' ')},	/* Haya */
   {"haz",	HB_TAG('H','A','Z',' ')},	/* Hazaragi */
   {"he",	HB_TAG('I','W','R',' ')},	/* Hebrew */
   {"hi",	HB_TAG('H','I','N',' ')},	/* Hindi */
   {"hil",	HB_TAG('H','I','L',' ')},	/* Hiligaynon */
+  {"hlt",	HB_TAG('Q','I','N',' ')},	/* Matu Chin */
   {"hmn",	HB_TAG('H','M','N',' ')},	/* Hmong */
   {"hnd",	HB_TAG('H','N','D',' ')},	/* [Southern] Hindko */
   {"hne",	HB_TAG('C','H','H',' ')},	/* Chattisgarhi */
   {"hno",	HB_TAG('H','N','D',' ')},	/* [Northern] Hindko */
   {"ho",	HB_TAG('H','M','O',' ')},	/* Hiri Motu */
   {"hoc",	HB_TAG('H','O',' ',' ')},	/* Ho */
   {"hoj",	HB_TAG('H','A','R',' ')},	/* Harauti */
   {"hr",	HB_TAG('H','R','V',' ')},	/* Croatian */
@@ -548,16 +564,17 @@ static const LangTag ot_languages[] = {
   {"mnk",	HB_TAG('M','N','D',' ')},	/* Mandinka */
   {"mns",	HB_TAG('M','A','N',' ')},	/* Mansi */
   {"mnw",	HB_TAG('M','O','N',' ')},	/* Mon */
   {"mo",	HB_TAG('M','O','L',' ')},	/* Moldavian */
   {"moh",	HB_TAG('M','O','H',' ')},	/* Mohawk */
   {"mos",	HB_TAG('M','O','S',' ')},	/* Mossi */
   {"mpe",	HB_TAG('M','A','J',' ')},	/* Majang */
   {"mr",	HB_TAG('M','A','R',' ')},	/* Marathi */
+  {"mrh",	HB_TAG('Q','I','N',' ')},	/* Mara Chin */
   {"mrj",	HB_TAG('H','M','A',' ')},	/* High Mari */
   {"ms",	HB_TAG('M','L','Y',' ')},	/* Malay [macrolanguage] */
   {"msc",	HB_TAG('M','N','K',' ')},	/* Sankaran Maninka */
   {"mt",	HB_TAG('M','T','S',' ')},	/* Maltese */
   {"mtr",	HB_TAG('M','A','W',' ')},	/* Mewari */
   {"mus",	HB_TAG('M','U','S',' ')},	/* Creek */
   {"mve",	HB_TAG('M','A','W',' ')},	/* Marwari (Pakistan) */
   {"mwk",	HB_TAG('M','N','K',' ')},	/* Kita Maninkakan */
@@ -612,16 +629,17 @@ static const LangTag ot_languages[] = {
   {"pa",	HB_TAG('P','A','N',' ')},	/* Panjabi */
   {"pag",	HB_TAG('P','A','G',' ')},	/* Pangasinan */
   {"pam",	HB_TAG('P','A','M',' ')},	/* Kapampangan/Pampanga */
   {"pap",	HB_TAG('P','A','P','0')},	/* Papiamento */
   {"pau",	HB_TAG('P','A','U',' ')},	/* Palauan */
   {"pcc",	HB_TAG('P','C','C',' ')},	/* Bouyei */
   {"pcd",	HB_TAG('P','C','D',' ')},	/* Picard */
   {"pce",	HB_TAG('P','L','G',' ')},	/* [Ruching] Palaung */
+  {"pck",	HB_TAG('Q','I','N',' ')},	/* Paite Chin */
   {"pdc",	HB_TAG('P','D','C',' ')},	/* Pennsylvania German */
   {"pes",	HB_TAG('F','A','R',' ')},	/* Iranian Persian */
   {"phk",	HB_TAG('P','H','K',' ')},	/* Phake */
   {"pi",	HB_TAG('P','A','L',' ')},	/* Pali */
   {"pih",	HB_TAG('P','I','H',' ')},	/* Pitcairn-Norfolk */
   {"pl",	HB_TAG('P','L','K',' ')},	/* Polish */
   {"pll",	HB_TAG('P','L','G',' ')},	/* [Shwe] Palaung */
   {"plp",	HB_TAG('P','A','P',' ')},	/* Palpa */
@@ -669,16 +687,17 @@ static const LangTag ot_languages[] = {
   {"sck",	HB_TAG('S','A','D',' ')},	/* Sadri */
   {"scn",	HB_TAG('S','C','N',' ')},	/* Sicilian */
   {"sco",	HB_TAG('S','C','O',' ')},	/* Scots */
   {"scs",	HB_TAG('S','L','A',' ')},	/* [North] Slavey */
   {"sd",	HB_TAG('S','N','D',' ')},	/* Sindhi */
   {"se",	HB_TAG('N','S','M',' ')},	/* Northern Sami */
   {"seh",	HB_TAG('S','N','A',' ')},	/* Sena */
   {"sel",	HB_TAG('S','E','L',' ')},	/* Selkup */
+  {"sez",	HB_TAG('Q','I','N',' ')},	/* Senthang Chin */
   {"sg",	HB_TAG('S','G','O',' ')},	/* Sango */
   {"sga",	HB_TAG('S','G','A',' ')},	/* Old Irish (to 900) */
   {"sgs",	HB_TAG('S','G','S',' ')},	/* Samogitian */
   {"sgw",	HB_TAG('C','H','G',' ')},	/* Sebat Bet Gurage */
 /*{"sgw",	HB_TAG('S','G','W',' ')},*/	/* Sebat Bet Gurage (in SIL fonts) */
   {"shi",	HB_TAG('S','H','I',' ')},	/* Tachelhit */
   {"shn",	HB_TAG('S','H','N',' ')},	/* Shan */
   {"si",	HB_TAG('S','N','H',' ')},	/* Sinhala */
@@ -708,22 +727,25 @@ static const LangTag ot_languages[] = {
   {"suq",	HB_TAG('S','U','R',' ')},	/* Suri */
   {"sv",	HB_TAG('S','V','E',' ')},	/* Swedish */
   {"sva",	HB_TAG('S','V','A',' ')},	/* Svan */
   {"sw",	HB_TAG('S','W','K',' ')},	/* Swahili [macrolanguage] */
   {"swb",	HB_TAG('C','M','R',' ')},	/* Comorian */
   {"swh",	HB_TAG('S','W','K',' ')},	/* Kiswahili/Swahili */
   {"swv",	HB_TAG('M','A','W',' ')},	/* Shekhawati */
   {"sxu",	HB_TAG('S','X','U',' ')},	/* Upper Saxon */
+  {"syc",	HB_TAG('S','Y','R',' ')},	/* Classical Syriac */
   {"syl",	HB_TAG('S','Y','L',' ')},	/* Sylheti */
   {"syr",	HB_TAG('S','Y','R',' ')},	/* Syriac [macrolanguage] */
   {"szl",	HB_TAG('S','Z','L',' ')},	/* Silesian */
   {"ta",	HB_TAG('T','A','M',' ')},	/* Tamil */
   {"tab",	HB_TAG('T','A','B',' ')},	/* Tabasaran */
+  {"tcp",	HB_TAG('Q','I','N',' ')},	/* Tawr Chin */
   {"tcy",	HB_TAG('T','U','L',' ')},	/* Tulu */
+  {"tcz",	HB_TAG('Q','I','N',' ')},	/* Thado Chin */
   {"tdd",	HB_TAG('T','D','D',' ')},	/* Tai Nüa */
   {"te",	HB_TAG('T','E','L',' ')},	/* Telugu */
   {"tem",	HB_TAG('T','M','N',' ')},	/* Temne */
   {"tet",	HB_TAG('T','E','T',' ')},	/* Tetum */
   {"tg",	HB_TAG('T','A','J',' ')},	/* Tajik */
   {"th",	HB_TAG('T','H','A',' ')},	/* Thai */
   {"ti",	HB_TAG('T','G','Y',' ')},	/* Tigrinya */
   {"tig",	HB_TAG('T','G','R',' ')},	/* Tigre */
@@ -781,21 +803,23 @@ static const LangTag ot_languages[] = {
   {"xpe",	HB_TAG('X','P','E',' ')},	/* Kpelle (Liberia) */
   {"xsl",	HB_TAG('S','S','L',' ')},	/* South Slavey */
   {"xst",	HB_TAG('S','I','G',' ')},	/* Silt'e (retired code) */
   {"xwo",	HB_TAG('T','O','D',' ')},	/* Written Oirat (Todo) */
   {"yao",	HB_TAG('Y','A','O',' ')},	/* Yao */
   {"yap",	HB_TAG('Y','A','P',' ')},	/* Yapese */
   {"yi",	HB_TAG('J','I','I',' ')},	/* Yiddish [macrolanguage] */
   {"yo",	HB_TAG('Y','B','A',' ')},	/* Yoruba */
+  {"yos",	HB_TAG('Q','I','N',' ')},	/* Yos, deprecated by IANA in favor of Zou [zom] */
   {"yso",	HB_TAG('N','I','S',' ')},	/* Nisi (China) */
   {"za",	HB_TAG('Z','H','A',' ')},	/* Chuang/Zhuang [macrolanguage] */
   {"zea",	HB_TAG('Z','E','A',' ')},	/* Zeeuws */
   {"zgh",	HB_TAG('Z','G','H',' ')},	/* Standard Morrocan Tamazigh */
   {"zne",	HB_TAG('Z','N','D',' ')},	/* Zande */
+  {"zom",	HB_TAG('Q','I','N',' ')},	/* Zou */
   {"zu",	HB_TAG('Z','U','L',' ')}, 	/* Zulu */
   {"zum",	HB_TAG('L','R','C',' ')},	/* Kumzari */
   {"zza",	HB_TAG('Z','Z','A',' ')},	/* Zazaki */
 
   /* The corresponding languages IDs for the following IDs are unclear,
    * overlap, or are architecturally weird. Needs more research. */
 
 /*{"chp",	HB_TAG('S','A','Y',' ')},*/	/* Sayisi */
@@ -902,16 +926,40 @@ hb_ot_tag_from_language (hb_language_t l
   /*
    * "fonnapa" is a variant tag in BCP-47, meaning the North American Phonetic Alphabet
    * also known as Americanist Phonetic Notation.  It can be applied to any language.
    */
   if (strstr (lang_str, "-fonnapa")) {
     return HB_TAG('A','P','P','H');  /* Phonetic transcription—Americanist conventions */
   }
 
+  /*
+   * "Syre" is a BCP-47 script tag, meaning the Estrangela variant of the Syriac script.
+   * It can be applied to any language.
+   */
+  if (strstr (lang_str, "-syre")) {
+    return HB_TAG('S','Y','R','E');  /* Estrangela Syriac */
+  }
+
+  /*
+   * "Syrj" is a BCP-47 script tag, meaning the Western variant of the Syriac script.
+   * It can be applied to any language.
+   */
+  if (strstr (lang_str, "-syrj")) {
+    return HB_TAG('S','Y','R','J');  /* Western Syriac */
+  }
+
+  /*
+   * "Syrn" is a BCP-47 script tag, meaning the Eastern variant of the Syriac script.
+   * It can be applied to any language.
+   */
+  if (strstr (lang_str, "-syrn")) {
+    return HB_TAG('S','Y','R','N');  /* Eastern Syriac */
+  }
+
   /* Find a language matching in the first component */
   {
     const LangTag *lang_tag;
     lang_tag = (LangTag *) bsearch (lang_str, ot_languages,
 				    ARRAY_LENGTH (ot_languages), sizeof (LangTag),
 				    (hb_compare_func_t) lang_compare_first_component);
     if (lang_tag)
       return lang_tag->tag;
@@ -957,38 +1005,46 @@ hb_ot_tag_from_language (hb_language_t l
 hb_language_t
 hb_ot_tag_to_language (hb_tag_t tag)
 {
   unsigned int i;
 
   if (tag == HB_OT_TAG_DEFAULT_LANGUAGE)
     return NULL;
 
+  /* struct LangTag has only room for 3-letter language tags. */
+  switch (tag) {
+  case HB_TAG('A','P','P','H'):  /* Phonetic transcription—Americanist conventions */
+    return hb_language_from_string ("und-fonnapa", -1);
+  case HB_TAG('I','P','P','H'):  /* Phonetic transcription—IPA conventions */
+    return hb_language_from_string ("und-fonipa", -1);
+  case HB_TAG('S','Y','R',' '):  /* Syriac [macrolanguage] */
+    return hb_language_from_string ("syr", -1);
+  case HB_TAG('S','Y','R','E'):  /* Estrangela Syriac */
+    return hb_language_from_string ("und-Syre", -1);
+  case HB_TAG('S','Y','R','J'):  /* Western Syriac */
+    return hb_language_from_string ("und-Syrj", -1);
+  case HB_TAG('S','Y','R','N'):  /* Eastern Syriac */
+    return hb_language_from_string ("und-Syrn", -1);
+  }
+
   for (i = 0; i < ARRAY_LENGTH (ot_languages); i++)
     if (ot_languages[i].tag == tag)
       return hb_language_from_string (ot_languages[i].language, -1);
 
   /* If tag starts with ZH, it's Chinese */
   if ((tag & 0xFFFF0000u)  == 0x5A480000u) {
     switch (tag) {
       case HB_TAG('Z','H','H',' '): return hb_language_from_string ("zh-hk", -1); /* Hong Kong */
       case HB_TAG('Z','H','S',' '): return hb_language_from_string ("zh-Hans", -1); /* Simplified */
       case HB_TAG('Z','H','T',' '): return hb_language_from_string ("zh-Hant", -1); /* Traditional */
       default: break; /* Fall through */
     }
   }
 
-  /* struct LangTag has only room for 3-letter language tags. */
-  switch (tag) {
-  case HB_TAG('A','P','P','H'):  /* Phonetic transcription—Americanist conventions */
-    return hb_language_from_string ("und-fonnapa", -1);
-  case HB_TAG('I','P','P','H'):  /* Phonetic transcription—IPA conventions */
-    return hb_language_from_string ("und-fonipa", -1);
-  }
-
   /* Else return a custom language in the form of "x-hbotABCD" */
   {
     unsigned char buf[11] = "x-hbot";
     buf[6] = tag >> 24;
     buf[7] = (tag >> 16) & 0xFF;
     buf[8] = (tag >> 8) & 0xFF;
     buf[9] = tag & 0xFF;
     if (buf[9] == 0x20)
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-ot-var-avar-table.hh
@@ -0,0 +1,144 @@
+/*
+ * Copyright © 2017  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_VAR_AVAR_TABLE_HH
+#define HB_OT_VAR_AVAR_TABLE_HH
+
+#include "hb-open-type-private.hh"
+
+namespace OT {
+
+
+struct AxisValueMap
+{
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  public:
+  F2DOT14	fromCoord;	/* A normalized coordinate value obtained using
+				 * default normalization. */
+  F2DOT14	toCoord;	/* The modified, normalized coordinate value. */
+
+  public:
+  DEFINE_SIZE_STATIC (4);
+};
+
+struct SegmentMaps : ArrayOf<AxisValueMap>
+{
+  inline int map (int value) const
+  {
+    /* The following special-cases are not part of OpenType, which requires
+     * that at least -1, 0, and +1 must be mapped. But we include these as
+     * part of a better error recovery scheme. */
+
+    if (!len)
+      return value;
+
+    if (value <= array[0].fromCoord)
+      return value - array[0].fromCoord + array[0].toCoord;
+
+    unsigned int i;
+    unsigned int count = len;
+    for (i = 1; i < count && value > array[i].fromCoord; i++)
+      ;
+
+    if (value >= array[i].fromCoord)
+      return value - array[i].fromCoord + array[i].toCoord;
+
+    if (unlikely (array[i-1].fromCoord == array[i].fromCoord))
+      return array[i-1].toCoord;
+
+    int denom = array[i].fromCoord - array[i-1].fromCoord;
+    return array[i-1].toCoord +
+	   (array[i].toCoord - array[i-1].toCoord) *
+	   (value - array[i-1].fromCoord + denom/2) / denom;
+  }
+
+  DEFINE_SIZE_ARRAY (2, array);
+};
+
+/*
+ * avar — Axis Variations Table
+ */
+
+#define HB_OT_TAG_avar HB_TAG('a','v','a','r')
+
+struct avar
+{
+  static const hb_tag_t tableTag	= HB_OT_TAG_avar;
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    if (unlikely (!(version.sanitize (c) &&
+		    version.major == 1 &&
+		    c->check_struct (this))))
+      return_trace (false);
+
+    const SegmentMaps *map = &axisSegmentMapsZ;
+    unsigned int count = axisCount;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      if (unlikely (!map->sanitize (c)))
+        return_trace (false);
+      map = &StructAfter<SegmentMaps> (*map);
+    }
+
+    return_trace (true);
+  }
+
+  inline void map_coords (int *coords, unsigned int coords_length) const
+  {
+    unsigned int count = MIN<unsigned int> (coords_length, axisCount);
+
+    const SegmentMaps *map = &axisSegmentMapsZ;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      coords[i] = map->map (coords[i]);
+      map = &StructAfter<SegmentMaps> (*map);
+    }
+  }
+
+  protected:
+  FixedVersion<>version;	/* Version of the avar table
+				 * initially set to 0x00010000u */
+  USHORT	reserved;	/* This field is permanently reserved. Set to 0. */
+  USHORT	axisCount;	/* The number of variation axes in the font. This
+				 * must be the same number as axisCount in the
+				 * 'fvar' table. */
+  SegmentMaps	axisSegmentMapsZ;
+
+  public:
+  DEFINE_SIZE_MIN (8);
+};
+
+} /* namespace OT */
+
+
+#endif /* HB_OT_VAR_AVAR_TABLE_HH */
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-ot-var-fvar-table.hh
@@ -0,0 +1,209 @@
+/*
+ * Copyright © 2017  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_VAR_FVAR_TABLE_HH
+#define HB_OT_VAR_FVAR_TABLE_HH
+
+#include "hb-open-type-private.hh"
+
+namespace OT {
+
+
+struct InstanceRecord
+{
+  inline bool sanitize (hb_sanitize_context_t *c, unsigned int axis_count) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  c->check_array (coordinates, coordinates[0].static_size, axis_count));
+  }
+
+  protected:
+  USHORT	subfamilyNameID;/* The name ID for entries in the 'name' table
+				 * that provide subfamily names for this instance. */
+  USHORT	reserved;	/* Reserved for future use — set to 0. */
+  Fixed		coordinates[VAR];/* The coordinates array for this instance. */
+  //USHORT	postScriptNameIDX;/*Optional. The name ID for entries in the 'name'
+  //				  * table that provide PostScript names for this
+  //				  * instance. */
+
+  public:
+  DEFINE_SIZE_ARRAY (4, coordinates);
+};
+
+struct AxisRecord
+{
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  public:
+  Tag		axisTag;	/* Tag identifying the design variation for the axis. */
+  Fixed		minValue;	/* The minimum coordinate value for the axis. */
+  Fixed		defaultValue;	/* The default coordinate value for the axis. */
+  Fixed		maxValue;	/* The maximum coordinate value for the axis. */
+  USHORT	reserved;	/* Reserved for future use — set to 0. */
+  USHORT	axisNameID;	/* The name ID for entries in the 'name' table that
+				 * provide a display name for this axis. */
+
+  public:
+  DEFINE_SIZE_STATIC (20);
+};
+
+
+/*
+ * fvar — Font Variations Table
+ */
+
+#define HB_OT_TAG_fvar HB_TAG('f','v','a','r')
+
+struct fvar
+{
+  static const hb_tag_t tableTag	= HB_OT_TAG_fvar;
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (version.sanitize (c) &&
+		  likely (version.major == 1) &&
+		  c->check_struct (this) &&
+		  instanceSize >= axisCount * 4 + 4 &&
+		  axisSize <= 1024 && /* Arbitrary, just to simplify overflow checks. */
+		  instanceSize <= 1024 && /* Arbitrary, just to simplify overflow checks. */
+		  c->check_range (this, things) &&
+		  c->check_range (&StructAtOffset<char> (this, things),
+				  axisCount * axisSize + instanceCount * instanceSize));
+  }
+
+  inline unsigned int get_axis_count (void) const
+  { return axisCount; }
+
+  inline bool get_axis (unsigned int index, hb_ot_var_axis_t *info) const
+  {
+    if (unlikely (index >= axisCount))
+      return false;
+
+    if (info)
+    {
+      const AxisRecord &axis = get_axes ()[index];
+      info->tag = axis.axisTag;
+      info->name_id =  axis.axisNameID;
+      info->default_value = axis.defaultValue / 65536.;
+      /* Ensure order, to simplify client math. */
+      info->min_value = MIN<float> (info->default_value, axis.minValue / 65536.);
+      info->max_value = MAX<float> (info->default_value, axis.maxValue / 65536.);
+    }
+
+    return true;
+  }
+
+  inline unsigned int get_axis_infos (unsigned int      start_offset,
+				      unsigned int     *axes_count /* IN/OUT */,
+				      hb_ot_var_axis_t *axes_array /* OUT */) const
+  {
+    if (axes_count)
+    {
+      unsigned int count = axisCount;
+      start_offset = MIN (start_offset, count);
+
+      count -= start_offset;
+      axes_array += start_offset;
+
+      count = MIN (count, *axes_count);
+      *axes_count = count;
+
+      for (unsigned int i = 0; i < count; i++)
+	get_axis (start_offset + i, axes_array + i);
+    }
+    return axisCount;
+  }
+
+  inline bool find_axis (hb_tag_t tag, unsigned int *index, hb_ot_var_axis_t *info) const
+  {
+    const AxisRecord *axes = get_axes ();
+    unsigned int count = get_axis_count ();
+    for (unsigned int i = 0; i < count; i++)
+      if (axes[i].axisTag == tag)
+      {
+        if (index)
+	  *index = i;
+	return get_axis (i, info);
+      }
+    if (index)
+      *index = HB_OT_VAR_NO_AXIS_INDEX;
+    return false;
+  }
+
+  inline int normalize_axis_value (unsigned int axis_index, float v) const
+  {
+    hb_ot_var_axis_t axis;
+    if (!get_axis (axis_index, &axis))
+      return 0;
+
+    v = MAX (MIN (v, axis.max_value), axis.min_value); /* Clamp. */
+
+    if (v == axis.default_value)
+      return 0;
+    else if (v < axis.default_value)
+      v = (v - axis.default_value) / (axis.default_value - axis.min_value);
+    else
+      v = (v - axis.default_value) / (axis.max_value - axis.default_value);
+    return (int) (v * 16384. + (v >= 0. ? .5 : -.5));
+  }
+
+  protected:
+  inline const AxisRecord * get_axes (void) const
+  { return &StructAtOffset<AxisRecord> (this, things); }
+
+  inline const InstanceRecord * get_instances (void) const
+  { return &StructAtOffset<InstanceRecord> (get_axes () + axisCount, 0); }
+
+  protected:
+  FixedVersion<>version;	/* Version of the fvar table
+				 * initially set to 0x00010000u */
+  Offset<>	things;		/* Offset in bytes from the beginning of the table
+				 * to the start of the AxisRecord array. */
+  USHORT	reserved;	/* This field is permanently reserved. Set to 2. */
+  USHORT	axisCount;	/* The number of variation axes in the font (the
+				 * number of records in the axes array). */
+  USHORT	axisSize;	/* The size in bytes of each VariationAxisRecord —
+				 * set to 20 (0x0014) for this version. */
+  USHORT	instanceCount;	/* The number of named instances defined in the font
+				 * (the number of records in the instances array). */
+  USHORT	instanceSize;	/* The size in bytes of each InstanceRecord — set
+				 * to either axisCount * sizeof(Fixed) + 4, or to
+				 * axisCount * sizeof(Fixed) + 6. */
+
+  public:
+  DEFINE_SIZE_STATIC (16);
+};
+
+} /* namespace OT */
+
+
+#endif /* HB_OT_VAR_FVAR_TABLE_HH */
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-ot-var-hvar-table.hh
@@ -0,0 +1,165 @@
+/*
+ * Copyright © 2017  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_VAR_HVAR_TABLE_HH
+#define HB_OT_VAR_HVAR_TABLE_HH
+
+#include "hb-ot-layout-common-private.hh"
+
+
+namespace OT {
+
+
+struct DeltaSetIndexMap
+{
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+		  c->check_array (mapData, get_width (), mapCount));
+  }
+
+  unsigned int map (unsigned int v) const /* Returns 16.16 outer.inner. */
+  {
+    /* If count is zero, pass value unchanged.  This takes
+     * care of direct mapping for advance map. */
+    if (!mapCount)
+      return v;
+
+    if (v >= mapCount)
+      v = mapCount - 1;
+
+    unsigned int u = 0;
+    { /* Fetch it. */
+      unsigned int w = get_width ();
+      const BYTE *p = mapData + w * v;
+      for (; w; w--)
+	u = (u << 8) + *p++;
+    }
+
+    { /* Repack it. */
+      unsigned int n = get_inner_bitcount ();
+      unsigned int outer = u >> n;
+      unsigned int inner = u & ((1 << n) - 1);
+      u = (outer<<16) | inner;
+    }
+
+    return u;
+  }
+
+  protected:
+  inline unsigned int get_width (void) const
+  { return ((format >> 4) & 3) + 1; }
+
+  inline unsigned int get_inner_bitcount (void) const
+  { return (format & 0xF) + 1; }
+
+  protected:
+  USHORT format;		/* A packed field that describes the compressed
+				 * representation of delta-set indices. */
+  USHORT mapCount;		/* The number of mapping entries. */
+  BYTE mapData[VAR];		/* The delta-set index mapping data. */
+
+  public:
+  DEFINE_SIZE_ARRAY (4, mapData);
+};
+
+
+/*
+ * HVAR -- The Horizontal Metrics Variations Table
+ * VVAR -- The Vertical Metrics Variations Table
+ */
+
+#define HB_OT_TAG_HVAR HB_TAG('H','V','A','R')
+#define HB_OT_TAG_VVAR HB_TAG('V','V','A','R')
+
+struct HVARVVAR
+{
+  static const hb_tag_t HVARTag	= HB_OT_TAG_HVAR;
+  static const hb_tag_t VVARTag	= HB_OT_TAG_VVAR;
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (version.sanitize (c) &&
+		  likely (version.major == 1) &&
+		  varStore.sanitize (c, this) &&
+		  advMap.sanitize (c, this) &&
+		  lsbMap.sanitize (c, this) &&
+		  rsbMap.sanitize (c, this));
+  }
+
+  inline float get_advance_var (hb_codepoint_t glyph,
+				int *coords, unsigned int coord_count) const
+  {
+    unsigned int varidx = (this+advMap).map (glyph);
+    return (this+varStore).get_delta (varidx, coords, coord_count);
+  }
+
+  inline bool has_sidebearing_deltas (void) const
+  { return lsbMap && rsbMap; }
+
+  protected:
+  FixedVersion<>version;	/* Version of the metrics variation table
+				 * initially set to 0x00010000u */
+  LOffsetTo<VariationStore>
+		varStore;	/* Offset to item variation store table. */
+  LOffsetTo<DeltaSetIndexMap>
+		advMap;		/* Offset to advance var-idx mapping. */
+  LOffsetTo<DeltaSetIndexMap>
+		lsbMap;		/* Offset to lsb/tsb var-idx mapping. */
+  LOffsetTo<DeltaSetIndexMap>
+		rsbMap;		/* Offset to rsb/bsb var-idx mapping. */
+
+  public:
+  DEFINE_SIZE_STATIC (20);
+};
+
+struct HVAR : HVARVVAR {
+  static const hb_tag_t tableTag	= HB_OT_TAG_HVAR;
+};
+struct VVAR : HVARVVAR {
+  static const hb_tag_t tableTag	= HB_OT_TAG_VVAR;
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (static_cast<const HVARVVAR *> (this)->sanitize (c) &&
+		  vorgMap.sanitize (c, this));
+  }
+
+  protected:
+  LOffsetTo<DeltaSetIndexMap>
+		vorgMap;	/* Offset to vertical-origin var-idx mapping. */
+
+  public:
+  DEFINE_SIZE_STATIC (24);
+};
+
+} /* namespace OT */
+
+
+#endif /* HB_OT_VAR_HVAR_TABLE_HH */
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-ot-var.cc
@@ -0,0 +1,160 @@
+/*
+ * Copyright © 2017  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#include "hb-open-type-private.hh"
+
+#include "hb-ot-layout-private.hh"
+#include "hb-ot-var-avar-table.hh"
+#include "hb-ot-var-fvar-table.hh"
+#include "hb-ot-var.h"
+
+HB_SHAPER_DATA_ENSURE_DECLARE(ot, face)
+
+/*
+ * fvar/avar
+ */
+
+static inline const OT::fvar&
+_get_fvar (hb_face_t *face)
+{
+  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::fvar);
+  hb_ot_layout_t * layout = hb_ot_layout_from_face (face);
+  return *(layout->fvar.get ());
+}
+static inline const OT::avar&
+_get_avar (hb_face_t *face)
+{
+  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::avar);
+  hb_ot_layout_t * layout = hb_ot_layout_from_face (face);
+  return *(layout->avar.get ());
+}
+
+/**
+ * hb_ot_var_has_data:
+ * @face: #hb_face_t to test
+ *
+ * This function allows to verify the presence of OpenType variation data on the face.
+ * Alternatively, use hb_ot_var_get_axis_count().
+ *
+ * Return value: true if face has a `fvar' table and false otherwise
+ *
+ * Since: 1.4.2
+ **/
+hb_bool_t
+hb_ot_var_has_data (hb_face_t *face)
+{
+  return &_get_fvar (face) != &OT::Null(OT::fvar);
+}
+
+/**
+ * hb_ot_var_get_axis_count:
+ *
+ * Since: 1.4.2
+ **/
+unsigned int
+hb_ot_var_get_axis_count (hb_face_t *face)
+{
+  const OT::fvar &fvar = _get_fvar (face);
+  return fvar.get_axis_count ();
+}
+
+/**
+ * hb_ot_var_get_axes:
+ *
+ * Since: 1.4.2
+ **/
+unsigned int
+hb_ot_var_get_axes (hb_face_t        *face,
+		    unsigned int      start_offset,
+		    unsigned int     *axes_count /* IN/OUT */,
+		    hb_ot_var_axis_t *axes_array /* OUT */)
+{
+  const OT::fvar &fvar = _get_fvar (face);
+  return fvar.get_axis_infos (start_offset, axes_count, axes_array);
+}
+
+/**
+ * hb_ot_var_find_axis:
+ *
+ * Since: 1.4.2
+ **/
+hb_bool_t
+hb_ot_var_find_axis (hb_face_t        *face,
+		     hb_tag_t          axis_tag,
+		     unsigned int     *axis_index,
+		     hb_ot_var_axis_t *axis_info)
+{
+  const OT::fvar &fvar = _get_fvar (face);
+  return fvar.find_axis (axis_tag, axis_index, axis_info);
+}
+
+
+/**
+ * hb_ot_var_normalize_variations:
+ *
+ * Since: 1.4.2
+ **/
+void
+hb_ot_var_normalize_variations (hb_face_t            *face,
+				const hb_variation_t *variations, /* IN */
+				unsigned int          variations_length,
+				int                  *coords, /* OUT */
+				unsigned int          coords_length)
+{
+  for (unsigned int i = 0; i < coords_length; i++)
+    coords[i] = 0;
+
+  const OT::fvar &fvar = _get_fvar (face);
+  for (unsigned int i = 0; i < variations_length; i++)
+  {
+    unsigned int axis_index;
+    if (hb_ot_var_find_axis (face, variations[i].tag, &axis_index, NULL) &&
+	axis_index < coords_length)
+      coords[axis_index] = fvar.normalize_axis_value (axis_index, variations[i].value);
+  }
+
+  const OT::avar &avar = _get_avar (face);
+  avar.map_coords (coords, coords_length);
+}
+
+/**
+ * hb_ot_var_normalize_coords:
+ *
+ * Since: 1.4.2
+ **/
+void
+hb_ot_var_normalize_coords (hb_face_t    *face,
+			    unsigned int coords_length,
+			    const float *design_coords, /* IN */
+			    int *normalized_coords /* OUT */)
+{
+  const OT::fvar &fvar = _get_fvar (face);
+  for (unsigned int i = 0; i < coords_length; i++)
+    normalized_coords[i] = fvar.normalize_axis_value (i, design_coords[i]);
+
+  const OT::avar &avar = _get_avar (face);
+  avar.map_coords (normalized_coords, coords_length);
+}
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-ot-var.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright © 2017  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_H_IN
+#error "Include <hb-ot.h> instead."
+#endif
+
+#ifndef HB_OT_VAR_H
+#define HB_OT_VAR_H
+
+#include "hb.h"
+
+HB_BEGIN_DECLS
+
+
+#define HB_OT_TAG_VAR_AXIS_ITALIC	HB_TAG('i','t','a','l')
+#define HB_OT_TAG_VAR_AXIS_OPTICAL_SIZE	HB_TAG('o','p','s','z')
+#define HB_OT_TAG_VAR_AXIS_SLANT	HB_TAG('s','l','n','t')
+#define HB_OT_TAG_VAR_AXIS_WIDTH	HB_TAG('w','d','t','h')
+#define HB_OT_TAG_VAR_AXIS_WEIGHT	HB_TAG('w','g','h','t')
+
+
+/*
+ * fvar / avar
+ */
+
+/**
+ * hb_ot_var_axis_t:
+ *
+ * Since: 1.4.2
+ */
+typedef struct hb_ot_var_axis_t {
+  hb_tag_t tag;
+  unsigned int name_id;
+  float min_value;
+  float default_value;
+  float max_value;
+} hb_ot_var_axis_t;
+
+HB_EXTERN hb_bool_t
+hb_ot_var_has_data (hb_face_t *face);
+
+/**
+ * HB_OT_VAR_NO_AXIS_INDEX:
+ *
+ * Since: 1.4.2
+ */
+#define HB_OT_VAR_NO_AXIS_INDEX		0xFFFFFFFFu
+
+HB_EXTERN unsigned int
+hb_ot_var_get_axis_count (hb_face_t *face);
+
+HB_EXTERN unsigned int
+hb_ot_var_get_axes (hb_face_t        *face,
+		    unsigned int      start_offset,
+		    unsigned int     *axes_count /* IN/OUT */,
+		    hb_ot_var_axis_t *axes_array /* OUT */);
+
+HB_EXTERN hb_bool_t
+hb_ot_var_find_axis (hb_face_t        *face,
+		     hb_tag_t          axis_tag,
+		     unsigned int     *axis_index,
+		     hb_ot_var_axis_t *axis_info);
+
+
+HB_EXTERN void
+hb_ot_var_normalize_variations (hb_face_t            *face,
+				const hb_variation_t *variations, /* IN */
+				unsigned int          variations_length,
+				int                  *coords, /* OUT */
+				unsigned int          coords_length);
+
+HB_EXTERN void
+hb_ot_var_normalize_coords (hb_face_t    *face,
+			    unsigned int coords_length,
+			    const float *design_coords, /* IN */
+			    int *normalized_coords /* OUT */);
+
+
+HB_END_DECLS
+
+#endif /* HB_OT_VAR_H */
--- a/gfx/harfbuzz/src/hb-ot.h
+++ b/gfx/harfbuzz/src/hb-ot.h
@@ -30,15 +30,16 @@
 
 #include "hb.h"
 
 #include "hb-ot-font.h"
 #include "hb-ot-layout.h"
 #include "hb-ot-math.h"
 #include "hb-ot-tag.h"
 #include "hb-ot-shape.h"
+#include "hb-ot-var.h"
 
 HB_BEGIN_DECLS
 
 HB_END_DECLS
 
 #undef HB_OT_H_IN
 #endif /* HB_OT_H */
--- a/gfx/harfbuzz/src/hb-set.cc
+++ b/gfx/harfbuzz/src/hb-set.cc
@@ -100,17 +100,17 @@ hb_set_destroy (hb_set_t *set)
   free (set);
 }
 
 /**
  * hb_set_set_user_data: (skip)
  * @set: a set.
  * @key:
  * @data:
- * @destroy (closure data):
+ * @destroy:
  * @replace:
  *
  * Return value:
  *
  * Since: 0.9.2
  **/
 hb_bool_t
 hb_set_set_user_data (hb_set_t           *set,
--- a/gfx/harfbuzz/src/hb-shape.cc
+++ b/gfx/harfbuzz/src/hb-shape.cc
@@ -40,264 +40,16 @@
  * @include: hb.h
  *
  * Shaping is the central operation of HarfBuzz. Shaping operates on buffers,
  * which are sequences of Unicode characters that use the same font and have
  * the same text direction, script and language. After shaping the buffer
  * contains the output glyphs and their positions.
  **/
 
-static bool
-parse_space (const char **pp, const char *end)
-{
-  while (*pp < end && ISSPACE (**pp))
-    (*pp)++;
-  return true;
-}
-
-static bool
-parse_char (const char **pp, const char *end, char c)
-{
-  parse_space (pp, end);
-
-  if (*pp == end || **pp != c)
-    return false;
-
-  (*pp)++;
-  return true;
-}
-
-static bool
-parse_uint (const char **pp, const char *end, unsigned int *pv)
-{
-  char buf[32];
-  unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp));
-  strncpy (buf, *pp, len);
-  buf[len] = '\0';
-
-  char *p = buf;
-  char *pend = p;
-  unsigned int v;
-
-  /* Intentionally use strtol instead of strtoul, such that
-   * -1 turns into "big number"... */
-  errno = 0;
-  v = strtol (p, &pend, 0);
-  if (errno || p == pend)
-    return false;
-
-  *pv = v;
-  *pp += pend - p;
-  return true;
-}
-
-static bool
-parse_bool (const char **pp, const char *end, unsigned int *pv)
-{
-  parse_space (pp, end);
-
-  const char *p = *pp;
-  while (*pp < end && ISALPHA(**pp))
-    (*pp)++;
-
-  /* CSS allows on/off as aliases 1/0. */
-  if (*pp - p == 2 || 0 == strncmp (p, "on", 2))
-    *pv = 1;
-  else if (*pp - p == 3 || 0 == strncmp (p, "off", 2))
-    *pv = 0;
-  else
-    return false;
-
-  return true;
-}
-
-static bool
-parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature)
-{
-  if (parse_char (pp, end, '-'))
-    feature->value = 0;
-  else {
-    parse_char (pp, end, '+');
-    feature->value = 1;
-  }
-
-  return true;
-}
-
-static bool
-parse_feature_tag (const char **pp, const char *end, hb_feature_t *feature)
-{
-  parse_space (pp, end);
-
-  char quote = 0;
-
-  if (*pp < end && (**pp == '\'' || **pp == '"'))
-  {
-    quote = **pp;
-    (*pp)++;
-  }
-
-  const char *p = *pp;
-  while (*pp < end && ISALNUM(**pp))
-    (*pp)++;
-
-  if (p == *pp || *pp - p > 4)
-    return false;
-
-  feature->tag = hb_tag_from_string (p, *pp - p);
-
-  if (quote)
-  {
-    /* CSS expects exactly four bytes.  And we only allow quotations for
-     * CSS compatibility.  So, enforce the length. */
-     if (*pp - p != 4)
-       return false;
-    if (*pp == end || **pp != quote)
-      return false;
-    (*pp)++;
-  }
-
-  return true;
-}
-
-static bool
-parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature)
-{
-  parse_space (pp, end);
-
-  bool has_start;
-
-  feature->start = 0;
-  feature->end = (unsigned int) -1;
-
-  if (!parse_char (pp, end, '['))
-    return true;
-
-  has_start = parse_uint (pp, end, &feature->start);
-
-  if (parse_char (pp, end, ':')) {
-    parse_uint (pp, end, &feature->end);
-  } else {
-    if (has_start)
-      feature->end = feature->start + 1;
-  }
-
-  return parse_char (pp, end, ']');
-}
-
-static bool
-parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature)
-{
-  bool had_equal = parse_char (pp, end, '=');
-  bool had_value = parse_uint (pp, end, &feature->value) ||
-                   parse_bool (pp, end, &feature->value);
-  /* CSS doesn't use equal-sign between tag and value.
-   * If there was an equal-sign, then there *must* be a value.
-   * A value without an eqaul-sign is ok, but not required. */
-  return !had_equal || had_value;
-}
-
-
-static bool
-parse_one_feature (const char **pp, const char *end, hb_feature_t *feature)
-{
-  return parse_feature_value_prefix (pp, end, feature) &&
-	 parse_feature_tag (pp, end, feature) &&
-	 parse_feature_indices (pp, end, feature) &&
-	 parse_feature_value_postfix (pp, end, feature) &&
-	 parse_space (pp, end) &&
-	 *pp == end;
-}
-
-/**
- * hb_feature_from_string:
- * @str: (array length=len) (element-type uint8_t): a string to parse
- * @len: length of @str, or -1 if string is %NULL terminated
- * @feature: (out): the #hb_feature_t to initialize with the parsed values
- *
- * Parses a string into a #hb_feature_t.
- *
- * TODO: document the syntax here.
- *
- * Return value:
- * %true if @str is successfully parsed, %false otherwise.
- *
- * Since: 0.9.5
- **/
-hb_bool_t
-hb_feature_from_string (const char *str, int len,
-			hb_feature_t *feature)
-{
-  hb_feature_t feat;
-
-  if (len < 0)
-    len = strlen (str);
-
-  if (likely (parse_one_feature (&str, str + len, &feat)))
-  {
-    if (feature)
-      *feature = feat;
-    return true;
-  }
-
-  if (feature)
-    memset (feature, 0, sizeof (*feature));
-  return false;
-}
-
-/**
- * hb_feature_to_string:
- * @feature: an #hb_feature_t to convert
- * @buf: (array length=size) (out): output string
- * @size: the allocated size of @buf
- *
- * Converts a #hb_feature_t into a %NULL-terminated string in the format
- * understood by hb_feature_from_string(). The client in responsible for
- * allocating big enough size for @buf, 128 bytes is more than enough.
- *
- * Since: 0.9.5
- **/
-void
-hb_feature_to_string (hb_feature_t *feature,
-		      char *buf, unsigned int size)
-{
-  if (unlikely (!size)) return;
-
-  char s[128];
-  unsigned int len = 0;
-  if (feature->value == 0)
-    s[len++] = '-';
-  hb_tag_to_string (feature->tag, s + len);
-  len += 4;
-  while (len && s[len - 1] == ' ')
-    len--;
-  if (feature->start != 0 || feature->end != (unsigned int) -1)
-  {
-    s[len++] = '[';
-    if (feature->start)
-      len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start));
-    if (feature->end != feature->start + 1) {
-      s[len++] = ':';
-      if (feature->end != (unsigned int) -1)
-	len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end));
-    }
-    s[len++] = ']';
-  }
-  if (feature->value > 1)
-  {
-    s[len++] = '=';
-    len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value));
-  }
-  assert (len < ARRAY_LENGTH (s));
-  len = MIN (len, size - 1);
-  memcpy (buf, s, len);
-  buf[len] = '\0';
-}
-
-
 static const char **static_shaper_list;
 
 #ifdef HB_USE_ATEXIT
 static
 void free_static_shaper_list (void)
 {
   free (static_shaper_list);
 }
@@ -357,17 +109,17 @@ retry:
  * @num_features: the length of @features array
  * @shaper_list: (array zero-terminated=1) (allow-none): a %NULL-terminated
  *    array of shapers to use or %NULL
  *
  * See hb_shape() for details. If @shaper_list is not %NULL, the specified
  * shapers will be used in the given order, otherwise the default shapers list
  * will be used.
  *
- * Return value: %FALSE if all shapers failed, %TRUE otherwise
+ * Return value: false if all shapers failed, true otherwise
  *
  * Since: 0.9.2
  **/
 hb_bool_t
 hb_shape_full (hb_font_t          *font,
 	       hb_buffer_t        *buffer,
 	       const hb_feature_t *features,
 	       unsigned int        num_features,
--- a/gfx/harfbuzz/src/hb-shape.h
+++ b/gfx/harfbuzz/src/hb-shape.h
@@ -35,32 +35,16 @@
 
 #include "hb-common.h"
 #include "hb-buffer.h"
 #include "hb-font.h"
 
 HB_BEGIN_DECLS
 
 
-typedef struct hb_feature_t {
-  hb_tag_t      tag;
-  uint32_t      value;
-  unsigned int  start;
-  unsigned int  end;
-} hb_feature_t;
-
-HB_EXTERN hb_bool_t
-hb_feature_from_string (const char *str, int len,
-			hb_feature_t *feature);
-
-HB_EXTERN void
-hb_feature_to_string (hb_feature_t *feature,
-		      char *buf, unsigned int size);
-
-
 HB_EXTERN void
 hb_shape (hb_font_t           *font,
 	  hb_buffer_t         *buffer,
 	  const hb_feature_t  *features,
 	  unsigned int         num_features);
 
 HB_EXTERN hb_bool_t
 hb_shape_full (hb_font_t          *font,
--- a/gfx/harfbuzz/src/hb-version.h
+++ b/gfx/harfbuzz/src/hb-version.h
@@ -33,19 +33,19 @@
 
 #include "hb-common.h"
 
 HB_BEGIN_DECLS
 
 
 #define HB_VERSION_MAJOR 1
 #define HB_VERSION_MINOR 4
-#define HB_VERSION_MICRO 1
+#define HB_VERSION_MICRO 2
 
-#define HB_VERSION_STRING "1.4.1"
+#define HB_VERSION_STRING "1.4.2"
 
 #define HB_VERSION_ATLEAST(major,minor,micro) \
 	((major)*10000+(minor)*100+(micro) <= \
 	 HB_VERSION_MAJOR*10000+HB_VERSION_MINOR*100+HB_VERSION_MICRO)
 
 
 HB_EXTERN void
 hb_version (unsigned int *major,
--- a/gfx/harfbuzz/src/moz.build
+++ b/gfx/harfbuzz/src/moz.build
@@ -11,16 +11,17 @@ EXPORTS.harfbuzz += [
     'hb-deprecated.h',
     'hb-face.h',
     'hb-font.h',
     'hb-ot-font.h',
     'hb-ot-layout.h',
     'hb-ot-math.h',
     'hb-ot-shape.h',
     'hb-ot-tag.h',
+    'hb-ot-var.h',
     'hb-ot.h',
     'hb-set.h',
     'hb-shape-plan.h',
     'hb-shape.h',
     'hb-unicode.h',
     'hb-version.h',
     'hb.h',
 ]
@@ -49,16 +50,17 @@ UNIFIED_SOURCES += [
     'hb-ot-shape-complex-indic-table.cc',
     'hb-ot-shape-complex-myanmar.cc',
     'hb-ot-shape-complex-thai.cc',
     'hb-ot-shape-complex-tibetan.cc',
     'hb-ot-shape-complex-use-table.cc',
     'hb-ot-shape-fallback.cc',
     'hb-ot-shape-normalize.cc',
     'hb-ot-tag.cc',
+    'hb-ot-var.cc',
     'hb-set.cc',
     'hb-shape.cc',
     'hb-shaper.cc',
     'hb-unicode.cc',
     'hb-warning.cc',
 ]
 
 if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
--- a/gfx/thebes/gfxBlur.cpp
+++ b/gfx/thebes/gfxBlur.cpp
@@ -1240,17 +1240,23 @@ gfxAlphaBoxBlur::BlurInsetBox(gfxContext
   GetBlurMargins(aInnerClipRadii, aBlurRadius, blurMargin, innerMargin);
 
   Rect whitespaceRect;
   Rect outerRect;
   bool useDestRect =
     GetInsetBoxShadowRects(blurMargin, innerMargin, aShadowClipRect,
                            aDestinationRect, whitespaceRect, outerRect);
 
-  bool mirrorCorners = !aInnerClipRadii || aInnerClipRadii->AreRadiiSame();
+  // Check that the inset margin between the outer and whitespace rects is symmetric,
+  // and that all corner radii are the same, in which case the blur can be mirrored.
+  Margin checkMargin = outerRect - whitespaceRect;
+  bool mirrorCorners =
+    checkMargin.left == checkMargin.right &&
+    checkMargin.top == checkMargin.bottom &&
+    (!aInnerClipRadii || aInnerClipRadii->AreRadiiSame());
   RefPtr<SourceSurface> minBlur =
     GetInsetBlur(outerRect, whitespaceRect, useDestRect, aShadowColor,
                  aBlurRadius, aInnerClipRadii, destDrawTarget, mirrorCorners);
   if (!minBlur) {
     return;
   }
 
   if (useDestRect) {
--- a/ipc/app/moz.build
+++ b/ipc/app/moz.build
@@ -94,8 +94,11 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk
         'binder',
         'utils',
     ]
 
 if CONFIG['GNU_CXX']:
     CXXFLAGS += ['-Wshadow']
 
 DEFINES['MOZ_PLUGIN_CONTAINER'] = 1;
+
+with Files("**"):
+    BUG_COMPONENT = ("Core", "DOM: Content Processes")
--- a/ipc/dbus/moz.build
+++ b/ipc/dbus/moz.build
@@ -26,8 +26,11 @@ include('/ipc/chromium/chromium-config.m
 
 FINAL_LIBRARY = 'xul'
 
 if CONFIG['MOZ_ENABLE_DBUS']:
     CFLAGS += CONFIG['MOZ_DBUS_CFLAGS']
     CFLAGS += CONFIG['MOZ_DBUS_GLIB_CFLAGS']
     CXXFLAGS += CONFIG['MOZ_DBUS_CFLAGS']
     CXXFLAGS += CONFIG['MOZ_DBUS_GLIB_CFLAGS']
+
+with Files("**"):
+    BUG_COMPONENT = ("Firefox OS", "General")
--- a/ipc/hal/moz.build
+++ b/ipc/hal/moz.build
@@ -20,8 +20,11 @@ UNIFIED_SOURCES += [
     'DaemonSocketConsumer.cpp',
     'DaemonSocketPDU.cpp',
     'DaemonSocketPDUHelpers.cpp'
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
+
+with Files("**"):
+    BUG_COMPONENT = ("Firefox OS", "General")
--- a/ipc/moz.build
+++ b/ipc/moz.build
@@ -22,8 +22,11 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android':
     DIRS += ['contentproc']
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     DIRS += ['mscom']
 
 DIRS += ['app']
+
+with Files("**"):
+    BUG_COMPONENT = ("Core", "IPC")
--- a/ipc/mscom/moz.build
+++ b/ipc/mscom/moz.build
@@ -53,8 +53,11 @@ if CONFIG['ACCESSIBILITY']:
 LOCAL_INCLUDES += [
     '/xpcom/base',
     '/xpcom/build',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
+
+with Files("**"):
+    BUG_COMPONENT = ("Core", "Disability Access APIs")
--- a/ipc/netd/moz.build
+++ b/ipc/netd/moz.build
@@ -10,8 +10,11 @@ EXPORTS.mozilla.ipc += [
 
 SOURCES += [
     'Netd.cpp',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
+
+with Files("**"):
+    BUG_COMPONENT = ("Firefox OS", "General")
--- a/ipc/ril/moz.build
+++ b/ipc/ril/moz.build
@@ -15,8 +15,11 @@ SOURCES += [
     'RilConnector.cpp',
     'RilSocket.cpp',
     'RilSocketConsumer.cpp'
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
+
+with Files("**"):
+    BUG_COMPONENT = ("Firefox OS", "General")
--- a/ipc/testshell/moz.build
+++ b/ipc/testshell/moz.build
@@ -30,8 +30,11 @@ FINAL_LIBRARY = 'xul'
 # For xpcshell error messages and nsAutoJSString
 LOCAL_INCLUDES += [
     '/dom/base',
     '/js/xpconnect/src',
 ]
 
 if CONFIG['GNU_CXX']:
     CXXFLAGS += ['-Wno-error=shadow']
+
+with Files("**"):
+    BUG_COMPONENT = ("Core", "XPConnect")
--- a/ipc/unixfd/moz.build
+++ b/ipc/unixfd/moz.build
@@ -14,8 +14,11 @@ SOURCES += [
     'UnixFdWatcher.cpp',
     'UnixFileWatcher.cpp',
     'UnixSocketWatcher.cpp'
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
+
+with Files("**"):
+    BUG_COMPONENT = ("Firefox OS", "General")
--- a/ipc/unixsocket/moz.build
+++ b/ipc/unixsocket/moz.build
@@ -24,8 +24,11 @@ SOURCES += [
     'StreamSocket.cpp',
     'StreamSocketConsumer.cpp',
     'UnixSocketConnector.cpp'
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
+
+with Files("**"):
+    BUG_COMPONENT = ("Firefox OS", "General")
--- a/js/src/builtin/AtomicsObject.cpp
+++ b/js/src/builtin/AtomicsObject.cpp
@@ -45,16 +45,17 @@
  * shared memory, or compilation will fail with a predictable error.
  */
 
 #include "builtin/AtomicsObject.h"
 
 #include "mozilla/Atomics.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/Maybe.h"
+#include "mozilla/ScopeExit.h"
 #include "mozilla/Unused.h"
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "jsnum.h"
 
 #include "jit/AtomicOperations.h"
 #include "jit/InlinableNatives.h"
@@ -967,29 +968,32 @@ js::FutexRuntime::wait(JSContext* cx, js
     // See explanation below.
 
     if (state_ == WaitingInterrupted) {
         UnlockGuard<Mutex> unlock(locked);
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_ATOMICS_WAIT_NOT_ALLOWED);
         return false;
     }
 
+    // Go back to Idle after returning.
+    auto onFinish = mozilla::MakeScopeExit([&] {
+        state_ = Idle;
+    });
+
     const bool isTimed = timeout.isSome();
 
     auto finalEnd = timeout.map([](mozilla::TimeDuration& timeout) {
         return mozilla::TimeStamp::Now() + timeout;
     });
 
 
     // 4000s is about the longest timeout slice that is guaranteed to
     // work cross-platform.
     auto maxSlice = mozilla::TimeDuration::FromSeconds(4000.0);
 
-    bool retval = true;
-
     for (;;) {
         // If we are doing a timed wait, calculate the end time for this wait
         // slice.
         auto sliceEnd = finalEnd.map([&](mozilla::TimeStamp& finalEnd) {
             auto sliceEnd = mozilla::TimeStamp::Now() + maxSlice;
             if (finalEnd < sliceEnd)
                 sliceEnd = finalEnd;
             return sliceEnd;
@@ -1005,24 +1009,24 @@ js::FutexRuntime::wait(JSContext* cx, js
 
         switch (state_) {
           case FutexRuntime::Waiting:
             // Timeout or spurious wakeup.
             if (isTimed) {
                 auto now = mozilla::TimeStamp::Now();
                 if (now >= *finalEnd) {
                     *result = FutexTimedOut;
-                    goto finished;
+                    return true;
                 }
             }
             break;
 
           case FutexRuntime::Woken:
             *result = FutexOK;
-            goto finished;
+            return true;
 
           case FutexRuntime::WaitingNotifiedForInterrupt:
             // The interrupt handler may reenter the engine.  In that case
             // there are two complications:
             //
             // - The waiting thread is not actually waiting on the
             //   condition variable so we have to record that it
             //   should be woken when the interrupt handler returns.
@@ -1047,33 +1051,29 @@ js::FutexRuntime::wait(JSContext* cx, js
             //   occasionally (very rarely) be surprising but is
             //   expedient.  Other solutions exist, see bug #1131943.  The
             //   code that performs the check is above, at the head of
             //   this function.
 
             state_ = WaitingInterrupted;
             {
                 UnlockGuard<Mutex> unlock(locked);
-                retval = cx->runtime()->handleInterrupt(cx);
+                if (!cx->runtime()->handleInterrupt(cx))
+                    return false;
             }
-            if (!retval)
-                goto finished;
             if (state_ == Woken) {
                 *result = FutexOK;
-                goto finished;
+                return true;
             }
             break;
 
           default:
             MOZ_CRASH("Bad FutexState in wait()");
         }
     }
-finished:
-    state_ = Idle;
-    return retval;
 }
 
 void
 js::FutexRuntime::wake(WakeReason reason)
 {
     MOZ_ASSERT(isWaiting());
 
     if ((state_ == WaitingInterrupted || state_ == WaitingNotifiedForInterrupt) && reason == WakeExplicit) {
--- a/js/src/gc/Allocator.cpp
+++ b/js/src/gc/Allocator.cpp
@@ -7,16 +7,17 @@
 #include "gc/Allocator.h"
 
 #include "jscntxt.h"
 
 #include "gc/GCInternals.h"
 #include "gc/GCTrace.h"
 #include "gc/Nursery.h"
 #include "jit/JitCompartment.h"
+#include "threading/CpuCount.h"
 #include "vm/Runtime.h"
 #include "vm/String.h"
 
 #include "jsobjinlines.h"
 
 #include "gc/Heap-inl.h"
 
 using namespace js;
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -172,19 +172,19 @@ template <> bool ThingIsPermanentAtomOrW
     return name->isPermanent();
 }
 template <> bool ThingIsPermanentAtomOrWellKnownSymbol<JS::Symbol>(JS::Symbol* sym) {
     return sym->isWellKnownSymbol();
 }
 
 template <typename T>
 static inline bool
-IsOwnedByOtherRuntime(JSTracer* trc, T thing)
+IsOwnedByOtherRuntime(JSRuntime* rt, T thing)
 {
-    bool other = thing->runtimeFromAnyThread() != trc->runtime();
+    bool other = thing->runtimeFromAnyThread() != rt;
     MOZ_ASSERT_IF(other,
                   ThingIsPermanentAtomOrWellKnownSymbol(thing) ||
                   thing->zoneFromAnyThread()->isSelfHostingZone());
     return other;
 }
 
 template<typename T>
 void
@@ -205,17 +205,17 @@ js::CheckTracedThing(JSTracer* trc, T* t
         return;
 
     MOZ_ASSERT_IF(!IsMovingTracer(trc) && !trc->isTenuringTracer(), !IsForwarded(thing));
 
     /*
      * Permanent atoms and things in the self-hosting zone are not associated
      * with this runtime, but will be ignored during marking.
      */
-    if (IsOwnedByOtherRuntime(trc, thing))
+    if (IsOwnedByOtherRuntime(trc->runtime(), thing))
         return;
 
     Zone* zone = thing->zoneFromAnyThread();
     JSRuntime* rt = trc->runtime();
 
     MOZ_ASSERT_IF(!IsMovingTracer(trc), CurrentThreadCanAccessZone(zone));
     MOZ_ASSERT_IF(!IsMovingTracer(trc), CurrentThreadCanAccessRuntime(rt));
 
@@ -749,29 +749,29 @@ GCMarker::markImplicitEdges(T* thing)
 
 } // namespace js
 
 template <typename T>
 static inline bool
 MustSkipMarking(GCMarker* gcmarker, T thing)
 {
     // Don't trace things that are owned by another runtime.
-    if (IsOwnedByOtherRuntime(gcmarker, thing))
+    if (IsOwnedByOtherRuntime(gcmarker->runtime(), thing))
         return true;
 
     // Don't mark things outside a zone if we are in a per-zone GC.
     return !thing->zone()->isGCMarking();
 }
 
 template <>
 bool
 MustSkipMarking<JSObject*>(GCMarker* gcmarker, JSObject* obj)
 {
     // Don't trace things that are owned by another runtime.
-    if (IsOwnedByOtherRuntime(gcmarker, obj))
+    if (IsOwnedByOtherRuntime(gcmarker->runtime(), obj))
         return true;
 
     // We may mark a Nursery thing outside the context of the
     // MinorCollectionTracer because of a pre-barrier. The pre-barrier is not
     // needed in this case because we perform a minor collection before each
     // incremental slice.
     if (IsInsideNursery(obj))
         return true;
@@ -815,17 +815,17 @@ NoteWeakEdge(GCMarker* gcmarker, T** thi
 {
     // Do per-type marking precondition checks.
     if (MustSkipMarking(gcmarker, *thingp))
         return;
 
     CheckTracedThing(gcmarker, *thingp);
 
     // If the target is already marked, there's no need to store the edge.
-    if (IsMarkedUnbarriered(thingp))
+    if (IsMarkedUnbarriered(gcmarker->runtime(), thingp))
         return;
 
     gcmarker->noteWeakEdge(thingp);
 }
 
 template <typename T>
 void
 NoteWeakEdge(GCMarker* gcmarker, T* thingp)
@@ -2690,47 +2690,52 @@ IsMarkedInternalCommon(T* thingp)
         return true;
     if (zone->isGCCompacting() && IsForwarded(*thingp))
         *thingp = Forwarded(*thingp);
     return (*thingp)->asTenured().isMarked();
 }
 
 template <typename T>
 static bool
-IsMarkedInternal(T** thingp)
+IsMarkedInternal(JSRuntime* rt, T** thingp)
 {
+    if (IsOwnedByOtherRuntime(rt, *thingp))
+        return true;
+
     return IsMarkedInternalCommon(thingp);
 }
 
 template <>
 /* static */ bool
-IsMarkedInternal(JSObject** thingp)
+IsMarkedInternal(JSRuntime* rt, JSObject** thingp)
 {
+    if (IsOwnedByOtherRuntime(rt, *thingp))
+        return true;
+
     if (IsInsideNursery(*thingp)) {
-        JSRuntime* rt = (*thingp)->runtimeFromAnyThread();
         MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
         return rt->gc.nursery.getForwardedPointer(thingp);
     }
     return IsMarkedInternalCommon(thingp);
 }
 
 template <typename S>
 struct IsMarkedFunctor : public IdentityDefaultAdaptor<S> {
-    template <typename T> S operator()(T* t, bool* rv) {
-        *rv = IsMarkedInternal(&t);
+    template <typename T> S operator()(T* t, JSRuntime* rt, bool* rv) {
+        *rv = IsMarkedInternal(rt, &t);
         return js::gc::RewrapTaggedPointer<S, T>::wrap(t);
     }
 };
 
 template <typename T>
 static bool
-IsMarkedInternal(T* thingp)
+IsMarkedInternal(JSRuntime* rt, T* thingp)
 {
     bool rv = true;
-    *thingp = DispatchTyped(IsMarkedFunctor<T>(), *thingp, &rv);
+    *thingp = DispatchTyped(IsMarkedFunctor<T>(), *thingp, rt, &rv);
     return rv;
 }
 
 bool
 js::gc::IsAboutToBeFinalizedDuringSweep(TenuredCell& tenured)
 {
     MOZ_ASSERT(!IsInsideNursery(&tenured));
     MOZ_ASSERT(tenured.zoneFromAnyThread()->isGCSweeping());
@@ -2785,26 +2790,26 @@ IsAboutToBeFinalizedInternal(T* thingp)
     return rv;
 }
 
 namespace js {
 namespace gc {
 
 template <typename T>
 bool
-IsMarkedUnbarriered(T* thingp)
+IsMarkedUnbarriered(JSRuntime* rt, T* thingp)
 {
-    return IsMarkedInternal(ConvertToBase(thingp));
+    return IsMarkedInternal(rt, ConvertToBase(thingp));
 }
 
 template <typename T>
 bool
-IsMarked(WriteBarrieredBase<T>* thingp)
+IsMarked(JSRuntime* rt, WriteBarrieredBase<T>* thingp)
 {
-    return IsMarkedInternal(ConvertToBase(thingp->unsafeUnbarrieredForTracing()));
+    return IsMarkedInternal(rt, ConvertToBase(thingp->unsafeUnbarrieredForTracing()));
 }
 
 template <typename T>
 bool
 IsAboutToBeFinalizedUnbarriered(T* thingp)
 {
     return IsAboutToBeFinalizedInternal(ConvertToBase(thingp));
 }
@@ -2827,18 +2832,18 @@ template <typename T>
 JS_PUBLIC_API(bool)
 EdgeNeedsSweep(JS::Heap<T>* thingp)
 {
     return IsAboutToBeFinalizedInternal(ConvertToBase(thingp->unsafeGet()));
 }
 
 // Instantiate a copy of the Tracing templates for each derived type.
 #define INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS(type) \
-    template bool IsMarkedUnbarriered<type>(type*); \
-    template bool IsMarked<type>(WriteBarrieredBase<type>*); \
+    template bool IsMarkedUnbarriered<type>(JSRuntime*, type*);                \
+    template bool IsMarked<type>(JSRuntime*, WriteBarrieredBase<type>*); \
     template bool IsAboutToBeFinalizedUnbarriered<type>(type*); \
     template bool IsAboutToBeFinalized<type>(WriteBarrieredBase<type>*); \
     template bool IsAboutToBeFinalized<type>(ReadBarrieredBase<type>*);
 #define INSTANTIATE_ALL_VALID_HEAP_TRACE_FUNCTIONS(type) \
     template JS_PUBLIC_API(bool) EdgeNeedsSweep<type>(JS::Heap<type>*);
 FOR_EACH_GC_POINTER_TYPE(INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS)
 FOR_EACH_PUBLIC_GC_POINTER_TYPE(INSTANTIATE_ALL_VALID_HEAP_TRACE_FUNCTIONS)
 FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(INSTANTIATE_ALL_VALID_HEAP_TRACE_FUNCTIONS)
--- a/js/src/gc/Marking.h
+++ b/js/src/gc/Marking.h
@@ -372,23 +372,29 @@ namespace gc {
 
 /*** Special Cases ***/
 
 void
 PushArena(GCMarker* gcmarker, Arena* arena);
 
 /*** Liveness ***/
 
+// Report whether a thing has been marked.  Things which are in zones that are
+// not currently being collected or are owned by another runtime are always
+// reported as being marked.
 template <typename T>
 bool
-IsMarkedUnbarriered(T* thingp);
+IsMarkedUnbarriered(JSRuntime* rt, T* thingp);
 
+// Report whether a thing has been marked.  Things which are in zones that are
+// not currently being collected or are owned by another runtime are always
+// reported as being marked.
 template <typename T>
 bool
-IsMarked(WriteBarrieredBase<T>* thingp);
+IsMarked(JSRuntime* rt, WriteBarrieredBase<T>* thingp);
 
 template <typename T>
 bool
 IsAboutToBeFinalizedUnbarriered(T* thingp);
 
 template <typename T>
 bool
 IsAboutToBeFinalized(WriteBarrieredBase<T>* thingp);
--- a/js/src/jit/BaselineCacheIRCompiler.cpp
+++ b/js/src/jit/BaselineCacheIRCompiler.cpp
@@ -895,16 +895,125 @@ BaselineCacheIRCompiler::emitStoreTypedO
     Address dest(scratch1, 0);
 
     BaselineStoreToTypedArray(cx_, masm, type, val, dest, scratch2,
                               failure->label(), failure->label());
 
     return true;
 }
 
+typedef bool (*CallNativeSetterFn)(JSContext*, HandleFunction, HandleObject, HandleValue);
+static const VMFunction CallNativeSetterInfo =
+    FunctionInfo<CallNativeSetterFn>(CallNativeSetter, "CallNativeSetter");
+
+bool
+BaselineCacheIRCompiler::emitCallNativeSetter()
+{
+    AutoStubFrame stubFrame(*this);
+
+    Register obj = allocator.useRegister(masm, reader.objOperandId());
+    Address setterAddr(stubAddress(reader.stubOffset()));
+    ValueOperand val = allocator.useValueRegister(masm, reader.valOperandId());
+
+    AutoScratchRegister scratch(allocator, masm);
+
+    allocator.discardStack(masm);
+
+    stubFrame.enter(masm, scratch);
+
+    // Load the callee in the scratch register.
+    masm.loadPtr(setterAddr, scratch);
+
+    masm.Push(val);
+    masm.Push(obj);
+    masm.Push(scratch);
+
+    if (!callVM(masm, CallNativeSetterInfo))
+        return false;
+
+    stubFrame.leave(masm);
+    return true;
+}
+
+bool
+BaselineCacheIRCompiler::emitCallScriptedSetter()
+{
+    AutoStubFrame stubFrame(*this);
+
+    // We don't have many registers available on x86, so we use a single
+    // scratch register.
+    AutoScratchRegisterExcluding scratch(allocator, masm, ArgumentsRectifierReg);
+
+    Register obj = allocator.useRegister(masm, reader.objOperandId());
+    Address setterAddr(stubAddress(reader.stubOffset()));
+    ValueOperand val = allocator.useValueRegister(masm, reader.valOperandId());
+
+    // First, ensure our setter is non-lazy and has JIT code.
+    {
+        FailurePath* failure;
+        if (!addFailurePath(&failure))
+            return false;
+
+        masm.loadPtr(setterAddr, scratch);
+        masm.branchIfFunctionHasNoScript(scratch, failure->label());
+        masm.loadPtr(Address(scratch, JSFunction::offsetOfNativeOrScript()), scratch);
+        masm.loadBaselineOrIonRaw(scratch, scratch, failure->label());
+    }
+
+    allocator.discardStack(masm);
+
+    stubFrame.enter(masm, scratch);
+
+    // Align the stack such that the JitFrameLayout is aligned on
+    // JitStackAlignment.
+    masm.alignJitStackBasedOnNArgs(1);
+
+    // Setter is called with 1 argument, and |obj| as thisv. Note that we use
+    // Push, not push, so that callJit will align the stack properly on ARM.
+    masm.Push(val);
+    masm.Push(TypedOrValueRegister(MIRType::Object, AnyRegister(obj)));
+
+    // Now that the object register is no longer needed, use it as second
+    // scratch.
+    Register scratch2 = obj;
+    EmitBaselineCreateStubFrameDescriptor(masm, scratch2, JitFrameLayout::Size());
+    masm.Push(Imm32(1));  // ActualArgc
+
+    // Push callee.
+    masm.loadPtr(setterAddr, scratch);
+    masm.Push(scratch);
+
+    // Push frame descriptor.
+    masm.Push(scratch2);
+
+    // Load callee->nargs in scratch2 and the JIT code in scratch.
+    Label noUnderflow;
+    masm.load16ZeroExtend(Address(scratch, JSFunction::offsetOfNargs()), scratch2);
+    masm.loadPtr(Address(scratch, JSFunction::offsetOfNativeOrScript()), scratch);
+    masm.loadBaselineOrIonRaw(scratch, scratch, nullptr);
+
+    // Handle arguments underflow.
+    masm.branch32(Assembler::BelowOrEqual, scratch2, Imm32(1), &noUnderflow);
+    {
+        // Call the arguments rectifier.
+        MOZ_ASSERT(ArgumentsRectifierReg != scratch);
+
+        JitCode* argumentsRectifier = cx_->runtime()->jitRuntime()->getArgumentsRectifier();
+        masm.movePtr(ImmGCPtr(argumentsRectifier), scratch);
+        masm.loadPtr(Address(scratch, JitCode::offsetOfCode()), scratch);
+        masm.movePtr(ImmWord(1), ArgumentsRectifierReg);
+    }
+
+    masm.bind(&noUnderflow);
+    masm.callJit(scratch);
+
+    stubFrame.leave(masm, true);
+    return true;
+}
+
 bool
 BaselineCacheIRCompiler::emitTypeMonitorResult()
 {
     allocator.discardStack(masm);
     EmitEnterTypeMonitorIC(masm);
     return true;
 }
 
@@ -1180,8 +1289,28 @@ ICCacheIR_Monitored::Clone(JSContext* cx
     if (!newStub)
         return nullptr;
 
     ICCacheIR_Monitored* res = new(newStub) ICCacheIR_Monitored(other.jitCode(), firstMonitorStub,
                                                                 stubInfo);
     stubInfo->copyStubData(&other, res);
     return res;
 }
+
+/* static */ ICCacheIR_Updated*
+ICCacheIR_Updated::Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub,
+                         ICCacheIR_Updated& other)
+{
+    const CacheIRStubInfo* stubInfo = other.stubInfo();
+    MOZ_ASSERT(stubInfo->makesGCCalls());
+
+    size_t bytesNeeded = stubInfo->stubDataOffset() + stubInfo->stubDataSize();
+    void* newStub = space->alloc(bytesNeeded);
+    if (!newStub)
+        return nullptr;
+
+    ICCacheIR_Updated* res = new(newStub) ICCacheIR_Updated(other.jitCode(), stubInfo);
+    res->updateStubGroup() = other.updateStubGroup();
+    res->updateStubId() = other.updateStubId();
+
+    stubInfo->copyStubData(&other, res);
+    return res;
+}
--- a/js/src/jit/BaselineDebugModeOSR.cpp
+++ b/js/src/jit/BaselineDebugModeOSR.cpp
@@ -686,26 +686,25 @@ RecompileBaselineScriptForDebugMode(JSCo
     // Don't destroy the old baseline script yet, since if we fail any of the
     // recompiles we need to rollback all the old baseline scripts.
     MOZ_ASSERT(script->baselineScript()->hasDebugInstrumentation() == observing);
     return true;
 }
 
 #define PATCHABLE_ICSTUB_KIND_LIST(_)           \
     _(CacheIR_Monitored)                        \
+    _(CacheIR_Updated)                          \
     _(Call_Scripted)                            \
     _(Call_AnyScripted)                         \
     _(Call_Native)                              \
     _(Call_ClassHook)                           \
     _(Call_ScriptedApplyArray)                  \
     _(Call_ScriptedApplyArguments)              \
     _(Call_ScriptedFunCall)                     \
-    _(GetProp_Generic)                          \
-    _(SetProp_CallScripted)                     \
-    _(SetProp_CallNative)
+    _(GetProp_Generic)
 
 static bool
 CloneOldBaselineStub(JSContext* cx, DebugModeOSREntryVector& entries, size_t entryIndex)
 {
     DebugModeOSREntry& entry = entries[entryIndex];
     if (!entry.oldStub)
         return true;
 
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -818,58 +818,16 @@ IsCacheableSetPropAddSlot(JSContext* cx,
     if (NativeObject::dynamicSlotsCount(propertyShape) != NativeObject::dynamicSlotsCount(oldShape))
         return false;
 
     *protoChainDepth = chainDepth;
     return true;
 }
 
 static bool
-IsCacheableSetPropCall(JSContext* cx, JSObject* obj, JSObject* holder, Shape* shape,
-                       bool* isScripted, bool* isTemporarilyUnoptimizable)
-{
-    MOZ_ASSERT(isScripted);
-
-    if (!shape || !IsCacheableProtoChain(obj, holder))
-        return false;
-
-    if (shape->hasSlot() || shape->hasDefaultSetter())
-        return false;
-
-    if (!shape->hasSetterValue())
-        return false;
-
-    if (!shape->setterValue().isObject() || !shape->setterObject()->is<JSFunction>())
-        return false;
-
-    JSFunction* func = &shape->setterObject()->as<JSFunction>();
-
-    if (IsWindow(obj)) {
-        if (!func->isNative())
-            return false;
-
-        if (!func->jitInfo() || func->jitInfo()->needsOuterizedThisObject())
-            return false;
-    }
-
-    if (func->isNative()) {
-        *isScripted = false;
-        return true;
-    }
-
-    if (!func->hasJITCode()) {
-        *isTemporarilyUnoptimizable = true;
-        return false;
-    }
-
-    *isScripted = true;
-    return true;
-}
-
-static bool
 IsOptimizableElementPropertyName(JSContext* cx, HandleValue key, MutableHandleId idp)
 {
     if (!key.isString())
         return false;
 
     // Convert to interned property name.
     if (!ValueToId<CanGC>(cx, key, idp))
         return false;
@@ -985,17 +943,17 @@ DoGetElemFallback(JSContext* cx, Baselin
                     newStub->toCacheIR_Monitored()->notePreliminaryObject();
                 else if (gen.shouldUnlinkPreliminaryObjectStubs())
                     StripPreliminaryObjectStubs(cx, stub);
             }
         }
     }
 
     if (!isOptimizedArgs) {
-        if (!GetElementOperation(cx, op, &lhsCopy, rhs, res))
+        if (!GetElementOperation(cx, op, lhsCopy, rhs, res))
             return false;
         TypeScript::Monitor(cx, frame->script(), pc, res);
     }
 
     // Check if debug mode toggling made the stub invalid.
     if (stub.invalid())
         return true;
 
@@ -2657,146 +2615,16 @@ TryAttachSetValuePropStub(JSContext* cx,
         stub->addNewStub(newStub);
         *attached = true;
         return true;
     }
 
     return true;
 }
 
-// Try to update existing SetProp setter call stubs for the given holder in
-// place with a new shape and setter.
-static bool
-UpdateExistingSetPropCallStubs(ICSetProp_Fallback* fallbackStub,
-                               ICStub::Kind kind,
-                               NativeObject* holder,
-                               JSObject* receiver,
-                               JSFunction* setter)
-{
-    MOZ_ASSERT(kind == ICStub::SetProp_CallScripted ||
-               kind == ICStub::SetProp_CallNative);
-    MOZ_ASSERT(holder);
-    MOZ_ASSERT(receiver);
-
-    bool isOwnSetter = (holder == receiver);
-    bool foundMatchingStub = false;
-    ReceiverGuard receiverGuard(receiver);
-    for (ICStubConstIterator iter = fallbackStub->beginChainConst(); !iter.atEnd(); iter++) {
-        if (iter->kind() == kind) {
-            ICSetPropCallSetter* setPropStub = static_cast<ICSetPropCallSetter*>(*iter);
-            if (setPropStub->holder() == holder && setPropStub->isOwnSetter() == isOwnSetter) {
-                // If this is an own setter, update the receiver guard as well,
-                // since that's the shape we'll be guarding on. Furthermore,
-                // isOwnSetter() relies on holderShape_ and receiverGuard_ being
-                // the same shape.
-                if (isOwnSetter)
-                    setPropStub->receiverGuard().update(receiverGuard);
-
-                MOZ_ASSERT(setPropStub->holderShape() != holder->lastProperty() ||
-                           !setPropStub->receiverGuard().matches(receiverGuard),
-                           "Why didn't we end up using this stub?");
-
-                // We want to update the holder shape to match the new one no
-                // matter what, even if the receiver shape is different.
-                setPropStub->holderShape() = holder->lastProperty();
-
-                // Make sure to update the setter, since a shape change might
-                // have changed which setter we want to use.
-                setPropStub->setter() = setter;
-                if (setPropStub->receiverGuard().matches(receiverGuard))
-                    foundMatchingStub = true;
-            }
-        }
-    }
-
-    return foundMatchingStub;
-}
-
-// Attach an optimized property set stub for a SETPROP/SETGNAME/SETNAME op on
-// an accessor property.
-static bool
-TryAttachSetAccessorPropStub(JSContext* cx, HandleScript script, jsbytecode* pc,
-                             ICSetProp_Fallback* stub,
-                             HandleObject obj, const RootedReceiverGuard& receiverGuard,
-                             HandlePropertyName name,
-                             HandleId id, HandleValue rhs, bool* attached,
-                             bool* isTemporarilyUnoptimizable)
-{
-    MOZ_ASSERT(!*attached);
-    MOZ_ASSERT(!*isTemporarilyUnoptimizable);
-
-    if (obj->watched())
-        return true;
-
-    Rooted<PropertyResult> prop(cx);
-    RootedObject holder(cx);
-    if (!EffectlesslyLookupProperty(cx, obj, id, &holder, &prop))
-        return false;
-
-    if (prop.isNonNativeProperty()) {
-        MOZ_ASSERT(!IsCacheableProtoChain(obj, holder));
-        return true;
-    }
-
-    RootedShape shape(cx, prop.maybeShape());
-    bool isScripted = false;
-    bool cacheableCall = IsCacheableSetPropCall(cx, obj, holder, shape,
-                                                &isScripted, isTemporarilyUnoptimizable);
-
-    // Try handling scripted setters.
-    if (cacheableCall && isScripted) {
-        RootedFunction callee(cx, &shape->setterObject()->as<JSFunction>());
-        MOZ_ASSERT(callee->hasScript());
-
-        if (UpdateExistingSetPropCallStubs(stub, ICStub::SetProp_CallScripted,
-                                           &holder->as<NativeObject>(), obj, callee)) {
-            *attached = true;
-            return true;
-        }
-
-        JitSpew(JitSpew_BaselineIC, "  Generating SetProp(NativeObj/ScriptedSetter %s:%" PRIuSIZE ") stub",
-                    callee->nonLazyScript()->filename(), callee->nonLazyScript()->lineno());
-
-        ICSetProp_CallScripted::Compiler compiler(cx, obj, holder, callee, script->pcToOffset(pc));
-        ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
-        if (!newStub)
-            return false;
-
-        stub->addNewStub(newStub);
-        *attached = true;
-        return true;
-    }
-
-    // Try handling JSNative setters.
-    if (cacheableCall && !isScripted) {
-        RootedFunction callee(cx, &shape->setterObject()->as<JSFunction>());
-        MOZ_ASSERT(callee->isNative());
-
-        if (UpdateExistingSetPropCallStubs(stub, ICStub::SetProp_CallNative,
-                                           &holder->as<NativeObject>(), obj, callee)) {
-            *attached = true;
-            return true;
-        }
-
-        JitSpew(JitSpew_BaselineIC, "  Generating SetProp(NativeObj/NativeSetter %p) stub",
-                    callee->native());
-
-        ICSetProp_CallNative::Compiler compiler(cx, obj, holder, callee, script->pcToOffset(pc));
-        ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
-        if (!newStub)
-            return false;
-
-        stub->addNewStub(newStub);
-        *attached = true;
-        return true;
-    }
-
-    return true;
-}
-
 static bool
 DoSetPropFallback(JSContext* cx, BaselineFrame* frame, ICSetProp_Fallback* stub_, Value* stack,
                   HandleValue lhs, HandleValue rhs)
 {
     // This fallback stub may trigger debug mode toggling.
     DebugModeOSRVolatileStub<ICSetProp_Fallback*> stub(frame, stub_);
 
     RootedScript script(cx, frame->script());
@@ -2834,46 +2662,40 @@ DoSetPropFallback(JSContext* cx, Baselin
     RootedReceiverGuard oldGuard(cx, ReceiverGuard(obj));
 
     if (obj->is<UnboxedPlainObject>()) {
         MOZ_ASSERT(!oldShape);
         if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando())
             oldShape = expando->lastProperty();
     }
 
-    bool attached = false;
     // There are some reasons we can fail to attach a stub that are temporary.
     // We want to avoid calling noteUnoptimizableAccess() if the reason we
     // failed to attach a stub is one of those temporary reasons, since we might
     // end up attaching a stub for the exact same access later.
     bool isTemporarilyUnoptimizable = false;
+
+    bool attached = false;
     if (stub->numOptimizedStubs() < ICSetProp_Fallback::MAX_OPTIMIZED_STUBS &&
-        lhs.isObject() &&
-        !TryAttachSetAccessorPropStub(cx, script, pc, stub, obj, oldGuard, name, id,
-                                      rhs, &attached, &isTemporarilyUnoptimizable))
-    {
-        return false;
-    }
-
-    if (!attached &&
-        stub->numOptimizedStubs() < ICSetProp_Fallback::MAX_OPTIMIZED_STUBS &&
         !JitOptions.disableCacheIR)
     {
         RootedValue idVal(cx, StringValue(name));
         SetPropIRGenerator gen(cx, pc, CacheKind::SetProp, &isTemporarilyUnoptimizable,
                                lhs, idVal, rhs);
         if (gen.tryAttachStub()) {
             ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
                                                         ICStubEngine::Baseline, frame->script(), stub);
             if (newStub) {
                 JitSpew(JitSpew_BaselineIC, "  Attached CacheIR stub");
                 attached = true;
 
-                newStub->toCacheIR_Updated()->updateStubGroup() = gen.updateStubGroup();
-                newStub->toCacheIR_Updated()->updateStubId() = gen.updateStubId();
+                if (gen.needUpdateStub()) {
+                    newStub->toCacheIR_Updated()->updateStubGroup() = gen.updateStubGroup();
+                    newStub->toCacheIR_Updated()->updateStubId() = gen.updateStubId();
+                }
 
                 if (gen.shouldNotePreliminaryObjectStub())
                     newStub->toCacheIR_Updated()->notePreliminaryObject();
                 else if (gen.shouldUnlinkPreliminaryObjectStubs())
                     StripPreliminaryObjectStubs(cx, stub);
             }
         }
     }
@@ -3186,214 +3008,16 @@ ICSetPropNativeAddCompiler::generateStub
     masm.bind(&failureUnstow);
     EmitUnstowICValues(masm, 2);
 
     masm.bind(&failure);
     EmitStubGuardFailure(masm);
     return true;
 }
 
-bool
-ICSetProp_CallScripted::Compiler::generateStubCode(MacroAssembler& masm)
-{
-    MOZ_ASSERT(engine_ == Engine::Baseline);
-
-    Label failure;
-    Label failureUnstow;
-    Label failureLeaveStubFrame;
-
-    // Guard input is an object.
-    masm.branchTestObject(Assembler::NotEqual, R0, &failure);
-
-    // Stow R0 and R1 to free up registers.
-    EmitStowICValues(masm, 2);
-
-    AllocatableGeneralRegisterSet regs(availableGeneralRegs(1));
-    Register scratch = regs.takeAnyExcluding(ICTailCallReg);
-
-    // Unbox and shape guard.
-    uint32_t framePushed = masm.framePushed();
-    Register objReg = masm.extractObject(R0, ExtractTemp0);
-    GuardReceiverObject(masm, ReceiverGuard(receiver_), objReg, scratch,
-                        ICSetProp_CallScripted::offsetOfReceiverGuard(), &failureUnstow);
-
-    if (receiver_ != holder_) {
-        Register holderReg = regs.takeAny();
-        masm.loadPtr(Address(ICStubReg, ICSetProp_CallScripted::offsetOfHolder()), holderReg);
-        masm.loadPtr(Address(ICStubReg, ICSetProp_CallScripted::offsetOfHolderShape()), scratch);
-        masm.branchTestObjShape(Assembler::NotEqual, holderReg, scratch, &failureUnstow);
-        regs.add(holderReg);
-    }
-
-    // Push a stub frame so that we can perform a non-tail call.
-    enterStubFrame(masm, scratch);
-
-    // Load callee function and code.  To ensure that |code| doesn't end up being
-    // ArgumentsRectifierReg, if it's available we assign it to |callee| instead.
-    Register callee;
-    if (regs.has(ArgumentsRectifierReg)) {
-        callee = ArgumentsRectifierReg;
-        regs.take(callee);
-    } else {
-        callee = regs.takeAny();
-    }
-    Register code = regs.takeAny();
-    masm.loadPtr(Address(ICStubReg, ICSetProp_CallScripted::offsetOfSetter()), callee);
-    masm.branchIfFunctionHasNoScript(callee, &failureLeaveStubFrame);
-    masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), code);
-    masm.loadBaselineOrIonRaw(code, code, &failureLeaveStubFrame);
-
-    // Align the stack such that the JitFrameLayout is aligned on
-    // JitStackAlignment.
-    masm.alignJitStackBasedOnNArgs(1);
-
-    // Setter is called with the new value as the only argument, and |obj| as thisv.
-    // Note that we use Push, not push, so that callJit will align the stack
-    // properly on ARM.
-
-    // To Push R1, read it off of the stowed values on stack.
-    // Stack: [ ..., R0, R1, ..STUBFRAME-HEADER.., padding? ]
-    masm.PushValue(Address(BaselineFrameReg, STUB_FRAME_SIZE));
-    masm.Push(R0);
-    EmitBaselineCreateStubFrameDescriptor(masm, scratch, JitFrameLayout::Size());
-    masm.Push(Imm32(1));  // ActualArgc is 1
-    masm.Push(callee);
-    masm.Push(scratch);
-
-    // Handle arguments underflow.
-    Label noUnderflow;
-    masm.load16ZeroExtend(Address(callee, JSFunction::offsetOfNargs()), scratch);
-    masm.branch32(Assembler::BelowOrEqual, scratch, Imm32(1), &noUnderflow);
-    {
-        // Call the arguments rectifier.
-        MOZ_ASSERT(ArgumentsRectifierReg != code);
-
-        JitCode* argumentsRectifier =
-            cx->runtime()->jitRuntime()->getArgumentsRectifier();
-
-        masm.movePtr(ImmGCPtr(argumentsRectifier), code);
-        masm.loadPtr(Address(code, JitCode::offsetOfCode()), code);
-        masm.movePtr(ImmWord(1), ArgumentsRectifierReg);
-    }
-
-    masm.bind(&noUnderflow);
-    masm.callJit(code);
-
-    uint32_t framePushedAfterCall = masm.framePushed();
-
-    leaveStubFrame(masm, true);
-    // Do not care about return value from function. The original RHS should be returned
-    // as the result of this operation.
-    EmitUnstowICValues(masm, 2);
-    EmitReturnFromIC(masm);
-
-    // Leave stub frame and go to next stub.
-    masm.bind(&failureLeaveStubFrame);
-    masm.setFramePushed(framePushedAfterCall);
-    inStubFrame_ = true;
-    leaveStubFrame(masm, false);
-
-    // Unstow R0 and R1
-    masm.bind(&failureUnstow);
-    masm.setFramePushed(framePushed);
-    EmitUnstowICValues(masm, 2);
-
-    // Failure case - jump to next stub
-    masm.bind(&failure);
-    EmitStubGuardFailure(masm);
-    return true;
-}
-
-static bool
-DoCallNativeSetter(JSContext* cx, HandleFunction callee, HandleObject obj, HandleValue val)
-{
-    MOZ_ASSERT(callee->isNative());
-    JSNative natfun = callee->native();
-
-    JS::AutoValueArray<3> vp(cx);
-    vp[0].setObject(*callee.get());
-    vp[1].setObject(*obj.get());
-    vp[2].set(val);
-
-    return natfun(cx, 1, vp.begin());
-}
-
-typedef bool (*DoCallNativeSetterFn)(JSContext*, HandleFunction, HandleObject, HandleValue);
-static const VMFunction DoCallNativeSetterInfo =
-    FunctionInfo<DoCallNativeSetterFn>(DoCallNativeSetter, "DoNativeCallSetter");
-
-bool
-ICSetProp_CallNative::Compiler::generateStubCode(MacroAssembler& masm)
-{
-    MOZ_ASSERT(engine_ == Engine::Baseline);
-
-    Label failure;
-    Label failureUnstow;
-
-    // Guard input is an object.
-    masm.branchTestObject(Assembler::NotEqual, R0, &failure);
-
-    // Stow R0 and R1 to free up registers.
-    EmitStowICValues(masm, 2);
-
-    AllocatableGeneralRegisterSet regs(availableGeneralRegs(1));
-    Register scratch = regs.takeAnyExcluding(ICTailCallReg);
-
-    // Unbox and shape guard.
-    uint32_t framePushed = masm.framePushed();
-    Register objReg = masm.extractObject(R0, ExtractTemp0);
-    GuardReceiverObject(masm, ReceiverGuard(receiver_), objReg, scratch,
-                        ICSetProp_CallNative::offsetOfReceiverGuard(), &failureUnstow);
-
-    if (receiver_ != holder_) {
-        Register holderReg = regs.takeAny();
-        masm.loadPtr(Address(ICStubReg, ICSetProp_CallNative::offsetOfHolder()), holderReg);
-        masm.loadPtr(Address(ICStubReg, ICSetProp_CallNative::offsetOfHolderShape()), scratch);
-        masm.branchTestObjShape(Assembler::NotEqual, holderReg, scratch, &failureUnstow);
-        regs.add(holderReg);
-    }
-
-    // Push a stub frame so that we can perform a non-tail call.
-    enterStubFrame(masm, scratch);
-
-    // Load callee function and code.  To ensure that |code| doesn't end up being
-    // ArgumentsRectifierReg, if it's available we assign it to |callee| instead.
-    Register callee = regs.takeAny();
-    masm.loadPtr(Address(ICStubReg, ICSetProp_CallNative::offsetOfSetter()), callee);
-
-    // To Push R1, read it off of the stowed values on stack.
-    // Stack: [ ..., R0, R1, ..STUBFRAME-HEADER.. ]
-    masm.moveStackPtrTo(scratch);
-    masm.pushValue(Address(scratch, STUB_FRAME_SIZE));
-    masm.push(objReg);
-    masm.push(callee);
-
-    // Don't need to preserve R0 anymore.
-    regs.add(R0);
-
-    if (!callVM(DoCallNativeSetterInfo, masm))
-        return false;
-    leaveStubFrame(masm);
-
-    // Do not care about return value from function. The original RHS should be returned
-    // as the result of this operation.
-    EmitUnstowICValues(masm, 2);
-    EmitReturnFromIC(masm);
-
-    // Unstow R0 and R1
-    masm.bind(&failureUnstow);
-    masm.setFramePushed(framePushed);
-    EmitUnstowICValues(masm, 2);
-
-    // Failure case - jump to next stub
-    masm.bind(&failure);
-    EmitStubGuardFailure(masm);
-    return true;
-}
-
 //
 // Call_Fallback
 //
 
 static bool
 TryAttachFunApplyStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, jsbytecode* pc,
                       HandleValue thisv, uint32_t argc, Value* argv, bool* attached)
 {
@@ -6394,46 +6018,16 @@ ICSetPropNativeAddCompiler::ICSetPropNat
     oldGroup_(cx, oldGroup),
     protoChainDepth_(protoChainDepth),
     isFixedSlot_(isFixedSlot),
     offset_(offset)
 {
     MOZ_ASSERT(protoChainDepth_ <= ICSetProp_NativeAdd::MAX_PROTO_CHAIN_DEPTH);
 }
 
-ICSetPropCallSetter::ICSetPropCallSetter(Kind kind, JitCode* stubCode, ReceiverGuard receiverGuard,
-                                         JSObject* holder, Shape* holderShape,
-                                         JSFunction* setter, uint32_t pcOffset)
-  : ICStub(kind, stubCode),
-    receiverGuard_(receiverGuard),
-    holder_(holder),
-    holderShape_(holderShape),
-    setter_(setter),
-    pcOffset_(pcOffset)
-{
-    MOZ_ASSERT(kind == ICStub::SetProp_CallScripted || kind == ICStub::SetProp_CallNative);
-}
-
-/* static */ ICSetProp_CallScripted*
-ICSetProp_CallScripted::Clone(JSContext* cx, ICStubSpace* space, ICStub*,
-                              ICSetProp_CallScripted& other)
-{
-    return New<ICSetProp_CallScripted>(cx, space, other.jitCode(), other.receiverGuard(),
-                                       other.holder_, other.holderShape_, other.setter_,
-                                       other.pcOffset_);
-}
-
-/* static */ ICSetProp_CallNative*
-ICSetProp_CallNative::Clone(JSContext* cx, ICStubSpace* space, ICStub*, ICSetProp_CallNative& other)
-{
-    return New<ICSetProp_CallNative>(cx, space, other.jitCode(), other.receiverGuard(),
-                                     other.holder_, other.holderShape_, other.setter_,
-                                     other.pcOffset_);
-}
-
 ICCall_Scripted::ICCall_Scripted(JitCode* stubCode, ICStub* firstMonitorStub,
                                  JSFunction* callee, JSObject* templateObject,
                                  uint32_t pcOffset)
   : ICMonitoredStub(ICStub::Call_Scripted, stubCode, firstMonitorStub),
     callee_(callee),
     templateObject_(templateObject),
     pcOffset_(pcOffset)
 { }
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -1230,179 +1230,16 @@ class ICSetPropNativeAddCompiler : publi
 
         return newStub<ICSetProp_NativeAddImpl<ProtoChainDepth>>(
             space, getStubCode(), oldGroup_, shapes, newShape, newGroup, offset_);
     }
 
     ICUpdatedStub* getStub(ICStubSpace* space);
 };
 
-// Base stub for calling a setters on a native or unboxed object.
-class ICSetPropCallSetter : public ICStub
-{
-    friend class ICStubSpace;
-
-  protected:
-    // Shape/group of receiver object. Used for both own and proto setters.
-    HeapReceiverGuard receiverGuard_;
-
-    // Holder and holder shape. For own setters, guarding on receiverGuard_ is
-    // sufficient, although Ion may use holder_ and holderShape_ even for own
-    // setters. In this case holderShape_ == receiverGuard_.shape_ (isOwnSetter
-    // below relies on this).
-    GCPtrObject holder_;
-    GCPtrShape holderShape_;
-
-    // Function to call.
-    GCPtrFunction setter_;
-
-    // PC of call, for profiler
-    uint32_t pcOffset_;
-
-    ICSetPropCallSetter(Kind kind, JitCode* stubCode, ReceiverGuard receiverGuard,
-                        JSObject* holder, Shape* holderShape, JSFunction* setter,
-                        uint32_t pcOffset);
-
-  public:
-    HeapReceiverGuard& receiverGuard() {
-        return receiverGuard_;
-    }
-    GCPtrObject& holder() {
-        return holder_;
-    }
-    GCPtrShape& holderShape() {
-        return holderShape_;
-    }
-    GCPtrFunction& setter() {
-        return setter_;
-    }
-
-    bool isOwnSetter() const {
-        MOZ_ASSERT(holder_->isNative());
-        MOZ_ASSERT(holderShape_);
-        return receiverGuard_.shape() == holderShape_;
-    }
-
-    static size_t offsetOfReceiverGuard() {
-        return offsetof(ICSetPropCallSetter, receiverGuard_);
-    }
-    static size_t offsetOfHolder() {
-        return offsetof(ICSetPropCallSetter, holder_);
-    }
-    static size_t offsetOfHolderShape() {
-        return offsetof(ICSetPropCallSetter, holderShape_);
-    }
-    static size_t offsetOfSetter() {
-        return offsetof(ICSetPropCallSetter, setter_);
-    }
-    static size_t offsetOfPCOffset() {
-        return offsetof(ICSetPropCallSetter, pcOffset_);
-    }
-
-    class Compiler : public ICStubCompiler {
-      protected:
-        RootedObject receiver_;
-        RootedObject holder_;
-        RootedFunction setter_;
-        uint32_t pcOffset_;
-
-        virtual int32_t getKey() const {
-            return static_cast<int32_t>(engine_) |
-                  (static_cast<int32_t>(kind) << 1) |
-                  (HeapReceiverGuard::keyBits(receiver_) << 17) |
-                  (static_cast<int32_t>(receiver_ != holder_) << 20);
-        }
-
-      public:
-        Compiler(JSContext* cx, ICStub::Kind kind, HandleObject receiver, HandleObject holder,
-                 HandleFunction setter, uint32_t pcOffset)
-          : ICStubCompiler(cx, kind, Engine::Baseline),
-            receiver_(cx, receiver),
-            holder_(cx, holder),
-            setter_(cx, setter),
-            pcOffset_(pcOffset)
-        {
-            MOZ_ASSERT(kind == ICStub::SetProp_CallScripted || kind == ICStub::SetProp_CallNative);
-        }
-    };
-};
-
-// Stub for calling a scripted setter on a native object.
-class ICSetProp_CallScripted : public ICSetPropCallSetter
-{
-    friend class ICStubSpace;
-
-  protected:
-    ICSetProp_CallScripted(JitCode* stubCode, ReceiverGuard guard, JSObject* holder,
-                           Shape* holderShape, JSFunction* setter, uint32_t pcOffset)
-      : ICSetPropCallSetter(SetProp_CallScripted, stubCode, guard, holder, holderShape,
-                            setter, pcOffset)
-    {}
-
-  public:
-    static ICSetProp_CallScripted* Clone(JSContext* cx, ICStubSpace* space, ICStub*,
-                                         ICSetProp_CallScripted& other);
-
-    class Compiler : public ICSetPropCallSetter::Compiler {
-      protected:
-        MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
-
-      public:
-        Compiler(JSContext* cx, HandleObject obj, HandleObject holder, HandleFunction setter,
-                 uint32_t pcOffset)
-          : ICSetPropCallSetter::Compiler(cx, ICStub::SetProp_CallScripted,
-                                          obj, holder, setter, pcOffset)
-        {}
-
-        ICStub* getStub(ICStubSpace* space) {
-            ReceiverGuard guard(receiver_);
-            Shape* holderShape = holder_->as<NativeObject>().lastProperty();
-            return newStub<ICSetProp_CallScripted>(space, getStubCode(), guard, holder_,
-                                                       holderShape, setter_, pcOffset_);
-        }
-    };
-};
-
-// Stub for calling a native setter on a native object.
-class ICSetProp_CallNative : public ICSetPropCallSetter
-{
-    friend class ICStubSpace;
-
-  protected:
-    ICSetProp_CallNative(JitCode* stubCode, ReceiverGuard guard, JSObject* holder,
-                         Shape* holderShape, JSFunction* setter, uint32_t pcOffset)
-      : ICSetPropCallSetter(SetProp_CallNative, stubCode, guard, holder, holderShape,
-                            setter, pcOffset)
-    {}
-
-  public:
-    static ICSetProp_CallNative* Clone(JSContext* cx,
-                                       ICStubSpace* space, ICStub*,
-                                       ICSetProp_CallNative& other);
-
-    class Compiler : public ICSetPropCallSetter::Compiler {
-      protected:
-        MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
-
-      public:
-        Compiler(JSContext* cx, HandleObject obj, HandleObject holder, HandleFunction setter,
-                 uint32_t pcOffset)
-          : ICSetPropCallSetter::Compiler(cx, ICStub::SetProp_CallNative,
-                                          obj, holder, setter, pcOffset)
-        {}
-
-        ICStub* getStub(ICStubSpace* space) {
-            ReceiverGuard guard(receiver_);
-            Shape* holderShape = holder_->as<NativeObject>().lastProperty();
-            return newStub<ICSetProp_CallNative>(space, getStubCode(), guard, holder_, holderShape,
-                                                 setter_, pcOffset_);
-        }
-    };
-};
-
 // Call
 //      JSOP_CALL
 //      JSOP_FUNAPPLY
 //      JSOP_FUNCALL
 //      JSOP_NEW
 //      JSOP_SPREADCALL
 //      JSOP_SPREADNEW
 //      JSOP_SPREADEVAL
--- a/js/src/jit/BaselineICList.h
+++ b/js/src/jit/BaselineICList.h
@@ -65,18 +65,16 @@ namespace jit {
                                                  \
     _(BindName_Fallback)                         \
                                                  \
     _(GetIntrinsic_Fallback)                     \
     _(GetIntrinsic_Constant)                     \
                                                  \
     _(SetProp_Fallback)                          \
     _(SetProp_NativeAdd)                         \
-    _(SetProp_CallScripted)                      \
-    _(SetProp_CallNative)                        \
                                                  \
     _(TableSwitch)                               \
                                                  \
     _(IteratorNew_Fallback)                      \
     _(IteratorMore_Fallback)                     \
     _(IteratorMore_Native)                       \
     _(IteratorClose_Fallback)                    \
                                                  \
--- a/js/src/jit/BaselineInspector.cpp
+++ b/js/src/jit/BaselineInspector.cpp
@@ -749,18 +749,18 @@ BaselineInspector::templateCallObject()
 
     JSObject* res = baselineScript()->templateEnvironment();
     MOZ_ASSERT(res);
 
     return &res->as<CallObject>();
 }
 
 static bool
-MatchCacheIRReceiverGuard(CacheIRReader& reader, ICCacheIR_Monitored* stub, ObjOperandId objId,
-                          ReceiverGuard* receiver)
+MatchCacheIRReceiverGuard(CacheIRReader& reader, ICStub* stub, const CacheIRStubInfo* stubInfo,
+                          ObjOperandId objId, ReceiverGuard* receiver)
 {
     // This matches the CacheIR emitted in TestMatchingReceiver.
     //
     // Either:
     //
     //   GuardShape objId
     //
     // or:
@@ -773,36 +773,36 @@ MatchCacheIRReceiverGuard(CacheIRReader&
     //   GuardGroup objId
     //   expandoId: GuardAndLoadUnboxedExpando
     //   GuardShape expandoId
 
     *receiver = ReceiverGuard();
 
     if (reader.matchOp(CacheOp::GuardShape, objId)) {
         // The first case.
-        receiver->shape = stub->stubInfo()->getStubField<Shape*>(stub, reader.stubOffset());
+        receiver->shape = stubInfo->getStubField<Shape*>(stub, reader.stubOffset());
         return true;
     }
 
     if (!reader.matchOp(CacheOp::GuardGroup, objId))
         return false;
-    receiver->group = stub->stubInfo()->getStubField<ObjectGroup*>(stub, reader.stubOffset());
+    receiver->group = stubInfo->getStubField<ObjectGroup*>(stub, reader.stubOffset());
 
     if (!reader.matchOp(CacheOp::GuardAndLoadUnboxedExpando, objId)) {
         // Second case, just a group guard.
         reader.matchOp(CacheOp::GuardNoUnboxedExpando, objId);
         return true;
     }
 
     // Third case.
     ObjOperandId expandoId = reader.objOperandId();
     if (!reader.matchOp(CacheOp::GuardShape, expandoId))
         return false;
 
-    receiver->shape = stub->stubInfo()->getStubField<Shape*>(stub, reader.stubOffset());
+    receiver->shape = stubInfo->getStubField<Shape*>(stub, reader.stubOffset());
     return true;
 }
 
 static bool
 AddCacheIRGlobalGetter(ICCacheIR_Monitored* stub, bool innerized,
                        JSObject** holder_, Shape** holderShape_,
                        JSFunction** commonGetter, Shape** globalShape_, bool* isOwnProperty,
                        BaselineInspector::ReceiverVector& receivers,
@@ -928,17 +928,17 @@ AddCacheIRGetPropFunction(ICCacheIR_Moni
             return false;
         objId = reader.objOperandId();
         DebugOnly<JSObject*> obj =
             stub->stubInfo()->getStubField<JSObject*>(stub, reader.stubOffset()).get();
         MOZ_ASSERT(obj->is<GlobalObject>());
     }
 
     ReceiverGuard receiver;
-    if (!MatchCacheIRReceiverGuard(reader, stub, objId, &receiver))
+    if (!MatchCacheIRReceiverGuard(reader, stub, stub->stubInfo(), objId, &receiver))
         return false;
 
     if (reader.matchOp(CacheOp::CallScriptedGetterResult, objId) ||
         reader.matchOp(CacheOp::CallNativeGetterResult, objId))
     {
         // This is an own property getter, the first case.
         MOZ_ASSERT(receiver.shape);
         MOZ_ASSERT(!receiver.group);
@@ -1050,47 +1050,131 @@ BaselineInspector::commonGetPropFunction
     if (!*commonGetter)
         return false;
 
     MOZ_ASSERT(*isOwnProperty == !*holder);
     MOZ_ASSERT(*isOwnProperty == (receivers.empty() && convertUnboxedGroups.empty()));
     return true;
 }
 
+static bool
+AddCacheIRSetPropFunction(ICCacheIR_Updated* stub, JSObject** holder, Shape** holderShape,
+                          JSFunction** commonSetter, bool* isOwnProperty,
+                          BaselineInspector::ReceiverVector& receivers,
+                          BaselineInspector::ObjectGroupVector& convertUnboxedGroups)
+{
+    // We match either an own setter:
+    //
+    //   GuardIsObject objId
+    //   <GuardReceiver objId>
+    //   Call(Scripted|Native)Setter objId
+    //
+    // Or a setter on the prototype:
+    //
+    //   GuardIsObject objId
+    //   <GuardReceiver objId>
+    //   LoadObject holderId
+    //   GuardShape holderId
+    //   Call(Scripted|Native)Setter objId
+
+    CacheIRReader reader(stub->stubInfo());
+
+    ObjOperandId objId = ObjOperandId(0);
+    if (!reader.matchOp(CacheOp::GuardIsObject, objId))
+        return false;
+
+    ReceiverGuard receiver;
+    if (!MatchCacheIRReceiverGuard(reader, stub, stub->stubInfo(), objId, &receiver))
+        return false;
+
+    if (reader.matchOp(CacheOp::CallScriptedSetter, objId) ||
+        reader.matchOp(CacheOp::CallNativeSetter, objId))
+    {
+        // This is an own property setter, the first case.
+        MOZ_ASSERT(receiver.shape);
+        MOZ_ASSERT(!receiver.group);
+
+        size_t offset = reader.stubOffset();
+        JSFunction* setter =
+            &stub->stubInfo()->getStubField<JSObject*>(stub, offset)->as<JSFunction>();
+
+        if (*commonSetter && (!*isOwnProperty || *holderShape != receiver.shape))
+            return false;
+
+        MOZ_ASSERT_IF(*commonSetter, *commonSetter == setter);
+        *holder = nullptr;
+        *holderShape = receiver.shape;
+        *commonSetter = setter;
+        *isOwnProperty = true;
+        return true;
+    }
+
+    if (!reader.matchOp(CacheOp::LoadObject))
+        return false;
+    ObjOperandId holderId = reader.objOperandId();
+    JSObject* obj = stub->stubInfo()->getStubField<JSObject*>(stub, reader.stubOffset());
+
+    if (!reader.matchOp(CacheOp::GuardShape, holderId))
+        return false;
+    Shape* objShape = stub->stubInfo()->getStubField<Shape*>(stub, reader.stubOffset());
+
+    if (!reader.matchOp(CacheOp::CallScriptedSetter, objId) &&
+        !reader.matchOp(CacheOp::CallNativeSetter, objId))
+    {
+        return false;
+    }
+
+    // A setter on the prototype.
+    size_t offset = reader.stubOffset();
+    JSFunction* setter =
+        &stub->stubInfo()->getStubField<JSObject*>(stub, offset)->as<JSFunction>();
+
+    if (*commonSetter && (*isOwnProperty || *holderShape != objShape))
+        return false;
+
+    MOZ_ASSERT_IF(*commonSetter, *commonSetter == setter);
+
+    if (obj->as<NativeObject>().lastProperty() != objShape) {
+        // Skip this stub as the shape is no longer correct.
+        return true;
+    }
+
+    if (!AddReceiver(receiver, receivers, convertUnboxedGroups))
+        return false;
+
+    *holder = obj;
+    *holderShape = objShape;
+    *commonSetter = setter;
+    *isOwnProperty = false;
+    return true;
+}
+
 bool
 BaselineInspector::commonSetPropFunction(jsbytecode* pc, JSObject** holder, Shape** holderShape,
                                          JSFunction** commonSetter, bool* isOwnProperty,
                                          ReceiverVector& receivers,
                                          ObjectGroupVector& convertUnboxedGroups)
 {
     if (!hasBaselineScript())
         return false;
 
     MOZ_ASSERT(receivers.empty());
     MOZ_ASSERT(convertUnboxedGroups.empty());
 
     *commonSetter = nullptr;
     const ICEntry& entry = icEntryFromPC(pc);
 
     for (ICStub* stub = entry.firstStub(); stub; stub = stub->next()) {
-        if (stub->isSetProp_CallScripted() || stub->isSetProp_CallNative()) {
-            ICSetPropCallSetter* nstub = static_cast<ICSetPropCallSetter*>(stub);
-            bool isOwn = nstub->isOwnSetter();
-            if (!isOwn && !AddReceiver(nstub->receiverGuard(), receivers, convertUnboxedGroups))
+        if (stub->isCacheIR_Updated()) {
+            if (!AddCacheIRSetPropFunction(stub->toCacheIR_Updated(),
+                                           holder, holderShape,
+                                           commonSetter, isOwnProperty, receivers,
+                                           convertUnboxedGroups))
+            {
                 return false;
-
-            if (!*commonSetter) {
-                *holder = isOwn ? nullptr : nstub->holder().get();
-                *holderShape = nstub->holderShape();
-                *commonSetter = nstub->setter();
-                *isOwnProperty = isOwn;
-            } else if (nstub->holderShape() != *holderShape || isOwn != *isOwnProperty) {
-                return false;
-            } else {
-                MOZ_ASSERT(*commonSetter == nstub->setter());
             }
         } else if (!stub->isSetProp_Fallback() ||
                    stub->toSetProp_Fallback()->hadUnoptimizableAccess())
         {
             // We have an unoptimizable access, so don't try to optimize.
             return false;
         }
     }
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -1619,16 +1619,18 @@ SetPropIRGenerator::tryAttachStub()
         ObjOperandId objId = writer.guardIsObject(lhsValId);
         if (nameOrSymbol) {
             if (tryAttachNativeSetSlot(obj, objId, id, rhsValId))
                 return true;
             if (tryAttachUnboxedExpandoSetSlot(obj, objId, id, rhsValId))
                 return true;
             if (tryAttachUnboxedProperty(obj, objId, id, rhsValId))
                 return true;
+            if (tryAttachSetter(obj, objId, id, rhsValId))
+                return true;
             if (tryAttachTypedObjectProperty(obj, objId, id, rhsValId))
                 return true;
         }
         return false;
     }
 
     return false;
 }
@@ -1813,8 +1815,62 @@ SetPropIRGenerator::tryAttachTypedObject
         writer.guardType(rhsId, JSVAL_TYPE_STRING);
         break;
     }
 
     writer.storeTypedObjectReferenceProperty(objId, fieldOffset, layout, type, rhsId);
     writer.returnFromIC();
     return true;
 }
+
+static void
+EmitCallSetterResultNoGuards(CacheIRWriter& writer, JSObject* obj, JSObject* holder,
+                             Shape* shape, ObjOperandId objId, ValOperandId rhsId)
+{
+    if (IsCacheableSetPropCallNative(obj, holder, shape)) {
+        JSFunction* target = &shape->setterValue().toObject().as<JSFunction>();
+        MOZ_ASSERT(target->isNative());
+        writer.callNativeSetter(objId, target, rhsId);
+        writer.returnFromIC();
+        return;
+    }
+
+    MOZ_ASSERT(IsCacheableSetPropCallScripted(obj, holder, shape));
+
+    JSFunction* target = &shape->setterValue().toObject().as<JSFunction>();
+    MOZ_ASSERT(target->hasJITCode());
+    writer.callScriptedSetter(objId, target, rhsId);
+    writer.returnFromIC();
+}
+
+bool
+SetPropIRGenerator::tryAttachSetter(HandleObject obj, ObjOperandId objId, HandleId id,
+                                    ValOperandId rhsId)
+{
+    PropertyResult prop;
+    JSObject* holder;
+    if (!LookupPropertyPure(cx_, obj, id, &holder, &prop))
+        return false;
+
+    if (prop.isNonNativeProperty())
+        return false;
+
+    Shape* shape = prop.maybeShape();
+    if (!IsCacheableSetPropCallScripted(obj, holder, shape, isTemporarilyUnoptimizable_) &&
+        !IsCacheableSetPropCallNative(obj, holder, shape))
+    {
+        return false;
+    }
+
+    Maybe<ObjOperandId> expandoId;
+    TestMatchingReceiver(writer, obj, shape, objId, &expandoId);
+
+    if (obj != holder) {
+        GeneratePrototypeGuards(writer, obj, holder, objId);
+
+        // Guard on the holder's shape.
+        ObjOperandId holderId = writer.loadObject(holder);
+        writer.guardShape(holderId, holder->as<NativeObject>().lastProperty());
+    }
+
+    EmitCallSetterResultNoGuards(writer, obj, holder, shape, objId, rhsId);
+    return true;
+}
--- a/js/src/jit/CacheIR.h
+++ b/js/src/jit/CacheIR.h
@@ -170,16 +170,18 @@ enum class CacheKind : uint8_t
     _(LoadDOMExpandoValueIgnoreGeneration)\
     _(GuardDOMExpandoMissingOrGuardShape) \
                                           \
     _(StoreFixedSlot)                     \
     _(StoreDynamicSlot)                   \
     _(StoreTypedObjectReferenceProperty)  \
     _(StoreTypedObjectScalarProperty)     \
     _(StoreUnboxedProperty)               \
+    _(CallNativeSetter)                   \
+    _(CallScriptedSetter)                 \
                                           \
     /* The *Result ops load a value into the cache's result register. */ \
     _(LoadFixedSlotResult)                \
     _(LoadDynamicSlotResult)              \
     _(LoadUnboxedPropertyResult)          \
     _(LoadTypedObjectResult)              \
     _(LoadDenseElementResult)             \
     _(LoadDenseElementHoleResult)         \
@@ -593,16 +595,26 @@ class MOZ_RAII CacheIRWriter : public JS
     void storeUnboxedProperty(ObjOperandId obj, JSValueType type, size_t offset,
                               ValOperandId rhs)
     {
         writeOpWithOperandId(CacheOp::StoreUnboxedProperty, obj);
         buffer_.writeByte(uint32_t(type));
         addStubField(offset, StubField::Type::RawWord);
         writeOperandId(rhs);
     }
+    void callScriptedSetter(ObjOperandId obj, JSFunction* setter, ValOperandId rhs) {
+        writeOpWithOperandId(CacheOp::CallScriptedSetter, obj);
+        addStubField(uintptr_t(setter), StubField::Type::JSObject);
+        writeOperandId(rhs);
+    }
+    void callNativeSetter(ObjOperandId obj, JSFunction* setter, ValOperandId rhs) {
+        writeOpWithOperandId(CacheOp::CallNativeSetter, obj);
+        addStubField(uintptr_t(setter), StubField::Type::JSObject);
+        writeOperandId(rhs);
+    }
 
     void loadUndefinedResult() {
         writeOp(CacheOp::LoadUndefinedResult);
     }
     void loadFixedSlotResult(ObjOperandId obj, size_t offset) {
         writeOpWithOperandId(CacheOp::LoadFixedSlotResult, obj);
         addStubField(offset, StubField::Type::RawWord);
     }
@@ -910,30 +922,35 @@ class MOZ_RAII SetPropIRGenerator : publ
     bool tryAttachNativeSetSlot(HandleObject obj, ObjOperandId objId, HandleId id,
                                  ValOperandId rhsId);
     bool tryAttachUnboxedExpandoSetSlot(HandleObject obj, ObjOperandId objId, HandleId id,
                                         ValOperandId rhsId);
     bool tryAttachUnboxedProperty(HandleObject obj, ObjOperandId objId, HandleId id,
                                   ValOperandId rhsId);
     bool tryAttachTypedObjectProperty(HandleObject obj, ObjOperandId objId, HandleId id,
                                       ValOperandId rhsId);
+    bool tryAttachSetter(HandleObject obj, ObjOperandId objId, HandleId id,
+                         ValOperandId rhsId);
 
   public:
     SetPropIRGenerator(JSContext* cx, jsbytecode* pc, CacheKind cacheKind,
                        bool* isTemporarilyUnoptimizable, HandleValue lhsVal, HandleValue idVal,
                        HandleValue rhsVal);
 
     bool tryAttachStub();
 
     bool shouldUnlinkPreliminaryObjectStubs() const {
         return preliminaryObjectAction_ == PreliminaryObjectAction::Unlink;
     }
     bool shouldNotePreliminaryObjectStub() const {
         return preliminaryObjectAction_ == PreliminaryObjectAction::NotePreliminary;
     }
+
+    bool needUpdateStub() const { return needUpdateStub_; }
+
     ObjectGroup* updateStubGroup() const {
         MOZ_ASSERT(updateStubGroup_);
         return updateStubGroup_;
     }
     jsid updateStubId() const {
         MOZ_ASSERT(needUpdateStub_);
         return updateStubId_;
     }
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -214,17 +214,17 @@ CodeGenerator::visitOutOfLineCache(OutOf
     // Register the location of the OOL path in the IC.
     cache->setFallbackLabel(masm.labelForPatch());
     masm.bind(&ool->entry());
 
     // Dispatch to ICs' accept functions.
     cache->accept(this, ool);
 }
 
-typedef bool (*IonGetPropertyICFn)(JSContext*, HandleScript, IonGetPropertyIC*, HandleObject, HandleValue,
+typedef bool (*IonGetPropertyICFn)(JSContext*, HandleScript, IonGetPropertyIC*, HandleValue, HandleValue,
                                    MutableHandleValue);
 const VMFunction IonGetPropertyICInfo =
     FunctionInfo<IonGetPropertyICFn>(IonGetPropertyIC::update, "IonGetPropertyIC::update");
 
 void
 CodeGenerator::visitOutOfLineICFallback(OutOfLineICFallback* ool)
 {
     LInstruction* lir = ool->lir();
@@ -239,17 +239,17 @@ CodeGenerator::visitOutOfLineICFallback(
     switch (ic->kind()) {
       case CacheKind::GetProp:
       case CacheKind::GetElem: {
         IonGetPropertyIC* getPropIC = ic->asGetPropertyIC();
 
         saveLive(lir);
 
         pushArg(getPropIC->id());
-        pushArg(getPropIC->object());
+        pushArg(getPropIC->value());
         icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
         pushArg(ImmGCPtr(gen->info().script()));
 
         callVM(IonGetPropertyICInfo, lir);
 
         StoreValueTo(getPropIC->output()).generate(this);
         restoreLiveIgnore(lir, StoreValueTo(getPropIC->output()).clobbered());
 
@@ -10203,29 +10203,30 @@ CodeGenerator::visitNameIC(OutOfLineUpda
     callVM(NameIC::UpdateInfo, lir);
     StoreValueTo(ic->outputReg()).generate(this);
     restoreLiveIgnore(lir, StoreValueTo(ic->outputReg()).clobbered());
 
     masm.jump(ool->rejoin());
 }
 
 void
-CodeGenerator::addGetPropertyCache(LInstruction* ins, LiveRegisterSet liveRegs, Register objReg,
-                                   const ConstantOrRegister& id, TypedOrValueRegister output,
-                                   Register maybeTemp, bool monitoredResult,
-                                   bool allowDoubleResult, jsbytecode* profilerLeavePc)
+CodeGenerator::addGetPropertyCache(LInstruction* ins, LiveRegisterSet liveRegs,
+                                   TypedOrValueRegister value, const ConstantOrRegister& id,
+                                   TypedOrValueRegister output, Register maybeTemp,
+                                   bool monitoredResult, bool allowDoubleResult,
+                                   jsbytecode* profilerLeavePc)
 {
     CacheKind kind = CacheKind::GetElem;
     if (id.constant() && id.value().isString()) {
         JSString* idString = id.value().toString();
         uint32_t dummy;
         if (idString->isAtom() && !idString->asAtom().isIndex(&dummy))
             kind = CacheKind::GetProp;
     }
-    IonGetPropertyIC cache(kind, liveRegs, objReg, id, output, maybeTemp, monitoredResult,
+    IonGetPropertyIC cache(kind, liveRegs, value, id, output, maybeTemp, monitoredResult,
                            allowDoubleResult);
     addIC(ins, allocateIC(cache));
 }
 
 void
 CodeGenerator::addSetPropertyCache(LInstruction* ins, LiveRegisterSet liveRegs, Register objReg,
                                    Register temp, Register tempUnbox, FloatRegister tempDouble,
                                    FloatRegister tempF32, const ConstantOrRegister& id,
@@ -10251,37 +10252,39 @@ CodeGenerator::toConstantOrRegister(LIns
 
     return TypedOrValueRegister(type, ToAnyRegister(value));
 }
 
 void
 CodeGenerator::visitGetPropertyCacheV(LGetPropertyCacheV* ins)
 {
     LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
-    Register objReg = ToRegister(ins->getOperand(0));
+    TypedOrValueRegister value =
+        toConstantOrRegister(ins, LGetPropertyCacheV::Value, ins->mir()->value()->type()).reg();
     ConstantOrRegister id = toConstantOrRegister(ins, LGetPropertyCacheV::Id, ins->mir()->idval()->type());
     bool monitoredResult = ins->mir()->monitoredResult();
     TypedOrValueRegister output = TypedOrValueRegister(GetValueOutput(ins));
     Register maybeTemp = ins->temp()->isBogusTemp() ? InvalidReg : ToRegister(ins->temp());
 
-    addGetPropertyCache(ins, liveRegs, objReg, id, output, maybeTemp, monitoredResult,
+    addGetPropertyCache(ins, liveRegs, value, id, output, maybeTemp, monitoredResult,
                         ins->mir()->allowDoubleResult(), ins->mir()->profilerLeavePc());
 }
 
 void
 CodeGenerator::visitGetPropertyCacheT(LGetPropertyCacheT* ins)
 {
     LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
-    Register objReg = ToRegister(ins->getOperand(0));
+    TypedOrValueRegister value =
+        toConstantOrRegister(ins, LGetPropertyCacheV::Value, ins->mir()->value()->type()).reg();
     ConstantOrRegister id = toConstantOrRegister(ins, LGetPropertyCacheT::Id, ins->mir()->idval()->type());
     bool monitoredResult = ins->mir()->monitoredResult();
     TypedOrValueRegister output(ins->mir()->type(), ToAnyRegister(ins->getDef(0)));
     Register maybeTemp = ins->temp()->isBogusTemp() ? InvalidReg : ToRegister(ins->temp());
 
-    addGetPropertyCache(ins, liveRegs, objReg, id, output, maybeTemp, monitoredResult,
+    addGetPropertyCache(ins, liveRegs, value, id, output, maybeTemp, monitoredResult,
                         ins->mir()->allowDoubleResult(), ins->mir()->profilerLeavePc());
 }
 
 void
 CodeGenerator::visitBindNameCache(LBindNameCache* ins)
 {
     Register envChain = ToRegister(ins->environmentChain());
     Register output = ToRegister(ins->output());
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -442,20 +442,20 @@ class CodeGenerator final : public CodeG
 
     IonScriptCounts* extractScriptCounts() {
         IonScriptCounts* counts = scriptCounts_;
         scriptCounts_ = nullptr;  // prevent delete in dtor
         return counts;
     }
 
   private:
-    void addGetPropertyCache(LInstruction* ins, LiveRegisterSet liveRegs, Register objReg,
-                             const ConstantOrRegister& id, TypedOrValueRegister output,
-                             Register maybeTemp, bool monitoredResult, bool allowDoubleResult,
-                             jsbytecode* profilerLeavePc);
+    void addGetPropertyCache(LInstruction* ins, LiveRegisterSet liveRegs,
+                             TypedOrValueRegister value, const ConstantOrRegister& id,
+                             TypedOrValueRegister output, Register maybeTemp, bool monitoredResult,
+                             bool allowDoubleResult, jsbytecode* profilerLeavePc);
     void addSetPropertyCache(LInstruction* ins, LiveRegisterSet liveRegs, Register objReg,
                              Register temp, Register tempUnbox, FloatRegister tempDouble,
                              FloatRegister tempF32, const ConstantOrRegister& id,
                              const ConstantOrRegister& value,
                              bool strict, bool needsTypeBarrier, bool guardHoles,
                              jsbytecode* profilerLeavePc);
 
     MOZ_MUST_USE bool generateBranchV(const ValueOperand& value, Label* ifTrue, Label* ifFalse,
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -668,26 +668,27 @@ JitCompartment::sweep(FreeOp* fop, JSCom
         baselineCallReturnAddrs_[1] = nullptr;
 
     // Similarly for the ICGetProp_Fallback stub.
     if (!stubCodes_->lookup(ICGetProp_Fallback::Compiler::BASELINE_KEY))
         baselineGetPropReturnAddr_ = nullptr;
     if (!stubCodes_->lookup(ICSetProp_Fallback::Compiler::BASELINE_KEY))
         baselineSetPropReturnAddr_ = nullptr;
 
-    if (stringConcatStub_ && !IsMarkedUnbarriered(&stringConcatStub_))
+    JSRuntime* rt = fop->runtime();
+    if (stringConcatStub_ && !IsMarkedUnbarriered(rt, &stringConcatStub_))
         stringConcatStub_ = nullptr;
 
-    if (regExpMatcherStub_ && !IsMarkedUnbarriered(&regExpMatcherStub_))
+    if (regExpMatcherStub_ && !IsMarkedUnbarriered(rt, &regExpMatcherStub_))
         regExpMatcherStub_ = nullptr;
 
-    if (regExpSearcherStub_ && !IsMarkedUnbarriered(&regExpSearcherStub_))
+    if (regExpSearcherStub_ && !IsMarkedUnbarriered(rt, &regExpSearcherStub_))
         regExpSearcherStub_ = nullptr;
 
-    if (regExpTesterStub_ && !IsMarkedUnbarriered(&regExpTesterStub_))
+    if (regExpTesterStub_ && !IsMarkedUnbarriered(rt, &regExpTesterStub_))
         regExpTesterStub_ = nullptr;
 
     for (ReadBarrieredObject& obj : simdTemplateObjects_) {
         if (obj && IsAboutToBeFinalized(&obj))
             obj.set(nullptr);
     }
 }
 
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -4030,18 +4030,20 @@ IonBuilder::selectInliningTargets(const 
 
     MOZ_ASSERT(choiceSet.length() == targets.length());
     return Ok();
 }
 
 static bool
 CanInlineGetPropertyCache(MGetPropertyCache* cache, MDefinition* thisDef)
 {
-    MOZ_ASSERT(cache->object()->type() == MIRType::Object);
-    if (cache->object() != thisDef)
+    if (cache->value()->type() != MIRType::Object)
+        return false;
+
+    if (cache->value() != thisDef)
         return false;
 
     InlinePropertyTable* table = cache->propTable();
     if (!table)
         return false;
     if (table->numEntries() == 0)
         return false;
     return true;
@@ -4318,17 +4320,17 @@ IonBuilder::inlineObjectGroupFallback(Ca
     MOZ_TRY_VAR(getPropBlock, newBlock(prepBlock, propTable->pc(), priorResumePoint));
     graph().addBlock(getPropBlock);
 
     prepBlock->end(MGoto::New(alloc(), getPropBlock));
 
     // Since the getPropBlock inherited the stack from right before the MGetPropertyCache,
     // the target of the MGetPropertyCache is still on the stack.
     DebugOnly<MDefinition*> checkObject = getPropBlock->pop();
-    MOZ_ASSERT(checkObject == cache->object());
+    MOZ_ASSERT(checkObject == cache->value());
 
     // Move the MGetPropertyCache and friends into the getPropBlock.
     if (fallbackInfo.fun()->isGetPropertyCache()) {
         MOZ_ASSERT(fallbackInfo.fun()->toGetPropertyCache() == cache);
         getPropBlock->addFromElsewhere(cache);
         getPropBlock->push(cache);
     } else {
         MTypeBarrier* barrier = callInfo.fun()->toTypeBarrier();
@@ -4360,16 +4362,17 @@ AbortReasonOr<Ok>
 IonBuilder::inlineCalls(CallInfo& callInfo, const ObjectVector& targets, BoolVector& choiceSet,
                         MGetPropertyCache* maybeCache)
 {
     // Only handle polymorphic inlining.
     MOZ_ASSERT(IsIonInlinablePC(pc));
     MOZ_ASSERT(choiceSet.length() == targets.length());
     MOZ_ASSERT_IF(!maybeCache, targets.length() >= 2);
     MOZ_ASSERT_IF(maybeCache, targets.length() >= 1);
+    MOZ_ASSERT_IF(maybeCache, maybeCache->value()->type() == MIRType::Object);
 
     MBasicBlock* dispatchBlock = current;
     callInfo.setImplicitlyUsedUnchecked();
     callInfo.pushFormals(dispatchBlock);
 
     // Patch any InlinePropertyTable to only contain functions that are
     // inlineable. The InlinePropertyTable will also be patched at the end to
     // exclude native functions that vetoed inlining.
@@ -4378,17 +4381,17 @@ IonBuilder::inlineCalls(CallInfo& callIn
         propTable->trimToTargets(targets);
         if (propTable->numEntries() == 0)
             maybeCache = nullptr;
     }
 
     // Generate a dispatch based on guard kind.
     MDispatchInstruction* dispatch;
     if (maybeCache) {
-        dispatch = MObjectGroupDispatch::New(alloc(), maybeCache->object(), maybeCache->propTable());
+        dispatch = MObjectGroupDispatch::New(alloc(), maybeCache->value(), maybeCache->propTable());
         callInfo.fun()->setImplicitlyUsedUnchecked();
     } else {
         dispatch = MFunctionDispatch::New(alloc(), callInfo.fun());
     }
 
     // Generate a return block to host the rval-collecting MPhi.
     jsbytecode* postCall = GetNextPc(pc);
     MBasicBlock* returnBlock;
@@ -4462,17 +4465,17 @@ IonBuilder::inlineCalls(CallInfo& callIn
         if (!inlineInfo.init(callInfo))
             return abort(AbortReason::Alloc);
         inlineInfo.popFormals(inlineBlock);
         inlineInfo.setFun(funcDef);
 
         if (maybeCache) {
             // Assign the 'this' value a TypeSet specialized to the groups that
             // can generate this inlining target.
-            MOZ_ASSERT(callInfo.thisArg() == maybeCache->object());
+            MOZ_ASSERT(callInfo.thisArg() == maybeCache->value());
             TemporaryTypeSet* thisTypes = maybeCache->propTable()->buildTypeSetForFunction(target);
             if (!thisTypes)
                 return abort(AbortReason::Alloc);
 
             MFilterTypeSet* filter = MFilterTypeSet::New(alloc(), inlineInfo.thisArg(), thisTypes);
             inlineBlock->add(filter);
             inlineInfo.setThis(filter);
         }
@@ -4516,17 +4519,17 @@ IonBuilder::inlineCalls(CallInfo& callIn
             // If all paths were vetoed, output only a generic fallback path.
             MOZ_ASSERT(dispatch->numCases() == 0);
             maybeCache = nullptr;
             useFallback = true;
         } else {
             // We need a fallback path if the ObjectGroup dispatch does not
             // handle all incoming objects.
             useFallback = false;
-            TemporaryTypeSet* objectTypes = maybeCache->object()->resultTypeSet();
+            TemporaryTypeSet* objectTypes = maybeCache->value()->resultTypeSet();
             for (uint32_t i = 0; i < objectTypes->getObjectCount(); i++) {
                 TypeSet::ObjectKey* obj = objectTypes->getObject(i);
                 if (!obj)
                     continue;
 
                 if (!obj->isGroup()) {
                     useFallback = true;
                     break;
@@ -10072,43 +10075,19 @@ IonBuilder::jsop_getprop(PropertyName* n
 
         // Try to emit loads from a module namespace.
         trackOptimizationAttempt(TrackedStrategy::GetProp_ModuleNamespace);
         MOZ_TRY(getPropTryModuleNamespace(&emitted, obj, name, barrier, types));
         if (emitted)
             return Ok();
     }
 
-    // Try to emit a polymorphic cache.
+    // Emit a polymorphic cache.
     trackOptimizationAttempt(TrackedStrategy::GetProp_InlineCache);
-    MOZ_TRY(getPropTryCache(&emitted, obj, name, barrier, types));
-    if (emitted)
-        return Ok();
-
-    // Try to emit a shared stub.
-    trackOptimizationAttempt(TrackedStrategy::GetProp_SharedCache);
-    MOZ_TRY(getPropTrySharedStub(&emitted, obj, types));
-    if (emitted)
-        return Ok();
-
-    // Emit a call.
-    MCallGetProperty* call = MCallGetProperty::New(alloc(), obj, name);
-    current->add(call);
-    current->push(call);
-    MOZ_TRY(resumeAfter(call));
-
-    if (*pc == JSOP_CALLPROP && IsNullOrUndefined(obj->type())) {
-        // Due to inlining, it's possible the observed TypeSet is non-empty,
-        // even though we know |obj| is null/undefined and the MCallGetProperty
-        // will throw. Don't push a TypeBarrier in this case, to avoid
-        // inlining the following (unreachable) JSOP_CALL.
-        return Ok();
-    }
-
-    return pushTypeBarrier(call, types, BarrierKind::TypeSet);
+    return getPropAddCache(obj, name, barrier, types);
 }
 
 AbortReasonOr<Ok>
 IonBuilder::improveThisTypesForCall()
 {
     // After a CALLPROP (or CALLELEM) for obj.prop(), the this-value and callee
     // for the call are on top of the stack:
     //
@@ -10970,30 +10949,23 @@ IonBuilder::getPropTryInlineAccess(bool*
     MOZ_TRY(pushTypeBarrier(load, types, barrier));
 
     trackOptimizationOutcome(TrackedOutcome::Polymorphic);
     *emitted = true;
     return Ok();
 }
 
 AbortReasonOr<Ok>
-IonBuilder::getPropTryCache(bool* emitted, MDefinition* obj, PropertyName* name,
+IonBuilder::getPropAddCache(MDefinition* obj, PropertyName* name,
                             BarrierKind barrier, TemporaryTypeSet* types)
 {
-    MOZ_ASSERT(*emitted == false);
-
-    // The input value must either be an object, or we should have strong suspicions
-    // that it can be safely unboxed to an object.
-    if (obj->type() != MIRType::Object) {
-        TemporaryTypeSet* types = obj->resultTypeSet();
-        if (!types || !types->objectOrSentinel()) {
-            trackOptimizationOutcome(TrackedOutcome::NoTypeInfo);
-            return Ok();
-        }
-    }
+    // PropertyReadNeedsTypeBarrier only accounts for object types, so for now
+    // always insert a barrier if the input is not known to be an object.
+    if (obj->type() != MIRType::Object)
+        barrier = BarrierKind::TypeSet;
 
     // Since getters have no guaranteed return values, we must barrier in order to be
     // able to attach stubs for them.
     if (inspector->hasSeenAccessedGetter(pc))
         barrier = BarrierKind::TypeSet;
 
     // Caches can read values from prototypes, so update the barrier to
     // reflect such possible values.
@@ -11053,47 +11025,25 @@ IonBuilder::getPropTryCache(bool* emitte
     if (load->isEffectful())
         MOZ_TRY(resumeAfter(load));
 
     MIRType rvalType = types->getKnownMIRType();
     if (barrier != BarrierKind::NoBarrier || IsNullOrUndefined(rvalType))
         rvalType = MIRType::Value;
     load->setResultType(rvalType);
 
-    MOZ_TRY(pushTypeBarrier(load, types, barrier));
+    if (*pc != JSOP_CALLPROP || !IsNullOrUndefined(obj->type())) {
+        // Due to inlining, it's possible the observed TypeSet is non-empty,
+        // even though we know |obj| is null/undefined and the MCallGetProperty
+        // will throw. Don't push a TypeBarrier in this case, to avoid
+        // inlining the following (unreachable) JSOP_CALL.
+        MOZ_TRY(pushTypeBarrier(load, types, barrier));
+    }
 
     trackOptimizationSuccess();
-    *emitted = true;
-    return Ok();
-}
-
-AbortReasonOr<Ok>
-IonBuilder::getPropTrySharedStub(bool* emitted, MDefinition* obj, TemporaryTypeSet* types)
-{
-    MOZ_ASSERT(*emitted == false);
-
-    // Try to emit a shared stub cache.
-
-    if (JitOptions.disableSharedStubs)
-        return Ok();
-
-    MInstruction* stub = MUnarySharedStub::New(alloc(), obj);
-    current->add(stub);
-    current->push(stub);
-
-    MOZ_TRY(resumeAfter(stub));
-
-    // Due to inlining, it's possible the observed TypeSet is non-empty,
-    // even though we know |obj| is null/undefined and the MCallGetProperty
-    // will throw. Don't push a TypeBarrier in this case, to avoid
-    // inlining the following (unreachable) JSOP_CALL.
-    if (*pc != JSOP_CALLPROP || !IsNullOrUndefined(obj->type()))
-        MOZ_TRY(pushTypeBarrier(stub, types, BarrierKind::TypeSet));
-
-    *emitted = true;
     return Ok();
 }
 
 MDefinition*
 IonBuilder::tryInnerizeWindow(MDefinition* obj)
 {
     // Try to optimize accesses on outer window proxies (window.foo, for
     // example) to go directly to the inner window, the global.
@@ -11164,21 +11114,19 @@ IonBuilder::getPropTryInnerize(bool* emi
             return Ok();
     }
 
     // Passing the inner object to GetProperty IC is safe, see the
     // needsOuterizedThisObject check in IsCacheableGetPropCallNative.
     BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(),
                                                        inner, name, types);
     trackOptimizationAttempt(TrackedStrategy::GetProp_InlineCache);
-    MOZ_TRY(getPropTryCache(emitted, inner, name, barrier, types));
-    if (*emitted)
-        return Ok();
-
-    MOZ_ASSERT(*emitted == false);
+    MOZ_TRY(getPropAddCache(inner, name, barrier, types));
+
+    *emitted = true;
     return Ok();
 }
 
 AbortReasonOr<Ok>
 IonBuilder::jsop_setprop(PropertyName* name)
 {
     MDefinition* value = current->pop();
     MDefinition* obj = convertUnboxedObjects(current->pop());
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -256,20 +256,18 @@ class IonBuilder
                                                            TypedObjectPrediction fieldPrediction,
                                                            PropertyName* name);
     AbortReasonOr<Ok> getPropTryComplexPropOfTypedObject(bool* emitted, MDefinition* typedObj,
                                                          int32_t fieldOffset,
                                                          TypedObjectPrediction fieldTypeReprs,
                                                          size_t fieldIndex);
     AbortReasonOr<Ok> getPropTryInnerize(bool* emitted, MDefinition* obj, PropertyName* name,
                                          TemporaryTypeSet* types);
-    AbortReasonOr<Ok> getPropTryCache(bool* emitted, MDefinition* obj, PropertyName* name,
+    AbortReasonOr<Ok> getPropAddCache(MDefinition* obj, PropertyName* name,
                                       BarrierKind barrier, TemporaryTypeSet* types);
-    AbortReasonOr<Ok> getPropTrySharedStub(bool* emitted, MDefinition* obj,
-                                           TemporaryTypeSet* types);
 
     // jsop_setprop() helpers.
     AbortReasonOr<Ok> setPropTryCommonSetter(bool* emitted, MDefinition* obj,
                                              PropertyName* name, MDefinition* value);
     AbortReasonOr<Ok> setPropTryCommonDOMSetter(bool* emitted, MDefinition* obj,
                                                 MDefinition* value, JSFunction* setter,
                                                 TemporaryTypeSet* objTypes);
     AbortReasonOr<Ok> setPropTryDefiniteSlot(bool* emitted, MDefinition* obj,
--- a/js/src/jit/IonCacheIRCompiler.cpp
+++ b/js/src/jit/IonCacheIRCompiler.cpp
@@ -359,17 +359,17 @@ IonCacheIRCompiler::init()
 
         liveRegs_.emplace(ic->liveRegs());
         outputUnchecked_.emplace(output);
 
         allowDoubleResult_.emplace(ic->allowDoubleResult());
 
         MOZ_ASSERT(numInputs == 1 || numInputs == 2);
 
-        allocator.initInputLocation(0, ic->object(), JSVAL_TYPE_OBJECT);
+        allocator.initInputLocation(0, ic->value());
         if (numInputs > 1)
             allocator.initInputLocation(1, ic->id());
     } else {
         MOZ_CRASH("Invalid cache");
     }
 
     allocator.initAvailableRegs(available);
     allocator.initAvailableRegsAfterSpill();
@@ -863,16 +863,28 @@ IonCacheIRCompiler::emitStoreTypedObject
 
 bool
 IonCacheIRCompiler::emitStoreTypedObjectScalarProperty()
 {
     MOZ_CRASH("Baseline-specific op");
 }
 
 bool
+IonCacheIRCompiler::emitCallNativeSetter()
+{
+    MOZ_CRASH("Baseline-specific op");
+}
+
+bool
+IonCacheIRCompiler::emitCallScriptedSetter()
+{
+    MOZ_CRASH("Baseline-specific op");
+}
+
+bool
 IonCacheIRCompiler::emitLoadTypedObjectResult()
 {
     AutoOutputRegister output(*this);
     Register obj = allocator.useRegister(masm, reader.objOperandId());
     AutoScratchRegister scratch1(allocator, masm);
     AutoScratchRegister scratch2(allocator, masm);
 
     TypedThingLayout layout = reader.typedThingLayout();
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -1306,18 +1306,18 @@ SetPropertyIC::attachSetSlot(JSContext* 
 
     GenerateSetSlot(cx, masm, attacher, obj, shape, object(), temp(), value(), needsTypeBarrier(),
                     checkTypeset, &failures);
 
     return linkAndAttachStub(cx, masm, attacher, ion, "setting",
                              JS::TrackedOutcome::ICSetPropStub_Slot);
 }
 
-static bool
-IsCacheableSetPropCallNative(HandleObject obj, HandleObject holder, HandleShape shape)
+bool
+jit::IsCacheableSetPropCallNative(JSObject* obj, JSObject* holder, Shape* shape)
 {
     if (!shape || !IsCacheableProtoChainForIonOrCacheIR(obj, holder))
         return false;
 
     if (!shape->hasSetterValue())
         return false;
 
     if (!shape->setterObject() || !shape->setterObject()->is<JSFunction>())
@@ -1328,28 +1328,43 @@ IsCacheableSetPropCallNative(HandleObjec
         return false;
 
     if (setter.jitInfo() && !setter.jitInfo()->needsOuterizedThisObject())
         return true;
 
     return !IsWindow(obj);
 }
 
-static bool
-IsCacheableSetPropCallScripted(HandleObject obj, HandleObject holder, HandleShape shape)
+bool
+jit::IsCacheableSetPropCallScripted(JSObject* obj, JSObject* holder, Shape* shape,
+                                    bool* isTemporarilyUnoptimizable)
 {
     if (!shape || !IsCacheableProtoChainForIonOrCacheIR(obj, holder))
         return false;
 
     if (IsWindow(obj))
         return false;
 
-    return shape->hasSetterValue() && shape->setterObject() &&
-           shape->setterObject()->is<JSFunction>() &&
-           shape->setterObject()->as<JSFunction>().hasJITCode();
+    if (!shape->hasSetterValue())
+        return false;
+
+    if (!shape->setterObject() || !shape->setterObject()->is<JSFunction>())
+        return false;
+
+    JSFunction& setter = shape->setterObject()->as<JSFunction>();
+    if (setter.isNative())
+        return false;
+
+    if (!setter.hasJITCode()) {
+        if (isTemporarilyUnoptimizable)
+            *isTemporarilyUnoptimizable = true;
+        return false;
+    }
+
+    return true;
 }
 
 static bool
 IsCacheableSetPropCallPropertyOp(HandleObject obj, HandleObject holder, HandleShape shape)
 {
     if (!shape)
         return false;
 
--- a/js/src/jit/IonCaches.h
+++ b/js/src/jit/IonCaches.h
@@ -625,16 +625,20 @@ IONCACHE_KIND_LIST(CACHE_CASTS)
 bool IsCacheableProtoChainForIonOrCacheIR(JSObject* obj, JSObject* holder);
 bool IsCacheableGetPropReadSlotForIonOrCacheIR(JSObject* obj, JSObject* holder,
                                                PropertyResult prop);
 
 bool IsCacheableGetPropCallScripted(JSObject* obj, JSObject* holder, Shape* shape,
                                     bool* isTemporarilyUnoptimizable = nullptr);
 bool IsCacheableGetPropCallNative(JSObject* obj, JSObject* holder, Shape* shape);
 
+bool IsCacheableSetPropCallScripted(JSObject* obj, JSObject* holder, Shape* shape,
+                                    bool* isTemporarilyUnoptimizable = nullptr);
+bool IsCacheableSetPropCallNative(JSObject* obj, JSObject* holder, Shape* shape);
+
 bool ValueToNameOrSymbolId(JSContext* cx, HandleValue idval, MutableHandleId id,
                            bool* nameOrSymbol);
 
 void* GetReturnAddressToIonCode(JSContext* cx);
 
 } // namespace jit
 } // namespace js
 
--- a/js/src/jit/IonCode.h
+++ b/js/src/jit/IonCode.h
@@ -791,17 +791,17 @@ struct AutoFlushICache
     explicit AutoFlushICache(const char* nonce, bool inhibit=false);
 };
 
 } // namespace jit
 
 namespace gc {
 
 inline bool
-IsMarked(const jit::VMFunction*)
+IsMarked(JSRuntime* rt, const jit::VMFunction*)
 {
     // VMFunction are only static objects which are used by WeakMaps as keys.
     // It is considered as a root object which is always marked.
     return true;
 }
 
 } // namespace gc
 
--- a/js/src/jit/IonIC.cpp
+++ b/js/src/jit/IonIC.cpp
@@ -111,17 +111,17 @@ IonGetPropertyIC::maybeDisable(Zone* zon
     if (++failedUpdates_ > MAX_FAILED_UPDATES) {
         JitSpew(JitSpew_IonIC, "Disable inline cache");
         disable(zone);
     }
 }
 
 /* static */ bool
 IonGetPropertyIC::update(JSContext* cx, HandleScript outerScript, IonGetPropertyIC* ic,
-			 HandleObject obj, HandleValue idVal, MutableHandleValue res)
+			 HandleValue val, HandleValue idVal, MutableHandleValue res)
 {
     // Override the return value if we are invalidated (bug 728188).
     AutoDetectInvalidation adi(cx, res, outerScript->ionScript());
 
     // If the IC is idempotent, we will redo the op in the interpreter.
     if (ic->idempotent())
         adi.disable();
 
@@ -130,21 +130,20 @@ IonGetPropertyIC::update(JSContext* cx, 
         if (ic->canAttachStub()) {
             // IonBuilder calls PropertyReadNeedsTypeBarrier to determine if it
             // needs a type barrier. Unfortunately, PropertyReadNeedsTypeBarrier
             // does not account for getters, so we should only attach a getter
             // stub if we inserted a type barrier.
             CanAttachGetter canAttachGetter =
                 ic->monitoredResult() ? CanAttachGetter::Yes : CanAttachGetter::No;
             jsbytecode* pc = ic->idempotent() ? nullptr : ic->pc();
-            RootedValue objVal(cx, ObjectValue(*obj));
             bool isTemporarilyUnoptimizable;
             GetPropIRGenerator gen(cx, pc, ic->kind(), ICStubEngine::IonIC,
                                    &isTemporarilyUnoptimizable,
-                                   objVal, idVal, canAttachGetter);
+                                   val, idVal, canAttachGetter);
             if (ic->idempotent() ? gen.tryAttachIdempotentStub() : gen.tryAttachStub()) {
                 attached = ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
                                                  outerScript);
             }
         }
         ic->maybeDisable(cx->zone(), attached);
     }
 
@@ -164,21 +163,22 @@ IonGetPropertyIC::update(JSContext* cx, 
         if (outerScript->hasIonScript())
             Invalidate(cx, outerScript);
 
         // We will redo the potentially effectful lookup in Baseline.
         return true;
     }
 
     if (ic->kind() == CacheKind::GetProp) {
-        if (!GetProperty(cx, obj, obj, idVal.toString()->asAtom().asPropertyName(), res))
+        RootedPropertyName name(cx, idVal.toString()->asAtom().asPropertyName());
+        if (!GetProperty(cx, val, name, res))
             return false;
     } else {
         MOZ_ASSERT(ic->kind() == CacheKind::GetElem);
-        if (!GetObjectElementOperation(cx, JSOp(*ic->pc()), obj, obj, idVal, res))
+        if (!GetElementOperation(cx, JSOp(*ic->pc()), val, idVal, res))
             return false;
     }
 
     if (!ic->idempotent()) {
         // Monitor changes to cache entry.
         if (!ic->monitoredResult())
             TypeScript::Monitor(cx, ic->script(), ic->pc(), res);
     }
--- a/js/src/jit/IonIC.h
+++ b/js/src/jit/IonIC.h
@@ -150,52 +150,52 @@ class IonIC
     bool attachCacheIRStub(JSContext* cx, const CacheIRWriter& writer, CacheKind kind,
                            HandleScript outerScript);
 };
 
 class IonGetPropertyIC : public IonIC
 {
     LiveRegisterSet liveRegs_;
 
-    Register object_;
+    TypedOrValueRegister value_;
     ConstantOrRegister id_;
     TypedOrValueRegister output_;
     Register maybeTemp_; // Might be InvalidReg.
 
     static const size_t MAX_FAILED_UPDATES = 16;
     uint16_t failedUpdates_;
 
     bool monitoredResult_ : 1;
     bool allowDoubleResult_ : 1;
 
   public:
-    IonGetPropertyIC(CacheKind kind, LiveRegisterSet liveRegs, Register object,
+    IonGetPropertyIC(CacheKind kind, LiveRegisterSet liveRegs, TypedOrValueRegister value,
                      const ConstantOrRegister& id, TypedOrValueRegister output, Register maybeTemp,
                      bool monitoredResult, bool allowDoubleResult)
       : IonIC(kind),
         liveRegs_(liveRegs),
-        object_(object),
+        value_(value),
         id_(id),
         output_(output),
         maybeTemp_(maybeTemp),
         failedUpdates_(0),
         monitoredResult_(monitoredResult),
         allowDoubleResult_(allowDoubleResult)
     { }
 
     bool monitoredResult() const { return monitoredResult_; }
-    Register object() const { return object_; }
+    TypedOrValueRegister value() const { return value_; }
     ConstantOrRegister id() const { return id_; }
     TypedOrValueRegister output() const { return output_; }
     Register maybeTemp() const { return maybeTemp_; }
     LiveRegisterSet liveRegs() const { return liveRegs_; }
     bool allowDoubleResult() const { return allowDoubleResult_; }
 
     void maybeDisable(Zone* zone, bool attached);
 
     static MOZ_MUST_USE bool update(JSContext* cx, HandleScript outerScript, IonGetPropertyIC* ic,
-                                    HandleObject obj, HandleValue idVal, MutableHandleValue res);
+                                    HandleValue val, HandleValue idVal, MutableHandleValue res);
 };
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_IonIC_h */
--- a/js/src/jit/JitcodeMap.cpp
+++ b/js/src/jit/JitcodeMap.cpp
@@ -719,17 +719,17 @@ JitcodeGlobalTable::setAllEntriesAsExpir
     AutoSuppressProfilerSampling suppressSampling(rt);
     for (Range r(*this); !r.empty(); r.popFront())
         r.front()->setAsExpired();
 }
 
 struct Unconditionally
 {
     template <typename T>
-    static bool ShouldTrace(T* thingp) { return true; }
+    static bool ShouldTrace(JSRuntime* rt, T* thingp) { return true; }
 };
 
 void
 JitcodeGlobalTable::trace(JSTracer* trc)
 {
     // Trace all entries unconditionally. This is done during minor collection
     // to tenure and update object pointers.
 
@@ -738,23 +738,23 @@ JitcodeGlobalTable::trace(JSTracer* trc)
     AutoSuppressProfilerSampling suppressSampling(trc->runtime());
     for (Range r(*this); !r.empty(); r.popFront())
         r.front()->trace<Unconditionally>(trc);
 }
 
 struct IfUnmarked
 {
     template <typename T>
-    static bool ShouldTrace(T* thingp) { return !IsMarkedUnbarriered(thingp); }
+    static bool ShouldTrace(JSRuntime* rt, T* thingp) { return !IsMarkedUnbarriered(rt, thingp); }
 };
 
 template <>
-bool IfUnmarked::ShouldTrace<TypeSet::Type>(TypeSet::Type* type)
+bool IfUnmarked::ShouldTrace<TypeSet::Type>(JSRuntime* rt, TypeSet::Type* type)
 {
-    return !TypeSet::IsTypeMarked(type);
+    return !TypeSet::IsTypeMarked(rt, type);
 }
 
 bool
 JitcodeGlobalTable::markIteratively(GCMarker* marker)
 {
     // JitcodeGlobalTable must keep entries that are in the sampler buffer
     // alive. This conditionality is akin to holding the entries weakly.
     //
@@ -795,17 +795,17 @@ JitcodeGlobalTable::markIteratively(GCMa
         // generation, and conditionally mark the rest of the entry if its
         // JitCode is not already marked. This conditional marking ensures
         // that so long as the JitCode *may* be sampled, we keep any
         // information that may be handed out to the sampler, like tracked
         // types used by optimizations and scripts used for pc to line number
         // mapping, alive as well.
         if (!entry->isSampled(gen, lapCount)) {
             entry->setAsExpired();
-            if (!entry->baseEntry().isJitcodeMarkedFromAnyThread())
+            if (!entry->baseEntry().isJitcodeMarkedFromAnyThread(marker->runtime()))
                 continue;
         }
 
         // The table is runtime-wide. Not all zones may be participating in
         // the GC.
         if (!entry->zone()->isCollecting() || entry->zone()->isGCFinished())
             continue;
 
@@ -831,89 +831,90 @@ JitcodeGlobalTable::sweep(JSRuntime* rt)
             entry->sweepChildren(rt);
     }
 }
 
 template <class ShouldTraceProvider>
 bool
 JitcodeGlobalEntry::BaseEntry::traceJitcode(JSTracer* trc)
 {
-    if (ShouldTraceProvider::ShouldTrace(&jitcode_)) {
+    if (ShouldTraceProvider::ShouldTrace(trc->runtime(), &jitcode_)) {
         TraceManuallyBarrieredEdge(trc, &jitcode_, "jitcodglobaltable-baseentry-jitcode");
         return true;
     }
     return false;
 }
 
 bool
-JitcodeGlobalEntry::BaseEntry::isJitcodeMarkedFromAnyThread()
+JitcodeGlobalEntry::BaseEntry::isJitcodeMarkedFromAnyThread(JSRuntime* rt)
 {
-    return IsMarkedUnbarriered(&jitcode_) ||
+    return IsMarkedUnbarriered(rt, &jitcode_) ||
            jitcode_->arena()->allocatedDuringIncremental;
 }
 
 bool
 JitcodeGlobalEntry::BaseEntry::isJitcodeAboutToBeFinalized()
 {
     return IsAboutToBeFinalizedUnbarriered(&jitcode_);
 }
 
 template <class ShouldTraceProvider>
 bool
 JitcodeGlobalEntry::BaselineEntry::trace(JSTracer* trc)
 {
-    if (ShouldTraceProvider::ShouldTrace(&script_)) {
+    if (ShouldTraceProvider::ShouldTrace(trc->runtime(), &script_)) {
         TraceManuallyBarrieredEdge(trc, &script_, "jitcodeglobaltable-baselineentry-script");
         return true;
     }
     return false;
 }
 
 void
 JitcodeGlobalEntry::BaselineEntry::sweepChildren()
 {
     MOZ_ALWAYS_FALSE(IsAboutToBeFinalizedUnbarriered(&script_));
 }
 
 bool
-JitcodeGlobalEntry::BaselineEntry::isMarkedFromAnyThread()
+JitcodeGlobalEntry::BaselineEntry::isMarkedFromAnyThread(JSRuntime* rt)
 {
-    return IsMarkedUnbarriered(&script_) ||
+    return IsMarkedUnbarriered(rt, &script_) ||
            script_->arena()->allocatedDuringIncremental;
 }
 
 template <class ShouldTraceProvider>
 bool
 JitcodeGlobalEntry::IonEntry::trace(JSTracer* trc)
 {
     bool tracedAny = false;
 
+    JSRuntime* rt = trc->runtime();
     for (unsigned i = 0; i < numScripts(); i++) {
-        if (ShouldTraceProvider::ShouldTrace(&sizedScriptList()->pairs[i].script)) {
+        if (ShouldTraceProvider::ShouldTrace(rt, &sizedScriptList()->pairs[i].script)) {
             TraceManuallyBarrieredEdge(trc, &sizedScriptList()->pairs[i].script,
                                        "jitcodeglobaltable-ionentry-script");
             tracedAny = true;
         }
     }
 
     if (!optsAllTypes_)
         return tracedAny;
 
     for (IonTrackedTypeWithAddendum* iter = optsAllTypes_->begin();
          iter != optsAllTypes_->end(); iter++)
     {
-        if (ShouldTraceProvider::ShouldTrace(&iter->type)) {
+        if (ShouldTraceProvider::ShouldTrace(rt, &iter->type)) {
             iter->type.trace(trc);
             tracedAny = true;
         }
-        if (iter->hasAllocationSite() && ShouldTraceProvider::ShouldTrace(&iter->script)) {
+        if (iter->hasAllocationSite() && ShouldTraceProvider::ShouldTrace(rt, &iter->script)) {
             TraceManuallyBarrieredEdge(trc, &iter->script,
                                        "jitcodeglobaltable-ionentry-type-addendum-script");
             tracedAny = true;
-        } else if (iter->hasConstructor() && ShouldTraceProvider::ShouldTrace(&iter->constructor)) {
+        } else if (iter->hasConstructor() && ShouldTraceProvider::ShouldTrace(rt, &iter->constructor)) {
             TraceManuallyBarrieredEdge(trc, &iter->constructor,
                                        "jitcodeglobaltable-ionentry-type-addendum-constructor");
             tracedAny = true;
         }
     }
 
     return tracedAny;
 }
@@ -936,33 +937,33 @@ JitcodeGlobalEntry::IonEntry::sweepChild
         if (iter->hasAllocationSite())
             MOZ_ALWAYS_FALSE(IsAboutToBeFinalizedUnbarriered(&iter->script));
         else if (iter->hasConstructor())
             MOZ_ALWAYS_FALSE(IsAboutToBeFinalizedUnbarriered(&iter->constructor));
     }
 }
 
 bool
-JitcodeGlobalEntry::IonEntry::isMarkedFromAnyThread()
+JitcodeGlobalEntry::IonEntry::isMarkedFromAnyThread(JSRuntime* rt)
 {
     for (unsigned i = 0; i < numScripts(); i++) {
-        if (!IsMarkedUnbarriered(&sizedScriptList()->pairs[i].script) &&
+        if (!IsMarkedUnbarriered(rt, &sizedScriptList()->pairs[i].script) &&
             !sizedScriptList()->pairs[i].script->arena()->allocatedDuringIncremental)
         {
             return false;
         }
     }
 
     if (!optsAllTypes_)
         return true;
 
     for (IonTrackedTypeWithAddendum* iter = optsAllTypes_->begin();
          iter != optsAllTypes_->end(); iter++)
     {
-        if (!TypeSet::IsTypeMarked(&iter->type) &&
+        if (!TypeSet::IsTypeMarked(rt, &iter->type) &&
             !TypeSet::IsTypeAllocatedDuringIncremental(iter->type))
         {
             return false;
         }
     }
 
     return true;
 }
--- a/js/src/jit/JitcodeMap.h
+++ b/js/src/jit/JitcodeMap.h
@@ -203,17 +203,17 @@ class JitcodeGlobalEntry
         bool endsAbovePointer(void* ptr) const {
             return ((uint8_t*)nativeEndAddr()) > ((uint8_t*) ptr);
         }
         bool containsPointer(void* ptr) const {
             return startsBelowPointer(ptr) && endsAbovePointer(ptr);
         }
 
         template <class ShouldTraceProvider> bool traceJitcode(JSTracer* trc);
-        bool isJitcodeMarkedFromAnyThread();
+        bool isJitcodeMarkedFromAnyThread(JSRuntime* rt);
         bool isJitcodeAboutToBeFinalized();
     };
 
     struct IonEntry : public BaseEntry
     {
         // regionTable_ points to the start of the region table within the
         // packed map for compile represented by this entry.  Since the
         // region table occurs at the tail of the memory region, this pointer
@@ -365,17 +365,17 @@ class JitcodeGlobalEntry
 
         void forEachOptimizationAttempt(JSRuntime* rt, uint8_t index,
                                         JS::ForEachTrackedOptimizationAttemptOp& op);
         void forEachOptimizationTypeInfo(JSRuntime* rt, uint8_t index,
                                          IonTrackedOptimizationsTypeInfo::ForEachOpAdapter& op);
 
         template <class ShouldTraceProvider> bool trace(JSTracer* trc);
         void sweepChildren();
-        bool isMarkedFromAnyThread();
+        bool isMarkedFromAnyThread(JSRuntime* rt);
     };
 
     struct BaselineEntry : public BaseEntry
     {
         JSScript* script_;
         const char* str_;
 
         // Last location that caused Ion to abort compilation and the reason
@@ -423,17 +423,17 @@ class JitcodeGlobalEntry
         uint32_t callStackAtAddr(JSRuntime* rt, void* ptr, const char** results,
                                  uint32_t maxResults) const;
 
         void youngestFrameLocationAtAddr(JSRuntime* rt, void* ptr,
                                          JSScript** script, jsbytecode** pc) const;
 
         template <class ShouldTraceProvider> bool trace(JSTracer* trc);
         void sweepChildren();
-        bool isMarkedFromAnyThread();
+        bool isMarkedFromAnyThread(JSRuntime* rt);
     };
 
     struct IonCacheEntry : public BaseEntry
     {
         void* rejoinAddr_;
         JS::TrackedOutcome trackedOutcome_;
 
         void init(JitCode* code, void* nativeStartAddr, void* nativeEndAddr,
@@ -946,23 +946,23 @@ class JitcodeGlobalEntry
           case Dummy:
             break;
           default:
             MOZ_CRASH("Invalid JitcodeGlobalEntry kind.");
         }
     }
 
     bool isMarkedFromAnyThread(JSRuntime* rt) {
-        if (!baseEntry().isJitcodeMarkedFromAnyThread())
+        if (!baseEntry().isJitcodeMarkedFromAnyThread(rt))
             return false;
         switch (kind()) {
           case Ion:
-            return ionEntry().isMarkedFromAnyThread();
+            return ionEntry().isMarkedFromAnyThread(rt);
           case Baseline:
-            return baselineEntry().isMarkedFromAnyThread();
+            return baselineEntry().isMarkedFromAnyThread(rt);
           case IonCache:
             return ionCacheEntry().isMarkedFromAnyThread(rt);
           case Dummy:
             break;
           default:
             MOZ_CRASH("Invalid JitcodeGlobalEntry kind.");
         }
         return true;
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -3617,17 +3617,18 @@ LIRGenerator::visitCallGetIntrinsicValue
     LCallGetIntrinsicValue* lir = new(alloc()) LCallGetIntrinsicValue();
     defineReturn(lir, ins);
     assignSafepoint(lir, ins);
 }
 
 void
 LIRGenerator::visitGetPropertyCache(MGetPropertyCache* ins)
 {
-    MOZ_ASSERT(ins->object()->type() == MIRType::Object);
+    MDefinition* value = ins->value();
+    MOZ_ASSERT(value->type() == MIRType::Object || value->type() == MIRType::Value);
 
     MDefinition* id = ins->idval();
     MOZ_ASSERT(id->type() == MIRType::String ||
                id->type() == MIRType::Symbol ||
                id->type() == MIRType::Int32 ||
                id->type() == MIRType::Value);
 
     if (ins->monitoredResult()) {
@@ -3644,24 +3645,24 @@ LIRGenerator::visitGetPropertyCache(MGet
     // We need a temp register if we can't use the output register as scratch.
     // See IonIC::scratchRegisterForEntryJump.
     LDefinition maybeTemp = LDefinition::BogusTemp();
     if (ins->type() == MIRType::Double)
         maybeTemp = temp();
 
     if (ins->type() == MIRType::Value) {
         LGetPropertyCacheV* lir =
-            new(alloc()) LGetPropertyCacheV(useRegister(ins->object()),
+            new(alloc()) LGetPropertyCacheV(useBoxOrTyped(value),
                                             useBoxOrTypedOrConstant(id, useConstId),
                                             maybeTemp);
         defineBox(lir, ins);
         assignSafepoint(lir, ins);
     } else {
         LGetPropertyCacheT* lir =
-            new(alloc()) LGetPropertyCacheT(useRegister(ins->object()),
+            new(alloc()) LGetPropertyCacheT(useBoxOrTyped(value),
                                             useBoxOrTypedOrConstant(id, useConstId),
                                             maybeTemp);
         define(lir, ins);
         assignSafepoint(lir, ins);
     }
 }
 
 void
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -10666,17 +10666,17 @@ class InlinePropertyTable : public TempO
     // Ensure that the InlinePropertyTable's domain is a subset of |targets|.
     void trimToTargets(const ObjectVector& targets);
 
     bool appendRoots(MRootList& roots) const;
 };
 
 class MGetPropertyCache
   : public MBinaryInstruction,
-    public MixPolicy<ObjectPolicy<0>, CacheIdPolicy<1>>::Data
+    public MixPolicy<BoxExceptPolicy<0, MIRType::Object>, CacheIdPolicy<1>>::Data
 {
     bool idempotent_ : 1;
     bool monitoredResult_ : 1;
 
     InlinePropertyTable* inlinePropertyTable_;
 
     MGetPropertyCache(MDefinition* obj, MDefinition* id, bool monitoredResult)
       : MBinaryInstruction(obj, id),
@@ -10690,17 +10690,17 @@ class MGetPropertyCache
         // resolve hooks on the proto chain. setGuard ensures this check is not
         // eliminated.
         setGuard();
     }
 
   public:
     INSTRUCTION_HEADER(GetPropertyCache)
     TRIVIAL_NEW_WRAPPERS
-    NAMED_OPERANDS((0, object), (1, idval))
+    NAMED_OPERANDS((0, value), (1, idval))
 
     InlinePropertyTable* initInlinePropertyTable(TempAllocator& alloc, jsbytecode* pc) {
         MOZ_ASSERT(inlinePropertyTable_ == nullptr);
         inlinePropertyTable_ = new(alloc) InlinePropertyTable(alloc, pc);
         return inlinePropertyTable_;
     }
 
     void clearInlinePropertyTable() {
--- a/js/src/jit/SharedIC.cpp
+++ b/js/src/jit/SharedIC.cpp
@@ -171,18 +171,16 @@ ICStub::NonCacheIRStubMakesGCCalls(Kind 
       case Call_Native:
       case Call_ClassHook:
       case Call_ScriptedApplyArray:
       case Call_ScriptedApplyArguments:
       case Call_ScriptedFunCall:
       case Call_StringSplit:
       case WarmUpCounter_Fallback:
       case GetProp_Generic:
-      case SetProp_CallScripted:
-      case SetProp_CallNative:
       case RetSub_Fallback:
       // These two fallback stubs don't actually make non-tail calls,
       // but the fallback code for the bailout path needs to pop the stub frame
       // pushed during the bailout.
       case GetProp_Fallback:
       case SetProp_Fallback:
         return true;
       default:
@@ -367,32 +365,16 @@ ICStub::trace(JSTracer* trc)
           case 1: propStub->toImpl<1>()->traceShapes(trc); break;
           case 2: propStub->toImpl<2>()->traceShapes(trc); break;
           case 3: propStub->toImpl<3>()->traceShapes(trc); break;
           case 4: propStub->toImpl<4>()->traceShapes(trc); break;
           default: MOZ_CRASH("Invalid proto stub.");
         }
         break;
       }
-      case ICStub::SetProp_CallScripted: {
-        ICSetProp_CallScripted* callStub = toSetProp_CallScripted();
-        callStub->receiverGuard().trace(trc);
-        TraceEdge(trc, &callStub->holder(), "baseline-setpropcallscripted-stub-holder");
-        TraceEdge(trc, &callStub->holderShape(), "baseline-setpropcallscripted-stub-holdershape");
-        TraceEdge(trc, &callStub->setter(), "baseline-setpropcallscripted-stub-setter");
-        break;
-      }
-      case ICStub::SetProp_CallNative: {
-        ICSetProp_CallNative* callStub = toSetProp_CallNative();
-        callStub->receiverGuard().trace(trc);
-        TraceEdge(trc, &callStub->holder(), "baseline-setpropcallnative-stub-holder");
-        TraceEdge(trc, &callStub->holderShape(), "baseline-setpropcallnative-stub-holdershape");
-        TraceEdge(trc, &callStub->setter(), "baseline-setpropcallnative-stub-setter");
-        break;
-      }
       case ICStub::InstanceOf_Function: {
         ICInstanceOf_Function* instanceofStub = toInstanceOf_Function();
         TraceEdge(trc, &instanceofStub->shape(), "baseline-instanceof-fun-shape");
         TraceEdge(trc, &instanceofStub->prototypeObject(), "baseline-instanceof-fun-prototype");
         break;
       }
       case ICStub::NewArray_Fallback: {
         ICNewArray_Fallback* stub = toNewArray_Fallback();
@@ -410,17 +392,17 @@ ICStub::trace(JSTracer* trc)
         TraceEdge(trc, &stub->templateObject(), "baseline-rest-template");
         break;
       }
       case ICStub::CacheIR_Monitored:
         TraceCacheIRStub(trc, this, toCacheIR_Monitored()->stubInfo());
         break;
       case ICStub::CacheIR_Updated: {
         ICCacheIR_Updated* stub = toCacheIR_Updated();
-        TraceEdge(trc, &stub->updateStubGroup(), "baseline-update-stub-group");
+        TraceNullableEdge(trc, &stub->updateStubGroup(), "baseline-update-stub-group");
         TraceEdge(trc, &stub->updateStubId(), "baseline-update-stub-id");
         TraceCacheIRStub(trc, this, stub->stubInfo());
         break;
       }
       default:
         break;
     }
 }
--- a/js/src/jit/SharedIC.h
+++ b/js/src/jit/SharedIC.h
@@ -965,16 +965,19 @@ class ICCacheIR_Updated : public ICUpdat
   public:
     ICCacheIR_Updated(JitCode* stubCode, const CacheIRStubInfo* stubInfo)
       : ICUpdatedStub(ICStub::CacheIR_Updated, stubCode),
         stubInfo_(stubInfo),
         updateStubGroup_(nullptr),
         updateStubId_(JSID_EMPTY)
     {}
 
+    static ICCacheIR_Updated* Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub,
+                                    ICCacheIR_Updated& other);
+
     GCPtrObjectGroup& updateStubGroup() {
         return updateStubGroup_;
     }
     GCPtrId& updateStubId() {
         return updateStubId_;
     }
 
     void notePreliminaryObject() {
--- a/js/src/jit/TypePolicy.cpp
+++ b/js/src/jit/TypePolicy.cpp
@@ -1241,17 +1241,17 @@ FilterTypeSetPolicy::adjustInputs(TempAl
     _(Mix3Policy<ObjectPolicy<0>, CacheIdPolicy<1>, NoFloatPolicy<2>>)  \
     _(Mix4Policy<SimdScalarPolicy<0>, SimdScalarPolicy<1>, SimdScalarPolicy<2>, SimdScalarPolicy<3> >) \
     _(MixPolicy<BoxPolicy<0>, ObjectPolicy<1> >)                        \
     _(MixPolicy<ConvertToStringPolicy<0>, ConvertToStringPolicy<1> >)   \
     _(MixPolicy<ConvertToStringPolicy<0>, ObjectPolicy<1> >)            \
     _(MixPolicy<DoublePolicy<0>, DoublePolicy<1> >)                     \
     _(MixPolicy<IntPolicy<0>, IntPolicy<1> >)                           \
     _(MixPolicy<ObjectPolicy<0>, BoxPolicy<1> >)                        \
-    _(MixPolicy<ObjectPolicy<0>, CacheIdPolicy<1>>)                     \
+    _(MixPolicy<BoxExceptPolicy<0, MIRType::Object>, CacheIdPolicy<1>>) \
     _(MixPolicy<ObjectPolicy<0>, ConvertToStringPolicy<1> >)            \
     _(MixPolicy<ObjectPolicy<0>, IntPolicy<1> >)                        \
     _(MixPolicy<ObjectPolicy<0>, IntPolicy<2> >)                        \
     _(MixPolicy<ObjectPolicy<0>, NoFloatPolicy<1> >)                    \
     _(MixPolicy<ObjectPolicy<0>, NoFloatPolicy<2> >)                    \
     _(MixPolicy<ObjectPolicy<0>, NoFloatPolicy<3> >)                    \
     _(MixPolicy<ObjectPolicy<0>, ObjectPolicy<1> >)                     \
     _(MixPolicy<ObjectPolicy<0>, StringPolicy<1> >)                     \
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -1378,16 +1378,30 @@ CallNativeGetter(JSContext* cx, HandleFu
     if (!natfun(cx, 0, vp.begin()))
         return false;
 
     result.set(vp[0]);
     return true;
 }
 
 bool
+CallNativeSetter(JSContext* cx, HandleFunction callee, HandleObject obj, HandleValue rhs)
+{
+    MOZ_ASSERT(callee->isNative());
+    JSNative natfun = callee->native();
+
+    JS::AutoValueArray<3> vp(cx);
+    vp[0].setObject(*callee.get());
+    vp[1].setObject(*obj.get());
+    vp[2].set(rhs);
+
+    return natfun(cx, 1, vp.begin());
+}
+
+bool
 EqualStringsHelper(JSString* str1, JSString* str2)
 {
     MOZ_ASSERT(str1->isAtom());
     MOZ_ASSERT(!str2->isAtom());
     MOZ_ASSERT(str1->length() == str2->length());
 
     JSLinearString* str2Linear = str2->ensureLinear(nullptr);
     if (!str2Linear)
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -810,16 +810,19 @@ ThrowObjectCoercible(JSContext* cx, Hand
 
 MOZ_MUST_USE bool
 BaselineGetFunctionThis(JSContext* cx, BaselineFrame* frame, MutableHandleValue res);
 
 MOZ_MUST_USE bool
 CallNativeGetter(JSContext* cx, HandleFunction callee, HandleObject obj,
                  MutableHandleValue result);
 
+MOZ_MUST_USE bool
+CallNativeSetter(JSContext* cx, HandleFunction callee, HandleObject obj,
+                 HandleValue rhs);
 
 MOZ_MUST_USE bool
 EqualStringsHelper(JSString* str1, JSString* str2);
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_VMFunctions_h */
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -6643,49 +6643,51 @@ class LCallGetIntrinsicValue : public LC
 
     const MCallGetIntrinsicValue* mir() const {
         return mir_->toCallGetIntrinsicValue();
     }
 };
 
 // Patchable jump to stubs generated for a GetProperty cache, which loads a
 // boxed value.
-class LGetPropertyCacheV : public LInstructionHelper<BOX_PIECES, 1 + BOX_PIECES, 1>
+class LGetPropertyCacheV : public LInstructionHelper<BOX_PIECES, 2 * BOX_PIECES, 1>
 {
   public:
     LIR_HEADER(GetPropertyCacheV)
 
-    static const size_t Id = 1;
-
-    LGetPropertyCacheV(const LAllocation& object, const LBoxAllocation& id,
+    static const size_t Value = 0;
+    static const size_t Id = BOX_PIECES;
+
+    LGetPropertyCacheV(const LBoxAllocation& value, const LBoxAllocation& id,
                        const LDefinition& temp) {
-        setOperand(0, object);
+        setBoxOperand(Value, value);
         setBoxOperand(Id, id);
         setTemp(0, temp);
     }
     const MGetPropertyCache* mir() const {
         return mir_->toGetPropertyCache();
     }
     const LDefinition* temp() {
         return getTemp(0);
     }
 };
 
 // Patchable jump to stubs generated for a GetProperty cache, which loads a
 // value of a known type, possibly into an FP register.
-class LGetPropertyCacheT : public LInstructionHelper<1, 1 + BOX_PIECES, 1>
+class LGetPropertyCacheT : public LInstructionHelper<1, 2 * BOX_PIECES, 1>
 {
   public:
     LIR_HEADER(GetPropertyCacheT)
 
-    static const size_t Id = 1;
-
-    LGetPropertyCacheT(const LAllocation& object, const LBoxAllocation& id,
+    static const size_t Value = 0;
+    static const size_t Id = BOX_PIECES;
+
+    LGetPropertyCacheT(const LBoxAllocation& value, const LBoxAllocation& id,
                        const LDefinition& temp) {
-        setOperand(0, object);
+        setBoxOperand(Value, value);
         setBoxOperand(Id, id);
         setTemp(0, temp);
     }
     const MGetPropertyCache* mir() const {
         return mir_->toGetPropertyCache();
     }
     const LDefinition* temp() {
         return getTemp(0);
--- a/js/src/jit/shared/Lowering-shared-inl.h
+++ b/js/src/jit/shared/Lowering-shared-inl.h
@@ -745,35 +745,41 @@ LIRGeneratorShared::useBox(MDefinition* 
     return LBoxAllocation(LUse(mir->virtualRegister(), policy, useAtStart),
                           LUse(VirtualRegisterOfPayload(mir), policy, useAtStart));
 #else
     return LBoxAllocation(LUse(mir->virtualRegister(), policy, useAtStart));
 #endif
 }
 
 LBoxAllocation
-LIRGeneratorShared::useBoxOrTypedOrConstant(MDefinition* mir, bool useConstant)
+LIRGeneratorShared::useBoxOrTyped(MDefinition* mir)
 {
     if (mir->type() == MIRType::Value)
         return useBox(mir);
 
 
+#if defined(JS_NUNBOX32)
+    return LBoxAllocation(useRegister(mir), LAllocation());
+#else
+    return LBoxAllocation(useRegister(mir));
+#endif
+}
+
+LBoxAllocation
+LIRGeneratorShared::useBoxOrTypedOrConstant(MDefinition* mir, bool useConstant)
+{
     if (useConstant && mir->isConstant()) {
 #if defined(JS_NUNBOX32)
         return LBoxAllocation(LAllocation(mir->toConstant()), LAllocation());
 #else
         return LBoxAllocation(LAllocation(mir->toConstant()));
 #endif
     }
 
-#if defined(JS_NUNBOX32)
-    return LBoxAllocation(useRegister(mir), LAllocation());
-#else
-    return LBoxAllocation(useRegister(mir));
-#endif
+    return useBoxOrTyped(mir);
 }
 
 LInt64Allocation
 LIRGeneratorShared::useInt64(MDefinition* mir, LUse::Policy policy, bool useAtStart)
 {
     MOZ_ASSERT(mir->type() == MIRType::Int64);
 
     ensureDefined(mir);
--- a/js/src/jit/shared/Lowering-shared.h
+++ b/js/src/jit/shared/Lowering-shared.h
@@ -192,16 +192,17 @@ class LIRGeneratorShared : public MDefin
 
     // Returns a box allocation for a Value-typed instruction.
     inline LBoxAllocation useBox(MDefinition* mir, LUse::Policy policy = LUse::REGISTER,
                                  bool useAtStart = false);
 
     // Returns a box allocation. The use is either typed, a Value, or
     // a constant (if useConstant is true).
     inline LBoxAllocation useBoxOrTypedOrConstant(MDefinition* mir, bool useConstant);
+    inline LBoxAllocation useBoxOrTyped(MDefinition* mir);
 
     // Returns an int64 allocation for an Int64-typed instruction.
     inline LInt64Allocation useInt64(MDefinition* mir, LUse::Policy policy, bool useAtStart);
     inline LInt64Allocation useInt64(MDefinition* mir, bool useAtStart = false);
     inline LInt64Allocation useInt64AtStart(MDefinition* mir);
     inline LInt64Allocation useInt64OrConstant(MDefinition* mir, bool useAtStart = false);
     inline LInt64Allocation useInt64Register(MDefinition* mir, bool useAtStart = false);
     inline LInt64Allocation useInt64RegisterOrConstant(MDefinition* mir, bool useAtStart = false);
--- a/js/src/jsapi-tests/testIntern.cpp
+++ b/js/src/jsapi-tests/testIntern.cpp
@@ -38,11 +38,11 @@ BEGIN_TEST(testPinAcrossGC)
     CHECK(sw.strOk);
     return true;
 }
 
 static void
 FinalizeCallback(JSFreeOp* fop, JSFinalizeStatus status, bool isZoneGC, void* data)
 {
     if (status == JSFINALIZE_GROUP_START)
-        sw.strOk = js::gc::IsMarkedUnbarriered(&sw.str);
+        sw.strOk = js::gc::IsMarkedUnbarriered(fop->runtime(), &sw.str);
 }
 END_TEST(testPinAcrossGC)
--- a/js/src/jsapi-tests/testThreadingMutex.cpp
+++ b/js/src/jsapi-tests/testThreadingMutex.cpp
@@ -29,22 +29,8 @@ END_TEST(testThreadingLockGuard)
 BEGIN_TEST(testThreadingUnlockGuard)
 {
     js::Mutex mutex(js::mutexid::TestMutex);
     js::LockGuard<js::Mutex> guard(mutex);
     js::UnlockGuard<js::Mutex> unguard(guard);
     return true;
 }
 END_TEST(testThreadingUnlockGuard)
-
-BEGIN_TEST(testThreadingMoveMutex)
-{
-    js::Mutex mutex(js::mutexid::TestMutex);
-    mutex.lock();
-    mutex.unlock();
-
-    js::Mutex another(mozilla::Move(mutex));
-    another.lock();
-    another.unlock();
-
-    return true;
-}
-END_TEST(testThreadingMoveMutex)
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -806,25 +806,29 @@ JSFunction::initializeExtended()
     toExtended()->extendedSlots[0].init(js::UndefinedValue());
     toExtended()->extendedSlots[1].init(js::UndefinedValue());
 }
 
 inline void
 JSFunction::initExtendedSlot(size_t which, const js::Value& val)
 {
     MOZ_ASSERT(which < mozilla::ArrayLength(toExtended()->extendedSlots));
+    MOZ_ASSERT_IF(js::IsMarkedBlack(this) && val.isGCThing(),
+                  !JS::GCThingIsMarkedGray(JS::GCCellPtr(val)));
+    MOZ_ASSERT(js::IsObjectValueInCompartment(val, compartment()));
     toExtended()->extendedSlots[which].init(val);
 }
 
 inline void
 JSFunction::setExtendedSlot(size_t which, const js::Value& val)
 {
     MOZ_ASSERT(which < mozilla::ArrayLength(toExtended()->extendedSlots));
     MOZ_ASSERT_IF(js::IsMarkedBlack(this) && val.isGCThing(),
                   !JS::GCThingIsMarkedGray(JS::GCCellPtr(val)));
+    MOZ_ASSERT(js::IsObjectValueInCompartment(val, compartment()));
     toExtended()->extendedSlots[which] = val;
 }
 
 inline const js::Value&
 JSFunction::getExtendedSlot(size_t which) const
 {
     MOZ_ASSERT(which < mozilla::ArrayLength(toExtended()->extendedSlots));
     return toExtended()->extendedSlots[which];
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -3200,38 +3200,16 @@ GCRuntime::assertBackgroundSweepingFinis
             MOZ_ASSERT(!zone->arenas.arenaListsToSweep[i]);
             MOZ_ASSERT(zone->arenas.doneBackgroundFinalize(i));
         }
     }
     MOZ_ASSERT(blocksToFreeAfterSweeping.computedSizeOfExcludingThis() == 0);
 #endif
 }
 
-unsigned
-js::GetCPUCount()
-{
-    static unsigned ncpus = 0;
-    if (ncpus == 0) {
-#if defined(XP_WIN)
-        SYSTEM_INFO sysinfo;
-        GetSystemInfo(&sysinfo);
-        ncpus = unsigned(sysinfo.dwNumberOfProcessors);
-#elif (defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64)) && defined(_SC_NPROCESSORS_CONF)
-        // See bug 1333059: _SC_NPROCESSORS_ONLN is not reliable on ARM
-        // due to power saving mode.
-        long n = sysconf(_SC_NPROCESSORS_CONF);
-        ncpus = (n > 0) ? unsigned(n) : 1;
-#else
-        long n = sysconf(_SC_NPROCESSORS_ONLN);
-        ncpus = (n > 0) ? unsigned(n) : 1;
-#endif
-    }
-    return ncpus;
-}
-
 void
 GCHelperState::finish()
 {
     // Wait for any lingering background sweeping to finish.
     waitBackgroundSweepEnd();
 }
 
 GCHelperState::State
@@ -4670,21 +4648,21 @@ MarkIncomingCrossCompartmentPointers(JSR
         for (JSObject* src = c->gcIncomingGrayPointers;
              src;
              src = NextIncomingCrossCompartmentPointer(src, unlinkList))
         {
             JSObject* dst = CrossCompartmentPointerReferent(src);
             MOZ_ASSERT(dst->compartment() == c);
 
             if (color == GRAY) {
-                if (IsMarkedUnbarriered(&src) && src->asTenured().isMarked(GRAY))
+                if (IsMarkedUnbarriered(rt, &src) && src->asTenured().isMarked(GRAY))
                     TraceManuallyBarrieredEdge(&rt->gc.marker, &dst,
                                                "cross-compartment gray pointer");
             } else {
-                if (IsMarkedUnbarriered(&src) && !src->asTenured().isMarked(GRAY))
+                if (IsMarkedUnbarriered(rt, &src) && !src->asTenured().isMarked(GRAY))
                     TraceManuallyBarrieredEdge(&rt->gc.marker, &dst,
                                                "cross-compartment black pointer");
             }
         }
 
         if (unlinkList)
             c->gcIncomingGrayPointers = nullptr;
     }
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -20,17 +20,16 @@
 #include "js/Vector.h"
 #include "threading/ConditionVariable.h"
 #include "threading/Thread.h"
 #include "vm/NativeObject.h"
 
 namespace js {
 
 class AutoLockHelperThreadState;
-unsigned GetCPUCount();
 
 namespace gcstats {
 struct Statistics;
 } // namespace gcstats
 
 class Nursery;
 
 namespace gc {
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -1375,11 +1375,22 @@ extern bool
 SpeciesConstructor(JSContext* cx, HandleObject obj, HandleValue defaultCtor, MutableHandleValue pctor);
 
 extern bool
 SpeciesConstructor(JSContext* cx, HandleObject obj, JSProtoKey ctorKey, MutableHandleValue pctor);
 
 extern bool
 GetObjectFromIncumbentGlobal(JSContext* cx, MutableHandleObject obj);
 
+
+#ifdef DEBUG
+inline bool
+IsObjectValueInCompartment(const Value& v, JSCompartment* comp)
+{
+    if (!v.isObject())
+        return true;
+    return v.toObject().compartment() == comp;
+}
+#endif
+
 }  /* namespace js */
 
 #endif /* jsobj_h */
--- a/js/src/jswatchpoint.cpp
+++ b/js/src/jswatchpoint.cpp
@@ -149,30 +149,30 @@ bool
 WatchpointMap::markIteratively(GCMarker* marker)
 {
     bool marked = false;
     for (Map::Enum e(map); !e.empty(); e.popFront()) {
         Map::Entry& entry = e.front();
         JSObject* priorKeyObj = entry.key().object;
         jsid priorKeyId(entry.key().id.get());
         bool objectIsLive =
-            IsMarked(const_cast<PreBarrieredObject*>(&entry.key().object));
+            IsMarked(marker->runtime(), const_cast<PreBarrieredObject*>(&entry.key().object));
         if (objectIsLive || entry.value().held) {
             if (!objectIsLive) {
                 TraceEdge(marker, const_cast<PreBarrieredObject*>(&entry.key().object),
                            "held Watchpoint object");
                 marked = true;
             }
 
             MOZ_ASSERT(JSID_IS_STRING(priorKeyId) ||
                        JSID_IS_INT(priorKeyId) ||
                        JSID_IS_SYMBOL(priorKeyId));
             TraceEdge(marker, const_cast<PreBarrieredId*>(&entry.key().id), "WatchKey::id");
 
-            if (entry.value().closure && !IsMarked(&entry.value().closure)) {
+            if (entry.value().closure && !IsMarked(marker->runtime(), &entry.value().closure)) {
                 TraceEdge(marker, &entry.value().closure, "Watchpoint::closure");
                 marked = true;
             }
 
             /* We will sweep this entry in sweepAll if !objectIsLive. */
             if (priorKeyObj != entry.key().object || priorKeyId != entry.key().id)
                 e.rekeyFront(WatchKey(entry.key().object, entry.key().id));
         }
--- a/js/src/jsweakmap.h
+++ b/js/src/jsweakmap.h
@@ -183,17 +183,17 @@ class WeakMap : public HashMap<Key, Valu
         // If this cast fails, then you're instantiating the WeakMap with a
         // Lookup that can't be constructed from a Cell*. The WeakKeyTable
         // mechanism is indexed with a GCCellPtr, so that won't work.
         Ptr p = Base::lookup(static_cast<Lookup>(origKey.asCell()));
         MOZ_ASSERT(p.found());
 
         Key key(p->key());
         MOZ_ASSERT((markedCell == extractUnbarriered(key)) || (markedCell == getDelegate(key)));
-        if (gc::IsMarked(&key)) {
+        if (gc::IsMarked(marker->runtime(), &key)) {
             TraceEdge(marker, &p->value(), "ephemeron value");
         } else if (keyNeedsMark(key)) {
             TraceEdge(marker, &p->value(), "WeakMap ephemeron value");
             TraceEdge(marker, &key, "proxy-preserved WeakMap ephemeron key");
             MOZ_ASSERT(key == p->key()); // No moving
         }
         key.unsafeSet(nullptr); // Prevent destructor from running barriers.
     }
@@ -243,25 +243,25 @@ class WeakMap : public HashMap<Key, Valu
 
     bool markIteratively(GCMarker* marker) override {
         MOZ_ASSERT(marked);
 
         bool markedAny = false;
 
         for (Enum e(*this); !e.empty(); e.popFront()) {
             // If the entry is live, ensure its key and value are marked.
-            bool keyIsMarked = gc::IsMarked(&e.front().mutableKey());
+            bool keyIsMarked = gc::IsMarked(marker->runtime(), &e.front().mutableKey());
             if (!keyIsMarked && keyNeedsMark(e.front().key())) {
                 TraceEdge(marker, &e.front().mutableKey(), "proxy-preserved WeakMap entry key");
                 keyIsMarked = true;
                 markedAny = true;
             }
 
             if (keyIsMarked) {
-                if (!gc::IsMarked(&e.front().value())) {
+                if (!gc::IsMarked(marker->runtime(), &e.front().value())) {
                     TraceEdge(marker, &e.front().value(), "WeakMap entry value");
                     markedAny = true;
                 }
             } else if (marker->isWeakMarkingTracer()) {
                 // Entry is not yet known to be live. Record this weakmap and
                 // the lookup key in the list of weak keys. Also record the
                 // delegate, if any, because marking the delegate also marks
                 // the entry.
@@ -290,17 +290,17 @@ class WeakMap : public HashMap<Key, Valu
     void exposeGCThingToActiveJS(JSObject* obj) const { JS::ExposeObjectToActiveJS(obj); }
 
     bool keyNeedsMark(JSObject* key) const {
         JSObject* delegate = getDelegate(key);
         /*
          * Check if the delegate is marked with any color to properly handle
          * gray marking when the key's delegate is black and the map is gray.
          */
-        return delegate && gc::IsMarkedUnbarriered(&delegate);
+        return delegate && gc::IsMarkedUnbarriered(zone->runtimeFromMainThread(), &delegate);
     }
 
     bool keyNeedsMark(JSScript* script) const {
         return false;
     }
 
     bool findZoneEdges() override {
         // This is overridden by ObjectValueMap.
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -582,25 +582,27 @@ elif CONFIG['JS_CODEGEN_MIPS32'] or CONF
             UNIFIED_SOURCES += [
                 'jit/mips64/Simulator-mips64.cpp'
             ]
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     SOURCES += [
         'jit/ExecutableAllocatorWin.cpp',
         'threading/windows/ConditionVariable.cpp',
+        'threading/windows/CpuCount.cpp',
         'threading/windows/MutexImpl.cpp',
         'threading/windows/Thread.cpp',
     ]
     # _CRT_RAND_S must be #defined before #including stdlib.h to get rand_s()
     DEFINES['_CRT_RAND_S'] = True
 else:
     SOURCES += [
         'jit/ExecutableAllocatorPosix.cpp',
         'threading/posix/ConditionVariable.cpp',
+        'threading/posix/CpuCount.cpp',
         'threading/posix/MutexImpl.cpp',
         'threading/posix/Thread.cpp',
     ]
 
 if CONFIG['JS_HAS_CTYPES']:
     SOURCES += [
         'ctypes/CTypes.cpp',
         'ctypes/Library.cpp',
new file mode 100644
--- /dev/null
+++ b/js/src/threading/CpuCount.h
@@ -0,0 +1,16 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 <inttypes.h>
+
+namespace js {
+
+/*
+ * Return the number of cores on the system.  If the system provides logical
+ * cores (such as hyperthreads) then count each logical core as an actual core.
+ */
+uint32_t GetCPUCount();
+
+}
--- a/js/src/threading/Mutex.h
+++ b/js/src/threading/Mutex.h
@@ -3,70 +3,66 @@
 /* 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 threading_Mutex_h
 #define threading_Mutex_h
 
 #include "mozilla/Assertions.h"
-#include "mozilla/Attributes.h"
 #include "mozilla/Move.h"
 #include "mozilla/ThreadLocal.h"
 #include "mozilla/Vector.h"
 
-#include <new>
-#include <string.h>
-
 namespace js {
 
 class ConditionVariable;
 
 namespace detail {
 
 class MutexImpl
 {
 public:
   struct PlatformData;
 
   MutexImpl();
   ~MutexImpl();
 
-  MutexImpl(MutexImpl&& rhs)
-    : platformData_(rhs.platformData_)
-  {
-    MOZ_ASSERT(this != &rhs, "self move disallowed!");
-    rhs.platformData_ = nullptr;
-  }
-
-  MutexImpl& operator=(MutexImpl&& rhs) {
-    this->~MutexImpl();
-    new (this) MutexImpl(mozilla::Move(rhs));
-    return *this;
-  }
-
   bool operator==(const MutexImpl& rhs) {
     return platformData_ == rhs.platformData_;
   }
 
 protected:
   void lock();
   void unlock();
 
 private:
   MutexImpl(const MutexImpl&) = delete;
   void operator=(const MutexImpl&) = delete;
+  MutexImpl(MutexImpl&&) = delete;
+  void operator=(MutexImpl&&) = delete;
+
+  PlatformData* platformData();
+
+// Linux and maybe other platforms define the storage size of pthread_mutex_t in
+// bytes. However, we must define it as an array of void pointers to ensure
+// proper alignment.
+#if defined(__APPLE__) && defined(__MACH__) && defined(__i386__)
+  void* platformData_[11];
+#elif defined(__APPLE__) && defined(__MACH__) && defined(__amd64__)
+  void* platformData_[8];
+#elif defined(__linux__)
+  void* platformData_[40 / sizeof(void*)];
+#elif defined(_WIN32)
+  void* platformData_[6];
+#else
+  void* platformData_[64 / sizeof(void*)];
+#endif
 
   friend class js::ConditionVariable;
-  PlatformData* platformData() {
-    MOZ_ASSERT(platformData_);
-    return platformData_;
-  };
-
-  PlatformData* platformData_;
 };
 
 } // namespace detail
 
 // A MutexId secifies the name and mutex order for a mutex.
 //
 // The mutex order defines the allowed order of mutex acqusition on a single
 // thread. Mutexes must be acquired in strictly increasing order. Mutexes with
new file mode 100644
--- /dev/null
+++ b/js/src/threading/posix/CpuCount.cpp
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 <unistd.h>
+
+#include "threading/CpuCount.h"
+
+uint32_t
+js::GetCPUCount()
+{
+    static uint32_t ncpus = 0;
+
+    // _SC_NPROCESSORS_CONF and _SC_NPROCESSORS_ONLN are common, but not
+    // standard.
+    if (ncpus == 0) {
+#if defined(_SC_NPROCESSORS_CONF)
+        long n = sysconf(_SC_NPROCESSORS_CONF);
+        ncpus = (n > 0) ? uint32_t(n) : 1;
+#elif defined(_SC_NPROCESSORS_ONLN)
+        long n = sysconf(_SC_NPROCESSORS_ONLN);
+        ncpus = (n > 0) ? uint32_t(n) : 1;
+#else
+        ncpus = 1;
+#endif
+    }
+
+    return ncpus;
+}
--- a/js/src/threading/posix/MutexImpl.cpp
+++ b/js/src/threading/posix/MutexImpl.cpp
@@ -1,40 +1,35 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "mozilla/Assertions.h"
+
 #include <errno.h>
 #include <pthread.h>
 #include <stdio.h>
 
-#include "js/Utility.h"
-
 #include "threading/Mutex.h"
 #include "threading/posix/MutexPlatformData.h"
 
 #define TRY_CALL_PTHREADS(call, msg)            \
   {                                             \
     int result = (call);                        \
     if (result != 0) {                          \
       errno = result;                           \
       perror(msg);                              \
       MOZ_CRASH(msg);                           \
     }                                           \
   }
 
 js::detail::MutexImpl::MutexImpl()
 {
-  AutoEnterOOMUnsafeRegion oom;
-  platformData_ = js_new<PlatformData>();
-  if (!platformData_)
-    oom.crash("js::detail::MutexImpl::MutexImpl");
-
   pthread_mutexattr_t* attrp = nullptr;
 
 #ifdef DEBUG
   pthread_mutexattr_t attr;
 
   TRY_CALL_PTHREADS(pthread_mutexattr_init(&attr),
                     "js::detail::MutexImpl::MutexImpl: pthread_mutexattr_init failed");
 
@@ -50,23 +45,18 @@ js::detail::MutexImpl::MutexImpl()
 #ifdef DEBUG
   TRY_CALL_PTHREADS(pthread_mutexattr_destroy(&attr),
                     "js::detail::MutexImpl::MutexImpl: pthread_mutexattr_destroy failed");
 #endif
 }
 
 js::detail::MutexImpl::~MutexImpl()
 {
-  if (!platformData_)
-    return;
-
   TRY_CALL_PTHREADS(pthread_mutex_destroy(&platformData()->ptMutex),
                     "js::detail::MutexImpl::~MutexImpl: pthread_mutex_destroy failed");
-
-  js_delete(platformData());
 }
 
 void
 js::detail::MutexImpl::lock()
 {
   TRY_CALL_PTHREADS(pthread_mutex_lock(&platformData()->ptMutex),
                     "js::detail::MutexImpl::lock: pthread_mutex_lock failed");
 }
@@ -74,8 +64,16 @@ js::detail::MutexImpl::lock()
 void
 js::detail::MutexImpl::unlock()
 {
   TRY_CALL_PTHREADS(pthread_mutex_unlock(&platformData()->ptMutex),
                     "js::detail::MutexImpl::unlock: pthread_mutex_unlock failed");
 }
 
 #undef TRY_CALL_PTHREADS
+
+js::detail::MutexImpl::PlatformData*
+js::detail::MutexImpl::platformData()
+{
+  static_assert(sizeof(platformData_) >= sizeof(PlatformData),
+                "platformData_ is too small");
+  return reinterpret_cast<PlatformData*>(platformData_);
+}
new file mode 100644
--- /dev/null
+++ b/js/src/threading/windows/CpuCount.cpp
@@ -0,0 +1,22 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "jswin.h"
+
+#include "threading/CpuCount.h"
+
+uint32_t
+js::GetCPUCount()
+{
+    static uint32_t ncpus = 0;
+
+    if (ncpus == 0) {
+        SYSTEM_INFO sysinfo;
+        GetSystemInfo(&sysinfo);
+        ncpus = uint32_t(sysinfo.dwNumberOfProcessors);
+    }
+
+    return ncpus;
+}
--- a/js/src/threading/windows/MutexImpl.cpp
+++ b/js/src/threading/windows/MutexImpl.cpp
@@ -1,20 +1,19 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "mozilla/Assertions.h"
 #include "mozilla/DebugOnly.h"
 
 #include "jswin.h"
 
-#include "js/Utility.h"
-
 #include "threading/Mutex.h"
 #include "threading/windows/MutexPlatformData.h"
 
 namespace {
 
 // We build with a toolkit that supports WinXP, so we have to probe
 // for modern features at runtime. This is necessary because Vista and
 // later automatically allocate and subsequently leak a debug info
@@ -39,47 +38,46 @@ struct MutexNativeImports
 };
 
 static MutexNativeImports NativeImports;
 
 } // (anonymous namespace)
 
 js::detail::MutexImpl::MutexImpl()
 {
-  AutoEnterOOMUnsafeRegion oom;
-  platformData_ = js_new<PlatformData>();
-  if (!platformData_)
-    oom.crash("js::Mutex::Mutex");
-
   // This number was adopted from NSPR.
   const static DWORD LockSpinCount = 1500;
   BOOL r;
   if (NativeImports.hasInitializeCriticalSectionEx()) {
     r = NativeImports.InitializeCriticalSectionEx(&platformData()->criticalSection,
                                                   LockSpinCount,
                                                   CRITICAL_SECTION_NO_DEBUG_INFO);
   } else {
     r = InitializeCriticalSectionAndSpinCount(&platformData()->criticalSection,
                                               LockSpinCount);
   }
   MOZ_RELEASE_ASSERT(r);
 }
 
 js::detail::MutexImpl::~MutexImpl()
 {
-  if (!platformData_)
-    return;
-
   DeleteCriticalSection(&platformData()->criticalSection);
-  js_delete(platformData());
 }
 
 void
 js::detail::MutexImpl::lock()
 {
   EnterCriticalSection(&platformData()->criticalSection);
 }
 
 void
 js::detail::MutexImpl::unlock()
 {
   LeaveCriticalSection(&platformData()->criticalSection);
 }
+
+js::detail::MutexImpl::PlatformData*
+js::detail::MutexImpl::platformData()
+{
+  static_assert(sizeof(platformData_) >= sizeof(PlatformData),
+                "platformData_ is too small");
+  return reinterpret_cast<PlatformData*>(platformData_);
+}
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -801,32 +801,32 @@ JSObject*
 Debugger::getHook(Hook hook) const
 {
     MOZ_ASSERT(hook >= 0 && hook < HookCount);
     const Value& v = object->getReservedSlot(JSSLOT_DEBUG_HOOK_START + hook);
     return v.isUndefined() ? nullptr : &v.toObject();
 }
 
 bool
-Debugger::hasAnyLiveHooks() const
+Debugger::hasAnyLiveHooks(JSRuntime* rt) const
 {
     if (!enabled)
         return false;
 
     if (getHook(OnDebuggerStatement) ||
         getHook(OnExceptionUnwind) ||
         getHook(OnNewScript) ||
         getHook(OnEnterFrame))
     {
         return true;
     }
 
     /* If any breakpoints are in live scripts, return true. */
     for (Breakpoint* bp = firstBreakpoint(); bp; bp = bp->nextInDebugger()) {
-        if (IsMarkedUnbarriered(&bp->site->script))
+        if (IsMarkedUnbarriered(rt, &bp->site->script))
             return true;
     }
 
     for (FrameMap::Range r = frames.all(); !r.empty(); r.popFront()) {
         NativeObject* frameObj = r.front().value();
         if (!frameObj->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER).isUndefined() ||
             !frameObj->getReservedSlot(JSSLOT_DEBUGFRAME_ONPOP_HANDLER).isUndefined())
             return true;
@@ -2999,17 +2999,17 @@ Debugger::markIteratively(GCMarker* mark
     /*
      * Find all Debugger objects in danger of GC. This code is a little
      * convoluted since the easiest way to find them is via their debuggees.
      */
     JSRuntime* rt = marker->runtime();
     for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
         if (c->isDebuggee()) {
             GlobalObject* global = c->unsafeUnbarrieredMaybeGlobal();
-            if (!IsMarkedUnbarriered(&global))
+            if (!IsMarkedUnbarriered(rt, &global))
                 continue;
 
             /*
              * Every debuggee has at least one debugger, so in this case
              * getDebuggers can't return nullptr.
              */
             const GlobalObject::DebuggerVector* debuggers = global->getDebuggers();
             MOZ_ASSERT(debuggers);
@@ -3021,36 +3021,36 @@ Debugger::markIteratively(GCMarker* mark
                  *   - dbg is actually in a compartment that is being marked
                  *   - it isn't already marked
                  *   - it actually has hooks that might be called
                  */
                 GCPtrNativeObject& dbgobj = dbg->toJSObjectRef();
                 if (!dbgobj->zone()->isGCMarking())
                     continue;
 
-                bool dbgMarked = IsMarked(&dbgobj);
-                if (!dbgMarked && dbg->hasAnyLiveHooks()) {
+                bool dbgMarked = IsMarked(rt, &dbgobj);
+                if (!dbgMarked && dbg->hasAnyLiveHooks(rt)) {
                     /*
                      * obj could be reachable only via its live, enabled
                      * debugger hooks, which may yet be called.
                      */
                     TraceEdge(marker, &dbgobj, "enabled Debugger");
                     markedAny = true;
                     dbgMarked = true;
                 }
 
                 if (dbgMarked) {
                     /* Search for breakpoints to mark. */
                     for (Breakpoint* bp = dbg->firstBreakpoint(); bp; bp = bp->nextInDebugger()) {
-                        if (IsMarkedUnbarriered(&bp->site->script)) {
+                        if (IsMarkedUnbarriered(rt, &bp->site->script)) {
                             /*
                              * The debugger and the script are both live.
                              * Therefore the breakpoint handler is live.
                              */
-                            if (!IsMarked(&bp->getHandlerRef())) {
+                            if (!IsMarked(rt, &bp->getHandlerRef())) {
                                 TraceEdge(marker, &bp->getHandlerRef(), "breakpoint handler");
                                 markedAny = true;
                             }
                         }
                     }
                 }
             }
         }
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -692,17 +692,17 @@ class Debugger : private mozilla::Linked
 
     static bool hookObservesAllExecution(Hook which);
 
     MOZ_MUST_USE bool updateObservesAllExecutionOnDebuggees(JSContext* cx, IsObserving observing);
     MOZ_MUST_USE bool updateObservesCoverageOnDebuggees(JSContext* cx, IsObserving observing);
     void updateObservesAsmJSOnDebuggees(IsObserving observing);
 
     JSObject* getHook(Hook hook) const;
-    bool hasAnyLiveHooks() const;
+    bool hasAnyLiveHooks(JSRuntime* rt) const;
 
     static MOZ_MUST_USE bool slowPathCheckNoExecute(JSContext* cx, HandleScript script);
     static JSTrapStatus slowPathOnEnterFrame(JSContext* cx, AbstractFramePtr frame);
     static MOZ_MUST_USE bool slowPathOnLeaveFrame(JSContext* cx, AbstractFramePtr frame,
                                                   jsbytecode* pc, bool ok);
     static JSTrapStatus slowPathOnDebuggerStatement(JSContext* cx, AbstractFramePtr frame);
     static JSTrapStatus slowPathOnExceptionUnwind(JSContext* cx, AbstractFramePtr frame);
     static void slowPathOnNewScript(JSContext* cx, HandleScript script);
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -11,16 +11,17 @@
 
 #include "jsnativestack.h"
 #include "jsnum.h" // For FIX_FPU()
 
 #include "builtin/Promise.h"
 #include "frontend/BytecodeCompiler.h"
 #include "gc/GCInternals.h"
 #include "jit/IonBuilder.h"
+#include "threading/CpuCount.h"
 #include "vm/Debugger.h"
 #include "vm/SharedImmutableStringsCache.h"
 #include "vm/Time.h"
 #include "vm/TraceLogging.h"
 
 #include "jscntxtinlines.h"
 #include "jscompartmentinlines.h"
 #include "jsobjinlines.h"
--- a/js/src/vm/Interpreter-inl.h
+++ b/js/src/vm/Interpreter-inl.h
@@ -538,17 +538,17 @@ GetElemOptimizedArguments(JSContext* cx,
 
         lref.set(ObjectValue(frame.argsObj()));
     }
 
     return true;
 }
 
 static MOZ_ALWAYS_INLINE bool
-GetElementOperation(JSContext* cx, JSOp op, MutableHandleValue lref, HandleValue rref,
+GetElementOperation(JSContext* cx, JSOp op, HandleValue lref, HandleValue rref,
                     MutableHandleValue res)
 {
     MOZ_ASSERT(op == JSOP_GETELEM || op == JSOP_CALLELEM);
 
     uint32_t index;
     if (lref.isString() && IsDefinitelyIndex(rref, &index)) {
         JSString* str = lref.toString();
         if (index < str->length()) {
--- a/js/src/vm/NativeObject.h
+++ b/js/src/vm/NativeObject.h
@@ -328,21 +328,16 @@ extern HeapSlot* const emptyObjectElemen
 extern HeapSlot* const emptyObjectElementsShared;
 
 struct Class;
 class GCMarker;
 class Shape;
 
 class NewObjectCache;
 
-#ifdef DEBUG
-static inline bool
-IsObjectValueInCompartment(const Value& v, JSCompartment* comp);
-#endif
-
 // Operations which change an object's dense elements can either succeed, fail,
 // or be unable to complete. For native objects, the latter is used when the
 // object's elements must become sparse instead. The enum below is used for
 // such operations, and for similar operations on unboxed arrays and methods
 // that work on both kinds of objects.
 enum class DenseElementResult {
     Failure,
     Success,
@@ -929,21 +924,23 @@ class NativeObject : public ShapedObject
 
     const Value& getFixedSlot(uint32_t slot) const {
         MOZ_ASSERT(slot < numFixedSlots());
         return fixedSlots()[slot];
     }
 
     void setFixedSlot(uint32_t slot, const Value& value) {
         MOZ_ASSERT(slot < numFixedSlots());
+        MOZ_ASSERT(IsObjectValueInCompartment(value, compartment()));
         fixedSlots()[slot].set(this, HeapSlot::Slot, slot, value);
     }
 
     void initFixedSlot(uint32_t slot, const Value& value) {
         MOZ_ASSERT(slot < numFixedSlots());
+        MOZ_ASSERT(IsObjectValueInCompartment(value, compartment()));
         fixedSlots()[slot].init(this, HeapSlot::Slot, slot, value);
     }
 
     /*
      * Get the number of dynamic slots to allocate to cover the properties in
      * an object with the given number of fixed slots and slot span. The slot
      * capacity is not stored explicitly, and the allocated size of the slot
      * array is kept in sync with this count.
@@ -1335,26 +1332,16 @@ class PlainObject : public NativeObject
 inline void
 NativeObject::privateWriteBarrierPre(void** oldval)
 {
     JS::shadow::Zone* shadowZone = this->shadowZoneFromAnyThread();
     if (shadowZone->needsIncrementalBarrier() && *oldval && getClass()->hasTrace())
         getClass()->doTrace(shadowZone->barrierTracer(), this);
 }
 
-#ifdef DEBUG
-static inline bool
-IsObjectValueInCompartment(const Value& v, JSCompartment* comp)
-{
-    if (!v.isObject())
-        return true;
-    return v.toObject().compartment() == comp;
-}
-#endif
-
 
 /*** Standard internal methods *******************************************************************/
 
 /*
  * These functions should follow the algorithms in ES6 draft rev 29 section 9.1
  * ("Ordinary Object Internal Methods"). It's an ongoing project.
  *
  * Many native objects are not "ordinary" in ES6, so these functions also have
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -1270,17 +1270,17 @@ RegExpShared::needsSweep(JSRuntime* rt)
     // being subsequently cleared. This can happen if a GC is restarted while
     // in progress (i.e. performing a full GC in the middle of an incremental
     // GC) or if a RegExpShared referenced via the stack is traced but is not
     // in a zone being collected.
     //
     // Because of this we only treat the marked_ bit as a hint, and destroy the
     // RegExpShared if it was accidentally marked earlier but wasn't marked by
     // the current trace.
-    bool keep = marked() && IsMarked(&source);
+    bool keep = marked() && IsMarked(rt, &source);
     for (size_t i = 0; i < ArrayLength(compilationArray); i++) {
         RegExpShared::RegExpCompilation& compilation = compilationArray[i];
         if (compilation.jitCode && gc::IsAboutToBeFinalized(&compilation.jitCode))
             keep = false;
     }
 
     MOZ_ASSERT(rt->isHeapMajorCollecting());
     if (keep || rt->gc.isHeapCompacting()) {
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -789,26 +789,26 @@ TypeSet::readBarrier(const TypeSet* type
                 (void) key->singleton();
             else
                 (void) key->group();
         }
     }
 }
 
 /* static */ bool
-TypeSet::IsTypeMarked(TypeSet::Type* v)
+TypeSet::IsTypeMarked(JSRuntime* rt, TypeSet::Type* v)
 {
     bool rv;
     if (v->isSingletonUnchecked()) {
         JSObject* obj = v->singletonNoBarrier();
-        rv = IsMarkedUnbarriered(&obj);
+        rv = IsMarkedUnbarriered(rt, &obj);
         *v = TypeSet::ObjectType(obj);
     } else if (v->isGroupUnchecked()) {
         ObjectGroup* group = v->groupNoBarrier();
-        rv = IsMarkedUnbarriered(&group);
+        rv = IsMarkedUnbarriered(rt, &group);
         *v = TypeSet::ObjectType(group);
     } else {
         rv = true;
     }
     return rv;
 }
 
 /* static */ bool
--- a/js/src/vm/TypeInference.h
+++ b/js/src/vm/TypeInference.h
@@ -524,17 +524,17 @@ class TypeSet
 
     static inline bool IsUntrackedValue(const Value& val);
 
     // Get the type of a possibly optimized out or uninitialized let value.
     // This generally only happens on unconditional type monitors on bailing
     // out of Ion, such as for argument and local types.
     static inline Type GetMaybeUntrackedValueType(const Value& val);
 
-    static bool IsTypeMarked(Type* v);
+    static bool IsTypeMarked(JSRuntime* rt, Type* v);
     static bool IsTypeAllocatedDuringIncremental(Type v);
     static bool IsTypeAboutToBeFinalized(Type* v);
 } JS_HAZ_GC_POINTER;
 
 #if JS_BITS_PER_WORD == 32
 static const uintptr_t TypeInferenceMagic = 0xa1a2b3b4;
 #else
 static const uintptr_t TypeInferenceMagic = 0xa1a2b3b4c5c6d7d8;
--- a/js/src/wasm/WasmSignalHandlers.cpp
+++ b/js/src/wasm/WasmSignalHandlers.cpp
@@ -15,16 +15,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #include "wasm/WasmSignalHandlers.h"
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/PodOperations.h"
+#include "mozilla/ScopeExit.h"
 
 #include "jit/AtomicOperations.h"
 #include "jit/Disassembler.h"
 #include "vm/Runtime.h"
 #include "wasm/WasmInstance.h"
 
 using namespace js;
 using namespace js::jit;
@@ -1070,49 +1071,50 @@ MachExceptionHandler::uninstall()
 
 bool
 MachExceptionHandler::install(JSRuntime* rt)
 {
     MOZ_ASSERT(!installed());
     kern_return_t kret;
     mach_port_t thread;
 
+    auto onFailure = mozilla::MakeScopeExit([&] {
+        uninstall();
+    });
+
     // Get a port which can send and receive data.
     kret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port_);
     if (kret != KERN_SUCCESS)
-        goto error;
+        return false;
     kret = mach_port_insert_right(mach_task_self(), port_, port_, MACH_MSG_TYPE_MAKE_SEND);
     if (kret != KERN_SUCCESS)
-        goto error;
+        return false;
 
     // Create a thread to block on reading port_.
     if (!thread_.init(MachExceptionHandlerThread, rt))
-        goto error;
+        return false;
 
     // Direct exceptions on this thread to port_ (and thus our handler thread).
     // Note: we are totally clobbering any existing *thread* exception ports and
     // not even attempting to forward. Breakpad and gdb both use the *process*
     // exception ports which are only called if the thread doesn't handle the
     // exception, so we should be fine.
     thread = mach_thread_self();
     kret = thread_set_exception_ports(thread,
                                       EXC_MASK_BAD_ACCESS,
                                       port_,
                                       EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES,
                                       THREAD_STATE_NONE);
     mach_port_deallocate(mach_task_self(), thread);
     if (kret != KERN_SUCCESS)
-        goto error;
+        return false;
 
     installed_ = true;
+    onFailure.release();
     return true;
-
-  error:
-    uninstall();
-    return false;
 }
 
 #else  // If not Windows or Mac, assume Unix
 
 enum class Signal {
     SegFault,
     BusError
 };
--- a/media/webrtc/trunk/webrtc/modules/video_coding/rtt_filter.cc
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/rtt_filter.cc
@@ -44,18 +44,18 @@ VCMRttFilter& VCMRttFilter::operator=(co
 void VCMRttFilter::Reset() {
   _gotNonZeroUpdate = false;
   _avgRtt = 0;
   _varRtt = 0;
   _maxRtt = 0;
   _filtFactCount = 1;
   _jumpCount = 0;
   _driftCount = 0;
-  memset(_jumpBuf, 0, kMaxDriftJumpCount);
-  memset(_driftBuf, 0, kMaxDriftJumpCount);
+  memset(_jumpBuf, 0, sizeof(_jumpBuf));
+  memset(_driftBuf, 0, sizeof(_driftBuf));
 }
 
 void VCMRttFilter::Update(int64_t rttMs) {
   if (!_gotNonZeroUpdate) {
     if (rttMs == 0) {
       return;
     }
     _gotNonZeroUpdate = true;
--- a/netwerk/cache2/CacheEntry.cpp
+++ b/netwerk/cache2/CacheEntry.cpp
@@ -1474,17 +1474,17 @@ NS_IMETHODIMP CacheEntry::Recreate(bool 
 
   RefPtr<CacheEntryHandle> handle = ReopenTruncated(aMemoryOnly, nullptr);
   if (handle) {
     handle.forget(_retval);
     return NS_OK;
   }
 
   BackgroundOp(Ops::CALLBACKS, true);
-  return NS_OK;
+  return NS_ERROR_NOT_AVAILABLE;
 }
 
 NS_IMETHODIMP CacheEntry::GetDataSize(int64_t *aDataSize)
 {
   LOG(("CacheEntry::GetDataSize [this=%p]", this));
   *aDataSize = 0;
 
   {
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -595,16 +595,21 @@ HttpChannelParent::ConnectChannel(const 
   nsCOMPtr<nsIChannel> channel;
   rv = NS_LinkRedirectChannels(registrarId, this, getter_AddRefs(channel));
   // It's safe to cast here since the found parent-side real channel is ensured
   // to be http (nsHttpChannel).  ConnectChannel called from HttpChannelParent::Init
   // can only be called for http channels.  It's bound by ipdl.
   mChannel = static_cast<nsHttpChannel*>(channel.get());
   LOG(("  found channel %p, rv=%08x", mChannel.get(), rv));
 
+  if (NS_FAILED(rv)) {
+    NS_ERROR("Could not find the http channel to connect its IPC parent");
+    return false;
+  }
+
   nsCOMPtr<nsINetworkInterceptController> controller;
   NS_QueryNotificationCallbacks(channel, controller);
   RefPtr<HttpChannelParentListener> parentListener = do_QueryObject(controller);
   MOZ_ASSERT(parentListener);
   parentListener->SetupInterceptionAfterRedirect(shouldIntercept);
 
   if (mPBOverride != kPBOverride_Unset) {
     // redirected-to channel may not support PB
--- a/taskcluster/ci/nightly-l10n/kind.yml
+++ b/taskcluster/ci/nightly-l10n/kind.yml
@@ -66,20 +66,24 @@ job-template:
             macosx64-nightly: macosx64/opt
             android-api-15-nightly: android-4-0-armv7-api15/opt
    env:
       by-build-platform:
          linux.*:   # linux64 and 32 get same treatment here
             EN_US_PACKAGE_NAME: target.tar.bz2
             EN_US_BINARY_URL:
                task-reference: https://queue.taskcluster.net/v1/task/<unsigned-build>/artifacts/public/build
+            MAR_TOOLS_URL:
+               task-reference: https://queue.taskcluster.net/v1/task/<unsigned-build>/artifacts/public/build/host/bin
          macosx64-nightly:
             EN_US_PACKAGE_NAME: target.dmg
             EN_US_BINARY_URL:
                task-reference: https://queue.taskcluster.net/v1/task/<unsigned-build>/artifacts/public/build
+            MAR_TOOLS_URL:
+               task-reference: https://queue.taskcluster.net/v1/task/<unsigned-build>/artifacts/public/build/host/bin
          android-api-15-nightly:
             EN_US_PACKAGE_NAME: target.apk
             EN_US_BINARY_URL:
                task-reference: https://queue.taskcluster.net/v1/task/<unsigned-build>/artifacts/public/build/en-US
    mozharness:
       config:
          by-build-platform:
             linux-nightly:
--- a/taskcluster/ci/test/tests.yml
+++ b/taskcluster/ci/test/tests.yml
@@ -462,24 +462,21 @@ mochitest-chrome:
     treeherder-symbol: tc-M(c)
     loopback-video: true
     instance-size:
         by-test-platform:
             android.*: xlarge
             default: default
     chunks:
         by-test-platform:
-            android-4.3-arm7-api-15/debug: 2
-            android.*: 1
+            android-4.3-arm7-api-15/debug: 4
+            android.*: 2
             macosx.*: 1
             default: 3
-    max-run-time:
-        by-test-platform:
-            android.*: 5400
-            default: 3600
+    max-run-time: 3600
     e10s: false
     mozharness:
         by-test-platform:
             android.*:
                 script: android_emulator_unittest.py
                 no-read-buildbot-config: true
                 config:
                     by-test-platform:
@@ -635,16 +632,17 @@ mochitest-gpu:
 
 mochitest-jetpack:
     description: "Mochitest jetpack run"
     suite: mochitest/jetpack-package
     treeherder-symbol: tc-M(JP)
     loopback-video: true
     e10s: false
     max-run-time: 5400
+    docker-image: {"in-tree": "desktop1604-test"}
     run-on-projects:
         by-test-platform:
             windows.*: ['mozilla-central', 'try']
             default: ['all']
     mozharness:
         script: desktop_unittest.py
         no-read-buildbot-config: true
         chunked: true
@@ -1176,29 +1174,25 @@ web-platform-tests-wdspec:
 
 xpcshell:
     description: "xpcshell test run"
     suite: xpcshell
     treeherder-symbol: tc-X()
     chunks:
         by-test-platform:
             linux64/debug: 10
-            android-4.2-x86/opt: 4
-            android.*: 6
+            android-4.2-x86/opt: 6
             # windows.*: 1
             macosx.*: 1
             default: 8
     instance-size:
         by-test-platform:
             android.*: xlarge
             default: legacy # Bug 1281241: migrating to m3.large instances
-    max-run-time:
-        by-test-platform:
-            android.*: 7200
-            default: 5400
+    max-run-time: 5400
     e10s: false
     allow-software-gl-layers: false
     mozharness:
         by-test-platform:
             android.*:
                 script: android_emulator_unittest.py
                 no-read-buildbot-config: true
                 extra-options:
--- a/taskcluster/taskgraph/transforms/task.py
+++ b/taskcluster/taskgraph/transforms/task.py
@@ -240,20 +240,24 @@ task_description_schema = Schema({
             Optional('repository'): basestring,
             Optional('project'): basestring,
         },
         'properties': {
             'product': basestring,
             Extra: basestring,  # additional properties are allowed
         },
     }, {
-        'implementation': 'macosx-engine',
+        'implementation': 'native-engine',
 
         # A link for an executable to download
-        Optional('link'): basestring,
+        Optional('context'): basestring,
+
+        # Tells the worker whether machine should reboot
+        # after the task is finished.
+        Optional('reboot'): bool,
 
         # the command to run
         Required('command'): [taskref_or_string],
 
         # environment variables
         Optional('env'): {basestring: taskref_or_string},
 
         # artifacts to extract from the task image after completion
@@ -571,35 +575,36 @@ def build_beetmover_payload(config, task
 def build_balrog_payload(config, task, task_def):
     worker = task['worker']
 
     task_def['payload'] = {
         'upstreamArtifacts':  worker['upstream-artifacts']
     }
 
 
-@payload_builder('macosx-engine')
+@payload_builder('native-engine')
 def build_macosx_engine_payload(config, task, task_def):
     worker = task['worker']
     artifacts = map(lambda artifact: {
         'name': artifact['name'],
         'path': artifact['path'],
         'type': artifact['type'],
         'expires': task_def['expires'],
     }, worker['artifacts'])
 
     task_def['payload'] = {
-        'link': worker['link'],
+        'context': worker['context'],
         'command': worker['command'],
         'env': worker['env'],
+        'reboot': worker['reboot'],
         'artifacts': artifacts,
     }
 
     if task.get('needs-sccache'):
-        raise Exception('needs-sccache not supported in macosx-engine')
+        raise Exception('needs-sccache not supported in native-engine')
 
 
 @payload_builder('buildbot-bridge')
 def build_buildbot_bridge_payload(config, task, task_def):
     del task['extra']['treeherder']
     del task['extra']['treeherderEnv']
     worker = task['worker']
     task_def['payload'] = {
--- a/taskcluster/taskgraph/transforms/tests.py
+++ b/taskcluster/taskgraph/transforms/tests.py
@@ -159,17 +159,17 @@ test_description_schema = Schema({
     # due to poor LLVMPipe performance (bug 1296086).  Defaults to true for
     # linux platforms and false otherwise
     Optional('allow-software-gl-layers'): bool,
 
     # The worker implementation for this test, as dictated by policy and by the
     # test platform.
     Optional('worker-implementation'): Any(
         'docker-worker',
-        'macosx-engine',
+        'native-engine',
         'generic-worker',
         # coming soon:
         'docker-engine',
         'buildbot-bridge',
     ),
 
     # For tasks that will run in docker-worker or docker-engine, this is the
     # name of the docker image or in-tree docker image to run the task in.  If
@@ -192,16 +192,19 @@ test_description_schema = Schema({
         int),
 
     # the exit status code that indicates the task should be retried
     Optional('retry-exit-status'): int,
 
     # Whether to perform a gecko checkout.
     Required('checkout', default=False): bool,
 
+    # Wheter to perform a machine reboot after test is done
+    Optional('reboot', default=False): bool,
+
     # What to run
     Required('mozharness'): optionally_keyed_by(
         'test-platform', 'test-platform-phylum', {
             # the mozharness script used to run this task
             Required('script'): basestring,
 
             # the config files required for the task
             Required('config'): optionally_keyed_by(
@@ -328,16 +331,17 @@ def set_defaults(config, tests):
         else:
             test['allow-software-gl-layers'] = False
 
         test.setdefault('os-groups', [])
         test.setdefault('chunks', 1)
         test.setdefault('run-on-projects', ['all'])
         test.setdefault('instance-size', 'default')
         test.setdefault('max-run-time', 3600)
+        test.setdefault('reboot', False)
         test['mozharness'].setdefault('extra-options', [])
         yield test
 
 
 @transforms.add
 def set_target(config, tests):
     for test in tests:
         build_platform = test['build-platform']
@@ -390,17 +394,17 @@ def set_asan_docker_image(config, tests)
 def set_worker_implementation(config, tests):
     """Set the worker implementation based on the test platform."""
     for test in tests:
         if test.get('suite', '') == 'talos':
             test['worker-implementation'] = 'buildbot-bridge'
         elif test['test-platform'].startswith('win'):
             test['worker-implementation'] = 'generic-worker'
         elif test['test-platform'].startswith('macosx'):
-            test['worker-implementation'] = 'macosx-engine'
+            test['worker-implementation'] = 'native-engine'
         else:
             test['worker-implementation'] = 'docker-worker'
         yield test
 
 
 @transforms.add
 def set_tier(config, tests):
     """Set the tier based on policy for all test descriptions that do not
@@ -896,32 +900,33 @@ def generic_worker_setup(config, test, t
 
     worker['command'] = artifact_link_commands + [
         {'task-reference': 'c:\\mozilla-build\\wget\\wget.exe {}'.format(mozharness_url)},
         'c:\\mozilla-build\\info-zip\\unzip.exe mozharness.zip',
         {'task-reference': ' '.join(mh_command)}
     ]
 
 
-@worker_setup_function("macosx-engine")
+@worker_setup_function("native-engine")
 def macosx_engine_setup(config, test, taskdesc):
     mozharness = test['mozharness']
 
     installer_url = ARTIFACT_URL.format('<build>', mozharness['build-artifact-name'])
     test_packages_url = ARTIFACT_URL.format('<build>',
                                             'public/build/target.test_packages.json')
     mozharness_url = ARTIFACT_URL.format('<build>',
                                          'public/build/mozharness.zip')
 
     # for now we have only 10.10 machines
     taskdesc['worker-type'] = 'tc-worker-provisioner/gecko-t-osx-10-10'
 
     worker = taskdesc['worker'] = {}
     worker['implementation'] = test['worker-implementation']
 
+    worker['reboot'] = test['reboot']
     worker['artifacts'] = [{
         'name': prefix.rstrip('/'),
         'path': path.rstrip('/'),
         'type': 'directory',
     } for (prefix, path) in ARTIFACTS]
 
     worker['env'] = {
         'GECKO_HEAD_REPOSITORY': config.params['head_repository'],
@@ -929,17 +934,17 @@ def macosx_engine_setup(config, test, ta
         'MOZHARNESS_CONFIG': ' '.join(mozharness['config']),
         'MOZHARNESS_SCRIPT': mozharness['script'],
         'MOZHARNESS_URL': {'task-reference': mozharness_url},
         'MOZILLA_BUILD_URL': {'task-reference': installer_url},
     }
 
     # assemble the command line
 
-    worker['link'] = '{}/raw-file/{}/taskcluster/scripts/tester/test-macosx.sh'.format(
+    worker['context'] = '{}/raw-file/{}/taskcluster/scripts/tester/test-macosx.sh'.format(
         config.params['head_repository'], config.params['head_rev']
     )
 
     command = worker['command'] = ["./test-macosx.sh"]
     if mozharness.get('no-read-buildbot-config'):
         command.append("--no-read-buildbot-config")
     command.extend([
         {"task-reference": "--installer-url=" + installer_url},
--- a/testing/firefox-ui/tests/functional/security/manifest.ini
+++ b/testing/firefox-ui/tests/functional/security/manifest.ini
@@ -6,17 +6,16 @@ tags = remote
 tags = local
 [test_ev_certificate.py]
 [test_mixed_content_page.py]
 [test_mixed_script_content_blocking.py]
 [test_no_certificate.py]
 tags = local
 [test_safe_browsing_initial_download.py]
 [test_safe_browsing_notification.py]
-skip-if = e10s # Bug 1315042
 [test_safe_browsing_warning_pages.py]
 [test_security_notification.py]
 [test_ssl_disabled_error_page.py]
 [test_ssl_status_after_restart.py]
 skip-if = (os == "win" && os_version == "5.1") # Bug 1167179: Fails to open popups after restart
 [test_submit_unencrypted_info_warning.py]
 [test_unknown_issuer.py]
 [test_untrusted_connection_error_page.py]
--- a/testing/mochitest/mochitest_options.py
+++ b/testing/mochitest/mochitest_options.py
@@ -583,16 +583,21 @@ class MochitestArguments(ArgumentContain
           }],
         [["--cleanup-crashes"],
          {"action": "store_true",
           "dest": "cleanupCrashes",
           "default": False,
           "help": "Delete pending crash reports before running tests.",
           "suppress": True,
           }],
+        [["--websocket-process-bridge-port"],
+         {"default": "8191",
+          "dest": "websocket_process_bridge_port",
+          "help": "Port for websocket/process bridge. Default 8191.",
+          }],
     ]
 
     defaults = {
         # Bug 1065098 - The geckomediaplugin process fails to produce a leak
         # log for some reason.
         'ignoreMissingLeaks': ["geckomediaplugin"],
         'extensionsToExclude': ['specialpowers'],
         # Set server information on the args object
--- a/testing/mochitest/runtests.py
+++ b/testing/mochitest/runtests.py
@@ -1086,17 +1086,19 @@ class MochitestDesktop(object):
 
     def startWebsocketProcessBridge(self, options):
         """Create a websocket server that can launch various processes that
         JS needs (eg; ICE server for webrtc testing)
         """
 
         command = [sys.executable,
                    os.path.join("websocketprocessbridge",
-                                "websocketprocessbridge.py")]
+                                "websocketprocessbridge.py"),
+                   "--port",
+                   options.websocket_process_bridge_port]
         self.websocketProcessBridge = mozprocess.ProcessHandler(command,
                                                                 cwd=SCRIPT_DIR)
         self.websocketProcessBridge.run()
         self.log.info("runtests.py | websocket/process bridge pid: %d"
                       % self.websocketProcessBridge.pid)
 
         # ensure the server is up, wait for at most ten seconds
         for i in range(1, 100):
--- a/testing/mozharness/configs/single_locale/alder.py
+++ b/testing/mozharness/configs/single_locale/alder.py
@@ -11,28 +11,34 @@
 #
 # If you really want to have localized alder builds, use the use the following
 # values:
 # * "en_us_binary_url": "http://ftp.mozilla.org/pub/mozilla.org/firefox/tinderbox-builds/alder-%(platform)s/latest/",
 # * "mar_tools_url": "http://ftp.mozilla.org/pub/mozilla.org/firefox/tinderbox-builds/alder-%(platform)s/latest/",
 # * "repo": "https://hg.mozilla.org/projects/alder",
 #
 
+import os
+
 config = {
     "nightly_build": True,
     "branch": "alder",
     "en_us_binary_url": "http://ftp.mozilla.org/pub/mozilla.org/firefox/nightly/latest-mozilla-central/",
     "update_channel": "nightly",
     "latest_mar_dir": '/pub/mozilla.org/firefox/nightly/latest-mozilla-central-l10n',
 
     # l10n
     "hg_l10n_base": "https://hg.mozilla.org/l10n-central",
 
     # mar
-    "mar_tools_url": "http://ftp.mozilla.org/pub/mozilla.org/firefox/nightly/latest-mozilla-central/mar-tools/%(platform)s",
+    "mar_tools_url": os.environ.get(
+        "MAR_TOOLS_URL",
+        # Buildbot l10n fetches from ftp rather than setting an environ var
+        "http://ftp.mozilla.org/pub/mozilla.org/firefox/nightly/latest-mozilla-central/mar-tools/%(platform)s"
+    ),
 
     # repositories
     "mozilla_dir": "alder",
     "repos": [{
         "vcs": "hg",
         "repo": "https://hg.mozilla.org/build/tools",
         "branch": "default",
         "dest": "tools",
--- a/testing/mozharness/configs/single_locale/ash.py
+++ b/testing/mozharness/configs/single_locale/ash.py
@@ -11,28 +11,34 @@
 #
 # If you really want to have localized ash builds, use the use the following
 # values:
 # * "en_us_binary_url": "http://ftp.mozilla.org/pub/mozilla.org/firefox/tinderbox-builds/ash-%(platform)s/latest/",
 # * "mar_tools_url": "http://ftp.mozilla.org/pub/mozilla.org/firefox/tinderbox-builds/ash-%(platform)s/latest/",
 # * "repo": "https://hg.mozilla.org/projects/ash",
 #
 
+import os
+
 config = {
     "nightly_build": True,
     "branch": "ash",
     "en_us_binary_url": "http://ftp.mozilla.org/pub/mozilla.org/firefox/nightly/latest-mozilla-central/",
     "update_channel": "nightly",
     "latest_mar_dir": '/pub/mozilla.org/firefox/nightly/latest-mozilla-central-l10n',
 
     # l10n
     "hg_l10n_base": "https://hg.mozilla.org/l10n-central",
 
     # mar
-    "mar_tools_url": "http://ftp.mozilla.org/pub/mozilla.org/firefox/nightly/latest-mozilla-central/mar-tools/%(platform)s",
+    "mar_tools_url": os.environ.get(
+        "MAR_TOOLS_URL",
+        # Buildbot l10n fetches from ftp rather than setting an environ var
+        "http://ftp.mozilla.org/pub/mozilla.org/firefox/nightly/latest-mozilla-central/mar-tools/%(platform)s"
+    ),
 
     # repositories
     "mozilla_dir": "ash",
     "repos": [{
         "vcs": "hg",
         "repo": "https://hg.mozilla.org/build/tools",
         "branch": "default",
         "dest": "tools",
--- a/testing/mozharness/configs/single_locale/date.py
+++ b/testing/mozharness/configs/single_locale/date.py
@@ -1,20 +1,26 @@
+import os
+
 config = {
     "nightly_build": True,
     "branch": "date",
     "en_us_binary_url": "http://archive.mozilla.org/pub/firefox/nightly/latest-mozilla-central",
     "update_channel": "nightly-date",
     "latest_mar_dir": '/pub/firefox/nightly/latest-date-l10n',
 
     # l10n
     "hg_l10n_base": "https://hg.mozilla.org/l10n-central",
 
     # mar
-    "mar_tools_url": "http://ftp.mozilla.org/pub/mozilla.org/firefox/nightly/latest-mozilla-central/mar-tools/%(platform)s",
+    "mar_tools_url": os.environ.get(
+        "MAR_TOOLS_URL",
+        # Buildbot l10n fetches from ftp rather than setting an environ var
+        "http://ftp.mozilla.org/pub/mozilla.org/firefox/nightly/latest-mozilla-central/mar-tools/%(platform)s"
+    ),
 
     # repositories
     "mozilla_dir": "date",
     "repos": [{
         "vcs": "hg",
         "repo": "https://hg.mozilla.org/build/tools",
         "branch": "default",
         "dest": "tools",
--- a/testing/mozharness/configs/single_locale/mozilla-aurora.py
+++ b/testing/mozharness/configs/single_locale/mozilla-aurora.py
@@ -1,20 +1,26 @@
+import os
+
 config = {
     "nightly_build": True,
     "branch": "mozilla-aurora",
     "en_us_binary_url": "http://ftp.mozilla.org/pub/mozilla.org/firefox/nightly/latest-mozilla-aurora/",
     "update_channel": "aurora",
     "latest_mar_dir": '/pub/mozilla.org/firefox/nightly/latest-mozilla-aurora-l10n',
 
     # l10n
     "hg_l10n_base": "https://hg.mozilla.org/releases/l10n/mozilla-aurora",
 
     # mar
-    "mar_tools_url": "http://ftp.mozilla.org/pub/mozilla.org/firefox/nightly/latest-mozilla-aurora/mar-tools/%(platform)s",
+    "mar_tools_url": os.environ.get(
+        "MAR_TOOLS_URL",
+        # Buildbot l10n fetches from ftp rather than setting an environ var
+        "http://ftp.mozilla.org/pub/mozilla.org/firefox/nightly/latest-mozilla-aurora/mar-tools/%(platform)s"
+    ),
 
     # repositories
     "mozilla_dir": "mozilla-aurora",
     "repos": [{
         "vcs": "hg",
         "repo": "https://hg.mozilla.org/build/tools",
         "branch": "default",
         "dest": "tools",
--- a/testing/mozharness/configs/single_locale/mozilla-central.py
+++ b/testing/mozharness/configs/single_locale/mozilla-central.py
@@ -1,20 +1,26 @@
+import os
+
 config = {
     "nightly_build": True,
     "branch": "mozilla-central",
     "en_us_binary_url": "http://archive.mozilla.org/pub/firefox/nightly/latest-mozilla-central",
     "update_channel": "nightly",
     "latest_mar_dir": '/pub/firefox/nightly/latest-mozilla-central-l10n',
 
     # l10n
     "hg_l10n_base": "https://hg.mozilla.org/l10n-central",
 
     # mar
-    "mar_tools_url": "http://ftp.mozilla.org/pub/mozilla.org/firefox/nightly/latest-mozilla-central/mar-tools/%(platform)s",
+    "mar_tools_url": os.environ.get(
+        "MAR_TOOLS_URL",
+        # Buildbot l10n fetches from ftp rather than setting an environ var
+        "http://ftp.mozilla.org/pub/mozilla.org/firefox/nightly/latest-mozilla-central/mar-tools/%(platform)s"
+    ),
 
     # repositories
     "mozilla_dir": "mozilla-central",
     "repos": [{
         "vcs": "hg",
         "repo": "https://hg.mozilla.org/build/tools",
         "branch": "default",
         "dest": "tools",
--- a/testing/mozharness/configs/single_locale/try.py
+++ b/testing/mozharness/configs/single_locale/try.py
@@ -1,21 +1,27 @@
+import os
+
 config = {
     "nightly_build": False,
     "branch": "try",
     "en_us_binary_url": "http://archive.mozilla.org/pub/firefox/nightly/latest-mozilla-central",
     "update_channel": "nightly",
     "latest_mar_dir": '/pub/firefox/nightly/latest-mozilla-central-l10n',
     "update_gecko_source_to_enUS": False,
 
     # l10n
     "hg_l10n_base": "https://hg.mozilla.org/l10n-central",
 
     # mar
-    "mar_tools_url": "http://ftp.mozilla.org/pub/mozilla.org/firefox/nightly/latest-mozilla-central/mar-tools/%(platform)s",
+    "mar_tools_url": os.environ.get(
+        "MAR_TOOLS_URL",
+        # Buildbot l10n fetches from ftp rather than setting an environ var
+        "http://ftp.mozilla.org/pub/mozilla.org/firefox/nightly/latest-mozilla-central/mar-tools/%(platform)s"
+    ),
 
     # repositories
     "mozilla_dir": "try",
     "repos": [{
         "vcs": "hg",
         "repo": "https://hg.mozilla.org/build/tools",
         "branch": "default",
         "dest": "tools",
--- a/testing/tools/websocketprocessbridge/websocketprocessbridge.py
+++ b/testing/tools/websocketprocessbridge/websocketprocessbridge.py
@@ -3,16 +3,17 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 from twisted.internet import protocol, reactor
 from twisted.internet.task import LoopingCall
 import txws
 import psutil
 
+import argparse
 import sys
 import os
 
 # maps a command issued via websocket to running an executable with args
 commands = {
     'iceserver' : [sys.executable,
                    "-u",
                    os.path.join("iceserver", "iceserver.py")]
@@ -84,17 +85,22 @@ parent_process = psutil.Process(os.getpi
 
 def check_parent():
     """ Checks if parent process is still alive, and exits if not """
     if not parent_process.is_running():
         print("websocket/process bridge exiting because parent process is gone")
         reactor.stop()
 
 if __name__ == "__main__":
+    parser = argparse.ArgumentParser(description='Starts websocket/process bridge.')
+    parser.add_argument("--port", type=str, dest="port", default="8191",
+                    help="Port for websocket/process bridge. Default 8191.")
+    args = parser.parse_args()
+
     parent_checker = LoopingCall(check_parent)
     parent_checker.start(1)
 
     bridgeFactory = ProcessSocketBridgeFactory()
-    reactor.listenTCP(8191, txws.WebSocketFactory(bridgeFactory))
-    print("websocket/process bridge listening on port 8191")
+    reactor.listenTCP(int(args.port), txws.WebSocketFactory(bridgeFactory))
+    print("websocket/process bridge listening on port %s" % args.port)
     reactor.run()
 
 
--- a/toolkit/content/widgets/datetimebox.xml
+++ b/toolkit/content/widgets/datetimebox.xml
@@ -71,16 +71,17 @@
         this.mDaySeparator.textContent = this.mSeparatorText;
         this.mYearSeparator =
           document.getAnonymousElementByAttribute(this, "anonid", "sep-second");
         this.mYearSeparator.textContent = this.mSeparatorText;
 
         if (this.mInputElement.value) {
           this.setFieldsFromInputValue();
         }
+        this.updateResetButtonVisibility();
       ]]>
       </constructor>
 
       <method name="clearInputFields">
         <parameter name="aFromInputElement"/>
         <body>
         <![CDATA[
           this.log("clearInputFields");
@@ -105,16 +106,18 @@
               !this.mYearField.readOnly) {
             this.mYearField.value = "";
             this.mYearField.setAttribute("typeBuffer", "");
           }
 
           if (!aFromInputElement) {
             this.mInputElement.setUserInput("");
           }
+
+          this.updateResetButtonVisibility();
         ]]>
         </body>
       </method>
 
       <method name="setFieldsFromInputValue">
         <body>
         <![CDATA[
           let value = this.mInputElement.value;
@@ -165,16 +168,21 @@
             return false;
           ]]>
         </body>
       </method>
 
       <method name="setInputValueFromFields">
         <body>
         <![CDATA[
+          if (!this.isAnyValueAvailable(false)) {
+            this.mInputElement.setUserInput("");
+            return;
+          }
+
           if (this.isFieldInvalid(this.mYearField) ||
               this.isFieldInvalid(this.mMonthField) ||
               this.isFieldInvalid(this.mDayField)) {
             // We still need to notify picker in case any of the field has
             // changed. If we can set input element value, then notifyPicker
             // will be called in setFieldsFromInputValue().
             this.notifyPicker();
             return;
@@ -401,21 +409,23 @@
 
             if (value.toString().length > this.mYearLength &&
                 value.toString().length <= this.mMaxYear.toString().length) {
               this.mYearField.size = value.toString().length;
             }
           }
 
           aField.value = value;
+          this.updateResetButtonVisibility();
         ]]>
         </body>
       </method>
 
-      <method name="isValueAvailable">
+      <method name="isAnyValueAvailable">
+        <parameter name="aForPicker"/>
         <body>
         <![CDATA[
           return !this.isEmpty(this.mMonthField.value) ||
                  !this.isEmpty(this.mDayField.value) ||
                  !this.isEmpty(this.mYearField.value);
         ]]>
         </body>
       </method>
@@ -487,16 +497,17 @@
         this.mSecondSeparator = null;
         this.mSecondField = null;
         this.mMillisecSeparator = null;
         this.mMillisecField = null;
 
         if (this.mInputElement.value) {
           this.setFieldsFromInputValue();
         }
+        this.updateResetButtonVisibility();
         ]]>
       </constructor>
 
       <method name="insertSeparator">
         <parameter name="aSeparatorText"/>
         <body>
         <![CDATA[
           let container = this.mHourField.parentNode;
@@ -612,16 +623,21 @@
           this.notifyPicker();
         ]]>
         </body>
       </method>
 
       <method name="setInputValueFromFields">
         <body>
         <![CDATA[
+          if (!this.isAnyValueAvailable(false)) {
+            this.mInputElement.setUserInput("");
+            return;
+          }
+
           if (this.isEmpty(this.mHourField.value) ||
               this.isEmpty(this.mMinuteField.value) ||
               (this.mDayPeriodField && this.isEmpty(this.mDayPeriodField.value)) ||
               (this.mSecondField && this.isEmpty(this.mSecondField.value)) ||
               (this.mMillisecField && this.isEmpty(this.mMillisecField.value))) {
             // We still need to notify picker in case any of the field has
             // changed. If we can set input element value, then notifyPicker
             // will be called in setFieldsFromInputValue().
@@ -719,16 +735,18 @@
           if (this.mDayPeriodField && !this.mDayPeriodField.disabled &&
               !this.mDayPeriodField.readOnly) {
             this.mDayPeriodField.value = "";
           }
 
           if (!aFromInputElement) {
             this.mInputElement.setUserInput("");
           }
+
+          this.updateResetButtonVisibility();
         ]]>
         </body>
       </method>
 
       <method name="incrementFieldValue">
         <parameter name="aTargetField"/>
         <parameter name="aTimes"/>
         <body>
@@ -788,16 +806,17 @@
             if (key == "Home" || key == "End") {
               return;
             }
 
             this.mDayPeriodField.value =
               this.mDayPeriodField.value == this.mAMIndicator ?
                 this.mPMIndicator : this.mAMIndicator;
             this.mDayPeriodField.select();
+            this.updateResetButtonVisibility();
             this.setInputValueFromFields();
             return;
           }
 
           switch (key) {
             case "ArrowUp":
               this.incrementFieldValue(targetField, 1);
               break;
@@ -845,16 +864,17 @@
               targetField == this.mDayPeriodField) {
             if (key == "a" || key == "A") {
               this.mDayPeriodField.value = this.mAMIndicator;
               this.mDayPeriodField.select();
             } else if (key == "p" || key == "P") {
               this.mDayPeriodField.value = this.mPMIndicator;
               this.mDayPeriodField.select();
             }
+            this.updateResetButtonVisibility();
             return;
           }
 
           if (targetField.classList.contains("numeric") && key.match(/[0-9]/)) {
             let buffer = targetField.getAttribute("typeBuffer") || "";
 
             buffer = buffer.concat(key);
             this.setFieldValue(targetField, buffer);
@@ -900,26 +920,40 @@
             if (value < 10) {
               value = "00" + value;
             } else if (value < 100) {
               value = "0" + value;
             }
           }
 
           aField.value = value;
+          this.updateResetButtonVisibility();
         ]]>
         </body>
       </method>
 
-      <method name="isValueAvailable">
+      <method name="isAnyValueAvailable">
+        <parameter name="aForPicker"/>
         <body>
         <![CDATA[
+          let available = !this.isEmpty(this.mHourField.value) ||
+                          !this.isEmpty(this.mMinuteField.value);
+
+          if (available) {
+            return true;
+          }
+
           // Picker only cares about hour:minute.
-          return !this.isEmpty(this.mHourField.value) ||
-                 !this.isEmpty(this.mMinuteField.value);
+          if (aForPicker) {
+            return false;
+          }
+
+          return (this.mDayPeriodField && !this.isEmpty(this.mDayPeriodField.value)) ||
+                 (this.mSecondField && !this.isEmpty(this.mSecondField.value)) ||
+                 (this.mMillisecField && !this.isEmpty(this.mMillisecField.value));
         ]]>
         </body>
       </method>
 
       <method name="getCurrentValue">
         <body>
         <![CDATA[
           let hour;
@@ -975,33 +1009,35 @@
                       xbl:inherits="disabled,readonly,tabindex"/>
           <html:span anonid="sep-second" class="datetime-separator"></html:span>
           <html:input anonid="input-three"
                       class="textbox-input datetime-input numeric"
                       size="2" maxlength="2"
                       xbl:inherits="disabled,readonly,tabindex"/>
         </html:span>
 
-        <html:button class="datetime-reset-button" anoid="reset-button"
-                     tabindex="-1" xbl:inherits="disabled"
-                     onclick="document.getBindingParent(this).clearInputFields(false);"/>
+        <html:button class="datetime-reset-button" anonid="reset-button"
+                     tabindex="-1" xbl:inherits="disabled"/>
       </html:div>
     </content>
 
     <implementation implements="nsIDateTimeInputArea">
       <constructor>
       <![CDATA[
         this.DEBUG = false;
         this.mInputElement = this.parentNode;
 
         this.mMin = this.mInputElement.min;
         this.mMax = this.mInputElement.max;
         this.mStep = this.mInputElement.step;
         this.mIsPickerOpen = false;
 
+        this.mResetButton =
+          document.getAnonymousElementByAttribute(this, "anonid", "reset-button");
+
         this.EVENTS.forEach((eventName) => {
           this.addEventListener(eventName, this, { mozSystemGroup: true });
         });
         // Handle keypress separately since we need to catch it on capturing.
         this.addEventListener("keypress", this, {
           capture: true,
           mozSystemGroup: true
         });
@@ -1036,16 +1072,28 @@
         <![CDATA[
           if (this.DEBUG) {
             dump("[DateTimeBox] " + aMsg + "\n");
           }
         ]]>
         </body>
       </method>
 
+      <method name="updateResetButtonVisibility">
+        <body>
+          <![CDATA[
+            if (this.isAnyValueAvailable(false)) {
+              this.mResetButton.style.visibility = "visible";
+            } else {
+              this.mResetButton.style.visibility = "hidden";
+            }
+          ]]>
+        </body>
+      </method>
+
       <method name="focusInnerTextBox">
         <body>
         <![CDATA[
           this.log("focusInnerTextBox");
           document.getAnonymousElementByAttribute(this, "anonid", "input-one").focus();
         ]]>
         </body>
       </method>
@@ -1159,20 +1207,26 @@
       </method>
 
       <method name="getCurrentValue">
         <body>
           throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
         </body>
       </method>
 
+      <method name="isAnyValueAvailable">
+        <body>
+          throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
+        </body>
+      </method>
+
       <method name="notifyPicker">
         <body>
         <![CDATA[
-          if (this.mIsPickerOpen && this.isValueAvailable()) {
+          if (this.mIsPickerOpen && this.isAnyValueAvailable(true)) {
             this.mInputElement.updateDateTimePicker(this.getCurrentValue());
           }
         ]]>
         </body>
       </method>
 
       <method name="isDisabled">
         <body>
@@ -1265,17 +1319,21 @@
             case "Enter":
             case " ": {
               this.mInputElement.closeDateTimePicker();
               aEvent.preventDefault();
               break;
             }
             case "Backspace": {
               let targetField = aEvent.originalTarget;
+              targetField.value = "";
               targetField.setAttribute("typeBuffer", "");
+              this.updateResetButtonVisibility();
+              this.setInputValueFromFields();
+              aEvent.preventDefault();
               break;
             }
             case "ArrowRight":
             case "ArrowLeft": {
               this.advanceToNextField(aEvent.key == "ArrowRight" ? false : true);
               aEvent.preventDefault();
               break;
             }
@@ -1307,22 +1365,24 @@
         <parameter name="aEvent"/>
         <body>
         <![CDATA[
           this.log("onClick originalTarget: " + aEvent.originalTarget);
 
           // XXX: .originalTarget is not expected.
           // When clicking on one of the inner text boxes, the .originalTarget is
           // a HTMLDivElement and when clicking on the reset button, it's a
-          // HTMLButtonElement but it's not equal to our reset-button.
+          // HTMLButtonElement.
           if (aEvent.defaultPrevented || this.isDisabled() || this.isReadonly()) {
             return;
           }
 
-          if (!(aEvent.originalTarget instanceof HTMLButtonElement)) {
+          if (aEvent.originalTarget == this.mResetButton) {
+            this.clearInputFields(false);
+          } else {
             this.mInputElement.openDateTimePicker(this.getCurrentValue());
           }
         ]]>
         </body>
       </method>
     </implementation>
   </binding>
 
new file mode 100644
--- /dev/null
+++ b/toolkit/library/dummydll/dummydll.cpp
@@ -0,0 +1,17 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <windows.h>
+
+BOOL WINAPI DllMain(
+  HANDLE hModule,
+  DWORD dwReason,
+  LPVOID lpvReserved
+)
+{
+  if (dwReason == DLL_PROCESS_ATTACH) {
+    ::DisableThreadLibraryCalls((HMODULE)hModule);
+  }
+  return TRUE;
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/library/dummydll/moz.build
@@ -0,0 +1,19 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+# Bug 1294650 - populate our install with a shim dll to work around a
+# 3rd party code injection crash.
+
+SOURCES += [
+    'dummydll.cpp',
+]
+
+if CONFIG['CPU_ARCH'] == 'x86_64':
+  GeckoSharedLibrary('qipcap64')
+else:
+  GeckoSharedLibrary('qipcap')
+
+NO_VISIBILITY_FLAGS = True
--- a/toolkit/library/moz.build
+++ b/toolkit/library/moz.build
@@ -104,16 +104,19 @@ LOCAL_INCLUDES += [
 
 if CONFIG['OS_ARCH'] == 'WINNT' and not CONFIG['GNU_CC']:
     LOCAL_INCLUDES += [
         '/xpcom/base',
     ]
 
 DIRS += ['gtest']
 
+if CONFIG['OS_ARCH'] == 'WINNT':
+  DIRS += ['dummydll']
+
 # js needs to come after xul for now, because it is an archive and its content
 # is discarded when it comes first.
 USE_LIBS += [
     'js',
 ]
 
 USE_LIBS += [
     'gkmedias',
--- a/toolkit/mozapps/extensions/test/browser/browser-common.ini
+++ b/toolkit/mozapps/extensions/test/browser/browser-common.ini
@@ -60,9 +60,8 @@ skip-if = os == 'win' # Disabled on Wind
 [browser_inlinesettings_info.js]
 [browser_tabsettings.js]
 [browser_pluginprefs.js]
 skip-if = buildapp == 'mulet'
 [browser_CTP_plugins.js]
 skip-if = buildapp == 'mulet'
 [browser_webext_options.js]
 tags = webextensions
-skip-if = e10s # Bug 1315042
--- a/tools/profiler/core/GeckoSampler.cpp
+++ b/tools/profiler/core/GeckoSampler.cpp
@@ -207,17 +207,17 @@ GeckoSampler::GeckoSampler(double aInter
   for (uint32_t i = 0; i < aFeatureCount; ++i) {
     mFeatures[i] = aFeatures[i];
   }
 
   bool ignore;
   sStartTime = mozilla::TimeStamp::ProcessCreation(ignore);
 
   {
-    ::MutexAutoLock lock(*sRegisteredThreadsMutex);
+    MutexAutoLock lock(*sRegisteredThreadsMutex);
 
     // Create ThreadProfile for each registered thread
     for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
       ThreadInfo* info = sRegisteredThreads->at(i);
 
       RegisterThread(info);
     }
 
@@ -237,17 +237,17 @@ GeckoSampler::~GeckoSampler()
 {
   if (IsActive())
     Stop();
 
   SetActiveSampler(nullptr);
 
   // Destroy ThreadProfile for all threads
   {
-    ::MutexAutoLock lock(*sRegisteredThreadsMutex);
+    MutexAutoLock lock(*sRegisteredThreadsMutex);
 
     for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
       ThreadInfo* info = sRegisteredThreads->at(i);
       // We've stopped profiling. We no longer need to retain
       // information for an old thread.
       if (info->IsPendingDelete()) {
         delete info;
         sRegisteredThreads->erase(sRegisteredThreads->begin() + i);
@@ -290,17 +290,17 @@ void GeckoSampler::StreamTaskTracer(Spli
   aWriter.StartArrayProperty("data");
     UniquePtr<nsTArray<nsCString>> data = mozilla::tasktracer::GetLoggedData(sStartTime);
     for (uint32_t i = 0; i < data->Length(); ++i) {
       aWriter.StringElement((data->ElementAt(i)).get());
     }
   aWriter.EndArray();
 
   aWriter.StartArrayProperty("threads");
-    ::MutexAutoLock lock(*sRegisteredThreadsMutex);
+    MutexAutoLock lock(*sRegisteredThreadsMutex);
     for (size_t i = 0; i < sRegisteredThreads->size(); i++) {
       // Thread meta data
       ThreadInfo* info = sRegisteredThreads->at(i);
       aWriter.StartObjectElement();
         if (XRE_GetProcessType() == GeckoProcessType_Plugin) {
           // TODO Add the proper plugin name
           aWriter.StringProperty("name", "Plugin");
         } else {
@@ -522,27 +522,27 @@ void GeckoSampler::StreamJSON(Spliceable
     }
 
     // Lists the samples for each ThreadProfile
     aWriter.StartArrayProperty("threads");
     {
       SetPaused(true);
 
       {
-        ::MutexAutoLock lock(*sRegisteredThreadsMutex);
+        MutexAutoLock lock(*sRegisteredThreadsMutex);
 
         for (size_t i = 0; i < sRegisteredThreads->size(); i++) {
           // Thread not being profiled, skip it
           if (!sRegisteredThreads->at(i)->Profile())
             continue;
 
           // Note that we intentionally include ThreadProfile which
           // have been marked for pending delete.
 
-          ::MutexAutoLock lock(sRegisteredThreads->at(i)->Profile()->GetMutex());
+          MutexAutoLock lock(sRegisteredThreads->at(i)->Profile()->GetMutex());
 
           sRegisteredThreads->at(i)->Profile()->StreamJSON(aWriter, aSinceTime);
         }
       }
 
       if (Sampler::CanNotifyObservers()) {
         // Send a event asking any subprocesses (plugins) to
         // give us their information
@@ -575,31 +575,31 @@ void GeckoSampler::StreamJSON(Spliceable
   aWriter.End();
 }
 
 void GeckoSampler::FlushOnJSShutdown(JSContext* aContext)
 {
   SetPaused(true);
 
   {
-    ::MutexAutoLock lock(*sRegisteredThreadsMutex);
+    MutexAutoLock lock(*sRegisteredThreadsMutex);
 
     for (size_t i = 0; i < sRegisteredThreads->size(); i++) {
       // Thread not being profiled, skip it.
       if (!sRegisteredThreads->at(i)->Profile() ||
           sRegisteredThreads->at(i)->IsPendingDelete()) {
         continue;
       }
 
       // Thread not profiling the context that's going away, skip it.
       if (sRegisteredThreads->at(i)->Profile()->GetPseudoStack()->mContext != aContext) {
         continue;
       }
 
-      ::MutexAutoLock lock(sRegisteredThreads->at(i)->Profile()->GetMutex());
+      MutexAutoLock lock(sRegisteredThreads->at(i)->Profile()->GetMutex());
       sRegisteredThreads->at(i)->Profile()->FlushSamplesAndMarkers();
     }
   }
 
   SetPaused(false);
 }
 
 void PseudoStack::flushSamplerOnJSShutdown()
--- a/tools/profiler/core/ProfilerBacktrace.cpp
+++ b/tools/profiler/core/ProfilerBacktrace.cpp
@@ -23,11 +23,11 @@ ProfilerBacktrace::~ProfilerBacktrace()
     delete mProfile;
   }
 }
 
 void
 ProfilerBacktrace::StreamJSON(SpliceableJSONWriter& aWriter,
                               UniqueStacks& aUniqueStacks)
 {
-  ::MutexAutoLock lock(mProfile->GetMutex());
+  MutexAutoLock lock(mProfile->GetMutex());
   mProfile->StreamJSON(aWriter, aUniqueStacks);
 }
--- a/tools/profiler/core/SyncProfile.cpp
+++ b/tools/profiler/core/SyncProfile.cpp
@@ -20,17 +20,17 @@ SyncProfile::~SyncProfile()
   // SyncProfile owns the ThreadInfo; see NewSyncProfile.
   ThreadInfo* info = GetThreadInfo();
   delete info;
 }
 
 bool
 SyncProfile::ShouldDestroy()
 {
-  ::MutexAutoLock lock(GetMutex());
+  MutexAutoLock lock(GetMutex());
   if (mOwnerState == OWNED) {
     mOwnerState = OWNER_DESTROYING;
     return true;
   }
   mOwnerState = ORPHANED;
   return false;
 }
 
--- a/tools/profiler/core/ThreadProfile.cpp
+++ b/tools/profiler/core/ThreadProfile.cpp
@@ -3,17 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 ThreadProfile::ThreadProfile(ThreadInfo* aInfo, ProfileBuffer* aBuffer)
   : mThreadInfo(aInfo)
   , mBuffer(aBuffer)
   , mPseudoStack(aInfo->Stack())
-  , mMutex(OS::CreateMutex("ThreadProfile::mMutex"))
+  , mMutex(MakeUnique<Mutex>("ThreadProfile::mMutex"))
   , mThreadId(int(aInfo->ThreadId()))
   , mIsMainThread(aInfo->IsMainThread())
   , mPlatformData(aInfo->GetPlatformData())
   , mStackTop(aInfo->StackTop())
   , mRespInfo(this)
 #ifdef XP_LINUX
   , mRssMemory(0)
   , mUssMemory(0)
--- a/tools/profiler/core/ThreadProfile.h
+++ b/tools/profiler/core/ThreadProfile.h
@@ -19,17 +19,17 @@ public:
 
   /**
    * Track a marker which has been inserted into the ThreadProfile.
    * This marker can safely be deleted once the generation has
    * expired.
    */
   void addStoredMarker(ProfilerMarker *aStoredMarker);
   PseudoStack* GetPseudoStack();
-  ::Mutex& GetMutex();
+  mozilla::Mutex& GetMutex();
   void StreamJSON(SpliceableJSONWriter& aWriter, double aSinceTime = 0);
 
   /**
    * Call this method when the JS entries inside the buffer are about to
    * become invalid, i.e., just before JS shutdown.
    */
   void FlushSamplesAndMarkers();
 
@@ -78,17 +78,17 @@ private:
   // stringifying JIT frames). In the case of JSRuntime destruction,
   // FlushSamplesAndMarkers should be called to save them. These are spliced
   // into the final stream.
   mozilla::UniquePtr<char[]> mSavedStreamedSamples;
   mozilla::UniquePtr<char[]> mSavedStreamedMarkers;
   mozilla::Maybe<UniqueStacks> mUniqueStacks;
 
   PseudoStack*   mPseudoStack;
-  mozilla::UniquePtr<Mutex>  mMutex;
+  mozilla::UniquePtr<mozilla::Mutex> mMutex;
   int            mThreadId;
   bool           mIsMainThread;
   PlatformData*  mPlatformData;  // Platform specific data.
   void* const    mStackTop;
   ThreadResponsiveness mRespInfo;
 
   // Only Linux is using a signal sender, instead of stopping the thread, so we
   // need some space to store the data which cannot be collected in the signal
--- a/tools/profiler/core/platform-linux.cc
+++ b/tools/profiler/core/platform-linux.cc
@@ -311,17 +311,17 @@ static void* SignalSender(void* arg) {
   TimeDuration lastSleepOverhead = 0;
   TimeStamp sampleStart = TimeStamp::Now();
   while (SamplerRegistry::sampler->IsActive()) {
 
     SamplerRegistry::sampler->HandleSaveRequest();
     SamplerRegistry::sampler->DeleteExpiredMarkers();
 
     if (!SamplerRegistry::sampler->IsPaused()) {
-      ::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
+      MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
       const std::vector<ThreadInfo*>& threads =
         SamplerRegistry::sampler->GetRegisteredThreads();
 
       bool isFirstProfiledThread = true;
       for (uint32_t i = 0; i < threads.size(); i++) {
         ThreadInfo* info = threads[i];
 
         // This will be null if we're not interested in profiling this thread.
--- a/tools/profiler/core/platform-macos.cc
+++ b/tools/profiler/core/platform-macos.cc
@@ -203,17 +203,17 @@ class SamplerThread : public Thread {
 
   // Implement Thread::Run().
   virtual void Run() {
     TimeDuration lastSleepOverhead = 0;
     TimeStamp sampleStart = TimeStamp::Now();
     while (SamplerRegistry::sampler->IsActive()) {
       SamplerRegistry::sampler->DeleteExpiredMarkers();
       if (!SamplerRegistry::sampler->IsPaused()) {
-        ::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
+        MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
         const std::vector<ThreadInfo*>& threads =
           SamplerRegistry::sampler->GetRegisteredThreads();
         bool isFirstProfiledThread = true;
         for (uint32_t i = 0; i < threads.size(); i++) {
           ThreadInfo* info = threads[i];
 
           // This will be null if we're not interested in profiling this thread.
           if (!info->Profile() || info->IsPendingDelete())
--- a/tools/profiler/core/platform-win32.cc
+++ b/tools/profiler/core/platform-win32.cc
@@ -121,17 +121,17 @@ class SamplerThread : public Thread {
     // adjust the resolution to match.
     if (interval_ < 10)
         ::timeBeginPeriod(interval_);
 
     while (sampler_->IsActive()) {
       sampler_->DeleteExpiredMarkers();
 
       if (!sampler_->IsPaused()) {
-        ::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
+        mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
         const std::vector<ThreadInfo*>& threads =
           sampler_->GetRegisteredThreads();
         bool isFirstProfiledThread = true;
         for (uint32_t i = 0; i < threads.size(); i++) {
           ThreadInfo* info = threads[i];
 
           // This will be null if we're not interested in profiling this thread.
           if (!info->Profile() || info->IsPendingDelete())
--- a/tools/profiler/core/platform.cpp
+++ b/tools/profiler/core/platform.cpp
@@ -109,17 +109,17 @@ static mozilla::StaticAutoPtr<mozilla::P
                                                             sInterposeObserver;
 
 // The name that identifies the gecko thread for calls to
 // profiler_register_thread.
 static const char * gGeckoThreadName = "GeckoMain";
 
 void Sampler::Startup() {
   sRegisteredThreads = new std::vector<ThreadInfo*>();
-  sRegisteredThreadsMutex = OS::CreateMutex("sRegisteredThreads mutex");
+  sRegisteredThreadsMutex = MakeUnique<Mutex>("sRegisteredThreadsMutex");
 
   // We could create the sLUL object and read unwind info into it at
   // this point.  That would match the lifetime implied by destruction
   // of it in Sampler::Shutdown just below.  However, that gives a big
   // delay on startup, even if no profiling is actually to be done.
   // So, instead, sLUL is created on demand at the first call to
   // Sampler::Start.
 }
@@ -150,17 +150,17 @@ void Sampler::Shutdown() {
 bool
 Sampler::RegisterCurrentThread(const char* aName,
                                PseudoStack* aPseudoStack,
                                bool aIsMainThread, void* stackTop)
 {
   if (!sRegisteredThreadsMutex)
     return false;
 
-  ::MutexAutoLock lock(*sRegisteredThreadsMutex);
+  MutexAutoLock lock(*sRegisteredThreadsMutex);
 
   Thread::tid_t id = Thread::GetCurrentId();
 
   for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
     ThreadInfo* info = sRegisteredThreads->at(i);
     if (info->ThreadId() == id && !info->IsPendingDelete()) {
       // Thread already registered. This means the first unregister will be
       // too early.
@@ -182,17 +182,17 @@ Sampler::RegisterCurrentThread(const cha
 }
 
 void
 Sampler::UnregisterCurrentThread()
 {
   if (!sRegisteredThreadsMutex)
     return;
 
-  ::MutexAutoLock lock(*sRegisteredThreadsMutex);
+  MutexAutoLock lock(*sRegisteredThreadsMutex);
 
   Thread::tid_t id = Thread::GetCurrentId();
 
   for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
     ThreadInfo* info = sRegisteredThreads->at(i);
     if (info->ThreadId() == id && !info->IsPendingDelete()) {
       if (profiler_is_active()) {
         // We still want to show the results of this thread if you
@@ -828,17 +828,17 @@ profiler_start(int aProfileEntries, doub
   t = new GeckoSampler(aInterval ? aInterval : PROFILE_DEFAULT_INTERVAL,
                       aProfileEntries ? aProfileEntries : PROFILE_DEFAULT_ENTRY,
                       aFeatures, aFeatureCount,
                       aThreadNameFilters, aFilterCount);
 
   tlsTicker.set(t);
   t->Start();
   if (t->ProfileJS() || t->InPrivacyMode()) {
-    ::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
+    MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
     const std::vector<ThreadInfo*>& threads = t->GetRegisteredThreads();
 
     for (uint32_t i = 0; i < threads.size(); i++) {
       ThreadInfo* info = threads[i];
       if (info->IsPendingDelete()) {
         continue;
       }
       ThreadProfile* thread_profile = info->Profile();
@@ -1278,38 +1278,10 @@ profiler_add_marker(const char *aMarker,
   }
 
   mozilla::TimeStamp origin = (aPayload && !aPayload->GetStartTime().IsNull()) ?
                      aPayload->GetStartTime() : mozilla::TimeStamp::Now();
   mozilla::TimeDuration delta = origin - sStartTime;
   stack->addMarker(aMarker, payload.release(), delta.ToMilliseconds());
 }
 
-#include "mozilla/Mutex.h"
-
-class GeckoMutex : public ::Mutex {
- public:
-  explicit GeckoMutex(const char* aDesc) :
-    mMutex(aDesc)
-  {}
-
-  virtual ~GeckoMutex() {}
-
-  virtual int Lock() {
-    mMutex.Lock();
-    return 0;
-  }
-
-  virtual int Unlock() {
-    mMutex.Unlock();
-    return 0;
-  }
-
- private:
-  mozilla::Mutex mMutex;
-};
-
-mozilla::UniquePtr< ::Mutex> OS::CreateMutex(const char* aDesc) {
-  return mozilla::MakeUnique<GeckoMutex>(aDesc);
-}
-
 // END externally visible functions
 ////////////////////////////////////////////////////////////////////////
--- a/tools/profiler/core/platform.h
+++ b/tools/profiler/core/platform.h
@@ -110,81 +110,34 @@ bool profiler_verbose();
 
 typedef int32_t Atomic32;
 
 extern mozilla::TimeStamp sStartTime;
 
 typedef uint8_t* Address;
 
 // ----------------------------------------------------------------------------
-// Mutex
-//
-// Mutexes are used for serializing access to non-reentrant sections of code.
-// The implementations of mutex should allow for nested/recursive locking.
-
-class Mutex {
- public:
-  virtual ~Mutex() {}
-
-  // Locks the given mutex. If the mutex is currently unlocked, it becomes
-  // locked and owned by the calling thread, and immediately. If the mutex
-  // is already locked by another thread, suspends the calling thread until
-  // the mutex is unlocked.
-  virtual int Lock() = 0;
-
-  // Unlocks the given mutex. The mutex is assumed to be locked and owned by
-  // the calling thread on entrance.
-  virtual int Unlock() = 0;
-};
-
-class MutexAutoLock {
- public:
-  explicit MutexAutoLock(::Mutex& aMutex)
-    : mMutex(&aMutex)
-  {
-    mMutex->Lock();
-  }
-
-  ~MutexAutoLock() {
-    mMutex->Unlock();
-  }
-
- private:
-  Mutex* mMutex;
-};
-
-// ----------------------------------------------------------------------------
 // OS
 //
 // This class has static methods for the different platform specific
 // functions. Add methods here to cope with differences between the
 // supported platforms.
 
 class OS {
- public:
-
+public:
   // Sleep for a number of milliseconds.
   static void Sleep(const int milliseconds);
 
   // Sleep for a number of microseconds.
   static void SleepMicro(const int microseconds);
 
   // Called on startup to initialize platform specific things
   static void Startup();
-
-  static mozilla::UniquePtr< ::Mutex> CreateMutex(const char* aDesc);
-
- private:
-  static const int msPerSecond = 1000;
-
 };
 
-
-
-
 // ----------------------------------------------------------------------------
 // Thread
 //
 // Thread objects are used for creating and running threads. When the start()
 // method is called the new thread starts running the run() method in the new
 // thread. The Thread object should not be deallocated before the thread has
 // terminated.
 
@@ -387,17 +340,17 @@ class Sampler {
 
   static void Startup();
   // Should only be called on shutdown
   static void Shutdown();
 
   static GeckoSampler* GetActiveSampler() { return sActiveSampler; }
   static void SetActiveSampler(GeckoSampler* sampler) { sActiveSampler = sampler; }
 
-  static mozilla::UniquePtr<Mutex> sRegisteredThreadsMutex;
+  static mozilla::UniquePtr<mozilla::Mutex> sRegisteredThreadsMutex;
 
   static bool CanNotifyObservers() {
 #ifdef MOZ_WIDGET_GONK
     // We use profile.sh on b2g to manually select threads and options per process.
     return false;
 #elif defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
     // Android ANR reporter uses the profiler off the main thread
     return NS_IsMainThread();
--- a/tools/update-packaging/Makefile.in
+++ b/tools/update-packaging/Makefile.in
@@ -55,25 +55,27 @@ dir-stage := $(call mkdir_deps,$(STAGE_D
 
 complete-patch:: $(dir-stage)
 ifeq ($(OS_TARGET), WINNT)
 	test -f $(UNPACKAGE)
 	$(RM) -rf '$(PACKAGE_DIR)'
 	cd $(PACKAGE_BASE_DIR) && $(INNER_UNMAKE_PACKAGE)
 endif
 	MAR=$(MAR_BIN) \
+	MOZ_PRODUCT_VERSION=$(MOZ_APP_VERSION) \
 	  $(srcdir)/make_full_update.sh \
 	  '$(DIST)/$(COMPLETE_MAR)' \
 	  '$(PACKAGE_DIR)'
 ifdef MOZ_SIGN_CMD
 	$(MOZ_SIGN_CMD) -f mar '$(DIST)/$(COMPLETE_MAR)'
 endif
 
 partial-patch:: $(dir-stage)
 	MAR=$(MAR_BIN) \
 	MBSDIFF=$(MBSDIFF_BIN) \
+	MOZ_PRODUCT_VERSION=$(MOZ_APP_VERSION) \
 	  $(srcdir)/make_incremental_update.sh \
 	  '$(STAGE_DIR)/$(PKG_UPDATE_BASENAME).partial.$(SRC_BUILD_ID)-$(DST_BUILD_ID).mar' \
 	  '$(SRC_BUILD)' \
 	  '$(DST_BUILD)'
 ifdef MOZ_SIGN_CMD
 	$(MOZ_SIGN_CMD) -f mar '$(STAGE_DIR)/$(PKG_UPDATE_BASENAME).partial.$(SRC_BUILD_ID)-$(DST_BUILD_ID).mar'
 endif
--- a/tools/update-packaging/make_full_update.sh
+++ b/tools/update-packaging/make_full_update.sh
@@ -102,17 +102,23 @@ done
 # Append remove instructions for any dead files.
 notice ""
 notice "Adding file and directory remove instructions from file 'removed-files'"
 append_remove_instructions "$targetdir" "$updatemanifestv2" "$updatemanifestv3"
 
 $BZIP2 -z9 "$updatemanifestv2" && mv -f "$updatemanifestv2.bz2" "$updatemanifestv2"
 $BZIP2 -z9 "$updatemanifestv3" && mv -f "$updatemanifestv3.bz2" "$updatemanifestv3"
 
-eval "$MAR -C \"$workdir\" -c output.mar $targetfiles"
+mar_command="$MAR"
+if [[ -n $MOZ_PRODUCT_VERSION ]]
+then
+  mar_command="$mar_command -V $MOZ_PRODUCT_VERSION"
+fi
+mar_command="$mar_command -C \"$workdir\" -c output.mar"
+eval "$mar_command $targetfiles"
 mv -f "$workdir/output.mar" "$archive"
 
 # cleanup
 rm -fr "$workdir"
 
 notice ""
 notice "Finished"
 notice ""
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -2758,22 +2758,29 @@ nsWindow::GeckoViewSupport::FlushIMEChan
     // Only send change notifications if we are *not* masking events,
     // i.e. if we have a focused editor,
     NS_ENSURE_TRUE_VOID(!mIMEMaskEventsCount);
 
     nsCOMPtr<nsISelection> imeSelection;
     nsCOMPtr<nsIContent> imeRoot;
 
     // If we are receiving notifications, we must have selection/root content.
-    MOZ_ALWAYS_SUCCEEDS(IMEStateManager::GetFocusSelectionAndRoot(
-            getter_AddRefs(imeSelection), getter_AddRefs(imeRoot)));
+    nsresult rv = IMEStateManager::GetFocusSelectionAndRoot(
+            getter_AddRefs(imeSelection), getter_AddRefs(imeRoot));
+
+    // With e10s enabled, GetFocusSelectionAndRoot will fail because the IME
+    // content observer is out of process.
+    const bool e10sEnabled = rv == NS_ERROR_NOT_AVAILABLE;
+    if (!e10sEnabled) {
+        MOZ_ALWAYS_SUCCEEDS(rv);
+    }
 
     // Make sure we still have a valid selection/root. We can potentially get
     // a stale selection/root if the editor becomes hidden, for example.
-    NS_ENSURE_TRUE_VOID(imeRoot->IsInComposedDoc());
+    NS_ENSURE_TRUE_VOID(e10sEnabled || imeRoot->IsInComposedDoc());
 
     RefPtr<nsWindow> kungFuDeathGrip(&window);
     window.UserActivity();
 
     struct TextRecord {
         nsString text;
         int32_t start;
         int32_t oldEnd;
@@ -2812,17 +2819,17 @@ nsWindow::GeckoViewSupport::FlushIMEChan
         WidgetQueryContentEvent event(true, eQueryTextContent, &window);
 
         if (change.mNewEnd != change.mStart) {
             window.InitEvent(event, nullptr);
             event.InitForQueryTextContent(change.mStart,
                                           change.mNewEnd - change.mStart);
             window.DispatchEvent(&event);
             NS_ENSURE_TRUE_VOID(event.mSucceeded);
-            NS_ENSURE_TRUE_VOID(event.mReply.mContentsRoot == imeRoot.get());
+            NS_ENSURE_TRUE_VOID(e10sEnabled || event.mReply.mContentsRoot == imeRoot.get());
         }
 
         if (shouldAbort()) {
             return;
         }
 
         textTransaction.AppendElement(
                 TextRecord{event.mReply.mString, change.mStart,
@@ -2833,17 +2840,17 @@ nsWindow::GeckoViewSupport::FlushIMEChan
     int32_t selEnd = -1;
 
     if (mIMESelectionChanged) {
         WidgetQueryContentEvent event(true, eQuerySelectedText, &window);
         window.InitEvent(event, nullptr);
         window.DispatchEvent(&event);
 
         NS_ENSURE_TRUE_VOID(event.mSucceeded);
-        NS_ENSURE_TRUE_VOID(event.mReply.mContentsRoot == imeRoot.get());
+        NS_ENSURE_TRUE_VOID(e10sEnabled || event.mReply.mContentsRoot == imeRoot.get());
 
         if (shouldAbort()) {
             return;
         }
 
         selStart = int32_t(event.GetSelectionStart());
         selEnd = int32_t(event.GetSelectionEnd());
     }
--- a/widget/cocoa/nsChildView.mm
+++ b/widget/cocoa/nsChildView.mm
@@ -5469,20 +5469,33 @@ GetIntegerDeltaForEvent(NSEvent* aEvent)
 #endif // #if !defined(RELEASE_OR_BETA) || defined(DEBUG)
 
   nsAutoRetainCocoaObject kungFuDeathGrip(self);
   bool handled = false;
   if (mGeckoChild && mTextInputHandler) {
     handled = mTextInputHandler->HandleKeyDownEvent(theEvent);
   }
 
-  // We always allow keyboard events to propagate to keyDown: but if they are not
-  // handled we give special Application menu items a chance to act.
+  // We always allow keyboard events to propagate to keyDown: but if they are
+  // not handled we give menu items a chance to act. This allows for handling of
+  // custom shortcuts. Note that existing shortcuts cannot be reassigned yet and
+  // will have been handled by keyDown: before we get here.
+  if (!handled && mGeckoChild) {
+    nsCocoaWindow* widget = mGeckoChild->GetXULWindowWidget();
+    if (widget) {
+      nsMenuBarX* mb = widget->GetMenuBar();
+      if (mb) {
+        // Check if main menu wants to handle the event.
+        handled = mb->PerformKeyEquivalent(theEvent);
+      }
+    }
+  }
   if (!handled && sApplicationMenu) {
-    [sApplicationMenu performKeyEquivalent:theEvent];
+    // Check if application menu wants to handle the event.
+    handled = [sApplicationMenu performKeyEquivalent:theEvent];
   }
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 - (void)keyUp:(NSEvent*)theEvent
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
--- a/widget/cocoa/nsMenuBarX.h
+++ b/widget/cocoa/nsMenuBarX.h
@@ -42,16 +42,17 @@ protected:
   virtual ~nsNativeMenuServiceX() {}
 };
 
 // Objective-C class used to allow us to intervene with keyboard event handling.
 // We allow mouse actions to work normally.
 @interface GeckoNSMenu : NSMenu
 {
 }
+- (BOOL)performSuperKeyEquivalent:(NSEvent*)theEvent;
 @end
 
 // Objective-C class used as action target for menu items
 @interface NativeMenuItemTarget : NSObject
 {
 }
 -(IBAction)menuItemHit:(id)sender;
 @end
@@ -114,16 +115,17 @@ public:
   void              SetSystemHelpMenu();
   nsresult          Paint();
   void              ForceUpdateNativeMenuAt(const nsAString& indexString);
   void              ForceNativeMenuReload(); // used for testing
   static char       GetLocalizedAccelKey(const char *shortcutID);
   static void       ResetNativeApplicationMenu();
   void              SetNeedsRebuild();
   void              ApplicationMenuOpened();
+  bool              PerformKeyEquivalent(NSEvent* theEvent);
 
 protected:
   void              ConstructNativeMenus();
   void              ConstructFallbackNativeMenus();
   nsresult          InsertMenuAtIndex(nsMenuX* aMenu, uint32_t aIndex);
   void              RemoveMenuAtIndex(uint32_t aIndex);
   void              HideItem(nsIDOMDocument* inDoc, const nsAString & inID, nsIContent** outHiddenNode);
   void              AquifyMenuBar();
--- a/widget/cocoa/nsMenuBarX.mm
+++ b/widget/cocoa/nsMenuBarX.mm
@@ -522,16 +522,21 @@ void nsMenuBarX::ApplicationMenuOpened()
     if (!mMenuArray.IsEmpty()) {
       ResetNativeApplicationMenu();
       CreateApplicationMenu(mMenuArray[0].get());
     }
     mNeedsRebuild = false;
   }
 }
 
+bool nsMenuBarX::PerformKeyEquivalent(NSEvent* theEvent)
+{
+  return [mNativeMenu performSuperKeyEquivalent:theEvent];
+}
+
 // Hide the item in the menu by setting the 'hidden' attribute. Returns it in |outHiddenNode| so
 // the caller can hang onto it if they so choose. It is acceptable to pass nsull
 // for |outHiddenNode| if the caller doesn't care about the hidden node.
 void nsMenuBarX::HideItem(nsIDOMDocument* inDoc, const nsAString & inID, nsIContent** outHiddenNode)
 {
   nsCOMPtr<nsIDOMElement> menuItem;
   inDoc->GetElementById(inID, getter_AddRefs(menuItem));
   nsCOMPtr<nsIContent> menuContent(do_QueryInterface(menuItem));
@@ -871,16 +876,21 @@ static BOOL gMenuItemsExecuteCommands = 
   if (![NSApp keyWindow] || [[NSApp keyWindow] firstResponder] != firstResponder) {
     return YES;
   }
 
   // Return NO so that we can handle the event via NSView's "keyDown:".
   return NO;
 }
 
+- (BOOL)performSuperKeyEquivalent:(NSEvent*)theEvent
+{
+  return [super performKeyEquivalent:theEvent];
+}
+
 @end
 
 //
 // Objective-C class used as action target for menu items
 //
 
 @implementation NativeMenuItemTarget
 
--- a/widget/cocoa/nsPrintOptionsX.mm
+++ b/widget/cocoa/nsPrintOptionsX.mm
@@ -45,25 +45,27 @@ nsPrintOptionsX::SerializeToPrintData(ns
   if (aWBP) {
     // When serializing an nsIWebBrowserPrint, we need to pass up the first
     // document name. We could pass up the entire collection of document
     // names, but the OS X printing prompt code only really cares about
     // the first one, so we just send the first to save IPC traffic.
     char16_t** docTitles;
     uint32_t titleCount;
     rv = aWBP->EnumerateDocumentNames(&titleCount, &docTitles);
-    if (NS_SUCCEEDED(rv) && titleCount > 0) {
-      data->printJobName().Assign(docTitles[0]);
-    }
+    if (NS_SUCCEEDED(rv)) {
+      if (titleCount > 0) {
+        data->printJobName().Assign(docTitles[0]);
+      }
 
-    for (int32_t i = titleCount - 1; i >= 0; i--) {
-      free(docTitles[i]);
+      for (int32_t i = titleCount - 1; i >= 0; i--) {
+        free(docTitles[i]);
+      }
+      free(docTitles);
+      docTitles = nullptr;
     }
-    free(docTitles);
-    docTitles = nullptr;
   }
 
   RefPtr<nsPrintSettingsX> settingsX(do_QueryObject(aSettings));
   if (NS_WARN_IF(!settingsX)) {
     return NS_ERROR_FAILURE;
   }
 
   NSPrintInfo* printInfo = settingsX->GetCocoaPrintInfo();