Merge inbound to mozilla-central r=merge a=merge
authorNoemi Erli <nerli@mozilla.com>
Sat, 18 Nov 2017 00:01:46 +0200
changeset 444263 daa0dcd1616cbdf5541f548e44662d20d6d67d99
parent 444189 7ef46e350289add0a2afdb5098d0d8c0de362119 (current diff)
parent 444262 3395c151dabbf8fd4d2ee869d81be344cfe74570 (diff)
child 444290 9999a9025f644801944512c075c9ca7ccebb9988
child 444333 762a08559ce248567b2e81c5890f64a0934bc7b3
push id8527
push userCallek@gmail.com
push dateThu, 11 Jan 2018 21:05:50 +0000
treeherdermozilla-beta@95342d212a7a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
milestone59.0a1
first release with
nightly win32
daa0dcd1616c / 59.0a1 / 20171117222953 / files
nightly win64
daa0dcd1616c / 59.0a1 / 20171117222953 / files
nightly linux32
nightly linux64
nightly mac
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly win32
nightly win64
Merge inbound to mozilla-central r=merge a=merge
dom/webauthn/u2f-hid-rs/src/macos/iohid.rs
extensions/spellcheck/locales/Makefile.in
js/src/tests/js1_2/version120/browser.js
js/src/tests/js1_2/version120/shell.js
js/src/tests/js1_8_5/extensions/regress-677924.js
mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
toolkit/components/search/tests/xpcshell/data/langpack-metadata.json
toolkit/components/search/tests/xpcshell/data/metadata.json
toolkit/components/search/tests/xpcshell/data/search.sqlite
toolkit/components/search/tests/xpcshell/test_async_migration.js
toolkit/components/search/tests/xpcshell/test_migration_langpack.js
toolkit/components/search/tests/xpcshell/test_nodb.js
toolkit/components/search/tests/xpcshell/test_sync_migration.js
--- a/browser/components/dirprovider/DirectoryProvider.cpp
+++ b/browser/components/dirprovider/DirectoryProvider.cpp
@@ -35,33 +35,16 @@ NS_IMPL_ISUPPORTS(DirectoryProvider,
                   nsIDirectoryServiceProvider2)
 
 NS_IMETHODIMP
 DirectoryProvider::GetFile(const char *aKey, bool *aPersist, nsIFile* *aResult)
 {
   return NS_ERROR_FAILURE;
 }
 
-static void
-AppendFileKey(const char *key, nsIProperties* aDirSvc,
-              nsCOMArray<nsIFile> &array)
-{
-  nsCOMPtr<nsIFile> file;
-  nsresult rv = aDirSvc->Get(key, NS_GET_IID(nsIFile), getter_AddRefs(file));
-  if (NS_FAILED(rv))
-    return;
-
-  bool exists;
-  rv = file->Exists(&exists);
-  if (NS_FAILED(rv) || !exists)
-    return;
-
-  array.AppendObject(file);
-}
-
 // Appends the distribution-specific search engine directories to the
 // array.  The directory structure is as follows:
 
 // appdir/
 // \- distribution/
 //    \- searchplugins/
 //       |- common/
 //       \- locale/
@@ -143,76 +126,28 @@ AppendDistroSearchDirs(nsIProperties* aD
       }
     }
   }
 }
 
 NS_IMETHODIMP
 DirectoryProvider::GetFiles(const char *aKey, nsISimpleEnumerator* *aResult)
 {
-  /**
-   * We want to preserve the following order, since the search service loads
-   * engines in first-loaded-wins order.
-   *   - distro search plugin locations (Loaded by the search service using
-   *     NS_APP_DISTRIBUTION_SEARCH_DIR_LIST)
-   *
-   *   - engines shipped in chrome (Loaded from jar files by the search
-   *     service)
-   *
-   *   Then other locations, from NS_APP_SEARCH_DIR_LIST:
-   *   - extension search plugin locations (prepended below using
-   *     NS_NewUnionEnumerator)
-   *   - user search plugin locations (profile)
-   */
-
-  nsresult rv;
-
   if (!strcmp(aKey, NS_APP_DISTRIBUTION_SEARCH_DIR_LIST)) {
     nsCOMPtr<nsIProperties> dirSvc
       (do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID));
     if (!dirSvc)
       return NS_ERROR_FAILURE;
 
     nsCOMArray<nsIFile> distroFiles;
     AppendDistroSearchDirs(dirSvc, distroFiles);
 
     return NS_NewArrayEnumerator(aResult, distroFiles);
   }
 
-  if (!strcmp(aKey, NS_APP_SEARCH_DIR_LIST)) {
-    nsCOMPtr<nsIProperties> dirSvc
-      (do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID));
-    if (!dirSvc)
-      return NS_ERROR_FAILURE;
-
-    nsCOMArray<nsIFile> baseFiles;
-
-    AppendFileKey(NS_APP_USER_SEARCH_DIR, dirSvc, baseFiles);
-
-    nsCOMPtr<nsISimpleEnumerator> baseEnum;
-    rv = NS_NewArrayEnumerator(getter_AddRefs(baseEnum), baseFiles);
-    if (NS_FAILED(rv))
-      return rv;
-
-    nsCOMPtr<nsISimpleEnumerator> list;
-    rv = dirSvc->Get(XRE_EXTENSIONS_DIR_LIST,
-                     NS_GET_IID(nsISimpleEnumerator), getter_AddRefs(list));
-    if (NS_FAILED(rv))
-      return rv;
-
-    static char const *const kAppendSPlugins[] = {"searchplugins", nullptr};
-
-    nsCOMPtr<nsISimpleEnumerator> extEnum =
-      new AppendingEnumerator(list, kAppendSPlugins);
-    if (!extEnum)
-      return NS_ERROR_OUT_OF_MEMORY;
-
-    return NS_NewUnionEnumerator(aResult, extEnum, baseEnum);
-  }
-
   return NS_ERROR_FAILURE;
 }
 
 NS_IMPL_ISUPPORTS(DirectoryProvider::AppendingEnumerator, nsISimpleEnumerator)
 
 NS_IMETHODIMP
 DirectoryProvider::AppendingEnumerator::HasMoreElements(bool *aResult)
 {
--- a/devtools/shared/heapsnapshot/tests/gtest/DevTools.h
+++ b/devtools/shared/heapsnapshot/tests/gtest/DevTools.h
@@ -84,17 +84,16 @@ struct DevTools : public ::testing::Test
     return &globalClass;
   }
 
   JSObject* createGlobal()
   {
     /* Create the global object. */
     JS::RootedObject newGlobal(cx);
     JS::CompartmentOptions options;
-    options.behaviors().setVersion(JSVERSION_DEFAULT);
     newGlobal = JS_NewGlobalObject(cx, getGlobalClass(), nullptr,
                                    JS::FireOnNewGlobalHook, options);
     if (!newGlobal)
       return nullptr;
 
     JSAutoCompartment ac(cx, newGlobal);
 
     /* Populate the global object with the standard globals, like Object and
--- a/dom/base/nsFrameMessageManager.cpp
+++ b/dom/base/nsFrameMessageManager.cpp
@@ -1644,17 +1644,17 @@ nsMessageManagerScriptExecutor::TryCache
 
     JS::SourceBufferHolder srcBuf(dataStringBuf, dataStringLength,
                                   JS::SourceBufferHolder::GiveOwnership);
 
     if (!dataStringBuf || dataStringLength == 0) {
       return;
     }
 
-    JS::CompileOptions options(cx, JSVERSION_DEFAULT);
+    JS::CompileOptions options(cx);
     options.setFileAndLine(url.get(), 1);
     options.setNoScriptRval(true);
 
     if (aRunInGlobalScope) {
       if (!JS::Compile(cx, options, srcBuf, &script)) {
         return;
       }
     // We're going to run these against some non-global scope.
@@ -1709,17 +1709,16 @@ nsMessageManagerScriptExecutor::InitChil
 {
   AutoSafeJSContext cx;
   nsContentUtils::GetSecurityManager()->GetSystemPrincipal(getter_AddRefs(mPrincipal));
 
   const uint32_t flags = xpc::INIT_JS_STANDARD_CLASSES;
 
   JS::CompartmentOptions options;
   options.creationOptions().setSystemZone();
-  options.behaviors().setVersion(JSVERSION_DEFAULT);
 
   if (xpc::SharedMemoryEnabled()) {
     options.creationOptions().setSharedMemoryAndAtomicsEnabled(true);
   }
 
   JS::Rooted<JSObject*> global(cx);
   nsresult rv = xpc::InitClassesWithNewWrappedGlobal(cx, aScope, mPrincipal,
                                                      flags, options,
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -6340,17 +6340,17 @@ nsGlobalWindowInner::RunTimeoutHandler(T
       uint32_t lineNo = 0, dummyColumn = 0;
       handler->GetLocation(&filename, &lineNo, &dummyColumn);
 
       // New script entry point required, due to the "Create a script" sub-step of
       // http://www.whatwg.org/specs/web-apps/current-work/#timer-initialisation-steps
       nsAutoMicroTask mt;
       AutoEntryScript aes(this, reason, true);
       JS::CompileOptions options(aes.cx());
-      options.setFileAndLine(filename, lineNo).setVersion(JSVERSION_DEFAULT);
+      options.setFileAndLine(filename, lineNo);
       options.setNoScriptRval(true);
       JS::Rooted<JSObject*> global(aes.cx(), FastGetGlobalJSObject());
       nsresult rv = NS_OK;
       {
         nsJSUtils::ExecutionContext exec(aes.cx(), global);
         rv = exec.CompileAndExec(options, script);
       }
 
--- a/dom/base/nsJSUtils.cpp
+++ b/dom/base/nsJSUtils.cpp
@@ -97,17 +97,16 @@ nsJSUtils::CompileFunction(AutoJSAPI& js
                            const char** aArgArray,
                            const nsAString& aBody,
                            JSObject** aFunctionObject)
 {
   JSContext* cx = jsapi.cx();
   MOZ_ASSERT(js::GetEnterCompartmentDepth(cx) > 0);
   MOZ_ASSERT_IF(aScopeChain.length() != 0,
                 js::IsObjectInContextCompartment(aScopeChain[0], cx));
-  MOZ_ASSERT_IF(aOptions.versionSet, aOptions.version != JSVERSION_UNKNOWN);
 
   // Do the junk Gecko is supposed to do before calling into JSAPI.
   for (size_t i = 0; i < aScopeChain.length(); ++i) {
     JS::ExposeObjectToActiveJS(aScopeChain[i]);
   }
 
   // Compile.
   JS::Rooted<JSFunction*> fun(cx);
@@ -232,18 +231,16 @@ nsresult
 nsJSUtils::ExecutionContext::CompileAndExec(JS::CompileOptions& aCompileOptions,
                                             JS::SourceBufferHolder& aSrcBuf,
                                             JS::MutableHandle<JSScript*> aScript)
 {
   if (mSkip) {
     return mRv;
   }
 
-  MOZ_ASSERT_IF(aCompileOptions.versionSet,
-                aCompileOptions.version != JSVERSION_UNKNOWN);
   MOZ_ASSERT(aSrcBuf.get());
   MOZ_ASSERT(mRetValue.isUndefined());
 #ifdef DEBUG
   mWantsReturnValue = !aCompileOptions.noScriptRval;
 #endif
 
   bool compiled = true;
   if (mScopeChain.length() == 0) {
@@ -381,18 +378,16 @@ nsresult
 nsJSUtils::CompileModule(JSContext* aCx,
                        JS::SourceBufferHolder& aSrcBuf,
                        JS::Handle<JSObject*> aEvaluationGlobal,
                        JS::CompileOptions &aCompileOptions,
                        JS::MutableHandle<JSObject*> aModule)
 {
   AUTO_PROFILER_LABEL("nsJSUtils::CompileModule", JS);
 
-  MOZ_ASSERT_IF(aCompileOptions.versionSet,
-                aCompileOptions.version != JSVERSION_UNKNOWN);
   MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext());
   MOZ_ASSERT(aSrcBuf.get());
   MOZ_ASSERT(js::GetGlobalForObjectCrossCompartment(aEvaluationGlobal) ==
              aEvaluationGlobal);
   MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx) == aEvaluationGlobal);
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(CycleCollectedJSContext::Get() &&
              CycleCollectedJSContext::Get()->MicroTaskLevel());
--- a/dom/credentialmanagement/CredentialsContainer.cpp
+++ b/dom/credentialmanagement/CredentialsContainer.cpp
@@ -33,24 +33,24 @@ CredentialsContainer::WrapObject(JSConte
 {
   return CredentialsContainerBinding::Wrap(aCx, this, aGivenProto);
 }
 
 already_AddRefed<Promise>
 CredentialsContainer::Get(const CredentialRequestOptions& aOptions)
 {
   RefPtr<WebAuthnManager> mgr = WebAuthnManager::GetOrCreate();
-  return mgr->GetAssertion(mParent, aOptions.mPublicKey);
+  return mgr->GetAssertion(mParent, aOptions.mPublicKey, aOptions.mSignal);
 }
 
 already_AddRefed<Promise>
 CredentialsContainer::Create(const CredentialCreationOptions& aOptions)
 {
   RefPtr<WebAuthnManager> mgr = WebAuthnManager::GetOrCreate();
-  return mgr->MakeCredential(mParent, aOptions.mPublicKey);
+  return mgr->MakeCredential(mParent, aOptions.mPublicKey, aOptions.mSignal);
 }
 
 already_AddRefed<Promise>
 CredentialsContainer::Store(const Credential& aCredential)
 {
   RefPtr<WebAuthnManager> mgr = WebAuthnManager::GetOrCreate();
   return mgr->Store(mParent, aCredential);
 }
--- a/dom/events/EventListenerManager.cpp
+++ b/dom/events/EventListenerManager.cpp
@@ -1051,17 +1051,16 @@ EventListenerManager::CompileEventHandle
   // Get the reflector for |aElement|, so that we can pass to setElement.
   if (NS_WARN_IF(!GetOrCreateDOMReflector(cx, aElement, &v))) {
     return NS_ERROR_FAILURE;
   }
   JS::CompileOptions options(cx);
   // Use line 0 to make the function body starts from line 1.
   options.setIntroductionType("eventHandler")
          .setFileAndLine(url.get(), 0)
-         .setVersion(JSVERSION_DEFAULT)
          .setElement(&v.toObject())
          .setElementAttributeName(jsStr);
 
   JS::Rooted<JSObject*> handler(cx);
   result = nsJSUtils::CompileFunction(jsapi, scopeChain, options,
                                       nsAtomCString(typeAtom),
                                       argCount, argNames, *body, handler.address());
   NS_ENSURE_SUCCESS(result, result);
--- a/dom/jsurl/nsJSProtocolHandler.cpp
+++ b/dom/jsurl/nsJSProtocolHandler.cpp
@@ -254,18 +254,17 @@ nsresult nsJSThunk::EvaluateScript(nsICh
     // Fail if someone tries to execute in a global with system principal.
     if (nsContentUtils::IsSystemPrincipal(objectPrincipal)) {
         return NS_ERROR_DOM_SECURITY_ERR;
     }
 
     JS::Rooted<JS::Value> v (cx, JS::UndefinedValue());
     // Finally, we have everything needed to evaluate the expression.
     JS::CompileOptions options(cx);
-    options.setFileAndLine(mURL.get(), 1)
-           .setVersion(JSVERSION_DEFAULT);
+    options.setFileAndLine(mURL.get(), 1);
     {
         nsJSUtils::ExecutionContext exec(cx, globalJSObject);
         exec.SetCoerceToString(true);
         exec.CompileAndExec(options, NS_ConvertUTF8toUTF16(script));
         rv = exec.ExtractReturnValue(&v);
     }
 
     js::AssertSameCompartment(cx, v);
--- a/dom/plugins/base/nsNPAPIPlugin.cpp
+++ b/dom/plugins/base/nsNPAPIPlugin.cpp
@@ -1039,18 +1039,17 @@ bool
     }
   }
 
   NPN_PLUGIN_LOG(PLUGIN_LOG_NOISY,
                  ("NPN_Evaluate(npp %p, npobj %p, script <<<%s>>>) called\n",
                   npp, npobj, script->UTF8Characters));
 
   JS::CompileOptions options(cx);
-  options.setFileAndLine(spec, 0)
-         .setVersion(JSVERSION_DEFAULT);
+  options.setFileAndLine(spec, 0);
   JS::Rooted<JS::Value> rval(cx);
   JS::AutoObjectVector scopeChain(cx);
   if (obj != js::GetGlobalForObjectCrossCompartment(obj) &&
       !scopeChain.append(obj)) {
     return false;
   }
   obj = js::GetGlobalForObjectCrossCompartment(obj);
   nsresult rv = NS_OK;
--- a/dom/script/ScriptLoader.cpp
+++ b/dom/script/ScriptLoader.cpp
@@ -2002,17 +2002,16 @@ ScriptLoader::FillCompileOptionsForReque
     mDocument->NoteScriptTrackingStatus(aRequest->mURL, aRequest->IsTracking());
   }
 
   bool isScriptElement = !aRequest->IsModuleRequest() ||
                          aRequest->AsModuleRequest()->IsTopLevel();
   aOptions->setIntroductionType(isScriptElement ? "scriptElement"
                                                 : "importedModule");
   aOptions->setFileAndLine(aRequest->mURL.get(), aRequest->mLineNo);
-  aOptions->setVersion(JSVersion(aRequest->mJSVersion));
   aOptions->setIsRunOnce(true);
   aOptions->setNoScriptRval(true);
   if (aRequest->mHasSourceMapURL) {
     aOptions->setSourceMapURL(aRequest->mSourceMapURL.get());
   }
   if (aRequest->mOriginPrincipal) {
     nsIPrincipal* scriptPrin = nsContentUtils::ObjectPrincipal(aScopeChain);
     bool subsumes = scriptPrin->Subsumes(aRequest->mOriginPrincipal);
--- a/dom/webauthn/WebAuthnManager.cpp
+++ b/dom/webauthn/WebAuthnManager.cpp
@@ -194,16 +194,17 @@ WebAuthnManager::WebAuthnManager()
 void
 WebAuthnManager::ClearTransaction()
 {
   if (!NS_WARN_IF(mTransaction.isNothing())) {
     StopListeningForVisibilityEvents(mTransaction.ref().mParent, this);
   }
 
   mTransaction.reset();
+  Unfollow();
 }
 
 void
 WebAuthnManager::RejectTransaction(const nsresult& aError)
 {
   if (!NS_WARN_IF(mTransaction.isNothing())) {
     mTransaction.ref().mPromise->MaybeReject(aError);
   }
@@ -284,33 +285,40 @@ WebAuthnManager*
 WebAuthnManager::Get()
 {
   MOZ_ASSERT(NS_IsMainThread());
   return gWebAuthnManager;
 }
 
 already_AddRefed<Promise>
 WebAuthnManager::MakeCredential(nsPIDOMWindowInner* aParent,
-                                const MakePublicKeyCredentialOptions& aOptions)
+                                const MakePublicKeyCredentialOptions& aOptions,
+                                const Optional<OwningNonNull<AbortSignal>>& aSignal)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aParent);
 
   if (mTransaction.isSome()) {
     CancelTransaction(NS_ERROR_ABORT);
   }
 
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aParent);
 
   ErrorResult rv;
   RefPtr<Promise> promise = Promise::Create(global, rv);
   if (rv.Failed()) {
     return nullptr;
   }
 
+  // Abort the request if aborted flag is already set.
+  if (aSignal.WasPassed() && aSignal.Value().Aborted()) {
+    promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+    return promise.forget();
+  }
+
   nsString origin;
   nsCString rpId;
   rv = GetOrigin(aParent, origin, rpId);
   if (NS_WARN_IF(rv.Failed())) {
     promise->MaybeReject(rv);
     return promise.forget();
   }
 
@@ -470,45 +478,59 @@ WebAuthnManager::MakeCredential(nsPIDOMW
   WebAuthnTransactionInfo info(rpIdHash,
                                clientDataHash,
                                adjustedTimeout,
                                excludeList,
                                extensions);
 
   ListenForVisibilityEvents(aParent, this);
 
+  AbortSignal* signal = nullptr;
+  if (aSignal.WasPassed()) {
+    signal = &aSignal.Value();
+    Follow(signal);
+  }
+
   MOZ_ASSERT(mTransaction.isNothing());
   mTransaction = Some(WebAuthnTransaction(aParent,
                                           promise,
                                           Move(info),
-                                          Move(clientDataJSON)));
+                                          Move(clientDataJSON),
+                                          signal));
 
   mChild->SendRequestRegister(mTransaction.ref().mId, mTransaction.ref().mInfo);
   return promise.forget();
 }
 
 already_AddRefed<Promise>
 WebAuthnManager::GetAssertion(nsPIDOMWindowInner* aParent,
-                              const PublicKeyCredentialRequestOptions& aOptions)
+                              const PublicKeyCredentialRequestOptions& aOptions,
+                              const Optional<OwningNonNull<AbortSignal>>& aSignal)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aParent);
 
   if (mTransaction.isSome()) {
     CancelTransaction(NS_ERROR_ABORT);
   }
 
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aParent);
 
   ErrorResult rv;
   RefPtr<Promise> promise = Promise::Create(global, rv);
   if (rv.Failed()) {
     return nullptr;
   }
 
+  // Abort the request if aborted flag is already set.
+  if (aSignal.WasPassed() && aSignal.Value().Aborted()) {
+    promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+    return promise.forget();
+  }
+
   nsString origin;
   nsCString rpId;
   rv = GetOrigin(aParent, origin, rpId);
   if (NS_WARN_IF(rv.Failed())) {
     promise->MaybeReject(rv);
     return promise.forget();
   }
 
@@ -619,21 +641,28 @@ WebAuthnManager::GetAssertion(nsPIDOMWin
   WebAuthnTransactionInfo info(rpIdHash,
                                clientDataHash,
                                adjustedTimeout,
                                allowList,
                                extensions);
 
   ListenForVisibilityEvents(aParent, this);
 
+  AbortSignal* signal = nullptr;
+  if (aSignal.WasPassed()) {
+    signal = &aSignal.Value();
+    Follow(signal);
+  }
+
   MOZ_ASSERT(mTransaction.isNothing());
   mTransaction = Some(WebAuthnTransaction(aParent,
                                           promise,
                                           Move(info),
-                                          Move(clientDataJSON)));
+                                          Move(clientDataJSON),
+                                          signal));
 
   mChild->SendRequestSign(mTransaction.ref().mId, mTransaction.ref().mInfo);
   return promise.forget();
 }
 
 already_AddRefed<Promise>
 WebAuthnManager::Store(nsPIDOMWindowInner* aParent,
                        const Credential& aCredential)
@@ -906,16 +935,22 @@ WebAuthnManager::HandleEvent(nsIDOMEvent
 
     CancelTransaction(NS_ERROR_ABORT);
   }
 
   return NS_OK;
 }
 
 void
+WebAuthnManager::Abort()
+{
+  CancelTransaction(NS_ERROR_DOM_ABORT_ERR);
+}
+
+void
 WebAuthnManager::ActorDestroyed()
 {
   MOZ_ASSERT(NS_IsMainThread());
   mChild = nullptr;
 }
 
 }
 }
--- a/dom/webauthn/WebAuthnManager.h
+++ b/dom/webauthn/WebAuthnManager.h
@@ -61,21 +61,23 @@ class Promise;
 class WebAuthnTransactionChild;
 
 class WebAuthnTransaction
 {
 public:
   WebAuthnTransaction(nsPIDOMWindowInner* aParent,
                       const RefPtr<Promise>& aPromise,
                       const WebAuthnTransactionInfo&& aInfo,
-                      const nsAutoCString&& aClientData)
+                      const nsAutoCString&& aClientData,
+                      AbortSignal* aSignal)
     : mParent(aParent)
     , mPromise(aPromise)
     , mInfo(aInfo)
     , mClientData(aClientData)
+    , mSignal(aSignal)
     , mId(NextId())
   {
     MOZ_ASSERT(mId > 0);
   }
 
   // Parent of the context we're running the transaction in.
   nsCOMPtr<nsPIDOMWindowInner> mParent;
 
@@ -84,61 +86,69 @@ public:
 
   // Holds the parameters of the current transaction, as we need them both
   // before the transaction request is sent, and on successful return.
   WebAuthnTransactionInfo mInfo;
 
   // Client data used to assemble reply objects.
   nsCString mClientData;
 
+  // An optional AbortSignal instance.
+  RefPtr<AbortSignal> mSignal;
+
   // Unique transaction id.
   uint64_t mId;
 
 private:
   // Generates a unique id for new transactions. This doesn't have to be unique
   // forever, it's sufficient to differentiate between temporally close
   // transactions, where messages can intersect. Can overflow.
   static uint64_t NextId() {
     static uint64_t id = 0;
     return ++id;
   }
 };
 
 class WebAuthnManager final : public nsIDOMEventListener
+                            , public AbortFollower
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIDOMEVENTLISTENER
 
   static WebAuthnManager* GetOrCreate();
   static WebAuthnManager* Get();
 
   already_AddRefed<Promise>
   MakeCredential(nsPIDOMWindowInner* aParent,
-                 const MakePublicKeyCredentialOptions& aOptions);
+                 const MakePublicKeyCredentialOptions& aOptions,
+                 const Optional<OwningNonNull<AbortSignal>>& aSignal);
 
   already_AddRefed<Promise>
   GetAssertion(nsPIDOMWindowInner* aParent,
-               const PublicKeyCredentialRequestOptions& aOptions);
+               const PublicKeyCredentialRequestOptions& aOptions,
+               const Optional<OwningNonNull<AbortSignal>>& aSignal);
 
   already_AddRefed<Promise>
   Store(nsPIDOMWindowInner* aParent, const Credential& aCredential);
 
   void
   FinishMakeCredential(const uint64_t& aTransactionId,
                        nsTArray<uint8_t>& aRegBuffer);
 
   void
   FinishGetAssertion(const uint64_t& aTransactionId,
                      nsTArray<uint8_t>& aCredentialId,
                      nsTArray<uint8_t>& aSigBuffer);
 
   void
   RequestAborted(const uint64_t& aTransactionId, const nsresult& aError);
 
+  void Abort() override;
+
   void ActorDestroyed();
 
 private:
   WebAuthnManager();
   virtual ~WebAuthnManager();
 
   // Clears all information we have about the current transaction.
   void ClearTransaction();
--- a/dom/webauthn/tests/browser/browser.ini
+++ b/dom/webauthn/tests/browser/browser.ini
@@ -1,9 +1,11 @@
 [DEFAULT]
 support-files =
+  tab_webauthn_result.html
   tab_webauthn_success.html
   ../cbor/*
   ../pkijs/*
   ../u2futil.js
 skip-if = !e10s
 
+[browser_abort_visibility.js]
 [browser_webauthn_telemetry.js]
new file mode 100644
--- /dev/null
+++ b/dom/webauthn/tests/browser/browser_abort_visibility.js
@@ -0,0 +1,113 @@
+/* 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/. */
+
+"use strict";
+
+const TEST_URL = "https://example.com/browser/dom/webauthn/tests/browser/tab_webauthn_result.html";
+
+async function assertStatus(tab, expected) {
+  let actual = await ContentTask.spawn(tab.linkedBrowser, null, async function () {
+    return content.document.getElementById("status").value;
+  });
+  is(actual, expected, "webauthn request " + expected);
+}
+
+async function waitForStatus(tab, expected) {
+  await ContentTask.spawn(tab.linkedBrowser, [expected], async function (expected) {
+    return ContentTaskUtils.waitForCondition(() => {
+      return content.document.getElementById("status").value == expected;
+    });
+  });
+
+  await assertStatus(tab, expected);
+}
+
+function startMakeCredentialRequest(tab) {
+  return ContentTask.spawn(tab.linkedBrowser, null, async function () {
+    const cose_alg_ECDSA_w_SHA256 = -7;
+
+    let publicKey = {
+      rp: {id: content.document.domain, name: "none", icon: "none"},
+      user: {id: new Uint8Array(), name: "none", icon: "none", displayName: "none"},
+      challenge: content.crypto.getRandomValues(new Uint8Array(16)),
+      timeout: 5000, // the minimum timeout is actually 15 seconds
+      pubKeyCredParams: [{type: "public-key", alg: cose_alg_ECDSA_w_SHA256}],
+    };
+
+    let status = content.document.getElementById("status");
+
+    content.navigator.credentials.create({publicKey}).then(() => {
+      status.value = "completed";
+    }).catch(() => {
+      status.value = "aborted";
+    });
+
+    status.value = "pending";
+  });
+}
+
+function startGetAssertionRequest(tab) {
+  return ContentTask.spawn(tab.linkedBrowser, null, async function () {
+    let newCredential = {
+      type: "public-key",
+      id: content.crypto.getRandomValues(new Uint8Array(16)),
+      transports: ["usb"],
+    };
+
+    let publicKey = {
+      challenge: content.crypto.getRandomValues(new Uint8Array(16)),
+      timeout: 5000, // the minimum timeout is actually 15 seconds
+      rpId: content.document.domain,
+      allowCredentials: [newCredential]
+    };
+
+    let status = content.document.getElementById("status");
+
+    content.navigator.credentials.get({publicKey}).then(() => {
+      status.value = "completed";
+    }).catch(() => {
+      status.value = "aborted";
+    });
+
+    status.value = "pending";
+  });
+}
+
+
+// Test that MakeCredential() and GetAssertion() requests
+// are aborted when the current tab loses its focus.
+add_task(async function test_abort() {
+  // Enable the USB token.
+  Services.prefs.setBoolPref("security.webauth.webauthn", true);
+  Services.prefs.setBoolPref("security.webauth.webauthn_enable_softtoken", false);
+  Services.prefs.setBoolPref("security.webauth.webauthn_enable_usbtoken", true);
+
+  // Create a new tab for the MakeCredential() request.
+  let tab_create = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
+
+  // Start the request.
+  await startMakeCredentialRequest(tab_create);
+  await assertStatus(tab_create, "pending");
+
+  // Open another tab and switch to it. The first will lose focus.
+  let tab_get = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
+  await waitForStatus(tab_create, "aborted");
+
+  // Start a GetAssertion() request in the second tab.
+  await startGetAssertionRequest(tab_get);
+  await assertStatus(tab_get, "pending");
+
+  // Switch back to the first tab, the get() request is aborted.
+  await BrowserTestUtils.switchTab(gBrowser, tab_create);
+  await waitForStatus(tab_get, "aborted");
+
+  // Close tabs.
+  await BrowserTestUtils.removeTab(tab_create);
+  await BrowserTestUtils.removeTab(tab_get);
+
+  // Cleanup.
+  Services.prefs.clearUserPref("security.webauth.webauthn");
+  Services.prefs.clearUserPref("security.webauth.webauthn_enable_softtoken");
+  Services.prefs.clearUserPref("security.webauth.webauthn_enable_usbtoken");
+});
new file mode 100644
--- /dev/null
+++ b/dom/webauthn/tests/browser/tab_webauthn_result.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<head>
+  <title>Generic W3C Web Authentication Test Result Page</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<h1>Generic W3C Web Authentication Test Result Page</h1>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1415675">Mozilla Bug 1415675</a>
+<input type="text" id="status" value="init" />
+
+</body>
+</html>
--- a/dom/webauthn/tests/mochitest.ini
+++ b/dom/webauthn/tests/mochitest.ini
@@ -1,15 +1,17 @@
 [DEFAULT]
 support-files =
   cbor/*
   pkijs/*
   u2futil.js
 skip-if = !e10s
 scheme = https
 
+[test_webauthn_abort_signal.html]
 [test_webauthn_loopback.html]
 [test_webauthn_no_token.html]
 [test_webauthn_make_credential.html]
 [test_webauthn_get_assertion.html]
+[test_webauthn_override_request.html]
 [test_webauthn_store_credential.html]
 [test_webauthn_sameorigin.html]
 [test_webauthn_isplatformauthenticatoravailable.html]
new file mode 100644
--- /dev/null
+++ b/dom/webauthn/tests/test_webauthn_abort_signal.html
@@ -0,0 +1,148 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<head>
+  <title>Test for aborting W3C Web Authentication request</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
+  <script type="text/javascript" src="u2futil.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+  <h1>Test for aborting W3C Web Authentication request</h1>
+  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1415675">Mozilla Bug 1415675</a>
+
+  <script class="testbody" type="text/javascript">
+    "use strict";
+
+    function arrivingHereIsBad(aResult) {
+      ok(false, "Bad result! Received a: " + aResult);
+    }
+
+    function expectAbortError(aResult) {
+      is(aResult.code, DOMException.ABORT_ERR, "Expecting an AbortError");
+    }
+
+    add_task(() => {
+      // Enable USB tokens.
+      return SpecialPowers.pushPrefEnv({"set": [
+        ["security.webauth.webauthn", true],
+        ["security.webauth.webauthn_enable_softtoken", false],
+        ["security.webauth.webauthn_enable_usbtoken", true],
+      ]});
+    });
+
+    // Start a new MakeCredential() request.
+    function requestMakeCredential(signal) {
+      let publicKey = {
+        rp: {id: document.domain, name: "none", icon: "none"},
+        user: {id: new Uint8Array(), name: "none", icon: "none", displayName: "none"},
+        challenge: crypto.getRandomValues(new Uint8Array(16)),
+        timeout: 5000, // the minimum timeout is actually 15 seconds
+        pubKeyCredParams: [{type: "public-key", alg: cose_alg_ECDSA_w_SHA256}],
+      };
+
+      return navigator.credentials.create({publicKey, signal});
+    }
+
+    // Start a new GetAssertion() request.
+    async function requestGetAssertion(signal) {
+      let newCredential = {
+        type: "public-key",
+        id: crypto.getRandomValues(new Uint8Array(16)),
+        transports: ["usb"],
+      };
+
+      let publicKey = {
+        challenge: crypto.getRandomValues(new Uint8Array(16)),
+        timeout: 5000, // the minimum timeout is actually 15 seconds
+        rpId: document.domain,
+        allowCredentials: [newCredential]
+      };
+
+      // Start the request, handle failures only.
+      return navigator.credentials.get({publicKey, signal});
+    }
+
+    // Create an AbortController and abort immediately.
+    add_task(async () => {
+      let ctrl = new AbortController();
+      ctrl.abort();
+
+      // The event shouldn't fire.
+      ctrl.signal.onabort = arrivingHereIsBad;
+
+      // MakeCredential() should abort immediately.
+      await requestMakeCredential(ctrl.signal)
+        .then(arrivingHereIsBad)
+        .catch(expectAbortError);
+
+      // GetAssertion() should abort immediately.
+      await requestGetAssertion(ctrl.signal)
+        .then(arrivingHereIsBad)
+        .catch(expectAbortError);
+    });
+
+    // Request a new credential and abort the request.
+    add_task(async () => {
+      let aborted = false;
+      let ctrl = new AbortController();
+
+      ctrl.signal.onabort = () => {
+        ok(!aborted, "abort event fired once");
+        aborted = true;
+      };
+
+      // Request a new credential.
+      let request = requestMakeCredential(ctrl.signal)
+        .then(arrivingHereIsBad)
+        .catch(err => {
+          ok(aborted, "abort event was fired");
+          expectAbortError(err);
+        });
+
+      // Wait a tick for the statemachine to start.
+      await Promise.resolve();
+
+      // Abort the request.
+      ok(!aborted, "request still pending");
+      ctrl.abort();
+      ok(aborted, "request aborted");
+
+      // Wait for the request to terminate.
+      await request;
+    });
+
+    // Request a new assertion and abort the request.
+    add_task(async () => {
+      let aborted = false;
+      let ctrl = new AbortController();
+
+      ctrl.signal.onabort = () => {
+        ok(!aborted, "abort event fired once");
+        aborted = true;
+      };
+
+      // Request a new assertion.
+      let request = requestGetAssertion(ctrl.signal)
+        .then(arrivingHereIsBad)
+        .catch(err => {
+          ok(aborted, "abort event was fired");
+          expectAbortError(err);
+        });
+
+      // Wait a tick for the statemachine to start.
+      await Promise.resolve();
+
+      // Abort the request.
+      ok(!aborted, "request still pending");
+      ctrl.abort();
+      ok(aborted, "request aborted");
+
+      // Wait for the request to terminate.
+      await request;
+    });
+  </script>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/webauthn/tests/test_webauthn_override_request.html
@@ -0,0 +1,97 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<head>
+  <title>Test for overriding W3C Web Authentication request</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
+  <script type="text/javascript" src="u2futil.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+  <h1>Test for overriding W3C Web Authentication request</h1>
+  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1415675">Mozilla Bug 1415675</a>
+
+  <script class="testbody" type="text/javascript">
+    "use strict";
+
+    // Last request status.
+    let status = "";
+
+    add_task(() => {
+      // Enable USB tokens.
+      return SpecialPowers.pushPrefEnv({"set": [
+        ["security.webauth.webauthn", true],
+        ["security.webauth.webauthn_enable_softtoken", false],
+        ["security.webauth.webauthn_enable_usbtoken", true],
+      ]});
+    });
+
+    // Start a new MakeCredential() request.
+    async function requestMakeCredential(status_value) {
+      let publicKey = {
+        rp: {id: document.domain, name: "none", icon: "none"},
+        user: {id: new Uint8Array(), name: "none", icon: "none", displayName: "none"},
+        challenge: crypto.getRandomValues(new Uint8Array(16)),
+        timeout: 5000, // the minimum timeout is actually 15 seconds
+        pubKeyCredParams: [{type: "public-key", alg: cose_alg_ECDSA_w_SHA256}],
+      };
+
+      // Start the request, handle failures only.
+      navigator.credentials.create({publicKey}).catch(() => {
+        status = status_value;
+      });
+
+      // Wait a tick to let the statemachine start.
+      await Promise.resolve();
+    }
+
+    // Start a new GetAssertion() request.
+    async function requestGetAssertion(status_value) {
+      let newCredential = {
+        type: "public-key",
+        id: crypto.getRandomValues(new Uint8Array(16)),
+        transports: ["usb"],
+      };
+
+      let publicKey = {
+        challenge: crypto.getRandomValues(new Uint8Array(16)),
+        timeout: 5000, // the minimum timeout is actually 15 seconds
+        rpId: document.domain,
+        allowCredentials: [newCredential]
+      };
+
+      // Start the request, handle failures only.
+      navigator.credentials.get({publicKey}).catch(() => {
+        status = status_value;
+      });
+
+      // Wait a tick to let the statemachine start.
+      await Promise.resolve();
+    }
+
+    // Test that .create() and .get() requests override any pending requests.
+    add_task(async () => {
+      // Request a new credential.
+      await requestMakeCredential("aborted1");
+
+      // Request another credential, the first request will abort.
+      await requestMakeCredential("aborted2");
+      is(status, "aborted1", "first request aborted");
+
+      // Request an assertion, the second request will abort.
+      await requestGetAssertion("aborted3");
+      is(status, "aborted2", "second request aborted");
+
+      // Request another assertion, the third request will abort.
+      await requestGetAssertion("aborted4");
+      is(status, "aborted3", "third request aborted");
+
+      // Request another credential, the fourth request will abort.
+      await requestMakeCredential("aborted5");
+      is(status, "aborted4", "fourth request aborted");
+    });
+  </script>
+
+</body>
+</html>
deleted file mode 100644
--- a/dom/webauthn/u2f-hid-rs/src/macos/iohid.rs
+++ /dev/null
@@ -1,136 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-extern crate log;
-extern crate libc;
-
-use consts::{FIDO_USAGE_PAGE, FIDO_USAGE_U2FHID};
-use core_foundation_sys::base::*;
-use core_foundation_sys::dictionary::*;
-use core_foundation_sys::number::*;
-use core_foundation_sys::runloop::*;
-use core_foundation_sys::string::*;
-use libc::c_void;
-use platform::iokit::{CFRunLoopObserverContext, CFRunLoopObserverCreate};
-
-pub struct IOHIDDeviceMatcher {
-    dict: CFDictionaryRef,
-    keys: Vec<CFStringRef>,
-    values: Vec<CFNumberRef>,
-}
-
-impl IOHIDDeviceMatcher {
-    pub fn new() -> Self {
-        let keys = vec![
-            IOHIDDeviceMatcher::cf_string("DeviceUsage"),
-            IOHIDDeviceMatcher::cf_string("DeviceUsagePage"),
-        ];
-
-        let values = vec![
-            IOHIDDeviceMatcher::cf_number(FIDO_USAGE_U2FHID as i32),
-            IOHIDDeviceMatcher::cf_number(FIDO_USAGE_PAGE as i32),
-        ];
-
-        let dict = unsafe {
-            CFDictionaryCreate(
-                kCFAllocatorDefault,
-                keys.as_ptr() as *const *const libc::c_void,
-                values.as_ptr() as *const *const libc::c_void,
-                keys.len() as CFIndex,
-                &kCFTypeDictionaryKeyCallBacks,
-                &kCFTypeDictionaryValueCallBacks,
-            )
-        };
-
-        Self { dict, keys, values }
-    }
-
-    fn cf_number(number: i32) -> CFNumberRef {
-        let nbox = Box::new(number);
-        let nptr = Box::into_raw(nbox) as *mut libc::c_void;
-
-        unsafe {
-            // Drop when out of scope.
-            let _num = Box::from_raw(nptr as *mut i32);
-            CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, nptr)
-        }
-    }
-
-    fn cf_string(string: &str) -> CFStringRef {
-        unsafe {
-            CFStringCreateWithBytes(
-                kCFAllocatorDefault,
-                string.as_ptr(),
-                string.len() as CFIndex,
-                kCFStringEncodingUTF8,
-                false as Boolean,
-                kCFAllocatorNull,
-            )
-        }
-    }
-
-    pub fn get(&self) -> CFDictionaryRef {
-        self.dict
-    }
-}
-
-impl Drop for IOHIDDeviceMatcher {
-    fn drop(&mut self) {
-        unsafe { CFRelease(self.dict as *mut libc::c_void) };
-
-        for key in &self.keys {
-            unsafe { CFRelease(*key as *mut libc::c_void) };
-        }
-
-        for value in &self.values {
-            unsafe { CFRelease(*value as *mut libc::c_void) };
-        }
-    }
-}
-
-pub struct CFRunLoopEntryObserver {
-    observer: CFRunLoopObserverRef,
-    // Keep alive until the observer goes away.
-    context_ptr: *mut CFRunLoopObserverContext,
-}
-
-impl CFRunLoopEntryObserver {
-    pub fn new(callback: CFRunLoopObserverCallBack, context: *mut c_void) -> Self {
-        let context = CFRunLoopObserverContext::new(context);
-        let context_ptr = Box::into_raw(Box::new(context));
-
-        let observer = unsafe {
-            CFRunLoopObserverCreate(
-                kCFAllocatorDefault,
-                kCFRunLoopEntry,
-                false as Boolean,
-                0,
-                callback,
-                context_ptr,
-            )
-        };
-
-        Self {
-            observer,
-            context_ptr,
-        }
-    }
-
-    pub fn add_to_current_runloop(&self) {
-        unsafe {
-            CFRunLoopAddObserver(CFRunLoopGetCurrent(), self.observer, kCFRunLoopDefaultMode)
-        };
-    }
-}
-
-impl Drop for CFRunLoopEntryObserver {
-    fn drop(&mut self) {
-        unsafe {
-            CFRelease(self.observer as *mut c_void);
-
-            // Drop the CFRunLoopObserverContext.
-            let _ = Box::from_raw(self.context_ptr);
-        };
-    }
-}
--- a/dom/webauthn/u2f-hid-rs/src/macos/iokit.rs
+++ b/dom/webauthn/u2f-hid-rs/src/macos/iokit.rs
@@ -2,20 +2,22 @@
  * 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/. */
 
 #![allow(non_snake_case, non_camel_case_types, non_upper_case_globals)]
 
 extern crate core_foundation_sys;
 extern crate libc;
 
-use core_foundation_sys::base::{Boolean, CFIndex, CFAllocatorRef, CFOptionFlags};
-use core_foundation_sys::string::CFStringRef;
-use core_foundation_sys::runloop::{CFRunLoopRef, CFRunLoopObserverRef, CFRunLoopObserverCallBack};
-use core_foundation_sys::dictionary::CFDictionaryRef;
+use consts::{FIDO_USAGE_PAGE, FIDO_USAGE_U2FHID};
+use core_foundation_sys::base::*;
+use core_foundation_sys::dictionary::*;
+use core_foundation_sys::number::*;
+use core_foundation_sys::runloop::*;
+use core_foundation_sys::string::*;
 use libc::c_void;
 use std::ops::Deref;
 
 type IOOptionBits = u32;
 
 pub type IOReturn = libc::c_int;
 
 pub type IOHIDManagerRef = *mut __IOHIDManager;
@@ -46,28 +48,43 @@ pub struct __IOHIDManager {
 
 #[repr(C)]
 #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
 pub struct IOHIDDeviceRef(*const c_void);
 
 unsafe impl Send for IOHIDDeviceRef {}
 unsafe impl Sync for IOHIDDeviceRef {}
 
-pub struct SendableRunLoop(pub CFRunLoopRef);
+pub struct SendableRunLoop(CFRunLoopRef);
+
+impl SendableRunLoop {
+    pub fn new(runloop: CFRunLoopRef) -> Self {
+        // Keep the CFRunLoop alive for as long as we are.
+        unsafe { CFRetain(runloop as *mut c_void) };
+
+        SendableRunLoop(runloop)
+    }
+}
 
 unsafe impl Send for SendableRunLoop {}
 
 impl Deref for SendableRunLoop {
     type Target = CFRunLoopRef;
 
     fn deref(&self) -> &CFRunLoopRef {
         &self.0
     }
 }
 
+impl Drop for SendableRunLoop {
+    fn drop(&mut self) {
+        unsafe { CFRelease(self.0 as *mut c_void) };
+    }
+}
+
 #[repr(C)]
 pub struct CFRunLoopObserverContext {
     pub version: CFIndex,
     pub info: *mut c_void,
     pub retain: Option<extern "C" fn(info: *const c_void) -> *const c_void>,
     pub release: Option<extern "C" fn(info: *const c_void)>,
     pub copyDescription: Option<extern "C" fn(info: *const c_void) -> CFStringRef>,
 }
@@ -79,16 +96,137 @@ impl CFRunLoopObserverContext {
             info: context,
             retain: None,
             release: None,
             copyDescription: None,
         }
     }
 }
 
+pub struct CFRunLoopEntryObserver {
+    observer: CFRunLoopObserverRef,
+    // Keep alive until the observer goes away.
+    context_ptr: *mut CFRunLoopObserverContext,
+}
+
+impl CFRunLoopEntryObserver {
+    pub fn new(callback: CFRunLoopObserverCallBack, context: *mut c_void) -> Self {
+        let context = CFRunLoopObserverContext::new(context);
+        let context_ptr = Box::into_raw(Box::new(context));
+
+        let observer = unsafe {
+            CFRunLoopObserverCreate(
+                kCFAllocatorDefault,
+                kCFRunLoopEntry,
+                false as Boolean,
+                0,
+                callback,
+                context_ptr,
+            )
+        };
+
+        Self {
+            observer,
+            context_ptr,
+        }
+    }
+
+    pub fn add_to_current_runloop(&self) {
+        unsafe {
+            CFRunLoopAddObserver(CFRunLoopGetCurrent(), self.observer, kCFRunLoopDefaultMode)
+        };
+    }
+}
+
+impl Drop for CFRunLoopEntryObserver {
+    fn drop(&mut self) {
+        unsafe {
+            CFRelease(self.observer as *mut c_void);
+
+            // Drop the CFRunLoopObserverContext.
+            let _ = Box::from_raw(self.context_ptr);
+        };
+    }
+}
+
+pub struct IOHIDDeviceMatcher {
+    dict: CFDictionaryRef,
+    keys: Vec<CFStringRef>,
+    values: Vec<CFNumberRef>,
+}
+
+impl IOHIDDeviceMatcher {
+    pub fn new() -> Self {
+        let keys = vec![
+            IOHIDDeviceMatcher::cf_string("DeviceUsage"),
+            IOHIDDeviceMatcher::cf_string("DeviceUsagePage"),
+        ];
+
+        let values = vec![
+            IOHIDDeviceMatcher::cf_number(FIDO_USAGE_U2FHID as i32),
+            IOHIDDeviceMatcher::cf_number(FIDO_USAGE_PAGE as i32),
+        ];
+
+        let dict = unsafe {
+            CFDictionaryCreate(
+                kCFAllocatorDefault,
+                keys.as_ptr() as *const *const libc::c_void,
+                values.as_ptr() as *const *const libc::c_void,
+                keys.len() as CFIndex,
+                &kCFTypeDictionaryKeyCallBacks,
+                &kCFTypeDictionaryValueCallBacks,
+            )
+        };
+
+        Self { dict, keys, values }
+    }
+
+    fn cf_number(number: i32) -> CFNumberRef {
+        let nbox = Box::new(number);
+        let nptr = Box::into_raw(nbox) as *mut libc::c_void;
+
+        unsafe {
+            // Drop when out of scope.
+            let _num = Box::from_raw(nptr as *mut i32);
+            CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, nptr)
+        }
+    }
+
+    fn cf_string(string: &str) -> CFStringRef {
+        unsafe {
+            CFStringCreateWithBytes(
+                kCFAllocatorDefault,
+                string.as_ptr(),
+                string.len() as CFIndex,
+                kCFStringEncodingUTF8,
+                false as Boolean,
+                kCFAllocatorNull,
+            )
+        }
+    }
+
+    pub fn get(&self) -> CFDictionaryRef {
+        self.dict
+    }
+}
+
+impl Drop for IOHIDDeviceMatcher {
+    fn drop(&mut self) {
+        unsafe { CFRelease(self.dict as *mut libc::c_void) };
+
+        for key in &self.keys {
+            unsafe { CFRelease(*key as *mut libc::c_void) };
+        }
+
+        for value in &self.values {
+            unsafe { CFRelease(*value as *mut libc::c_void) };
+        }
+    }
+}
+
 #[link(name = "IOKit", kind = "framework")]
 extern "C" {
     // CFRunLoop
     pub fn CFRunLoopObserverCreate(
         allocator: CFAllocatorRef,
         activities: CFOptionFlags,
         repeats: Boolean,
         order: CFIndex,
@@ -129,8 +267,78 @@ extern "C" {
     pub fn IOHIDDeviceSetReport(
         device: IOHIDDeviceRef,
         reportType: IOHIDReportType,
         reportID: CFIndex,
         report: *const u8,
         reportLength: CFIndex,
     ) -> IOReturn;
 }
+
+////////////////////////////////////////////////////////////////////////
+// Tests
+////////////////////////////////////////////////////////////////////////
+
+#[cfg(test)]
+mod tests {
+    use core_foundation_sys::base::*;
+    use core_foundation_sys::runloop::*;
+    use libc::c_void;
+    use std::ptr;
+    use std::sync::mpsc::{channel, Sender};
+    use std::thread;
+    use super::*;
+
+    extern "C" fn observe(_: CFRunLoopObserverRef, _: CFRunLoopActivity, context: *mut c_void) {
+        let tx: &Sender<SendableRunLoop> = unsafe { &*(context as *mut _) };
+
+        // Send the current runloop to the receiver to unblock it.
+        let _ = tx.send(SendableRunLoop::new(unsafe { CFRunLoopGetCurrent() }));
+    }
+
+    #[test]
+    fn test_sendable_runloop() {
+        let (tx, rx) = channel();
+
+        let thread = thread::spawn(move || {
+            // Send the runloop to the owning thread.
+            let context = &tx as *const _ as *mut c_void;
+            let obs = CFRunLoopEntryObserver::new(observe, context);
+            obs.add_to_current_runloop();
+
+            unsafe {
+                // We need some source for the runloop to run.
+                let manager = IOHIDManagerCreate(kCFAllocatorDefault, 0);
+                assert!(!manager.is_null());
+
+                IOHIDManagerScheduleWithRunLoop(
+                    manager,
+                    CFRunLoopGetCurrent(),
+                    kCFRunLoopDefaultMode,
+                );
+                IOHIDManagerSetDeviceMatching(manager, ptr::null_mut());
+
+                let rv = IOHIDManagerOpen(manager, 0);
+                assert_eq!(rv, 0);
+
+                // This will run until `CFRunLoopStop()` is called.
+                CFRunLoopRun();
+
+                let rv = IOHIDManagerClose(manager, 0);
+                assert_eq!(rv, 0);
+
+                CFRelease(manager as *mut c_void);
+            }
+        });
+
+        // Block until we enter the CFRunLoop.
+        let runloop: SendableRunLoop = rx.recv().expect("failed to receive runloop");
+
+        // Stop the runloop.
+        unsafe { CFRunLoopStop(*runloop) };
+
+        // Stop the thread.
+        thread.join().expect("failed to join the thread");
+
+        // Try to stop the runloop again (without crashing).
+        unsafe { CFRunLoopStop(*runloop) };
+    }
+}
--- a/dom/webauthn/u2f-hid-rs/src/macos/mod.rs
+++ b/dom/webauthn/u2f-hid-rs/src/macos/mod.rs
@@ -1,17 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 extern crate log;
 
 mod device;
 mod iokit;
-mod iohid;
 mod monitor;
 mod transaction;
 
 use consts::PARAMETER_SIZE;
 use platform::device::Device;
 use platform::transaction::Transaction;
 use std::thread;
 use std::time::Duration;
--- a/dom/webauthn/u2f-hid-rs/src/macos/monitor.rs
+++ b/dom/webauthn/u2f-hid-rs/src/macos/monitor.rs
@@ -3,17 +3,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 extern crate log;
 extern crate libc;
 
 use core_foundation_sys::base::*;
 use core_foundation_sys::runloop::*;
 use libc::c_void;
-use platform::iohid::*;
 use platform::iokit::*;
 use runloop::RunLoop;
 use std::{io, slice};
 use std::collections::HashMap;
 use std::sync::mpsc::{channel, Receiver, Sender};
 use util::io_err;
 
 struct DeviceData {
--- a/dom/webauthn/u2f-hid-rs/src/macos/transaction.rs
+++ b/dom/webauthn/u2f-hid-rs/src/macos/transaction.rs
@@ -1,83 +1,81 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 extern crate libc;
 
 use core_foundation_sys::runloop::*;
 use libc::c_void;
-use platform::iohid::CFRunLoopEntryObserver;
-use platform::iokit::{IOHIDDeviceRef, SendableRunLoop};
+use platform::iokit::{CFRunLoopEntryObserver, IOHIDDeviceRef, SendableRunLoop};
 use platform::monitor::Monitor;
 use std::io;
 use std::sync::mpsc::{channel, Receiver, Sender};
 use std::thread;
 use util::{io_err, to_io_err, OnceCallback};
 
 // A transaction will run the given closure in a new thread, thereby using a
 // separate per-thread state machine for each HID. It will either complete or
 // fail through user action, timeout, or be cancelled when overridden by a new
 // transaction.
 pub struct Transaction {
-    runloop: SendableRunLoop,
+    runloop: Option<SendableRunLoop>,
     thread: Option<thread::JoinHandle<()>>,
 }
 
 impl Transaction {
     pub fn new<F, T>(timeout: u64, callback: OnceCallback<T>, new_device_cb: F) -> io::Result<Self>
     where
         F: Fn(IOHIDDeviceRef, Receiver<Vec<u8>>, &Fn() -> bool) + Sync + Send + 'static,
         T: 'static,
     {
         let (tx, rx) = channel();
-        let cbc = callback.clone();
         let timeout = (timeout as f64) / 1000.0;
 
         let builder = thread::Builder::new();
         let thread = builder.spawn(move || {
             // Add a runloop observer that will be notified when we enter the
             // runloop and tx.send() the current runloop to the owning thread.
             // We need to ensure the runloop was entered before unblocking
             // Transaction::new(), so we can always properly cancel.
             let context = &tx as *const _ as *mut c_void;
             let obs = CFRunLoopEntryObserver::new(Transaction::observe, context);
             obs.add_to_current_runloop();
 
             // Create a new HID device monitor and start polling.
             let mut monitor = Monitor::new(new_device_cb);
-            try_or!(monitor.start(), |e| cbc.call(Err(e)));
+            try_or!(monitor.start(), |e| callback.call(Err(e)));
 
             // This will block until completion, abortion, or timeout.
             unsafe { CFRunLoopRunInMode(kCFRunLoopDefaultMode, timeout, 0) };
 
             // Close the monitor and its devices.
             monitor.stop();
 
             // Send an error, if the callback wasn't called already.
-            cbc.call(Err(io_err("aborted or timed out")));
+            callback.call(Err(io_err("aborted or timed out")));
         })?;
 
         // Block until we enter the CFRunLoop.
         let runloop = rx.recv().map_err(to_io_err)?;
 
         Ok(Self {
-            runloop,
+            runloop: Some(runloop),
             thread: Some(thread),
         })
     }
 
     extern "C" fn observe(_: CFRunLoopObserverRef, _: CFRunLoopActivity, context: *mut c_void) {
         let tx: &Sender<SendableRunLoop> = unsafe { &*(context as *mut _) };
 
         // Send the current runloop to the receiver to unblock it.
-        let _ = tx.send(SendableRunLoop(unsafe { CFRunLoopGetCurrent() }));
+        let _ = tx.send(SendableRunLoop::new(unsafe { CFRunLoopGetCurrent() }));
     }
 
     pub fn cancel(&mut self) {
-        // (This call doesn't block.)
-        unsafe { CFRunLoopStop(*self.runloop) };
+        // This must never be None. This won't block.
+        unsafe { CFRunLoopStop(*self.runloop.take().unwrap()) };
 
         // This must never be None. Ignore return value.
         let _ = self.thread.take().unwrap().join();
     }
 }
--- a/dom/webauthn/u2f-hid-rs/src/stub/mod.rs
+++ b/dom/webauthn/u2f-hid-rs/src/stub/mod.rs
@@ -2,44 +2,45 @@
  * 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/. */
 
 // No-op module to permit compiling token HID support for Android, where
 // no results are returned.
 
 #![allow(dead_code)]
 
-use util::OnceCallback;
+use util::{io_err, OnceCallback};
 
 pub struct PlatformManager {}
 
 impl PlatformManager {
     pub fn new() -> Self {
         Self {}
     }
 
     pub fn register(
         &mut self,
         timeout: u64,
         challenge: Vec<u8>,
         application: Vec<u8>,
         key_handles: Vec<Vec<u8>>,
         callback: OnceCallback<Vec<u8>>,
     ) {
-        // No-op on Android
+        // Not implemented.
+        callback.call(Err(io_err("not implemented")));
     }
 
     pub fn sign(
         &mut self,
         timeout: u64,
         challenge: Vec<u8>,
         application: Vec<u8>,
         key_handles: Vec<Vec<u8>>,
         callback: OnceCallback<(Vec<u8>, Vec<u8>)>,
     ) {
-        // No-op on Android
+        // Not implemented.
+        callback.call(Err(io_err("not implemented")));
     }
 
-    // This blocks.
     pub fn cancel(&mut self) {
-        // No-op on Android
+        // No-op.
     }
 }
--- a/dom/webidl/CredentialManagement.webidl
+++ b/dom/webidl/CredentialManagement.webidl
@@ -17,13 +17,15 @@ interface Credential {
 interface CredentialsContainer {
   Promise<Credential?> get(optional CredentialRequestOptions options);
   Promise<Credential?> create(optional CredentialCreationOptions options);
   Promise<Credential> store(Credential credential);
 };
 
 dictionary CredentialRequestOptions {
   PublicKeyCredentialRequestOptions publicKey;
+  AbortSignal signal;
 };
 
 dictionary CredentialCreationOptions {
   MakePublicKeyCredentialOptions publicKey;
+  AbortSignal signal;
 };
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -1918,17 +1918,16 @@ RuntimeService::Init()
   AssertIsOnMainThread();
 
   nsLayoutStatics::AddRef();
 
   // Initialize JSSettings.
   if (sDefaultJSSettings.gcSettings[0].key.isNothing()) {
     sDefaultJSSettings.contextOptions = JS::ContextOptions();
     sDefaultJSSettings.chrome.maxScriptRuntime = -1;
-    sDefaultJSSettings.chrome.compartmentOptions.behaviors().setVersion(JSVERSION_DEFAULT);
     sDefaultJSSettings.content.maxScriptRuntime = MAX_SCRIPT_RUN_TIME_SEC;
 #ifdef JS_GC_ZEAL
     sDefaultJSSettings.gcZealFrequency = JS_DEFAULT_ZEAL_FREQ;
     sDefaultJSSettings.gcZeal = 0;
 #endif
     SetDefaultJSGCSettings(JSGC_MAX_BYTES, WORKER_DEFAULT_RUNTIME_HEAPSIZE);
     SetDefaultJSGCSettings(JSGC_ALLOCATION_THRESHOLD,
                            WORKER_DEFAULT_ALLOCATION_THRESHOLD);
--- a/dom/workers/ScriptLoader.cpp
+++ b/dom/workers/ScriptLoader.cpp
@@ -1968,20 +1968,16 @@ ScriptExecutorRunnable::WorkerRun(JSCont
     }
 
     NS_ConvertUTF16toUTF8 filename(loadInfo.mURL);
 
     JS::CompileOptions options(aCx);
     options.setFileAndLine(filename.get(), 1)
            .setNoScriptRval(true);
 
-    if (mScriptLoader.mWorkerScriptType == DebuggerScript) {
-      options.setVersion(JSVERSION_DEFAULT);
-    }
-
     MOZ_ASSERT(loadInfo.mMutedErrorFlag.isSome());
     options.setMutedErrors(loadInfo.mMutedErrorFlag.valueOr(true));
 
     JS::SourceBufferHolder srcBuf(loadInfo.mScriptTextBuf,
                                   loadInfo.mScriptTextLength,
                                   JS::SourceBufferHolder::GiveOwnership);
     loadInfo.mScriptTextBuf = nullptr;
     loadInfo.mScriptTextLength = 0;
--- a/dom/worklet/Worklet.cpp
+++ b/dom/worklet/Worklet.cpp
@@ -201,17 +201,16 @@ public:
 
     (void) new XPCWrappedNativeScope(cx, globalObj);
 
     NS_ConvertUTF16toUTF8 url(mURL);
 
     JS::CompileOptions compileOptions(cx);
     compileOptions.setIntroductionType("Worklet");
     compileOptions.setFileAndLine(url.get(), 0);
-    compileOptions.setVersion(JSVERSION_DEFAULT);
     compileOptions.setIsRunOnce(true);
     compileOptions.setNoScriptRval(true);
 
     JSAutoCompartment comp(cx, globalObj);
 
     JS::Rooted<JS::Value> unused(cx);
     if (!JS::Evaluate(cx, compileOptions, buffer, &unused)) {
       ErrorResult error;
--- a/dom/xbl/nsXBLProtoImplField.cpp
+++ b/dom/xbl/nsXBLProtoImplField.cpp
@@ -428,18 +428,17 @@ nsXBLProtoImplField::InstallField(JS::Ha
     xpc::GetScopeForXBLExecution(jsapi.cx(), aBoundNode, addonId));
   NS_ENSURE_TRUE(scopeObject, NS_ERROR_OUT_OF_MEMORY);
 
   AutoEntryScript aes(scopeObject, "XBL <field> initialization", true);
   JSContext* cx = aes.cx();
 
   JS::Rooted<JS::Value> result(cx);
   JS::CompileOptions options(cx);
-  options.setFileAndLine(uriSpec.get(), mLineNumber)
-         .setVersion(JSVERSION_DEFAULT);
+  options.setFileAndLine(uriSpec.get(), mLineNumber);
   JS::AutoObjectVector scopeChain(cx);
   if (!nsJSUtils::GetScopeChainForElement(cx, boundElement, scopeChain)) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
   rv = NS_OK;
   {
     nsJSUtils::ExecutionContext exec(cx, scopeObject);
     exec.SetScopeChain(scopeChain);
--- a/dom/xbl/nsXBLProtoImplMethod.cpp
+++ b/dom/xbl/nsXBLProtoImplMethod.cpp
@@ -187,18 +187,17 @@ nsXBLProtoImplMethod::CompileMember(Auto
   if (hash != kNotFound) {
     functionUri.Truncate(hash);
   }
 
   JSContext *cx = jsapi.cx();
   JSAutoCompartment ac(cx, aClassObject);
   JS::CompileOptions options(cx);
   options.setFileAndLine(functionUri.get(),
-                         uncompiledMethod->mBodyText.GetLineNumber())
-         .setVersion(JSVERSION_DEFAULT);
+                         uncompiledMethod->mBodyText.GetLineNumber());
   JS::Rooted<JSObject*> methodObject(cx);
   JS::AutoObjectVector emptyVector(cx);
   nsresult rv = nsJSUtils::CompileFunction(jsapi, emptyVector, options, cname,
                                            paramCount,
                                            const_cast<const char**>(args),
                                            body, methodObject.address());
 
   // Destroy our uncompiled method and delete our arg list.
--- a/dom/xbl/nsXBLProtoImplProperty.cpp
+++ b/dom/xbl/nsXBLProtoImplProperty.cpp
@@ -194,18 +194,17 @@ nsXBLProtoImplProperty::CompileMember(Au
 
   bool deletedGetter = false;
   nsXBLTextWithLineNumber *getterText = mGetter.GetUncompiled();
   if (getterText && getterText->GetText()) {
     nsDependentString getter(getterText->GetText());
     if (!getter.IsEmpty()) {
       JSAutoCompartment ac(cx, aClassObject);
       JS::CompileOptions options(cx);
-      options.setFileAndLine(functionUri.get(), getterText->GetLineNumber())
-             .setVersion(JSVERSION_DEFAULT);
+      options.setFileAndLine(functionUri.get(), getterText->GetLineNumber());
       nsCString name = NS_LITERAL_CSTRING("get_") + NS_ConvertUTF16toUTF8(mName);
       JS::Rooted<JSObject*> getterObject(cx);
       JS::AutoObjectVector emptyVector(cx);
       rv = nsJSUtils::CompileFunction(jsapi, emptyVector, options, name, 0,
                                       nullptr, getter, getterObject.address());
 
       delete getterText;
       deletedGetter = true;
@@ -240,18 +239,17 @@ nsXBLProtoImplProperty::CompileMember(Au
 
   bool deletedSetter = false;
   nsXBLTextWithLineNumber *setterText = mSetter.GetUncompiled();
   if (setterText && setterText->GetText()) {
     nsDependentString setter(setterText->GetText());
     if (!setter.IsEmpty()) {
       JSAutoCompartment ac(cx, aClassObject);
       JS::CompileOptions options(cx);
-      options.setFileAndLine(functionUri.get(), setterText->GetLineNumber())
-             .setVersion(JSVERSION_DEFAULT);
+      options.setFileAndLine(functionUri.get(), setterText->GetLineNumber());
       nsCString name = NS_LITERAL_CSTRING("set_") + NS_ConvertUTF16toUTF8(mName);
       JS::Rooted<JSObject*> setterObject(cx);
       JS::AutoObjectVector emptyVector(cx);
       rv = nsJSUtils::CompileFunction(jsapi, emptyVector, options, name, 1,
                                       gPropertyArgs, setter,
                                       setterObject.address());
 
       delete setterText;
--- a/dom/xbl/nsXBLPrototypeHandler.cpp
+++ b/dom/xbl/nsXBLPrototypeHandler.cpp
@@ -429,18 +429,17 @@ nsXBLPrototypeHandler::EnsureEventHandle
   uint32_t argCount;
   const char **argNames;
   nsContentUtils::GetEventArgNames(kNameSpaceID_XBL, aName, false, &argCount,
                                    &argNames);
 
   // Compile the event handler in the xbl scope.
   JSAutoCompartment ac(cx, scopeObject);
   JS::CompileOptions options(cx);
-  options.setFileAndLine(bindingURI.get(), mLineNumber)
-         .setVersion(JSVERSION_DEFAULT);
+  options.setFileAndLine(bindingURI.get(), mLineNumber);
 
   JS::Rooted<JSObject*> handlerFun(cx);
   JS::AutoObjectVector emptyVector(cx);
   rv = nsJSUtils::CompileFunction(jsapi, emptyVector, options,
                                   nsAtomCString(aName), argCount,
                                   argNames, handlerText,
                                   handlerFun.address());
   NS_ENSURE_SUCCESS(rv, rv);
--- a/dom/xul/nsXULElement.cpp
+++ b/dom/xul/nsXULElement.cpp
@@ -2694,18 +2694,17 @@ nsXULPrototypeScript::Compile(JS::Source
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     // Ok, compile it to create a prototype script object!
     NS_ENSURE_TRUE(JSVersion(mLangVersion) != JSVERSION_UNKNOWN, NS_OK);
     JS::CompileOptions options(cx);
     options.setIntroductionType("scriptElement")
-           .setFileAndLine(urlspec.get(), aLineNo)
-           .setVersion(JSVersion(mLangVersion));
+           .setFileAndLine(urlspec.get(), aLineNo);
     // If the script was inline, tell the JS parser to save source for
     // Function.prototype.toSource(). If it's out of line, we retrieve the
     // source from the files on demand.
     options.setSourceIsLazy(mOutOfLine);
     JS::Rooted<JSObject*> scope(cx, JS::CurrentGlobalOrNull(cx));
     if (scope) {
       JS::ExposeObjectToActiveJS(scope);
     }
--- a/extensions/cookie/test/unit/test_cookies_async_failure.js
+++ b/extensions/cookie/test/unit/test_cookies_async_failure.js
@@ -158,19 +158,39 @@ function* run_test_1(generator)
 
   // Attempt to insert a cookie with the same (name, host, path) triplet.
   Services.cookiemgr.add(cookie.host, cookie.path, cookie.name, "hallo",
     cookie.isSecure, cookie.isHttpOnly, cookie.isSession, cookie.expiry, {});
 
   // Check that the cookie service accepted the new cookie.
   do_check_eq(Services.cookiemgr.countCookiesFromHost(cookie.host), 1);
 
-  // Wait for the cookie service to rename the old database and rebuild.
-  new _observer(sub_generator, "cookie-db-rebuilding");
-  yield;
+  let isRebuildingDone = false;
+  let rebuildingObserve = function (subject, topic, data) {
+    isRebuildingDone = true;
+    Services.obs.removeObserver(rebuildingObserve, "cookie-db-rebuilding");
+  };
+  Services.obs.addObserver(rebuildingObserve, "cookie-db-rebuilding");
+
+  // Crash test: we're going to rebuild the cookie database. Close all the db
+  // connections in the main thread and initialize a new database file in the
+  // cookie thread. Trigger some access of cookies to ensure we won't crash in
+  // the chaos status.
+  for (let i = 0; i < 10; ++i) {
+    do_check_eq(Services.cookiemgr.countCookiesFromHost(cookie.host), 1);
+    do_execute_soon(function() { do_run_generator(sub_generator); });
+    yield;
+  }
+
+  // Wait for the cookie service to rename the old database and rebuild if not yet.
+  if (!isRebuildingDone) {
+    Services.obs.removeObserver(rebuildingObserve, "cookie-db-rebuilding");
+    new _observer(sub_generator, "cookie-db-rebuilding");
+    yield;
+  }
   do_execute_soon(function() { do_run_generator(sub_generator); });
   yield;
 
   // At this point, the cookies should still be in memory.
   do_check_eq(Services.cookiemgr.countCookiesFromHost("foo.com"), 1);
   do_check_eq(Services.cookiemgr.countCookiesFromHost(cookie.host), 1);
   do_check_eq(do_count_cookies(), 2);
 
--- a/gfx/thebes/gfxDWriteFonts.cpp
+++ b/gfx/thebes/gfxDWriteFonts.cpp
@@ -509,29 +509,16 @@ gfxDWriteFont::InitCairoScaledFont()
     if (!mScaledFont) {
         cairo_matrix_t sizeMatrix;
         cairo_matrix_t identityMatrix;
 
         cairo_matrix_init_scale(&sizeMatrix, mAdjustedSize, mAdjustedSize);
         cairo_matrix_init_identity(&identityMatrix);
 
         cairo_font_options_t *fontOptions = cairo_font_options_create();
-        if (mNeedsOblique) {
-            double skewfactor = OBLIQUE_SKEW_FACTOR;
-
-            cairo_matrix_t style;
-            cairo_matrix_init(&style,
-                              1,                //xx
-                              0,                //yx
-                              -1 * skewfactor,  //xy
-                              1,                //yy
-                              0,                //x0
-                              0);               //y0
-            cairo_matrix_multiply(&sizeMatrix, &sizeMatrix, &style);
-        }
 
         if (mAntialiasOption != kAntialiasDefault) {
             cairo_font_options_set_antialias(fontOptions,
                 GetCairoAntialiasOption(mAntialiasOption));
         }
 
         mScaledFont = cairo_scaled_font_create(CairoFontFace(),
                                                     &sizeMatrix,
--- a/gfx/thebes/gfxFT2FontList.cpp
+++ b/gfx/thebes/gfxFT2FontList.cpp
@@ -176,33 +176,16 @@ FT2FontEntry::CreateScaledFont(const gfx
 
     cairo_matrix_t sizeMatrix;
     cairo_matrix_t identityMatrix;
 
     // XXX deal with adjusted size
     cairo_matrix_init_scale(&sizeMatrix, aStyle->size, aStyle->size);
     cairo_matrix_init_identity(&identityMatrix);
 
-    // synthetic oblique by skewing via the font matrix
-    bool needsOblique = IsUpright() &&
-                        aStyle->style != NS_FONT_STYLE_NORMAL &&
-                        aStyle->allowSyntheticStyle;
-
-    if (needsOblique) {
-        cairo_matrix_t style;
-        cairo_matrix_init(&style,
-                          1,                //xx
-                          0,                //yx
-                          -1 * OBLIQUE_SKEW_FACTOR, //xy
-                          1,                //yy
-                          0,                //x0
-                          0);               //y0
-        cairo_matrix_multiply(&sizeMatrix, &sizeMatrix, &style);
-    }
-
     cairo_font_options_t *fontOptions = cairo_font_options_create();
 
     if (gfxPlatform::GetPlatform()->RequiresLinearZoom()) {
         cairo_font_options_set_hint_metrics(fontOptions, CAIRO_HINT_METRICS_OFF);
     }
 
     scaledFont = cairo_scaled_font_create(cairoFace,
                                           &sizeMatrix,
--- a/gfx/thebes/gfxFcPlatformFontList.cpp
+++ b/gfx/thebes/gfxFcPlatformFontList.cpp
@@ -678,17 +678,17 @@ gfxFontconfigFontEntry::CreateScaledFont
                                          gfxFloat aAdjustedSize,
                                          const gfxFontStyle *aStyle,
                                          bool aNeedsBold)
 {
     if (aNeedsBold) {
         FcPatternAddBool(aRenderPattern, FC_EMBOLDEN, FcTrue);
     }
 
-    // synthetic oblique by skewing via the font matrix
+    // will synthetic oblique be applied using a transform?
     bool needsOblique = IsUpright() &&
                         aStyle->style != NS_FONT_STYLE_NORMAL &&
                         aStyle->allowSyntheticStyle;
 
     if (needsOblique) {
         // disable embedded bitmaps (mimics behavior in 90-synthetic.conf)
         FcPatternDel(aRenderPattern, FC_EMBEDDED_BITMAP);
         FcPatternAddBool(aRenderPattern, FC_EMBEDDED_BITMAP, FcFalse);
@@ -717,30 +717,16 @@ gfxFontconfigFontEntry::CreateScaledFont
     cairo_scaled_font_t *scaledFont = nullptr;
 
     cairo_matrix_t sizeMatrix;
     cairo_matrix_t identityMatrix;
 
     cairo_matrix_init_scale(&sizeMatrix, aAdjustedSize, aAdjustedSize);
     cairo_matrix_init_identity(&identityMatrix);
 
-    if (needsOblique) {
-        const double kSkewFactor = OBLIQUE_SKEW_FACTOR;
-
-        cairo_matrix_t style;
-        cairo_matrix_init(&style,
-                          1,                //xx
-                          0,                //yx
-                          -1 * kSkewFactor,  //xy
-                          1,                //yy
-                          0,                //x0
-                          0);               //y0
-        cairo_matrix_multiply(&sizeMatrix, &sizeMatrix, &style);
-    }
-
     cairo_font_options_t *fontOptions = cairo_font_options_create();
     PrepareFontOptions(aRenderPattern, fontOptions);
 
     scaledFont = cairo_scaled_font_create(face, &sizeMatrix,
                                           &identityMatrix, fontOptions);
     cairo_font_options_destroy(fontOptions);
 
     NS_ASSERTION(cairo_scaled_font_status(scaledFont) == CAIRO_STATUS_SUCCESS,
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -1595,17 +1595,17 @@ public:
         , mCapacity(0)
         , mNumGlyphs(0)
     {
     }
 
     ~GlyphBufferAzure()
     {
         if (mNumGlyphs > 0) {
-            Flush();
+            FlushGlyphs();
         }
 
         if (mBuffer != *mAutoBuffer.addr()) {
             free(mBuffer);
         }
     }
 
     // Ensure the buffer has enough space for aGlyphCount glyphs to be added.
@@ -1638,32 +1638,40 @@ public:
     }
 
     void OutputGlyph(uint32_t aGlyphID, const gfx::Point& aPt)
     {
         // Check that AddCapacity has been used appropriately!
         MOZ_ASSERT(mNumGlyphs < mCapacity);
         Glyph* glyph = mBuffer + mNumGlyphs++;
         glyph->mIndex = aGlyphID;
-        glyph->mPosition = mFontParams.matInv.TransformPoint(aPt);
+        glyph->mPosition = aPt;
+    }
+
+    void Flush()
+    {
+        if (mNumGlyphs > 0) {
+            FlushGlyphs();
+            mNumGlyphs = 0;
+        }
     }
 
     const TextRunDrawParams& mRunParams;
     const FontDrawParams& mFontParams;
 
 private:
     static DrawMode
     GetStrokeMode(DrawMode aMode)
     {
         return aMode & (DrawMode::GLYPH_STROKE |
                         DrawMode::GLYPH_STROKE_UNDERNEATH);
     }
 
     // Render the buffered glyphs to the draw target.
-    void Flush()
+    void FlushGlyphs()
     {
         if (mRunParams.isRTL) {
             std::reverse(mBuffer, mBuffer + mNumGlyphs);
         }
 
         gfx::GlyphBuffer buf;
         buf.mGlyphs = mBuffer;
         buf.mNumGlyphs = mNumGlyphs;
@@ -1692,46 +1700,18 @@ private:
                     } else {
                         pat = nullptr;
                     }
                 } else {
                     pat = fillPattern->GetPattern(mRunParams.dt);
                 }
 
                 if (pat) {
-                    Matrix saved;
-                    Matrix *mat = nullptr;
-                    if (mFontParams.passedInvMatrix) {
-                        // The brush matrix needs to be multiplied with the
-                        // inverted matrix as well, to move the brush into the
-                        // space of the glyphs.
-
-                        // This relies on the returned Pattern not to be reused
-                        // by others, but regenerated on GetPattern calls. This
-                        // is true!
-                        if (pat->GetType() == PatternType::LINEAR_GRADIENT) {
-                            mat = &static_cast<LinearGradientPattern*>(pat)->mMatrix;
-                        } else if (pat->GetType() == PatternType::RADIAL_GRADIENT) {
-                            mat = &static_cast<RadialGradientPattern*>(pat)->mMatrix;
-                        } else if (pat->GetType() == PatternType::SURFACE) {
-                            mat = &static_cast<SurfacePattern*>(pat)->mMatrix;
-                        }
-
-                        if (mat) {
-                            saved = *mat;
-                            *mat = (*mat) * (*mFontParams.passedInvMatrix);
-                        }
-                    }
-
                     mRunParams.dt->FillGlyphs(mFontParams.scaledFont, buf,
                                               *pat, mFontParams.drawOptions);
-
-                    if (mat) {
-                        *mat = saved;
-                    }
                 }
             } else if (state.sourceSurface) {
                 mRunParams.dt->FillGlyphs(mFontParams.scaledFont, buf,
                                           SurfacePattern(state.sourceSurface,
                                                          ExtendMode::CLAMP,
                                                          state.surfTransform),
                                           mFontParams.drawOptions);
             } else {
@@ -1745,44 +1725,17 @@ private:
             Pattern *pat;
             if (mRunParams.textStrokePattern) {
                 pat = mRunParams.textStrokePattern->GetPattern(
                   mRunParams.dt, state.patternTransformChanged
                                    ? &state.patternTransform
                                    : nullptr);
 
                 if (pat) {
-                    Matrix saved;
-                    Matrix *mat = nullptr;
-                    if (mFontParams.passedInvMatrix) {
-                        // The brush matrix needs to be multiplied with the
-                        // inverted matrix as well, to move the brush into the
-                        // space of the glyphs.
-
-                        // This relies on the returned Pattern not to be reused
-                        // by others, but regenerated on GetPattern calls. This
-                        // is true!
-                        if (pat->GetType() == PatternType::LINEAR_GRADIENT) {
-                            mat = &static_cast<LinearGradientPattern*>(pat)->mMatrix;
-                        } else if (pat->GetType() == PatternType::RADIAL_GRADIENT) {
-                            mat = &static_cast<RadialGradientPattern*>(pat)->mMatrix;
-                        } else if (pat->GetType() == PatternType::SURFACE) {
-                            mat = &static_cast<SurfacePattern*>(pat)->mMatrix;
-                        }
-
-                        if (mat) {
-                            saved = *mat;
-                            *mat = (*mat) * (*mFontParams.passedInvMatrix);
-                        }
-                    }
                     FlushStroke(buf, *pat);
-
-                    if (mat) {
-                        *mat = saved;
-                    }
                 }
             } else {
                 FlushStroke(buf,
                             ColorPattern(
                               Color::FromABGR(mRunParams.textStrokeColor)));
             }
         }
         if (mRunParams.drawMode & DrawMode::GLYPH_PATH) {
@@ -1964,18 +1917,36 @@ void
 gfxFont::DrawOneGlyph(uint32_t aGlyphID, const gfx::Point& aPt,
                       GlyphBufferAzure& aBuffer, bool *aEmittedGlyphs) const
 {
     const TextRunDrawParams& runParams(aBuffer.mRunParams);
 
     gfx::Point devPt(ToDeviceUnits(aPt.x, runParams.devPerApp),
                      ToDeviceUnits(aPt.y, runParams.devPerApp));
 
+    gfxContextMatrixAutoSaveRestore matrixRestore;
+
     if (FC == FontComplexityT::ComplexFont) {
         const FontDrawParams& fontParams(aBuffer.mFontParams);
+
+        if (fontParams.needsOblique && fontParams.isVerticalFont) {
+            // We have to flush each glyph individually when doing
+            // synthetic-oblique for vertical-upright text, because
+            // the skew transform needs to be applied to a separate
+            // origin for each glyph, not once for the whole run.
+            aBuffer.Flush();
+            matrixRestore.SetContext(runParams.context);
+            gfx::Matrix mat =
+                runParams.context->CurrentMatrix().
+                PreTranslate(devPt).
+                PreMultiply(gfx::Matrix(1, 0, -OBLIQUE_SKEW_FACTOR, 1, 0, 0)).
+                PreTranslate(-devPt);
+            runParams.context->SetMatrix(mat);
+        }
+
         if (fontParams.haveSVGGlyphs) {
             if (!runParams.paintSVGGlyphs) {
                 return;
             }
             NS_WARNING_ASSERTION(
               runParams.drawMode != DrawMode::GLYPH_PATH,
               "Rendering SVG glyph despite request for glyph path");
             if (RenderSVGGlyph(runParams.context, devPt,
@@ -1984,17 +1955,17 @@ gfxFont::DrawOneGlyph(uint32_t aGlyphID,
                 return;
             }
         }
 
         if (fontParams.haveColorGlyphs &&
             RenderColorGlyph(runParams.dt, runParams.context,
                              fontParams.scaledFont,
                              fontParams.drawOptions,
-                             fontParams.matInv.TransformPoint(devPt),
+                             devPt,
                              aGlyphID)) {
             return;
         }
     }
 
     aBuffer.OutputGlyph(aGlyphID, devPt);
 
     if (FC == FontComplexityT::ComplexFont) {
@@ -2003,16 +1974,20 @@ gfxFont::DrawOneGlyph(uint32_t aGlyphID,
         for (int32_t i = 0; i < fontParams.extraStrikes; ++i) {
             if (fontParams.isVerticalFont) {
                 devPt.y += fontParams.synBoldOnePixelOffset;
             } else {
                 devPt.x += fontParams.synBoldOnePixelOffset;
             }
             aBuffer.OutputGlyph(aGlyphID, devPt);
         }
+
+        if (fontParams.needsOblique && fontParams.isVerticalFont) {
+            aBuffer.Flush();
+        }
     }
 
     *aEmittedGlyphs = true;
 }
 
 bool
 gfxFont::DrawMissingGlyph(const TextRunDrawParams&            aRunParams,
                           const FontDrawParams&               aFontParams,
@@ -2035,35 +2010,33 @@ gfxFont::DrawMissingGlyph(const TextRunD
         Float height = GetMetrics(eHorizontal).maxAscent;
         Rect glyphRect = aFontParams.isVerticalFont ?
             Rect(pt.x - height / 2, pt.y,
                  height, advanceDevUnits) :
             Rect(pt.x, pt.y - height,
                  advanceDevUnits, height);
 
         // If there's a fake-italic skew in effect as part
-        // of the drawTarget's transform, we need to remove
+        // of the drawTarget's transform, we need to undo
         // this before drawing the hexbox. (Bug 983985)
-        Matrix oldMat;
-        if (aFontParams.passedInvMatrix) {
-            oldMat = aRunParams.dt->GetTransform();
-            aRunParams.dt->SetTransform(
-                *aFontParams.passedInvMatrix * oldMat);
+        gfxContextMatrixAutoSaveRestore matrixRestore;
+        if (aFontParams.needsOblique && !aFontParams.isVerticalFont) {
+            matrixRestore.SetContext(aRunParams.context);
+            gfx::Matrix mat =
+                aRunParams.context->CurrentMatrix().
+                PreTranslate(pt).
+                PreMultiply(gfx::Matrix(1, 0, OBLIQUE_SKEW_FACTOR, 1, 0, 0)).
+                PreTranslate(-pt);
+            aRunParams.context->SetMatrix(mat);
         }
 
         gfxFontMissingGlyphs::DrawMissingGlyph(
             aDetails->mGlyphID, glyphRect, *aRunParams.dt,
             PatternFromState(aRunParams.context),
             1.0 / aRunParams.devPerApp);
-
-        // Restore the matrix, if we modified it before
-        // drawing the hexbox.
-        if (aFontParams.passedInvMatrix) {
-            aRunParams.dt->SetTransform(oldMat);
-        }
     }
     return true;
 }
 
 // This method is mostly parallel to DrawGlyphs.
 void
 gfxFont::DrawEmphasisMarks(const gfxTextRun* aShapedText, gfx::Point* aPt,
                            uint32_t aOffset, uint32_t aCount,
@@ -2123,19 +2096,23 @@ gfxFont::Draw(const gfxTextRun *aTextRun
     }
 
     fontParams.scaledFont = GetScaledFont(aRunParams.dt);
     if (!fontParams.scaledFont) {
         return;
     }
 
     auto* textDrawer = aRunParams.context->GetTextDrawer();
+
+    fontParams.needsOblique = mFontEntry->IsUpright() &&
+                              mStyle.style != NS_FONT_STYLE_NORMAL &&
+                              mStyle.allowSyntheticStyle;
     fontParams.haveSVGGlyphs = GetFontEntry()->TryGetSVGData(this);
 
-    if (fontParams.haveSVGGlyphs && textDrawer) {
+    if ((fontParams.needsOblique || fontParams.haveSVGGlyphs) && textDrawer) {
         textDrawer->FoundUnsupportedFeature();
         return;
     }
 
     fontParams.haveColorGlyphs = GetFontEntry()->TryGetColorGlyphs();
     fontParams.contextPaint = aRunParams.runContextPaint;
     fontParams.isVerticalFont =
         aOrientation == gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT;
@@ -2180,16 +2157,33 @@ gfxFont::Draw(const gfxTextRun *aTextRun
         if (aTextRun->UseCenterBaseline()) {
             gfxPoint baseAdj(0, (metrics.emAscent - metrics.emDescent) / 2);
             mat.PreTranslate(baseAdj);
         }
 
         aRunParams.context->SetMatrixDouble(mat);
     }
 
+    if (fontParams.needsOblique && !fontParams.isVerticalFont) {
+        // Adjust matrix for synthetic-oblique, except if we're doing vertical-
+        // upright text, in which case this will be handled for each glyph
+        // individually in DrawOneGlyph.
+        if (!matrixRestore.HasMatrix()) {
+            matrixRestore.SetContext(aRunParams.context);
+        }
+        gfx::Point p(aPt->x * aRunParams.devPerApp,
+                     aPt->y * aRunParams.devPerApp);
+        gfx::Matrix mat =
+            aRunParams.context->CurrentMatrix().
+            PreTranslate(p).
+            PreMultiply(gfx::Matrix(1, 0, -OBLIQUE_SKEW_FACTOR, 1, 0, 0)).
+            PreTranslate(-p);
+        aRunParams.context->SetMatrix(mat);
+    }
+
     RefPtr<SVGContextPaint> contextPaint;
     if (fontParams.haveSVGGlyphs && !fontParams.contextPaint) {
         // If no pattern is specified for fill, use the current pattern
         NS_ASSERTION((int(aRunParams.drawMode) & int(DrawMode::GLYPH_STROKE)) == 0,
                      "no pattern supplied for stroking text");
         RefPtr<gfxPattern> fillPattern = aRunParams.context->GetPattern();
         contextPaint =
             new SimpleTextContextPaint(fillPattern, nullptr,
@@ -2220,71 +2214,35 @@ gfxFont::Draw(const gfxTextRun *aTextRun
     bool oldSubpixelAA = aRunParams.dt->GetPermitSubpixelAA();
     if (!AllowSubpixelAA()) {
         aRunParams.dt->SetPermitSubpixelAA(false);
     }
 
     Matrix mat;
     Matrix oldMat = aRunParams.dt->GetTransform();
 
-    // This is nullptr when we have inverse-transformed glyphs and we need
-    // to transform the Brush inside flush.
-    fontParams.passedInvMatrix = nullptr;
-
     fontParams.drawOptions.mAntialiasMode = Get2DAAMode(mAntialiasOption);
 
-    // The cairo DrawTarget backend uses the cairo_scaled_font directly
-    // and so has the font skew matrix applied already.
-    if (mScaledFont &&
-        aRunParams.dt->GetBackendType() != BackendType::CAIRO) {
-        cairo_matrix_t matrix;
-        cairo_scaled_font_get_font_matrix(mScaledFont, &matrix);
-        if (matrix.xy != 0) {
-            if (textDrawer) {
-                textDrawer->FoundUnsupportedFeature();
-            }
-
-            // If this matrix applies a skew, which can happen when drawing
-            // oblique fonts, we will set the DrawTarget matrix to apply the
-            // skew. We'll need to move the glyphs by the inverse of the skew to
-            // get the glyphs positioned correctly in the new device space
-            // though, since the font matrix should only be applied to drawing
-            // the glyphs, and not to their position.
-            mat = Matrix(matrix.xx, matrix.yx,
-                         matrix.xy, matrix.yy,
-                         matrix.x0, matrix.y0);
-
-            mat._11 = mat._22 = 1.0;
-            mat._21 /= GetAdjustedSize();
-
-            aRunParams.dt->SetTransform(mat * oldMat);
-
-            fontParams.matInv = mat;
-            fontParams.matInv.Invert();
-
-            fontParams.passedInvMatrix = &fontParams.matInv;
-        }
-    }
-
     float& baseline = fontParams.isVerticalFont ? aPt->x : aPt->y;
     float origBaseline = baseline;
     if (mStyle.baselineOffset != 0.0) {
         baseline +=
             mStyle.baselineOffset * aTextRun->GetAppUnitsPerDevUnit();
     }
 
     bool emittedGlyphs;
     {
         // Select appropriate version of the templated DrawGlyphs method
         // to output glyphs to the buffer, depending on complexity needed
         // for the type of font, and whether added inter-glyph spacing
         // is specified.
         GlyphBufferAzure buffer(aRunParams, fontParams);
         if (fontParams.haveSVGGlyphs || fontParams.haveColorGlyphs ||
-            fontParams.extraStrikes) {
+            fontParams.extraStrikes ||
+            (fontParams.needsOblique && fontParams.isVerticalFont)) {
             if (aRunParams.spacing) {
                 emittedGlyphs =
                     DrawGlyphs<FontComplexityT::ComplexFont,
                                SpacingT::HasSpacing>(aTextRun, aStart,
                                                      aEnd - aStart, aPt,
                                                      buffer);
             } else {
                 emittedGlyphs =
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -56,21 +56,21 @@ class gfxMathTable;
 
 #define NO_FONT_LANGUAGE_OVERRIDE      0
 
 #define SMALL_CAPS_SCALE_FACTOR        0.8
 
 // The skew factor used for synthetic-italic [oblique] fonts;
 // we use a platform-dependent value to harmonize with the platform's own APIs.
 #ifdef XP_WIN
-#define OBLIQUE_SKEW_FACTOR  0.3
+#define OBLIQUE_SKEW_FACTOR  0.3f
 #elif defined(MOZ_WIDGET_GTK)
-#define OBLIQUE_SKEW_FACTOR  0.2
+#define OBLIQUE_SKEW_FACTOR  0.2f
 #else
-#define OBLIQUE_SKEW_FACTOR  0.25
+#define OBLIQUE_SKEW_FACTOR  0.25f
 #endif
 
 struct gfxTextRunDrawCallbacks;
 
 namespace mozilla {
 class SVGContextPaint;
 } // namespace mozilla
 
@@ -2333,24 +2333,23 @@ struct MOZ_STACK_CLASS TextRunDrawParams
     bool                     isVerticalRun;
     bool                     isRTL;
     bool                     paintSVGGlyphs;
 };
 
 struct MOZ_STACK_CLASS FontDrawParams {
     RefPtr<mozilla::gfx::ScaledFont>            scaledFont;
     mozilla::SVGContextPaint *contextPaint;
-    mozilla::gfx::Matrix     *passedInvMatrix;
-    mozilla::gfx::Matrix      matInv;
     mozilla::gfx::Float       synBoldOnePixelOffset;
     int32_t                   extraStrikes;
     mozilla::gfx::DrawOptions drawOptions;
     bool                      isVerticalFont;
     bool                      haveSVGGlyphs;
     bool                      haveColorGlyphs;
+    bool                      needsOblique;
 };
 
 struct MOZ_STACK_CLASS EmphasisMarkDrawParams {
     gfxContext* context;
     gfxFont::Spacing* spacing;
     gfxTextRun* mark;
     gfxFloat advance;
     gfxFloat direction;
--- a/gfx/thebes/gfxGDIFont.cpp
+++ b/gfx/thebes/gfxGDIFont.cpp
@@ -416,31 +416,16 @@ gfxGDIFont::Initialize()
 
     mFontFace = cairo_win32_font_face_create_for_logfontw_hfont(&logFont,
                                                                 mFont);
 
     cairo_matrix_t sizeMatrix, ctm;
     cairo_matrix_init_identity(&ctm);
     cairo_matrix_init_scale(&sizeMatrix, mAdjustedSize, mAdjustedSize);
 
-    if (useCairoFakeItalic) {
-        // Skew the matrix to do fake italic if it wasn't already applied
-        // via the LOGFONT
-        double skewfactor = OBLIQUE_SKEW_FACTOR;
-        cairo_matrix_t style;
-        cairo_matrix_init(&style,
-                          1,                //xx
-                          0,                //yx
-                          -1 * skewfactor,  //xy
-                          1,                //yy
-                          0,                //x0
-                          0);               //y0
-        cairo_matrix_multiply(&sizeMatrix, &sizeMatrix, &style);
-    }
-
     cairo_font_options_t *fontOptions = cairo_font_options_create();
     if (mAntialiasOption != kAntialiasDefault) {
         cairo_font_options_set_antialias(fontOptions,
             GetCairoAntialiasOption(mAntialiasOption));
     }
     mScaledFont = cairo_scaled_font_create(mFontFace, &sizeMatrix,
                                            &ctm, fontOptions);
     cairo_font_options_destroy(fontOptions);
--- a/gfx/thebes/gfxMacFont.cpp
+++ b/gfx/thebes/gfxMacFont.cpp
@@ -80,34 +80,16 @@ gfxMacFont::gfxMacFont(const RefPtr<Unsc
 #endif
         return;
     }
 
     cairo_matrix_t sizeMatrix, ctm;
     cairo_matrix_init_identity(&ctm);
     cairo_matrix_init_scale(&sizeMatrix, mAdjustedSize, mAdjustedSize);
 
-    // synthetic oblique by skewing via the font matrix
-    bool needsOblique = mFontEntry != nullptr &&
-                        mFontEntry->IsUpright() &&
-                        mStyle.style != NS_FONT_STYLE_NORMAL &&
-                        mStyle.allowSyntheticStyle;
-
-    if (needsOblique) {
-        cairo_matrix_t style;
-        cairo_matrix_init(&style,
-                          1,                //xx
-                          0,                //yx
-                          -1 * OBLIQUE_SKEW_FACTOR, //xy
-                          1,                //yy
-                          0,                //x0
-                          0);               //y0
-        cairo_matrix_multiply(&sizeMatrix, &sizeMatrix, &style);
-    }
-
     cairo_font_options_t *fontOptions = cairo_font_options_create();
 
     // turn off font anti-aliasing based on user pref setting
     if (mAdjustedSize <=
         (gfxFloat)gfxPlatformMac::GetPlatform()->GetAntiAliasingThreshold()) {
         cairo_font_options_set_antialias(fontOptions, CAIRO_ANTIALIAS_NONE);
         mAntialiasOption = kAntialiasNone;
     } else if (mStyle.useGrayscaleAntialiasing) {
--- a/gfx/thebes/gfxMacPlatformFontList.mm
+++ b/gfx/thebes/gfxMacPlatformFontList.mm
@@ -1049,17 +1049,19 @@ gfxMacPlatformFontList::AddFamily(const 
 
 void
 gfxMacPlatformFontList::AddFamily(CFStringRef aFamily)
 {
     NSString* family = (NSString*)aFamily;
 
     // CTFontManager includes weird internal family names and
     // LastResort, skip over those
-    if (!family || [family caseInsensitiveCompare:@"LastResort"] == NSOrderedSame) {
+    if (!family ||
+        [family caseInsensitiveCompare:@"LastResort"] == NSOrderedSame ||
+        [family caseInsensitiveCompare:@".LastResort"] == NSOrderedSame) {
         return;
     }
 
     nsAutoString familyName;
     nsCocoaUtils::GetStringForNSString(family, familyName);
 
     bool isHiddenSystemFont = familyName[0] == '.';
     AddFamily(familyName, isHiddenSystemFont);
@@ -1396,16 +1398,18 @@ gfxMacPlatformFontList::PlatformGlobalFo
                                        ::CFRangeMake(0, length));
 
     if (fallback) {
         CFStringRef familyNameRef = ::CTFontCopyFamilyName(fallback);
         ::CFRelease(fallback);
 
         if (familyNameRef &&
             ::CFStringCompare(familyNameRef, CFSTR("LastResort"),
+                              kCFCompareCaseInsensitive) != kCFCompareEqualTo &&
+            ::CFStringCompare(familyNameRef, CFSTR(".LastResort"),
                               kCFCompareCaseInsensitive) != kCFCompareEqualTo)
         {
             AutoTArray<UniChar, 1024> buffer;
             CFIndex familyNameLen = ::CFStringGetLength(familyNameRef);
             buffer.SetLength(familyNameLen+1);
             ::CFStringGetCharacters(familyNameRef, ::CFRangeMake(0, familyNameLen),
                                     buffer.Elements());
             buffer[familyNameLen] = 0;
--- a/image/ClippedImage.cpp
+++ b/image/ClippedImage.cpp
@@ -351,16 +351,47 @@ ClippedImage::GetImageContainer(LayerMan
 
   if (!ShouldClip()) {
     return InnerImage()->GetImageContainer(aManager, aFlags);
   }
 
   return nullptr;
 }
 
+NS_IMETHODIMP_(bool)
+ClippedImage::IsImageContainerAvailableAtSize(LayerManager* aManager,
+                                              const IntSize& aSize,
+                                              uint32_t aFlags)
+{
+  if (!ShouldClip()) {
+    return InnerImage()->IsImageContainerAvailableAtSize(aManager, aSize, aFlags);
+  }
+  return false;
+}
+
+NS_IMETHODIMP_(already_AddRefed<ImageContainer>)
+ClippedImage::GetImageContainerAtSize(LayerManager* aManager,
+                                      const IntSize& aSize,
+                                      const Maybe<SVGImageContext>& aSVGContext,
+                                      uint32_t aFlags)
+{
+  // XXX(seth): We currently don't have a way of clipping the result of
+  // GetImageContainer. We work around this by always returning null, but if it
+  // ever turns out that ClippedImage is widely used on codepaths that can
+  // actually benefit from GetImageContainer, it would be a good idea to fix
+  // that method for performance reasons.
+
+  if (!ShouldClip()) {
+    return InnerImage()->GetImageContainerAtSize(aManager, aSize,
+                                                 aSVGContext, aFlags);
+  }
+
+  return nullptr;
+}
+
 static bool
 MustCreateSurface(gfxContext* aContext,
                   const nsIntSize& aSize,
                   const ImageRegion& aRegion,
                   const uint32_t aFlags)
 {
   gfxRect imageRect(0, 0, aSize.width, aSize.height);
   bool willTile = !imageRect.Contains(aRegion.Rect()) &&
--- a/image/ClippedImage.h
+++ b/image/ClippedImage.h
@@ -42,16 +42,25 @@ public:
     GetFrameAtSize(const gfx::IntSize& aSize,
                    uint32_t aWhichFrame,
                    uint32_t aFlags) override;
   NS_IMETHOD_(bool) IsImageContainerAvailable(layers::LayerManager* aManager,
                                               uint32_t aFlags) override;
   NS_IMETHOD_(already_AddRefed<layers::ImageContainer>)
     GetImageContainer(layers::LayerManager* aManager,
                       uint32_t aFlags) override;
+  NS_IMETHOD_(bool)
+    IsImageContainerAvailableAtSize(layers::LayerManager* aManager,
+                                    const gfx::IntSize& aSize,
+                                    uint32_t aFlags) override;
+  NS_IMETHOD_(already_AddRefed<layers::ImageContainer>)
+    GetImageContainerAtSize(layers::LayerManager* aManager,
+                            const gfx::IntSize& aSize,
+                            const Maybe<SVGImageContext>& aSVGContext,
+                            uint32_t aFlags) override;
   NS_IMETHOD_(DrawResult) Draw(gfxContext* aContext,
                                const nsIntSize& aSize,
                                const ImageRegion& aRegion,
                                uint32_t aWhichFrame,
                                gfx::SamplingFilter aSamplingFilter,
                                const Maybe<SVGImageContext>& aSVGContext,
                                uint32_t aFlags,
                                float aOpacity) override;
--- a/image/DynamicImage.cpp
+++ b/image/DynamicImage.cpp
@@ -221,16 +221,33 @@ DynamicImage::IsImageContainerAvailable(
 }
 
 NS_IMETHODIMP_(already_AddRefed<ImageContainer>)
 DynamicImage::GetImageContainer(LayerManager* aManager, uint32_t aFlags)
 {
   return nullptr;
 }
 
+NS_IMETHODIMP_(bool)
+DynamicImage::IsImageContainerAvailableAtSize(LayerManager* aManager,
+                                              const IntSize& aSize,
+                                              uint32_t aFlags)
+{
+  return false;
+}
+
+NS_IMETHODIMP_(already_AddRefed<ImageContainer>)
+DynamicImage::GetImageContainerAtSize(LayerManager* aManager,
+                                      const IntSize& aSize,
+                                      const Maybe<SVGImageContext>& aSVGContext,
+                                      uint32_t aFlags)
+{
+  return nullptr;
+}
+
 NS_IMETHODIMP_(DrawResult)
 DynamicImage::Draw(gfxContext* aContext,
                    const nsIntSize& aSize,
                    const ImageRegion& aRegion,
                    uint32_t aWhichFrame,
                    SamplingFilter aSamplingFilter,
                    const Maybe<SVGImageContext>& aSVGContext,
                    uint32_t aFlags,
--- a/image/FrozenImage.cpp
+++ b/image/FrozenImage.cpp
@@ -66,16 +66,38 @@ FrozenImage::GetImageContainer(layers::L
   // XXX(seth): GetImageContainer does not currently support anything but the
   // current frame. We work around this by always returning null, but if it ever
   // turns out that FrozenImage is widely used on codepaths that can actually
   // benefit from GetImageContainer, it would be a good idea to fix that method
   // for performance reasons.
   return nullptr;
 }
 
+NS_IMETHODIMP_(bool)
+FrozenImage::IsImageContainerAvailableAtSize(LayerManager* aManager,
+                                             const IntSize& aSize,
+                                             uint32_t aFlags)
+{
+  return false;
+}
+
+NS_IMETHODIMP_(already_AddRefed<ImageContainer>)
+FrozenImage::GetImageContainerAtSize(layers::LayerManager* aManager,
+                                     const IntSize& aSize,
+                                     const Maybe<SVGImageContext>& aSVGContext,
+                                     uint32_t aFlags)
+{
+  // XXX(seth): GetImageContainer does not currently support anything but the
+  // current frame. We work around this by always returning null, but if it ever
+  // turns out that FrozenImage is widely used on codepaths that can actually
+  // benefit from GetImageContainer, it would be a good idea to fix that method
+  // for performance reasons.
+  return nullptr;
+}
+
 NS_IMETHODIMP_(DrawResult)
 FrozenImage::Draw(gfxContext* aContext,
                   const nsIntSize& aSize,
                   const ImageRegion& aRegion,
                   uint32_t /* aWhichFrame - ignored */,
                   SamplingFilter aSamplingFilter,
                   const Maybe<SVGImageContext>& aSVGContext,
                   uint32_t aFlags,
--- a/image/FrozenImage.h
+++ b/image/FrozenImage.h
@@ -41,16 +41,25 @@ public:
     GetFrameAtSize(const gfx::IntSize& aSize,
                    uint32_t aWhichFrame,
                    uint32_t aFlags) override;
   NS_IMETHOD_(bool) IsImageContainerAvailable(layers::LayerManager* aManager,
                                               uint32_t aFlags) override;
   NS_IMETHOD_(already_AddRefed<layers::ImageContainer>)
     GetImageContainer(layers::LayerManager* aManager,
                       uint32_t aFlags) override;
+  NS_IMETHOD_(bool)
+    IsImageContainerAvailableAtSize(layers::LayerManager* aManager,
+                                    const gfx::IntSize& aSize,
+                                    uint32_t aFlags) override;
+  NS_IMETHOD_(already_AddRefed<layers::ImageContainer>)
+    GetImageContainerAtSize(layers::LayerManager* aManager,
+                            const gfx::IntSize& aSize,
+                            const Maybe<SVGImageContext>& aSVGContext,
+                            uint32_t aFlags) override;
   NS_IMETHOD_(DrawResult) Draw(gfxContext* aContext,
                                const nsIntSize& aSize,
                                const ImageRegion& aRegion,
                                uint32_t aWhichFrame,
                                gfx::SamplingFilter aSamplingFilter,
                                const Maybe<SVGImageContext>& aSVGContext,
                                uint32_t aFlags,
                                float aOpacity) override;
--- a/image/Image.cpp
+++ b/image/Image.cpp
@@ -1,16 +1,18 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "Image.h"
+#include "Layers.h"               // for LayerManager
 #include "nsRefreshDriver.h"
 #include "mozilla/TimeStamp.h"
+#include "mozilla/Tuple.h"        // for Tie
 
 namespace mozilla {
 namespace image {
 
 ///////////////////////////////////////////////////////////////////////////////
 // Memory Reporting
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -45,25 +47,241 @@ ImageMemoryCounter::ImageMemoryCounter(I
   }
 }
 
 
 ///////////////////////////////////////////////////////////////////////////////
 // Image Base Types
 ///////////////////////////////////////////////////////////////////////////////
 
+void
+ImageResource::SetCurrentImage(ImageContainer* aContainer,
+                               SourceSurface* aSurface,
+                               bool aInTransaction)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aContainer);
+
+  if (!aSurface) {
+    // The OS threw out some or all of our buffer. We'll need to wait for the
+    // redecode (which was automatically triggered by GetFrame) to complete.
+    return;
+  }
+
+  // |image| holds a reference to a SourceSurface which in turn holds a lock on
+  // the current frame's data buffer, ensuring that it doesn't get freed as
+  // long as the layer system keeps this ImageContainer alive.
+  RefPtr<layers::Image> image = new layers::SourceSurfaceImage(aSurface);
+
+  // We can share the producer ID with other containers because it is only
+  // used internally to validate the frames given to a particular container
+  // so that another object cannot add its own. Similarly the frame ID is
+  // only used internally to ensure it is always increasing, and skipping
+  // IDs from an individual container's perspective is acceptable.
+  AutoTArray<ImageContainer::NonOwningImage, 1> imageList;
+  imageList.AppendElement(ImageContainer::NonOwningImage(image, TimeStamp(),
+                                                         mLastFrameID++,
+                                                         mImageProducerID));
+
+  if (aInTransaction) {
+    aContainer->SetCurrentImagesInTransaction(imageList);
+  } else {
+    aContainer->SetCurrentImages(imageList);
+  }
+}
+
+already_AddRefed<ImageContainer>
+ImageResource::GetImageContainerImpl(LayerManager* aManager,
+                                     const IntSize& aSize,
+                                     const Maybe<SVGImageContext>& aSVGContext,
+                                     uint32_t aFlags)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aManager);
+  MOZ_ASSERT((aFlags & ~(FLAG_SYNC_DECODE |
+                         FLAG_SYNC_DECODE_IF_FAST |
+                         FLAG_ASYNC_NOTIFY |
+                         FLAG_HIGH_QUALITY_SCALING))
+               == FLAG_NONE,
+             "Unsupported flag passed to GetImageContainer");
+
+  IntSize size = GetImageContainerSize(aManager, aSize, aFlags);
+  if (size.IsEmpty()) {
+    return nullptr;
+  }
+
+  if (mAnimationConsumers == 0) {
+    SendOnUnlockedDraw(aFlags);
+  }
+
+  uint32_t flags = (aFlags & ~(FLAG_SYNC_DECODE |
+                               FLAG_SYNC_DECODE_IF_FAST)) | FLAG_ASYNC_NOTIFY;
+  RefPtr<layers::ImageContainer> container;
+  ImageContainerEntry* entry = nullptr;
+  int i = mImageContainers.Length() - 1;
+  for (; i >= 0; --i) {
+    entry = &mImageContainers[i];
+    container = entry->mContainer.get();
+    if (size == entry->mSize && flags == entry->mFlags &&
+        aSVGContext == entry->mSVGContext) {
+      // Lack of a container is handled below.
+      break;
+    } else if (!container) {
+      // Stop tracking if our weak pointer to the image container was freed.
+      mImageContainers.RemoveElementAt(i);
+    } else {
+      // It isn't a match, but still valid. Forget the container so we don't
+      // try to reuse it below.
+      container = nullptr;
+    }
+  }
+
+  if (container) {
+    switch (entry->mLastDrawResult) {
+      case DrawResult::SUCCESS:
+      case DrawResult::BAD_IMAGE:
+      case DrawResult::BAD_ARGS:
+        return container.forget();
+      case DrawResult::NOT_READY:
+      case DrawResult::INCOMPLETE:
+      case DrawResult::TEMPORARY_ERROR:
+        // Temporary conditions where we need to rerequest the frame to recover.
+        break;
+      case DrawResult::WRONG_SIZE:
+        // Unused by GetFrameInternal
+      default:
+        MOZ_ASSERT_UNREACHABLE("Unhandled DrawResult type!");
+        return container.forget();
+    }
+  }
+
+#ifdef DEBUG
+  NotifyDrawingObservers();
+#endif
+
+  DrawResult drawResult;
+  IntSize bestSize;
+  RefPtr<SourceSurface> surface;
+  Tie(drawResult, bestSize, surface) =
+    GetFrameInternal(size, aSVGContext, FRAME_CURRENT,
+                     aFlags | FLAG_ASYNC_NOTIFY);
+
+  // The requested size might be refused by the surface cache (i.e. due to
+  // factor-of-2 mode). In that case we don't want to create an entry for this
+  // specific size, but rather re-use the entry for the substituted size.
+  if (bestSize != size) {
+    MOZ_ASSERT(!bestSize.IsEmpty());
+
+    // We can only remove the entry if we no longer have a container, because if
+    // there are strong references to it remaining, we need to still update it
+    // in UpdateImageContainer.
+    if (i >= 0 && !container) {
+      mImageContainers.RemoveElementAt(i);
+    }
+
+    // Forget about the stale container, if any. This lets the entry creation
+    // logic do its job below, if it turns out there is no existing best entry
+    // or the best entry doesn't have a container.
+    container = nullptr;
+
+    // We need to do the entry search again for the new size. We skip pruning
+    // because we did this above once already, but ImageContainer is threadsafe,
+    // so there is a remote possibility it got freed.
+    i = mImageContainers.Length() - 1;
+    for (; i >= 0; --i) {
+      entry = &mImageContainers[i];
+      if (bestSize == entry->mSize && flags == entry->mFlags &&
+          aSVGContext == entry->mSVGContext) {
+        container = entry->mContainer.get();
+        if (container) {
+          switch (entry->mLastDrawResult) {
+            case DrawResult::SUCCESS:
+            case DrawResult::BAD_IMAGE:
+            case DrawResult::BAD_ARGS:
+              return container.forget();
+            case DrawResult::NOT_READY:
+            case DrawResult::INCOMPLETE:
+            case DrawResult::TEMPORARY_ERROR:
+              // Temporary conditions where we need to rerequest the frame to
+              // recover. We have already done so!
+              break;
+           case DrawResult::WRONG_SIZE:
+              // Unused by GetFrameInternal
+            default:
+              MOZ_ASSERT_UNREACHABLE("Unhandled DrawResult type!");
+              return container.forget();
+          }
+        }
+        break;
+      }
+    }
+  }
+
+  if (!container) {
+    // We need a new ImageContainer, so create one.
+    container = LayerManager::CreateImageContainer();
+
+    if (i >= 0) {
+      entry->mContainer = container;
+    } else {
+      entry = mImageContainers.AppendElement(
+        ImageContainerEntry(bestSize, aSVGContext, container.get(), flags));
+    }
+  }
+
+  SetCurrentImage(container, surface, true);
+  entry->mLastDrawResult = drawResult;
+  return container.forget();
+}
+
+void
+ImageResource::UpdateImageContainer()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  for (int i = mImageContainers.Length() - 1; i >= 0; --i) {
+    ImageContainerEntry& entry = mImageContainers[i];
+    RefPtr<ImageContainer> container = entry.mContainer.get();
+    if (container) {
+      IntSize bestSize;
+      RefPtr<SourceSurface> surface;
+      Tie(entry.mLastDrawResult, bestSize, surface) =
+        GetFrameInternal(entry.mSize, entry.mSVGContext,
+                         FRAME_CURRENT, entry.mFlags);
+
+      // It is possible that this is a factor-of-2 substitution. Since we
+      // managed to convert the weak reference into a strong reference, that
+      // means that an imagelib user still is holding onto the container. thus
+      // we cannot consolidate and must keep updating the duplicate container.
+      SetCurrentImage(container, surface, false);
+    } else {
+      // Stop tracking if our weak pointer to the image container was freed.
+      mImageContainers.RemoveElementAt(i);
+    }
+  }
+}
+
+void
+ImageResource::ReleaseImageContainer()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  mImageContainers.Clear();
+}
+
 // Constructor
 ImageResource::ImageResource(ImageURL* aURI) :
   mURI(aURI),
   mInnerWindowId(0),
   mAnimationConsumers(0),
   mAnimationMode(kNormalAnimMode),
   mInitialized(false),
   mAnimating(false),
-  mError(false)
+  mError(false),
+  mImageProducerID(ImageContainer::AllocateProducerID()),
+  mLastFrameID(0)
 { }
 
 ImageResource::~ImageResource()
 {
   // Ask our ProgressTracker to drop its weak reference to us.
   mProgressTracker->ResetImage();
 }
 
@@ -164,10 +382,36 @@ ImageResource::SendOnUnlockedDraw(uint32
         if (tracker) {
           tracker->OnUnlockedDraw();
         }
       });
     eventTarget->Dispatch(ev.forget(), NS_DISPATCH_NORMAL);
   }
 }
 
+#ifdef DEBUG
+void
+ImageResource::NotifyDrawingObservers()
+{
+  if (!mURI || !NS_IsMainThread()) {
+    return;
+  }
+
+  bool match = false;
+  if ((NS_FAILED(mURI->SchemeIs("resource", &match)) || !match) &&
+      (NS_FAILED(mURI->SchemeIs("chrome", &match)) || !match)) {
+    return;
+  }
+
+  // Record the image drawing for startup performance testing.
+  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+  NS_WARNING_ASSERTION(obs, "Can't get an observer service handle");
+  if (obs) {
+    nsCOMPtr<nsIURI> imageURI = mURI->ToIURI();
+    nsAutoCString spec;
+    imageURI->GetSpec(spec);
+    obs->NotifyObservers(nullptr, "image-drawing", NS_ConvertUTF8toUTF16(spec).get());
+  }
+}
+#endif
+
 } // namespace image
 } // namespace mozilla
--- a/image/Image.h
+++ b/image/Image.h
@@ -2,20 +2,23 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_image_Image_h
 #define mozilla_image_Image_h
 
 #include "mozilla/MemoryReporting.h"
+#include "mozilla/Tuple.h"
 #include "mozilla/TimeStamp.h"
 #include "gfx2DGlue.h"
 #include "imgIContainer.h"
 #include "ImageURL.h"
+#include "ImageContainer.h"
+#include "LookupResult.h"
 #include "nsStringFwd.h"
 #include "ProgressTracker.h"
 #include "SurfaceCache.h"
 
 class nsIRequest;
 class nsIInputStream;
 
 namespace mozilla {
@@ -313,24 +316,96 @@ protected:
     return mAnimationConsumers > 0 && mAnimationMode != kDontAnimMode;
   }
 
   virtual nsresult StartAnimation() = 0;
   virtual nsresult StopAnimation() = 0;
 
   void SendOnUnlockedDraw(uint32_t aFlags);
 
+#ifdef DEBUG
+  // Records the image drawing for startup performance testing.
+  void NotifyDrawingObservers();
+#endif
+
   // Member data shared by all implementations of this abstract class
   RefPtr<ProgressTracker>     mProgressTracker;
   RefPtr<ImageURL>            mURI;
   TimeStamp                     mLastRefreshTime;
   uint64_t                      mInnerWindowId;
   uint32_t                      mAnimationConsumers;
   uint16_t                      mAnimationMode; // Enum values in imgIContainer
   bool                          mInitialized:1; // Have we been initalized?
   bool                          mAnimating:1;   // Are we currently animating?
   bool                          mError:1;       // Error handling
+
+  virtual Tuple<DrawResult, gfx::IntSize, RefPtr<gfx::SourceSurface>>
+    GetFrameInternal(const gfx::IntSize& aSize,
+                     const Maybe<SVGImageContext>& aSVGContext,
+                     uint32_t aWhichFrame,
+                     uint32_t aFlags)
+  {
+    return MakeTuple(DrawResult::BAD_IMAGE, aSize,
+                     RefPtr<gfx::SourceSurface>());
+  }
+
+  /**
+   * Calculate the estimated size to use for an image container with the given
+   * parameters. It may not be the same as the given size, and it may not be
+   * the same as the size of the surface in the image container, but it is the
+   * best effort estimate.
+   */
+  virtual gfx::IntSize GetImageContainerSize(layers::LayerManager* aManager,
+                                             const gfx::IntSize& aSize,
+                                             uint32_t aFlags)
+  {
+    return gfx::IntSize(0, 0);
+  }
+
+  already_AddRefed<layers::ImageContainer>
+    GetImageContainerImpl(layers::LayerManager* aManager,
+                          const gfx::IntSize& aSize,
+                          const Maybe<SVGImageContext>& aSVGContext,
+                          uint32_t aFlags);
+
+  void UpdateImageContainer();
+
+  void ReleaseImageContainer();
+
+private:
+  void SetCurrentImage(layers::ImageContainer* aContainer,
+                       gfx::SourceSurface* aSurface,
+                       bool aInTransaction);
+
+  struct ImageContainerEntry {
+    ImageContainerEntry(const gfx::IntSize& aSize,
+                        const Maybe<SVGImageContext>& aSVGContext,
+                        layers::ImageContainer* aContainer,
+                        uint32_t aFlags)
+      : mSize(aSize)
+      , mSVGContext(aSVGContext)
+      , mContainer(aContainer)
+      , mLastDrawResult(DrawResult::NOT_READY)
+      , mFlags(aFlags)
+    { }
+
+    gfx::IntSize                        mSize;
+    Maybe<SVGImageContext>              mSVGContext;
+    // A weak pointer to our ImageContainer, which stays alive only as long as
+    // the layer system needs it.
+    WeakPtr<layers::ImageContainer>     mContainer;
+    // If mContainer is non-null, this contains the DrawResult we obtained
+    // the last time we updated it.
+    DrawResult                          mLastDrawResult;
+    // Cached flags to use for decoding. FLAG_ASYNC_NOTIFY should always be set
+    // but FLAG_HIGH_QUALITY_SCALING may vary.
+    uint32_t                            mFlags;
+  };
+
+  AutoTArray<ImageContainerEntry, 1> mImageContainers;
+  layers::ImageContainer::ProducerID mImageProducerID;
+  layers::ImageContainer::FrameID mLastFrameID;
 };
 
 } // namespace image
 } // namespace mozilla
 
 #endif // mozilla_image_Image_h
--- a/image/ImageWrapper.cpp
+++ b/image/ImageWrapper.cpp
@@ -209,16 +209,34 @@ ImageWrapper::IsImageContainerAvailable(
 }
 
 NS_IMETHODIMP_(already_AddRefed<ImageContainer>)
 ImageWrapper::GetImageContainer(LayerManager* aManager, uint32_t aFlags)
 {
   return mInnerImage->GetImageContainer(aManager, aFlags);
 }
 
+NS_IMETHODIMP_(bool)
+ImageWrapper::IsImageContainerAvailableAtSize(LayerManager* aManager,
+                                              const IntSize& aSize,
+                                              uint32_t aFlags)
+{
+  return mInnerImage->IsImageContainerAvailableAtSize(aManager, aSize, aFlags);
+}
+
+NS_IMETHODIMP_(already_AddRefed<ImageContainer>)
+ImageWrapper::GetImageContainerAtSize(LayerManager* aManager,
+                                      const IntSize& aSize,
+                                      const Maybe<SVGImageContext>& aSVGContext,
+                                      uint32_t aFlags)
+{
+  return mInnerImage->GetImageContainerAtSize(aManager, aSize,
+                                              aSVGContext, aFlags);
+}
+
 NS_IMETHODIMP_(DrawResult)
 ImageWrapper::Draw(gfxContext* aContext,
                    const nsIntSize& aSize,
                    const ImageRegion& aRegion,
                    uint32_t aWhichFrame,
                    SamplingFilter aSamplingFilter,
                    const Maybe<SVGImageContext>& aSVGContext,
                    uint32_t aFlags,
--- a/image/LookupResult.h
+++ b/image/LookupResult.h
@@ -49,16 +49,17 @@ public:
     MOZ_ASSERT(mMatchType == MatchType::NOT_FOUND ||
                mMatchType == MatchType::PENDING,
                "Only NOT_FOUND or PENDING make sense with no surface");
   }
 
   LookupResult(LookupResult&& aOther)
     : mSurface(Move(aOther.mSurface))
     , mMatchType(aOther.mMatchType)
+    , mSuggestedSize(aOther.mSuggestedSize)
   { }
 
   LookupResult(DrawableSurface&& aSurface, MatchType aMatchType)
     : mSurface(Move(aSurface))
     , mMatchType(aMatchType)
   {
     MOZ_ASSERT(!mSurface || !(mMatchType == MatchType::NOT_FOUND ||
                               mMatchType == MatchType::PENDING),
@@ -69,21 +70,22 @@ public:
   }
 
   LookupResult(DrawableSurface&& aSurface, MatchType aMatchType,
                const gfx::IntSize& aSuggestedSize)
     : mSurface(Move(aSurface))
     , mMatchType(aMatchType)
     , mSuggestedSize(aSuggestedSize)
   {
-    MOZ_ASSERT(!mSuggestedSize.IsEmpty());
-    MOZ_ASSERT(!mSurface || aMatchType == MatchType::SUBSTITUTE_BECAUSE_NOT_FOUND,
-               "Only SUBSTITUTE_BECAUSE_NOT_FOUND make sense with no surface");
-    MOZ_ASSERT(mSurface || aMatchType == MatchType::NOT_FOUND,
-               "NOT_FOUND does not make sense with a surface");
+    MOZ_ASSERT(!mSurface || !(mMatchType == MatchType::NOT_FOUND ||
+                              mMatchType == MatchType::PENDING),
+               "Only NOT_FOUND or PENDING make sense with no surface");
+    MOZ_ASSERT(mSurface || mMatchType == MatchType::NOT_FOUND ||
+                           mMatchType == MatchType::PENDING,
+               "NOT_FOUND or PENDING do not make sense with a surface");
   }
 
   LookupResult& operator=(LookupResult&& aOther)
   {
     MOZ_ASSERT(&aOther != this, "Self-move-assignment is not supported");
     mSurface = Move(aOther.mSurface);
     mMatchType = aOther.mMatchType;
     mSuggestedSize = aOther.mSuggestedSize;
@@ -97,16 +99,17 @@ public:
   /// @return true if this LookupResult contains a surface.
   explicit operator bool() const { return bool(mSurface); }
 
   /// @return what kind of match this is (exact, substitute, etc.)
   MatchType Type() const { return mMatchType; }
 
 private:
   LookupResult(const LookupResult&) = delete;
+  LookupResult& operator=(const LookupResult& aOther) = delete;
 
   DrawableSurface mSurface;
   MatchType mMatchType;
 
   /// If given, the size the caller should request a decode at. This may or may
   /// not match the size the caller requested from the cache.
   gfx::IntSize mSuggestedSize;
 };
--- a/image/OrientedImage.cpp
+++ b/image/OrientedImage.cpp
@@ -168,16 +168,47 @@ OrientedImage::GetImageContainer(LayerMa
 
   if (mOrientation.IsIdentity()) {
     return InnerImage()->GetImageContainer(aManager, aFlags);
   }
 
   return nullptr;
 }
 
+NS_IMETHODIMP_(bool)
+OrientedImage::IsImageContainerAvailableAtSize(LayerManager* aManager,
+                                               const IntSize& aSize,
+                                               uint32_t aFlags)
+{
+  if (mOrientation.IsIdentity()) {
+    return InnerImage()->IsImageContainerAvailableAtSize(aManager, aSize, aFlags);
+  }
+  return false;
+}
+
+NS_IMETHODIMP_(already_AddRefed<ImageContainer>)
+OrientedImage::GetImageContainerAtSize(LayerManager* aManager,
+                                       const IntSize& aSize,
+                                       const Maybe<SVGImageContext>& aSVGContext,
+                                       uint32_t aFlags)
+{
+  // XXX(seth): We currently don't have a way of orienting the result of
+  // GetImageContainer. We work around this by always returning null, but if it
+  // ever turns out that OrientedImage is widely used on codepaths that can
+  // actually benefit from GetImageContainer, it would be a good idea to fix
+  // that method for performance reasons.
+
+  if (mOrientation.IsIdentity()) {
+    return InnerImage()->GetImageContainerAtSize(aManager, aSize,
+                                                 aSVGContext, aFlags);
+  }
+
+  return nullptr;
+}
+
 struct MatrixBuilder
 {
   explicit MatrixBuilder(bool aInvert) : mInvert(aInvert) { }
 
   gfxMatrix Build() { return mMatrix; }
 
   void Scale(gfxFloat aX, gfxFloat aY)
   {
--- a/image/OrientedImage.h
+++ b/image/OrientedImage.h
@@ -39,16 +39,25 @@ public:
     GetFrameAtSize(const gfx::IntSize& aSize,
                    uint32_t aWhichFrame,
                    uint32_t aFlags) override;
   NS_IMETHOD_(bool) IsImageContainerAvailable(layers::LayerManager* aManager,
                                               uint32_t aFlags) override;
   NS_IMETHOD_(already_AddRefed<layers::ImageContainer>)
     GetImageContainer(layers::LayerManager* aManager,
                       uint32_t aFlags) override;
+  NS_IMETHOD_(bool)
+    IsImageContainerAvailableAtSize(layers::LayerManager* aManager,
+                                    const gfx::IntSize& aSize,
+                                    uint32_t aFlags) override;
+  NS_IMETHOD_(already_AddRefed<layers::ImageContainer>)
+    GetImageContainerAtSize(layers::LayerManager* aManager,
+                            const gfx::IntSize& aSize,
+                            const Maybe<SVGImageContext>& aSVGContext,
+                            uint32_t aFlags) override;
   NS_IMETHOD_(DrawResult) Draw(gfxContext* aContext,
                                const nsIntSize& aSize,
                                const ImageRegion& aRegion,
                                uint32_t aWhichFrame,
                                gfx::SamplingFilter aSamplingFilter,
                                const Maybe<SVGImageContext>& aSVGContext,
                                uint32_t aFlags,
                                float aOpacity) override;
--- a/image/RasterImage.cpp
+++ b/image/RasterImage.cpp
@@ -11,17 +11,16 @@
 
 #include "gfxPlatform.h"
 #include "nsComponentManagerUtils.h"
 #include "nsError.h"
 #include "Decoder.h"
 #include "prenv.h"
 #include "prsystem.h"
 #include "IDecodingTask.h"
-#include "ImageContainer.h"
 #include "ImageRegion.h"
 #include "Layers.h"
 #include "LookupResult.h"
 #include "nsIConsoleService.h"
 #include "nsIInputStream.h"
 #include "nsIScriptError.h"
 #include "nsISupportsPrimitives.h"
 #include "nsPresContext.h"
@@ -68,19 +67,16 @@ NS_IMPL_ISUPPORTS(RasterImage, imgIConta
 #endif
 
 //******************************************************************************
 RasterImage::RasterImage(ImageURL* aURI /* = nullptr */) :
   ImageResource(aURI), // invoke superclass's constructor
   mSize(0,0),
   mLockCount(0),
   mDecodeCount(0),
-  mImageProducerID(ImageContainer::AllocateProducerID()),
-  mLastFrameID(0),
-  mLastImageContainerDrawResult(DrawResult::NOT_READY),
 #ifdef DEBUG
   mFramesNotified(0),
 #endif
   mSourceBuffer(MakeNotNull<SourceBuffer*>()),
   mHasSize(false),
   mTransient(false),
   mSyncLoad(false),
   mDiscardable(false),
@@ -323,41 +319,42 @@ RasterImage::LookupFrameInternal(const I
 
   // We'll return the best match we can find to the requested frame.
   return SurfaceCache::LookupBestMatch(ImageKey(this),
                                        RasterSurfaceKey(aSize,
                                                         surfaceFlags,
                                                         PlaybackType::eStatic));
 }
 
-DrawableSurface
+LookupResult
 RasterImage::LookupFrame(const IntSize& aSize,
                          uint32_t aFlags,
                          PlaybackType aPlaybackType)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // If we're opaque, we don't need to care about premultiplied alpha, because
   // that can only matter for frames with transparency.
   if (IsOpaque()) {
     aFlags &= ~FLAG_DECODE_NO_PREMULTIPLY_ALPHA;
   }
 
   IntSize requestedSize = CanDownscaleDuringDecode(aSize, aFlags)
                         ? aSize : mSize;
   if (requestedSize.IsEmpty()) {
-    return DrawableSurface();  // Can't decode to a surface of zero size.
+    // Can't decode to a surface of zero size.
+    return LookupResult(MatchType::NOT_FOUND);
   }
 
   LookupResult result =
     LookupFrameInternal(requestedSize, aFlags, aPlaybackType);
 
   if (!result && !mHasSize) {
     // We can't request a decode without knowing our intrinsic size. Give up.
-    return DrawableSurface();
+    return LookupResult(MatchType::NOT_FOUND);
   }
 
   if (result.Type() == MatchType::NOT_FOUND ||
       result.Type() == MatchType::SUBSTITUTE_BECAUSE_NOT_FOUND ||
       ((aFlags & FLAG_SYNC_DECODE) && !result)) {
     // We don't have a copy of this frame, and there's no decoder working on
     // one. (Or we're sync decoding and the existing decoder hasn't even started
     // yet.) Trigger decoding so it'll be available next time.
@@ -379,21 +376,22 @@ RasterImage::LookupFrame(const IntSize& 
     // If we can or did sync decode, we should already have the frame.
     if (ranSync || (aFlags & FLAG_SYNC_DECODE)) {
       result = LookupFrameInternal(requestedSize, aFlags, aPlaybackType);
     }
   }
 
   if (!result) {
     // We still weren't able to get a frame. Give up.
-    return DrawableSurface();
+    return result;
   }
 
   if (result.Surface()->GetCompositingFailed()) {
-    return DrawableSurface();
+    DrawableSurface tmp = Move(result.Surface());
+    return result;
   }
 
   MOZ_ASSERT(!result.Surface()->GetIsPaletted(),
              "Should not have a paletted frame");
 
   // Sync decoding guarantees that we got the frame, but if it's owned by an
   // async decoder that's currently running, the contents of the frame may not
   // be available yet. Make sure we get everything.
@@ -402,20 +400,21 @@ RasterImage::LookupFrame(const IntSize& 
   }
 
   // If we could have done some decoding in this function we need to check if
   // that decoding encountered an error and hence aborted the surface. We want
   // to avoid calling IsAborted if we weren't passed any sync decode flag because
   // IsAborted acquires the monitor for the imgFrame.
   if (aFlags & (FLAG_SYNC_DECODE | FLAG_SYNC_DECODE_IF_FAST) &&
     result.Surface()->IsAborted()) {
-    return DrawableSurface();
+    DrawableSurface tmp = Move(result.Surface());
+    return result;
   }
 
-  return Move(result.Surface());
+  return result;
 }
 
 bool
 RasterImage::IsOpaque()
 {
   if (mError) {
     return false;
   }
@@ -497,17 +496,17 @@ RasterImage::OnSurfaceDiscarded(const Su
 
 void
 RasterImage::OnSurfaceDiscardedInternal(bool aAnimatedFramesDiscarded)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (aAnimatedFramesDiscarded && mAnimationState) {
     MOZ_ASSERT(gfxPrefs::ImageMemAnimatedDiscardable());
-    mImageContainer = nullptr;
+    ReleaseImageContainer();
     gfx::IntRect rect =
       mAnimationState->UpdateState(mAnimationFinished, this, mSize);
     NotifyProgress(NoProgress, rect);
   }
 
   if (mProgressTracker) {
     mProgressTracker->OnDiscard();
   }
@@ -568,185 +567,129 @@ RasterImage::GetFrame(uint32_t aWhichFra
   return GetFrameAtSize(mSize, aWhichFrame, aFlags);
 }
 
 NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
 RasterImage::GetFrameAtSize(const IntSize& aSize,
                             uint32_t aWhichFrame,
                             uint32_t aFlags)
 {
-
 #ifdef DEBUG
   NotifyDrawingObservers();
 #endif
 
-  RefPtr<SourceSurface> surf =
-    GetFrameInternal(aSize, aWhichFrame, aFlags).second().forget();
+  auto result = GetFrameInternal(aSize, Nothing(), aWhichFrame, aFlags);
+  RefPtr<SourceSurface> surf = mozilla::Get<2>(result).forget();
+
   // If we are here, it suggests the image is embedded in a canvas or some
   // other path besides layers, and we won't need the file handle.
   MarkSurfaceShared(surf);
   return surf.forget();
 }
 
-Pair<DrawResult, RefPtr<SourceSurface>>
+Tuple<DrawResult, IntSize, RefPtr<SourceSurface>>
 RasterImage::GetFrameInternal(const IntSize& aSize,
+                              const Maybe<SVGImageContext>& aSVGContext,
                               uint32_t aWhichFrame,
                               uint32_t aFlags)
 {
   MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE);
 
-  if (aSize.IsEmpty()) {
-    return MakePair(DrawResult::BAD_ARGS, RefPtr<SourceSurface>());
-  }
-
-  if (aWhichFrame > FRAME_MAX_VALUE) {
-    return MakePair(DrawResult::BAD_ARGS, RefPtr<SourceSurface>());
+  if (aSize.IsEmpty() || aWhichFrame > FRAME_MAX_VALUE) {
+    return MakeTuple(DrawResult::BAD_ARGS, aSize,
+                     RefPtr<SourceSurface>());
   }
 
   if (mError) {
-    return MakePair(DrawResult::BAD_IMAGE, RefPtr<SourceSurface>());
+    return MakeTuple(DrawResult::BAD_IMAGE, aSize,
+                     RefPtr<SourceSurface>());
   }
 
   // Get the frame. If it's not there, it's probably the caller's fault for
   // not waiting for the data to be loaded from the network or not passing
   // FLAG_SYNC_DECODE.
-  DrawableSurface surface =
+  LookupResult result =
     LookupFrame(aSize, aFlags, ToPlaybackType(aWhichFrame));
-  if (!surface) {
+
+  // The surface cache may have suggested we use a different size than the
+  // given size in the future. This may or may not be accompanied by an
+  // actual surface, depending on what it has in its cache.
+  IntSize suggestedSize = result.SuggestedSize().IsEmpty()
+                          ? aSize : result.SuggestedSize();
+  MOZ_ASSERT_IF(result.Type() == MatchType::SUBSTITUTE_BECAUSE_BEST,
+                suggestedSize != aSize);
+
+  if (!result) {
     // The OS threw this frame away and we couldn't redecode it.
-    return MakePair(DrawResult::TEMPORARY_ERROR, RefPtr<SourceSurface>());
+    return MakeTuple(DrawResult::TEMPORARY_ERROR, suggestedSize,
+                     RefPtr<SourceSurface>());
   }
 
-  RefPtr<SourceSurface> sourceSurface = surface->GetSourceSurface();
-
-  if (!surface->IsFinished()) {
-    return MakePair(DrawResult::INCOMPLETE, Move(sourceSurface));
+  RefPtr<SourceSurface> surface = result.Surface()->GetSourceSurface();
+  if (!result.Surface()->IsFinished()) {
+    return MakeTuple(DrawResult::INCOMPLETE, suggestedSize, Move(surface));
   }
 
-  return MakePair(DrawResult::SUCCESS, Move(sourceSurface));
+  return MakeTuple(DrawResult::SUCCESS, suggestedSize, Move(surface));
 }
 
-Pair<DrawResult, RefPtr<layers::Image>>
-RasterImage::GetCurrentImage(ImageContainer* aContainer, uint32_t aFlags)
+IntSize
+RasterImage::GetImageContainerSize(LayerManager* aManager,
+                                   const IntSize& aSize,
+                                   uint32_t aFlags)
 {
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(aContainer);
-
-  DrawResult drawResult;
-  RefPtr<SourceSurface> surface;
-  Tie(drawResult, surface) =
-    GetFrameInternal(mSize, FRAME_CURRENT, aFlags | FLAG_ASYNC_NOTIFY);
-  if (!surface) {
-    // The OS threw out some or all of our buffer. We'll need to wait for the
-    // redecode (which was automatically triggered by GetFrame) to complete.
-    return MakePair(drawResult, RefPtr<layers::Image>());
+  if (!IsImageContainerAvailableAtSize(aManager, aSize, aFlags)) {
+    return IntSize(0, 0);
   }
 
-  RefPtr<layers::Image> image = new layers::SourceSurfaceImage(surface);
-  return MakePair(drawResult, Move(image));
+  if (!CanDownscaleDuringDecode(aSize, aFlags)) {
+    return mSize;
+  }
+
+  return aSize;
 }
 
 NS_IMETHODIMP_(bool)
 RasterImage::IsImageContainerAvailable(LayerManager* aManager, uint32_t aFlags)
 {
+  return IsImageContainerAvailableAtSize(aManager, mSize, aFlags);
+}
+
+NS_IMETHODIMP_(already_AddRefed<ImageContainer>)
+RasterImage::GetImageContainer(LayerManager* aManager, uint32_t aFlags)
+{
+  return GetImageContainerImpl(aManager, mSize, Nothing(), aFlags);
+}
+
+NS_IMETHODIMP_(bool)
+RasterImage::IsImageContainerAvailableAtSize(LayerManager* aManager,
+                                             const IntSize& aSize,
+                                             uint32_t aFlags)
+{
+  // We check the minimum size because while we support downscaling, we do not
+  // support upscaling. If aSize > mSize, we will never give a larger surface
+  // than mSize. If mSize > aSize, and mSize > maxTextureSize, we still want to
+  // use image containers if aSize <= maxTextureSize.
   int32_t maxTextureSize = aManager->GetMaxTextureSize();
-  if (!mHasSize ||
-      mSize.width > maxTextureSize ||
-      mSize.height > maxTextureSize) {
+  if (!mHasSize || aSize.IsEmpty() ||
+      min(mSize.width, aSize.width) > maxTextureSize ||
+      min(mSize.height, aSize.height) > maxTextureSize) {
     return false;
   }
 
   return true;
 }
 
 NS_IMETHODIMP_(already_AddRefed<ImageContainer>)
-RasterImage::GetImageContainer(LayerManager* aManager, uint32_t aFlags)
+RasterImage::GetImageContainerAtSize(LayerManager* aManager,
+                                     const IntSize& aSize,
+                                     const Maybe<SVGImageContext>& aSVGContext,
+                                     uint32_t aFlags)
 {
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(aManager);
-  MOZ_ASSERT((aFlags & ~(FLAG_SYNC_DECODE |
-                         FLAG_SYNC_DECODE_IF_FAST |
-                         FLAG_ASYNC_NOTIFY))
-               == FLAG_NONE,
-             "Unsupported flag passed to GetImageContainer");
-
-  int32_t maxTextureSize = aManager->GetMaxTextureSize();
-  if (!mHasSize ||
-      mSize.width > maxTextureSize ||
-      mSize.height > maxTextureSize) {
-    return nullptr;
-  }
-
-  if (mAnimationConsumers == 0) {
-    SendOnUnlockedDraw(aFlags);
-  }
-
-  RefPtr<layers::ImageContainer> container = mImageContainer.get();
-
-  bool mustRedecode =
-    (aFlags & (FLAG_SYNC_DECODE | FLAG_SYNC_DECODE_IF_FAST)) &&
-    mLastImageContainerDrawResult != DrawResult::SUCCESS &&
-    mLastImageContainerDrawResult != DrawResult::BAD_IMAGE;
-
-  if (container && !mustRedecode) {
-    return container.forget();
-  }
-
-  // We need a new ImageContainer, so create one.
-  container = LayerManager::CreateImageContainer();
-
-  DrawResult drawResult;
-  RefPtr<layers::Image> image;
-  Tie(drawResult, image) = GetCurrentImage(container, aFlags);
-  if (!image) {
-    return nullptr;
-  }
-
-#ifdef DEBUG
-  NotifyDrawingObservers();
-#endif
-
-  // |image| holds a reference to a SourceSurface which in turn holds a lock on
-  // the current frame's data buffer, ensuring that it doesn't get freed as
-  // long as the layer system keeps this ImageContainer alive.
-  AutoTArray<ImageContainer::NonOwningImage, 1> imageList;
-  imageList.AppendElement(ImageContainer::NonOwningImage(image, TimeStamp(),
-                                                         mLastFrameID++,
-                                                         mImageProducerID));
-  container->SetCurrentImagesInTransaction(imageList);
-
-  mLastImageContainerDrawResult = drawResult;
-  mImageContainer = container;
-
-  return container.forget();
-}
-
-void
-RasterImage::UpdateImageContainer()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  RefPtr<layers::ImageContainer> container = mImageContainer.get();
-  if (!container) {
-    return;
-  }
-
-  DrawResult drawResult;
-  RefPtr<layers::Image> image;
-  Tie(drawResult, image) = GetCurrentImage(container, FLAG_NONE);
-  if (!image) {
-    return;
-  }
-
-  mLastImageContainerDrawResult = drawResult;
-  AutoTArray<ImageContainer::NonOwningImage, 1> imageList;
-  imageList.AppendElement(ImageContainer::NonOwningImage(image, TimeStamp(),
-                                                         mLastFrameID++,
-                                                         mImageProducerID));
-  container->SetCurrentImages(imageList);
+  return GetImageContainerImpl(aManager, aSize, aSVGContext, aFlags);
 }
 
 size_t
 RasterImage::SizeOfSourceWithComputedFallback(SizeOfState& aState) const
 {
   return mSourceBuffer->SizeOfIncludingThisWithComputedFallback(
     aState.mMallocSizeOf);
 }
@@ -1122,17 +1065,17 @@ RasterImage::Discard()
   MOZ_ASSERT(CanDiscard(), "Asked to discard but can't");
   MOZ_ASSERT(!mAnimationState || gfxPrefs::ImageMemAnimatedDiscardable(),
     "Asked to discard for animated image");
 
   // Delete all the decoded frames.
   SurfaceCache::RemoveImage(ImageKey(this));
 
   if (mAnimationState) {
-    mImageContainer = nullptr;
+    ReleaseImageContainer();
     gfx::IntRect rect =
       mAnimationState->UpdateState(mAnimationFinished, this, mSize);
     NotifyProgress(NoProgress, rect);
   }
 
   // Notify that we discarded.
   if (mProgressTracker) {
     mProgressTracker->OnDiscard();
@@ -1213,18 +1156,20 @@ RasterImage::RequestDecodeForSizeInterna
   bool shouldSyncDecodeIfFast =
     !mHasBeenDecoded && (aFlags & FLAG_SYNC_DECODE_IF_FAST);
 
   uint32_t flags = shouldSyncDecodeIfFast
                  ? aFlags
                  : aFlags & ~FLAG_SYNC_DECODE_IF_FAST;
 
   // Perform a frame lookup, which will implicitly start decoding if needed.
-  return LookupFrame(aSize, flags, mAnimationState ? PlaybackType::eAnimated
-                                                   : PlaybackType::eStatic);
+  PlaybackType playbackType = mAnimationState ? PlaybackType::eAnimated
+                                              : PlaybackType::eStatic;
+  LookupResult result = LookupFrame(aSize, flags, playbackType);
+  return Move(result.Surface());
 }
 
 static bool
 LaunchDecodingTask(IDecodingTask* aTask,
                    RasterImage* aImage,
                    uint32_t aFlags,
                    bool aHaveSourceData)
 {
@@ -1507,45 +1452,45 @@ RasterImage::Draw(gfxContext* aContext,
 
 
   // If we're not using SamplingFilter::GOOD, we shouldn't high-quality scale or
   // downscale during decode.
   uint32_t flags = aSamplingFilter == SamplingFilter::GOOD
                  ? aFlags
                  : aFlags & ~FLAG_HIGH_QUALITY_SCALING;
 
-  DrawableSurface surface =
+  LookupResult result =
     LookupFrame(aSize, flags, ToPlaybackType(aWhichFrame));
-  if (!surface) {
+  if (!result) {
     // Getting the frame (above) touches the image and kicks off decoding.
     if (mDrawStartTime.IsNull()) {
       mDrawStartTime = TimeStamp::Now();
     }
     return DrawResult::NOT_READY;
   }
 
   bool shouldRecordTelemetry = !mDrawStartTime.IsNull() &&
-                               surface->IsFinished();
+                               result.Surface()->IsFinished();
 
-  auto result = DrawInternal(Move(surface), aContext, aSize,
-                             aRegion, aSamplingFilter, flags, aOpacity);
+  auto drawResult = DrawInternal(Move(result.Surface()), aContext, aSize,
+                                 aRegion, aSamplingFilter, flags, aOpacity);
 
   if (shouldRecordTelemetry) {
       TimeDuration drawLatency = TimeStamp::Now() - mDrawStartTime;
       Telemetry::Accumulate(Telemetry::IMAGE_DECODE_ON_DRAW_LATENCY,
                             int32_t(drawLatency.ToMicroseconds()));
       if (mAnimationState) {
         Telemetry::Accumulate(Telemetry::IMAGE_ANIMATED_DECODE_ON_DRAW_LATENCY,
                               int32_t(drawLatency.ToMicroseconds()));
 
       }
       mDrawStartTime = TimeStamp();
   }
 
-  return result;
+  return drawResult;
 }
 
 //******************************************************************************
 
 NS_IMETHODIMP
 RasterImage::LockImage()
 {
   MOZ_ASSERT(NS_IsMainThread(),
@@ -1686,42 +1631,16 @@ RasterImage::GetFramesNotified(uint32_t*
   NS_ENSURE_ARG_POINTER(aFramesNotified);
 
   *aFramesNotified = mFramesNotified;
 
   return NS_OK;
 }
 #endif
 
-#ifdef DEBUG
-void
-RasterImage::NotifyDrawingObservers()
-{
-  if (!mURI || !NS_IsMainThread()) {
-    return;
-  }
-
-  bool match = false;
-  if ((NS_FAILED(mURI->SchemeIs("resource", &match)) || !match) &&
-      (NS_FAILED(mURI->SchemeIs("chrome", &match)) || !match)) {
-    return;
-  }
-
-  // Record the image drawing for startup performance testing.
-  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
-  NS_WARNING_ASSERTION(obs, "Can't get an observer service handle");
-  if (obs) {
-    nsCOMPtr<nsIURI> imageURI = mURI->ToIURI();
-    nsAutoCString spec;
-    imageURI->GetSpec(spec);
-    obs->NotifyObservers(nullptr, "image-drawing", NS_ConvertUTF8toUTF16(spec).get());
-  }
-}
-#endif
-
 void
 RasterImage::NotifyProgress(Progress aProgress,
                             const IntRect& aInvalidRect /* = IntRect() */,
                             const Maybe<uint32_t>& aFrameCount /* = Nothing() */,
                             DecoderFlags aDecoderFlags
                               /* = DefaultDecoderFlags() */,
                             SurfaceFlags aSurfaceFlags
                               /* = DefaultSurfaceFlags() */)
--- a/image/RasterImage.h
+++ b/image/RasterImage.h
@@ -30,17 +30,16 @@
 #include "ImageMetadata.h"
 #include "ISurfaceProvider.h"
 #include "Orientation.h"
 #include "nsIObserver.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/NotNull.h"
-#include "mozilla/Pair.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/WeakPtr.h"
 #include "mozilla/UniquePtr.h"
 #include "ImageContainer.h"
 #include "PlaybackType.h"
 #ifdef DEBUG
   #include "imgIContainerDebug.h"
 #endif
@@ -126,16 +125,17 @@ class nsIRequest;
  * it's not allocated until the second frame is added.
  */
 
 namespace mozilla {
 
 namespace layers {
 class ImageContainer;
 class Image;
+class LayersManager;
 } // namespace layers
 
 namespace image {
 
 class Decoder;
 struct DecoderFinalStatus;
 struct DecoderTelemetry;
 class ImageMetadata;
@@ -285,47 +285,42 @@ private:
    * data, we'll attempt a sync decode if no matching surface is found. If
    * FLAG_SYNC_DECODE was not specified and no matching surface was found, we'll
    * kick off an async decode so that the surface is (hopefully) available next
    * time it's requested.
    *
    * @return a drawable surface, which may be empty if the requested surface
    *         could not be found.
    */
-  DrawableSurface LookupFrame(const gfx::IntSize& aSize,
-                              uint32_t aFlags,
-                              PlaybackType aPlaybackType);
+  LookupResult LookupFrame(const gfx::IntSize& aSize,
+                           uint32_t aFlags,
+                           PlaybackType aPlaybackType);
 
   /// Helper method for LookupFrame().
   LookupResult LookupFrameInternal(const gfx::IntSize& aSize,
                                    uint32_t aFlags,
                                    PlaybackType aPlaybackType);
 
   DrawResult DrawInternal(DrawableSurface&& aFrameRef,
                           gfxContext* aContext,
                           const nsIntSize& aSize,
                           const ImageRegion& aRegion,
                           gfx::SamplingFilter aSamplingFilter,
                           uint32_t aFlags,
                           float aOpacity);
 
-  Pair<DrawResult, RefPtr<gfx::SourceSurface>>
+  Tuple<DrawResult, gfx::IntSize, RefPtr<gfx::SourceSurface>>
     GetFrameInternal(const gfx::IntSize& aSize,
+                     const Maybe<SVGImageContext>& aSVGContext,
                      uint32_t aWhichFrame,
-                     uint32_t aFlags);
+                     uint32_t aFlags) override;
 
-  Pair<DrawResult, RefPtr<layers::Image>>
-    GetCurrentImage(layers::ImageContainer* aContainer, uint32_t aFlags);
-
-  void UpdateImageContainer();
-
-#ifdef DEBUG
-  // Records the image drawing for startup performance testing.
-  void NotifyDrawingObservers();
-#endif
+  gfx::IntSize GetImageContainerSize(layers::LayerManager* aManager,
+                                     const gfx::IntSize& aSize,
+                                     uint32_t aFlags) override;
 
   //////////////////////////////////////////////////////////////////////////////
   // Decoding.
   //////////////////////////////////////////////////////////////////////////////
 
   /**
    * Creates and runs a decoder, either synchronously or asynchronously
    * according to @aFlags. Decodes at the provided target size @aSize, using
@@ -400,27 +395,16 @@ private: // data
 
   // The type of decoder this image needs. Computed from the MIME type in Init().
   DecoderType                mDecoderType;
 
   // How many times we've decoded this image.
   // This is currently only used for statistics
   int32_t                        mDecodeCount;
 
-  // A weak pointer to our ImageContainer, which stays alive only as long as
-  // the layer system needs it.
-  WeakPtr<layers::ImageContainer> mImageContainer;
-
-  layers::ImageContainer::ProducerID mImageProducerID;
-  layers::ImageContainer::FrameID mLastFrameID;
-
-  // If mImageContainer is non-null, this contains the DrawResult we obtained
-  // the last time we updated it.
-  DrawResult mLastImageContainerDrawResult;
-
 #ifdef DEBUG
   uint32_t                       mFramesNotified;
 #endif
 
   // The source data for this image.
   NotNull<RefPtr<SourceBuffer>>  mSourceBuffer;
 
   // Boolean flags (clustered together to conserve space):
--- a/image/SurfaceCache.cpp
+++ b/image/SurfaceCache.cpp
@@ -330,16 +330,17 @@ public:
 
     // Try for a best match second, if using compact.
     IntSize suggestedSize = SuggestedSize(aIdealKey.Size());
     if (mFactor2Mode) {
       if (!exactMatch) {
         SurfaceKey compactKey = aIdealKey.CloneWithSize(suggestedSize);
         mSurfaces.Get(compactKey, getter_AddRefs(exactMatch));
         if (exactMatch && exactMatch->IsDecoded()) {
+          MOZ_ASSERT(suggestedSize != aIdealKey.Size());
           return MakeTuple(exactMatch.forget(),
                            MatchType::SUBSTITUTE_BECAUSE_BEST,
                            suggestedSize);
         }
       }
     }
 
     // There's no perfect match, so find the best match we can.
@@ -385,36 +386,31 @@ public:
                       currentKey.Size())) {
         bestMatch = current;
       }
     }
 
     MatchType matchType;
     if (bestMatch) {
       if (!exactMatch) {
-        const IntSize& bestMatchSize = bestMatch->GetSurfaceKey().Size();
-        if (mFactor2Mode && suggestedSize == bestMatchSize) {
-          // No exact match, but this is the best we are willing to decode.
-          matchType = MatchType::SUBSTITUTE_BECAUSE_BEST;
-        } else {
-          // No exact match, but we found a substitute.
-          matchType = MatchType::SUBSTITUTE_BECAUSE_NOT_FOUND;
-        }
+        // No exact match, neither ideal nor factor of 2.
+        MOZ_ASSERT(suggestedSize != bestMatch->GetSurfaceKey().Size(),
+                   "No exact match despite the fact the sizes match!");
+        matchType = MatchType::SUBSTITUTE_BECAUSE_NOT_FOUND;
       } else if (exactMatch != bestMatch) {
         // The exact match is still decoding, but we found a substitute.
         matchType = MatchType::SUBSTITUTE_BECAUSE_PENDING;
+      } else if (aIdealKey.Size() != bestMatch->GetSurfaceKey().Size()) {
+        // The best factor of 2 match is still decoding, but the best we've got.
+        MOZ_ASSERT(suggestedSize != aIdealKey.Size());
+        MOZ_ASSERT(mFactor2Mode);
+        matchType = MatchType::SUBSTITUTE_BECAUSE_BEST;
       } else {
-        const IntSize& bestMatchSize = bestMatch->GetSurfaceKey().Size();
-        if (mFactor2Mode && aIdealKey.Size() != bestMatchSize) {
-          // The best factor of 2 match is still decoding.
-          matchType = MatchType::SUBSTITUTE_BECAUSE_BEST;
-        } else {
-          // The exact match is still decoding, but it's the best we've got.
-          matchType = MatchType::EXACT;
-        }
+        // The exact match is still decoding, but it's the best we've got.
+        matchType = MatchType::EXACT;
       }
     } else {
       if (exactMatch) {
         // We found an "exact match"; it must have been a placeholder.
         MOZ_ASSERT(exactMatch->IsPlaceholder());
         matchType = MatchType::PENDING;
       } else {
         // We couldn't find an exact match *or* a substitute.
@@ -979,25 +975,17 @@ public:
 
     if (matchType == MatchType::EXACT ||
         matchType == MatchType::SUBSTITUTE_BECAUSE_BEST) {
       if (!MarkUsed(WrapNotNull(surface), WrapNotNull(cache), aAutoLock)) {
         Remove(WrapNotNull(surface), /* aStopTracking */ false, aAutoLock);
       }
     }
 
-    // When the caller may choose to decode at an uncached size because there is
-    // no pending decode at the requested size, we should give it the alternative
-    // size it should decode at.
-    if (matchType == MatchType::SUBSTITUTE_BECAUSE_NOT_FOUND ||
-        matchType == MatchType::NOT_FOUND) {
-      return LookupResult(Move(drawableSurface), matchType, suggestedSize);
-    }
-
-    return LookupResult(Move(drawableSurface), matchType);
+    return LookupResult(Move(drawableSurface), matchType, suggestedSize);
   }
 
   bool CanHold(const Cost aCost) const
   {
     return aCost <= mMaxCost;
   }
 
   size_t MaximumCapacity() const
--- a/image/VectorImage.cpp
+++ b/image/VectorImage.cpp
@@ -10,17 +10,19 @@
 #include "gfxDrawable.h"
 #include "gfxPlatform.h"
 #include "gfxUtils.h"
 #include "imgFrame.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/dom/SVGSVGElement.h"
 #include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/gfxVars.h"
 #include "mozilla/RefPtr.h"
+#include "mozilla/Tuple.h"
 #include "nsIDOMEvent.h"
 #include "nsIPresShell.h"
 #include "nsIStreamListener.h"
 #include "nsMimeTypes.h"
 #include "nsPresContext.h"
 #include "nsRect.h"
 #include "nsString.h"
 #include "nsStubDocumentObserver.h"
@@ -577,16 +579,18 @@ VectorImage::SendInvalidationNotificatio
   // we would miss the subsequent invalidations if we didn't send out the
   // notifications directly in |InvalidateObservers...|.
 
   if (mProgressTracker) {
     SurfaceCache::RemoveImage(ImageKey(this));
     mProgressTracker->SyncNotifyProgress(FLAG_FRAME_COMPLETE,
                                          GetMaxSizedIntRect());
   }
+
+  UpdateImageContainer();
 }
 
 NS_IMETHODIMP_(IntRect)
 VectorImage::GetImageSpaceInvalidationRect(const IntRect& aRect)
 {
   return aRect;
 }
 
@@ -732,63 +736,199 @@ VectorImage::GetFrame(uint32_t aWhichFra
   return GetFrameAtSize(imageIntSize, aWhichFrame, aFlags);
 }
 
 NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
 VectorImage::GetFrameAtSize(const IntSize& aSize,
                             uint32_t aWhichFrame,
                             uint32_t aFlags)
 {
+  auto result = GetFrameInternal(aSize, Nothing(), aWhichFrame, aFlags);
+  RefPtr<SourceSurface> surf = Get<2>(result).forget();
+
+  // If we are here, it suggests the image is embedded in a canvas or some
+  // other path besides layers, and we won't need the file handle.
+  MarkSurfaceShared(surf);
+  return surf.forget();
+}
+
+Tuple<DrawResult, IntSize, RefPtr<SourceSurface>>
+VectorImage::GetFrameInternal(const IntSize& aSize,
+                              const Maybe<SVGImageContext>& aSVGContext,
+                              uint32_t aWhichFrame,
+                              uint32_t aFlags)
+{
   MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE);
 
-  if (aSize.IsEmpty()) {
-    return nullptr;
+  if (aSize.IsEmpty() || aWhichFrame > FRAME_MAX_VALUE) {
+    return MakeTuple(DrawResult::BAD_ARGS, aSize,
+                     RefPtr<SourceSurface>());
   }
 
-  if (aWhichFrame > FRAME_MAX_VALUE) {
-    return nullptr;
+  if (mError) {
+    return MakeTuple(DrawResult::BAD_IMAGE, aSize,
+                     RefPtr<SourceSurface>());
+  }
+
+  if (!mIsFullyLoaded) {
+    return MakeTuple(DrawResult::NOT_READY, aSize,
+                     RefPtr<SourceSurface>());
   }
 
-  if (mError || !mIsFullyLoaded) {
-    return nullptr;
+  RefPtr<SourceSurface> sourceSurface =
+    LookupCachedSurface(aSize, aSVGContext, aFlags);
+  if (sourceSurface) {
+    return MakeTuple(DrawResult::SUCCESS, aSize, Move(sourceSurface));
+  }
+
+  if (mIsDrawing) {
+    NS_WARNING("Refusing to make re-entrant call to VectorImage::Draw");
+    return MakeTuple(DrawResult::TEMPORARY_ERROR, aSize,
+                     RefPtr<SourceSurface>());
   }
 
   // Make our surface the size of what will ultimately be drawn to it.
   // (either the full image size, or the restricted region)
   RefPtr<DrawTarget> dt = gfxPlatform::GetPlatform()->
     CreateOffscreenContentDrawTarget(aSize, SurfaceFormat::B8G8R8A8);
   if (!dt || !dt->IsValid()) {
     NS_ERROR("Could not create a DrawTarget");
-    return nullptr;
+    return MakeTuple(DrawResult::TEMPORARY_ERROR, aSize,
+                     RefPtr<SourceSurface>());
   }
 
   RefPtr<gfxContext> context = gfxContext::CreateOrNull(dt);
   MOZ_ASSERT(context); // already checked the draw target above
 
-  auto result = Draw(context, aSize, ImageRegion::Create(aSize),
-                     aWhichFrame, SamplingFilter::POINT, Nothing(), aFlags,
-                     1.0);
+  SVGDrawingParameters params(context, aSize, ImageRegion::Create(aSize),
+                              SamplingFilter::POINT, aSVGContext,
+                              mSVGDocumentWrapper->GetCurrentTime(),
+                              aFlags, 1.0);
 
-  return result == DrawResult::SUCCESS ? dt->Snapshot() : nullptr;
+  // DrawInternal may return a surface which is stored in the cache. It is
+  // important to prefer this result over the snapshot because it may be a
+  // different surface type (e.g. SourceSurfaceSharedData for WebRender). If
+  // we did not put anything in the cache, we will need to fallback to the
+  // snapshot surface.
+  bool contextPaint = aSVGContext && aSVGContext->GetContextPaint();
+  RefPtr<SourceSurface> surface = DrawInternal(params, contextPaint);
+  if (!surface) {
+    surface = dt->Snapshot();
+  }
+
+  return MakeTuple(DrawResult::SUCCESS, aSize, Move(surface));
+}
+
+//******************************************************************************
+IntSize
+VectorImage::GetImageContainerSize(LayerManager* aManager,
+                                   const IntSize& aSize,
+                                   uint32_t aFlags)
+{
+  if (!IsImageContainerAvailableAtSize(aManager, aSize, aFlags)) {
+    return IntSize(0, 0);
+  }
+
+  return aSize;
 }
 
 NS_IMETHODIMP_(bool)
 VectorImage::IsImageContainerAvailable(LayerManager* aManager, uint32_t aFlags)
 {
   return false;
 }
 
 //******************************************************************************
 NS_IMETHODIMP_(already_AddRefed<ImageContainer>)
 VectorImage::GetImageContainer(LayerManager* aManager, uint32_t aFlags)
 {
   return nullptr;
 }
 
 //******************************************************************************
+NS_IMETHODIMP_(bool)
+VectorImage::IsImageContainerAvailableAtSize(LayerManager* aManager,
+                                             const IntSize& aSize,
+                                             uint32_t aFlags)
+{
+  if (mError || !mIsFullyLoaded || aSize.IsEmpty() ||
+      mHaveAnimations || !gfxVars::UseWebRender()) {
+    return false;
+  }
+
+  int32_t maxTextureSize = aManager->GetMaxTextureSize();
+  return aSize.width <= maxTextureSize &&
+         aSize.height <= maxTextureSize;
+}
+
+//******************************************************************************
+NS_IMETHODIMP_(already_AddRefed<ImageContainer>)
+VectorImage::GetImageContainerAtSize(LayerManager* aManager,
+                                     const IntSize& aSize,
+                                     const Maybe<SVGImageContext>& aSVGContext,
+                                     uint32_t aFlags)
+{
+  Maybe<SVGImageContext> newSVGContext;
+  MaybeRestrictSVGContext(newSVGContext, aSVGContext, aFlags);
+
+  // Since we do not support high quality scaling with SVG, we mask it off so
+  // that container requests with and without it map to the same container.
+  // Similarly the aspect ratio flag was already handled as part of the SVG
+  // context restriction above.
+  uint32_t flags = aFlags & ~(FLAG_HIGH_QUALITY_SCALING |
+                              FLAG_FORCE_PRESERVEASPECTRATIO_NONE);
+  return GetImageContainerImpl(aManager, aSize,
+                               newSVGContext ? newSVGContext : aSVGContext,
+                               flags);
+}
+
+bool
+VectorImage::MaybeRestrictSVGContext(Maybe<SVGImageContext>& aNewSVGContext,
+                                     const Maybe<SVGImageContext>& aSVGContext,
+                                     uint32_t aFlags)
+{
+  bool overridePAR = (aFlags & FLAG_FORCE_PRESERVEASPECTRATIO_NONE) && aSVGContext;
+
+  bool haveContextPaint = aSVGContext && aSVGContext->GetContextPaint();
+  bool blockContextPaint = false;
+  if (haveContextPaint) {
+    nsCOMPtr<nsIURI> imageURI = mURI->ToIURI();
+    blockContextPaint = !SVGContextPaint::IsAllowedForImageFromURI(imageURI);
+  }
+
+  if (overridePAR || blockContextPaint) {
+    // The key that we create for the image surface cache must match the way
+    // that the image will be painted, so we need to initialize a new matching
+    // SVGImageContext here in order to generate the correct key.
+
+    aNewSVGContext = aSVGContext; // copy
+
+    if (overridePAR) {
+      // The SVGImageContext must take account of the preserveAspectRatio
+      // overide:
+      MOZ_ASSERT(!aSVGContext->GetPreserveAspectRatio(),
+                 "FLAG_FORCE_PRESERVEASPECTRATIO_NONE is not expected if a "
+                 "preserveAspectRatio override is supplied");
+      Maybe<SVGPreserveAspectRatio> aspectRatio =
+        Some(SVGPreserveAspectRatio(SVG_PRESERVEASPECTRATIO_NONE,
+                                    SVG_MEETORSLICE_UNKNOWN));
+      aNewSVGContext->SetPreserveAspectRatio(aspectRatio);
+    }
+
+    if (blockContextPaint) {
+      // The SVGImageContext must not include context paint if the image is
+      // not allowed to use it:
+      aNewSVGContext->ClearContextPaint();
+    }
+  }
+
+  return haveContextPaint && !blockContextPaint;
+}
+
+//******************************************************************************
 NS_IMETHODIMP_(DrawResult)
 VectorImage::Draw(gfxContext* aContext,
                   const nsIntSize& aSize,
                   const ImageRegion& aRegion,
                   uint32_t aWhichFrame,
                   SamplingFilter aSamplingFilter,
                   const Maybe<SVGImageContext>& aSVGContext,
                   uint32_t aFlags,
@@ -814,132 +954,120 @@ VectorImage::Draw(gfxContext* aContext,
     SendOnUnlockedDraw(aFlags);
   }
 
   MOZ_ASSERT(!(aFlags & FLAG_FORCE_PRESERVEASPECTRATIO_NONE) ||
              (aSVGContext && aSVGContext->GetViewportSize()),
              "Viewport size is required when using "
              "FLAG_FORCE_PRESERVEASPECTRATIO_NONE");
 
-  bool overridePAR = (aFlags & FLAG_FORCE_PRESERVEASPECTRATIO_NONE) && aSVGContext;
-
-  bool haveContextPaint = aSVGContext && aSVGContext->GetContextPaint();
-  bool blockContextPaint = false;
-  if (haveContextPaint) {
-    nsCOMPtr<nsIURI> imageURI = mURI->ToIURI();
-    blockContextPaint = !SVGContextPaint::IsAllowedForImageFromURI(imageURI);
-  }
+  float animTime = (aWhichFrame == FRAME_FIRST)
+                     ? 0.0f : mSVGDocumentWrapper->GetCurrentTime();
 
   Maybe<SVGImageContext> newSVGContext;
-  if (overridePAR || blockContextPaint) {
-    // The key that we create for the image surface cache must match the way
-    // that the image will be painted, so we need to initialize a new matching
-    // SVGImageContext here in order to generate the correct key.
-
-    newSVGContext = aSVGContext; // copy
-
-    if (overridePAR) {
-      // The SVGImageContext must take account of the preserveAspectRatio
-      // overide:
-      MOZ_ASSERT(!aSVGContext->GetPreserveAspectRatio(),
-                 "FLAG_FORCE_PRESERVEASPECTRATIO_NONE is not expected if a "
-                 "preserveAspectRatio override is supplied");
-      Maybe<SVGPreserveAspectRatio> aspectRatio =
-        Some(SVGPreserveAspectRatio(SVG_PRESERVEASPECTRATIO_NONE,
-                                    SVG_MEETORSLICE_UNKNOWN));
-      newSVGContext->SetPreserveAspectRatio(aspectRatio);
-    }
-
-    if (blockContextPaint) {
-      // The SVGImageContext must not include context paint if the image is
-      // not allowed to use it:
-      newSVGContext->ClearContextPaint();
-    }
-  }
-
-  float animTime = (aWhichFrame == FRAME_FIRST)
-                     ? 0.0f : mSVGDocumentWrapper->GetCurrentTime();
+  bool contextPaint =
+    MaybeRestrictSVGContext(newSVGContext, aSVGContext, aFlags);
 
   SVGDrawingParameters params(aContext, aSize, aRegion, aSamplingFilter,
                               newSVGContext ? newSVGContext : aSVGContext,
                               animTime, aFlags, aOpacity);
 
   // If we have an prerasterized version of this image that matches the
   // drawing parameters, use that.
-  RefPtr<gfxDrawable> svgDrawable = LookupCachedSurface(params);
-  if (svgDrawable) {
+  RefPtr<SourceSurface> sourceSurface =
+    LookupCachedSurface(aSize, params.svgContext, aFlags);
+  if (sourceSurface) {
+    RefPtr<gfxDrawable> svgDrawable =
+      new gfxSurfaceDrawable(sourceSurface, sourceSurface->GetSize());
     Show(svgDrawable, params);
     return DrawResult::SUCCESS;
   }
 
   // else, we need to paint the image:
 
   if (mIsDrawing) {
     NS_WARNING("Refusing to make re-entrant call to VectorImage::Draw");
     return DrawResult::TEMPORARY_ERROR;
   }
+
+  RefPtr<SourceSurface> surface = DrawInternal(params, contextPaint);
+
+  // Image got put into a painted layer, it will not be shared with another
+  // process.
+  MarkSurfaceShared(surface);
+  return DrawResult::SUCCESS;
+}
+
+already_AddRefed<SourceSurface>
+VectorImage::DrawInternal(const SVGDrawingParameters& aParams,
+                          bool aContextPaint)
+{
+  MOZ_ASSERT(!mIsDrawing);
+
   AutoRestore<bool> autoRestoreIsDrawing(mIsDrawing);
   mIsDrawing = true;
 
   // Apply any 'preserveAspectRatio' override (if specified) to the root
   // element:
-  AutoPreserveAspectRatioOverride autoPAR(newSVGContext ? newSVGContext : aSVGContext,
+  AutoPreserveAspectRatioOverride autoPAR(aParams.svgContext,
                                           mSVGDocumentWrapper->GetRootSVGElem());
 
   // Set the animation time:
   AutoSVGTimeSetRestore autoSVGTime(mSVGDocumentWrapper->GetRootSVGElem(),
-                                    animTime);
+                                    aParams.animationTime);
 
   // Set context paint (if specified) on the document:
   Maybe<AutoSetRestoreSVGContextPaint> autoContextPaint;
-  if (haveContextPaint && !blockContextPaint) {
-    autoContextPaint.emplace(aSVGContext->GetContextPaint(),
+  if (aContextPaint) {
+    autoContextPaint.emplace(aParams.svgContext->GetContextPaint(),
                              mSVGDocumentWrapper->GetDocument());
   }
 
   // We didn't get a hit in the surface cache, so we'll need to rerasterize.
-  CreateSurfaceAndShow(params, aContext->GetDrawTarget()->GetBackendType());
-  return DrawResult::SUCCESS;
+  BackendType backend = aParams.context->GetDrawTarget()->GetBackendType();
+  return CreateSurfaceAndShow(aParams, backend);
 }
 
-already_AddRefed<gfxDrawable>
-VectorImage::LookupCachedSurface(const SVGDrawingParameters& aParams)
+already_AddRefed<SourceSurface>
+VectorImage::LookupCachedSurface(const IntSize& aSize,
+                                 const Maybe<SVGImageContext>& aSVGContext,
+                                 uint32_t aFlags)
 {
   // If we're not allowed to use a cached surface, don't attempt a lookup.
-  if (aParams.flags & FLAG_BYPASS_SURFACE_CACHE) {
+  if (aFlags & FLAG_BYPASS_SURFACE_CACHE) {
     return nullptr;
   }
 
   // We don't do any caching if we have animation, so don't bother with a lookup
   // in this case either.
   if (mHaveAnimations) {
     return nullptr;
   }
 
   LookupResult result =
     SurfaceCache::Lookup(ImageKey(this),
-                         VectorSurfaceKey(aParams.size, aParams.svgContext));
+                         VectorSurfaceKey(aSize, aSVGContext));
+
+  MOZ_ASSERT(result.SuggestedSize().IsEmpty(), "SVG should not substitute!");
   if (!result) {
     return nullptr;  // No matching surface, or the OS freed the volatile buffer.
   }
 
   RefPtr<SourceSurface> sourceSurface = result.Surface()->GetSourceSurface();
   if (!sourceSurface) {
     // Something went wrong. (Probably a GPU driver crash or device reset.)
     // Attempt to recover.
     RecoverFromLossOfSurfaces();
     return nullptr;
   }
 
-  RefPtr<gfxDrawable> svgDrawable =
-    new gfxSurfaceDrawable(sourceSurface, result.Surface()->GetSize());
-  return svgDrawable.forget();
+  return sourceSurface.forget();
 }
 
-void
+already_AddRefed<SourceSurface>
 VectorImage::CreateSurfaceAndShow(const SVGDrawingParameters& aParams, BackendType aBackend)
 {
   mSVGDocumentWrapper->UpdateViewportBounds(aParams.viewportSize);
   mSVGDocumentWrapper->FlushImageTransformInvalidation();
 
   RefPtr<gfxDrawingCallback> cb =
     new SVGDrawingCallback(mSVGDocumentWrapper,
                            aParams.viewportSize,
@@ -951,17 +1079,18 @@ VectorImage::CreateSurfaceAndShow(const 
 
   bool bypassCache = bool(aParams.flags & FLAG_BYPASS_SURFACE_CACHE) ||
                      // Refuse to cache animated images:
                      // XXX(seth): We may remove this restriction in bug 922893.
                      mHaveAnimations ||
                      // The image is too big to fit in the cache:
                      !SurfaceCache::CanHold(aParams.size);
   if (bypassCache) {
-    return Show(svgDrawable, aParams);
+    Show(svgDrawable, aParams);
+    return nullptr;
   }
 
   // We're about to rerasterize, which may mean that some of the previous
   // surfaces we've rasterized aren't useful anymore. We can allow them to
   // expire from the cache by unlocking them here, and then sending out an
   // invalidation. If this image is locked, any surfaces that are still useful
   // will become locked again when Draw touches them, and the remainder will
   // eventually expire.
@@ -975,24 +1104,26 @@ VectorImage::CreateSurfaceAndShow(const 
                             SurfaceFormat::B8G8R8A8,
                             SamplingFilter::POINT, aParams.flags,
                             aBackend);
 
   // If we couldn't create the frame, it was probably because it would end
   // up way too big. Generally it also wouldn't fit in the cache, but the prefs
   // could be set such that the cache isn't the limiting factor.
   if (NS_FAILED(rv)) {
-    return Show(svgDrawable, aParams);
+    Show(svgDrawable, aParams);
+    return nullptr;
   }
 
   // Take a strong reference to the frame's surface and make sure it hasn't
   // already been purged by the operating system.
   RefPtr<SourceSurface> surface = frame->GetSourceSurface();
   if (!surface) {
-    return Show(svgDrawable, aParams);
+    Show(svgDrawable, aParams);
+    return nullptr;
   }
 
   // Attempt to cache the frame.
   SurfaceKey surfaceKey = VectorSurfaceKey(aParams.size, aParams.svgContext);
   NotNull<RefPtr<ISurfaceProvider>> provider =
     MakeNotNull<SimpleSurfaceProvider*>(ImageKey(this), surfaceKey, frame);
   SurfaceCache::Insert(provider);
 
@@ -1013,42 +1144,34 @@ VectorImage::CreateSurfaceAndShow(const 
                               [=]() -> void {
       RefPtr<ProgressTracker> tracker = image->GetProgressTracker();
       if (tracker) {
         tracker->SyncNotifyProgress(FLAG_FRAME_COMPLETE,
                                     GetMaxSizedIntRect());
       }
     }));
   }
+
+  return surface.forget();
 }
 
 
 void
 VectorImage::Show(gfxDrawable* aDrawable, const SVGDrawingParameters& aParams)
 {
   MOZ_ASSERT(aDrawable, "Should have a gfxDrawable by now");
   gfxUtils::DrawPixelSnapped(aParams.context, aDrawable,
                              SizeDouble(aParams.size),
                              aParams.region,
                              SurfaceFormat::B8G8R8A8,
                              aParams.samplingFilter,
                              aParams.flags, aParams.opacity);
 
 #ifdef DEBUG
-  // Record the image drawing for startup performance testing.
-  if (NS_IsMainThread()) {
-    nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
-    NS_WARNING_ASSERTION(obs, "Can't get an observer service handle");
-    if (obs) {
-      nsCOMPtr<nsIURI> imageURI = mURI->ToIURI();
-      nsAutoCString spec;
-      imageURI->GetSpec(spec);
-      obs->NotifyObservers(nullptr, "image-drawing", NS_ConvertUTF8toUTF16(spec).get());
-    }
-  }
+  NotifyDrawingObservers();
 #endif
 
   MOZ_ASSERT(mRenderingObserver, "Should have a rendering observer by now");
   mRenderingObserver->ResumeHonoringInvalidations();
 }
 
 void
 VectorImage::RecoverFromLossOfSurfaces()
--- a/image/VectorImage.h
+++ b/image/VectorImage.h
@@ -76,22 +76,43 @@ protected:
   explicit VectorImage(ImageURL* aURI = nullptr);
   virtual ~VectorImage();
 
   virtual nsresult StartAnimation() override;
   virtual nsresult StopAnimation() override;
   virtual bool     ShouldAnimate() override;
 
 private:
-  /// Attempt to find a cached surface matching @aParams in the SurfaceCache.
-  already_AddRefed<gfxDrawable>
-    LookupCachedSurface(const SVGDrawingParameters& aParams);
+  Tuple<DrawResult, IntSize, RefPtr<SourceSurface>>
+    GetFrameInternal(const IntSize& aSize,
+                     const Maybe<SVGImageContext>& aSVGContext,
+                     uint32_t aWhichFrame,
+                     uint32_t aFlags) override;
+
+  IntSize GetImageContainerSize(layers::LayerManager* aManager,
+                                const IntSize& aSize,
+                                uint32_t aFlags) override;
 
-  void CreateSurfaceAndShow(const SVGDrawingParameters& aParams,
-                            gfx::BackendType aBackend);
+  /// Attempt to find a matching cached surface in the SurfaceCache.
+  already_AddRefed<SourceSurface>
+    LookupCachedSurface(const IntSize& aSize,
+                        const Maybe<SVGImageContext>& aSVGContext,
+                        uint32_t aFlags);
+
+  bool MaybeRestrictSVGContext(Maybe<SVGImageContext>& aNewSVGContext,
+                               const Maybe<SVGImageContext>& aSVGContext,
+                               uint32_t aFlags);
+
+  already_AddRefed<SourceSurface>
+    DrawInternal(const SVGDrawingParameters& aParams, bool aContextPaint);
+
+  already_AddRefed<SourceSurface>
+    CreateSurfaceAndShow(const SVGDrawingParameters& aParams,
+                         gfx::BackendType aBackend);
+
   void Show(gfxDrawable* aDrawable, const SVGDrawingParameters& aParams);
 
   nsresult Init(const char* aMimeType, uint32_t aFlags);
 
   /**
    * In catastrophic circumstances like a GPU driver crash, we may lose our
    * surfaces even if they're locked. RecoverFromLossOfSurfaces discards all
    * existing surfaces, allowing us to recover.
--- a/image/imgIContainer.idl
+++ b/image/imgIContainer.idl
@@ -275,32 +275,67 @@ interface imgIContainer : nsISupports
    * @return true if getImageContainer() is expected to return a valid
    *         ImageContainer when passed the given @Manager and @Flags
    *         parameters.
    */
   [noscript, notxpcom] boolean isImageContainerAvailable(in LayerManager aManager,
                                                          in uint32_t aFlags);
   /**
    * Attempts to create an ImageContainer (and Image) containing the current
-   * frame.
+   * frame at its native size.
    *
    * Avoid calling this unless you're actually going to layerize this image.
    *
    * @param aManager The LayerManager which will be used to create the
    *                 ImageContainer.
    * @param aFlags Decoding / drawing flags (in other words, FLAG_* flags).
    *               Currently only FLAG_SYNC_DECODE and FLAG_SYNC_DECODE_IF_FAST
    *               are supported.
    * @return An ImageContainer for the current frame, or nullptr if one could
    *         not be created.
    */
   [noscript, notxpcom] TempRefImageContainer getImageContainer(in LayerManager aManager,
                                                                in uint32_t aFlags);
 
   /**
+   * @return true if getImageContainer() is expected to return a valid
+   *         ImageContainer when passed the given @Manager, @Size and @Flags
+   *         parameters.
+   */
+  [noscript, notxpcom] boolean isImageContainerAvailableAtSize(in LayerManager aManager,
+                                                               [const] in nsIntSize aSize,
+                                                               in uint32_t aFlags);
+
+  /**
+   * Attempts to create an ImageContainer (and Image) containing the current
+   * frame at the given size. Match the requested size is best effort; it's
+   * not guaranteed that the surface you get will be a perfect match. (Some
+   * reasons you may get a surface of a different size include: if you
+   * requested upscaling, or if downscale-during-decode is disabled.)
+   *
+   * Avoid calling this unless you're actually going to layerize this image.
+   *
+   * @param aManager The LayerManager which will be used to create the
+   *                 ImageContainer.
+   * @param aSVGContext If specified, SVG-related rendering context, such as
+   *                    overridden attributes on the image document's root <svg>
+   *                    node, and the size of the viewport that the full image
+   *                    would occupy. Ignored for raster images.
+   * @param aFlags Decoding / drawing flags (in other words, FLAG_* flags).
+   *               Currently only FLAG_SYNC_DECODE and FLAG_SYNC_DECODE_IF_FAST
+   *               are supported.
+   * @return An ImageContainer for the current frame, or nullptr if one could
+   *         not be created.
+   */
+  [noscript, notxpcom] TempRefImageContainer getImageContainerAtSize(in LayerManager aManager,
+                                                                     [const] in nsIntSize aSize,
+                                                                     [const] in MaybeSVGImageContext aSVGContext,
+                                                                     in uint32_t aFlags);
+
+  /**
    * Draw the requested frame of this image onto the context specified.
    *
    * Drawing an image involves scaling it to a certain size (which may be
    * implemented as a "smart" scale by substituting an HQ-scaled frame or
    * rendering at a high DPI), and then selecting a region of that image to
    * draw. That region is drawn onto the graphics context and in the process
    * transformed by the context matrix, which determines the final area that is
    * filled. The basic process looks like this:
--- a/image/imgLoader.cpp
+++ b/image/imgLoader.cpp
@@ -35,16 +35,17 @@
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIProgressEventSink.h"
 #include "nsIChannelEventSink.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
 #include "nsIFileURL.h"
 #include "nsIFile.h"
 #include "nsCRT.h"
 #include "nsINetworkPredictor.h"
+#include "nsReadableUtils.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/nsMixedContentBlocker.h"
 
 #include "nsIApplicationCache.h"
 #include "nsIApplicationCacheContainer.h"
 
 #include "nsIMemoryReporter.h"
 #include "DecoderFactory.h"
@@ -307,16 +308,58 @@ private:
                                  ? " (animation)"
                                  : "");
 
         if (counter.Key().Flags() != DefaultSurfaceFlags()) {
           surfacePathPrefix.AppendLiteral(", flags:");
           surfacePathPrefix.AppendInt(uint32_t(counter.Key().Flags()),
                                       /* aRadix = */ 16);
         }
+
+        if (counter.Key().SVGContext()) {
+          const SVGImageContext& context = counter.Key().SVGContext().ref();
+          surfacePathPrefix.AppendLiteral(", svgContext:[ ");
+          if (context.GetViewportSize()) {
+            const CSSIntSize& size = context.GetViewportSize().ref();
+            surfacePathPrefix.AppendLiteral("viewport=(");
+            surfacePathPrefix.AppendInt(size.width);
+            surfacePathPrefix.AppendLiteral("x");
+            surfacePathPrefix.AppendInt(size.height);
+            surfacePathPrefix.AppendLiteral(") ");
+          }
+          if (context.GetPreserveAspectRatio()) {
+            nsAutoString aspect;
+            context.GetPreserveAspectRatio()->ToString(aspect);
+            surfacePathPrefix.AppendLiteral("preserveAspectRatio=(");
+            LossyAppendUTF16toASCII(aspect, surfacePathPrefix);
+            surfacePathPrefix.AppendLiteral(") ");
+          }
+          if (context.GetContextPaint()) {
+            const SVGEmbeddingContextPaint* paint = context.GetContextPaint();
+            surfacePathPrefix.AppendLiteral("contextPaint=(");
+            if (paint->GetFill()) {
+              surfacePathPrefix.AppendLiteral(" fill=");
+              surfacePathPrefix.AppendInt(paint->GetFill()->ToABGR(), 16);
+            }
+            if (paint->GetFillOpacity()) {
+              surfacePathPrefix.AppendLiteral(" fillOpa=");
+              surfacePathPrefix.AppendFloat(paint->GetFillOpacity());
+            }
+            if (paint->GetStroke()) {
+              surfacePathPrefix.AppendLiteral(" stroke=");
+              surfacePathPrefix.AppendInt(paint->GetStroke()->ToABGR(), 16);
+            }
+            if (paint->GetStrokeOpacity()) {
+              surfacePathPrefix.AppendLiteral(" strokeOpa=");
+              surfacePathPrefix.AppendFloat(paint->GetStrokeOpacity());
+            }
+            surfacePathPrefix.AppendLiteral(" ) ");
+          }
+          surfacePathPrefix.AppendLiteral("]");
+        }
       } else if (counter.Type() == SurfaceMemoryCounterType::COMPOSITING) {
         surfacePathPrefix.AppendLiteral(", compositing frame");
       } else if (counter.Type() == SurfaceMemoryCounterType::COMPOSITING_PREV) {
         surfacePathPrefix.AppendLiteral(", compositing prev frame");
       } else {
         MOZ_ASSERT_UNREACHABLE("Unknown counter type");
       }
 
new file mode 100644
--- /dev/null
+++ b/image/test/gtest/TestContainers.cpp
@@ -0,0 +1,105 @@
+/* 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 "gtest/gtest.h"
+
+#include "BasicLayers.h"
+#include "Common.h"
+#include "imgIContainer.h"
+#include "imgITools.h"
+#include "ImageFactory.h"
+#include "ImageContainer.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/RefPtr.h"
+#include "nsIInputStream.h"
+#include "nsString.h"
+#include "ProgressTracker.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using namespace mozilla::image;
+
+class ImageContainers : public ::testing::Test
+{
+protected:
+  AutoInitializeImageLib mInit;
+};
+
+TEST_F(ImageContainers, RasterImageContainer)
+{
+  ImageTestCase testCase = GreenPNGTestCase();
+
+  // Create an image.
+  RefPtr<Image> image =
+    ImageFactory::CreateAnonymousImage(nsDependentCString(testCase.mMimeType));
+  ASSERT_TRUE(!image->HasError());
+
+  nsCOMPtr<nsIInputStream> inputStream = LoadFile(testCase.mPath);
+  ASSERT_TRUE(inputStream);
+
+  // Figure out how much data we have.
+  uint64_t length;
+  nsresult rv = inputStream->Available(&length);
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+  // Write the data into the image.
+  rv = image->OnImageDataAvailable(nullptr, nullptr, inputStream, 0,
+                                   static_cast<uint32_t>(length));
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+  // Let the image know we've sent all the data.
+  rv = image->OnImageDataComplete(nullptr, nullptr, NS_OK, true);
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+  RefPtr<ProgressTracker> tracker = image->GetProgressTracker();
+  tracker->SyncNotifyProgress(FLAG_LOAD_COMPLETE);
+
+  RefPtr<layers::LayerManager> layerManager =
+    new layers::BasicLayerManager(layers::BasicLayerManager::BLM_OFFSCREEN);
+
+  // Get at native size.
+  RefPtr<layers::ImageContainer> nativeContainer =
+    image->GetImageContainer(layerManager,
+                             imgIContainer::FLAG_SYNC_DECODE);
+  ASSERT_TRUE(nativeContainer != nullptr);
+  IntSize containerSize = nativeContainer->GetCurrentSize();
+  EXPECT_EQ(testCase.mSize.width, containerSize.width);
+  EXPECT_EQ(testCase.mSize.height, containerSize.height);
+
+  // Upscaling should give the native size.
+  IntSize requestedSize = testCase.mSize;
+  requestedSize.Scale(2, 2);
+  RefPtr<layers::ImageContainer> upscaleContainer =
+    image->GetImageContainerAtSize(layerManager,
+                                   requestedSize,
+                                   Nothing(),
+                                   imgIContainer::FLAG_SYNC_DECODE |
+                                   imgIContainer::FLAG_HIGH_QUALITY_SCALING);
+  ASSERT_TRUE(upscaleContainer != nullptr);
+  containerSize = upscaleContainer->GetCurrentSize();
+  EXPECT_EQ(testCase.mSize.width, containerSize.width);
+  EXPECT_EQ(testCase.mSize.height, containerSize.height);
+
+  // Downscaling should give the downscaled size.
+  requestedSize = testCase.mSize;
+  requestedSize.width /= 2;
+  requestedSize.height /= 2;
+  RefPtr<layers::ImageContainer> downscaleContainer =
+    image->GetImageContainerAtSize(layerManager,
+                                   requestedSize,
+                                   Nothing(),
+                                   imgIContainer::FLAG_SYNC_DECODE |
+                                   imgIContainer::FLAG_HIGH_QUALITY_SCALING);
+  containerSize = downscaleContainer->GetCurrentSize();
+  EXPECT_EQ(requestedSize.width, containerSize.width);
+  EXPECT_EQ(requestedSize.height, containerSize.height);
+
+  // Get at native size again. Should give same container.
+  RefPtr<layers::ImageContainer> againContainer =
+    image->GetImageContainerAtSize(layerManager,
+                                   testCase.mSize,
+                                   Nothing(),
+                                   imgIContainer::FLAG_SYNC_DECODE);
+  ASSERT_EQ(nativeContainer.get(), againContainer.get());
+}
--- a/image/test/gtest/moz.build
+++ b/image/test/gtest/moz.build
@@ -4,16 +4,17 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 Library('imagetest')
 
 UNIFIED_SOURCES = [
     'Common.cpp',
     'TestADAM7InterpolatingFilter.cpp',
+    'TestContainers.cpp',
     'TestCopyOnWrite.cpp',
     'TestDecoders.cpp',
     'TestDecodeToSurface.cpp',
     'TestDeinterlacingFilter.cpp',
     'TestMetadata.cpp',
     'TestRemoveFrameRectFilter.cpp',
     'TestSourceBuffer.cpp',
     'TestStreamingLexer.cpp',
--- a/ipc/mscom/PassthruProxy.h
+++ b/ipc/mscom/PassthruProxy.h
@@ -6,16 +6,19 @@
 
 #ifndef mozilla_mscom_PassthruProxy_h
 #define mozilla_mscom_PassthruProxy_h
 
 #include "mozilla/Atomics.h"
 #include "mozilla/mscom/ProxyStream.h"
 #include "mozilla/mscom/Ptr.h"
 #include "mozilla/NotNull.h"
+#if defined(MOZ_CONTENT_SANDBOX)
+#include "mozilla/SandboxSettings.h"
+#endif // defined(MOZ_CONTENT_SANDBOX)
 
 #include <objbase.h>
 
 namespace mozilla {
 namespace mscom {
 namespace detail {
 
 template <typename Iface>
@@ -35,28 +38,38 @@ class PassthruProxy final : public IMars
                           , public IClientSecurity
 {
 public:
   template <typename Iface>
   static RefPtr<Iface> Wrap(NotNull<Iface*> aIn)
   {
     static_assert(detail::VTableSizer<Iface>::Size >= 3, "VTable too small");
 
+#if defined(MOZ_CONTENT_SANDBOX)
+    if (mozilla::GetEffectiveContentSandboxLevel() < 3) {
+      // The sandbox isn't strong enough to be a problem; no wrapping required
+      return aIn.get();
+    }
+
     typename detail::EnvironmentSelector<Iface>::Type env;
 
     RefPtr<PassthruProxy> passthru(new PassthruProxy(&env, __uuidof(Iface),
                                                      detail::VTableSizer<Iface>::Size,
                                                      aIn));
 
     RefPtr<Iface> result;
     if (FAILED(passthru->QueryProxyInterface(getter_AddRefs(result)))) {
       return nullptr;
     }
 
     return result;
+#else
+    // No wrapping required
+    return aIn.get();
+#endif // defined(MOZ_CONTENT_SANDBOX)
   }
 
   static HRESULT Register();
 
   PassthruProxy();
 
   // IUnknown
   STDMETHODIMP QueryInterface(REFIID riid, void** ppv) override;
--- a/ipc/testshell/XPCShellEnvironment.cpp
+++ b/ipc/testshell/XPCShellEnvironment.cpp
@@ -169,28 +169,16 @@ Load(JSContext *cx,
             return false;
         }
     }
     args.rval().setUndefined();
     return true;
 }
 
 static bool
-Version(JSContext *cx,
-        unsigned argc,
-        JS::Value *vp)
-{
-    JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
-    args.rval().setInt32(JS_GetVersion(cx));
-    if (args.get(0).isInt32())
-        JS::SetVersionForCurrentRealm(cx, JSVersion(args[0].toInt32()));
-    return true;
-}
-
-static bool
 Quit(JSContext *cx,
      unsigned argc,
      JS::Value *vp)
 {
     Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
     XPCShellEnvironment* env = Environment(global);
     env->SetIsQuitting();
 
@@ -245,17 +233,16 @@ GCZeal(JSContext *cx, unsigned argc, JS:
 }
 #endif
 
 const JSFunctionSpec gGlobalFunctions[] =
 {
     JS_FN("print",           Print,          0,0),
     JS_FN("load",            Load,           1,0),
     JS_FN("quit",            Quit,           0,0),
-    JS_FN("version",         Version,        1,0),
     JS_FN("dumpXPC",         DumpXPC,        1,0),
     JS_FN("dump",            Dump,           1,0),
     JS_FN("gc",              GC,             0,0),
  #ifdef JS_GC_ZEAL
     JS_FN("gczeal",          GCZeal,         1,0),
  #endif
     JS_FS_END
 };
@@ -439,17 +426,16 @@ XPCShellEnvironment::Init()
     rv = NS_NewBackstagePass(getter_AddRefs(backstagePass));
     if (NS_FAILED(rv)) {
         NS_ERROR("Failed to create backstage pass!");
         return false;
     }
 
     JS::CompartmentOptions options;
     options.creationOptions().setSystemZone();
-    options.behaviors().setVersion(JSVERSION_DEFAULT);
     if (xpc::SharedMemoryEnabled())
         options.creationOptions().setSharedMemoryAndAtomicsEnabled(true);
 
     JS::Rooted<JSObject*> globalObj(cx);
     rv = xpc::InitClassesWithNewWrappedGlobal(cx,
                                               static_cast<nsIGlobalObject *>(backstagePass),
                                               principal, 0,
                                               options,
--- a/js/public/Realm.h
+++ b/js/public/Realm.h
@@ -112,24 +112,13 @@ extern JS_PUBLIC_API(JSObject*)
 GetRealmArrayPrototype(JSContext* cx);
 
 extern JS_PUBLIC_API(JSObject*)
 GetRealmErrorPrototype(JSContext* cx);
 
 extern JS_PUBLIC_API(JSObject*)
 GetRealmIteratorPrototype(JSContext* cx);
 
-/**
- * Change the JS language version for the current Realm. This is discouraged,
- * but necessary to support the `version()` builtin function in the js and xpc
- * shells.
- *
- * It would be nice to put this in jsfriendapi, but the linkage requirements
- * of the shells make that impossible.
- */
-JS_PUBLIC_API(void)
-SetVersionForCurrentRealm(JSContext* cx, JSVersion version);
-
 } // namespace JS
 
 #endif // js_Realm_h
 
 
--- a/js/rust/src/jsglue.cpp
+++ b/js/rust/src/jsglue.cpp
@@ -528,17 +528,16 @@ GetSecurityWrapper()
   return &js::CrossCompartmentSecurityWrapper::singleton;
 }
 
 JS::ReadOnlyCompileOptions*
 NewCompileOptions(JSContext* aCx, const char* aFile, unsigned aLine)
 {
     JS::OwningCompileOptions *opts = new JS::OwningCompileOptions(aCx);
     opts->setFileAndLine(aCx, aFile, aLine);
-    opts->setVersion(JSVERSION_DEFAULT);
     return opts;
 }
 
 void
 DeleteCompileOptions(JS::ReadOnlyCompileOptions *aOpts)
 {
     delete static_cast<JS::OwningCompileOptions *>(aOpts);
 }
--- a/js/src/builtin/Eval.cpp
+++ b/js/src/builtin/Eval.cpp
@@ -50,29 +50,26 @@ IsEvalCacheCandidate(JSScript* script)
 
 /* static */ HashNumber
 EvalCacheHashPolicy::hash(const EvalCacheLookup& l)
 {
     AutoCheckCannotGC nogc;
     uint32_t hash = l.str->hasLatin1Chars()
                     ? HashString(l.str->latin1Chars(nogc), l.str->length())
                     : HashString(l.str->twoByteChars(nogc), l.str->length());
-    return AddToHash(hash, l.callerScript.get(), l.version, l.pc);
+    return AddToHash(hash, l.callerScript.get(), l.pc);
 }
 
 /* static */ bool
 EvalCacheHashPolicy::match(const EvalCacheEntry& cacheEntry, const EvalCacheLookup& l)
 {
-    JSScript* script = cacheEntry.script;
-
-    MOZ_ASSERT(IsEvalCacheCandidate(script));
+    MOZ_ASSERT(IsEvalCacheCandidate(cacheEntry.script));
 
     return EqualStrings(cacheEntry.str, l.str) &&
            cacheEntry.callerScript == l.callerScript &&
-           script->getVersion() == l.version &&
            cacheEntry.pc == l.pc;
 }
 
 // Add the script to the eval cache when EvalKernel is finished
 class EvalScriptGuard
 {
     JSContext* cx_;
     Rooted<JSScript*> script_;
@@ -100,17 +97,16 @@ class EvalScriptGuard
         }
     }
 
     void lookupInEvalCache(JSLinearString* str, JSScript* callerScript, jsbytecode* pc)
     {
         lookupStr_ = str;
         lookup_.str = str;
         lookup_.callerScript = callerScript;
-        lookup_.version = cx_->findVersion();
         lookup_.pc = pc;
         p_.emplace(cx_, cx_->caches().evalCache, lookup_);
         if (*p_) {
             script_ = (*p_)->script;
             p_->remove(cx_, cx_->caches().evalCache, lookup_);
             script_->uncacheForEval();
         }
     }
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -173,17 +173,17 @@ js::ExecuteRegExpLegacy(JSContext* cx, R
     }
 
     return CreateRegExpMatchResult(cx, input, matches, rval);
 }
 
 static bool
 CheckPatternSyntax(JSContext* cx, HandleAtom pattern, RegExpFlag flags)
 {
-    CompileOptions options(cx, JSVERSION_DEFAULT);
+    CompileOptions options(cx);
     frontend::TokenStream dummyTokenStream(cx, options, nullptr, 0, nullptr);
     return irregexp::ParsePatternSyntax(dummyTokenStream, cx->tempLifoAlloc(), pattern,
                                         flags & UnicodeFlag);
 }
 
 enum RegExpSharedUse {
     UseRegExpShared,
     DontUseRegExpShared
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -47,17 +47,16 @@ class MOZ_STACK_CLASS BytecodeCompiler
     JSScript* compileGlobalScript(ScopeKind scopeKind);
     JSScript* compileEvalScript(HandleObject environment, HandleScope enclosingScope);
     ModuleObject* compileModule();
     bool compileStandaloneFunction(MutableHandleFunction fun, GeneratorKind generatorKind,
                                    FunctionAsyncKind asyncKind,
                                    const Maybe<uint32_t>& parameterListEnd);
 
     ScriptSourceObject* sourceObjectPtr() const;
-    SourceCompressionTask* sourceCompressionTask() const;
 
   private:
     JSScript* compileScript(HandleObject environment, SharedContext* sc);
     bool checkLength();
     bool createScriptSource(const Maybe<uint32_t>& parameterListEnd);
     bool canLazilyParse();
     bool createParser();
     bool createSourceAndParser(const Maybe<uint32_t>& parameterListEnd = Nothing());
@@ -90,29 +89,16 @@ class MOZ_STACK_CLASS BytecodeCompiler
 
     Directives directives;
     TokenStream::Position startPosition;
 
     RootedScript script;
 };
 
 AutoFrontendTraceLog::AutoFrontendTraceLog(JSContext* cx, const TraceLoggerTextId id,
-                                           const char* filename, size_t line, size_t column)
-#ifdef JS_TRACE_LOGGING
-  : logger_(TraceLoggerForCurrentThread(cx))
-{
-    frontendEvent_.emplace(TraceLogger_Frontend, filename, line, column);
-    frontendLog_.emplace(logger_, *frontendEvent_);
-    typeLog_.emplace(logger_, id);
-}
-#else
-{ }
-#endif
-
-AutoFrontendTraceLog::AutoFrontendTraceLog(JSContext* cx, const TraceLoggerTextId id,
                                            const ErrorReporter& errorReporter)
 #ifdef JS_TRACE_LOGGING
   : logger_(TraceLoggerForCurrentThread(cx))
 {
     // If the tokenizer hasn't yet gotten any tokens, use the line and column
     // numbers from CompileOptions.
     uint32_t line, column;
     if (errorReporter.hasTokenizationStarted()) {
@@ -643,17 +629,17 @@ frontend::CompileModule(JSContext* cx, c
     return module;
 }
 
 bool
 frontend::CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const char16_t* chars, size_t length)
 {
     MOZ_ASSERT(cx->compartment() == lazy->functionNonDelazifying()->compartment());
 
-    CompileOptions options(cx, lazy->version());
+    CompileOptions options(cx);
     options.setMutedErrors(lazy->mutedErrors())
            .setFileAndLine(lazy->filename(), lazy->lineno())
            .setColumn(lazy->column())
            .setScriptSourceOffset(lazy->begin())
            .setNoScriptRval(false)
            .setSelfHostingMode(false);
 
     // Update statistics to find out if we are delazifying just after having
--- a/js/src/frontend/BytecodeCompiler.h
+++ b/js/src/frontend/BytecodeCompiler.h
@@ -130,19 +130,16 @@ class MOZ_STACK_CLASS AutoFrontendTraceL
     TraceLoggerThread* logger_;
     mozilla::Maybe<TraceLoggerEvent> frontendEvent_;
     mozilla::Maybe<AutoTraceLog> frontendLog_;
     mozilla::Maybe<AutoTraceLog> typeLog_;
 #endif
 
   public:
     AutoFrontendTraceLog(JSContext* cx, const TraceLoggerTextId id,
-                         const char* filename, size_t line, size_t column);
-
-    AutoFrontendTraceLog(JSContext* cx, const TraceLoggerTextId id,
                          const ErrorReporter& reporter);
 
     AutoFrontendTraceLog(JSContext* cx, const TraceLoggerTextId id,
                          const ErrorReporter& reporter, FunctionBox* funbox);
 
     AutoFrontendTraceLog(JSContext* cx, const TraceLoggerTextId id,
                          const ErrorReporter& reporter, ParseNode* pn);
 };
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -7835,17 +7835,16 @@ BytecodeEmitter::emitFunction(ParseNode*
             if (emittingRunOnceLambda)
                 fun->lazyScript()->setTreatAsRunOnce();
         } else {
             MOZ_ASSERT_IF(outersc->strict(), funbox->strictScript);
 
             // Inherit most things (principals, version, etc) from the
             // parent.  Use default values for the rest.
             Rooted<JSScript*> parent(cx, script);
-            MOZ_ASSERT(parent->getVersion() == parser.options().version);
             MOZ_ASSERT(parent->mutedErrors() == parser.options().mutedErrors());
             const TransitiveCompileOptions& transitiveOptions = parser.options();
             CompileOptions options(cx, transitiveOptions);
 
             Rooted<JSObject*> sourceObject(cx, script->sourceObject());
             Rooted<JSScript*> script(cx, JSScript::Create(cx, options, sourceObject,
                                                           funbox->bufStart, funbox->bufEnd,
                                                           funbox->toStringStart,
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -341,17 +341,16 @@ struct MOZ_STACK_CLASS BytecodeEmitter
     }
 
     void setVarEmitterScope(EmitterScope* emitterScope) {
         MOZ_ASSERT(emitterScope);
         MOZ_ASSERT(!varEmitterScope);
         varEmitterScope = emitterScope;
     }
 
-    Scope* bodyScope() const { return scopeList.vector[bodyScopeIndex]; }
     Scope* outermostScope() const { return scopeList.vector[0]; }
     Scope* innermostScope() const;
 
     MOZ_ALWAYS_INLINE
     MOZ_MUST_USE bool makeAtomIndex(JSAtom* atom, uint32_t* indexp) {
         MOZ_ASSERT(atomIndices);
         AtomIndexMap::AddPtr p = atomIndices->lookupForAdd(atom);
         if (p) {
@@ -388,17 +387,16 @@ struct MOZ_STACK_CLASS BytecodeEmitter
     ptrdiff_t prologueOffset() const { return prologue.code.end() - prologue.code.begin(); }
     void switchToMain() { current = &main; }
     void switchToPrologue() { current = &prologue; }
     bool inPrologue() const { return current == &prologue; }
 
     SrcNotesVector& notes() const { return current->notes; }
     ptrdiff_t lastNoteOffset() const { return current->lastNoteOffset; }
     unsigned currentLine() const { return current->currentLine; }
-    unsigned lastColumn() const { return current->lastColumn; }
 
     // Check if the last emitted opcode is a jump target.
     bool lastOpcodeIsJumpTarget() const {
         return offset() - current->lastTarget.offset == ptrdiff_t(JSOP_JUMPTARGET_LENGTH);
     }
 
     // JumpTarget should not be part of the emitted statement, as they can be
     // aliased by multiple statements. If we included the jump target as part of
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -24,24 +24,16 @@ class FullParseHandler
 {
     ParseNodeAllocator allocator;
 
     ParseNode* allocParseNode(size_t size) {
         MOZ_ASSERT(size == sizeof(ParseNode));
         return static_cast<ParseNode*>(allocator.allocNode());
     }
 
-    ParseNode* cloneNode(const ParseNode& other) {
-        ParseNode* node = allocParseNode(sizeof(ParseNode));
-        if (!node)
-            return nullptr;
-        mozilla::PodAssign(node, &other);
-        return node;
-    }
-
     /*
      * If this is a full parse to construct the bytecode for a function that
      * was previously lazily parsed, that lazy function and the current index
      * into its inner functions. We do not want to reparse the inner functions.
      */
     const Rooted<LazyScript*> lazyOuterFunction_;
     size_t lazyInnerFunctionIndex;
     size_t lazyClosedOverBindingIndex;
@@ -68,21 +60,16 @@ class FullParseHandler
     static bool isParenthesizedDestructuringPattern(ParseNode* node) {
         // Technically this isn't a destructuring pattern at all -- the grammar
         // doesn't treat it as such.  But we need to know when this happens to
         // consider it a SyntaxError rather than an invalid-left-hand-side
         // ReferenceError.
         return node->isInParens() && (node->isKind(PNK_OBJECT) || node->isKind(PNK_ARRAY));
     }
 
-    static bool isDestructuringPatternAnyParentheses(ParseNode* node) {
-        return isUnparenthesizedDestructuringPattern(node) ||
-               isParenthesizedDestructuringPattern(node);
-    }
-
     FullParseHandler(JSContext* cx, LifoAlloc& alloc, LazyScript* lazyOuterFunction)
       : allocator(cx, alloc),
         lazyOuterFunction_(cx, lazyOuterFunction),
         lazyInnerFunctionIndex(0),
         lazyClosedOverBindingIndex(0)
     {}
 
     static ParseNode* null() { return nullptr; }
@@ -624,17 +611,16 @@ class FullParseHandler
     }
 
     inline MOZ_MUST_USE bool addCatchBlock(ParseNode* catchList, ParseNode* lexicalScope,
                                            ParseNode* catchName, ParseNode* catchGuard,
                                            ParseNode* catchBody);
 
     inline MOZ_MUST_USE bool setLastFunctionFormalParameterDefault(ParseNode* funcpn,
                                                                    ParseNode* pn);
-    inline void setLastFunctionFormalParameterDestructuring(ParseNode* funcpn, ParseNode* pn);
 
     void checkAndSetIsDirectRHSAnonFunction(ParseNode* pn) {
         if (IsAnonymousFunctionDefinition(pn))
             pn->setDirectRHSAnonFunction(true);
     }
 
     ParseNode* newFunctionStatement(const TokenPos& pos) {
         return new_<CodeNode>(PNK_FUNCTION, JSOP_NOP, pos);
@@ -681,20 +667,16 @@ class FullParseHandler
         addList(newExpr, ctor);
         return newExpr;
     }
 
     ParseNode* newAssignment(ParseNodeKind kind, ParseNode* lhs, ParseNode* rhs) {
         return newBinary(kind, lhs, rhs);
     }
 
-    bool isUnparenthesizedYieldExpression(ParseNode* node) {
-        return node->isKind(PNK_YIELD) && !node->isInParens();
-    }
-
     bool isUnparenthesizedAssignment(Node node) {
         if (node->isKind(PNK_ASSIGN) && !node->isInParens()) {
             // PNK_ASSIGN is also (mis)used for things like |var name = expr;|.
             // But this method is only called on actual expressions, so we can
             // just assert the node's op is the one used for plain assignment.
             MOZ_ASSERT(node->isOp(JSOP_NOP));
             return true;
         }
@@ -752,20 +734,16 @@ class FullParseHandler
     }
 
     ParseNode* newList(ParseNodeKind kind, const TokenPos& pos) {
         MOZ_ASSERT(!isDeclarationKind(kind));
         return new_<ListNode>(kind, JSOP_NOP, pos);
     }
 
   private:
-    ParseNode* newList(ParseNodeKind kind, uint32_t begin) {
-        return newList(kind, TokenPos(begin, begin + 1));
-    }
-
     template<typename T>
     ParseNode* newList(ParseNodeKind kind, const T& begin) = delete;
 
   public:
     ParseNode* newList(ParseNodeKind kind, ParseNode* kid) {
         MOZ_ASSERT(!isDeclarationKind(kind));
         return new_<ListNode>(kind, JSOP_NOP, kid);
     }
@@ -814,52 +792,34 @@ class FullParseHandler
     void setInDirectivePrologue(ParseNode* pn) {
         pn->pn_prologue = true;
     }
 
     bool isConstant(ParseNode* pn) {
         return pn->isConstant();
     }
 
-    bool isUnparenthesizedName(ParseNode* node) {
-        return node->isKind(PNK_NAME) && !node->isInParens();
-    }
-
-    bool isNameAnyParentheses(ParseNode* node) {
+    bool isName(ParseNode* node) {
         return node->isKind(PNK_NAME);
     }
 
-    bool isArgumentsAnyParentheses(ParseNode* node, JSContext* cx) {
+    bool isArgumentsName(ParseNode* node, JSContext* cx) {
         return node->isKind(PNK_NAME) && node->pn_atom == cx->names().arguments;
     }
 
-    bool isEvalAnyParentheses(ParseNode* node, JSContext* cx) {
+    bool isEvalName(ParseNode* node, JSContext* cx) {
         return node->isKind(PNK_NAME) && node->pn_atom == cx->names().eval;
     }
 
-    const char* nameIsArgumentsEvalAnyParentheses(ParseNode* node, JSContext* cx) {
-        MOZ_ASSERT(isNameAnyParentheses(node),
-                   "must only call this function on known names");
-
-        if (isEvalAnyParentheses(node, cx))
-            return js_eval_str;
-        if (isArgumentsAnyParentheses(node, cx))
-            return js_arguments_str;
-        return nullptr;
-    }
-
     bool isAsyncKeyword(ParseNode* node, JSContext* cx) {
         return node->isKind(PNK_NAME) &&
                node->pn_pos.begin + strlen("async") == node->pn_pos.end &&
                node->pn_atom == cx->names().async;
     }
 
-    bool isCall(ParseNode* pn) {
-        return pn->isKind(PNK_CALL);
-    }
     PropertyName* maybeDottedProperty(ParseNode* pn) {
         return pn->is<PropertyAccess>() ? &pn->as<PropertyAccess>().name() : nullptr;
     }
     JSAtom* isStringExprStatement(ParseNode* pn, TokenPos* pos) {
         if (JSAtom* atom = pn->isStringExprStatement()) {
             *pos = pn->pn_kid->pn_pos;
             return atom;
         }
@@ -871,26 +831,23 @@ class FullParseHandler
     }
 
     bool canSkipLazyInnerFunctions() {
         return !!lazyOuterFunction_;
     }
     bool canSkipLazyClosedOverBindings() {
         return !!lazyOuterFunction_;
     }
-    LazyScript* lazyOuterFunction() {
-        return lazyOuterFunction_;
-    }
     JSFunction* nextLazyInnerFunction() {
-        MOZ_ASSERT(lazyInnerFunctionIndex < lazyOuterFunction()->numInnerFunctions());
-        return lazyOuterFunction()->innerFunctions()[lazyInnerFunctionIndex++];
+        MOZ_ASSERT(lazyInnerFunctionIndex < lazyOuterFunction_->numInnerFunctions());
+        return lazyOuterFunction_->innerFunctions()[lazyInnerFunctionIndex++];
     }
     JSAtom* nextLazyClosedOverBinding() {
-        MOZ_ASSERT(lazyClosedOverBindingIndex < lazyOuterFunction()->numClosedOverBindings());
-        return lazyOuterFunction()->closedOverBindings()[lazyClosedOverBindingIndex++];
+        MOZ_ASSERT(lazyClosedOverBindingIndex < lazyOuterFunction_->numClosedOverBindings());
+        return lazyOuterFunction_->closedOverBindings()[lazyClosedOverBindingIndex++];
     }
 };
 
 inline bool
 FullParseHandler::addCatchBlock(ParseNode* catchList, ParseNode* lexicalScope,
                                 ParseNode* catchName, ParseNode* catchGuard,
                                 ParseNode* catchBody)
 {
--- a/js/src/frontend/LanguageExtensions.h
+++ b/js/src/frontend/LanguageExtensions.h
@@ -28,17 +28,17 @@ enum class DeprecatedLanguageExtension
     // NO LONGER USING 2
     ExpressionClosure = 3, // Added in JS 1.8
     // NO LONGER USING 4
     // NO LONGER USING 5
     // NO LONGER USING 6
     // NO LONGER USING 7
     // NO LONGER USING 8
     // NO LONGER USING 9
-    BlockScopeFunRedecl = 10,
+    // NO LONGER USING 10
 
     // Sentinel value.  MAY change as extension initializers are added (only to
     // the end) above.
     Count
 };
 
 } // namespace js
 
--- a/js/src/frontend/NameCollections.h
+++ b/js/src/frontend/NameCollections.h
@@ -42,20 +42,16 @@ class CollectionPool
         return collection;
     }
 
   public:
     ~CollectionPool() {
         purgeAll();
     }
 
-    bool empty() const {
-        return all_.empty();
-    }
-
     void purgeAll() {
         void** end = all_.end();
         for (void** it = all_.begin(); it != end; ++it)
             js_delete(asRepresentative(*it));
 
         all_.clearAndFree();
         recyclable_.clearAndFree();
     }
@@ -151,18 +147,16 @@ using RecyclableNameMap = InlineMap<JSAt
                                     DefaultHasher<JSAtom*>,
                                     SystemAllocPolicy>;
 
 using DeclaredNameMap = RecyclableNameMap<DeclaredNameInfo>;
 using CheckTDZMap = RecyclableNameMap<MaybeCheckTDZ>;
 using NameLocationMap = RecyclableNameMap<NameLocation>;
 using AtomIndexMap = RecyclableNameMap<uint32_t>;
 
-#undef RECYCLABLE_NAME_MAP_TYPE
-
 template <typename RepresentativeTable>
 class InlineTablePool
   : public CollectionPool<RepresentativeTable, InlineTablePool<RepresentativeTable>>
 {
   public:
     template <typename Table>
     static void assertInvariants() {
         static_assert(Table::SizeOfInlineEntries == RepresentativeTable::SizeOfInlineEntries,
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -2471,17 +2471,17 @@ Parser<SyntaxParseHandler, char16_t>::fi
     {
         MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
         return false;
     }
 
     FunctionBox* funbox = pc->functionBox();
     RootedFunction fun(context, funbox->function());
     LazyScript* lazy = LazyScript::Create(context, fun, pc->closedOverBindingsForLazy(),
-                                          pc->innerFunctionsForLazy, versionNumber(),
+                                          pc->innerFunctionsForLazy,
                                           funbox->bufStart, funbox->bufEnd,
                                           funbox->toStringStart,
                                           funbox->startLine, funbox->startColumn);
     if (!lazy)
         return false;
 
     // Flags that need to be copied into the JSScript when we do the full
     // parse.
@@ -6136,19 +6136,18 @@ Parser<ParseHandler, CharT>::forHeadStar
     }
 
     *forHeadKind = isForIn ? PNK_FORIN : PNK_FOROF;
 
     // Verify the left-hand side expression doesn't have a forbidden form.
     if (handler.isUnparenthesizedDestructuringPattern(*forInitialPart)) {
         if (!possibleError.checkForDestructuringErrorOrWarning())
             return false;
-    } else if (handler.isNameAnyParentheses(*forInitialPart)) {
-        const char* chars = handler.nameIsArgumentsEvalAnyParentheses(*forInitialPart, context);
-        if (chars) {
+    } else if (handler.isName(*forInitialPart)) {
+        if (const char* chars = nameIsArgumentsOrEval(*forInitialPart)) {
             // |chars| is "arguments" or "eval" here.
             if (!strictModeErrorAt(exprOffset, JSMSG_BAD_STRICT_ASSIGN, chars))
                 return false;
         }
 
         handler.adjustGetToSet(*forInitialPart);
     } else if (handler.isPropertyAccess(*forInitialPart)) {
         // Permitted: no additional testing/fixup needed.
@@ -8130,18 +8129,18 @@ Parser<ParseHandler, CharT>::assignExpr(
     if (handler.isUnparenthesizedDestructuringPattern(lhs)) {
         if (kind != PNK_ASSIGN) {
             error(JSMSG_BAD_DESTRUCT_ASS);
             return null();
         }
 
         if (!possibleErrorInner.checkForDestructuringErrorOrWarning())
             return null();
-    } else if (handler.isNameAnyParentheses(lhs)) {
-        if (const char* chars = handler.nameIsArgumentsEvalAnyParentheses(lhs, context)) {
+    } else if (handler.isName(lhs)) {
+        if (const char* chars = nameIsArgumentsOrEval(lhs)) {
             // |chars| is "arguments" or "eval" here.
             if (!strictModeErrorAt(exprPos.begin, JSMSG_BAD_STRICT_ASSIGN, chars))
                 return null();
         }
 
         handler.adjustGetToSet(lhs);
     } else if (handler.isPropertyAccess(lhs)) {
         // Permitted: no additional testing/fixup needed.
@@ -8173,40 +8172,53 @@ template <class ParseHandler, typename C
 bool
 Parser<ParseHandler, CharT>::isValidSimpleAssignmentTarget(Node node,
                                                            FunctionCallBehavior behavior /* = ForbidAssignmentToFunctionCalls */)
 {
     // Note that this method implements *only* a boolean test.  Reporting an
     // error for the various syntaxes that fail this, and warning for the
     // various syntaxes that "pass" this but should not, occurs elsewhere.
 
-    if (handler.isNameAnyParentheses(node)) {
+    if (handler.isName(node)) {
         if (!pc->sc()->strict())
             return true;
 
-        return !handler.nameIsArgumentsEvalAnyParentheses(node, context);
+        return !nameIsArgumentsOrEval(node);
     }
 
     if (handler.isPropertyAccess(node))
         return true;
 
     if (behavior == PermitAssignmentToFunctionCalls) {
         if (handler.isFunctionCall(node))
             return true;
     }
 
     return false;
 }
 
 template <class ParseHandler, typename CharT>
+const char*
+Parser<ParseHandler, CharT>::nameIsArgumentsOrEval(Node node)
+{
+    MOZ_ASSERT(handler.isName(node), "must only call this function on known names");
+
+    if (handler.isEvalName(node, context))
+        return js_eval_str;
+    if (handler.isArgumentsName(node, context))
+        return js_arguments_str;
+    return nullptr;
+}
+
+template <class ParseHandler, typename CharT>
 bool
 Parser<ParseHandler, CharT>::checkIncDecOperand(Node operand, uint32_t operandOffset)
 {
-    if (handler.isNameAnyParentheses(operand)) {
-        if (const char* chars = handler.nameIsArgumentsEvalAnyParentheses(operand, context)) {
+    if (handler.isName(operand)) {
+        if (const char* chars = nameIsArgumentsOrEval(operand)) {
             if (!strictModeErrorAt(operandOffset, JSMSG_BAD_STRICT_ASSIGN, chars))
                 return false;
         }
     } else if (handler.isPropertyAccess(operand)) {
         // Permitted: no additional testing/fixup needed.
     } else if (handler.isFunctionCall(operand)) {
         // Assignment to function calls is forbidden in ES6.  We're still
         // somewhat concerned about sites using this in dead code, so forbid it
@@ -8302,17 +8314,17 @@ Parser<ParseHandler, CharT>::unaryExpr(Y
             return null();
 
         Node expr = unaryExpr(yieldHandling, TripledotProhibited);
         if (!expr)
             return null();
 
         // Per spec, deleting any unary expression is valid -- it simply
         // returns true -- except for one case that is illegal in strict mode.
-        if (handler.isNameAnyParentheses(expr)) {
+        if (handler.isName(expr)) {
             if (!strictModeErrorAt(exprOffset, JSMSG_DEPRECATED_DELETE_OPERAND))
                 return null();
 
             pc->sc()->setBindingsAccessedDynamically();
         }
 
         return handler.newDelete(begin, expr);
       }
@@ -8602,17 +8614,17 @@ Parser<ParseHandler, CharT>::memberExpr(
                     if (handler.isAsyncKeyword(lhs, context)) {
                         // |async (| can be the start of an async arrow
                         // function, so we need to defer reporting possible
                         // errors from destructuring syntax. To give better
                         // error messages, we only allow the AsyncArrowHead
                         // part of the CoverCallExpressionAndAsyncArrowHead
                         // syntax when the initial name is "async".
                         maybeAsyncArrow = true;
-                    } else if (handler.isEvalAnyParentheses(lhs, context)) {
+                    } else if (handler.isEvalName(lhs, context)) {
                         // Select the right EVAL op and flag pc as having a
                         // direct eval.
                         op = pc->sc()->strict() ? JSOP_STRICTEVAL : JSOP_EVAL;
                         pc->sc()->setBindingsAccessedDynamically();
                         pc->sc()->setHasDirectEval();
 
                         // In non-strict mode code, direct calls to eval can
                         // add variables to the call object.
@@ -8911,17 +8923,17 @@ Parser<ParseHandler, CharT>::checkDestru
     // those are already handled above.
 
     exprPossibleError->transferErrorsTo(possibleError);
 
     // Return early if a pending destructuring error is already present.
     if (possibleError->hasPendingDestructuringError())
         return true;
 
-    if (handler.isNameAnyParentheses(expr)) {
+    if (handler.isName(expr)) {
         checkDestructuringAssignmentName(expr, exprPos, possibleError);
         return true;
     }
 
     if (handler.isUnparenthesizedDestructuringPattern(expr)) {
         if (behavior == TargetBehavior::ForbidAssignmentPattern)
             possibleError->setPendingDestructuringErrorAt(exprPos, JSMSG_BAD_DESTRUCT_TARGET);
         return true;
@@ -8941,35 +8953,35 @@ Parser<ParseHandler, CharT>::checkDestru
     return true;
 }
 
 template <class ParseHandler, typename CharT>
 void
 Parser<ParseHandler, CharT>::checkDestructuringAssignmentName(Node name, TokenPos namePos,
                                                               PossibleError* possibleError)
 {
-    MOZ_ASSERT(handler.isNameAnyParentheses(name));
+    MOZ_ASSERT(handler.isName(name));
 
     // Return early if a pending destructuring error is already present.
     if (possibleError->hasPendingDestructuringError())
         return;
 
     if (pc->sc()->needStrictChecks()) {
-        if (handler.isArgumentsAnyParentheses(name, context)) {
+        if (handler.isArgumentsName(name, context)) {
             if (pc->sc()->strict()) {
                 possibleError->setPendingDestructuringErrorAt(namePos,
                                                               JSMSG_BAD_STRICT_ASSIGN_ARGUMENTS);
             } else {
                 possibleError->setPendingDestructuringWarningAt(namePos,
                                                                 JSMSG_BAD_STRICT_ASSIGN_ARGUMENTS);
             }
             return;
         }
 
-        if (handler.isEvalAnyParentheses(name, context)) {
+        if (handler.isEvalName(name, context)) {
             if (pc->sc()->strict()) {
                 possibleError->setPendingDestructuringErrorAt(namePos,
                                                               JSMSG_BAD_STRICT_ASSIGN_EVAL);
             } else {
                 possibleError->setPendingDestructuringWarningAt(namePos,
                                                                 JSMSG_BAD_STRICT_ASSIGN_EVAL);
             }
             return;
@@ -9494,17 +9506,17 @@ Parser<ParseHandler, CharT>::objectLiter
 
                     // Here we set a pending error so that later in the parse,
                     // once we've determined whether or not we're
                     // destructuring, the error can be reported or ignored
                     // appropriately.
                     possibleError->setPendingExpressionErrorAt(pos(), JSMSG_COLON_AFTER_ID);
                 }
 
-                if (const char* chars = handler.nameIsArgumentsEvalAnyParentheses(lhs, context)) {
+                if (const char* chars = nameIsArgumentsOrEval(lhs)) {
                     // |chars| is "arguments" or "eval" here.
                     if (!strictModeErrorAt(namePos.begin, JSMSG_BAD_STRICT_ASSIGN, chars))
                         return null();
                 }
 
                 Node rhs = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
                 if (!rhs)
                     return null();
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -163,17 +163,16 @@ class ParserBase : public StrictModeGett
     }
 
     ParserBase(JSContext* cx, LifoAlloc& alloc, const ReadOnlyCompileOptions& options,
                const char16_t* chars, size_t length, bool foldConstants,
                UsedNameTracker& usedNames, LazyScript* lazyOuterFunction);
     ~ParserBase();
 
     const char* getFilename() const { return tokenStream.getFilename(); }
-    JSVersion versionNumber() const { return tokenStream.versionNumber(); }
     TokenPos pos() const { return tokenStream.currentToken().pos; }
 
     // Determine whether |yield| is a valid name in the current context.
     bool yieldExpressionsSupported() const {
         return pc->isGenerator();
     }
 
     virtual bool strictMode() { return pc->sc()->strict(); }
@@ -857,16 +856,17 @@ class Parser final : public ParserBase, 
         PermitAssignmentToFunctionCalls,
         ForbidAssignmentToFunctionCalls
     };
 
     bool isValidSimpleAssignmentTarget(Node node,
                                        FunctionCallBehavior behavior = ForbidAssignmentToFunctionCalls);
 
   private:
+    const char* nameIsArgumentsOrEval(Node node);
     bool checkIncDecOperand(Node operand, uint32_t operandOffset);
     bool checkStrictAssignment(Node lhs);
 
     void reportMissingClosing(unsigned errorNumber, unsigned noteNumber, uint32_t openedPos);
 
     void reportRedeclaration(HandlePropertyName name, DeclarationKind prevKind, TokenPos pos,
                              uint32_t prevPos);
     bool notePositionalFormalParameter(Node fn, HandlePropertyName name, uint32_t beginPos,
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -54,33 +54,27 @@ class SyntaxParseHandler
 
         // This is needed for proper assignment-target handling.  ES6 formally
         // requires function calls *not* pass IsValidSimpleAssignmentTarget,
         // but at last check there were still sites with |f() = 5| and similar
         // in code not actually executed (or at least not executed enough to be
         // noticed).
         NodeFunctionCall,
 
-        // Nodes representing *parenthesized* IsValidSimpleAssignmentTarget
-        // nodes.  We can't simply treat all such parenthesized nodes
-        // identically, because in assignment and increment/decrement contexts
-        // ES6 says that parentheses constitute a syntax error.
-        //
-        //   var obj = {};
-        //   var val;
-        //   (val) = 3; (obj.prop) = 4;       // okay per ES5's little mind
-        //   [(a)] = [3]; [(obj.prop)] = [4]; // invalid ES6 syntax
-        //   // ...and so on for the other IsValidSimpleAssignmentTarget nodes
-        //
-        // We don't know in advance in the current parser when we're parsing
-        // in a place where name parenthesization changes meaning, so we must
-        // have multiple node values for these cases.
-        NodeParenthesizedArgumentsName,
-        NodeParenthesizedEvalName,
-        NodeParenthesizedName,
+        // Node representing normal names which don't require any special
+        // casing.
+        NodeName,
+
+        // Nodes representing the names "arguments" and "eval".
+        NodeArgumentsName,
+        NodeEvalName,
+
+        // Node representing the "async" name, which may actually be a
+        // contextual keyword.
+        NodePotentialAsyncKeyword,
 
         NodeDottedProperty,
         NodeElement,
 
         // Destructuring target patterns can't be parenthesized: |([a]) = [3];|
         // must be a syntax error.  (We can't use NodeGeneric instead of these
         // because that would trigger invalid-left-hand-side ReferenceError
         // semantics when SyntaxError semantics are desired.)
@@ -88,25 +82,16 @@ class SyntaxParseHandler
         NodeParenthesizedObject,
 
         // In rare cases a parenthesized |node| doesn't have the same semantics
         // as |node|.  Each such node has a special Node value, and we use a
         // different Node value to represent the parenthesized form.  See also
         // is{Unp,P}arenthesized*(Node), parenthesize(Node), and the various
         // functions that deal in NodeUnparenthesized* below.
 
-        // Nodes representing unparenthesized names.
-        NodeUnparenthesizedArgumentsName,
-        NodeUnparenthesizedEvalName,
-        NodeUnparenthesizedName,
-
-        // Node representing the "async" name, which may actually be a
-        // contextual keyword.
-        NodePotentialAsyncKeyword,
-
         // Valuable for recognizing potential destructuring patterns.
         NodeUnparenthesizedArray,
         NodeUnparenthesizedObject,
 
         // The directive prologue at the start of a FunctionBody or ScriptBody
         // is the longest sequence (possibly empty) of string literal
         // expression statements at the start of a function.  Thus we need this
         // to treat |"use strict";| as a possible Use Strict Directive and
@@ -150,50 +135,43 @@ class SyntaxParseHandler
     static bool isParenthesizedDestructuringPattern(Node node) {
         // Technically this isn't a destructuring target at all -- the grammar
         // doesn't treat it as such.  But we need to know when this happens to
         // consider it a SyntaxError rather than an invalid-left-hand-side
         // ReferenceError.
         return node == NodeParenthesizedArray || node == NodeParenthesizedObject;
     }
 
-    static bool isDestructuringPatternAnyParentheses(Node node) {
-        return isUnparenthesizedDestructuringPattern(node) ||
-                isParenthesizedDestructuringPattern(node);
-    }
-
   public:
     SyntaxParseHandler(JSContext* cx, LifoAlloc& alloc, LazyScript* lazyOuterFunction)
       : lastAtom(nullptr)
     {}
 
     static Node null() { return NodeFailure; }
 
     void prepareNodeForMutation(Node node) {}
     void freeTree(Node node) {}
 
-    void trace(JSTracer* trc) {}
-
     Node newName(PropertyName* name, const TokenPos& pos, JSContext* cx) {
         lastAtom = name;
         if (name == cx->names().arguments)
-            return NodeUnparenthesizedArgumentsName;
+            return NodeArgumentsName;
         if (pos.begin + strlen("async") == pos.end && name == cx->names().async)
             return NodePotentialAsyncKeyword;
         if (name == cx->names().eval)
-            return NodeUnparenthesizedEvalName;
-        return NodeUnparenthesizedName;
+            return NodeEvalName;
+        return NodeName;
     }
 
     Node newComputedName(Node expr, uint32_t start, uint32_t end) {
         return NodeGeneric;
     }
 
     Node newObjectLiteralPropertyName(JSAtom* atom, const TokenPos& pos) {
-        return NodeUnparenthesizedName;
+        return NodeName;
     }
 
     Node newNumber(double value, DecimalPoint decimalPoint, const TokenPos& pos) { return NodeGeneric; }
     Node newBooleanLiteral(bool cond, const TokenPos& pos) { return NodeGeneric; }
 
     Node newStringLiteral(JSAtom* atom, const TokenPos& pos) {
         lastAtom = atom;
         lastStringPos = pos;
@@ -396,35 +374,18 @@ class SyntaxParseHandler
         MOZ_ASSERT(kind == PNK_LET || kind == PNK_CONST);
         return NodeLexicalDeclaration;
     }
 
     bool isDeclarationList(Node node) {
         return node == NodeVarDeclaration || node == NodeLexicalDeclaration;
     }
 
-    Node singleBindingFromDeclaration(Node decl) {
-        MOZ_ASSERT(isDeclarationList(decl));
-
-        // This is, unfortunately, very dodgy.  Obviously NodeVarDeclaration
-        // and NodeLexicalDeclaration can store no info on the arbitrary
-        // number of bindings it could contain.
-        //
-        // But this method is called only for cloning for-in/of declarations
-        // as initialization targets.  That context simplifies matters.  If the
-        // binding is a single name, it'll always syntax-parse (or it would
-        // already have been rejected as assigning/binding a forbidden name).
-        // Otherwise the binding is a destructuring pattern.  But syntax
-        // parsing would *already* have aborted when it saw a destructuring
-        // pattern.  So we can just say any old thing here, because the only
-        // time we'll be wrong is a case that syntax parsing has already
-        // rejected.  Use NodeUnparenthesizedName so the SyntaxParseHandler
-        // Parser::cloneLeftHandSide can assert it sees only this.
-        return NodeUnparenthesizedName;
-    }
+    // This method should only be called from parsers using FullParseHandler.
+    Node singleBindingFromDeclaration(Node decl) = delete;
 
     Node newCatchList(const TokenPos& pos) {
         return NodeGeneric;
     }
 
     Node newCommaExpressionList(Node kid) {
         return NodeGeneric;
     }
@@ -470,80 +431,58 @@ class SyntaxParseHandler
     }
 
     void setOp(Node pn, JSOp op) {}
     void setListFlag(Node pn, unsigned flag) {}
     MOZ_MUST_USE Node parenthesize(Node node) {
         // A number of nodes have different behavior upon parenthesization, but
         // only in some circumstances.  Convert these nodes to special
         // parenthesized forms.
-        if (node == NodeUnparenthesizedArgumentsName)
-            return NodeParenthesizedArgumentsName;
-        if (node == NodeUnparenthesizedEvalName)
-            return NodeParenthesizedEvalName;
-        if (node == NodeUnparenthesizedName || node == NodePotentialAsyncKeyword)
-            return NodeParenthesizedName;
-
         if (node == NodeUnparenthesizedArray)
             return NodeParenthesizedArray;
         if (node == NodeUnparenthesizedObject)
             return NodeParenthesizedObject;
 
         // Other nodes need not be recognizable after parenthesization; convert
         // them to a generic node.
         if (node == NodeUnparenthesizedString ||
             node == NodeUnparenthesizedAssignment ||
             node == NodeUnparenthesizedUnary)
         {
             return NodeGeneric;
         }
 
+        // Convert parenthesized |async| to a normal name node.
+        if (node == NodePotentialAsyncKeyword)
+            return NodeName;
+
         // In all other cases, the parenthesized form of |node| is equivalent
         // to the unparenthesized form: return |node| unchanged.
         return node;
     }
     MOZ_MUST_USE Node setLikelyIIFE(Node pn) {
         return pn; // Remain in syntax-parse mode.
     }
     void setInDirectivePrologue(Node pn) {}
 
     bool isConstant(Node pn) { return false; }
 
-    bool isUnparenthesizedName(Node node) {
-        return node == NodeUnparenthesizedArgumentsName ||
-               node == NodeUnparenthesizedEvalName ||
-               node == NodeUnparenthesizedName ||
+    bool isName(Node node) {
+        return node == NodeName ||
+               node == NodeArgumentsName ||
+               node == NodeEvalName ||
                node == NodePotentialAsyncKeyword;
     }
 
-    bool isNameAnyParentheses(Node node) {
-        if (isUnparenthesizedName(node))
-            return true;
-        return node == NodeParenthesizedArgumentsName ||
-               node == NodeParenthesizedEvalName ||
-               node == NodeParenthesizedName;
-    }
-
-    bool isArgumentsAnyParentheses(Node node, JSContext* cx) {
-        return node == NodeUnparenthesizedArgumentsName || node == NodeParenthesizedArgumentsName;
+    bool isArgumentsName(Node node, JSContext* cx) {
+        return node == NodeArgumentsName;
     }
 
-    bool isEvalAnyParentheses(Node node, JSContext* cx) {
-        return node == NodeUnparenthesizedEvalName || node == NodeParenthesizedEvalName;
-    }
-
-    const char* nameIsArgumentsEvalAnyParentheses(Node node, JSContext* cx) {
-        MOZ_ASSERT(isNameAnyParentheses(node),
-                   "must only call this method on known names");
-
-        if (isEvalAnyParentheses(node, cx))
-            return js_eval_str;
-        if (isArgumentsAnyParentheses(node, cx))
-            return js_arguments_str;
-        return nullptr;
+    bool isEvalName(Node node, JSContext* cx) {
+        return node == NodeEvalName;
     }
 
     bool isAsyncKeyword(Node node, JSContext* cx) {
         return node == NodePotentialAsyncKeyword;
     }
 
     PropertyName* maybeDottedProperty(Node node) {
         // Note: |super.apply(...)| is a special form that calls an "apply"
--- a/js/src/frontend/TokenStream.h
+++ b/js/src/frontend/TokenStream.h
@@ -278,18 +278,16 @@ class TokenStreamAnyChars: public ErrorR
   public:
     // Accessors.
     const Token& currentToken() const { return tokens[cursor]; }
     bool isCurrentTokenType(TokenKind type) const {
         return currentToken().type == type;
     }
 
     bool getMutedErrors() const { return mutedErrors; }
-    JSVersion versionNumber() const { return VersionNumber(options().version); }
-    JSVersion versionWithFlags() const { return options().version; }
 
     MOZ_MUST_USE bool checkOptions();
 
   protected:
     PropertyName* reservedWordToPropertyName(TokenKind tt) const;
 
   public:
     PropertyName* currentName() const {
--- a/js/src/gdb/gdb-tests.cpp
+++ b/js/src/gdb/gdb-tests.cpp
@@ -74,18 +74,16 @@ main(int argc, const char** argv)
 
     checkBool(JS::InitSelfHostedCode(cx));
     JS::SetWarningReporter(cx, reportWarning);
 
     JSAutoRequest ar(cx);
 
     /* Create the global object. */
     JS::CompartmentOptions options;
-    options.behaviors().setVersion(JSVERSION_DEFAULT);
-
     RootedObject global(cx, checkPtr(JS_NewGlobalObject(cx, &global_class,
                         nullptr, JS::FireOnNewGlobalHook, options)));
     JSAutoCompartment ac(cx, global);
 
     /* Populate the global object with the standard globals,
        like Object and Array. */
     checkBool(JS_InitStandardClasses(cx, global));
 
--- a/js/src/jit-test/lib/prologue.js
+++ b/js/src/jit-test/lib/prologue.js
@@ -1,16 +1,13 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
 
-// explicitly run on ECMA 5
-version(185);
-
 var appendToActual = function(s) {
     actual += s + ',';
 }
 
 if (!("gczeal" in this)) {
   gczeal = function() { }
 }
 
--- a/js/src/jit-test/tests/auto-regress/bug648852.js
+++ b/js/src/jit-test/tests/auto-regress/bug648852.js
@@ -1,12 +1,12 @@
 // |jit-test| error:TypeError
 
 // Binary: cache/js-dbg-64-d3215d1e985a-linux
 // Flags: -m -n -a
 //
 var toString = 1;
 VERSION = "";
 function startTest() {
-    if (version) if (VERSION == "ECMA_1") {}
+    if (print) if (VERSION == "ECMA_1") {}
 }
 var VERSION = this;
 startTest();
--- a/js/src/jit-test/tests/for-of/bug-728079-js17-1.js
+++ b/js/src/jit-test/tests/for-of/bug-728079-js17-1.js
@@ -1,12 +1,10 @@
 // for-of does not trigger the JS 1.7 for-in destructuring special case.
 
-version(170);
-
 var data = [[1, 2, 3], [4, 5, 6, 7]];
 
 function test(vars, expr, result) {
     var s = '';
     eval("for (" + vars + " of data) s += (" + expr + ") + ';';");
     assertEq(s, result);
 }
 
--- a/js/src/jit-test/tests/for-of/bug-728079-js17-4.js
+++ b/js/src/jit-test/tests/for-of/bug-728079-js17-4.js
@@ -1,4 +1,3 @@
 // Test case from bug 785989 comment 3.
 
-version(170);
 Reflect.parse("for (let [a, b] of c) ;");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/inlining/exception-during-inlining-decision.js
@@ -0,0 +1,115 @@
+function runNearStackLimit(f) {
+  function t() {
+    try {
+      return t();
+    } catch (e) {
+      return f();
+    }
+  }
+    return t()
+}
+var helpers = function helpers() {
+  return {
+    get isCompatVersion9() {
+    }  };
+}();
+var testRunner = function testRunner() {
+  var testRunner = {
+    asyncTestHasNoErr: function asyncTestHasNoErr() {
+    },
+    runTests: function runTests(testsToRun) {
+      for (var i in testsToRun) {
+          this.runTest(i, testsToRun[i].name, testsToRun[i].body);
+      }
+    },
+    runTest: function runTest(testIndex, testName, testBody) {
+      try {
+        testBody();
+      } catch (ex) {
+      }
+    },
+    asyncTestBegin: function asyncTestBegin() {
+        return explicitAsyncTestExit ? `
+                    ` : `
+                    `;
+    }  };
+  return testRunner;
+}();
+var assert = function assert() {
+  var validate = function validate() {
+  };
+  return {
+    areEqual: function areEqual() {
+      validate( message);
+    },
+    areNotEqual: function areNotEqual() {
+    }  };
+}();
+class __c_19 {
+  constructor() {
+      this.foo = 'SimpleParent';
+  }
+}
+var __v_2735 = [{
+  body: function () {
+    class __c_23 extends __c_19 {
+      constructor() {
+          super()
+      }
+    }
+    let __v_2738 = new __c_23();
+  }
+}, {
+  body: function () {
+    class __c_26 extends __c_19 {
+      constructor() {
+          super();
+      }
+    }
+    let __v_2739 = new __c_26();
+  }
+}, {
+  body: function () {
+    class __c_29 extends __c_19 {
+      constructor() {
+            super()
+      }
+    }
+    let __v_2743 = new __c_29();
+    class __c_30 extends __c_19 {
+      constructor() {
+            super()
+            super();
+      }
+    }
+    let __v_2746 = new __c_30();
+  }
+}, {
+  body: function () {
+    class __c_34 extends __c_19 {}
+    let __v_2749 = new __c_34();
+  }
+}, {
+  body: function () {
+    class __c_87 extends __c_19 {
+      constructor() {
+        try {
+          assert.areEqual();
+        } catch (e) {}
+            eval('super();')
+      }
+    }
+    function __f_683(__v_2812) {
+ __v_2812.foo
+    }
+      __f_683(new __c_87())
+      runNearStackLimit(() => {
+        return __f_683();
+      })
+  }
+}, {
+  body: function () {
+  }
+}];
+  testRunner.runTests(__v_2735, {
+  });
\ No newline at end of file
--- a/js/src/jit-test/tests/jaeger/bug549602.js
+++ b/js/src/jit-test/tests/jaeger/bug549602.js
@@ -1,9 +1,8 @@
-version(180)
 function f1(code) {
     var c
     var t = code.replace(/s/, "")
     var f = new Function(code)
     var o
     e = v = f2(f, c)
 }
 function f2(f, e) {
--- a/js/src/jit/BaselineCacheIRCompiler.cpp
+++ b/js/src/jit/BaselineCacheIRCompiler.cpp
@@ -618,18 +618,17 @@ BaselineCacheIRCompiler::emitCallScripte
     // First, ensure our getter is non-lazy and has JIT code.
     {
         FailurePath* failure;
         if (!addFailurePath(&failure))
             return false;
 
         masm.loadPtr(getterAddr, callee);
         masm.branchIfFunctionHasNoScript(callee, failure->label());
-        masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), code);
-        masm.loadBaselineOrIonRaw(code, code, failure->label());
+        masm.loadJitCodeRaw(callee, code, failure->label());
     }
 
     allocator.discardStack(masm);
 
     AutoStubFrame stubFrame(*this);
     stubFrame.enter(masm, scratch);
 
     // Align the stack such that the JitFrameLayout is aligned on
@@ -1718,18 +1717,17 @@ BaselineCacheIRCompiler::emitCallScripte
     // the callee in scratch1.
     {
         FailurePath* failure;
         if (!addFailurePath(&failure))
             return false;
 
         masm.loadPtr(setterAddr, scratch1);
         masm.branchIfFunctionHasNoScript(scratch1, failure->label());
-        masm.loadPtr(Address(scratch1, JSFunction::offsetOfNativeOrScript()), scratch2);
-        masm.loadBaselineOrIonRaw(scratch2, scratch2, failure->label());
+        masm.loadJitCodeRaw(scratch1, scratch2, failure->label());
     }
 
     allocator.discardStack(masm);
 
     AutoStubFrame stubFrame(*this);
     stubFrame.enter(masm, scratch2);
 
     // Align the stack such that the JitFrameLayout is aligned on
@@ -1750,18 +1748,17 @@ BaselineCacheIRCompiler::emitCallScripte
     masm.Push(scratch1);
 
     // Push frame descriptor.
     masm.Push(scratch2);
 
     // Load callee->nargs in scratch2 and the JIT code in scratch.
     Label noUnderflow;
     masm.load16ZeroExtend(Address(scratch1, JSFunction::offsetOfNargs()), scratch2);
-    masm.loadPtr(Address(scratch1, JSFunction::offsetOfNativeOrScript()), scratch1);
-    masm.loadBaselineOrIonRaw(scratch1, scratch1, nullptr);
+    masm.loadJitCodeRaw(scratch1, scratch1, nullptr);
 
     // Handle arguments underflow.
     masm.branch32(Assembler::BelowOrEqual, scratch2, Imm32(1), &noUnderflow);
     {
         // Call the arguments rectifier.
         TrampolinePtr argumentsRectifier = cx_->runtime()->jitRuntime()->getArgumentsRectifier();
         masm.movePtr(argumentsRectifier, scratch1);
     }
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -4687,17 +4687,17 @@ BaselineCompiler::emit_JSOP_RESUME()
 
     // Load callee.
     Register callee = regs.takeAny();
     masm.unboxObject(Address(genObj, GeneratorObject::offsetOfCalleeSlot()), callee);
 
     // Load the script. Note that we don't relazify generator scripts, so it's
     // guaranteed to be non-lazy.
     Register scratch1 = regs.takeAny();
-    masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), scratch1);
+    masm.loadPtr(Address(callee, JSFunction::offsetOfScript()), scratch1);
 
     // Load the BaselineScript or call a stub if we don't have one.
     Label interpret;
     masm.loadPtr(Address(scratch1, JSScript::offsetOfBaselineScript()), scratch1);
     masm.branchPtr(Assembler::BelowOrEqual, scratch1, ImmPtr(BASELINE_DISABLED_SCRIPT), &interpret);
 
 #ifdef JS_TRACE_LOGGING
     if (!emitTraceLoggerResume(scratch1, regs))
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -2750,17 +2750,17 @@ ICCallStubCompiler::pushSpreadCallArgume
 
     // Push the callee and |this|.
     masm.pushValue(Address(BaselineFrameReg, STUB_FRAME_SIZE + (1 + isConstructing) * sizeof(Value)));
     masm.pushValue(Address(BaselineFrameReg, STUB_FRAME_SIZE + (2 + isConstructing) * sizeof(Value)));
 }
 
 Register
 ICCallStubCompiler::guardFunApply(MacroAssembler& masm, AllocatableGeneralRegisterSet regs,
-                                  Register argcReg, bool checkNative, FunApplyThing applyThing,
+                                  Register argcReg, FunApplyThing applyThing,
                                   Label* failure)
 {
     // Ensure argc == 2
     masm.branch32(Assembler::NotEqual, argcReg, Imm32(2), failure);
 
     // Stack looks like:
     //      [..., CalleeV, ThisV, Arg0V, Arg1V <MaybeReturnReg>]
 
@@ -2840,44 +2840,38 @@ ICCallStubCompiler::guardFunApply(MacroA
     Address calleeSlot(masm.getStackPointer(), ICStackValueOffset + (3 * sizeof(Value)));
     masm.loadValue(calleeSlot, val);
 
     masm.branchTestObject(Assembler::NotEqual, val, failure);
     Register callee = masm.extractObject(val, ExtractTemp1);
 
     masm.branchTestObjClass(Assembler::NotEqual, callee, regs.getAny(), &JSFunction::class_,
                             failure);
-    masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), callee);
+    masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrEnv()), callee);
 
     masm.branchPtr(Assembler::NotEqual, callee, ImmPtr(fun_apply), failure);
 
     // Load the |thisv|, ensure that it's a scripted function with a valid baseline or ion
     // script, or a native function.
     Address thisSlot(masm.getStackPointer(), ICStackValueOffset + (2 * sizeof(Value)));
     masm.loadValue(thisSlot, val);
 
     masm.branchTestObject(Assembler::NotEqual, val, failure);
     Register target = masm.extractObject(val, ExtractTemp1);
     regs.add(val);
     regs.takeUnchecked(target);
 
     masm.branchTestObjClass(Assembler::NotEqual, target, regs.getAny(), &JSFunction::class_,
                             failure);
 
-    if (checkNative) {
-        masm.branchIfInterpreted(target, failure);
-    } else {
-        Register temp = regs.takeAny();
-        masm.branchIfFunctionHasNoScript(target, failure);
-        masm.branchFunctionKind(Assembler::Equal, JSFunction::ClassConstructor,
-                                callee, temp, failure);
-        masm.loadPtr(Address(target, JSFunction::offsetOfNativeOrScript()), temp);
-        masm.loadBaselineOrIonRaw(temp, temp, failure);
-        regs.add(temp);
-    }
+    Register temp = regs.takeAny();
+    masm.branchIfFunctionHasNoScript(target, failure);
+    masm.branchFunctionKind(Assembler::Equal, JSFunction::ClassConstructor, callee, temp, failure);
+    masm.loadJitCodeRaw(target, temp, failure);
+    regs.add(temp);
     return target;
 }
 
 void
 ICCallStubCompiler::pushCallerArguments(MacroAssembler& masm, AllocatableGeneralRegisterSet regs)
 {
     // Initialize copyReg to point to start caller arguments vector.
     // Initialize argcReg to poitn to the end of it.
@@ -3120,25 +3114,23 @@ ICCallScriptedCompiler::generateStubCode
             masm.branchIfNotInterpretedConstructor(callee, regs.getAny(), &failure);
         } else {
             masm.branchIfFunctionHasNoScript(callee, &failure);
             masm.branchFunctionKind(Assembler::Equal, JSFunction::ClassConstructor, callee,
                                     regs.getAny(), &failure);
         }
     }
 
-    // Load the JSScript.
-    masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), callee);
-
     // Load the start of the target JitCode.
     Register code;
     if (!isConstructing_) {
         code = regs.takeAny();
-        masm.loadBaselineOrIonRaw(callee, code, &failure);
+        masm.loadJitCodeRaw(callee, code, &failure);
     } else {
+        masm.loadPtr(Address(callee, JSFunction::offsetOfScript()), callee);
         Address scriptCode(callee, JSScript::offsetOfBaselineOrIonRaw());
         masm.branchPtr(Assembler::Equal, scriptCode, ImmPtr(nullptr), &failure);
     }
 
     // We no longer need R1.
     regs.add(R1);
 
     // Push a stub frame so that we can perform a non-tail call.
@@ -3177,17 +3169,17 @@ ICCallScriptedCompiler::generateStubCode
         Label createdThisOK;
         masm.branchTestObject(Assembler::Equal, JSReturnOperand, &createdThisOK);
         masm.branchTestMagic(Assembler::Equal, JSReturnOperand, &createdThisOK);
         masm.assumeUnreachable("The return of CreateThis must be an object or uninitialized.");
         masm.bind(&createdThisOK);
 #endif
 
         // Reset the register set from here on in.
-        MOZ_ASSERT(JSReturnOperand == R0);
+        static_assert(JSReturnOperand == R0, "The code below needs to be adapted.");
         regs = availableGeneralRegs(0);
         regs.take(R0);
         argcReg = regs.takeAny();
 
         // Restore saved argc so we can use it to calculate the address to save
         // the resulting this object to.
         masm.pop(argcReg);
 
@@ -3202,37 +3194,36 @@ ICCallScriptedCompiler::generateStubCode
                                     STUB_FRAME_SIZE + isConstructing_ * sizeof(Value));
             masm.storeValue(R0, thisSlot);
         }
 
         // Restore the stub register from the baseline stub frame.
         masm.loadPtr(Address(masm.getStackPointer(), STUB_FRAME_SAVED_STUB_OFFSET), ICStubReg);
 
         // Reload callee script. Note that a GC triggered by CreateThis may
-        // have destroyed the callee BaselineScript and IonScript. CreateThis is
-        // safely repeatable though, so in this case we just leave the stub frame
-        // and jump to the next stub.
+        // have destroyed the callee BaselineScript and IonScript. CreateThis
+        // is safely repeatable though, so in this case we just leave the stub
+        // frame and jump to the next stub.
 
         // Just need to load the script now.
         if (isSpread_) {
             unsigned skipForCallee = (2 + isConstructing_) * sizeof(Value);
             masm.loadValue(Address(masm.getStackPointer(), skipForCallee + STUB_FRAME_SIZE), R0);
         } else {
             // Account for newTarget, if necessary
             unsigned nonArgsSkip = (1 + isConstructing_) * sizeof(Value);
             BaseValueIndex calleeSlot3(masm.getStackPointer(), argcReg, nonArgsSkip + STUB_FRAME_SIZE);
             masm.loadValue(calleeSlot3, R0);
         }
         callee = masm.extractObject(R0, ExtractTemp0);
         regs.add(R0);
         regs.takeUnchecked(callee);
-        masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), callee);
 
         code = regs.takeAny();
-        masm.loadBaselineOrIonRaw(callee, code, &failureLeaveStubFrame);
+        masm.loadJitCodeRaw(callee, code, &failureLeaveStubFrame);
 
         // Release callee register, but don't add ExtractTemp0 back into the pool
         // ExtractTemp0 is used later, and if it's allocated to some other register at that
         // point, it will get clobbered when used.
         if (callee != ExtractTemp0)
             regs.add(callee);
 
         if (canUseTailCallReg)
@@ -3374,17 +3365,17 @@ ICCall_ConstStringSplit::Compiler::gener
         masm.branchTestObject(Assembler::NotEqual, calleeVal, &failureRestoreArgc);
 
         // Ensure that callee is a function.
         Register calleeObj = masm.extractObject(calleeVal, ExtractTemp0);
         masm.branchTestObjClass(Assembler::NotEqual, calleeObj, scratchReg,
                                 &JSFunction::class_, &failureRestoreArgc);
 
         // Ensure that callee's function impl is the native intrinsic_StringSplitString.
-        masm.loadPtr(Address(calleeObj, JSFunction::offsetOfNativeOrScript()), scratchReg);
+        masm.loadPtr(Address(calleeObj, JSFunction::offsetOfNativeOrEnv()), scratchReg);
         masm.branchPtr(Assembler::NotEqual, scratchReg, ImmPtr(js::intrinsic_StringSplitString),
                        &failureRestoreArgc);
 
         regs.add(calleeVal);
     }
 
     // Guard sep.
     {
@@ -3529,17 +3520,16 @@ ICCall_Native::Compiler::generateStubCod
     // Values are on the stack left-to-right. Calling convention wants them
     // right-to-left so duplicate them on the stack in reverse order.
     // |this| and callee are pushed last.
     if (isSpread_)
         pushSpreadCallArguments(masm, regs, argcReg, /* isJitCall = */ false, isConstructing_);
     else
         pushCallArguments(masm, regs, argcReg, /* isJitCall = */ false, isConstructing_);
 
-
     // Native functions have the signature:
     //
     //    bool (*)(JSContext*, unsigned, Value* vp)
     //
     // Where vp[0] is space for callee/return value, vp[1] is |this|, and vp[2] onward
     // are the function arguments.
 
     // Initialize vp.
@@ -3568,17 +3558,17 @@ ICCall_Native::Compiler::generateStubCod
     // instruction to handle them, so we store the redirected pointer in the
     // stub and use that instead of the original one.
     masm.callWithABI(Address(ICStubReg, ICCall_Native::offsetOfNative()));
 #else
     if (ignoresReturnValue_) {
         masm.loadPtr(Address(callee, JSFunction::offsetOfJitInfo()), callee);
         masm.callWithABI(Address(callee, JSJitInfo::offsetOfIgnoresReturnValueNative()));
     } else {
-        masm.callWithABI(Address(callee, JSFunction::offsetOfNativeOrScript()));
+        masm.callWithABI(Address(callee, JSFunction::offsetOfNative()));
     }
 #endif
 
     // Test for failure.
     masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
 
     // Load the return value into R0.
     masm.loadValue(Address(masm.getStackPointer(), NativeExitFrameLayout::offsetOfResult()), R0);
@@ -3689,18 +3679,17 @@ ICCall_ScriptedApplyArray::Compiler::gen
     Register argcReg = R0.scratchReg();
     regs.take(argcReg);
     regs.takeUnchecked(ICTailCallReg);
 
     //
     // Validate inputs
     //
 
-    Register target = guardFunApply(masm, regs, argcReg, /*checkNative=*/false,
-                                    FunApply_Array, &failure);
+    Register target = guardFunApply(masm, regs, argcReg, FunApply_Array, &failure);
     if (regs.has(target)) {
         regs.take(target);
     } else {
         // If target is already a reserved reg, take another register for it, because it's
         // probably currently an ExtractTemp, which might get clobbered later.
         Register targetTemp = regs.takeAny();
         masm.movePtr(target, targetTemp);
         target = targetTemp;
@@ -3743,18 +3732,17 @@ ICCall_ScriptedApplyArray::Compiler::gen
     masm.load32(Address(argcReg, ObjectElements::offsetOfInitializedLength()), argcReg);
 
     masm.Push(argcReg);
     masm.Push(target);
     masm.Push(scratch);
 
     // Load nargs into scratch for underflow check, and then load jitcode pointer into target.
     masm.load16ZeroExtend(Address(target, JSFunction::offsetOfNargs()), scratch);
-    masm.loadPtr(Address(target, JSFunction::offsetOfNativeOrScript()), target);
-    masm.loadBaselineOrIonRaw(target, target, nullptr);
+    masm.loadJitCodeRaw(target, target, nullptr);
 
     // Handle arguments underflow.
     Label noUnderflow;
     masm.branch32(Assembler::AboveOrEqual, argcReg, scratch, &noUnderflow);
     {
         // Call the arguments rectifier.
         TrampolinePtr argumentsRectifier = cx->runtime()->jitRuntime()->getArgumentsRectifier();
         masm.movePtr(argumentsRectifier, target);
@@ -3785,18 +3773,17 @@ ICCall_ScriptedApplyArguments::Compiler:
     Register argcReg = R0.scratchReg();
     regs.take(argcReg);
     regs.takeUnchecked(ICTailCallReg);
 
     //
     // Validate inputs
     //
 
-    Register target = guardFunApply(masm, regs, argcReg, /*checkNative=*/false,
-                                    FunApply_MagicArgs, &failure);
+    Register target = guardFunApply(masm, regs, argcReg, FunApply_MagicArgs, &failure);
     if (regs.has(target)) {
         regs.take(target);
     } else {
         // If target is already a reserved reg, take another register for it, because it's
         // probably currently an ExtractTemp, which might get clobbered later.
         Register targetTemp = regs.takeAny();
         masm.movePtr(target, targetTemp);
         target = targetTemp;
@@ -3833,18 +3820,17 @@ ICCall_ScriptedApplyArguments::Compiler:
     masm.loadPtr(Address(BaselineFrameReg, 0), argcReg);
     masm.loadPtr(Address(argcReg, BaselineFrame::offsetOfNumActualArgs()), argcReg);
     masm.Push(argcReg);
     masm.Push(target);
     masm.Push(scratch);
 
     // Load nargs into scratch for underflow check, and then load jitcode pointer into target.
     masm.load16ZeroExtend(Address(target, JSFunction::offsetOfNargs()), scratch);
-    masm.loadPtr(Address(target, JSFunction::offsetOfNativeOrScript()), target);
-    masm.loadBaselineOrIonRaw(target, target, nullptr);
+    masm.loadJitCodeRaw(target, target, nullptr);
 
     // Handle arguments underflow.
     Label noUnderflow;
     masm.branch32(Assembler::AboveOrEqual, argcReg, scratch, &noUnderflow);
     {
         // Call the arguments rectifier.
         TrampolinePtr argumentsRectifier = cx->runtime()->jitRuntime()->getArgumentsRectifier();
         masm.movePtr(argumentsRectifier, target);
@@ -3884,36 +3870,35 @@ ICCall_ScriptedFunCall::Compiler::genera
     regs.take(R1);
 
     // Ensure callee is fun_call.
     masm.branchTestObject(Assembler::NotEqual, R1, &failure);
 
     Register callee = masm.extractObject(R1, ExtractTemp0);
     masm.branchTestObjClass(Assembler::NotEqual, callee, regs.getAny(), &JSFunction::class_,
                             &failure);
-    masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), callee);
+    masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrEnv()), callee);
     masm.branchPtr(Assembler::NotEqual, callee, ImmPtr(fun_call), &failure);
 
     // Ensure |this| is a scripted function with JIT code.
     BaseIndex thisSlot(masm.getStackPointer(), argcReg, TimesEight, ICStackValueOffset);
     masm.loadValue(thisSlot, R1);
 
     masm.branchTestObject(Assembler::NotEqual, R1, &failure);
     callee = masm.extractObject(R1, ExtractTemp0);
 
     masm.branchTestObjClass(Assembler::NotEqual, callee, regs.getAny(), &JSFunction::class_,
                             &failure);
     masm.branchIfFunctionHasNoScript(callee, &failure);
     masm.branchFunctionKind(Assembler::Equal, JSFunction::ClassConstructor,
                             callee, regs.getAny(), &failure);
-    masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), callee);
 
     // Load the start of the target JitCode.
     Register code = regs.takeAny();
-    masm.loadBaselineOrIonRaw(callee, code, &failure);
+    masm.loadJitCodeRaw(callee, code, &failure);
 
     // We no longer need R1.
     regs.add(R1);
 
     // Push a stub frame so that we can perform a non-tail call.
     enterStubFrame(masm, regs.getAny());
     if (canUseTailCallReg)
         regs.add(ICTailCallReg);
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -713,17 +713,17 @@ class ICCallStubCompiler : public ICStub
 
     void pushCallArguments(MacroAssembler& masm, AllocatableGeneralRegisterSet regs,
                            Register argcReg, bool isJitCall, bool isConstructing = false);
     void pushSpreadCallArguments(MacroAssembler& masm, AllocatableGeneralRegisterSet regs,
                                  Register argcReg, bool isJitCall, bool isConstructing);
     void guardSpreadCall(MacroAssembler& masm, Register argcReg, Label* failure,
                          bool isConstructing);
     Register guardFunApply(MacroAssembler& masm, AllocatableGeneralRegisterSet regs,
-                           Register argcReg, bool checkNative, FunApplyThing applyThing,
+                           Register argcReg, FunApplyThing applyThing,
                            Label* failure);
     void pushCallerArguments(MacroAssembler& masm, AllocatableGeneralRegisterSet regs);
     void pushArrayArguments(MacroAssembler& masm, Address arrayVal,
                             AllocatableGeneralRegisterSet regs);
 };
 
 class ICCall_Fallback : public ICMonitoredFallbackStub
 {
@@ -906,17 +906,17 @@ class ICCall_Native : public ICMonitored
     friend class ICStubSpace;
 
   protected:
     GCPtrFunction callee_;
     GCPtrObject templateObject_;
     uint32_t pcOffset_;
 
 #ifdef JS_SIMULATOR
-    void *native_;
+    void* native_;
 #endif
 
     ICCall_Native(JitCode* stubCode, ICStub* firstMonitorStub,
                   JSFunction* callee, JSObject* templateObject,
                   uint32_t pcOffset);
 
   public:
     static ICCall_Native* Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub,
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -4259,17 +4259,17 @@ CallIRGenerator::tryAttachArrayJoin()
     writer.arrayJoinResult(thisObjId);
 
     writer.returnFromIC();
 
     // The result of this stub does not need to be monitored because it will
     // always return a string.  We will add String to the stack typeset when
     // attaching this stub.
 
-    // Set the stub kind to Regular 
+    // Set the stub kind to Regular
     cacheIRStubKind_ = BaselineCacheIRStubKind::Regular;
 
     trackAttached("ArrayJoin");
     return true;
 }
 
 bool
 CallIRGenerator::tryAttachStub()
--- a/js/src/jit/CacheIRCompiler.cpp
+++ b/js/src/jit/CacheIRCompiler.cpp
@@ -1139,24 +1139,24 @@ FailurePath::canShareFailurePath(const F
     if (stackPushed_ != other.stackPushed_)
         return false;
 
     if (spilledRegs_.length() != other.spilledRegs_.length())
         return false;
 
     for (size_t i = 0; i < spilledRegs_.length(); i++) {
         if (spilledRegs_[i] != other.spilledRegs_[i])
-	    return false;
+            return false;
     }
 
     MOZ_ASSERT(inputs_.length() == other.inputs_.length());
 
     for (size_t i = 0; i < inputs_.length(); i++) {
         if (inputs_[i] != other.inputs_[i])
-	    return false;
+            return false;
     }
     return true;
 }
 
 bool
 CacheIRCompiler::addFailurePath(FailurePath** failure)
 {
     FailurePath newFailure;
@@ -1408,17 +1408,17 @@ CacheIRCompiler::emitGuardIsNativeFuncti
     if (!addFailurePath(&failure))
         return false;
 
     // Ensure obj is a function.
     const Class* clasp = &JSFunction::class_;
     masm.branchTestObjClass(Assembler::NotEqual, obj, scratch, clasp, failure->label());
 
     // Ensure function native matches.
-    masm.branchPtr(Assembler::NotEqual, Address(obj, JSFunction::offsetOfNativeOrScript()),
+    masm.branchPtr(Assembler::NotEqual, Address(obj, JSFunction::offsetOfNativeOrEnv()),
                    ImmPtr(nativeFunc), failure->label());
     return true;
 }
 
 bool
 CacheIRCompiler::emitGuardIsProxy()
 {
     Register obj = allocator.useRegister(masm, reader.objOperandId());
@@ -1780,17 +1780,17 @@ CacheIRCompiler::emitLoadFunctionLengthR
     // Bound functions might have a non-int32 length.
     Address boundLength(obj, FunctionExtended::offsetOfExtendedSlot(BOUND_FUN_LENGTH_SLOT));
     masm.branchTestInt32(Assembler::NotEqual, boundLength, failure->label());
     masm.unboxInt32(boundLength, scratch);
     masm.jump(&done);
 
     masm.bind(&interpreted);
     // Load the length from the function's script.
-    masm.loadPtr(Address(obj, JSFunction::offsetOfNativeOrScript()), scratch);
+    masm.loadPtr(Address(obj, JSFunction::offsetOfScript()), scratch);
     masm.load16ZeroExtend(Address(scratch, JSScript::offsetOfFunLength()), scratch);
 
     masm.bind(&done);
     EmitStoreResult(masm, scratch, JSVAL_TYPE_INT32, output);
     return true;
 }
 
 bool
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -2786,20 +2786,21 @@ CodeGenerator::emitLambdaInit(Register o
             uint16_t nargs;
             uint16_t flags;
         } s;
         uint32_t word;
     } u;
     u.s.nargs = info.nargs;
     u.s.flags = info.flags;
 
-    MOZ_ASSERT(JSFunction::offsetOfFlags() == JSFunction::offsetOfNargs() + 2);
+    static_assert(JSFunction::offsetOfFlags() == JSFunction::offsetOfNargs() + 2,
+                  "the code below needs to be adapted");
     masm.store32(Imm32(u.word), Address(output, JSFunction::offsetOfNargs()));
     masm.storePtr(ImmGCPtr(info.scriptOrLazyScript),
-                  Address(output, JSFunction::offsetOfNativeOrScript()));
+                  Address(output, JSFunction::offsetOfScriptOrLazyScript()));
     masm.storePtr(envChain, Address(output, JSFunction::offsetOfEnvironment()));
     masm.storePtr(ImmGCPtr(info.fun->displayAtom()), Address(output, JSFunction::offsetOfAtom()));
 }
 
 typedef bool (*SetFunNameFn)(JSContext*, HandleFunction, HandleValue, FunctionPrefixKind);
 static const VMFunction SetFunNameInfo =
     FunctionInfo<SetFunNameFn>(js::SetFunctionNameIfNoOwnName, "SetFunName");
 
@@ -4262,21 +4263,18 @@ CodeGenerator::visitCallGeneric(LCallGen
     // If we are constructing, also ensure the callee is a constructor.
     if (call->mir()->isConstructing()) {
         masm.branchIfNotInterpretedConstructor(calleereg, nargsreg, &invoke);
     } else {
         masm.branchIfFunctionHasNoScript(calleereg, &invoke);
         masm.branchFunctionKind(Assembler::Equal, JSFunction::ClassConstructor, calleereg, objreg, &invoke);
     }
 
-    // Knowing that calleereg is a non-native function, load the JSScript.
-    masm.loadPtr(Address(calleereg, JSFunction::offsetOfNativeOrScript()), objreg);
-
-    // Load script jitcode.
-    masm.loadBaselineOrIonRaw(objreg, objreg, &invoke);
+    // Knowing that calleereg is a non-native function, load the jit code.
+    masm.loadJitCodeRaw(calleereg, objreg, &invoke);
 
     // Nestle the StackPointer up to the argument vector.
     masm.freeStack(unusedStack);
 
     // Construct the IonFramePrefix.
     uint32_t descriptor = MakeFrameDescriptor(masm.framePushed(), JitFrame_IonJS,
                                               JitFrameLayout::Size());
     masm.Push(Imm32(call->numActualArgs()));
@@ -4287,17 +4285,17 @@ CodeGenerator::visitCallGeneric(LCallGen
     // We cannot have lowered to LCallGeneric with a known target. Assert that we didn't
     // add any undefineds in IonBuilder. NB: MCall::numStackArgs includes |this|.
     DebugOnly<unsigned> numNonArgsOnStack = 1 + call->isConstructing();
     MOZ_ASSERT(call->numActualArgs() == call->mir()->numStackArgs() - numNonArgsOnStack);
     masm.load16ZeroExtend(Address(calleereg, JSFunction::offsetOfNargs()), nargsreg);
     masm.branch32(Assembler::Above, nargsreg, Imm32(call->numActualArgs()), &thunk);
     masm.jump(&makeCall);
 
-    // Argument fixed needed. Load the ArgumentsRectifier.
+    // Argument fixup needed. Load the ArgumentsRectifier.
     masm.bind(&thunk);
     {
         TrampolinePtr argumentsRectifier = gen->jitRuntime()->getArgumentsRectifier();
         masm.movePtr(argumentsRectifier, objreg);
     }
 
     // Finally call the function in objreg.
     masm.bind(&makeCall);
@@ -4374,24 +4372,21 @@ CodeGenerator::visitCallKnown(LCallKnown
     }
 
     MOZ_ASSERT_IF(target->isClassConstructor(), call->isConstructing());
 
     // The calleereg is known to be a non-native function, but might point to
     // a LazyScript instead of a JSScript.
     masm.branchIfFunctionHasNoScript(calleereg, &uncompiled);
 
-    // Knowing that calleereg is a non-native function, load the JSScript.
-    masm.loadPtr(Address(calleereg, JSFunction::offsetOfNativeOrScript()), objreg);
-
-    // Load script jitcode.
+    // Load non-native jitcode from the script.
     if (call->mir()->needsArgCheck())
-        masm.loadBaselineOrIonRaw(objreg, objreg, &uncompiled);
+        masm.loadJitCodeRaw(calleereg, objreg, &uncompiled);
     else
-        masm.loadBaselineOrIonNoArgCheck(objreg, objreg, &uncompiled);
+        masm.loadJitCodeNoArgCheck(calleereg, objreg, &uncompiled);
 
     // Nestle the StackPointer up to the argument vector.
     masm.freeStack(unusedStack);
 
     // Construct the IonFramePrefix.
     uint32_t descriptor = MakeFrameDescriptor(masm.framePushed(), JitFrame_IonJS,
                                               JitFrameLayout::Size());
     masm.Push(Imm32(call->numActualArgs()));
@@ -4680,21 +4675,18 @@ CodeGenerator::emitApplyGeneric(T* apply
 
     // Guard that calleereg is an interpreted function with a JSScript.
     masm.branchIfFunctionHasNoScript(calleereg, &invoke);
 
     // Guard that calleereg is not a class constrcuctor
     masm.branchFunctionKind(Assembler::Equal, JSFunction::ClassConstructor,
                             calleereg, objreg, &invoke);
 
-    // Knowing that calleereg is a non-native function, load the JSScript.
-    masm.loadPtr(Address(calleereg, JSFunction::offsetOfNativeOrScript()), objreg);
-
-    // Load script jitcode.
-    masm.loadBaselineOrIonRaw(objreg, objreg, &invoke);
+    // Knowing that calleereg is a non-native function, load script's jitcode.
+    masm.loadJitCodeRaw(calleereg, objreg, &invoke);
 
     // Call with an Ion frame or a rectifier frame.
     {
         // Create the frame descriptor.
         unsigned pushed = masm.framePushed();
         Register stackSpace = extraStackSpace;
         masm.addPtr(Imm32(pushed), stackSpace);
         masm.makeFrameDescriptor(stackSpace, JitFrame_IonJS, JitFrameLayout::Size());
@@ -12818,17 +12810,17 @@ CodeGenerator::visitFinishBoundFunctionI
     {
         // Load the length property of a bound function.
         masm.unboxInt32(Address(target, boundLengthOffset), temp1);
         masm.jump(&lengthLoaded);
     }
     masm.bind(&isInterpreted);
     {
         // Load the length property of an interpreted function.
-        masm.loadPtr(Address(target, JSFunction::offsetOfNativeOrScript()), temp1);
+        masm.loadPtr(Address(target, JSFunction::offsetOfScript()), temp1);
         masm.load16ZeroExtend(Address(temp1, JSScript::offsetOfFunLength()), temp1);
     }
     masm.bind(&lengthLoaded);
 
     // Compute the bound function length: Max(0, target.length - argCount).
     Label nonNegative;
     masm.sub32(argCount, temp1);
     masm.branch32(Assembler::GreaterThanOrEqual, temp1, Imm32(0), &nonNegative);
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -4146,17 +4146,17 @@ IonBuilder::selectInliningTargets(const 
 
         trackOptimizationAttempt(TrackedStrategy::Call_Inline);
         trackTypeInfo(TrackedTypeSite::Call_Target, target);
 
         bool inlineable;
         InliningDecision decision = makeInliningDecision(target, callInfo);
         switch (decision) {
           case InliningDecision_Error:
-            return abort(AbortReason::Alloc);
+            return abort(AbortReason::Error);
           case InliningDecision_DontInline:
           case InliningDecision_WarmUpCountTooLow:
             inlineable = false;
             break;
           case InliningDecision_Inline:
             inlineable = true;
             break;
           default:
@@ -4358,17 +4358,17 @@ IonBuilder::inlineCallsite(const Inlinin
         JSObject* target = targets[0].target;
 
         trackOptimizationAttempt(TrackedStrategy::Call_Inline);
         trackTypeInfo(TrackedTypeSite::Call_Target, target);
 
         InliningDecision decision = makeInliningDecision(target, callInfo);
         switch (decision) {
           case InliningDecision_Error:
-            return abort(AbortReason::Alloc);
+            return abort(AbortReason::Error);
           case InliningDecision_DontInline:
             return InliningStatus_NotInlined;
           case InliningDecision_WarmUpCountTooLow:
             return InliningStatus_WarmUpCountTooLow;
           case InliningDecision_Inline:
             break;
         }
 
@@ -5109,17 +5109,17 @@ IonBuilder::jsop_funcall(uint32_t argc)
     if (!callInfo.init(current, argc))
         return abort(AbortReason::Alloc);
 
     // Try to inline the call.
     if (!zeroArguments) {
         InliningDecision decision = makeInliningDecision(target, callInfo);
         switch (decision) {
           case InliningDecision_Error:
-            return abort(AbortReason::Alloc);
+            return abort(AbortReason::Error);
           case InliningDecision_DontInline:
           case InliningDecision_WarmUpCountTooLow:
             break;
           case InliningDecision_Inline: {
             InliningStatus status;
             MOZ_TRY_VAR(status, inlineSingleCall(callInfo, target));
             if (status == InliningStatus_Inlined)
                 return Ok();
@@ -5341,17 +5341,17 @@ IonBuilder::jsop_funapplyarguments(uint3
     // Pop apply function.
     MDefinition* nativeFunc = current->pop();
     nativeFunc->setImplicitlyUsedUnchecked();
 
     // Try to inline the call.
     InliningDecision decision = makeInliningDecision(target, callInfo);
     switch (decision) {
       case InliningDecision_Error:
-        return abort(AbortReason::Alloc);
+        return abort(AbortReason::Error);
       case InliningDecision_DontInline:
       case InliningDecision_WarmUpCountTooLow:
         break;
       case InliningDecision_Inline: {
         InliningStatus status;
         MOZ_TRY_VAR(status, inlineSingleCall(callInfo, target));
         if (status == InliningStatus_Inlined)
             return Ok();
@@ -11084,17 +11084,17 @@ IonBuilder::getPropTryCommonGetter(bool*
         }
     }
 
     // Inline if we can, otherwise, forget it and just generate a call.
     if (commonGetter->isInterpreted()) {
         InliningDecision decision = makeInliningDecision(commonGetter, callInfo);
         switch (decision) {
           case InliningDecision_Error:
-            return abort(AbortReason::Alloc);
+            return abort(AbortReason::Error);
           case InliningDecision_DontInline:
           case InliningDecision_WarmUpCountTooLow:
             break;
           case InliningDecision_Inline: {
             InliningStatus status;
             MOZ_TRY_VAR(status, inlineScriptedCall(callInfo, commonGetter));
             if (status == InliningStatus_Inlined) {
                 *emitted = true;
@@ -11660,17 +11660,17 @@ IonBuilder::setPropTryCommonSetter(bool*
     // Ensure that we know we are calling a setter in case we inline it.
     callInfo.markAsSetter();
 
     // Inline the setter if we can.
     if (commonSetter->isInterpreted()) {
         InliningDecision decision = makeInliningDecision(commonSetter, callInfo);
         switch (decision) {
           case InliningDecision_Error:
-            return abort(AbortReason::Alloc);
+            return abort(AbortReason::Error);
           case InliningDecision_DontInline:
           case InliningDecision_WarmUpCountTooLow:
             break;
           case InliningDecision_Inline: {
             InliningStatus status;
             MOZ_TRY_VAR(status, inlineScriptedCall(callInfo, commonSetter));
             if (status == InliningStatus_Inlined) {
                 *emitted = true;
--- a/js/src/jit/IonCacheIRCompiler.cpp
+++ b/js/src/jit/IonCacheIRCompiler.cpp
@@ -1040,18 +1040,17 @@ IonCacheIRCompiler::emitCallScriptedGett
 
     // Check stack alignment. Add sizeof(uintptr_t) for the return address.
     MOZ_ASSERT(((masm.framePushed() + sizeof(uintptr_t)) % JitStackAlignment) == 0);
 
     // The getter has JIT code now and we will only discard the getter's JIT
     // code when discarding all JIT code in the Zone, so we can assume it'll
     // still have JIT code.
     MOZ_ASSERT(target->hasJITCode());
-    masm.loadPtr(Address(scratch, JSFunction::offsetOfNativeOrScript()), scratch);
-    masm.loadBaselineOrIonRaw(scratch, scratch, nullptr);
+    masm.loadJitCodeRaw(scratch, scratch, nullptr);
     masm.callJit(scratch);
     masm.storeCallResultValue(output);
 
     masm.freeStack(masm.framePushed() - framePushedBefore);
     return true;
 }
 
 bool
@@ -2068,18 +2067,17 @@ IonCacheIRCompiler::emitCallScriptedSett
 
     // Check stack alignment. Add sizeof(uintptr_t) for the return address.
     MOZ_ASSERT(((masm.framePushed() + sizeof(uintptr_t)) % JitStackAlignment) == 0);
 
     // The setter has JIT code now and we will only discard the setter's JIT
     // code when discarding all JIT code in the Zone, so we can assume it'll
     // still have JIT code.
     MOZ_ASSERT(target->hasJITCode());
-    masm.loadPtr(Address(scratch, JSFunction::offsetOfNativeOrScript()), scratch);
-    masm.loadBaselineOrIonRaw(scratch, scratch, nullptr);
+    masm.loadJitCodeRaw(scratch, scratch, nullptr);
     masm.callJit(scratch);
 
     masm.freeStack(masm.framePushed() - framePushedBefore);
     return true;
 }
 
 typedef bool (*SetArrayLengthFn)(JSContext*, HandleObject, HandleValue, bool);
 static const VMFunction SetArrayLengthInfo =
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -3602,19 +3602,17 @@ IonBuilder::InliningResult
 IonBuilder::inlineSimd(CallInfo& callInfo, JSFunction* target, SimdType type)
 {
     if (!JitSupportsSimd()) {
         trackOptimizationOutcome(TrackedOutcome::NoSimdJitSupport);
         return InliningStatus_NotInlined;
     }
 
     JSNative native = target->native();
-    const JSJitInfo* jitInfo = target->jitInfo();
-    MOZ_ASSERT(jitInfo && jitInfo->type() == JSJitInfo::InlinableNative);
-    SimdOperation simdOp = SimdOperation(jitInfo->nativeOp);
+    SimdOperation simdOp = SimdOperation(target->jitInfo()->nativeOp);
 
     switch(simdOp) {
       case SimdOperation::Constructor:
         // SIMD constructor calls are handled via inlineNonFunctionCall(), so
         // they won't show up here where target is required to be a JSFunction.
         // See also inlineConstructSimdObject().
         MOZ_CRASH("SIMD constructor call not expected.");
       case SimdOperation::Fn_check:
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -1667,27 +1667,29 @@ MacroAssembler::assertRectifierFramePare
         branch32(Assembler::Equal, frameType, Imm32(JitFrame_CppToJSJit), &checkOk);
         assumeUnreachable("Unrecognized frame type preceding RectifierFrame.");
         bind(&checkOk);
     }
 #endif
 }
 
 void
-MacroAssembler::loadBaselineOrIonRaw(Register script, Register dest, Label* failure)
+MacroAssembler::loadJitCodeRaw(Register func, Register dest, Label* failure)
 {
-    loadPtr(Address(script, JSScript::offsetOfBaselineOrIonRaw()), dest);
+    loadPtr(Address(func, JSFunction::offsetOfScript()), dest);
+    loadPtr(Address(dest, JSScript::offsetOfBaselineOrIonRaw()), dest);
     if (failure)
         branchTestPtr(Assembler::Zero, dest, dest, failure);
 }
 
 void
-MacroAssembler::loadBaselineOrIonNoArgCheck(Register script, Register dest, Label* failure)
+MacroAssembler::loadJitCodeNoArgCheck(Register func, Register dest, Label* failure)
 {
-    loadPtr(Address(script, JSScript::offsetOfBaselineOrIonSkipArgCheck()), dest);
+    loadPtr(Address(func, JSFunction::offsetOfScript()), dest);
+    loadPtr(Address(dest, JSScript::offsetOfBaselineOrIonSkipArgCheck()), dest);
     if (failure)
         branchTestPtr(Assembler::Zero, dest, dest, failure);
 }
 
 void
 MacroAssembler::loadBaselineFramePtr(Register framePtr, Register dest)
 {
     if (framePtr != dest)
@@ -2935,17 +2937,17 @@ MacroAssembler::maybeBranchTestType(MIRT
 
 void
 MacroAssembler::wasmCallImport(const wasm::CallSiteDesc& desc, const wasm::CalleeDesc& callee)
 {
     // Load the callee, before the caller's registers are clobbered.
     uint32_t globalDataOffset = callee.importGlobalDataOffset();
     loadWasmGlobalPtr(globalDataOffset + offsetof(wasm::FuncImportTls, code), ABINonArgReg0);
 
-    MOZ_ASSERT(ABINonArgReg0 != WasmTlsReg, "by constraint");
+    static_assert(ABINonArgReg0 != WasmTlsReg, "by constraint");
 
     // Switch to the callee's TLS and pinned registers and make the call.
     loadWasmGlobalPtr(globalDataOffset + offsetof(wasm::FuncImportTls, tls), WasmTlsReg);
     loadWasmPinnedRegsFromTls();
 
     call(desc, ABINonArgReg0);
 }
 
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -1912,18 +1912,18 @@ class MacroAssembler : public MacroAssem
     // provided and enabled, then instrumentation will be emitted around call
     // sites.
     bool emitProfilingInstrumentation_;
 
     // Record locations of the call sites.
     Vector<CodeOffset, 0, SystemAllocPolicy> profilerCallSites_;
 
   public:
-    void loadBaselineOrIonRaw(Register script, Register dest, Label* failure);
-    void loadBaselineOrIonNoArgCheck(Register callee, Register dest, Label* failure);
+    void loadJitCodeRaw(Register callee, Register dest, Label* failure);
+    void loadJitCodeNoArgCheck(Register callee, Register dest, Label* failure);
 
     void loadBaselineFramePtr(Register framePtr, Register dest);
 
     void pushBaselineFramePtr(Register framePtr, Register scratch) {
         loadBaselineFramePtr(framePtr, scratch);
         push(scratch);
     }
 
--- a/js/src/jit/RegisterSets.h
+++ b/js/src/jit/RegisterSets.h
@@ -122,20 +122,20 @@ class ValueOperand
         return payload_;
     }
     bool aliases(Register reg) const {
         return type_ == reg || payload_ == reg;
     }
     Register scratchReg() const {
         return payloadReg();
     }
-    bool operator==(const ValueOperand& o) const {
+    constexpr bool operator==(const ValueOperand& o) const {
         return type_ == o.type_ && payload_ == o.payload_;
     }
-    bool operator!=(const ValueOperand& o) const {
+    constexpr bool operator!=(const ValueOperand& o) const {
         return !(*this == o);
     }
 
 #elif defined(JS_PUNBOX64)
     Register value_;
 
   public:
     explicit constexpr ValueOperand(Register value)
@@ -146,20 +146,20 @@ class ValueOperand
         return value_;
     }
     bool aliases(Register reg) const {
         return value_ == reg;
     }
     Register scratchReg() const {
         return valueReg();
     }
-    bool operator==(const ValueOperand& o) const {
+    constexpr bool operator==(const ValueOperand& o) const {
         return value_ == o.value_;
     }
-    bool operator!=(const ValueOperand& o) const {
+    constexpr bool operator!=(const ValueOperand& o) const {
         return !(*this == o);
     }
 #endif
 
     ValueOperand() = default;
 };
 
 // Registers to hold either either a typed or untyped value.
@@ -494,17 +494,16 @@ class RegisterSet {
         return fpu_;
     }
     FloatRegisterSet& fpus() {
         return fpu_;
     }
     bool operator ==(const RegisterSet& other) const {
         return other.gpr_ == gpr_ && other.fpu_ == fpu_;
     }
-
 };
 
 // There are 2 use cases for register sets:
 //
 //   1. To serve as a pool of allocatable register. This is useful for working
 //      on the code produced by some stub where free registers are available, or
 //      when we can release some registers.
 //
--- a/js/src/jit/Registers.h
+++ b/js/src/jit/Registers.h
@@ -60,20 +60,20 @@ struct Register {
     }
     Encoding encoding() const {
         MOZ_ASSERT(Code(reg_) < Registers::Total);
         return reg_;
     }
     const char* name() const {
         return Registers::GetName(code());
     }
-    bool operator ==(Register other) const {
+    constexpr bool operator==(Register other) const {
         return reg_ == other.reg_;
     }
-    bool operator !=(Register other) const {
+    constexpr bool operator!=(Register other) const {
         return reg_ != other.reg_;
     }
     bool volatile_() const {
         return !!((SetType(1) << code()) & Registers::VolatileMask);
     }
     bool aliases(const Register& other) const {
         return reg_ == other.reg_;
     }
--- a/js/src/jit/arm/Assembler-arm.h
+++ b/js/src/jit/arm/Assembler-arm.h
@@ -412,17 +412,17 @@ enum VFPOp {
 ALUOp ALUNeg(ALUOp op, Register dest, Register scratch, Imm32* imm, Register* negDest);
 bool can_dbl(ALUOp op);
 bool condsAreSafe(ALUOp op);
 
 // If there is a variant of op that has a dest (think cmp/sub) return that
 // variant of it.
 ALUOp getDestVariant(ALUOp op);
 
-static const ValueOperand JSReturnOperand = ValueOperand(JSReturnReg_Type, JSReturnReg_Data);
+static constexpr ValueOperand JSReturnOperand{JSReturnReg_Type, JSReturnReg_Data};
 static const ValueOperand softfpReturnOperand = ValueOperand(r1, r0);
 
 // All of these classes exist solely to shuffle data into the various operands.
 // For example Operand2 can be an imm8, a register-shifted-by-a-constant or a
 // register-shifted-by-a-register. We represent this in C++ by having a base
 // class Operand2, which just stores the 32 bits of data as they will be encoded
 // in the instruction. You cannot directly create an Operand2 since it is
 // tricky, and not entirely sane to do so. Instead, you create one of its child
--- a/js/src/jit/arm/Trampoline-arm.cpp
+++ b/js/src/jit/arm/Trampoline-arm.cpp
@@ -524,18 +524,17 @@ JitRuntime::generateArgumentsRectifier(M
     // Construct JitFrameLayout.
     masm.ma_push(r0); // actual arguments.
     masm.ma_push(r1); // callee token
     masm.ma_push(r6); // frame descriptor.
 
     // Call the target function.
     // Note that this code assumes the function is JITted.
     masm.andPtr(Imm32(CalleeTokenMask), r1);
-    masm.ma_ldr(DTRAddr(r1, DtrOffImm(JSFunction::offsetOfNativeOrScript())), r3);
-    masm.loadBaselineOrIonRaw(r3, r3, nullptr);
+    masm.loadJitCodeRaw(r1, r3, nullptr);
     argumentsRectifierReturnOffset_ = masm.callJitNoProfiler(r3);
 
     // arg1
     //  ...
     // argN
     // num actual args
     // callee token
     // sizeDescriptor     <- sp now
--- a/js/src/jit/arm64/Trampoline-arm64.cpp
+++ b/js/src/jit/arm64/Trampoline-arm64.cpp
@@ -392,18 +392,17 @@ JitRuntime::generateArgumentsRectifier(M
     // Make that into a frame descriptor.
     masm.makeFrameDescriptor(r6, JitFrame_Rectifier, JitFrameLayout::Size());
 
     masm.push(r0,  // Number of actual arguments.
               r1,  // Callee token.
               r6); // Frame descriptor.
 
     // Load the address of the code that is getting called.
-    masm.Ldr(x3, MemOperand(x5, JSFunction::offsetOfNativeOrScript()));
-    masm.loadBaselineOrIonRaw(r3, r3, nullptr);
+    masm.loadJitCodeRaw(r5, r3, nullptr);
     argumentsRectifierReturnOffset_ = masm.callJitNoProfiler(r3);
 
     // Clean up!
     // Get the size of the stack frame, and clean up the later fixed frame.
     masm.Ldr(x4, MemOperand(masm.GetStackPointer64(), 24, vixl::PostIndex));
 
     // Now that the size of the stack frame sans the fixed frame has been loaded,
     // add that onto the stack pointer.
--- a/js/src/jit/mips32/MacroAssembler-mips32.h
+++ b/js/src/jit/mips32/MacroAssembler-mips32.h
@@ -25,17 +25,17 @@ struct ImmTag : public Imm32
 
 struct ImmType : public ImmTag
 {
     ImmType(JSValueType type)
       : ImmTag(JSVAL_TYPE_TO_TAG(type))
     { }
 };
 
-static const ValueOperand JSReturnOperand = ValueOperand(JSReturnReg_Type, JSReturnReg_Data);
+static constexpr ValueOperand JSReturnOperand{JSReturnReg_Type, JSReturnReg_Data};
 static const ValueOperand softfpReturnOperand = ValueOperand(v1, v0);
 
 static const int defaultShift = 3;
 static_assert(1 << defaultShift == sizeof(JS::Value), "The defaultShift is wrong");
 
 static const uint32_t LOW_32_MASK = (1LL << 32) - 1;
 static const int32_t LOW_32_OFFSET = 0;
 static const int32_t HIGH_32_OFFSET = 4;
--- a/js/src/jit/mips32/Trampoline-mips32.cpp
+++ b/js/src/jit/mips32/Trampoline-mips32.cpp
@@ -501,18 +501,17 @@ JitRuntime::generateArgumentsRectifier(M
     // Push callee token.
     masm.storePtr(calleeTokenReg, Address(StackPointer, sizeof(uintptr_t)));
     // Push frame descriptor.
     masm.storePtr(t0, Address(StackPointer, 0));
 
     // Call the target function.
     // Note that this code assumes the function is JITted.
     masm.andPtr(Imm32(CalleeTokenMask), calleeTokenReg);
-    masm.loadPtr(Address(calleeTokenReg, JSFunction::offsetOfNativeOrScript()), t1);
-    masm.loadBaselineOrIonRaw(t1, t1, nullptr);
+    masm.loadJitCodeRaw(calleeTokenReg, t1, nullptr);
     argumentsRectifierReturnOffset_ = masm.callJitNoProfiler(t1);
 
     // arg1
     //  ...
     // argN
     // num actual args
     // callee token
     // sizeDescriptor     <- sp now
--- a/js/src/jit/mips64/MacroAssembler-mips64.h
+++ b/js/src/jit/mips64/MacroAssembler-mips64.h
@@ -35,17 +35,17 @@ struct ImmShiftedTag : public ImmWord
 
 struct ImmTag : public Imm32
 {
     ImmTag(JSValueTag mask)
       : Imm32(int32_t(mask))
     { }
 };
 
-static const ValueOperand JSReturnOperand = ValueOperand(JSReturnReg);
+static constexpr ValueOperand JSReturnOperand{JSReturnReg};
 
 static const int defaultShift = 3;
 static_assert(1 << defaultShift == sizeof(JS::Value), "The defaultShift is wrong");
 
 class MacroAssemblerMIPS64 : public MacroAssemblerMIPSShared
 {
   public:
     using MacroAssemblerMIPSShared::ma_b;
--- a/js/src/jit/mips64/Trampoline-mips64.cpp
+++ b/js/src/jit/mips64/Trampoline-mips64.cpp
@@ -546,18 +546,17 @@ JitRuntime::generateArgumentsRectifier(M
     // Push callee token.
     masm.storePtr(calleeTokenReg, Address(StackPointer, sizeof(uintptr_t)));
     // Push frame descriptor.
     masm.storePtr(t2, Address(StackPointer, 0));
 
     // Call the target function.
     // Note that this code assumes the function is JITted.
     masm.andPtr(Imm32(uint32_t(CalleeTokenMask)), calleeTokenReg);
-    masm.loadPtr(Address(calleeTokenReg, JSFunction::offsetOfNativeOrScript()), t1);
-    masm.loadBaselineOrIonRaw(t1, t1, nullptr);
+    masm.loadJitCodeRaw(calleeTokenReg, t1, nullptr);
     argumentsRectifierReturnOffset_ = masm.callJitNoProfiler(t1);
 
     // Remove the rectifier frame.
     // t2 <- descriptor with FrameType.
     masm.loadPtr(Address(StackPointer, 0), t2);
     masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), t2); // t2 <- descriptor.
 
     // Discard descriptor, calleeToken and number of actual arguments.
--- a/js/src/jit/x64/Trampoline-x64.cpp
+++ b/js/src/jit/x64/Trampoline-x64.cpp
@@ -507,18 +507,17 @@ JitRuntime::generateArgumentsRectifier(M
     // Construct JitFrameLayout.
     masm.push(rdx); // numActualArgs
     masm.push(rax); // callee token
     masm.push(r9); // descriptor
 
     // Call the target function.
     // Note that this code assumes the function is JITted.
     masm.andq(Imm32(uint32_t(CalleeTokenMask)), rax);
-    masm.loadPtr(Address(rax, JSFunction::offsetOfNativeOrScript()), rax);
-    masm.loadBaselineOrIonRaw(rax, rax, nullptr);
+    masm.loadJitCodeRaw(rax, rax, nullptr);
     argumentsRectifierReturnOffset_ = masm.callJitNoProfiler(rax);
 
     // Remove the rectifier frame.
     masm.pop(r9);             // r9 <- descriptor with FrameType.
     masm.shrq(Imm32(FRAMESIZE_SHIFT), r9);
     masm.pop(r11);            // Discard calleeToken.
     masm.pop(r11);            // Discard numActualArgs.
     masm.addq(r9, rsp);       // Discard pushed arguments.
--- a/js/src/jit/x86/Assembler-x86.h
+++ b/js/src/jit/x86/Assembler-x86.h
@@ -223,17 +223,17 @@ HighWord(const Operand& op) {
     switch (op.kind()) {
       case Operand::MEM_REG_DISP: return Operand(HighWord(op.toAddress()));
       case Operand::MEM_SCALE:    return Operand(HighWord(op.toBaseIndex()));
       default:                    MOZ_CRASH("Invalid operand type");
     }
 }
 
 // Return operand from a JS -> JS call.
-static const ValueOperand JSReturnOperand = ValueOperand(JSReturnReg_Type, JSReturnReg_Data);
+static constexpr ValueOperand JSReturnOperand{JSReturnReg_Type, JSReturnReg_Data};
 
 class Assembler : public AssemblerX86Shared
 {
     void writeRelocation(JmpSrc src) {
         jumpRelocations_.writeUnsigned(src.offset());
     }
     void addPendingJump(JmpSrc src, ImmPtr target, Relocation::Kind kind) {
         enoughMemory_ &= jumps_.append(RelativePatch(src.offset(), target.value, kind));
--- a/js/src/jit/x86/Trampoline-x86.cpp
+++ b/js/src/jit/x86/Trampoline-x86.cpp
@@ -501,18 +501,17 @@ JitRuntime::generateArgumentsRectifier(M
     // Construct JitFrameLayout.
     masm.push(edx); // number of actual arguments
     masm.push(eax); // callee token
     masm.push(ebx); // descriptor
 
     // Call the target function.
     // Note that this assumes the function is JITted.
     masm.andl(Imm32(CalleeTokenMask), eax);
-    masm.loadPtr(Address(eax, JSFunction::offsetOfNativeOrScript()), eax);
-    masm.loadBaselineOrIonRaw(eax, eax, nullptr);
+    masm.loadJitCodeRaw(eax, eax, nullptr);
     argumentsRectifierReturnOffset_ = masm.callJitNoProfiler(eax);
 
     // Remove the rectifier frame.
     masm.pop(ebx);            // ebx <- descriptor with FrameType.
     masm.shrl(Imm32(FRAMESIZE_SHIFT), ebx); // ebx <- descriptor.
     masm.pop(edi);            // Discard calleeToken.
     masm.pop(edi);            // Discard number of actual arguments.
 
--- a/js/src/jsapi-tests/testPreserveJitCode.cpp
+++ b/js/src/jsapi-tests/testPreserveJitCode.cpp
@@ -86,12 +86,11 @@ testPreserveJitCode(bool preserveJitCode
     return true;
 }
 
 JSObject*
 createTestGlobal(bool preserveJitCode)
 {
     JS::CompartmentOptions options;
     options.creationOptions().setPreserveJitCode(preserveJitCode);
-    options.behaviors().setVersion(JSVERSION_DEFAULT);
     return JS_NewGlobalObject(cx, getGlobalClass(), nullptr, JS::FireOnNewGlobalHook, options);
 }
 END_TEST(test_PreserveJitCode)
--- a/js/src/jsapi-tests/testWeakMap.cpp
+++ b/js/src/jsapi-tests/testWeakMap.cpp
@@ -232,18 +232,16 @@ JSObject* newDelegate()
         &delegateClassOps,
         JS_NULL_CLASS_SPEC,
         &delegateClassExtension,
         JS_NULL_OBJECT_OPS
     };
 
     /* Create the global object. */
     JS::CompartmentOptions options;
-    options.behaviors().setVersion(JSVERSION_DEFAULT);
-
     JS::RootedObject global(cx, JS_NewGlobalObject(cx, Jsvalify(&delegateClass), nullptr,
                                                    JS::FireOnNewGlobalHook, options));
     if (!global)
         return nullptr;
 
     JS_SetReservedSlot(global, 0, JS::Int32Value(42));
     return global;
 }
--- a/js/src/jsapi-tests/tests.cpp
+++ b/js/src/jsapi-tests/tests.cpp
@@ -82,17 +82,16 @@ bool JSAPITest::definePrint()
 JSObject* JSAPITest::createGlobal(JSPrincipals* principals)
 {
     /* Create the global object. */
     JS::RootedObject newGlobal(cx);
     JS::CompartmentOptions options;
 #ifdef ENABLE_STREAMS
     options.creationOptions().setStreamsEnabled(true);
 #endif
-    options.behaviors().setVersion(JSVERSION_DEFAULT);
     newGlobal = JS_NewGlobalObject(cx, getGlobalClass(), principals, JS::FireOnNewGlobalHook,
                                    options);
     if (!newGlobal)
         return nullptr;
 
     JSAutoCompartment ac(cx, newGlobal);
 
     // Populate the global object with the standard globals like Object and
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -584,22 +584,16 @@ JS_PUBLIC_API(void)
 JS::SetSingleThreadedExecutionCallbacks(JSContext* cx,
                                         BeginSingleThreadedExecutionCallback begin,
                                         EndSingleThreadedExecutionCallback end)
 {
     cx->runtime()->beginSingleThreadedExecutionCallback = begin;
     cx->runtime()->endSingleThreadedExecutionCallback = end;
 }
 
-JS_PUBLIC_API(JSVersion)
-JS_GetVersion(JSContext* cx)
-{
-    return VersionNumber(cx->findVersion());
-}
-
 static const struct v2smap {
     JSVersion   version;
     const char* string;
 } v2smap[] = {
     {JSVERSION_ECMA_3,  "ECMAv3"},
     {JSVERSION_1_6,     "1.6"},
     {JSVERSION_1_7,     "1.7"},
     {JSVERSION_1_8,     "1.8"},
@@ -3976,18 +3970,16 @@ AutoFile::open(JSContext* cx, const char
     }
     return true;
 }
 
 void
 JS::TransitiveCompileOptions::copyPODTransitiveOptions(const TransitiveCompileOptions& rhs)
 {
     mutedErrors_ = rhs.mutedErrors_;
-    version = rhs.version;
-    versionSet = rhs.versionSet;
     utf8 = rhs.utf8;
     selfHostingMode = rhs.selfHostingMode;
     canLazilyParse = rhs.canLazilyParse;
     strictOption = rhs.strictOption;
     extraWarningsOption = rhs.extraWarningsOption;
     forEachStatementOption = rhs.forEachStatementOption;
     werrorOption = rhs.werrorOption;
     asmJSOption = rhs.asmJSOption;
@@ -4098,22 +4090,20 @@ JS::OwningCompileOptions::setIntroducerF
 
     // OwningCompileOptions always owns introducerFilename_, so this cast is okay.
     js_free(const_cast<char*>(introducerFilename_));
 
     introducerFilename_ = copy;
     return true;
 }
 
-JS::CompileOptions::CompileOptions(JSContext* cx, JSVersion version)
+JS::CompileOptions::CompileOptions(JSContext* cx)
     : ReadOnlyCompileOptions(), elementRoot(cx), elementAttributeNameRoot(cx),
       introductionScriptRoot(cx)
 {
-    this->version = (version != JSVERSION_UNKNOWN) ? version : cx->findVersion();
-
     strictOption = cx->options().strictMode();
     extraWarningsOption = cx->compartment()->behaviors().extraWarnings(cx);
     forEachStatementOption = cx->options().forEachStatement();
     isProbablySystemOrAddonCode = cx->compartment()->isProbablySystemOrAddonCode();
     werrorOption = cx->options().werror();
     if (!cx->options().asmJS())
         asmJSOption = AsmJSOption::Disabled;
     else if (cx->compartment()->debuggerObservesAsmJS())
@@ -4795,18 +4785,16 @@ Evaluate(JSContext* cx, ScopeKind scopeK
     MOZ_ASSERT_IF(!IsGlobalLexicalEnvironment(env), scopeKind == ScopeKind::NonSyntactic);
 
     options.setIsRunOnce(true);
     RootedScript script(cx, frontend::CompileGlobalScript(cx, cx->tempLifoAlloc(),
                                                           scopeKind, options, srcBuf));
     if (!script)
         return false;
 
-    MOZ_ASSERT(script->getVersion() == options.version);
-
     bool result = Execute(cx, script, *env,
                           options.noScriptRval ? nullptr : rval.address());
 
     return result;
 }
 
 static bool
 Evaluate(JSContext* cx, AutoObjectVector& envChain, const ReadOnlyCompileOptions& optionsArg,
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1118,19 +1118,16 @@ class MOZ_RAII JSAutoRequest
 
 #if 0
   private:
     static void* operator new(size_t) CPP_THROW_NEW { return 0; }
     static void operator delete(void*, size_t) { }
 #endif
 };
 
-extern JS_PUBLIC_API(JSVersion)
-JS_GetVersion(JSContext* cx);
-
 extern JS_PUBLIC_API(const char*)
 JS_VersionToString(JSVersion version);
 
 extern JS_PUBLIC_API(JSVersion)
 JS_StringToVersion(const char* string);
 
 namespace JS {
 
@@ -2589,30 +2586,22 @@ class JS_PUBLIC_API(CompartmentBehaviors
             ForceTrue,
             ForceFalse
         };
 
         Mode mode_;
     };
 
     CompartmentBehaviors()
-      : version_(JSVERSION_UNKNOWN)
-      , discardSource_(false)
+      : discardSource_(false)
       , disableLazyParsing_(false)
       , singletonsAsTemplates_(true)
     {
     }
 
-    JSVersion version() const { return version_; }
-    CompartmentBehaviors& setVersion(JSVersion aVersion) {
-        MOZ_ASSERT(aVersion != JSVERSION_UNKNOWN);
-        version_ = aVersion;
-        return *this;
-    }
-
     // For certain globals, we know enough about the code that will run in them
     // that we can discard script source entirely.
     bool discardSource() const { return discardSource_; }
     CompartmentBehaviors& setDiscardSource(bool flag) {
         discardSource_ = flag;
         return *this;
     }
 
@@ -2629,17 +2618,16 @@ class JS_PUBLIC_API(CompartmentBehaviors
         return singletonsAsTemplates_;
     }
     CompartmentBehaviors& setSingletonsAsValues() {
         singletonsAsTemplates_ = false;
         return *this;
     }
 
   private:
-    JSVersion version_;
     bool discardSource_;
     bool disableLazyParsing_;
     Override extraWarningsOverride_;
 
     // To XDR singletons, we need to ensure that all singletons are all used as
     // templates, by making JSOP_OBJECT return a clone of the JSScript
     // singleton, instead of returning the value which is baked in the JSScript.
     bool singletonsAsTemplates_;
@@ -4025,27 +4013,21 @@ class JS_FRIEND_API(TransitiveCompileOpt
     // for which it is loaded. Callers should set this flag for cross-origin
     // scripts, and it will be propagated appropriately to child scripts and
     // passed back in JSErrorReports.
     bool mutedErrors_;
     const char* filename_;
     const char* introducerFilename_;
     const char16_t* sourceMapURL_;
 
-    // This constructor leaves 'version' set to JSVERSION_UNKNOWN. The structure
-    // is unusable until that's set to something more specific; the derived
-    // classes' constructors take care of that, in ways appropriate to their
-    // purpose.
     TransitiveCompileOptions()
       : mutedErrors_(false),
         filename_(nullptr),
         introducerFilename_(nullptr),
         sourceMapURL_(nullptr),
-        version(JSVERSION_UNKNOWN),
-        versionSet(false),
         utf8(false),
         selfHostingMode(false),
         canLazilyParse(true),
         strictOption(false),
         extraWarningsOption(false),
         forEachStatementOption(false),
         werrorOption(false),
         asmJSOption(AsmJSOption::Disabled),
@@ -4071,18 +4053,16 @@ class JS_FRIEND_API(TransitiveCompileOpt
     const char* filename() const { return filename_; }
     const char* introducerFilename() const { return introducerFilename_; }
     const char16_t* sourceMapURL() const { return sourceMapURL_; }
     virtual JSObject* element() const = 0;
     virtual JSString* elementAttributeName() const = 0;
     virtual JSScript* introductionScript() const = 0;
 
     // POD options.
-    JSVersion version;
-    bool versionSet;
     bool utf8;
     bool selfHostingMode;
     bool canLazilyParse;
     bool strictOption;
     bool extraWarningsOption;
     bool forEachStatementOption;
     bool werrorOption;
     AsmJSOption asmJSOption;
@@ -4178,20 +4158,17 @@ class JS_FRIEND_API(ReadOnlyCompileOptio
  */
 class JS_FRIEND_API(OwningCompileOptions) : public ReadOnlyCompileOptions
 {
     PersistentRootedObject elementRoot;
     PersistentRootedString elementAttributeNameRoot;
     PersistentRootedScript introductionScriptRoot;
 
   public:
-    // A minimal constructor, for use with OwningCompileOptions::copy. This
-    // leaves |this.version| set to JSVERSION_UNKNOWN; the instance
-    // shouldn't be used until we've set that to something real (as |copy|
-    // will).
+    // A minimal constructor, for use with OwningCompileOptions::copy.
     explicit OwningCompileOptions(JSContext* cx);
     ~OwningCompileOptions();
 
     JSObject* element() const override { return elementRoot; }
     JSString* elementAttributeName() const override { return elementAttributeNameRoot; }
     JSScript* introductionScript() const override { return introductionScriptRoot; }
 
     // Set this to a copy of |rhs|. Return false on OOM.
@@ -4216,21 +4193,16 @@ class JS_FRIEND_API(OwningCompileOptions
     OwningCompileOptions& setIntroductionScript(JSScript* s) {
         introductionScriptRoot = s;
         return *this;
     }
     OwningCompileOptions& setMutedErrors(bool mute) {
         mutedErrors_ = mute;
         return *this;
     }
-    OwningCompileOptions& setVersion(JSVersion v) {
-        version = v;
-        versionSet = true;
-        return *this;
-    }
     OwningCompileOptions& setUTF8(bool u) { utf8 = u; return *this; }
     OwningCompileOptions& setColumn(unsigned c) { column = c; return *this; }
     OwningCompileOptions& setScriptSourceOffset(unsigned o) { scriptSourceOffset = o; return *this; }
     OwningCompileOptions& setIsRunOnce(bool once) { isRunOnce = once; return *this; }
     OwningCompileOptions& setNoScriptRval(bool nsr) { noScriptRval = nsr; return *this; }
     OwningCompileOptions& setSelfHostingMode(bool shm) { selfHostingMode = shm; return *this; }
     OwningCompileOptions& setCanLazilyParse(bool clp) { canLazilyParse = clp; return *this; }
     OwningCompileOptions& setSourceIsLazy(bool l) { sourceIsLazy = l; return *this; }
@@ -4261,17 +4233,17 @@ class JS_FRIEND_API(OwningCompileOptions
  */
 class MOZ_STACK_CLASS JS_FRIEND_API(CompileOptions) final : public ReadOnlyCompileOptions
 {
     RootedObject elementRoot;
     RootedString elementAttributeNameRoot;
     RootedScript introductionScriptRoot;
 
   public:
-    explicit CompileOptions(JSContext* cx, JSVersion version = JSVERSION_UNKNOWN);
+    explicit CompileOptions(JSContext* cx);
     CompileOptions(JSContext* cx, const ReadOnlyCompileOptions& rhs)
       : ReadOnlyCompileOptions(), elementRoot(cx), elementAttributeNameRoot(cx),
         introductionScriptRoot(cx)
     {
         copyPODOptions(rhs);
 
         filename_ = rhs.filename();
         introducerFilename_ = rhs.introducerFilename();
@@ -4313,21 +4285,16 @@ class MOZ_STACK_CLASS JS_FRIEND_API(Comp
     CompileOptions& setIntroductionScript(JSScript* s) {
         introductionScriptRoot = s;
         return *this;
     }
     CompileOptions& setMutedErrors(bool mute) {
         mutedErrors_ = mute;
         return *this;
     }
-    CompileOptions& setVersion(JSVersion v) {
-        version = v;
-        versionSet = true;
-        return *this;
-    }
     CompileOptions& setUTF8(bool u) { utf8 = u; return *this; }
     CompileOptions& setColumn(unsigned c) { column = c; return *this; }
     CompileOptions& setScriptSourceOffset(unsigned o) { scriptSourceOffset = o; return *this; }
     CompileOptions& setIsRunOnce(bool once) { isRunOnce = once; return *this; }
     CompileOptions& setNoScriptRval(bool nsr) { noScriptRval = nsr; return *this; }
     CompileOptions& setSelfHostingMode(bool shm) { selfHostingMode = shm; return *this; }
     CompileOptions& setCanLazilyParse(bool clp) { canLazilyParse = clp; return *this; }
     CompileOptions& setSourceIsLazy(bool l) { sourceIsLazy = l; return *this; }
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -1588,31 +1588,16 @@ JSContext::resetJitStackLimit()
 }
 
 void
 JSContext::initJitStackLimit()
 {
     resetJitStackLimit();
 }
 
-JSVersion
-JSContext::findVersion()
-{
-    if (JSScript* script = currentScript(nullptr, ALLOW_CROSS_COMPARTMENT))
-        return script->getVersion();
-
-    if (compartment() && compartment()->behaviors().version() != JSVERSION_UNKNOWN)
-        return compartment()->behaviors().version();
-
-    if (!CurrentThreadCanAccessRuntime(runtime()))
-        return JSVERSION_DEFAULT;
-
-    return runtime()->defaultVersion();
-}
-
 void
 JSContext::updateMallocCounter(size_t nbytes)
 {
     if (!zone()) {
         runtime()->updateMallocCounter(nbytes);
         return;
     }
 
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -667,26 +667,16 @@ struct JSContext : public JS::RootingCon
 
     /* Client opaque pointer. */
     js::UnprotectedData<void*> data;
 
     void initJitStackLimit();
     void resetJitStackLimit();
 
   public:
-    /*
-     * Return:
-     * - The newest scripted frame's version, if there is such a frame.
-     * - The version from the compartment.
-     * - The default version.
-     *
-     * Note: if this ever shows up in a profile, just add caching!
-     */
-    JSVersion findVersion();
-
     JS::ContextOptions& options() {
         return options_.ref();
     }
 
     bool runtimeMatches(JSRuntime* rt) const {
         return runtime_ == rt;
     }
 
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -790,22 +790,22 @@ JSFunction::trace(JSTracer* trc)
 
     TraceNullableEdge(trc, &atom_, "atom");
 
     if (isInterpreted()) {
         // Functions can be be marked as interpreted despite having no script
         // yet at some points when parsing, and can be lazy with no lazy script
         // for self-hosted code.
         if (hasScript() && !hasUncompiledScript())
-            TraceManuallyBarrieredEdge(trc, &u.i.s.script_, "script");
-        else if (isInterpretedLazy() && u.i.s.lazy_)
-            TraceManuallyBarrieredEdge(trc, &u.i.s.lazy_, "lazyScript");
+            TraceManuallyBarrieredEdge(trc, &u.scripted.s.script_, "script");
+        else if (isInterpretedLazy() && u.scripted.s.lazy_)
+            TraceManuallyBarrieredEdge(trc, &u.scripted.s.lazy_, "lazyScript");
 
-        if (u.i.env_)
-            TraceManuallyBarrieredEdge(trc, &u.i.env_, "fun_environment");
+        if (u.scripted.env_)
+            TraceManuallyBarrieredEdge(trc, &u.scripted.env_, "fun_environment");
     }
 }
 
 static void
 fun_trace(JSTracer* trc, JSObject* obj)
 {
     obj->as<JSFunction>().trace(trc);
 }
@@ -826,17 +826,16 @@ CreateFunctionConstructor(JSContext* cx,
     RootedObject functionCtor(cx,
       NewFunctionWithProto(cx, Function, 1, JSFunction::NATIVE_CTOR,
                            nullptr, HandlePropertyName(cx->names().Function),
                            functionProto, AllocKind::FUNCTION, SingletonObject));
     if (!functionCtor)
         return nullptr;
 
     return functionCtor;
-
 }
 
 static JSObject*
 CreateFunctionPrototype(JSContext* cx, JSProtoKey key)
 {
     Rooted<GlobalObject*> self(cx, cx->global());
 
     RootedObject objectProto(cx, &self->getPrototype(JSProto_Object).toObject());
@@ -866,18 +865,17 @@ CreateFunctionPrototype(JSContext* cx, J
     if (!ss)
         return nullptr;
     ScriptSourceHolder ssHolder(ss);
     if (!ss->setSource(cx, mozilla::Move(source), sourceLen))
         return nullptr;
 
     CompileOptions options(cx);
     options.setIntroductionType("Function.prototype")
-           .setNoScriptRval(true)
-           .setVersion(JSVERSION_DEFAULT);
+           .setNoScriptRval(true);
     if (!ss->initFromOptions(cx, options))
         return nullptr;
     RootedScriptSource sourceObject(cx, ScriptSourceObject::create(cx, ss));
     if (!sourceObject || !ScriptSourceObject::initFromOptions(cx, sourceObject, options))
         return nullptr;
 
     RootedScript script(cx, JSScript::Create(cx,
                                              options,
@@ -1648,17 +1646,17 @@ JSFunction::createScriptForLazilyInterpr
 }
 
 void
 JSFunction::maybeRelazify(JSRuntime* rt)
 {
     // Try to relazify functions with a non-lazy script. Note: functions can be
     // marked as interpreted despite having no script yet at some points when
     // parsing.
-    if (!hasScript() || !u.i.s.script_)
+    if (!hasScript() || !u.scripted.s.script_)
         return;
 
     // Don't relazify functions in compartments that are active.
     JSCompartment* comp = compartment();
     if (comp->hasBeenEntered() && !rt->allowRelazificationForTesting)
         return;
 
     // The caller should have checked we're not in the self-hosting zone (it's
@@ -1670,17 +1668,17 @@ JSFunction::maybeRelazify(JSRuntime* rt)
         return;
 
     // Don't relazify if the compartment and/or runtime is instrumented to
     // collect code coverage for analysis.
     if (comp->collectCoverageForDebug())
         return;
 
     // Don't relazify functions with JIT code.
-    if (!u.i.s.script_->isRelazifiable())
+    if (!u.scripted.s.script_->isRelazifiable())
         return;
 
     // To delazify self-hosted builtins we need the name of the function
     // to clone. This name is stored in the first extended slot. Since
     // that slot is sometimes also used for other purposes, make sure it
     // contains a string.
     if (isSelfHostedBuiltin() &&
         (!isExtended() || !getExtendedSlot(LAZY_FUNCTION_NAME_SLOT).isString()))
@@ -1688,17 +1686,17 @@ JSFunction::maybeRelazify(JSRuntime* rt)
         return;
     }
 
     JSScript* script = nonLazyScript();
 
     flags_ &= ~INTERPRETED;
     flags_ |= INTERPRETED_LAZY;
     LazyScript* lazy = script->maybeLazyScript();
-    u.i.s.lazy_ = lazy;
+    u.scripted.s.lazy_ = lazy;
     if (lazy) {
         MOZ_ASSERT(!isSelfHostedBuiltin());
     } else {
         MOZ_ASSERT(isSelfHostedBuiltin());
         MOZ_ASSERT(isExtended());
         MOZ_ASSERT(getExtendedSlot(LAZY_FUNCTION_NAME_SLOT).toString()->isAtom());
     }
 
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -112,34 +112,32 @@ class JSFunction : public js::NativeObje
     static_assert(((FunctionKindLimit - 1) << FUNCTION_KIND_SHIFT) <= FUNCTION_KIND_MASK,
                   "FunctionKind doesn't fit into flags_");
 
   private:
     uint16_t        nargs_;       /* number of formal arguments
                                      (including defaults and the rest parameter unlike f.length) */
     uint16_t        flags_;       /* bitfield composed of the above Flags enum, as well as the kind */
     union U {
-        class Native {
+        class {
             friend class JSFunction;
-            js::Native          native;       /* native method pointer or null */
-            const JSJitInfo*    jitinfo;     /* Information about this function to be
-                                                used by the JIT;
-                                                use the accessor! */
-        } n;
-        struct Scripted {
+            js::Native func_;          /* native method pointer or null */
+            const JSJitInfo* jitinfo_; /* Information about this function to be
+                                          used by the JIT; use the accessor! */
+        } native;
+        struct {
+            JSObject* env_;            /* environment for new activations */
             union {
-                JSScript* script_; /* interpreted bytecode descriptor or null;
-                                      use the accessor! */
+                JSScript* script_;     /* interpreted bytecode descriptor or
+                                          null; use the accessor! */
                 js::LazyScript* lazy_; /* lazily compiled script, or nullptr */
             } s;
-            JSObject*   env_;    /* environment for new activations */
-        } i;
-        void*           nativeOrScript;
+        } scripted;
     } u;
-    js::GCPtrAtom atom_;      /* name for diagnostics and decompiling */
+    js::GCPtrAtom atom_; /* name for diagnostics and decompiling */
 
   public:
     /* Call objects must be created for each invocation of this function. */
     bool needsCallObject() const {
         MOZ_ASSERT(!isInterpretedLazy());
 
         if (isNative())
             return false;
@@ -390,38 +388,38 @@ class JSFunction : public js::NativeObje
     enum { MAX_ARGS_AND_VARS = 2 * ((1U << 16) - 1) };
 
     /*
      * For an interpreted function, accessors for the initial scope object of
      * activations (stack frames) of the function.
      */
     JSObject* environment() const {
         MOZ_ASSERT(isInterpreted());
-        return u.i.env_;
+        return u.scripted.env_;
     }
 
     void setEnvironment(JSObject* obj) {
         MOZ_ASSERT(isInterpreted());
-        *reinterpret_cast<js::GCPtrObject*>(&u.i.env_) = obj;
+        *reinterpret_cast<js::GCPtrObject*>(&u.scripted.env_) = obj;
     }
 
     void initEnvironment(JSObject* obj) {
         MOZ_ASSERT(isInterpreted());
-        reinterpret_cast<js::GCPtrObject*>(&u.i.env_)->init(obj);
+        reinterpret_cast<js::GCPtrObject*>(&u.scripted.env_)->init(obj);
     }
 
     void unsetEnvironment() {
         setEnvironment(nullptr);
     }
 
   public:
-    static inline size_t offsetOfNargs() { return offsetof(JSFunction, nargs_); }
-    static inline size_t offsetOfFlags() { return offsetof(JSFunction, flags_); }
-    static inline size_t offsetOfEnvironment() { return offsetof(JSFunction, u.i.env_); }
-    static inline size_t offsetOfAtom() { return offsetof(JSFunction, atom_); }
+    static constexpr size_t offsetOfNargs() { return offsetof(JSFunction, nargs_); }
+    static constexpr size_t offsetOfFlags() { return offsetof(JSFunction, flags_); }
+    static size_t offsetOfEnvironment() { return offsetof(JSFunction, u.scripted.env_); }
+    static size_t offsetOfAtom() { return offsetof(JSFunction, atom_); }
 
     static bool createScriptForLazilyInterpretedFunction(JSContext* cx, js::HandleFunction fun);
     void maybeRelazify(JSRuntime* rt);
 
     // Function Scripts
     //
     // Interpreted functions may either have an explicit JSScript (hasScript())
     // or be lazy with sufficient information to construct the JSScript if
@@ -483,34 +481,34 @@ class JSFunction : public js::NativeObje
         return nonLazyScript();
     }
 
     // The state of a JSFunction whose script errored out during bytecode
     // compilation. Such JSFunctions are only reachable via GC iteration and
     // not from script.
     bool hasUncompiledScript() const {
         MOZ_ASSERT(hasScript());
-        return !u.i.s.script_;
+        return !u.scripted.s.script_;
     }
 
     JSScript* nonLazyScript() const {
         MOZ_ASSERT(!hasUncompiledScript());
-        return u.i.s.script_;
+        return u.scripted.s.script_;
     }
 
     static bool getLength(JSContext* cx, js::HandleFunction fun, uint16_t* length);
 
     js::LazyScript* lazyScript() const {
-        MOZ_ASSERT(isInterpretedLazy() && u.i.s.lazy_);
-        return u.i.s.lazy_;
+        MOZ_ASSERT(isInterpretedLazy() && u.scripted.s.lazy_);
+        return u.scripted.s.lazy_;
     }
 
     js::LazyScript* lazyScriptOrNull() const {
         MOZ_ASSERT(isInterpretedLazy());
-        return u.i.s.lazy_;
+        return u.scripted.s.lazy_;
     }
 
     js::GeneratorKind generatorKind() const {
         if (!isInterpreted())
             return js::GeneratorKind::NotGenerator;
         if (hasScript())
             return nonLazyScript()->generatorKind();
         if (js::LazyScript* lazy = lazyScriptOrNull())
@@ -553,59 +551,65 @@ class JSFunction : public js::NativeObje
         flags_ |= INTERPRETED;
         initScript(script);
     }
 
     void initLazyScript(js::LazyScript* lazy) {
         MOZ_ASSERT(isInterpreted());
         flags_ &= ~INTERPRETED;
         flags_ |= INTERPRETED_LAZY;
-        u.i.s.lazy_ = lazy;
+        u.scripted.s.lazy_ = lazy;
     }
 
     JSNative native() const {
         MOZ_ASSERT(isNative());
-        return u.n.native;
+        return u.native.func_;
     }
 
     JSNative maybeNative() const {
         return isInterpreted() ? nullptr : native();
     }
 
     void initNative(js::Native native, const JSJitInfo* jitinfo) {
         MOZ_ASSERT(native);
-        u.n.native = native;
-        u.n.jitinfo = jitinfo;
+        u.native.func_ = native;
+        u.native.jitinfo_ = jitinfo;
     }
 
     const JSJitInfo* jitInfo() const {
         MOZ_ASSERT(isNative());
-        return u.n.jitinfo;
+        return u.native.jitinfo_;
     }
 
     void setJitInfo(const JSJitInfo* data) {
         MOZ_ASSERT(isNative());
-        u.n.jitinfo = data;
+        u.native.jitinfo_ = data;
     }
 
     bool isDerivedClassConstructor();
 
-    static unsigned offsetOfNativeOrScript() {
-        static_assert(offsetof(U, n.native) == offsetof(U, i.s.script_),
-                      "native and script pointers must be in the same spot "
-                      "for offsetOfNativeOrScript() have any sense");
-        static_assert(offsetof(U, n.native) == offsetof(U, nativeOrScript),
-                      "U::nativeOrScript must be at the same offset as "
-                      "native");
-
-        return offsetof(JSFunction, u.nativeOrScript);
+    static unsigned offsetOfNative() {
+        return offsetof(JSFunction, u.native.func_);
+    }
+    static unsigned offsetOfScript() {
+        return offsetof(JSFunction, u.scripted.s.script_);
+    }
+    static unsigned offsetOfNativeOrEnv() {
+        static_assert(offsetof(U, native.func_) == offsetof(U, scripted.env_),
+                      "U.native.func_ must be at the same offset as U.scripted.env_");
+        return offsetOfNative();
+    }
+    static unsigned offsetOfScriptOrLazyScript() {
+        static_assert(offsetof(U, scripted.s.script_) == offsetof(U, scripted.s.lazy_),
+                      "U.scripted.s.script_ must be at the same offset as lazy_");
+        return offsetof(JSFunction, u.scripted.s.script_);
     }
 
     static unsigned offsetOfJitInfo() {
-        return offsetof(JSFunction, u.n.jitinfo);
+        return offsetof(JSFunction, u.native.jitinfo_);
     }
 
     inline void trace(JSTracer* trc);
 
     /* Bound function accessors. */
 
     JSObject* getBoundFunctionTarget() const;
     const js::Value& getBoundFunctionThis() const;
@@ -617,17 +621,17 @@ class JSFunction : public js::NativeObje
      * target is. Also assigns the prototype and sets the name and correct length.
      */
     static bool finishBoundFunctionInit(JSContext* cx, js::HandleFunction bound,
                                         js::HandleObject targetObj, int32_t argCount);
 
   private:
     js::GCPtrScript& mutableScript() {
         MOZ_ASSERT(hasScript());
-        return *(js::GCPtrScript*)&u.i.s.script_;
+        return *(js::GCPtrScript*)&u.scripted.s.script_;
     }
 
     inline js::FunctionExtended* toExtended();
     inline const js::FunctionExtended* toExtended() const;
 
   public:
     inline bool isExtended() const {
         bool extended = !!(flags() & EXTENDED);
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -355,17 +355,17 @@ js::XDRScript(XDRState<mode>* xdr, Handl
         NeedsHomeObject,
         IsDerivedClassConstructor,
         IsDefaultClassConstructor,
     };
 
     uint32_t length, lineno, column, nfixed, nslots;
     uint32_t natoms, nsrcnotes, i;
     uint32_t nconsts, nobjects, nscopes, nregexps, ntrynotes, nscopenotes, nyieldoffsets;
-    uint32_t prologueLength, version;
+    uint32_t prologueLength;
     uint32_t funLength = 0;
     uint32_t nTypeSets = 0;
     uint32_t scriptBits = 0;
     uint32_t bodyScopeIndex = 0;
 
     JSContext* cx = xdr->cx();
     RootedScript script(cx);
     natoms = nsrcnotes = 0;
@@ -394,18 +394,16 @@ js::XDRScript(XDRState<mode>* xdr, Handl
 
     if (mode == XDR_ENCODE)
         length = script->length();
     if (!xdr->codeUint32(&length))
         return false;
 
     if (mode == XDR_ENCODE) {
         prologueLength = script->mainOffset();
-        MOZ_ASSERT(script->getVersion() != JSVERSION_UNKNOWN);
-        version = script->getVersion();
         lineno = script->lineno();
         column = script->column();
         nfixed = script->nfixed();
         nslots = script->nslots();
 
         bodyScopeIndex = script->bodyScopeIndex();
         natoms = script->natoms();
 
@@ -476,18 +474,16 @@ js::XDRScript(XDRState<mode>* xdr, Handl
         if (script->isDerivedClassConstructor())
             scriptBits |= (1 << IsDerivedClassConstructor);
         if (script->isDefaultClassConstructor())
             scriptBits |= (1 << IsDefaultClassConstructor);
     }
 
     if (!xdr->codeUint32(&prologueLength))
         return false;
-    if (!xdr->codeUint32(&version))
-        return false;
 
     // To fuse allocations, we need lengths of all embedded arrays early.
     if (!xdr->codeUint32(&natoms))
         return false;
     if (!xdr->codeUint32(&nsrcnotes))
         return false;
     if (!xdr->codeUint32(&nconsts))
         return false;
@@ -507,34 +503,30 @@ js::XDRScript(XDRState<mode>* xdr, Handl
         return false;
     if (!xdr->codeUint32(&scriptBits))
         return false;
 
     MOZ_ASSERT(!!(scriptBits & (1 << OwnSource)) == !sourceObjectArg);
     RootedScriptSource sourceObject(cx, sourceObjectArg);
 
     if (mode == XDR_DECODE) {
-        JSVersion version_ = JSVersion(version);
-        MOZ_ASSERT((version_ & VersionFlags::MASK) == unsigned(version_));
-
-        // When loading from the bytecode cache, we get the CompileOption from
-        // the document, which specify the version to use. If the version does
-        // not match, then we should fail. This only applies to the top-level,
-        // and not its inner functions.
+        // When loading from the bytecode cache, we get the CompileOptions from
+        // the document. If the noScriptRval or selfHostingMode flag doesn't
+        // match, we should fail. This only applies to the top-level and not
+        // its inner functions.
         mozilla::Maybe<CompileOptions> options;
         if (xdr->hasOptions() && (scriptBits & (1 << OwnSource))) {
             options.emplace(xdr->cx(), xdr->options());
-            if (options->version != version_ ||
-                options->noScriptRval != !!(scriptBits & (1 << NoScriptRval)) ||
+            if (options->noScriptRval != !!(scriptBits & (1 << NoScriptRval)) ||
                 options->selfHostingMode != !!(scriptBits & (1 << SelfHosted)))
             {
                 return xdr->fail(JS::TranscodeResult_Failure_WrongCompileOption);
             }
         } else {
-            options.emplace(xdr->cx(), version_);
+            options.emplace(xdr->cx());
             (*options).setNoScriptRval(!!(scriptBits & (1 << NoScriptRval)))
                       .setSelfHostingMode(!!(scriptBits & (1 << SelfHosted)));
         }
 
         if (scriptBits & (1 << OwnSource)) {
             ScriptSource* ss = cx->new_<ScriptSource>();
             if (!ss)
                 return false;
@@ -2699,19 +2691,16 @@ JSScript::Create(JSContext* cx, const Re
     PodZero(script.get());
 
     script->initCompartment(cx);
 
     script->selfHosted_ = options.selfHostingMode;
     script->noScriptRval_ = options.noScriptRval;
     script->treatAsRunOnce_ = options.isRunOnce;
 
-    script->version = options.version;
-    MOZ_ASSERT(script->getVersion() == options.version);     // assert that no overflow occurred
-
     script->setSourceObject(sourceObject);
     if (cx->runtime()->lcovOutput().isEnabled() && !script->initScriptName(cx))
         return nullptr;
     script->sourceStart_ = bufStart;
     script->sourceEnd_ = bufEnd;
     script->toStringStart_ = toStringStart;
     script->toStringEnd_ = toStringEnd;
 
@@ -3696,18 +3685,17 @@ CreateEmptyScriptForClone(JSContext* cx,
         sourceObject = src->sourceObject();
         if (!cx->compartment()->wrap(cx, &sourceObject))
             return nullptr;
     }
 
     CompileOptions options(cx);
     options.setMutedErrors(src->mutedErrors())
            .setSelfHostingMode(src->selfHosted())
-           .setNoScriptRval(src->noScriptRval())
-           .setVersion(src->getVersion());
+           .setNoScriptRval(src->noScriptRval());
 
     return JSScript::Create(cx, options, sourceObject, src->sourceStart(), src->sourceEnd(),
                             src->toStringStart(), src->toStringEnd());
 }
 
 JSScript*
 js::CloneGlobalScript(JSContext* cx, ScopeKind scopeKind, HandleScript src)
 {
@@ -4353,26 +4341,24 @@ LazyScript::CreateRaw(JSContext* cx, Han
     return new (res) LazyScript(fun, table.forget(), packed, begin, end,
                                 toStringStart, lineno, column);
 }
 
 /* static */ LazyScript*
 LazyScript::Create(JSContext* cx, HandleFunction fun,
                    const frontend::AtomVector& closedOverBindings,
                    Handle<GCVector<JSFunction*, 8>> innerFunctions,
-                   JSVersion version,
                    uint32_t begin, uint32_t end,
                    uint32_t toStringStart, uint32_t lineno, uint32_t column)
 {
     union {
         PackedView p;
         uint64_t packedFields;
     };
 
-    p.version = version;
     p.shouldDeclareArguments = false;
     p.hasThisBinding = false;
     p.isAsync = false;
     p.hasRest = false;
     p.isExprBody = false;
     p.numClosedOverBindings = closedOverBindings.length();
     p.numInnerFunctions = innerFunctions.length();
     p.isGenerator = false;
@@ -4392,17 +4378,16 @@ LazyScript::Create(JSContext* cx, Handle
     JSAtom** resClosedOverBindings = res->closedOverBindings();
     for (size_t i = 0; i < res->numClosedOverBindings(); i++)
         resClosedOverBindings[i] = closedOverBindings[i];
 
     GCPtrFunction* resInnerFunctions = res->innerFunctions();
     for (size_t i = 0; i < res->numInnerFunctions(); i++)
         resInnerFunctions[i].init(innerFunctions[i]);
 
-    MOZ_ASSERT_IF(res, res->version() == version);
     return res;
 }
 
 /* static */ LazyScript*
 LazyScript::Create(JSContext* cx, HandleFunction fun,
                    HandleScript script, HandleScope enclosingScope,
                    HandleScriptSource sourceObject,
                    uint64_t packedFields, uint32_t begin, uint32_t end,
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -981,18 +981,16 @@ class JSScript : public js::gc::TenuredC
     mozilla::Atomic<uint32_t, mozilla::Relaxed> warmUpCount;
 
     // 16-bit fields.
 
     uint16_t        warmUpResetCount; /* Number of times the |warmUpCount| was
                                        * forcibly discarded. The counter is reset when
                                        * a script is successfully jit-compiled. */
 
-    uint16_t        version;    /* JS version under which script was compiled */
-
     uint16_t        funLength_; /* ES6 function length */
 
     uint16_t        nTypeSets_; /* number of type sets used in this script for
                                    dynamic type monitoring */
 
     // Bit fields.
 
   public:
@@ -1127,17 +1125,17 @@ class JSScript : public js::gc::TenuredC
 
     bool hasRest_:1;
     bool isExprBody_:1;
 
     // Add padding so JSScript is gc::Cell aligned. Make padding protected
     // instead of private to suppress -Wunused-private-field compiler warnings.
   protected:
 #if JS_BITS_PER_WORD == 32
-    // Currently no padding is needed.
+    uint32_t padding_;
 #endif
 
     //
     // End of fields.  Start methods.
     //
 
   public:
     static JSScript* Create(JSContext* cx,
@@ -1178,18 +1176,16 @@ class JSScript : public js::gc::TenuredC
 #endif
 
   public:
     inline JSPrincipals* principals();
 
     JSCompartment* compartment() const { return compartment_; }
     JSCompartment* maybeCompartment() const { return compartment(); }
 
-    void setVersion(JSVersion v) { version = v; }
-
     js::SharedScriptData* scriptData() {
         return scriptData_;
     }
 
     // Script bytecode is immutable after creation.
     jsbytecode* code() const {
         if (!scriptData_)
             return nullptr;
@@ -1910,20 +1906,16 @@ class JSScript : public js::gc::TenuredC
         // index. To search through ScopeNotes to look for a Scope using pc,
         // use lookupScope.
         MOZ_ASSERT(containsPC(pc) && containsPC(pc + sizeof(uint32_t)));
         MOZ_ASSERT(js::JOF_OPTYPE(JSOp(*pc)) == JOF_SCOPE,
                    "Did you mean to use lookupScope(pc)?");
         return getScope(GET_UINT32_INDEX(pc));
     }
 
-    JSVersion getVersion() const {
-        return JSVersion(version);
-    }
-
     inline JSFunction* getFunction(size_t index);
     JSFunction* function() const {
         if (functionNonDelazifying())
             return functionNonDelazifying();
         return nullptr;
     }
 
     inline js::RegExpObject* getRegExp(size_t index);
@@ -2085,19 +2077,16 @@ class LazyScript : public gc::TenuredCel
     uint32_t padding;
 #endif
 
   private:
     static const uint32_t NumClosedOverBindingsBits = 20;
     static const uint32_t NumInnerFunctionsBits = 20;
 
     struct PackedView {
-        // Assorted bits that should really be in ScriptSourceObject.
-        uint32_t version : 8;
-
         uint32_t shouldDeclareArguments : 1;
         uint32_t hasThisBinding : 1;
         uint32_t isAsync : 1;
         uint32_t isExprBody : 1;
 
         uint32_t numClosedOverBindings : NumClosedOverBindingsBits;
 
         // -- 32bit boundary --
@@ -2151,17 +2140,17 @@ class LazyScript : public gc::TenuredCel
     static const uint32_t NumClosedOverBindingsLimit = 1 << NumClosedOverBindingsBits;
     static const uint32_t NumInnerFunctionsLimit = 1 << NumInnerFunctionsBits;
 
     // Create a LazyScript and initialize closedOverBindings and innerFunctions
     // with the provided vectors.
     static LazyScript* Create(JSContext* cx, HandleFunction fun,
                               const frontend::AtomVector& closedOverBindings,
                               Handle<GCVector<JSFunction*, 8>> innerFunctions,
-                              JSVersion version, uint32_t begin, uint32_t end,
+                              uint32_t begin, uint32_t end,
                               uint32_t toStringStart, uint32_t lineno, uint32_t column);
 
     // Create a LazyScript and initialize the closedOverBindings and the
     // innerFunctions with dummy values to be replaced in a later initialization
     // phase.
     //
     // The "script" argument to this function can be null.  If it's non-null,
     // then this LazyScript should be associated with the given JSScript.
@@ -2201,20 +2190,16 @@ class LazyScript : public gc::TenuredCel
     ScriptSourceObject* sourceObject() const;
     ScriptSource* scriptSource() const {
         return sourceObject()->source();
     }
     ScriptSource* maybeForwardedScriptSource() const;
     bool mutedErrors() const {
         return scriptSource()->mutedErrors();
     }
-    JSVersion version() const {
-        JS_STATIC_ASSERT(JSVERSION_UNKNOWN == -1);
-        return (p_.version == JS_BIT(8) - 1) ? JSVERSION_UNKNOWN : JSVersion(p_.version);
-    }
 
     void setEnclosingScopeAndSource(Scope* enclosingScope, ScriptSourceObject* sourceObject);
 
     uint32_t numClosedOverBindings() const {
         return p_.numClosedOverBindings;
     }
     JSAtom** closedOverBindings() {
         return (JSAtom**)table_;
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -717,17 +717,16 @@ InitModuleLoader(JSContext* cx)
         return false;
     }
 
     CompileOptions options(cx);
     options.setIntroductionType("shell module loader");
     options.setFileAndLine("shell/ModuleLoader.js", 1);
     options.setSelfHostingMode(false);
     options.setCanLazilyParse(false);
-    options.setVersion(JSVERSION_DEFAULT);
     options.werrorOption = true;
     options.strictOption = true;
 
     RootedValue rv(cx);
     return Evaluate(cx, options, src, srcLen, &rv);
 }
 
 static bool
@@ -1057,46 +1056,16 @@ Process(JSContext* cx, const char* filen
         // It's an interactive filehandle; drop into read-eval-print loop.
         MOZ_ASSERT(kind == FileScript);
         if (!ReadEvalPrintLoop(cx, file, compileOnly))
             return false;
     }
     return true;
 }
 
-static bool
-Version(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    JSVersion origVersion = JS_GetVersion(cx);
-    if (args.length() == 0 || args[0].isUndefined()) {
-        /* Get version. */
-        args.rval().setInt32(origVersion);
-    } else {
-        /* Set version. */
-        int32_t v = -1;
-        if (args[0].isInt32()) {
-            v = args[0].toInt32();
-        } else if (args[0].isDouble()) {
-            double fv = args[0].toDouble();
-            int32_t fvi;
-            if (NumberEqualsInt32(fv, &fvi))
-                v = fvi;
-        }
-        if (v < 0 || v > JSVERSION_LATEST) {
-            JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS,
-                                      "version");
-            return false;
-        }
-        SetVersionForCurrentRealm(cx, JSVersion(v));
-        args.rval().setInt32(origVersion);
-    }
-    return true;
-}
-
 #ifdef XP_WIN
 #  define GET_FD_FROM_FILE(a) int(_get_osfhandle(fileno(a)))
 #else
 #  define GET_FD_FROM_FILE(a) fileno(a)
 #endif
 
 static bool
 CreateMappedArrayBuffer(JSContext* cx, unsigned argc, Value* vp)
@@ -3272,17 +3241,16 @@ static const JSClass sandbox_class = {
     "sandbox",
     JSCLASS_GLOBAL_FLAGS,
     &sandbox_classOps
 };
 
 static void
 SetStandardCompartmentOptions(JS::CompartmentOptions& options)
 {
-    options.behaviors().setVersion(JSVERSION_DEFAULT);
     options.creationOptions().setSharedMemoryAndAtomicsEnabled(enableSharedMemory);
 }
 
 static JSObject*
 NewSandbox(JSContext* cx, bool lazy)
 {
     JS::CompartmentOptions options;
     SetStandardCompartmentOptions(options);
@@ -6496,20 +6464,16 @@ WasmLoop(JSContext* cx, unsigned argc, V
     return true;
 }
 
 static const JSFunctionSpecWithHelp shell_functions[] = {
     JS_FN_HELP("clone", Clone, 1, 0,
 "clone(fun[, scope])",
 "  Clone function object."),
 
-    JS_FN_HELP("version", Version, 0, 0,
-"version([number])",
-"  Get or force a script compilation version number."),
-
     JS_FN_HELP("options", Options, 0, 0,
 "options([option ...])",
 "  Get or toggle JavaScript options."),
 
     JS_FN_HELP("load", Load, 1, 0,
 "load(['foo.js' ...])",
 "  Load files named by string arguments. Filename is relative to the\n"
 "      current working directory."),
--- a/js/src/tests/browser.js
+++ b/js/src/tests/browser.js
@@ -440,62 +440,19 @@
     var properties = Object.create(null);
     var fields = document.location.search.slice(1).split(";");
     for (var i = 0; i < fields.length; i++) {
       var propertycaptures = /^([^=]+)=(.*)$/.exec(fields[i]);
       if (propertycaptures === null) {
         properties[fields[i]] = true;
       } else {
         properties[propertycaptures[1]] = decodeURIComponent(propertycaptures[2]);
-        if (propertycaptures[1] === "language") {
-          // language=(type|language);mimetype
-          properties.mimetype = fields[i + 1];
-        }
       }
     }
 
-    if (properties.language !== "type") {
-      var mimetypeVersion = /javascript([.0-9]+)/.exec(properties.mimetype);
-      if (mimetypeVersion !== null) {
-        properties.version = mimetypeVersion[1];
-      }
-    }
-
-    if (!properties.version && navigator.userAgent.indexOf("Gecko/") !== -1) {
-      // If the version is not specified, and the browser is Gecko,
-      // use the default version corresponding to the shell's version(0).
-      // See https://bugzilla.mozilla.org/show_bug.cgi?id=522760#c11
-      // Otherwise adjust the version to match the suite version for 1.6,
-      // and later due to the use of for-each, let, yield, etc.
-      //
-      // The logic to upgrade the JS version in the shell lives in the
-      // corresponding shell.js.
-      //
-      // Note that js1_8, js1_8_1, and js1_8_5 are treated identically in
-      // the browser.
-      var versions = [
-         { path: "js1_6", version: "1.6" },
-         { path: "js1_7", version: "1.7" },
-         { path: "js1_8", version: "1.8" },
-      ];
-      for (var {path, version} of versions) {
-        if (properties.test.startsWith(path)) {
-          properties.version = version;
-          break;
-        }
-      }
-    }
-
-    // default to language=type;text/javascript. required for
-    // reftest style manifests.
-    if (!properties.language) {
-      properties.language = "type";
-      properties.mimetype = "text/javascript";
-    }
-
     global.gTestPath = properties.test;
 
     var testpathparts = properties.test.split("/");
     if (testpathparts.length < 2) {
       // must have at least suitepath/testcase.js
       return;
     }
 
@@ -533,34 +490,19 @@
     // Output the test script itself.
     var moduleTest = !!properties.module;
     scripts.push({src: prepath + testFileName, module: moduleTest});
 
     // Finally output the driver-end script to advance to the next test.
     scripts.push({src: "js-test-driver-end.js", module: false});
 
     if (!moduleTest) {
-      var key, value;
-      if (properties.language !== "type") {
-        key = "language";
-        value = "javascript";
-        if (properties.version) {
-          value += properties.version;
-        }
-      } else {
-        key = "type";
-        value = properties.mimetype;
-        if (properties.version) {
-          value += ";version=" + properties.version;
-        }
-      }
-
       for (var i = 0; i < scripts.length; i++) {
         var src = scripts[i].src;
-        document.write(`<script src="${src}" charset="utf-8" ${key}="${value}"><\/script>`);
+        document.write(`<script src="${src}" charset="utf-8"><\/script>`);
       }
     } else {
       // Modules are loaded asynchronously by default, but for the test harness
       // we need to execute all scripts and modules one after the other.
 
       // Appends the next script element to the DOM.
       function appendScript(index) {
         var script = scriptElements[index];
deleted file mode 100644
deleted file mode 100644
--- a/js/src/tests/js1_2/version120/shell.js
+++ /dev/null
@@ -1,9 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-/* all files in this dir need version(120) called before they are *loaded* */
-
-
-version(120);
--- a/js/src/tests/js1_5/Scope/regress-77578-001.js
+++ b/js/src/tests/js1_5/Scope/regress-77578-001.js
@@ -16,118 +16,85 @@ var summary = 'Testing eval scope inside
 var cnEquals = '=';
 var status = '';
 var statusitems = [];
 var actual = '';
 var actualvalues = [];
 var expect= '';
 var expectedvalues = [];
 
-
-// various versions of JavaScript -
-var JS_VER = [100, 110, 120, 130, 140, 150];
-
 // Note contrast with local variables i,j,k defined below -
 var i = 999;
 var j = 999;
 var k = 999;
 
 
 //--------------------------------------------------
 test();
 //--------------------------------------------------
 
 
 function test()
 {
   printBugNumber(BUGNUMBER);
   printStatus (summary);
 
-  // Run tests A,B,C on each version of JS and store results
-  for (var n=0; n!=JS_VER.length; n++)
-  {
-    testA(JS_VER[n]);
-  }
-  for (var n=0; n!=JS_VER.length; n++)
-  {
-    testB(JS_VER[n]);
-  }
-  for (var n=0; n!=JS_VER.length; n++)
-  {
-    testC(JS_VER[n]);
-  }
-
+  testA();
+  testB();
+  testC();
 
   // Compare actual values to expected values -
   for (var i=0; i<UBound; i++)
   {
     reportCompare(expectedvalues[i], actualvalues[i], statusitems[i]);
   }
 }
 
 
-function testA(ver)
+function testA()
 {
-  // Set the version of JS to test -
-  if (typeof version == 'function')
-  {
-    version(ver);
-  }
-
   // eval the test, so it compiles AFTER version() has executed -
   var sTestScript = "";
 
   // Define a local variable i
-  sTestScript += "status = 'Section A of test; JS ' + ver/100;";
+  sTestScript += "status = 'Section A of test';";
   sTestScript += "var i=1;";
   sTestScript += "actual = eval('i');";
   sTestScript += "expect = 1;";
   sTestScript += "captureThis('i');";
 
   eval(sTestScript);
 }
 
 
-function testB(ver)
+function testB()
 {
-  // Set the version of JS to test -
-  if (typeof version == 'function')
-  {
-    version(ver);
-  }
-
   // eval the test, so it compiles AFTER version() has executed -
   var sTestScript = "";
 
   // Define a local for-loop iterator j
-  sTestScript += "status = 'Section B of test; JS ' + ver/100;";
+  sTestScript += "status = 'Section B of test';";
   sTestScript += "for(var j=1; j<2; j++)";
   sTestScript += "{";
   sTestScript += "  actual = eval('j');";
   sTestScript += "};";
   sTestScript += "expect = 1;";
   sTestScript += "captureThis('j');";
 
   eval(sTestScript);
 }
 
 
-function testC(ver)
+function testC()
 {
-  // Set the version of JS to test -
-  if (typeof version == 'function')
-  {
-    version(ver);
-  }
-
   // eval the test, so it compiles AFTER version() has executed -
   var sTestScript = "";
 
   // Define a local variable k in a try-catch block -
-  sTestScript += "status = 'Section C of test; JS ' + ver/100;";
+  sTestScript += "status = 'Section C of test';";
   sTestScript += "try";
   sTestScript += "{";
   sTestScript += "  var k=1;";
   sTestScript += "  actual = eval('k');";
   sTestScript += "}";
   sTestScript += "catch(e)";
   sTestScript += "{";
   sTestScript += "};";
--- a/js/src/tests/js1_6/shell.js
+++ b/js/src/tests/js1_6/shell.js
@@ -1,9 +1,1 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
 
-if (typeof version != 'undefined')
-{
-  version(160);
-}
--- a/js/src/tests/js1_7/shell.js
+++ b/js/src/tests/js1_7/shell.js
@@ -1,9 +1,1 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
 
-if (typeof version != 'undefined')
-{
-  version(170);
-}
--- a/js/src/tests/js1_8/shell.js
+++ b/js/src/tests/js1_8/shell.js
@@ -1,9 +1,1 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
 
-if (typeof version != 'undefined')
-{
-  version(180);
-}
--- a/js/src/tests/js1_8_1/shell.js
+++ b/js/src/tests/js1_8_1/shell.js
@@ -1,9 +1,1 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
 
-if (typeof version != 'undefined')
-{
-  version(181);
-}
deleted file mode 100644
--- a/js/src/tests/js1_8_5/extensions/regress-677924.js
+++ /dev/null
@@ -1,14 +0,0 @@
-// Any copyright is dedicated to the Public Domain.
-// http://creativecommons.org/licenses/publicdomain/
-
-try {
-    version(4096);  // don't assert
-} catch (exc) {
-}
-
-try {
-    version(-1);  // don't assert
-} catch (exc) {
-}
-
-reportCompare(0, 0, 'ok');
--- a/js/src/tests/js1_8_5/shell.js
+++ b/js/src/tests/js1_8_5/shell.js
@@ -17,13 +17,8 @@ if (typeof assertThrowsInstanceOf === 'u
         }
         if (fullmsg === undefined)
             fullmsg = "Assertion failed: expected exception " + ctor.name + ", no exception thrown";
         if (msg !== undefined)
             fullmsg += " - " + msg;
         throw new Error(fullmsg);
     };
 }
-
-if (typeof version != 'undefined')
-{
-  version(185);
-}
--- a/js/src/vm/Caches.h
+++ b/js/src/vm/Caches.h
@@ -66,17 +66,16 @@ struct EvalCacheEntry
     jsbytecode* pc;
 };
 
 struct EvalCacheLookup
 {
     explicit EvalCacheLookup(JSContext* cx) : str(cx), callerScript(cx) {}
     RootedLinearString str;
     RootedScript callerScript;
-    JSVersion version;
     jsbytecode* pc;
 };
 
 struct EvalCacheHashPolicy
 {
     typedef EvalCacheLookup Lookup;
 
     static HashNumber hash(const Lookup& l);
--- a/js/src/vm/Realm.cpp
+++ b/js/src/vm/Realm.cpp
@@ -105,15 +105,8 @@ JS::GetRealmErrorPrototype(JSContext* cx
 }
 
 JS_PUBLIC_API(JSObject*)
 JS::GetRealmIteratorPrototype(JSContext* cx)
 {
     CHECK_REQUEST(cx);
     return GlobalObject::getOrCreateIteratorPrototype(cx, cx->global());
 }
-
-JS_PUBLIC_API(void)
-JS::SetVersionForCurrentRealm(JSContext* cx, JSVersion version)
-{
-    JSCompartment* compartment = GetContextCompartment(cx);
-    compartment->behaviors().setVersion(version);
-}
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -237,17 +237,17 @@ RegExpObject::create(JSContext* cx, cons
 
 RegExpObject*
 RegExpObject::create(JSContext* cx, HandleAtom source, RegExpFlag flags,
                      const ReadOnlyCompileOptions* options, TokenStream* tokenStream,
                      LifoAlloc& alloc, NewObjectKind newKind)
 {
     Maybe<CompileOptions> dummyOptions;
     if (!tokenStream && !options) {
-        dummyOptions.emplace(cx, JSVERSION_DEFAULT);
+        dummyOptions.emplace(cx);
         options = dummyOptions.ptr();
     }
     Maybe<TokenStream> dummyTokenStream;
     if (!tokenStream) {
         dummyTokenStream.emplace(cx, *options,
                                    (const char16_t*) nullptr, 0,
                                    (frontend::StrictModeGetter*) nullptr);
         tokenStream = dummyTokenStream.ptr();
@@ -982,17 +982,17 @@ RegExpShared::compile(JSContext* cx, Mut
 
 /* static */ bool
 RegExpShared::compile(JSContext* cx, MutableHandleRegExpShared re, HandleAtom pattern,
                       HandleLinearString input, CompilationMode mode, ForceByteCodeEnum force)
 {
     if (!re->ignoreCase() && !StringHasRegExpMetaChars(pattern))
         re->canStringMatch = true;
 
-    CompileOptions options(cx, JSVERSION_DEFAULT);
+    CompileOptions options(cx);
     frontend::TokenStream dummyTokenStream(cx, options, nullptr, 0, nullptr);
 
     LifoAllocScope scope(&cx->tempLifoAlloc());
 
     /* Parse the pattern. */
     irregexp::RegExpCompileData data;
     if (!irregexp::ParsePattern(dummyTokenStream, cx->tempLifoAlloc(), pattern,
                                 re->multiline(), mode == MatchOnly, re->unicode(),
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -1086,58 +1086,16 @@ struct JSRuntime : public js::MallocProv
     }
     void* wasmUnwindPC() const {
         return wasmUnwindPC_;
     }
 };
 
 namespace js {
 
-/*
- * Flags accompany script version data so that a) dynamically created scripts
- * can inherit their caller's compile-time properties and b) scripts can be
- * appropriately compared in the eval cache across global option changes. An
- * example of the latter is enabling the top-level-anonymous-function-is-error
- * option: subsequent evals of the same, previously-valid script text may have
- * become invalid.
- */
-namespace VersionFlags {
-static const unsigned MASK      = 0x0FFF; /* see JSVersion in jspubtd.h */
-} /* namespace VersionFlags */
-
-static inline JSVersion
-VersionNumber(JSVersion version)
-{
-    return JSVersion(uint32_t(version) & VersionFlags::MASK);
-}
-
-static inline JSVersion
-VersionExtractFlags(JSVersion version)
-{
-    return JSVersion(uint32_t(version) & ~VersionFlags::MASK);
-}
-
-static inline void
-VersionCopyFlags(JSVersion* version, JSVersion from)
-{
-    *version = JSVersion(VersionNumber(*version) | VersionExtractFlags(from));
-}
-
-static inline bool
-VersionHasFlags(JSVersion version)
-{
-    return !!VersionExtractFlags(version);
-}
-
-static inline bool
-VersionIsKnown(JSVersion version)
-{
-    return VersionNumber(version) != JSVERSION_UNKNOWN;
-}
-
 inline void
 FreeOp::free_(void* p)
 {
     js_free(p);
 }
 
 inline void
 FreeOp::freeLater(void* p)
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -2565,17 +2565,16 @@ js::FillSelfHostingCompileOptions(Compil
      * Additionally, the special syntax callFunction(fun, receiver, ...args)
      * is supported, for which bytecode is emitted that invokes |fun| with
      * |receiver| as the this-object and ...args as the arguments.
      */
     options.setIntroductionType("self-hosted");
     options.setFileAndLine("self-hosted", 1);
     options.setSelfHostingMode(true);
     options.setCanLazilyParse(false);
-    options.setVersion(JSVERSION_DEFAULT);
     options.werrorOption = true;
     options.strictOption = true;
 
 #ifdef DEBUG
     options.extraWarningsOption = true;
 #endif
 }
 
--- a/js/src/wasm/WasmJS.cpp
+++ b/js/src/wasm/WasmJS.cpp
@@ -1034,17 +1034,18 @@ WasmInstanceObject::getExportedFunction(
                                         uint32_t funcIndex, MutableHandleFunction fun)
 {
     if (ExportMap::Ptr p = instanceObj->exports().lookup(funcIndex)) {
         fun.set(p->value());
         return true;
     }
 
     const Instance& instance = instanceObj->instance();
-    unsigned numArgs = instance.metadata(instance.code().stableTier()).lookupFuncExport(funcIndex).sig().args().length();
+    auto tier = instance.code().stableTier();
+    unsigned numArgs = instance.metadata(tier).lookupFuncExport(funcIndex).sig().args().length();
 
     // asm.js needs to act like a normal JS function which means having the name
     // from the original source and being callable as a constructor.
     if (instance.isAsmJS()) {
         RootedAtom name(cx, instance.getFuncAtom(cx, funcIndex));
         if (!name)
             return false;
 
--- a/js/src/wasm/WasmStubs.cpp
+++ b/js/src/wasm/WasmStubs.cpp
@@ -258,30 +258,28 @@ static const unsigned FramePushedBeforeA
 static bool
 GenerateInterpEntry(MacroAssembler& masm, const FuncExport& fe, Offsets* offsets)
 {
     masm.haltingAlign(CodeAlignment);
 
     offsets->begin = masm.currentOffset();
 
     // Save the return address if it wasn't already saved by the call insn.
-#if defined(JS_CODEGEN_ARM)
-    masm.push(lr);
-#elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
-    masm.push(ra);
+#ifdef JS_USE_LINK_REGISTER
+    masm.pushReturnAddress();
 #endif
 
     // Save all caller non-volatile registers before we clobber them here and in
-    // the asm.js callee (which does not preserve non-volatile registers).
+    // the wasm callee (which does not preserve non-volatile registers).
     masm.setFramePushed(0);
     masm.PushRegsInMask(NonVolatileRegs);
     MOZ_ASSERT(masm.framePushed() == NonVolatileRegsPushSize);
 
     // Put the 'argv' argument into a non-argument/return/TLS register so that
-    // we can use 'argv' while we fill in the arguments for the asm.js callee.
+    // we can use 'argv' while we fill in the arguments for the wasm callee.
     // Use a second non-argument/return register as temporary scratch.
     Register argv = ABINonArgReturnReg0;
     Register scratch = ABINonArgReturnReg1;
 
     // Read the arguments of wasm::ExportFuncPtr according to the native ABI.
     // The entry stub's frame is 1 word.
     const unsigned argBase = sizeof(void*) + masm.framePushed();
     ABIArgGenerator abi;
@@ -757,18 +755,17 @@ GenerateImportJitExit(MacroAssembler& ma
 
     // 6. Check if we need to rectify arguments
     masm.load16ZeroExtend(Address(callee, JSFunction::offsetOfNargs()), scratch);
 
     Label rectify;
     masm.branch32(Assembler::Above, scratch, Imm32(fi.sig().args().length()), &rectify);
 
     // 7. If we haven't rectified arguments, load callee executable entry point
-    masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), callee);
-    masm.loadBaselineOrIonNoArgCheck(callee, callee, nullptr);
+    masm.loadJitCodeNoArgCheck(callee, callee, nullptr);
 
     Label rejoinBeforeCall;
     masm.bind(&rejoinBeforeCall);
 
     AssertStackAlignment(masm, JitStackAlignment, sizeOfRetAddr);
     masm.callJitNoProfiler(callee);
 
     // Note that there might be a GC thing in the JSReturnOperand now.
--- a/js/xpconnect/loader/ChromeScriptLoader.cpp
+++ b/js/xpconnect/loader/ChromeScriptLoader.cpp
@@ -44,18 +44,17 @@ public:
                         Promise* aPromise)
       : mozilla::Runnable("AsyncScriptCompiler")
       , mOptions(aCx)
       , mURL(aURL)
       , mGlobalObject(aGlobal)
       , mPromise(aPromise)
       , mCharset(aOptions.mCharset)
     {
-        mOptions.setVersion(JSVERSION_DEFAULT)
-                .setNoScriptRval(!aOptions.mHasReturnValue)
+        mOptions.setNoScriptRval(!aOptions.mHasReturnValue)
                 .setCanLazilyParse(aOptions.mLazilyParse)
                 .setFile(aCx, mURL.get());
     }
 
     nsresult Start(nsIPrincipal* aPrincipal);
 
     inline void
     SetToken(void* aToken)
--- a/js/xpconnect/loader/ScriptPreloader.cpp
+++ b/js/xpconnect/loader/ScriptPreloader.cpp
@@ -1005,17 +1005,17 @@ ScriptPreloader::DecodeNextBatch(size_t 
     if (size == 0 && mPendingScripts.isEmpty()) {
         return;
     }
 
     AutoSafeJSAPI jsapi;
     JSContext* cx = jsapi.cx();
     JSAutoCompartment ac(cx, scope ? scope : CompilationScope(cx));
 
-    JS::CompileOptions options(cx, JSVERSION_DEFAULT);
+    JS::CompileOptions options(cx);
     options.setNoScriptRval(true)
            .setSourceIsLazy(true);
 
     if (!JS::CanCompileOffThread(cx, options, size) ||
         !JS::DecodeMultiOffThreadScripts(cx, options, mParsingSources,
                                          OffThreadDecodeCallback,
                                          static_cast<void*>(this))) {
         // If we fail here, we don't move on to process the next batch, so make
--- a/js/xpconnect/loader/mozJSComponentLoader.cpp
+++ b/js/xpconnect/loader/mozJSComponentLoader.cpp
@@ -527,18 +527,16 @@ mozJSComponentLoader::CreateLoaderGlobal
     NS_ENSURE_SUCCESS_VOID(rv);
 
     CompartmentOptions options;
 
     options.creationOptions()
            .setSystemZone()
            .setAddonId(aAddonID);
 
-    options.behaviors().setVersion(JSVERSION_DEFAULT);
-
     if (xpc::SharedMemoryEnabled())
         options.creationOptions().setSharedMemoryAndAtomicsEnabled(true);
 
     // Defer firing OnNewGlobalObject until after the __URI__ property has
     // been defined so the JS debugger can tell what module the global is
     // for
     RootedObject global(aCx);
     rv = xpc::InitClassesWithNewWrappedGlobal(aCx,
@@ -773,17 +771,16 @@ mozJSComponentLoader::ObjectForLocation(
         // Use lazy source if we're using the startup cache. Non-lazy source +
         // startup cache regresses installer size (due to source code stored in
         // XDR encoded modules in omni.ja). Also, XDR decoding is relatively
         // fast. When we're not using the startup cache, we want to use non-lazy
         // source code so that we can use lazy parsing.
         // See bug 1303754.
         CompileOptions options(cx);
         options.setNoScriptRval(true)
-               .setVersion(JSVERSION_DEFAULT)
                .maybeMakeStrictMode(true)
                .setFileAndLine(nativePath.get(), 1)
                .setSourceIsLazy(cache || ScriptPreloader::GetSingleton().Active());
 
         if (realFile) {
             AutoMemMap map;
             MOZ_TRY(map.init(aComponentFile));
 
--- a/js/xpconnect/loader/mozJSSubScriptLoader.cpp
+++ b/js/xpconnect/loader/mozJSSubScriptLoader.cpp
@@ -89,23 +89,21 @@ mozJSSubScriptLoader::~mozJSSubScriptLoa
 
 NS_IMPL_ISUPPORTS(mozJSSubScriptLoader, mozIJSSubScriptLoader)
 
 #define JSSUB_CACHE_PREFIX(aType) "jssubloader/" aType
 
 static void
 SubscriptCachePath(JSContext* cx, nsIURI* uri, JS::HandleObject targetObj, nsACString& cachePath)
 {
-    // StartupCache must distinguish between non-syntactic vs global, as well as
-    // javascript version when computing the cache key.
+    // StartupCache must distinguish between non-syntactic vs global when
+    // computing the cache key.
     bool hasNonSyntacticScope = !JS_IsGlobalObject(targetObj);
-    JSVersion version = JS_GetVersion(cx);
     cachePath.Assign(hasNonSyntacticScope ? JSSUB_CACHE_PREFIX("non-syntactic")
                                           : JSSUB_CACHE_PREFIX("global"));
-    cachePath.AppendPrintf("/%d", version);
     PathifyURI(uri, cachePath);
 }
 
 static void
 ReportError(JSContext* cx, const nsACString& msg)
 {
     NS_ConvertUTF8toUTF16 ucMsg(msg);
 
@@ -142,17 +140,16 @@ PrepareScript(nsIURI* uri,
               const nsAString& charset,
               const char* buf,
               int64_t len,
               bool wantReturnValue,
               MutableHandleScript script)
 {
     JS::CompileOptions options(cx);
     options.setFileAndLine(uriStr, 1)
-           .setVersion(JSVERSION_DEFAULT)
            .setNoScriptRval(!wantReturnValue);
     if (!charset.IsVoid()) {
         char16_t* scriptBuf = nullptr;
         size_t scriptLength = 0;
 
         nsresult rv =
             ScriptLoader::ConvertToUTF16(nullptr, reinterpret_cast<const uint8_t*>(buf), len,
                                          charset, nullptr, scriptBuf, scriptLength);
--- a/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -1878,18 +1878,17 @@ xpc::EvalInSandbox(JSContext* cx, Handle
     {
         // We're about to evaluate script, so make an AutoEntryScript.
         // This is clearly Gecko-specific and not in any spec.
         mozilla::dom::AutoEntryScript aes(priv, "XPConnect sandbox evaluation");
         JSContext* sandcx = aes.cx();
         JSAutoCompartment ac(sandcx, sandbox);
 
         JS::CompileOptions options(sandcx);
-        options.setFileAndLine(filenameBuf.get(), lineNo)
-               .setVersion(jsVersion);
+        options.setFileAndLine(filenameBuf.get(), lineNo);
         MOZ_ASSERT(JS_IsGlobalObject(sandbox));
         ok = JS::Evaluate(sandcx, options,
                           PromiseFlatString(source).get(), source.Length(), &v);
 
         // If the sandbox threw an exception, grab it off the context.
         if (aes.HasException()) {
             if (!aes.StealException(&exn)) {
                 return NS_ERROR_OUT_OF_MEMORY;
--- a/js/xpconnect/src/XPCShellImpl.cpp
+++ b/js/xpconnect/src/XPCShellImpl.cpp
@@ -382,26 +382,16 @@ Load(JSContext* cx, unsigned argc, Value
             }
         }
     }
     args.rval().setUndefined();
     return true;
 }
 
 static bool
-Version(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    args.rval().setInt32(JS_GetVersion(cx));
-    if (args.get(0).isInt32())
-        SetVersionForCurrentRealm(cx, JSVersion(args[0].toInt32()));
-    return true;
-}
-
-static bool
 Quit(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     gExitCode = 0;
     if (!ToInt32(cx, args.get(0), &gExitCode))
         return false;
 
@@ -657,17 +647,16 @@ RegisterXPCTestComponents(JSContext* cx,
 }
 #endif
 
 static const JSFunctionSpec glob_functions[] = {
     JS_FN("print",           Print,          0,0),
     JS_FN("readline",        ReadLine,       1,0),
     JS_FN("load",            Load,           1,0),
     JS_FN("quit",            Quit,           0,0),
-    JS_FN("version",         Version,        1,0),
     JS_FN("dumpXPC",         DumpXPC,        1,0),
     JS_FN("dump",            Dump,           1,0),
     JS_FN("gc",              GC,             0,0),
 #ifdef JS_GC_ZEAL
     JS_FN("gczeal",          GCZeal,         1,0),
 #endif
     JS_FN("options",         Options,        0,0),
     JS_FN("sendCommand",     SendCommand,    1,0),
@@ -835,17 +824,17 @@ Process(AutoJSAPI& jsapi, const char* fi
         fclose(file);
     return ok;
 }
 
 static int
 usage()
 {
     fprintf(gErrFile, "%s\n", JS_GetImplementationVersion());
-    fprintf(gErrFile, "usage: xpcshell [-g gredir] [-a appdir] [-r manifest]... [-WwxiCSsmIp] [-v version] [-f scriptfile] [-e script] [scriptfile] [scriptarg...]\n");
+    fprintf(gErrFile, "usage: xpcshell [-g gredir] [-a appdir] [-r manifest]... [-WwxiCSsmIp] [-f scriptfile] [-e script] [scriptfile] [scriptarg...]\n");
     return 2;
 }
 
 static bool
 printUsageAndSetExitCode()
 {
     gExitCode = usage();
     return false;
@@ -948,22 +937,16 @@ ProcessArgs(AutoJSAPI& jsapi, char** arg
 
     for (int i = 0; i < argc; i++) {
         if (argv[i][0] != '-' || argv[i][1] == '\0') {
             filename = argv[i++];
             isInteractive = false;
             break;
         }
         switch (argv[i][1]) {
-        case 'v':
-            if (++i == argc) {
-                return printUsageAndSetExitCode();
-            }
-            SetVersionForCurrentRealm(cx, JSVersion(atoi(argv[i])));
-            break;
         case 'W':
             reportWarnings = false;
             break;
         case 'w':
             reportWarnings = true;
             break;
         case 'x':
             break;
@@ -1302,17 +1285,16 @@ XRE_XPCShellMain(int argc, char** argv, 
         }
 
         // Make the default XPCShell global use a fresh zone (rather than the
         // System Zone) to improve cross-zone test coverage.
         JS::CompartmentOptions options;
         options.creationOptions().setNewZoneInSystemZoneGroup();
         if (xpc::SharedMemoryEnabled())
             options.creationOptions().setSharedMemoryAndAtomicsEnabled(true);
-        options.behaviors().setVersion(JSVERSION_DEFAULT);
         JS::Rooted<JSObject*> glob(cx);
         rv = xpc::InitClassesWithNewWrappedGlobal(cx,
                                                   static_cast<nsIGlobalObject*>(backstagePass),
                                                   systemprincipal,
                                                   0,
                                                   options,
                                                   &glob);
         if (NS_FAILED(rv))
--- a/layout/forms/nsButtonFrameRenderer.cpp
+++ b/layout/forms/nsButtonFrameRenderer.cpp
@@ -288,16 +288,27 @@ nsDisplayButtonBorder::BuildLayer(nsDisp
 
 bool
 nsDisplayButtonBorder::CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
                                                mozilla::wr::IpcResourceUpdateQueue& aResources,
                                                const StackingContextHelper& aSc,
                                                mozilla::layers::WebRenderLayerManager* aManager,
                                                nsDisplayListBuilder* aDisplayListBuilder)
 {
+  // This is really a combination of paint box shadow inner +
+  // paint border.
+  nsRect buttonRect = nsRect(ToReferenceFrame(), mFrame->GetSize());
+  bool snap;
+  nsRegion visible = GetBounds(aDisplayListBuilder, &snap);
+  nsDisplayBoxShadowInner::CreateInsetBoxShadowWebRenderCommands(aBuilder,
+                                                                 aSc,
+                                                                 visible,
+                                                                 mFrame,
+                                                                 buttonRect);
+
   bool borderIsEmpty = false;
   Maybe<nsCSSBorderRenderer> br =
   nsCSSRendering::CreateBorderRenderer(mFrame->PresContext(),
                                        nullptr,
                                        mFrame,
                                        nsRect(),
                                        nsRect(ToReferenceFrame(), mFrame->GetSize()),
                                        mFrame->StyleContext(),
@@ -308,27 +319,16 @@ nsDisplayButtonBorder::CreateWebRenderCo
       return true;
     }
     return false;
   }
   if (!br->CanCreateWebRenderCommands()) {
     return false;
   }
 
-  // This is really a combination of paint box shadow inner +
-  // paint border.
-  nsRect buttonRect = nsRect(ToReferenceFrame(), mFrame->GetSize());
-  bool snap;
-  nsRegion visible = GetBounds(aDisplayListBuilder, &snap);
-  nsDisplayBoxShadowInner::CreateInsetBoxShadowWebRenderCommands(aBuilder,
-                                                                 aSc,
-                                                                 visible,
-                                                                 mFrame,
-                                                                 buttonRect);
-
   br->CreateWebRenderCommands(this, aBuilder, aResources, aSc);
 
   return true;
 }
 
 void
 nsDisplayButtonBorder::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                                  const nsDisplayItemGeometry* aGeometry,
--- a/layout/painting/FrameLayerBuilder.cpp
+++ b/layout/painting/FrameLayerBuilder.cpp
@@ -1278,19 +1278,21 @@ protected:
    * a layer doesn't exist.
    *
    * Since mask layers can exist either on the layer directly, or as a side-
    * attachment to FrameMetrics (for ancestor scrollframe clips), we key the
    * recycle operation on both the originating layer and the mask layer's
    * index in the layer, if any.
    */
   struct MaskLayerKey;
-  already_AddRefed<ImageLayer>
-  CreateOrRecycleMaskImageLayerFor(const MaskLayerKey& aKey,
-                                   void(*aSetUserData)(Layer* aLayer));
+  template<typename UserData>
+  already_AddRefed<ImageLayer> CreateOrRecycleMaskImageLayerFor(
+      const MaskLayerKey& aKey,
+      UserData* (*aGetUserData)(Layer* aLayer),
+      void (*aSetDefaultUserData)(Layer* aLayer));
   /**
    * Grabs all PaintedLayers and ColorLayers from the ContainerLayer and makes them
    * available for recycling.
    */
   void CollectOldLayers();
   /**
    * If aItem used to belong to a PaintedLayer, invalidates the area of
    * aItem in that layer. If aNewLayer is a PaintedLayer, invalidates the area of
@@ -1760,25 +1762,16 @@ private:
 
   bool mTextureClientLocked;
   gfx::IntSize mSize;
   LayerManager* mLayerManager;
   RefPtr<gfx::DrawTarget> mDrawTarget;
   RefPtr<TextureClient> mTextureClient;
 };
 
-/**
-  * Helper functions for getting user data and casting it to the correct type.
-  * aLayer is the layer where the user data is stored.
-  */
-MaskLayerUserData* GetMaskLayerUserData(Layer* aLayer)
-{
-  return static_cast<MaskLayerUserData*>(aLayer->GetUserData(&gMaskLayerUserData));
-}
-
 PaintedDisplayItemLayerUserData* GetPaintedDisplayItemLayerUserData(Layer* aLayer)
 {
   return static_cast<PaintedDisplayItemLayerUserData*>(
     aLayer->GetUserData(&gPaintedDisplayItemLayerUserData));
 }
 
 /* static */ void
 FrameLayerBuilder::Shutdown()
@@ -2200,31 +2193,36 @@ ContainerState::CreateOrRecycleImageLaye
     layer->SetUserData(&gImageLayerUserData, nullptr);
 
     // Remove other layer types we might have stored for this PaintedLayer
     data->mColorLayer = nullptr;
   }
   return layer.forget();
 }
 
+template<typename UserData>
 already_AddRefed<ImageLayer>
-ContainerState::CreateOrRecycleMaskImageLayerFor(const MaskLayerKey& aKey,
-                                                 void(*aSetUserData)(Layer* aLayer))
+ContainerState::CreateOrRecycleMaskImageLayerFor(
+    const MaskLayerKey& aKey,
+    UserData* (*aGetUserData)(Layer* aLayer),
+    void (*aSetDefaultUserData)(Layer* aLayer))
 {
   RefPtr<ImageLayer> result = mRecycledMaskImageLayers.Get(aKey);
-  if (result) {
+
+  if (result && aGetUserData(result.get())) {
     mRecycledMaskImageLayers.Remove(aKey);
     aKey.mLayer->ClearExtraDumpInfo();
     // XXX if we use clip on mask layers, null it out here
   } else {
     // Create a new layer
     result = mManager->CreateImageLayer();
-    if (!result)
+    if (!result) {
       return nullptr;
-    aSetUserData(result);
+    }
+    aSetDefaultUserData(result);
   }
 
   return result.forget();
 }
 
 static const double SUBPIXEL_OFFSET_EPSILON = 0.02;
 
 /**
@@ -3853,33 +3851,45 @@ GetASRForPerspective(const ActiveScrolle
     nsIFrame* scrolledFrame = asr->mScrollableFrame->GetScrolledFrame();
     if (nsLayoutUtils::IsAncestorFrameCrossDoc(scrolledFrame, aPerspectiveFrame)) {
       return asr;
     }
   }
   return nullptr;
 }
 
+CSSMaskLayerUserData*
+GetCSSMaskLayerUserData(Layer* aMaskLayer)
+{
+  if (!aMaskLayer) {
+    return nullptr;
+  }
+
+  return static_cast<CSSMaskLayerUserData*>(aMaskLayer->GetUserData(&gCSSMaskLayerUserData));
+}
+
 void
 SetCSSMaskLayerUserData(Layer* aMaskLayer)
 {
+  MOZ_ASSERT(aMaskLayer);
+
   aMaskLayer->SetUserData(&gCSSMaskLayerUserData,
                           new CSSMaskLayerUserData());
 }
 
 void
 ContainerState::SetupMaskLayerForCSSMask(Layer* aLayer,
                                          nsDisplayMask* aMaskItem)
 {
   RefPtr<ImageLayer> maskLayer =
     CreateOrRecycleMaskImageLayerFor(MaskLayerKey(aLayer, Nothing()),
+                                     GetCSSMaskLayerUserData,
                                      SetCSSMaskLayerUserData);
-
-  CSSMaskLayerUserData* oldUserData =
-    static_cast<CSSMaskLayerUserData*>(maskLayer->GetUserData(&gCSSMaskLayerUserData));
+  CSSMaskLayerUserData* oldUserData = GetCSSMaskLayerUserData(maskLayer.get());
+  MOZ_ASSERT(oldUserData);
 
   bool snap;
   nsRect bounds = aMaskItem->GetBounds(mBuilder, &snap);
   nsIntRect itemRect = ScaleToOutsidePixels(bounds, snap);
 
   // Setup mask layer offset.
   // We do not repaint mask for mask position change, so update base transform
   // each time is required.
@@ -6322,19 +6332,31 @@ ContainerState::SetupMaskLayer(Layer *aL
     SetClipCount(paintedData, 0);
     return;
   }
 
   aLayer->SetMaskLayer(maskLayer);
   SetClipCount(paintedData, aRoundedRectClipCount);
 }
 
+MaskLayerUserData*
+GetMaskLayerUserData(Layer* aMaskLayer)
+{
+  if (!aMaskLayer) {
+    return nullptr;
+  }
+
+  return static_cast<MaskLayerUserData*>(aMaskLayer->GetUserData(&gMaskLayerUserData));
+}
+
 void
 SetMaskLayerUserData(Layer* aMaskLayer)
 {
+  MOZ_ASSERT(aMaskLayer);
+
   aMaskLayer->SetUserData(&gMaskLayerUserData,
                           new MaskLayerUserData());
 }
 
 already_AddRefed<Layer>
 ContainerState::CreateMaskLayer(Layer *aLayer,
                                const DisplayItemClip& aClip,
                                const Maybe<size_t>& aForAncestorMaskLayer,
@@ -6343,20 +6365,21 @@ ContainerState::CreateMaskLayer(Layer *a
   // aLayer will never be the container layer created by an nsDisplayMask
   // because nsDisplayMask propagates the DisplayItemClip to its contents
   // and is not clipped itself.
   // This assertion will fail if that ever stops being the case.
   MOZ_ASSERT(!aLayer->GetUserData(&gCSSMaskLayerUserData),
              "A layer contains round clips should not have css-mask on it.");
 
   // check if we can re-use the mask layer
-  MaskLayerKey recycleKey(aLayer, aForAncestorMaskLayer);
   RefPtr<ImageLayer> maskLayer =
-    CreateOrRecycleMaskImageLayerFor(recycleKey, SetMaskLayerUserData);
-  MaskLayerUserData* userData = GetMaskLayerUserData(maskLayer);
+      CreateOrRecycleMaskImageLayerFor(MaskLayerKey(aLayer, aForAncestorMaskLayer),
+                                       GetMaskLayerUserData,
+                                       SetMaskLayerUserData);
+  MaskLayerUserData* userData = GetMaskLayerUserData(maskLayer.get());
 
   int32_t A2D = mContainerFrame->PresContext()->AppUnitsPerDevPixel();
   MaskLayerUserData newData(aClip, aRoundedRectClipCount, A2D, mParameters);
   if (*userData == newData) {
     return maskLayer.forget();
   }
 
   gfx::Rect boundingRect = CalculateBounds(newData.mRoundedClipRects,
--- a/layout/svg/SVGContextPaint.h
+++ b/layout/svg/SVGContextPaint.h
@@ -267,19 +267,25 @@ public:
            mStroke == aOther.mStroke &&
            mFillOpacity == aOther.mFillOpacity &&
            mStrokeOpacity == aOther.mStrokeOpacity;
   }
 
   void SetFill(nscolor aFill) {
     mFill.emplace(gfx::ToDeviceColor(aFill));
   }
+  const Maybe<Color>& GetFill() const {
+    return mFill;
+  }
   void SetStroke(nscolor aStroke) {
     mStroke.emplace(gfx::ToDeviceColor(aStroke));
   }
+  const Maybe<Color>& GetStroke() const {
+    return mStroke;
+  }
 
   /**
    * Returns a pattern of type PatternType::COLOR, or else nullptr.
    */
   already_AddRefed<gfxPattern>
   GetFillPattern(const DrawTarget* aDrawTarget, float aFillOpacity,
                  const gfxMatrix& aCTM, imgDrawingParams& aImgParams) override;
 
--- a/layout/tools/reftest/remotereftest.py
+++ b/layout/tools/reftest/remotereftest.py
@@ -75,17 +75,16 @@ class ReftestServer:
         "Run the Refest server, returning the process ID of the server."
 
         env = self.automation.environment(xrePath=self._xrePath)
         env["XPCOM_DEBUG_BREAK"] = "warn"
         if self.automation.IS_WIN32:
             env["PATH"] = env["PATH"] + ";" + self._xrePath
 
         args = ["-g", self._xrePath,
-                "-v", "170",
                 "-f", os.path.join(self._httpdPath, "httpd.js"),
                 "-e", "const _PROFILE_PATH = '%(profile)s';const _SERVER_PORT = "
                       "'%(port)s'; const _SERVER_ADDR ='%(server)s';" % {
                       "profile": self._profileDir.replace('\\', '\\\\'), "port": self.httpPort,
                       "server": self.webServer},
                 "-f", os.path.join(self.scriptDir, "server.js")]
 
         xpcshell = os.path.join(self._utilityPath,
--- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
@@ -616,18 +616,23 @@ public class BrowserApp extends GeckoApp
         return super.onKeyUp(keyCode, event);
     }
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
         final Context appContext = getApplicationContext();
 
         showSplashScreen = true;
-        GeckoLoader.loadMozGlue(appContext);
-        if (!HardwareUtils.isSupportedSystem() || !GeckoLoader.neonCompatible()) {
+
+        boolean supported = HardwareUtils.isSupportedSystem();
+        if (supported) {
+            GeckoLoader.loadMozGlue(appContext);
+            supported = GeckoLoader.neonCompatible();
+        }
+        if (!supported) {
             // This build does not support the Android version of the device; Exit early.
             super.onCreate(savedInstanceState);
             return;
         }
 
         final SafeIntent intent = new SafeIntent(getIntent());
         final boolean isInAutomation = IntentUtils.getIsInAutomationFromEnvironment(intent);
 
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
@@ -909,21 +909,22 @@ public abstract class GeckoApp extends G
      **/
     @Override
     public void onCreate(Bundle savedInstanceState) {
         // Enable Android Strict Mode for developers' local builds (the "default" channel).
         if ("default".equals(AppConstants.MOZ_UPDATE_CHANNEL)) {
             enableStrictMode();
         }
 
-        // Mozglue should already be loaded by BrowserApp.onCreate() in Fennec, but in
-        // custom tabs it may not be.
-        GeckoLoader.loadMozGlue(getApplicationContext());
-
-        if (!HardwareUtils.isSupportedSystem() || !GeckoLoader.neonCompatible()) {
+        boolean supported = HardwareUtils.isSupportedSystem();
+        if (supported) {
+            GeckoLoader.loadMozGlue(getApplicationContext());
+            supported = GeckoLoader.neonCompatible();
+        }
+        if (!supported) {
             // This build does not support the Android version of the device: Show an error and finish the app.
             mIsAbortingAppLaunch = true;
             super.onCreate(savedInstanceState);
             showSDKVersionError();
             finish();
             return;
         }
 
--- a/mobile/android/components/DirectoryProvider.js
+++ b/mobile/android/components/DirectoryProvider.js
@@ -15,20 +15,17 @@ XPCOMUtils.defineLazyModuleGetters(this,
   Services: "resource://gre/modules/Services.jsm",
 });
 
 // -----------------------------------------------------------------------
 // Directory Provider for special browser folders and files
 // -----------------------------------------------------------------------
 
 const NS_APP_CACHE_PARENT_DIR = "cachePDir";
-const NS_APP_SEARCH_DIR       = "SrchPlugns";
-const NS_APP_SEARCH_DIR_LIST  = "SrchPluginsDL";
 const NS_APP_DISTRIBUTION_SEARCH_DIR_LIST = "SrchPluginsDistDL";
-const NS_APP_USER_SEARCH_DIR  = "UsrSrchPlugns";
 const NS_XPCOM_CURRENT_PROCESS_DIR = "XCurProcD";
 const XRE_APP_DISTRIBUTION_DIR = "XREAppDist";
 const XRE_UPDATE_ROOT_DIR     = "UpdRootD";
 const ENVVAR_UPDATE_DIR       = "UPDATES_DIRECTORY";
 const WEBAPPS_DIR             = "webappsDir";
 
 const SYSTEM_DIST_PATH = `/system/${AppConstants.ANDROID_PACKAGE_NAME}/distribution`;
 
@@ -136,41 +133,21 @@ DirectoryProvider.prototype = {
       defLocalePlugins.append(defLocale);
       if (defLocalePlugins.exists())
         array.push(defLocalePlugins);
     } catch (e) {
     }
   },
 
   getFiles: function(prop) {
-    if (prop != NS_APP_SEARCH_DIR_LIST &&
-        prop != NS_APP_DISTRIBUTION_SEARCH_DIR_LIST)
+    if (prop != NS_APP_DISTRIBUTION_SEARCH_DIR_LIST)
       return null;
 
     let result = [];
-
-    if (prop == NS_APP_DISTRIBUTION_SEARCH_DIR_LIST) {
-      this._appendDistroSearchDirs(result);
-    } else {
-      /**
-       * We want to preserve the following order, since the search service
-       * loads engines in first-loaded-wins order.
-       *   - distro search plugin locations (loaded separately by the search
-       *     service)
-       *   - user search plugin locations (profile)
-       *   - app search plugin location (shipped engines)
-       */
-      let appUserSearchDir = FileUtils.getDir(NS_APP_USER_SEARCH_DIR, [], false);
-      if (appUserSearchDir.exists())
-        result.push(appUserSearchDir);
-
-      let appSearchDir = FileUtils.getDir(NS_APP_SEARCH_DIR, [], false);
-      if (appSearchDir.exists())
-        result.push(appSearchDir);
-    }
+    this._appendDistroSearchDirs(result);
 
     return {
       QueryInterface: XPCOMUtils.generateQI([Ci.nsISimpleEnumerator]),
       hasMoreElements: function() {
         return result.length > 0;
       },
       getNext: function() {
         return result.shift();
--- a/netwerk/base/ProxyAutoConfig.cpp
+++ b/netwerk/base/ProxyAutoConfig.cpp
@@ -649,17 +649,16 @@ private:
     if (!JS::InitSelfHostedCode(mContext)) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
 
     JSAutoRequest ar(mContext);
 
     JS::CompartmentOptions options;
     options.creationOptions().setSystemZone();
-    options.behaviors().setVersion(JSVERSION_LATEST);
     mGlobal = JS_NewGlobalObject(mContext, &sGlobalClass, nullptr,
                                  JS::DontFireOnNewGlobalHook, options);
     if (!mGlobal) {
       JS_ClearPendingException(mContext);
       return NS_ERROR_OUT_OF_MEMORY;
     }
     JS::Rooted<JSObject*> global(mContext, mGlobal);
 
--- a/netwerk/base/nsIBrowserSearchService.idl
+++ b/netwerk/base/nsIBrowserSearchService.idl
@@ -376,19 +376,17 @@ interface nsIBrowserSearchService : nsIS
                             in jsval iconURL,
                             [optional] in AString alias,
                             [optional] in AString description,
                             [optional] in AString method,
                             [optional] in AString url,
                             [optional] in AString extensionID);
 
   /**
-   * Un-hides all engines installed in the directory corresponding to
-   * the directory service's NS_APP_SEARCH_DIR key. (i.e. the set of
-   * engines returned by getDefaultEngines)
+   * Un-hides all engines in the set of engines returned by getDefaultEngines.
    */
   void restoreDefaultEngines();
 
   /**
    * Returns an engine with the specified alias.
    *
    * @param   alias
    *          The search engine's alias.
--- a/netwerk/cookie/nsCookieService.cpp
+++ b/netwerk/cookie/nsCookieService.cpp
@@ -1426,17 +1426,16 @@ nsCookieService::TryInitDB(bool aRecreat
       // Note that we have to switch out our DBState temporarily, in case we're in
       // private browsing mode; otherwise ImportCookies() won't be happy.
       DBState* initialState = gCookieService->mDBState;
       gCookieService->mDBState = gCookieService->mDefaultDBState;
       oldCookieFile->AppendNative(NS_LITERAL_CSTRING(OLD_COOKIE_FILE_NAME));
       gCookieService->ImportCookies(oldCookieFile);
       oldCookieFile->Remove(false);
       gCookieService->mDBState = initialState;
-      Telemetry::Accumulate(Telemetry::MOZ_SQLITE_COOKIES_OLD_SCHEMA, 0);
     });
 
   NS_DispatchToMainThread(runnable);
 
   return RESULT_OK;
 }
 
 void
@@ -1724,16 +1723,17 @@ nsCookieService::CloseDBStates()
   if (mDefaultDBState->dbConn) {
     // Asynchronously close the connection. We will null it below.
     mDefaultDBState->dbConn->AsyncClose(mDefaultDBState->closeListener);
   }
 
   CleanupDefaultDBConnection();
 
   mDefaultDBState = nullptr;
+  mInitializedDBConn = false;
   mInitializedDBStates = false;
 }
 
 // Null out the statements.
 // This must be done before closing the connection.
 void
 nsCookieService::CleanupCachedStatements()
 {
@@ -1760,18 +1760,16 @@ nsCookieService::CleanupDefaultDBConnect
 
   // Manually null out our listeners. This is necessary because they hold a
   // strong ref to the DBState itself. They'll stay alive until whatever
   // statements are still executing complete.
   mDefaultDBState->insertListener = nullptr;
   mDefaultDBState->updateListener = nullptr;
   mDefaultDBState->removeListener = nullptr;
   mDefaultDBState->closeListener = nullptr;
-
-  mInitializedDBConn = false;
 }
 
 void
 nsCookieService::HandleDBClosed(DBState* aDBState)
 {
   COOKIE_LOGSTRING(LogLevel::Debug,
     ("HandleDBClosed(): DBState %p closed", aDBState));
 
@@ -1911,17 +1909,17 @@ nsCookieService::RebuildCorruptDB(DBStat
             return;
           }
 
           // Notify observers that we're beginning the rebuild.
           if (os) {
             os->NotifyObservers(nullptr, "cookie-db-rebuilding", nullptr);
           }
 
-          gCookieService->InitDBConn();
+          gCookieService->InitDBConnInternal();
 
           // Enumerate the hash, and add cookies to the params array.
           mozIStorageAsyncStatement* stmt = gCookieService->mDefaultDBState->stmtInsert;
           nsCOMPtr<mozIStorageBindingParamsArray> paramsArray;
           stmt->NewBindingParamsArray(getter_AddRefs(paramsArray));
           for (auto iter = gCookieService->mDefaultDBState->hostTable.Iter();
                !iter.Done();
                iter.Next()) {
@@ -3015,16 +3013,19 @@ nsCookieService::ImportCookies(nsIFile *
       NS_ASSERT_SUCCESS(rv);
       nsCOMPtr<mozIStoragePendingStatement> handle;
       rv = mDefaultDBState->stmtInsert->ExecuteAsync(
         mDefaultDBState->insertListener, getter_AddRefs(handle));
       NS_ASSERT_SUCCESS(rv);
     }
   }
 
+  if (mDefaultDBState->cookieCount - originalCookieCount > 0) {
+    Telemetry::Accumulate(Telemetry::MOZ_SQLITE_COOKIES_OLD_SCHEMA, 0);
+  }
 
   COOKIE_LOGSTRING(LogLevel::Debug, ("ImportCookies(): %" PRIu32 " cookies imported",
     mDefaultDBState->cookieCount));
 
   return NS_OK;
 }
 
 /******************************************************************************
--- a/taskcluster/ci/test/awsy.yml
+++ b/taskcluster/ci/test/awsy.yml
@@ -2,17 +2,17 @@ job-defaults:
     suite: awsy
     max-run-time: 7200
     instance-size: xlarge
     allow-software-gl-layers: false
     mozharness:
         script: awsy_script.py
         config:
             by-test-platform:
-                windows.*/opt:
+                windows.*:
                     - awsy/taskcluster_windows_config.py
                 macosx.*/opt:
                     - awsy/macosx_config.py
                 default:
                     - awsy/linux_config.py
 
 awsy:
     description: "Are we slim yet"
--- a/testing/mochitest/runtests.py
+++ b/testing/mochitest/runtests.py
@@ -434,18 +434,16 @@ class MochitestServer(object):
         env["TSAN_OPTIONS"] = "report_bugs=0"
 
         if mozinfo.isWin:
             env["PATH"] = env["PATH"] + ";" + str(self._xrePath)
 
         args = [
             "-g",
             self._xrePath,
-            "-v",
-            "170",
             "-f",
             os.path.join(
                 self._httpdPath,
                 "httpd.js"),
             "-e",
             "const _PROFILE_PATH = '%(profile)s'; const _SERVER_PORT = '%(port)s'; "
             "const _SERVER_ADDR = '%(server)s'; const _TEST_PREFIX = %(testPrefix)s; "
             "const _DISPLAY_RESULTS = %(displayResults)s;" % {
--- a/toolkit/components/search/nsSearchService.js
+++ b/toolkit/components/search/nsSearchService.js
@@ -51,25 +51,21 @@ XPCOMUtils.defineLazyGetter(this, "gEnco
 const MODE_RDONLY   = 0x01;
 const MODE_WRONLY   = 0x02;
 const MODE_CREATE   = 0x08;
 const MODE_APPEND   = 0x10;
 const MODE_TRUNCATE = 0x20;
 const PERMS_FILE    = 0o644;
 
 // Directory service keys
-const NS_APP_SEARCH_DIR_LIST  = "SrchPluginsDL";
 const NS_APP_DISTRIBUTION_SEARCH_DIR_LIST = "SrchPluginsDistDL";
-const NS_APP_USER_SEARCH_DIR  = "UsrSrchPlugns";
-const NS_APP_SEARCH_DIR       = "SrchPlugns";
 const NS_APP_USER_PROFILE_50_DIR = "ProfD";
 
-// Loading plugins from NS_APP_SEARCH_DIR is no longer supported.
-// Instead, we now load plugins from APP_SEARCH_PREFIX, where a
-// list.txt file needs to exist to list available engines.
+// We load plugins from APP_SEARCH_PREFIX, where a list.txt
+// file needs to exist to list available engines.
 const APP_SEARCH_PREFIX = "resource://search-plugins/";
 
 // See documentation in nsIBrowserSearchService.idl.
 const SEARCH_ENGINE_TOPIC        = "browser-search-engine-modified";
 const REQ_LOCALES_CHANGED_TOPIC  = "intl:requested-locales-changed";
 const QUIT_APPLICATION_TOPIC     = "quit-application";
 
 const SEARCH_ENGINE_REMOVED      = "engine-removed";
@@ -1222,23 +1218,16 @@ function Engine(aLocation, aIsReadOnly) 
   this._readOnly = aIsReadOnly;
   this._urls = [];
   this._metaData = {};
 
   let file, uri;
   if (typeof aLocation == "string") {
     this._shortName = aLocation;
   } else if (aLocation instanceof Ci.nsIFile) {
-    if (!aIsReadOnly) {
-      // This is an engine that was installed in NS_APP_USER_SEARCH_DIR by a
-      // previous version. We are converting the file to an engine stored only
-      // in JSON, but we need to keep the reference to the profile file to
-      // remove it if the user ever removes the engine.
-      this._filePath = aLocation.persistentDescriptor;
-    }
     file = aLocation;
   } else if (aLocation instanceof Ci.nsIURI) {
     switch (aLocation.scheme) {
       case "https":
       case "http":
       case "ftp":
       case "data":
       case "file":
@@ -2037,20 +2026,16 @@ Engine.prototype = {
     this._iconUpdateURL = aJson._iconUpdateURL || null;
     this._readOnly = aJson._readOnly == undefined;
     this._iconURI = makeURI(aJson._iconURL);
     this._iconMapObj = aJson._iconMapObj;
     this._metaData = aJson._metaData || {};
     if (aJson.filePath) {
       this._filePath = aJson.filePath;
     }
-    if (aJson.dirPath) {
-      this._dirPath = aJson.dirPath;
-      this._dirLastModifiedTime = aJson.dirLastModifiedTime;
-    }
     if (aJson.extensionID) {
       this._extensionID = aJson.extensionID;
     }
     for (let i = 0; i < aJson._urls.length; ++i) {
       let url = aJson._urls[i];
       let engineURL = new EngineURL(url.type || URLTYPE_SEARCH_HTML,
                                     url.method || "GET", url.template,
                                     url.resultDomain || undefined);
@@ -2088,22 +2073,16 @@ Engine.prototype = {
       json.queryCharset = this.queryCharset;
     if (!this._readOnly)
       json._readOnly = this._readOnly;
     if (this._filePath) {
       // File path is stored so that we can remove legacy xml files
       // from the profile if the user removes the engine.
       json.filePath = this._filePath;
     }
-    if (this._dirPath) {
-      // The directory path is only stored for extension-shipped engines,
-      // it's used to invalidate the cache.
-      json.dirPath = this._dirPath;
-      json.dirLastModifiedTime = this._dirLastModifiedTime;
-    }
     if (this._extensionID) {
       json.extensionID = this._extensionID;
     }
 
     return json;
   },
 
   setAttr(name, val) {
@@ -2926,65 +2905,37 @@ SearchService.prototype = {
       locations = {hasMoreElements: () => false};
     }
     while (locations.hasMoreElements()) {
       let dir = locations.getNext().QueryInterface(Ci.nsIFile);
       if (dir.directoryEntries.hasMoreElements())
         distDirs.push(dir);
     }
 
-    let otherDirs = [];
-    let userSearchDir = getDir(NS_APP_USER_SEARCH_DIR);
-    locations = getDir(NS_APP_SEARCH_DIR_LIST, Ci.nsISimpleEnumerator);
-    while (locations.hasMoreElements()) {
-      let dir = locations.getNext().QueryInterface(Ci.nsIFile);
-      if ((!cache.engines || !dir.equals(userSearchDir)) &&
-          dir.directoryEntries.hasMoreElements())
-        otherDirs.push(dir);
-    }
-
-    function modifiedDir(aDir) {
-      return cacheOtherPaths.get(aDir.path) != aDir.lastModifiedTime;
-    }
-
     function notInCacheVisibleEngines(aEngineName) {
       return cache.visibleDefaultEngines.indexOf(aEngineName) == -1;
     }
 
     let buildID = Services.appinfo.platformBuildID;
-    let cacheOtherPaths = new Map();
-    if (cache.engines) {
-      for (let engine of cache.engines) {
-        if (engine._dirPath) {
-          cacheOtherPaths.set(engine._dirPath, engine._dirLastModifiedTime);
-        }
-      }
-    }
-
     let rebuildCache = !cache.engines ||
                        cache.version != CACHE_VERSION ||
                        cache.locale != getLocale() ||
                        cache.buildID != buildID ||
-                       cacheOtherPaths.size != otherDirs.length ||
-                       otherDirs.some(d => !cacheOtherPaths.has(d.path)) ||
                        cache.visibleDefaultEngines.length != this._visibleDefaultEngines.length ||
-                       this._visibleDefaultEngines.some(notInCacheVisibleEngines) ||
-                       otherDirs.some(modifiedDir);
+                       this._visibleDefaultEngines.some(notInCacheVisibleEngines);
 
     if (rebuildCache) {
       LOG("_loadEngines: Absent or outdated cache. Loading engines from disk.");
       distDirs.forEach(this._loadEnginesFromDir, this);
 
       this._loadFromChromeURLs(chromeURIs);
 
       LOG("_loadEngines: load user-installed engines from the obsolete cache");
       this._loadEnginesFromCache(cache, true);
 
-      otherDirs.forEach(this._loadEnginesFromDir, this);
-
       this._loadEnginesMetadataFromCache(cache);
       this._buildCache();
       return;
     }
 
     LOG("_loadEngines: loading from cache directories");
     this._loadEnginesFromCache(cache);
 
@@ -3024,100 +2975,42 @@ SearchService.prototype = {
         if (!done) {
           distDirs.push(dir);
         }
       } finally {
         iterator.close();
       }
     }
 
-    // Add the non-empty directories of NS_APP_SEARCH_DIR_LIST to
-    // otherDirs...
-    let otherDirs = [];
-    let userSearchDir = getDir(NS_APP_USER_SEARCH_DIR);
-    locations = getDir(NS_APP_SEARCH_DIR_LIST, Ci.nsISimpleEnumerator);
-    while (locations.hasMoreElements()) {
-      let dir = locations.getNext().QueryInterface(Ci.nsIFile);
-      if (cache.engines && dir.equals(userSearchDir))
-        continue;
-      let iterator = new OS.File.DirectoryIterator(dir.path,
-                                                   { winPattern: "*.xml" });
-      try {
-        // Add dir to otherDirs if it contains any files.
-        let {done} = await checkForSyncCompletion(iterator.next());
-        if (!done) {
-          otherDirs.push(dir);
-        }
-      } finally {
-        iterator.close();
-      }
-    }
-
-    let hasModifiedDir = async function(aList) {
-      let modifiedDir = false;
-
-      for (let dir of aList) {
-        let lastModifiedTime = cacheOtherPaths.get(dir.path);
-        if (!lastModifiedTime) {
-          continue;
-        }
-
-        let info = await OS.File.stat(dir.path);
-        if (lastModifiedTime != info.lastModificationDate.getTime()) {
-          modifiedDir = true;
-          break;
-        }
-      }
-      return modifiedDir;
-    };
-
     function notInCacheVisibleEngines(aEngineName) {
       return cache.visibleDefaultEngines.indexOf(aEngineName) == -1;
     }
 
     let buildID = Services.appinfo.platformBuildID;
-    let cacheOtherPaths = new Map();
-    if (cache.engines) {
-      for (let engine of cache.engines) {
-        if (engine._dirPath) {
-          cacheOtherPaths.set(engine._dirPath, engine._dirLastModifiedTime);
-        }
-      }
-    }
-
     let rebuildCache = !cache.engines ||
                        cache.version != CACHE_VERSION ||
                        cache.locale != getLocale() ||
                        cache.buildID != buildID ||
-                       cacheOtherPaths.size != otherDirs.length ||
-                       otherDirs.some(d => !cacheOtherPaths.has(d.path)) ||
                        cache.visibleDefaultEngines.length != this._visibleDefaultEngines.length ||
-                       this._visibleDefaultEngines.some(notInCacheVisibleEngines) ||
-                       (await checkForSyncCompletion(hasModifiedDir(otherDirs)));
+                       this._visibleDefaultEngines.some(notInCacheVisibleEngines);
 
     if (rebuildCache) {
       LOG("_asyncLoadEngines: Absent or outdated cache. Loading engines from disk.");
       for (let loadDir of distDirs) {
         let enginesFromDir =
           await checkForSyncCompletion(this._asyncLoadEnginesFromDir(loadDir));
         enginesFromDir.forEach(this._addEngineToStore, this);
       }
       let enginesFromURLs =
         await checkForSyncCompletion(this._asyncLoadFromChromeURLs(chromeURIs));
       enginesFromURLs.forEach(this._addEngineToStore, this);
 
       LOG("_asyncLoadEngines: loading user-installed engines from the obsolete cache");
       this._loadEnginesFromCache(cache, true);
 
-      for (let loadDir of otherDirs) {
-        let enginesFromDir =
-          await checkForSyncCompletion(this._asyncLoadEnginesFromDir(loadDir));
-        enginesFromDir.forEach(this._addEngineToStore, this);
-      }
-
       this._loadEnginesMetadataFromCache(cache);
       this._buildCache();
       return;
     }
 
     LOG("_asyncLoadEngines: loading from cache directories");
     this._loadEnginesFromCache(cache);
 
@@ -3169,18 +3062,17 @@ SearchService.prototype = {
         Services.obs.notifyObservers(null, SEARCH_SERVICE_TOPIC, "reinit-failed");
       } finally {
         Services.obs.notifyObservers(null, SEARCH_SERVICE_TOPIC, "reinit-complete");
       }
     })();
   },
 
   /**
-   * Read the cache file synchronously. This also imports data from the old
-   * search-metadata.json file if needed.
+   * Read the cache file synchronously.
    *
    * @returns A JS object containing the cached data.
    */
   _readCacheFile: function SRCH_SVC__readCacheFile() {
     if (this._cacheFileJSON) {
       return this._cacheFileJSON;
     }
 
@@ -3209,55 +3101,24 @@ SearchService.prototype = {
       if (json.appVersion != Services.appinfo.version &&
           geoSpecificDefaultsEnabled() &&
           json.metaData) {
         json.metaData.searchDefaultExpir = 0;
       }
       return json;
     } catch (ex) {
       LOG("_readCacheFile: Error reading cache file: " + ex);
-    } finally {
-      stream.close();
-    }
-
-    try {
-      cacheFile.leafName = "search-metadata.json";
-      stream = Cc["@mozilla.org/network/file-input-stream;1"].
-                 createInstance(Ci.nsIFileInputStream);
-      stream.init(cacheFile, MODE_RDONLY, PERMS_FILE, 0);
-      let metadata = parseJsonFromStream(stream);
-      let json = {};
-      if ("[global]" in metadata) {