Merge inbound to central, a=merge
authorWes Kocher <wkocher@mozilla.com>
Wed, 05 Apr 2017 14:16:37 -0700
changeset 556469 867df9483d5af4c8c12e19fab9b0de18bee30db7
parent 556468 9ffcfb1564d12239e9878026a92ffb58ba391d89 (current diff)
parent 556394 cb514a08571583d3602c809ee005414cd02b6fab (diff)
child 556470 b6600e1160451f597365c7458e6560b5f11f0081
child 556475 51d843ba5b12ce574ddfda84b88dd1541d5ebeda
child 556495 06873c5553a35ebd12df1eef3e78dcab2f0fdc97
child 556512 3e654460c195f54e4780e5c9b0980466e1f3797c
child 556536 f1f5393744f98c94cd0c995846c63981acf8892a
child 556572 f58e1015bf6312a23ef57cf6ca0682c3a2999dd4
child 556573 2452f698221f4a791ffa502893fde529abad20d1
child 556585 80f876a2c724dd9ad1f56e181c7adcbae29ab47a
child 556604 e457c1c65125b1fc4751a981be52bf07341308b2
child 556606 4dc154a3ad9a4673d46a6e2fedc0903b7dd6d36e
child 556613 e64c814db68c371c6e3f70ebf72e39be1436aec8
child 556616 cd16c550d3be8093f8b92637d5b1d370e016668d
child 556617 1bdfe8499c6ee378254d75424b3ba6085846c9d6
child 556619 4b95ab3d2e43eeeef6d6f42cc6445e1d4cc5e6f7
child 556622 ec8d1d3db50c85037e8077c32c8403570a5df493
child 556641 615d5ca8b00acc7160519d4529578d77a699af9a
child 556652 a61581ea53c60cd7bac3979cae19238b776b117b
child 556659 4590b7b71c6aea54aba842420eb0df57c0b6b915
child 556690 d1ea75b2b690f0ab258e0069704c3ad6e3b372e6
child 556881 4faa91fb814cc4d641d89b55ca7cd90f4e79765d
child 556979 b9475f769f87bdfd3cfc07e2023fcc42322acc06
child 557296 c6684529a1a2ad533f20e684e5f7f52cd8a22788
child 557424 8018bfbea433f51bd2fb894ee2bb55b48c42ec3e
child 557570 d89d18e5e3a8163eb92db7a1158379b8ce2a34ae
child 557571 e6c6afedd213a763e3aa4517ce2a67c628792161
child 557672 e330c88f7f378973a9b0bf1c6b7abc11aab2222a
child 559744 778fcd8adb022f4b158ddc24b722885fa8cc9e20
push id52556
push userbwerth@mozilla.com
push dateWed, 05 Apr 2017 22:48:12 +0000
reviewersmerge
milestone55.0a1
Merge inbound to central, a=merge
widget/windows/nsWindow.cpp
--- a/browser/components/sessionstore/SessionCookies.jsm
+++ b/browser/components/sessionstore/SessionCookies.jsm
@@ -127,16 +127,18 @@ var SessionCookiesInternal = {
 
   /**
    * Handles observers notifications that are sent whenever cookies are added,
    * changed, or removed. Ensures that the storage is updated accordingly.
    */
   observe(subject, topic, data) {
     switch (data) {
       case "added":
+        this._addCookie(subject);
+        break;
       case "changed":
         this._updateCookie(subject);
         break;
       case "deleted":
         this._removeCookie(subject);
         break;
       case "cleared":
         CookieStore.clear();
@@ -186,17 +188,28 @@ var SessionCookiesInternal = {
     if (entry.children) {
       for (let child of entry.children) {
         this._extractHostsFromEntry(child, hosts);
       }
     }
   },
 
   /**
-   * Updates or adds a given cookie to the store.
+   * Adds a given cookie to the store.
+   */
+  _addCookie(cookie) {
+    cookie.QueryInterface(Ci.nsICookie2);
+
+    if (cookie.isSession) {
+      CookieStore.set(cookie);
+    }
+  },
+
+  /**
+   * Updates a given cookie.
    */
   _updateCookie(cookie) {
     cookie.QueryInterface(Ci.nsICookie2);
 
     if (cookie.isSession) {
       CookieStore.set(cookie);
     } else {
       CookieStore.delete(cookie);
@@ -225,17 +238,17 @@ var SessionCookiesInternal = {
 
   /**
    * Iterates all cookies in the cookies service and puts them into the store
    * if they're session cookies.
    */
   _reloadCookies() {
     let iter = Services.cookies.enumerator;
     while (iter.hasMoreElements()) {
-      this._updateCookie(iter.getNext());
+      this._addCookie(iter.getNext());
     }
   }
 };
 
 /**
  * Generates all possible subdomains for a given host and prepends a leading
  * dot to all variants.
  *
@@ -378,17 +391,34 @@ var CookieStore = {
 
   /**
    * Removes a given cookie.
    *
    * @param cookie
    *        The nsICookie2 object to be removed from storage.
    */
   delete(cookie) {
-    this._ensureMap(cookie).delete(cookie.name);
+    // Deletion shouldn't create new maps.
+    // Bail out whenever we find that an entry or a map doesn't exist.
+    let map = this._hosts.get(cookie.host);
+    if (!map) {
+      return;
+    }
+
+    map = map.get(ChromeUtils.originAttributesToSuffix(cookie.originAttributes));
+    if (!map) {
+      return;
+    }
+
+    map = map.get(cookie.path);
+    if (!map) {
+      return;
+    }
+
+    map.delete(cookie.name);
   },
 
   /**
    * Removes all cookies.
    */
   clear() {
     this._hosts.clear();
   },
--- a/browser/locales/searchplugins/yahoo-jp.xml
+++ b/browser/locales/searchplugins/yahoo-jp.xml
@@ -2,15 +2,15 @@
    - 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/. -->
 
 <SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
 <ShortName>Yahoo! JAPAN</ShortName>
 <Description>Yahoo Search</Description>
 <InputEncoding>UTF-8</InputEncoding>
 <Image width="16" height="16">data:image/x-icon;base64,AAABAAIAEBAAAAEAIABoBAAAJgAAACAgAAABACAAqBAAAI4EAAAoAAAAEAAAACAAAAABACAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADMA/24zAP//MwD//zMA//8zAP//MwD//wAAAAAAAAAAMwD/7TMA/+MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADkI//8zAP//MwD/bwAAAAAAAAAAAAAAADEA/zQyAP8pNwD/FwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5CP//MwD//zIA/3AAAAAAAAAAAAAAAAAAAAAAMgD/wDIA/6cAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0AP8sMwD/9zMA//8zAP//NwD/FwAAAAAAAAAAAAAAADQA/3EzAP//MQD/PgAAAAAAAAAAAAAAAAAAAAA3AP8OMwD/2DMA//80AP9PMwD/WjMA//8xAP8qAAAAAAAAAAAyAP8kMwD//zMA/9EAAAAIAAAAAAAAAAAAAAADMwD/tDMA//8yAP+JAAAAAAAAAAAxAP85MwD//zQA/1gAAAAAAAAAADMA/9UzAP//MwD/qQAAAAAAAAAAMgD/kzMA//8zAP/EAAAABAAAAAAAAAAAMwD//zMA//8zAP//MwD//zMA/30xAP8+MwD/+TEA/z4yAP+sMwD//zMA//8zAP//MwD//zMA/5oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//AAD//wAA//8AAP//AADgZwAA+OMAAPjzAADwcQAA4DAAAMMYAADDAAAAA/8AAP//AAD//wAA//8AAP//AAAoAAAAIAAAAEAAAAABACAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMwD//zMA//8zAP//MwD//zMA//8zAP//MwD//zMA//8zAP//MwD//zMA//8zAP//AAAAAAAAAAAAAAAAMQD/HzQA/2MyAP+iAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAyAP+dMwD/9jMA//8zAP//MwD//zMA//8zAP//MwD//zMA//8zAP//MwD//zMA/3gAAAAAAAAAAAAAAAAzAP+/MwD//zMA//80AP9UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANAD/JzQA/4ozAP//MwD//zMA//8zAP/nMwD/NzQA/ycxAP8VAAAABAAAAAAAAAAAAAAAADMA/1AzAP//MwD/zjMA/18AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADMA//8zAP//MwD//zMA/98AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjAA/yUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMwD//zMA//8zAP//MwD/3wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADUA/yIzAP9zMwD/UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzAP//MwD//zMA//8zAP/fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANAD/njMA//8zAP/pMwD/DwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANAD/TzMA//8zAP//MwD//zMA/+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzAP9QMwD//zMA//80AP+PAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg0AP+UMwD//zMA//8zAP//MwD//jIA/1IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC4A/wszAP/2MwD//zMA//sxAP8qAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMwD/lzMA//8zAP//MwD//zMA/84zAP/5MwD/+TIA/1EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADMA/7MzAP//MwD//zQA/7wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADIA/1szAP//MwD//zMA//8zAP/fOQD/EjMA/0EzAP/tMwD//DQA/2wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMgD/ZTMA//8zAP//MwD//zIA/1IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAxAP8vMwD/9TMA//8zAP//MwD/+DEA/zQAAAAAAAAAADUA/yIzAP/UMwD//zMA/5YAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAzAP8ZMwD//TMA//8zAP//MwD/3wAAAAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALgD/FjMA/98zAP//MwD//zMA//8yAP9mAAAAAAAAAAAAAAAAAAAAADMA/w8zAP+4MwD//zMA/8MzAP8ZAAAAAAAAAAAAAAAAAAAAAAAAAAAzAP/IMwD//zMA//8zAP//MgD/fwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYzAP/DMwD//zMA//8zAP//MgD/ogAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYzAP+zMwD//zMA/+k0AP9JAAAAAAAAAAAAAAAAAAAAADMA/3kzAP//MwD//zMA//8zAP/2MAD/IAAAAAAAAAAAAAAAAAAAAAAAAAABMwD/pDMA//8zAP//MwD//zMA/9YuAP8LAAAAAAAAAAAAAAAAAAAAAAAAAAA1AP8/NAD/YjMA/5ozAP//MwD//zMA//8zAP+/MwD/gzIA/00wAP8QNQD/KzMA//8zAP//MwD//zMA//8zAP+zAAAAAAAAAAAAAAAAAAAAADMA/40zAP//MwD//zMA//8zAP/3MwD/LQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADMA/98zAP/fMwD/3zMA/98zAP/fMwD/3zMA/98zAP/fMwD/3zMA/58AAAAAMwD/3TMA//8zAP/fMwD/kTEA/0MzAP88MgD/YDMA/4IzAP+6MwD//zMA//8zAP//MwD//zMA/7oyAP9HNAD/NjAA/yUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0AP9FMgD/LgAAAAAAAAAAAAAAADMA//YzAP//MwD//zMA//8zAP//MwD//zMA//8zAP//MwD//zMA//8zAP//MwD//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/////////////////////////////////////+ABw//gAcP/+AHD//4fz//+H/H//h/w//wf8P/4D/B/+Af4f/AD+D/gYPgfwPB8H4H4PA8B8AAPA/ACAAD//nAA//////////////////////////////////////////////</Image>
-<Url type="text/html" method="GET" template="http://search.yahoo.co.jp/search" resultdomain="yahoo.co.jp">
+<Url type="text/html" method="GET" template="https://search.yahoo.co.jp/search" resultdomain="yahoo.co.jp">
   <Param name="p" value="{searchTerms}"/>
   <Param name="ei" value="UTF-8"/>
   <Param name="fr" value="mozff" />
 </Url>
-<SearchForm>http://search.yahoo.co.jp/</SearchForm>
+<SearchForm>https://search.yahoo.co.jp/</SearchForm>
 </SearchPlugin>
--- a/devtools/client/projecteditor/test/browser.ini
+++ b/devtools/client/projecteditor/test/browser.ini
@@ -5,16 +5,17 @@ support-files =
   head.js
   helper_homepage.html
   helper_edits.js
   projecteditor-test.xul
 
 [browser_projecteditor_app_options.js]
 [browser_projecteditor_confirm_unsaved.js]
 [browser_projecteditor_contextmenu_01.js]
+skip-if = asan # Bug 1083140
 [browser_projecteditor_contextmenu_02.js]
 skip-if = true # Bug 1173950
 [browser_projecteditor_delete_file.js]
 skip-if = e10s # Frequent failures in e10s - Bug 1020027
 [browser_projecteditor_rename_file_01.js]
 [browser_projecteditor_rename_file_02.js]
 [browser_projecteditor_editing_01.js]
 [browser_projecteditor_editors_image.js]
--- a/dom/filesystem/FileSystemTaskBase.cpp
+++ b/dom/filesystem/FileSystemTaskBase.cpp
@@ -79,17 +79,18 @@ DispatchToIOThread(nsIRunnable* aRunnabl
 
 // This runnable is used when an error value is set before doing any real
 // operation on the I/O thread. In this case we skip all and we directly
 // communicate the error.
 class ErrorRunnable final : public CancelableRunnable
 {
 public:
   explicit ErrorRunnable(FileSystemTaskChildBase* aTask)
-    : mTask(aTask)
+    : CancelableRunnable("ErrorRunnable")
+    , mTask(aTask)
   {
     MOZ_ASSERT(aTask);
   }
 
   NS_IMETHOD
   Run() override
   {
     MOZ_ASSERT(NS_IsMainThread());
@@ -106,22 +107,25 @@ private:
 } // anonymous namespace
 
 NS_IMPL_ISUPPORTS(FileSystemTaskChildBase, nsIIPCBackgroundChildCreateCallback)
 
 /**
  * FileSystemTaskBase class
  */
 
-FileSystemTaskChildBase::FileSystemTaskChildBase(FileSystemBase* aFileSystem)
+FileSystemTaskChildBase::FileSystemTaskChildBase(nsIGlobalObject* aGlobalObject,
+                                                 FileSystemBase* aFileSystem)
   : mErrorValue(NS_OK)
   , mFileSystem(aFileSystem)
+  , mGlobalObject(aGlobalObject)
 {
   MOZ_ASSERT(aFileSystem, "aFileSystem should not be null.");
   aFileSystem->AssertIsOnOwningThread();
+  MOZ_ASSERT(aGlobalObject);
 }
 
 FileSystemTaskChildBase::~FileSystemTaskChildBase()
 {
   mFileSystem->AssertIsOnOwningThread();
 }
 
 FileSystemBase*
@@ -155,18 +159,17 @@ FileSystemTaskChildBase::ActorFailed()
 }
 
 void
 FileSystemTaskChildBase::ActorCreated(mozilla::ipc::PBackgroundChild* aActor)
 {
   if (HasError()) {
     // In this case we don't want to use IPC at all.
     RefPtr<ErrorRunnable> runnable = new ErrorRunnable(this);
-    DebugOnly<nsresult> rv = NS_DispatchToCurrentThread(runnable);
-    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToCurrentThread failed");
+    FileSystemUtils::DispatchRunnable(mGlobalObject, runnable.forget());
     return;
   }
 
   if (mFileSystem->IsShutdown()) {
     return;
   }
 
   nsAutoString serialization;
@@ -179,21 +182,24 @@ FileSystemTaskChildBase::ActorCreated(mo
     return;
   }
 
   // Retain a reference so the task object isn't deleted without IPDL's
   // knowledge. The reference will be released by
   // mozilla::ipc::BackgroundChildImpl::DeallocPFileSystemRequestChild.
   NS_ADDREF_THIS();
 
-  mozilla::ipc::PBackgroundChild* actor =
-    mozilla::ipc::BackgroundChild::GetForCurrentThread();
-  MOZ_ASSERT(actor);
+  if (NS_IsMainThread()) {
+    nsIEventTarget* target = mGlobalObject->EventTargetFor(TaskCategory::Other);
+    MOZ_ASSERT(target);
 
-  actor->SendPFileSystemRequestConstructor(this, params);
+    aActor->SetEventTargetForActor(this, target);
+  }
+
+  aActor->SendPFileSystemRequestConstructor(this, params);
 }
 
 void
 FileSystemTaskChildBase::SetRequestResult(const FileSystemResponseValue& aValue)
 {
   mFileSystem->AssertIsOnOwningThread();
 
   if (aValue.type() == FileSystemResponseValue::TFileSystemErrorResponse) {
--- a/dom/filesystem/FileSystemTaskBase.h
+++ b/dom/filesystem/FileSystemTaskBase.h
@@ -133,17 +133,18 @@ public:
 
   bool
   HasError() const { return NS_FAILED(mErrorValue); }
 
 protected:
   /*
    * To create a task to handle the page content request.
    */
-  explicit FileSystemTaskChildBase(FileSystemBase* aFileSystem);
+  FileSystemTaskChildBase(nsIGlobalObject* aGlobalObject,
+                          FileSystemBase* aFileSystem);
 
   virtual
   ~FileSystemTaskChildBase();
 
   /*
    * Wrap the task parameter to FileSystemParams for sending it through IPC.
    * It will be called when we need to forward a task from the child process to
    * the parent process. This method runs in the owning thread.
@@ -164,16 +165,17 @@ protected:
                           ErrorResult& aRv) = 0;
 
   // Overrides PFileSystemRequestChild
   virtual mozilla::ipc::IPCResult
   Recv__delete__(const FileSystemResponseValue& value) override;
 
   nsresult mErrorValue;
   RefPtr<FileSystemBase> mFileSystem;
+  nsCOMPtr<nsIGlobalObject> mGlobalObject;
 
 private:
 
   /*
    * Unwrap the IPC message to get the task result.
    * It will be called when the task is completed and an IPC message is received
    * in the content process and we want to get the task result. This runs on the
    * owning thread.
@@ -182,16 +184,20 @@ private:
   SetRequestResult(const FileSystemResponseValue& aValue);
 };
 
 // This class is the 'alter ego' of FileSystemTaskChildBase in the PBackground
 // world.
 class FileSystemTaskParentBase : public Runnable
 {
 public:
+  FileSystemTaskParentBase()
+    : Runnable("FileSystemTaskParentBase")
+  {}
+
   /*
    * Start the task. This must be called from the PBackground thread only.
    */
   void
   Start();
 
   /*
    * The error codes are defined in xpcom/base/ErrorList.h and their
--- a/dom/filesystem/FileSystemUtils.cpp
+++ b/dom/filesystem/FileSystemUtils.cpp
@@ -66,10 +66,33 @@ FileSystemUtils::IsValidRelativeDOMPath(
     }
 
     aParts.AppendElement(pathComponent);
   }
 
   return true;
 }
 
+/* static */ nsresult
+FileSystemUtils::DispatchRunnable(nsIGlobalObject* aGlobal,
+                                  already_AddRefed<nsIRunnable>&& aRunnable)
+{
+  nsCOMPtr<nsIRunnable> runnable = aRunnable;
+
+  nsCOMPtr<nsIEventTarget> target;
+  if (!aGlobal) {
+    target = SystemGroup::EventTargetFor(TaskCategory::Other);
+  } else {
+    target = aGlobal->EventTargetFor(TaskCategory::Other);
+  }
+
+  MOZ_ASSERT(target);
+
+  nsresult rv = target->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/filesystem/FileSystemUtils.h
+++ b/dom/filesystem/FileSystemUtils.h
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_FileSystemUtils_h
 #define mozilla_dom_FileSystemUtils_h
 
 class nsIFile;
+class nsIRunnable;
 
 namespace mozilla {
 namespace dom {
 
 #define FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL "/"
 #define FILESYSTEM_DOM_PATH_SEPARATOR_CHAR '/'
 
 /*
@@ -31,14 +32,22 @@ public:
 
   /**
    * Return true if this is valid DOMPath. It also splits the path in
    * subdirectories and stores them in aParts.
    */
   static bool
   IsValidRelativeDOMPath(const nsAString& aPath,
                          nsTArray<nsString>& aParts);
+
+  /**
+   * Helper method. If aGlobal is null, the SystemGroup EventTarget will be
+   * used.
+   */
+  static nsresult
+  DispatchRunnable(nsIGlobalObject* aGlobal,
+                   already_AddRefed<nsIRunnable>&& aRunnable);
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_FileSystemUtils_h
--- a/dom/filesystem/GetDirectoryListingTask.cpp
+++ b/dom/filesystem/GetDirectoryListingTask.cpp
@@ -33,42 +33,43 @@ GetDirectoryListingTaskChild::Create(Fil
                                      nsIFile* aTargetPath,
                                      const nsAString& aFilters,
                                      ErrorResult& aRv)
 {
   MOZ_ASSERT(aFileSystem);
   MOZ_ASSERT(aDirectory);
   aFileSystem->AssertIsOnOwningThread();
 
-  RefPtr<GetDirectoryListingTaskChild> task =
-    new GetDirectoryListingTaskChild(aFileSystem, aDirectory, aTargetPath,
-                                     aFilters);
-
-  // aTargetPath can be null. In this case SetError will be called.
-
   nsCOMPtr<nsIGlobalObject> globalObject =
     do_QueryInterface(aFileSystem->GetParentObject());
   if (NS_WARN_IF(!globalObject)) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
+  RefPtr<GetDirectoryListingTaskChild> task =
+    new GetDirectoryListingTaskChild(globalObject, aFileSystem, aDirectory,
+                                     aTargetPath, aFilters);
+
+  // aTargetPath can be null. In this case SetError will be called.
+
   task->mPromise = Promise::Create(globalObject, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   return task.forget();
 }
 
-GetDirectoryListingTaskChild::GetDirectoryListingTaskChild(FileSystemBase* aFileSystem,
+GetDirectoryListingTaskChild::GetDirectoryListingTaskChild(nsIGlobalObject* aGlobalObject,
+                                                           FileSystemBase* aFileSystem,
                                                            Directory* aDirectory,
                                                            nsIFile* aTargetPath,
                                                            const nsAString& aFilters)
-  : FileSystemTaskChildBase(aFileSystem)
+  : FileSystemTaskChildBase(aGlobalObject, aFileSystem)
   , mDirectory(aDirectory)
   , mTargetPath(aTargetPath)
   , mFilters(aFilters)
 {
   MOZ_ASSERT(aFileSystem);
   aFileSystem->AssertIsOnOwningThread();
 }
 
--- a/dom/filesystem/GetDirectoryListingTask.h
+++ b/dom/filesystem/GetDirectoryListingTask.h
@@ -31,17 +31,18 @@ public:
   virtual
   ~GetDirectoryListingTaskChild();
 
   already_AddRefed<Promise>
   GetPromise();
 
 private:
   // If aDirectoryOnly is set, we should ensure that the target is a directory.
-  GetDirectoryListingTaskChild(FileSystemBase* aFileSystem,
+  GetDirectoryListingTaskChild(nsIGlobalObject* aGlobalObject,
+                               FileSystemBase* aFileSystem,
                                Directory* aDirectory,
                                nsIFile* aTargetPath,
                                const nsAString& aFilters);
 
   virtual FileSystemParams
   GetRequestParams(const nsString& aSerializedDOMPath,
                    ErrorResult& aRv) const override;
 
--- a/dom/filesystem/GetFileOrDirectoryTask.cpp
+++ b/dom/filesystem/GetFileOrDirectoryTask.cpp
@@ -27,39 +27,40 @@ namespace dom {
 /* static */ already_AddRefed<GetFileOrDirectoryTaskChild>
 GetFileOrDirectoryTaskChild::Create(FileSystemBase* aFileSystem,
                                     nsIFile* aTargetPath,
                                     ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   MOZ_ASSERT(aFileSystem);
 
-  RefPtr<GetFileOrDirectoryTaskChild> task =
-    new GetFileOrDirectoryTaskChild(aFileSystem, aTargetPath);
-
-  // aTargetPath can be null. In this case SetError will be called.
-
   nsCOMPtr<nsIGlobalObject> globalObject =
     do_QueryInterface(aFileSystem->GetParentObject());
   if (NS_WARN_IF(!globalObject)) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
+  RefPtr<GetFileOrDirectoryTaskChild> task =
+    new GetFileOrDirectoryTaskChild(globalObject, aFileSystem, aTargetPath);
+
+  // aTargetPath can be null. In this case SetError will be called.
+
   task->mPromise = Promise::Create(globalObject, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   return task.forget();
 }
 
-GetFileOrDirectoryTaskChild::GetFileOrDirectoryTaskChild(FileSystemBase* aFileSystem,
+GetFileOrDirectoryTaskChild::GetFileOrDirectoryTaskChild(nsIGlobalObject* aGlobalObject,
+                                                         FileSystemBase* aFileSystem,
                                                          nsIFile* aTargetPath)
-  : FileSystemTaskChildBase(aFileSystem)
+  : FileSystemTaskChildBase(aGlobalObject, aFileSystem)
   , mTargetPath(aTargetPath)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   MOZ_ASSERT(aFileSystem);
 }
 
 GetFileOrDirectoryTaskChild::~GetFileOrDirectoryTaskChild()
 {
--- a/dom/filesystem/GetFileOrDirectoryTask.h
+++ b/dom/filesystem/GetFileOrDirectoryTask.h
@@ -38,17 +38,18 @@ protected:
 
   virtual void
   SetSuccessRequestResult(const FileSystemResponseValue& aValue,
                           ErrorResult& aRv) override;
   virtual void
   HandlerCallback() override;
 
 private:
-  GetFileOrDirectoryTaskChild(FileSystemBase* aFileSystem,
+  GetFileOrDirectoryTaskChild(nsIGlobalObject* aGlobalObject,
+                              FileSystemBase* aFileSystem,
                               nsIFile* aTargetPath);
 
   RefPtr<Promise> mPromise;
   nsCOMPtr<nsIFile> mTargetPath;
 
   RefPtr<File> mResultFile;
   RefPtr<Directory> mResultDirectory;
 };
--- a/dom/filesystem/GetFilesHelper.cpp
+++ b/dom/filesystem/GetFilesHelper.cpp
@@ -3,42 +3,47 @@
 /* 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 "GetFilesHelper.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/FileBlobImpl.h"
+#include "FileSystemUtils.h"
 #include "nsProxyRelease.h"
 
 namespace mozilla {
 namespace dom {
 
 namespace {
 
 // This class is used in the DTOR of GetFilesHelper to release resources in the
 // correct thread.
 class ReleaseRunnable final : public Runnable
 {
 public:
+  ReleaseRunnable()
+    : Runnable("ReleaseRunnable")
+  {}
+
   static void
   MaybeReleaseOnMainThread(nsTArray<RefPtr<Promise>>& aPromises,
                            nsTArray<RefPtr<GetFilesCallback>>& aCallbacks,
                            Sequence<RefPtr<File>>& aFiles,
                            already_AddRefed<nsIGlobalObject> aGlobal)
   {
     nsCOMPtr<nsIGlobalObject> global(aGlobal);
     if (NS_IsMainThread()) {
       return;
     }
 
     RefPtr<ReleaseRunnable> runnable =
       new ReleaseRunnable(aPromises, aCallbacks, aFiles, global.forget());
-    NS_DispatchToMainThread(runnable);
+    FileSystemUtils::DispatchRunnable(nullptr, runnable.forget());
   }
 
   NS_IMETHOD
   Run() override
   {
     MOZ_ASSERT(NS_IsMainThread());
 
     mPromises.Clear();
@@ -125,17 +130,18 @@ GetFilesHelper::Create(nsIGlobalObject* 
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   return helper.forget();
 }
 
 GetFilesHelper::GetFilesHelper(nsIGlobalObject* aGlobal, bool aRecursiveFlag)
-  : GetFilesHelperBase(aRecursiveFlag)
+  : Runnable("GetFilesHelper")
+  , GetFilesHelperBase(aRecursiveFlag)
   , mGlobal(aGlobal)
   , mListingCompleted(false)
   , mErrorResult(NS_OK)
   , mMutex("GetFilesHelper::mMutex")
   , mCanceled(false)
 {
 }
 
@@ -222,17 +228,18 @@ GetFilesHelper::Run()
     RunIO();
 
     // If this operation has been canceled, we don't have to go back to
     // main-thread.
     if (IsCanceled()) {
       return NS_OK;
     }
 
-    return NS_DispatchToMainThread(this);
+    RefPtr<Runnable> runnable = this;
+    return FileSystemUtils::DispatchRunnable(nullptr, runnable.forget());
   }
 
   // We are here, but we should not do anything on this thread because, in the
   // meantime, the operation has been canceled.
   if (IsCanceled()) {
     return NS_OK;
   }
 
--- a/dom/filesystem/GetFilesTask.cpp
+++ b/dom/filesystem/GetFilesTask.cpp
@@ -39,34 +39,35 @@ GetFilesTaskChild::Create(FileSystemBase
   nsCOMPtr<nsIGlobalObject> globalObject =
     do_QueryInterface(aFileSystem->GetParentObject());
   if (NS_WARN_IF(!globalObject)) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   RefPtr<GetFilesTaskChild> task =
-    new GetFilesTaskChild(aFileSystem, aDirectory, aTargetPath,
+    new GetFilesTaskChild(globalObject, aFileSystem, aDirectory, aTargetPath,
                           aRecursiveFlag);
 
   // aTargetPath can be null. In this case SetError will be called.
 
   task->mPromise = Promise::Create(globalObject, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   return task.forget();
 }
 
-GetFilesTaskChild::GetFilesTaskChild(FileSystemBase* aFileSystem,
+GetFilesTaskChild::GetFilesTaskChild(nsIGlobalObject *aGlobalObject,
+                                     FileSystemBase* aFileSystem,
                                      Directory* aDirectory,
                                      nsIFile* aTargetPath,
                                      bool aRecursiveFlag)
-  : FileSystemTaskChildBase(aFileSystem)
+  : FileSystemTaskChildBase(aGlobalObject, aFileSystem)
   , mDirectory(aDirectory)
   , mTargetPath(aTargetPath)
   , mRecursiveFlag(aRecursiveFlag)
 {
   MOZ_ASSERT(aFileSystem);
   MOZ_ASSERT(aDirectory);
   aFileSystem->AssertIsOnOwningThread();
 }
--- a/dom/filesystem/GetFilesTask.h
+++ b/dom/filesystem/GetFilesTask.h
@@ -31,17 +31,18 @@ public:
   virtual
   ~GetFilesTaskChild();
 
   already_AddRefed<Promise>
   GetPromise();
 
 private:
   // If aDirectoryOnly is set, we should ensure that the target is a directory.
-  GetFilesTaskChild(FileSystemBase* aFileSystem,
+  GetFilesTaskChild(nsIGlobalObject* aGlobalObject,
+                    FileSystemBase* aFileSystem,
                     Directory* aDirectory,
                     nsIFile* aTargetPath,
                     bool aRecursiveFlag);
 
   virtual FileSystemParams
   GetRequestParams(const nsString& aSerializedDOMPath,
                    ErrorResult& aRv) const override;
 
--- a/dom/filesystem/compat/CallbackRunnables.cpp
+++ b/dom/filesystem/compat/CallbackRunnables.cpp
@@ -21,34 +21,36 @@
 
 #include "../GetFileOrDirectoryTask.h"
 
 namespace mozilla {
 namespace dom {
 
 EntryCallbackRunnable::EntryCallbackRunnable(FileSystemEntryCallback* aCallback,
                                              FileSystemEntry* aEntry)
-  : mCallback(aCallback)
+  : Runnable("EntryCallbackRunnable")
+  , mCallback(aCallback)
   , mEntry(aEntry)
 {
   MOZ_ASSERT(aCallback);
   MOZ_ASSERT(aEntry);
 }
 
 NS_IMETHODIMP
 EntryCallbackRunnable::Run()
 {
   mCallback->HandleEvent(*mEntry);
   return NS_OK;
 }
 
 ErrorCallbackRunnable::ErrorCallbackRunnable(nsIGlobalObject* aGlobalObject,
                                              ErrorCallback* aCallback,
                                              nsresult aError)
-  : mGlobal(aGlobalObject)
+  : Runnable("ErrorCallbackRunnable")
+  , mGlobal(aGlobalObject)
   , mCallback(aCallback)
   , mError(aError)
 {
   MOZ_ASSERT(aGlobalObject);
   MOZ_ASSERT(aCallback);
   MOZ_ASSERT(NS_FAILED(aError));
 }
 
@@ -56,17 +58,18 @@ NS_IMETHODIMP
 ErrorCallbackRunnable::Run()
 {
   RefPtr<DOMException> exception = DOMException::Create(mError);
   mCallback->HandleEvent(*exception);
   return NS_OK;
 }
 
 EmptyEntriesCallbackRunnable::EmptyEntriesCallbackRunnable(FileSystemEntriesCallback* aCallback)
-  : mCallback(aCallback)
+  : Runnable("EmptyEntriesCallbackRunnable")
+  , mCallback(aCallback)
 {
   MOZ_ASSERT(aCallback);
 }
 
 NS_IMETHODIMP
 EmptyEntriesCallbackRunnable::Run()
 {
   Sequence<OwningNonNull<FileSystemEntry>> sequence;
@@ -253,49 +256,51 @@ void
 GetEntryHelper::Error(nsresult aError)
 {
   MOZ_ASSERT(NS_FAILED(aError));
 
   if (mErrorCallback) {
     RefPtr<ErrorCallbackRunnable> runnable =
       new ErrorCallbackRunnable(mParentEntry->GetParentObject(),
                                 mErrorCallback, aError);
-    DebugOnly<nsresult> rv = NS_DispatchToMainThread(runnable);
-    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToMainThread failed");
+
+    FileSystemUtils::DispatchRunnable(mParentEntry->GetParentObject(),
+                                      runnable.forget());
   }
 }
 
 NS_IMPL_ISUPPORTS0(GetEntryHelper);
 
 /* static */ void
-FileSystemEntryCallbackHelper::Call(const Optional<OwningNonNull<FileSystemEntryCallback>>& aEntryCallback,
+FileSystemEntryCallbackHelper::Call(nsIGlobalObject* aGlobalObject,
+                                    const Optional<OwningNonNull<FileSystemEntryCallback>>& aEntryCallback,
                                     FileSystemEntry* aEntry)
 {
+  MOZ_ASSERT(aGlobalObject);
   MOZ_ASSERT(aEntry);
 
   if (aEntryCallback.WasPassed()) {
     RefPtr<EntryCallbackRunnable> runnable =
       new EntryCallbackRunnable(&aEntryCallback.Value(), aEntry);
 
-    DebugOnly<nsresult> rv = NS_DispatchToMainThread(runnable);
-    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToMainThread failed");
+    FileSystemUtils::DispatchRunnable(aGlobalObject, runnable.forget());
   }
 }
 
 /* static */ void
 ErrorCallbackHelper::Call(nsIGlobalObject* aGlobal,
                           const Optional<OwningNonNull<ErrorCallback>>& aErrorCallback,
                           nsresult aError)
 {
   MOZ_ASSERT(aGlobal);
   MOZ_ASSERT(NS_FAILED(aError));
 
   if (aErrorCallback.WasPassed()) {
     RefPtr<ErrorCallbackRunnable> runnable =
       new ErrorCallbackRunnable(aGlobal, &aErrorCallback.Value(), aError);
-    DebugOnly<nsresult> rv = NS_DispatchToMainThread(runnable);
-    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToMainThread failed");
+
+    FileSystemUtils::DispatchRunnable(aGlobal, runnable.forget());
   }
 }
 
 } // dom namespace
 } // mozilla namespace
 
--- a/dom/filesystem/compat/CallbackRunnables.h
+++ b/dom/filesystem/compat/CallbackRunnables.h
@@ -104,17 +104,18 @@ private:
 
   FileSystemDirectoryEntry::GetInternalType mType;
 };
 
 class FileSystemEntryCallbackHelper
 {
 public:
   static void
-  Call(const Optional<OwningNonNull<FileSystemEntryCallback>>& aEntryCallback,
+  Call(nsIGlobalObject* aGlobalObject,
+       const Optional<OwningNonNull<FileSystemEntryCallback>>& aEntryCallback,
        FileSystemEntry* aEntry);
 };
 
 class ErrorCallbackHelper
 {
 public:
   static void
   Call(nsIGlobalObject* aGlobal,
--- a/dom/filesystem/compat/FileSystemDirectoryReader.cpp
+++ b/dom/filesystem/compat/FileSystemDirectoryReader.cpp
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "FileSystemDirectoryReader.h"
 #include "CallbackRunnables.h"
 #include "FileSystemFileEntry.h"
 #include "mozilla/dom/FileBinding.h"
+#include "mozilla/dom/FileSystemUtils.h"
 #include "mozilla/dom/Directory.h"
 #include "mozilla/dom/DirectoryBinding.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PromiseNativeHandler.h"
 
 namespace mozilla {
 namespace dom {
 
@@ -95,18 +96,19 @@ public:
   virtual void
   RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
   {
     if (mErrorCallback) {
       RefPtr<ErrorCallbackRunnable> runnable =
         new ErrorCallbackRunnable(mParentEntry->GetParentObject(),
                                   mErrorCallback,
                                   NS_ERROR_DOM_INVALID_STATE_ERR);
-      DebugOnly<nsresult> rv = NS_DispatchToMainThread(runnable);
-      NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToMainThread failed");
+
+      FileSystemUtils::DispatchRunnable(mParentEntry->GetParentObject(),
+                                        runnable.forget());
     }
   }
 
 private:
   ~PromiseHandler() {}
 
   RefPtr<FileSystemDirectoryEntry> mParentEntry;
   RefPtr<FileSystem> mFileSystem;
@@ -156,18 +158,18 @@ FileSystemDirectoryReader::ReadEntries(F
                                        const Optional<OwningNonNull<ErrorCallback>>& aErrorCallback,
                                        ErrorResult& aRv)
 {
   MOZ_ASSERT(mDirectory);
 
   if (mAlreadyRead) {
     RefPtr<EmptyEntriesCallbackRunnable> runnable =
       new EmptyEntriesCallbackRunnable(&aSuccessCallback);
-    aRv = NS_DispatchToMainThread(runnable);
-    NS_WARNING_ASSERTION(!aRv.Failed(), "NS_DispatchToMainThread failed");
+
+    FileSystemUtils::DispatchRunnable(GetParentObject(), runnable.forget());
     return;
   }
 
   // This object can be used only once.
   mAlreadyRead = true;
 
   ErrorResult rv;
   RefPtr<Promise> promise = mDirectory->GetFilesAndDirectories(rv);
--- a/dom/filesystem/compat/FileSystemEntry.cpp
+++ b/dom/filesystem/compat/FileSystemEntry.cpp
@@ -73,17 +73,18 @@ void
 FileSystemEntry::GetParent(const Optional<OwningNonNull<FileSystemEntryCallback>>& aSuccessCallback,
                            const Optional<OwningNonNull<ErrorCallback>>& aErrorCallback)
 {
   if (!aSuccessCallback.WasPassed() && !aErrorCallback.WasPassed()) {
     return;
   }
 
   if (mParentEntry) {
-    FileSystemEntryCallbackHelper::Call(aSuccessCallback, mParentEntry);
+    FileSystemEntryCallbackHelper::Call(GetParentObject(), aSuccessCallback,
+                                        mParentEntry);
     return;
   }
 
-  FileSystemEntryCallbackHelper::Call(aSuccessCallback, this);
+  FileSystemEntryCallbackHelper::Call(GetParentObject(), aSuccessCallback, this);
 }
 
 } // dom namespace
 } // mozilla namespace
--- a/dom/filesystem/compat/FileSystemFileEntry.cpp
+++ b/dom/filesystem/compat/FileSystemFileEntry.cpp
@@ -2,30 +2,32 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "FileSystemFileEntry.h"
 #include "CallbackRunnables.h"
 #include "mozilla/dom/File.h"
+#include "mozilla/dom/FileSystemUtils.h"
 #include "mozilla/dom/MultipartBlobImpl.h"
 #include "mozilla/dom/FileSystemFileEntryBinding.h"
 
 namespace mozilla {
 namespace dom {
 
 namespace {
 
 class FileCallbackRunnable final : public Runnable
 {
 public:
   FileCallbackRunnable(FileCallback* aCallback, ErrorCallback* aErrorCallback,
                        File* aFile)
-    : mCallback(aCallback)
+    : Runnable("FileCallbackRunnable")
+    , mCallback(aCallback)
     , mErrorCallback(aErrorCallback)
     , mFile(aFile)
   {
     MOZ_ASSERT(aCallback);
     MOZ_ASSERT(aFile);
   }
 
   NS_IMETHOD
@@ -125,14 +127,14 @@ void
 FileSystemFileEntry::GetFile(FileCallback& aSuccessCallback,
                              const Optional<OwningNonNull<ErrorCallback>>& aErrorCallback) const
 {
   RefPtr<FileCallbackRunnable> runnable =
     new FileCallbackRunnable(&aSuccessCallback,
                              aErrorCallback.WasPassed()
                                ? &aErrorCallback.Value() : nullptr,
                              mFile);
-  DebugOnly<nsresult> rv = NS_DispatchToMainThread(runnable);
-  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToMainThread failed");
+
+  FileSystemUtils::DispatchRunnable(GetParentObject(), runnable.forget());
 }
 
 } // dom namespace
 } // mozilla namespace
--- a/dom/filesystem/compat/FileSystemRootDirectoryEntry.cpp
+++ b/dom/filesystem/compat/FileSystemRootDirectoryEntry.cpp
@@ -110,18 +110,19 @@ FileSystemRootDirectoryEntry::GetInterna
       ErrorCallbackHelper::Call(GetParentObject(), aErrorCallback,
                                 NS_ERROR_DOM_TYPE_MISMATCH_ERR);
       return;
     }
 
     if (aSuccessCallback.WasPassed()) {
       RefPtr<EntryCallbackRunnable> runnable =
         new EntryCallbackRunnable(&aSuccessCallback.Value(), entry);
-      DebugOnly<nsresult> rv = NS_DispatchToMainThread(runnable);
-      NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToMainThread failed");
+
+      FileSystemUtils::DispatchRunnable(GetParentObject(), runnable.forget());
+
     }
     return;
   }
 
   // Subdirectories, but this is a file.
   if (entry->IsFile()) {
     ErrorCallbackHelper::Call(GetParentObject(), aErrorCallback,
                               NS_ERROR_DOM_NOT_FOUND_ERR);
--- a/dom/filesystem/compat/FileSystemRootDirectoryReader.cpp
+++ b/dom/filesystem/compat/FileSystemRootDirectoryReader.cpp
@@ -2,28 +2,30 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "FileSystemRootDirectoryReader.h"
 #include "CallbackRunnables.h"
 #include "nsIGlobalObject.h"
+#include "mozilla/dom/FileSystemUtils.h"
 
 namespace mozilla {
 namespace dom {
 
 namespace {
 
 class EntriesCallbackRunnable final : public Runnable
 {
 public:
   EntriesCallbackRunnable(FileSystemEntriesCallback* aCallback,
                           const Sequence<RefPtr<FileSystemEntry>>& aEntries)
-    : mCallback(aCallback)
+    : Runnable("EntriesCallbackRunnable")
+    , mCallback(aCallback)
     , mEntries(aEntries)
   {
     MOZ_ASSERT(aCallback);
   }
 
   NS_IMETHOD
   Run() override
   {
@@ -73,24 +75,24 @@ FileSystemRootDirectoryReader::~FileSyst
 void
 FileSystemRootDirectoryReader::ReadEntries(FileSystemEntriesCallback& aSuccessCallback,
                                            const Optional<OwningNonNull<ErrorCallback>>& aErrorCallback,
                                            ErrorResult& aRv)
 {
   if (mAlreadyRead) {
     RefPtr<EmptyEntriesCallbackRunnable> runnable =
       new EmptyEntriesCallbackRunnable(&aSuccessCallback);
-    aRv = NS_DispatchToMainThread(runnable);
-    NS_WARNING_ASSERTION(!aRv.Failed(), "NS_DispatchToMainThread failed");
+
+    aRv = FileSystemUtils::DispatchRunnable(GetParentObject(), runnable.forget());
     return;
   }
 
   // This object can be used only once.
   mAlreadyRead = true;
 
   RefPtr<EntriesCallbackRunnable> runnable =
     new EntriesCallbackRunnable(&aSuccessCallback, mEntries);
-  aRv = NS_DispatchToMainThread(runnable);
-  NS_WARNING_ASSERTION(!aRv.Failed(), "NS_DispatchToMainThread failed");
+
+  aRv = FileSystemUtils::DispatchRunnable(GetParentObject(), runnable.forget());
 }
 
 } // dom namespace
 } // mozilla namespace
--- a/image/decoders/nsPNGDecoder.cpp
+++ b/image/decoders/nsPNGDecoder.cpp
@@ -938,33 +938,59 @@ nsPNGDecoder::DoYield(png_structp aPNGSt
 
   MOZ_ASSERT(pendingBytes < mLastChunkLength);
   size_t consumedBytes = mLastChunkLength - min(pendingBytes, mLastChunkLength);
 
   mNextTransition =
     Transition::ContinueUnbufferedAfterYield(State::PNG_DATA, consumedBytes);
 }
 
+nsresult
+nsPNGDecoder::FinishInternal()
+{
+  // We shouldn't be called in error cases.
+  MOZ_ASSERT(!HasError(), "Can't call FinishInternal on error!");
+
+  if (IsMetadataDecode()) {
+    return NS_OK;
+  }
+
+  int32_t loop_count = 0;
+#ifdef PNG_APNG_SUPPORTED
+  if (png_get_valid(mPNG, mInfo, PNG_INFO_acTL)) {
+    int32_t num_plays = png_get_num_plays(mPNG, mInfo);
+    loop_count = num_plays - 1;
+  }
+#endif
+
+  if (InFrame()) {
+    EndImageFrame();
+  }
+  PostDecodeDone(loop_count);
+
+  return NS_OK;
+}
+
+
 #ifdef PNG_APNG_SUPPORTED
 // got the header of a new frame that's coming
 void
 nsPNGDecoder::frame_info_callback(png_structp png_ptr, png_uint_32 frame_num)
 {
   nsPNGDecoder* decoder =
                static_cast<nsPNGDecoder*>(png_get_progressive_ptr(png_ptr));
 
   // old frame is done
   decoder->EndImageFrame();
 
   const bool previousFrameWasHidden = decoder->mFrameIsHidden;
 
   if (!previousFrameWasHidden && decoder->IsFirstFrameDecode()) {
     // We're about to get a second non-hidden frame, but we only want the first.
     // Stop decoding now. (And avoid allocating the unnecessary buffers below.)
-    decoder->PostDecodeDone();
     return decoder->DoTerminate(png_ptr, TerminalState::SUCCESS);
   }
 
   // Only the first frame can be hidden, so unhide unconditionally here.
   decoder->mFrameIsHidden = false;
 
   // Save the information necessary to create the frame; we'll actually create
   // it when we return from the yield.
@@ -1020,27 +1046,16 @@ nsPNGDecoder::end_callback(png_structp p
    */
 
   nsPNGDecoder* decoder =
                static_cast<nsPNGDecoder*>(png_get_progressive_ptr(png_ptr));
 
   // We shouldn't get here if we've hit an error
   MOZ_ASSERT(!decoder->HasError(), "Finishing up PNG but hit error!");
 
-  int32_t loop_count = 0;
-#ifdef PNG_APNG_SUPPORTED
-  if (png_get_valid(png_ptr, info_ptr, PNG_INFO_acTL)) {
-    int32_t num_plays = png_get_num_plays(png_ptr, info_ptr);
-    loop_count = num_plays - 1;
-  }
-#endif
-
-  // Send final notifications.
-  decoder->EndImageFrame();
-  decoder->PostDecodeDone(loop_count);
   return decoder->DoTerminate(png_ptr, TerminalState::SUCCESS);
 }
 
 
 void
 nsPNGDecoder::error_callback(png_structp png_ptr, png_const_charp error_msg)
 {
   MOZ_LOG(sPNGLog, LogLevel::Error, ("libpng error: %s\n", error_msg));
--- a/image/decoders/nsPNGDecoder.h
+++ b/image/decoders/nsPNGDecoder.h
@@ -22,16 +22,17 @@ class nsPNGDecoder : public Decoder
 public:
   virtual ~nsPNGDecoder();
 
   /// @return true if this PNG is a valid ICO resource.
   bool IsValidICO() const;
 
 protected:
   nsresult InitInternal() override;
+  nsresult FinishInternal() override;
   LexerResult DoDecode(SourceBufferIterator& aIterator,
                        IResumable* aOnResume) override;
 
   Maybe<Telemetry::HistogramID> SpeedHistogram() const override;
 
 private:
   friend class DecoderFactory;
 
--- a/image/test/mochitest/test_discardAnimatedImage.html
+++ b/image/test/mochitest/test_discardAnimatedImage.html
@@ -1,29 +1,30 @@
 <!DOCTYPE HTML>
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=686905
 -->
 <head>
   <title>Test that animated images can be discarded</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/WindowSnapshot.js"></script>
   <script type="text/javascript" src="imgutils.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=686905">Mozilla Bug 686905</a>
 <p id="display"></p>
 <div id="content">
   <div id="container">
     <canvas id="canvas" width="100" height="100"></canvas>
     <img id="infinitepng" src="infinite-apng.png">
     <img id="infinitegif" src="animated1.gif">
     <img id="finitepng" src="restore-previous.png">
-    <img id="finitegif" src="restore-previous.gif">
+    <img id="finitegif" src="animated-gif.gif">
   </div>
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 /** Test for Bug 686905. **/
 SimpleTest.waitForExplicitFinish();
 
@@ -31,64 +32,126 @@ var gNumDiscards = 0;
 
 window.onload = function() {
   // Enable discarding for the test.
   SpecialPowers.pushPrefEnv({
     'set':[['image.mem.discardable',true]]
   }, runTest);
 }
 
-var gImgs = ['finitepng', 'finitegif', 'infinitepng', 'infinitegif'];
+var gImgs = ['infinitepng', 'infinitegif', 'finitepng', 'finitegif'];
+// If we are currently counting frame updates.
+var gCountingFrameUpdates = false;
+// The number of frame update notifications for the images in gImgs that happen
+// after discarding. (The last two images are finite looping so we don't expect
+// them to get incremented but it's possible if they don't finish their
+// animation before we discard them.)
+var gNumFrameUpdates = [0, 0, 0, 0];
+// The last snapshot of the image. Used to check that the image actually changes.
+var gLastSnapShot = [null, null, null, null];
+// Number of observed changes in the snapshot.
+var gNumSnapShotChanges = [0, 0, 0, 0];
+
+// 2 would probably be a good enough test, we arbitrarily choose 4.
+var kNumFrameUpdatesToExpect = 4;
 
 function runTest() {
   var animatedDiscardable =
     SpecialPowers.getBoolPref('image.mem.animated.discardable');
   if (!animatedDiscardable) {
-    ok(true, "discarding of animated images is disabled, nothing to test")
+    ok(true, "discarding of animated images is disabled, nothing to test");
     SimpleTest.finish();
     return;
   }
 
+  setTimeout(step2, 0);
+}
+
+function step2() {
   // Draw the images to canvas to force them to be decoded.
   for (var i = 0; i < gImgs.length; i++) {
     drawCanvas(document.getElementById(gImgs[i]));
   }
 
   for (var i = 0; i < gImgs.length; i++) {
-    addCallback(document.getElementById(gImgs[i]));
+    addCallbacks(document.getElementById(gImgs[i]), i);
   }
 
+  setTimeout(step3, 0);
+}
+
+function step3() {
   document.getElementById("container").style.display = "none";
+  document.documentElement.offsetLeft; // force that style to take effect
 
   for (var i = 0; i < gImgs.length; i++) {
     requestDiscard(document.getElementById(gImgs[i]));
   }
+
+  // the discard observers will call step4
 }
 
-function step2() {
+function step4() {
+  gCountingFrameUpdates = true;
   document.getElementById("container").style.display = "";
 
   // Draw the images to canvas to force them to be decoded again.
   for (var i = 0; i < gImgs.length; i++) {
     drawCanvas(document.getElementById(gImgs[i]));
   }
-
-  SimpleTest.finish();
 }
 
-function addCallback(anImage) {
+function checkIfFinished() {
+  if ((gNumFrameUpdates[0] >= kNumFrameUpdatesToExpect) &&
+      (gNumFrameUpdates[1] >= kNumFrameUpdatesToExpect) &&
+      (gNumSnapShotChanges[0] >= kNumFrameUpdatesToExpect) &&
+      (gNumSnapShotChanges[1] >= kNumFrameUpdatesToExpect)) {
+    ok(true, "got expected frame updates");
+    SimpleTest.finish();
+  }
+}
+
+// arrayIndex is the index into the arrays gNumFrameUpdates and gNumDecodes
+// to increment when a frame update notification is received.
+function addCallbacks(anImage, arrayIndex) {
   var observer = new ImageDecoderObserverStub();
   observer.discard = function () {
-    imgLoadingContent.removeObserver(scriptedObserver);
     gNumDiscards++;
     ok(true, "got image discard");
+    if (arrayIndex >= 2) {
+      // The last two images are finite, so we don't expect any frame updates,
+      // this image is done the test, so remove the observer.
+      imgLoadingContent.removeObserver(scriptedObserver);
+    }
     if (gNumDiscards == gImgs.length) {
-      step2();
+      step4();
+    }
+  };
+  observer.frameUpdate = function () {
+    if (!gCountingFrameUpdates) {
+      return;
     }
-  }
+    gNumFrameUpdates[arrayIndex]++;
+
+    var r = document.getElementById(gImgs[arrayIndex]).getBoundingClientRect();
+    var snapshot = snapshotRect(window, r, "rgba(0,0,0,0)");
+    if (gLastSnapShot[arrayIndex] != null) {
+      if (snapshot.toDataURL() != gLastSnapShot[arrayIndex].toDataURL()) {
+        gNumSnapShotChanges[arrayIndex]++;
+      }
+    }
+    gLastSnapShot[arrayIndex] = snapshot;
+
+    if (gNumFrameUpdates[arrayIndex] >= kNumFrameUpdatesToExpect &&
+        gNumSnapShotChanges[arrayIndex] >= kNumFrameUpdatesToExpect) {
+      imgLoadingContent.removeObserver(scriptedObserver);
+    }
+    ok(true, "got frame update");
+    checkIfFinished();
+  };
   observer = SpecialPowers.wrapCallbackObject(observer);
 
   var scriptedObserver = SpecialPowers.Cc["@mozilla.org/image/tools;1"]
                            .getService(SpecialPowers.Ci.imgITools)
                            .createScriptedObserver(observer);
 
   var imgLoadingContent =
     SpecialPowers.wrap(anImage)
@@ -99,21 +162,25 @@ function addCallback(anImage) {
 function requestDiscard(anImage) {
   var request = SpecialPowers.wrap(anImage)
       .QueryInterface(SpecialPowers.Ci.nsIImageLoadingContent)
       .getRequest(SpecialPowers.Ci.nsIImageLoadingContent.CURRENT_REQUEST);
   setTimeout(() => request.requestDiscard(), 0);
 }
 
 function drawCanvas(anImage) {
-  dump(anImage + "\n");
   var canvas = document.getElementById('canvas');
   var context = canvas.getContext('2d');
 
+  context.clearRect(0,0,100,100);
+  var cleared = canvas.toDataURL();
+
   context.drawImage(anImage, 0, 0);
   ok(true, "we got through the drawImage call without an exception being thrown");
+
+  ok(cleared != canvas.toDataURL(), "drawImage drew something");
 }
 
 </script>
 </pre>
 </body>
 </html>
 
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -1771,16 +1771,34 @@ GetNameIRGenerator::tryAttachGlobalNameG
         ObjOperandId holderId = writer.loadObject(holder);
         writer.guardShape(holderId, holder->lastProperty());
     }
 
     EmitCallGetterResultNoGuards(writer, &globalLexical->global(), holder, shape, globalId);
     return true;
 }
 
+static bool
+NeedEnvironmentShapeGuard(JSObject* envObj)
+{
+    if (!envObj->is<CallObject>())
+        return true;
+
+    // We can skip a guard on the call object if the script's bindings are
+    // guaranteed to be immutable (and thus cannot introduce shadowing
+    // variables). The function might have been relazified under rare
+    // conditions. In that case, we pessimistically create the guard.
+    CallObject* callObj = &envObj->as<CallObject>();
+    JSFunction* fun = &callObj->callee();
+    if (!fun->hasScript() || fun->nonLazyScript()->funHasExtensibleScope())
+        return true;
+
+    return false;
+}
+
 bool
 GetNameIRGenerator::tryAttachEnvironmentName(ObjOperandId objId, HandleId id)
 {
     if (IsGlobalOp(JSOp(*pc_)) || script_->hasNonSyntacticScope())
         return false;
 
     RootedObject env(cx_, env_);
     RootedShape shape(cx_);
@@ -1813,22 +1831,22 @@ GetNameIRGenerator::tryAttachEnvironment
     if (!IsCacheableGetPropReadSlotForIonOrCacheIR(holder, holder, PropertyResult(shape)))
         return false;
     if (holder->getSlot(shape->slot()).isMagic())
         return false;
 
     ObjOperandId lastObjId = objId;
     env = env_;
     while (env) {
-        if (env == holder) {
-            writer.guardShape(lastObjId, holder->maybeShape());
+        if (NeedEnvironmentShapeGuard(env))
+            writer.guardShape(lastObjId, env->maybeShape());
+
+        if (env == holder)
             break;
-        }
-
-        writer.guardShape(lastObjId, env->maybeShape());
+
         lastObjId = writer.loadEnclosingEnvironment(lastObjId);
         env = env->enclosingEnvironment();
     }
 
     if (holder->isFixedSlot(shape->slot())) {
         writer.loadEnvironmentFixedSlotResult(lastObjId, NativeObject::getFixedSlotOffset(shape->slot()));
     } else {
         size_t dynamicSlotOffset = holder->dynamicSlotIndex(shape->slot()) * sizeof(Value);
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -226,16 +226,21 @@ typedef bool (*IonGetPropertyICFn)(JSCon
 static const VMFunction IonGetPropertyICInfo =
     FunctionInfo<IonGetPropertyICFn>(IonGetPropertyIC::update, "IonGetPropertyIC::update");
 
 typedef bool (*IonSetPropertyICFn)(JSContext*, HandleScript, IonSetPropertyIC*, HandleObject,
                                    HandleValue, HandleValue);
 static const VMFunction IonSetPropertyICInfo =
     FunctionInfo<IonSetPropertyICFn>(IonSetPropertyIC::update, "IonSetPropertyIC::update");
 
+typedef bool (*IonGetNameICFn)(JSContext*, HandleScript, IonGetNameIC*, HandleObject,
+                               MutableHandleValue);
+static const VMFunction IonGetNameICInfo =
+    FunctionInfo<IonGetNameICFn>(IonGetNameIC::update, "IonGetNameIC::update");
+
 void
 CodeGenerator::visitOutOfLineICFallback(OutOfLineICFallback* ool)
 {
     LInstruction* lir = ool->lir();
     size_t cacheIndex = ool->cacheIndex();
     size_t cacheInfoIndex = ool->cacheInfoIndex();
 
     DataPtr<IonIC> ic(this, cacheIndex);
@@ -277,17 +282,33 @@ CodeGenerator::visitOutOfLineICFallback(
 
         callVM(IonSetPropertyICInfo, lir);
 
         restoreLive(lir);
 
         masm.jump(ool->rejoin());
         return;
       }
-      case CacheKind::GetName:
+      case CacheKind::GetName: {
+        IonGetNameIC* getNameIC = ic->asGetNameIC();
+
+        saveLive(lir);
+
+        pushArg(getNameIC->environment());
+        icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
+        pushArg(ImmGCPtr(gen->info().script()));
+
+        callVM(IonGetNameICInfo, lir);
+
+        StoreValueTo(getNameIC->output()).generate(this);
+        restoreLiveIgnore(lir, StoreValueTo(getNameIC->output()).clobbered());
+
+        masm.jump(ool->rejoin());
+        return;
+      }
       case CacheKind::In:
         MOZ_CRASH("Baseline-specific for now");
     }
     MOZ_CRASH();
 }
 
 StringObject*
 MNewStringObject::templateObj() const
@@ -10267,41 +10288,21 @@ CodeGenerator::visitStoreFixedSlotT(LSto
     }
 }
 
 void
 CodeGenerator::visitGetNameCache(LGetNameCache* ins)
 {
     LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
     Register envChain = ToRegister(ins->envObj());
-    TypedOrValueRegister output(GetValueOutput(ins));
-    bool isTypeOf = ins->mir()->accessKind() != MGetNameCache::NAME;
-
-    NameIC cache(liveRegs, isTypeOf, envChain, ins->mir()->name(), output);
-    cache.setProfilerLeavePC(ins->mir()->profilerLeavePc());
-    addCache(ins, allocateCache(cache));
-}
-
-typedef bool (*NameICFn)(JSContext*, HandleScript, size_t, HandleObject, MutableHandleValue);
-const VMFunction NameIC::UpdateInfo = FunctionInfo<NameICFn>(NameIC::update, "NameIC::update");
-
-void
-CodeGenerator::visitNameIC(OutOfLineUpdateCache* ool, DataPtr<NameIC>& ic)
-{
-    LInstruction* lir = ool->lir();
-    saveLive(lir);
-
-    pushArg(ic->environmentChainReg());
-    pushArg(Imm32(ool->getCacheIndex()));
-    pushArg(ImmGCPtr(gen->info().script()));
-    callVM(NameIC::UpdateInfo, lir);
-    StoreValueTo(ic->outputReg()).generate(this);
-    restoreLiveIgnore(lir, StoreValueTo(ic->outputReg()).clobbered());
-
-    masm.jump(ool->rejoin());
+    ValueOperand output(GetValueOutput(ins));
+    Register temp = ToRegister(ins->temp());
+
+    IonGetNameIC ic(liveRegs, envChain, output, temp);
+    addIC(ins, allocateIC(ic));
 }
 
 void
 CodeGenerator::addGetPropertyCache(LInstruction* ins, LiveRegisterSet liveRegs,
                                    TypedOrValueRegister value, const ConstantOrRegister& id,
                                    TypedOrValueRegister output, Register maybeTemp,
                                    bool monitoredResult, bool allowDoubleResult,
                                    jsbytecode* profilerLeavePc)
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -419,17 +419,16 @@ class CodeGenerator final : public CodeG
     void visitGetPropertyCacheV(LGetPropertyCacheV* ins);
     void visitGetPropertyCacheT(LGetPropertyCacheT* ins);
     void visitBindNameCache(LBindNameCache* ins);
     void visitCallSetProperty(LInstruction* ins);
     void visitSetPropertyCache(LSetPropertyCache* ins);
     void visitGetNameCache(LGetNameCache* ins);
 
     void visitBindNameIC(OutOfLineUpdateCache* ool, DataPtr<BindNameIC>& ic);
-    void visitNameIC(OutOfLineUpdateCache* ool, DataPtr<NameIC>& ic);
 
     void visitAssertRangeI(LAssertRangeI* ins);
     void visitAssertRangeD(LAssertRangeD* ins);
     void visitAssertRangeF(LAssertRangeF* ins);
     void visitAssertRangeV(LAssertRangeV* ins);
 
     void visitAssertResultV(LAssertResultV* ins);
     void visitAssertResultT(LAssertResultT* ins);
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -7442,22 +7442,17 @@ AbortReasonOr<Ok>
 IonBuilder::jsop_getname(PropertyName* name)
 {
     MDefinition* object;
     if (IsGlobalOp(JSOp(*pc)) && !script()->hasNonSyntacticScope())
         object = constant(ObjectValue(script()->global().lexicalEnvironment()));
     else
         object = current->environmentChain();
 
-    MGetNameCache* ins;
-    if (JSOp(*GetNextPc(pc)) == JSOP_TYPEOF)
-        ins = MGetNameCache::New(alloc(), object, name, MGetNameCache::NAMETYPEOF);
-    else
-        ins = MGetNameCache::New(alloc(), object, name, MGetNameCache::NAME);
-
+    MGetNameCache* ins = MGetNameCache::New(alloc(), object);
     current->add(ins);
     current->push(ins);
 
     MOZ_TRY(resumeAfter(ins));
 
     TemporaryTypeSet* types = bytecodeTypes(pc);
     return pushTypeBarrier(ins, types, BarrierKind::TypeSet);
 }
--- a/js/src/jit/IonCacheIRCompiler.cpp
+++ b/js/src/jit/IonCacheIRCompiler.cpp
@@ -405,17 +405,30 @@ IonCacheIRCompiler::init()
             allocator.initInputLocation(1, ic->rhs());
         } else {
             MOZ_ASSERT(numInputs == 3);
             allocator.initInputLocation(1, ic->id());
             allocator.initInputLocation(2, ic->rhs());
         }
         break;
       }
-      case CacheKind::GetName:
+      case CacheKind::GetName: {
+        IonGetNameIC* ic = ic_->asGetNameIC();
+        ValueOperand output = ic->output();
+
+        available.add(output);
+        available.add(ic->temp());
+
+        liveRegs_.emplace(ic->liveRegs());
+        outputUnchecked_.emplace(output);
+
+        MOZ_ASSERT(numInputs == 1);
+        allocator.initInputLocation(0, ic->environment(), JSVAL_TYPE_OBJECT);
+        break;
+      }
       case CacheKind::In:
         MOZ_CRASH("Invalid cache");
     }
 
     if (liveRegs_)
         liveFloatRegs_ = LiveFloatRegisterSet(liveRegs_->fpus());
 
     allocator.initAvailableRegs(available);
@@ -1015,23 +1028,54 @@ bool
 IonCacheIRCompiler::emitLoadFrameArgumentResult()
 {
     MOZ_CRASH("Baseline-specific op");
 }
 
 bool
 IonCacheIRCompiler::emitLoadEnvironmentFixedSlotResult()
 {
-    MOZ_CRASH("Baseline-specific op");
+    AutoOutputRegister output(*this);
+    Register obj = allocator.useRegister(masm, reader.objOperandId());
+    int32_t offset = int32StubField(reader.stubOffset());
+
+    FailurePath* failure;
+    if (!addFailurePath(&failure))
+        return false;
+
+    // Check for uninitialized lexicals.
+    Address slot(obj, offset);
+    masm.branchTestMagic(Assembler::Equal, slot, failure->label());
+
+    // Load the value.
+    masm.loadTypedOrValue(slot, output);
+    return true;
 }
 
 bool
 IonCacheIRCompiler::emitLoadEnvironmentDynamicSlotResult()
 {
-    MOZ_CRASH("Baseline-specific op");
+    AutoOutputRegister output(*this);
+    Register obj = allocator.useRegister(masm, reader.objOperandId());
+    int32_t offset = int32StubField(reader.stubOffset());
+    AutoScratchRegisterMaybeOutput scratch(allocator, masm, output);
+
+    FailurePath* failure;
+    if (!addFailurePath(&failure))
+        return false;
+
+    masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), scratch);
+
+    // Check for uninitialized lexicals.
+    Address slot(scratch, offset);
+    masm.branchTestMagic(Assembler::Equal, slot, failure->label());
+
+    // Load the value.
+    masm.loadTypedOrValue(slot, output);
+    return true;
 }
 
 static bool
 GroupHasPropertyTypes(ObjectGroup* group, jsid* id, Value* v)
 {
     if (group->unknownProperties())
         return true;
     HeapTypeSet* propTypes = group->maybeGetProperty(*id);
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -401,59 +401,16 @@ jit::GetReturnAddressToIonCode(JSContext
     void* returnAddr = iter.returnAddress();
 #ifdef DEBUG
     ++iter;
     MOZ_ASSERT(iter.isIonJS());
 #endif
     return returnAddr;
 }
 
-static void
-GeneratePrototypeGuards(JSContext* cx, IonScript* ion, MacroAssembler& masm, JSObject* obj,
-                        JSObject* holder, Register objectReg, Register scratchReg,
-                        Label* failures)
-{
-    /*
-     * The guards here protect against the effects of JSObject::swap(). If the prototype chain
-     * is directly altered, then TI will toss the jitcode, so we don't have to worry about
-     * it, and any other change to the holder, or adding a shadowing property will result
-     * in reshaping the holder, and thus the failure of the shape guard.
-     */
-    MOZ_ASSERT(obj != holder);
-
-    if (obj->hasUncacheableProto()) {
-        // Note: objectReg and scratchReg may be the same register, so we cannot
-        // use objectReg in the rest of this function.
-        masm.loadPtr(Address(objectReg, JSObject::offsetOfGroup()), scratchReg);
-        Address proto(scratchReg, ObjectGroup::offsetOfProto());
-        masm.branchPtr(Assembler::NotEqual, proto, ImmGCPtr(obj->staticPrototype()), failures);
-    }
-
-    JSObject* pobj = obj->staticPrototype();
-    if (!pobj)
-        return;
-    while (pobj != holder) {
-        if (pobj->hasUncacheableProto()) {
-            masm.movePtr(ImmGCPtr(pobj), scratchReg);
-            Address groupAddr(scratchReg, JSObject::offsetOfGroup());
-            if (pobj->isSingleton()) {
-                // Singletons can have their group's |proto| mutated directly.
-                masm.loadPtr(groupAddr, scratchReg);
-                Address protoAddr(scratchReg, ObjectGroup::offsetOfProto());
-                masm.branchPtr(Assembler::NotEqual, protoAddr, ImmGCPtr(pobj->staticPrototype()),
-                              failures);
-            } else {
-                masm.branchPtr(Assembler::NotEqual, groupAddr, ImmGCPtr(pobj->group()), failures);
-            }
-        }
-
-        pobj = pobj->staticPrototype();
-    }
-}
-
 // Note: This differs from IsCacheableProtoChain in BaselineIC.cpp in that
 // Ion caches can deal with objects on the proto chain that have uncacheable
 // prototypes.
 bool
 jit::IsCacheableProtoChainForIonOrCacheIR(JSObject* obj, JSObject* holder)
 {
     while (obj != holder) {
         /*
@@ -477,56 +434,16 @@ jit::IsCacheableGetPropReadSlotForIonOrC
 
     Shape* shape = prop.shape();
     if (!shape->hasSlot() || !shape->hasDefaultGetter())
         return false;
 
     return true;
 }
 
-static bool
-IsCacheableNoProperty(JSObject* obj, JSObject* holder, PropertyResult prop, jsbytecode* pc,
-                      const TypedOrValueRegister& output)
-{
-    if (prop)
-        return false;
-
-    MOZ_ASSERT(!holder);
-
-    // Just because we didn't find the property on the object doesn't mean it
-    // won't magically appear through various engine hacks.
-    if (obj->getClass()->getGetProperty())
-        return false;
-
-    // Don't generate missing property ICs if we skipped a non-native object, as
-    // lookups may extend beyond the prototype chain (e.g. for DOMProxy
-    // proxies).
-    JSObject* obj2 = obj;
-    while (obj2) {
-        if (!obj2->isNative())
-            return false;
-        obj2 = obj2->staticPrototype();
-    }
-
-    // The pc is nullptr if the cache is idempotent. We cannot share missing
-    // properties between caches because TI can only try to prove that a type is
-    // contained, but does not attempts to check if something does not exists.
-    // So the infered type of getprop would be missing and would not contain
-    // undefined, as expected for missing properties.
-    if (!pc)
-        return false;
-
-    // TI has not yet monitored an Undefined value. The fallback path will
-    // monitor and invalidate the script.
-    if (!output.hasValue())
-        return false;
-
-    return true;
-}
-
 bool
 jit::IsCacheableGetPropCallNative(JSObject* obj, JSObject* holder, Shape* shape)
 {
     if (!shape || !IsCacheableProtoChainForIonOrCacheIR(obj, holder))
         return false;
 
     if (!shape->hasGetterValue() || !shape->getterValue().isObject())
         return false;
@@ -574,464 +491,16 @@ jit::IsCacheableGetPropCallScripted(JSOb
         if (isTemporarilyUnoptimizable)
             *isTemporarilyUnoptimizable = true;
         return false;
     }
 
     return true;
 }
 
-static bool
-IsCacheableGetPropCallPropertyOp(JSObject* obj, JSObject* holder, Shape* shape)
-{
-    if (!shape || !IsCacheableProtoChainForIonOrCacheIR(obj, holder))
-        return false;
-
-    if (shape->hasSlot() || shape->hasGetterValue() || shape->hasDefaultGetter())
-        return false;
-
-    return true;
-}
-
-static void
-TestMatchingReceiver(MacroAssembler& masm, IonCache::StubAttacher& attacher,
-                     Register object, JSObject* obj, Label* failure,
-                     bool alwaysCheckGroup = false)
-{
-    if (obj->is<UnboxedPlainObject>()) {
-        MOZ_ASSERT(failure);
-
-        masm.branchTestObjGroup(Assembler::NotEqual, object, obj->group(), failure);
-        Address expandoAddress(object, UnboxedPlainObject::offsetOfExpando());
-        if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando()) {
-            masm.branchPtr(Assembler::Equal, expandoAddress, ImmWord(0), failure);
-            Label success;
-            masm.push(object);
-            masm.loadPtr(expandoAddress, object);
-            masm.branchTestObjShape(Assembler::Equal, object, expando->lastProperty(),
-                                    &success);
-            masm.pop(object);
-            masm.jump(failure);
-            masm.bind(&success);
-            masm.pop(object);
-        } else {
-            masm.branchPtr(Assembler::NotEqual, expandoAddress, ImmWord(0), failure);
-        }
-    } else if (obj->is<UnboxedArrayObject>()) {
-        MOZ_ASSERT(failure);
-        masm.branchTestObjGroup(Assembler::NotEqual, object, obj->group(), failure);
-    } else if (obj->is<TypedObject>()) {
-        attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
-                                       Address(object, JSObject::offsetOfGroup()),
-                                       ImmGCPtr(obj->group()), failure);
-    } else {
-        Shape* shape = obj->maybeShape();
-        MOZ_ASSERT(shape);
-
-        attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
-                                       Address(object, ShapedObject::offsetOfShape()),
-                                       ImmGCPtr(shape), failure);
-
-        if (alwaysCheckGroup)
-            masm.branchTestObjGroup(Assembler::NotEqual, object, obj->group(), failure);
-    }
-}
-
-static inline void
-EmitLoadSlot(MacroAssembler& masm, NativeObject* holder, Shape* shape, Register holderReg,
-             TypedOrValueRegister output, Register scratchReg)
-{
-    MOZ_ASSERT(holder);
-    NativeObject::slotsSizeMustNotOverflow();
-    if (holder->isFixedSlot(shape->slot())) {
-        Address addr(holderReg, NativeObject::getFixedSlotOffset(shape->slot()));
-        masm.loadTypedOrValue(addr, output);
-    } else {
-        masm.loadPtr(Address(holderReg, NativeObject::offsetOfSlots()), scratchReg);
-
-        Address addr(scratchReg, holder->dynamicSlotIndex(shape->slot()) * sizeof(Value));
-        masm.loadTypedOrValue(addr, output);
-    }
-}
-
-static void
-GenerateReadSlot(JSContext* cx, IonScript* ion, MacroAssembler& masm,
-                 IonCache::StubAttacher& attacher, MaybeCheckTDZ checkTDZ,
-                 JSObject* obj, JSObject* holder, PropertyResult prop, Register object,
-                 TypedOrValueRegister output, Label* failures = nullptr)
-{
-    // If there's a single jump to |failures|, we can patch the shape guard
-    // jump directly. Otherwise, jump to the end of the stub, so there's a
-    // common point to patch.
-    bool multipleFailureJumps = (obj != holder)
-                             || obj->is<UnboxedPlainObject>()
-                             || (checkTDZ && output.hasValue())
-                             || (failures != nullptr && failures->used());
-
-    // If we have multiple failure jumps but didn't get a label from the
-    // outside, make one ourselves.
-    Label failures_;
-    if (multipleFailureJumps && !failures)
-        failures = &failures_;
-
-    TestMatchingReceiver(masm, attacher, object, obj, failures);
-
-    // If we need a scratch register, use either an output register or the
-    // object register. After this point, we cannot jump directly to
-    // |failures| since we may still have to pop the object register.
-    bool restoreScratch = false;
-    Register scratchReg = Register::FromCode(0); // Quell compiler warning.
-
-    if (obj != holder ||
-        obj->is<UnboxedPlainObject>() ||
-        !holder->as<NativeObject>().isFixedSlot(prop.shape()->slot()))
-    {
-        if (output.hasValue()) {
-            scratchReg = output.valueReg().scratchReg();
-        } else if (output.type() == MIRType::Double) {
-            scratchReg = object;
-            masm.push(scratchReg);
-            restoreScratch = true;
-        } else {
-            scratchReg = output.typedReg().gpr();
-        }
-    }
-
-    // Fast path: single failure jump, no prototype guards.
-    if (!multipleFailureJumps) {
-        EmitLoadSlot(masm, &holder->as<NativeObject>(), prop.shape(), object, output, scratchReg);
-        if (restoreScratch)
-            masm.pop(scratchReg);
-        attacher.jumpRejoin(masm);
-        return;
-    }
-
-    // Slow path: multiple jumps; generate prototype guards.
-    Label prototypeFailures;
-    Register holderReg;
-    if (obj != holder) {
-        // Note: this may clobber the object register if it's used as scratch.
-        GeneratePrototypeGuards(cx, ion, masm, obj, holder, object, scratchReg,
-                                &prototypeFailures);
-
-        if (holder) {
-            // Guard on the holder's shape.
-            holderReg = scratchReg;
-            masm.movePtr(ImmGCPtr(holder), holderReg);
-            masm.branchPtr(Assembler::NotEqual,
-                           Address(holderReg, ShapedObject::offsetOfShape()),
-                           ImmGCPtr(holder->as<NativeObject>().lastProperty()),
-                           &prototypeFailures);
-        } else {
-            // The property does not exist. Guard on everything in the
-            // prototype chain.
-            JSObject* proto = obj->staticPrototype();
-            Register lastReg = object;
-            MOZ_ASSERT(scratchReg != object);
-            while (proto) {
-                masm.loadObjProto(lastReg, scratchReg);
-
-                // Guard the shape of the current prototype.
-                MOZ_ASSERT(proto->hasStaticPrototype());
-                masm.branchPtr(Assembler::NotEqual,
-                               Address(scratchReg, ShapedObject::offsetOfShape()),
-                               ImmGCPtr(proto->as<NativeObject>().lastProperty()),
-                               &prototypeFailures);
-
-                proto = proto->staticPrototype();
-                lastReg = scratchReg;
-            }
-
-            holderReg = InvalidReg;
-        }
-    } else if (obj->is<UnboxedPlainObject>()) {
-        holder = obj->as<UnboxedPlainObject>().maybeExpando();
-        holderReg = scratchReg;
-        masm.loadPtr(Address(object, UnboxedPlainObject::offsetOfExpando()), holderReg);
-    } else {
-        holderReg = object;
-    }
-
-    // Slot access.
-    if (holder) {
-        EmitLoadSlot(masm, &holder->as<NativeObject>(), prop.shape(), holderReg, output,
-                     scratchReg);
-        if (checkTDZ && output.hasValue())
-            masm.branchTestMagic(Assembler::Equal, output.valueReg(), failures);
-    } else {
-        masm.moveValue(UndefinedValue(), output.valueReg());
-    }
-
-    // Restore scratch on success.
-    if (restoreScratch)
-        masm.pop(scratchReg);
-
-    attacher.jumpRejoin(masm);
-
-    masm.bind(&prototypeFailures);
-    if (restoreScratch)
-        masm.pop(scratchReg);
-    masm.bind(failures);
-
-    attacher.jumpNextStub(masm);
-}
-
-static bool
-EmitGetterCall(JSContext* cx, MacroAssembler& masm,
-               IonCache::StubAttacher& attacher, JSObject* obj,
-               JSObject* holder, HandleShape shape, bool holderIsReceiver,
-               LiveRegisterSet liveRegs, Register object,
-               TypedOrValueRegister output,
-               void* returnAddr)
-{
-    MOZ_ASSERT(output.hasValue());
-    MacroAssembler::AfterICSaveLive aic = masm.icSaveLive(liveRegs);
-
-    MOZ_ASSERT_IF(obj != holder, !holderIsReceiver);
-
-    // Remaining registers should basically be free, but we need to use |object| still
-    // so leave it alone.
-    AllocatableRegisterSet regSet(RegisterSet::All());
-    regSet.take(AnyRegister(object));
-
-    // This is a slower stub path, and we're going to be doing a call anyway.  Don't need
-    // to try so hard to not use the stack.  Scratch regs are just taken from the register
-    // set not including the input, current value saved on the stack, and restored when
-    // we're done with it.
-    Register scratchReg = regSet.takeAnyGeneral();
-
-    // Shape has a JSNative, PropertyOp or scripted getter function.
-    if (IsCacheableGetPropCallNative(obj, holder, shape)) {
-        Register argJSContextReg = regSet.takeAnyGeneral();
-        Register argUintNReg     = regSet.takeAnyGeneral();
-        Register argVpReg        = regSet.takeAnyGeneral();
-
-        JSFunction* target = &shape->getterValue().toObject().as<JSFunction>();
-        MOZ_ASSERT(target);
-        MOZ_ASSERT(target->isNative());
-
-        // Native functions have the signature:
-        //  bool (*)(JSContext*, unsigned, Value* vp)
-        // Where vp[0] is space for an outparam, vp[1] is |this|, and vp[2] onward
-        // are the function arguments.
-
-        // Construct vp array:
-        // Push object value for |this|
-        masm.Push(TypedOrValueRegister(MIRType::Object, AnyRegister(object)));
-        // Push callee/outparam.
-        masm.Push(ObjectValue(*target));
-
-        // Preload arguments into registers.
-        masm.loadJSContext(argJSContextReg);
-        masm.move32(Imm32(0), argUintNReg);
-        masm.moveStackPtrTo(argVpReg);
-
-        // Push marking data for later use.
-        masm.Push(argUintNReg);
-        attacher.pushStubCodePointer(masm);
-
-        if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
-            return false;
-        masm.enterFakeExitFrame(argJSContextReg, IonOOLNativeExitFrameLayoutToken);
-
-        // Construct and execute call.
-        masm.setupUnalignedABICall(scratchReg);
-        masm.passABIArg(argJSContextReg);
-        masm.passABIArg(argUintNReg);
-        masm.passABIArg(argVpReg);
-        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, target->native()));
-
-        // Test for failure.
-        masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
-
-        // Load the outparam vp[0] into output register(s).
-        Address outparam(masm.getStackPointer(), IonOOLNativeExitFrameLayout::offsetOfResult());
-        masm.loadTypedOrValue(outparam, output);
-
-        // masm.leaveExitFrame & pop locals
-        masm.adjustStack(IonOOLNativeExitFrameLayout::Size(0));
-    } else if (IsCacheableGetPropCallPropertyOp(obj, holder, shape)) {
-        Register argJSContextReg = regSet.takeAnyGeneral();
-        Register argObjReg       = regSet.takeAnyGeneral();
-        Register argIdReg        = regSet.takeAnyGeneral();
-        Register argVpReg        = regSet.takeAnyGeneral();
-
-        GetterOp target = shape->getterOp();
-        MOZ_ASSERT(target);
-
-        // Push stubCode for marking.
-        attacher.pushStubCodePointer(masm);
-
-        // JSGetterOp: bool fn(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp)
-
-        // Push args on stack first so we can take pointers to make handles.
-        masm.Push(UndefinedValue());
-        masm.moveStackPtrTo(argVpReg);
-
-        // Push canonical jsid from shape instead of propertyname.
-        masm.Push(shape->propid(), scratchReg);
-        masm.moveStackPtrTo(argIdReg);
-
-        // Push the holder.
-        if (holderIsReceiver) {
-            // When the holder is also the current receiver, we just have a shape guard,
-            // so we might end up with a random object which is also guaranteed to have
-            // this JSGetterOp.
-            masm.Push(object);
-        } else {
-            // If the holder is on the prototype chain, the prototype-guarding
-            // only allows objects with the same holder.
-            masm.movePtr(ImmGCPtr(holder), scratchReg);
-            masm.Push(scratchReg);
-        }
-        masm.moveStackPtrTo(argObjReg);
-
-        masm.loadJSContext(argJSContextReg);
-
-        if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
-            return false;
-        masm.enterFakeExitFrame(argJSContextReg, IonOOLPropertyOpExitFrameLayoutToken);
-
-        // Make the call.
-        masm.setupUnalignedABICall(scratchReg);
-        masm.passABIArg(argJSContextReg);
-        masm.passABIArg(argObjReg);
-        masm.passABIArg(argIdReg);
-        masm.passABIArg(argVpReg);
-        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, target));
-
-        // Test for failure.
-        masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
-
-        // Load the outparam vp[0] into output register(s).
-        Address outparam(masm.getStackPointer(), IonOOLPropertyOpExitFrameLayout::offsetOfResult());
-        masm.loadTypedOrValue(outparam, output);
-
-        // masm.leaveExitFrame & pop locals.
-        masm.adjustStack(IonOOLPropertyOpExitFrameLayout::Size());
-    } else {
-        MOZ_ASSERT(IsCacheableGetPropCallScripted(obj, holder, shape));
-
-        JSFunction* target = &shape->getterValue().toObject().as<JSFunction>();
-        uint32_t framePushedBefore = masm.framePushed();
-
-        // Construct IonICCallFrameLayout.
-        uint32_t descriptor = MakeFrameDescriptor(masm.framePushed(), JitFrame_IonJS,
-                                                  IonICCallFrameLayout::Size());
-        attacher.pushStubCodePointer(masm);
-        masm.Push(Imm32(descriptor));
-        masm.Push(ImmPtr(returnAddr));
-
-        // The JitFrameLayout pushed below will be aligned to JitStackAlignment,
-        // so we just have to make sure the stack is aligned after we push the
-        // |this| + argument Values.
-        uint32_t argSize = (target->nargs() + 1) * sizeof(Value);
-        uint32_t padding = ComputeByteAlignment(masm.framePushed() + argSize, JitStackAlignment);
-        MOZ_ASSERT(padding % sizeof(uintptr_t) == 0);
-        MOZ_ASSERT(padding < JitStackAlignment);
-        masm.reserveStack(padding);
-
-        for (size_t i = 0; i < target->nargs(); i++)
-            masm.Push(UndefinedValue());
-        masm.Push(TypedOrValueRegister(MIRType::Object, AnyRegister(object)));
-
-        masm.movePtr(ImmGCPtr(target), scratchReg);
-
-        descriptor = MakeFrameDescriptor(argSize + padding, JitFrame_IonICCall,
-                                         JitFrameLayout::Size());
-        masm.Push(Imm32(0)); // argc
-        masm.Push(scratchReg);
-        masm.Push(Imm32(descriptor));
-
-        // 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(scratchReg, JSFunction::offsetOfNativeOrScript()), scratchReg);
-        masm.loadBaselineOrIonRaw(scratchReg, scratchReg, nullptr);
-        masm.callJit(scratchReg);
-        masm.storeCallResultValue(output);
-
-        masm.freeStack(masm.framePushed() - framePushedBefore);
-    }
-
-    masm.icRestoreLive(liveRegs, aic);
-    return true;
-}
-
-static bool
-GenerateCallGetter(JSContext* cx, IonScript* ion, MacroAssembler& masm,
-                   IonCache::StubAttacher& attacher, JSObject* obj,
-                   JSObject* holder, HandleShape shape, LiveRegisterSet& liveRegs, Register object,
-                   TypedOrValueRegister output, void* returnAddr, Label* failures = nullptr)
-{
-    MOZ_ASSERT(output.hasValue());
-
-    // Use the passed in label if there was one. Otherwise, we'll have to make our own.
-    Label stubFailure;
-    failures = failures ? failures : &stubFailure;
-
-    TestMatchingReceiver(masm, attacher, object, obj, failures);
-
-    Register scratchReg = output.valueReg().scratchReg();
-    bool spillObjReg = scratchReg == object;
-    Label pop1AndFail;
-    Label* maybePopAndFail = failures;
-
-    // If we're calling a getter on the global, inline the logic for the
-    // 'this' hook on the global lexical env and manually push the global.
-    if (IsGlobalLexicalEnvironment(obj)) {
-        masm.extractObject(Address(object, EnvironmentObject::offsetOfEnclosingEnvironment()),
-                           object);
-    }
-
-    // Save off the object register if it aliases the scratchReg
-    if (spillObjReg) {
-        masm.push(object);
-        maybePopAndFail = &pop1AndFail;
-    }
-
-    // Note: this may clobber the object register if it's used as scratch.
-    if (obj != holder)
-        GeneratePrototypeGuards(cx, ion, masm, obj, holder, object, scratchReg, maybePopAndFail);
-
-    // Guard on the holder's shape.
-    Register holderReg = scratchReg;
-    masm.movePtr(ImmGCPtr(holder), holderReg);
-    masm.branchPtr(Assembler::NotEqual,
-                   Address(holderReg, ShapedObject::offsetOfShape()),
-                   ImmGCPtr(holder->as<NativeObject>().lastProperty()),
-                   maybePopAndFail);
-
-    if (spillObjReg)
-        masm.pop(object);
-
-    // Now we're good to go to invoke the native call.
-    bool holderIsReceiver = (obj == holder);
-    if (!EmitGetterCall(cx, masm, attacher, obj, holder, shape, holderIsReceiver, liveRegs, object,
-                        output, returnAddr))
-        return false;
-
-    // Rejoin jump.
-    attacher.jumpRejoin(masm);
-
-    // Jump to next stub.
-    if (spillObjReg) {
-        masm.bind(&pop1AndFail);
-        masm.pop(object);
-    }
-    masm.bind(failures);
-    attacher.jumpNextStub(masm);
-
-    return true;
-}
-
 bool
 jit::ValueToNameOrSymbolId(JSContext* cx, HandleValue idval, MutableHandleId id,
                            bool* nameOrSymbol)
 {
     *nameOrSymbol = false;
 
     if (!idval.isString() && !idval.isSymbol())
         return true;
@@ -1336,247 +805,8 @@ BindNameIC::update(JSContext* cx, Handle
         } else {
             JitSpew(JitSpew_IonIC, "BINDNAME uncacheable env chain");
         }
     }
 
     return holder;
 }
 
-bool
-NameIC::attachReadSlot(JSContext* cx, HandleScript outerScript, IonScript* ion,
-                       HandleObject envChain, HandleObject holderBase,
-                       HandleNativeObject holder, Handle<PropertyResult> prop)
-{
-    MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
-    Label failures;
-    StubAttacher attacher(*this);
-
-    Register scratchReg = outputReg().valueReg().scratchReg();
-
-    // Don't guard the base of the proto chain the name was found on. It will be guarded
-    // by GenerateReadSlot().
-    masm.mov(environmentChainReg(), scratchReg);
-    GenerateEnvironmentChainGuards(masm, envChain, holderBase, scratchReg, &failures,
-                             /* skipLastGuard = */true);
-
-    // GenerateEnvironmentChain leaves the last env chain in scratchReg, even though it
-    // doesn't generate the extra guard.
-    //
-    // NAME ops must do their own TDZ checks.
-    GenerateReadSlot(cx, ion, masm, attacher, CheckTDZ, holderBase, holder, prop, scratchReg,
-                     outputReg(), failures.used() ? &failures : nullptr);
-
-    return linkAndAttachStub(cx, masm, attacher, ion, "generic",
-                             JS::TrackedOutcome::ICNameStub_ReadSlot);
-}
-
-static bool
-IsCacheableEnvironmentChain(JSObject* envChain, JSObject* obj)
-{
-    JSObject* obj2 = envChain;
-    while (obj2) {
-        if (!IsCacheableEnvironment(obj2) && !obj2->is<GlobalObject>())
-            return false;
-
-        // Stop once we hit the global or target obj.
-        if (obj2->is<GlobalObject>() || obj2 == obj)
-            break;
-
-        obj2 = obj2->enclosingEnvironment();
-    }
-
-    return obj == obj2;
-}
-
-static bool
-IsCacheableNameReadSlot(JSContext* cx, HandleObject envChain, HandleObject obj,
-                        HandleObject holder, Handle<PropertyResult> prop, jsbytecode* pc,
-                        const TypedOrValueRegister& output)
-{
-    if (!prop)
-        return false;
-    if (!obj->isNative())
-        return false;
-
-    if (obj->is<GlobalObject>()) {
-        // Support only simple property lookups.
-        if (!IsCacheableGetPropReadSlotForIonOrCacheIR(obj, holder, prop) &&
-            !IsCacheableNoProperty(obj, holder, prop, pc, output))
-            return false;
-    } else if (obj->is<ModuleEnvironmentObject>()) {
-        // We don't yet support lookups in a module environment.
-        return false;
-    } else if (obj->is<CallObject>()) {
-        MOZ_ASSERT(obj == holder);
-        if (!prop.shape()->hasDefaultGetter())
-            return false;
-    } else {
-        // We don't yet support lookups on Block or DeclEnv objects.
-        return false;
-    }
-
-    return IsCacheableEnvironmentChain(envChain, obj);
-}
-
-bool
-NameIC::attachCallGetter(JSContext* cx, HandleScript outerScript, IonScript* ion,
-                         HandleObject envChain, HandleObject obj, HandleObject holder,
-                         HandleShape shape, void* returnAddr)
-{
-    MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
-    StubAttacher attacher(*this);
-
-    Label failures;
-    Register scratchReg = outputReg().valueReg().scratchReg();
-
-    // Don't guard the base of the proto chain the name was found on. It will be guarded
-    // by GenerateCallGetter().
-    masm.mov(environmentChainReg(), scratchReg);
-    GenerateEnvironmentChainGuards(masm, envChain, obj, scratchReg, &failures,
-                             /* skipLastGuard = */true);
-
-    // GenerateEnvironmentChain leaves the last env chain in scratchReg, even though it
-    // doesn't generate the extra guard.
-    if (!GenerateCallGetter(cx, ion, masm, attacher, obj, holder, shape, liveRegs_,
-                            scratchReg, outputReg(), returnAddr,
-                            failures.used() ? &failures : nullptr))
-    {
-         return false;
-    }
-
-    const char* attachKind = "name getter";
-    return linkAndAttachStub(cx, masm, attacher, ion, attachKind,
-                             JS::TrackedOutcome::ICNameStub_CallGetter);
-}
-
-static bool
-IsCacheableNameCallGetter(HandleObject envChain, HandleObject obj, HandleObject holder,
-                          Handle<PropertyResult> prop)
-{
-    if (!prop)
-        return false;
-    if (!obj->is<GlobalObject>())
-        return false;
-
-    if (!IsCacheableEnvironmentChain(envChain, obj))
-        return false;
-
-    if (!prop || !IsCacheableProtoChainForIonOrCacheIR(obj, holder))
-        return false;
-
-    Shape* shape = prop.shape();
-    return IsCacheableGetPropCallNative(obj, holder, shape) ||
-        IsCacheableGetPropCallPropertyOp(obj, holder, shape) ||
-        IsCacheableGetPropCallScripted(obj, holder, shape);
-}
-
-bool
-NameIC::attachTypeOfNoProperty(JSContext* cx, HandleScript outerScript, IonScript* ion,
-                               HandleObject envChain)
-{
-    MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
-    Label failures;
-    StubAttacher attacher(*this);
-
-    Register scratchReg = outputReg().valueReg().scratchReg();
-
-    masm.movePtr(environmentChainReg(), scratchReg);
-
-    // Generate env chain guards.
-    // Since the property was not defined on any object, iterate until reaching the global.
-    JSObject* tobj = envChain;
-    while (true) {
-        GenerateEnvironmentChainGuard(masm, tobj, scratchReg, nullptr, &failures);
-
-        if (tobj->is<GlobalObject>())
-            break;
-
-        // Load the next link.
-        tobj = &tobj->as<EnvironmentObject>().enclosingEnvironment();
-        masm.extractObject(Address(scratchReg, EnvironmentObject::offsetOfEnclosingEnvironment()),
-                           scratchReg);
-    }
-
-    masm.moveValue(UndefinedValue(), outputReg().valueReg());
-    attacher.jumpRejoin(masm);
-
-    masm.bind(&failures);
-    attacher.jumpNextStub(masm);
-
-    return linkAndAttachStub(cx, masm, attacher, ion, "generic",
-                             JS::TrackedOutcome::ICNameStub_TypeOfNoProperty);
-}
-
-static bool
-IsCacheableNameNoProperty(HandleObject envChain, HandleObject obj,
-                          HandleObject holder, Handle<PropertyResult> prop, jsbytecode* pc,
-                          NameIC& cache)
-{
-    if (cache.isTypeOf() && !prop) {
-        MOZ_ASSERT(!obj);
-        MOZ_ASSERT(!holder);
-        MOZ_ASSERT(envChain);
-
-        // Assert those extra things checked by IsCacheableNoProperty().
-        MOZ_ASSERT(cache.outputReg().hasValue());
-        MOZ_ASSERT(pc != nullptr);
-
-        return true;
-    }
-
-    return false;
-}
-
-bool
-NameIC::update(JSContext* cx, HandleScript outerScript, size_t cacheIndex, HandleObject envChain,
-               MutableHandleValue vp)
-{
-    IonScript* ion = outerScript->ionScript();
-
-    NameIC& cache = ion->getCache(cacheIndex).toName();
-    RootedPropertyName name(cx, cache.name());
-
-    RootedScript script(cx);
-    jsbytecode* pc;
-    cache.getScriptedLocation(&script, &pc);
-
-    RootedObject obj(cx);
-    RootedObject holder(cx);
-    Rooted<PropertyResult> prop(cx);
-    if (!LookupName(cx, name, envChain, &obj, &holder, &prop))
-        return false;
-
-    // Look first. Don't generate cache entries if the lookup fails.
-    if (cache.isTypeOf()) {
-        if (!FetchName<GetNameMode::TypeOf>(cx, obj, holder, name, prop, vp))
-            return false;
-    } else {
-        if (!FetchName<GetNameMode::Normal>(cx, obj, holder, name, prop, vp))
-            return false;
-    }
-
-    if (cache.canAttachStub()) {
-        if (IsCacheableNameReadSlot(cx, envChain, obj, holder, prop, pc, cache.outputReg())) {
-            if (!cache.attachReadSlot(cx, outerScript, ion, envChain, obj,
-                                      holder.as<NativeObject>(), prop))
-            {
-                return false;
-            }
-        } else if (IsCacheableNameCallGetter(envChain, obj, holder, prop)) {
-            void* returnAddr = GetReturnAddressToIonCode(cx);
-            RootedShape shape(cx, prop.shape());
-            if (!cache.attachCallGetter(cx, outerScript, ion, envChain, obj, holder, shape,
-                                        returnAddr))
-            {
-                return false;
-            }
-        } else if (IsCacheableNameNoProperty(envChain, obj, holder, prop, pc, cache)) {
-            if (!cache.attachTypeOfNoProperty(cx, outerScript, ion, envChain))
-                return false;
-        }
-    }
-
-    // Monitor changes to cache entry.
-    TypeScript::Monitor(cx, script, pc, vp);
-
-    return true;
-}
--- a/js/src/jit/IonCaches.h
+++ b/js/src/jit/IonCaches.h
@@ -24,18 +24,17 @@
 #include "vm/TypedArrayObject.h"
 
 namespace js {
 namespace jit {
 
 class LInstruction;
 
 #define IONCACHE_KIND_LIST(_)                                   \
-    _(BindName)                                                 \
-    _(Name)
+    _(BindName)
 
 // Forward declarations of Cache kinds.
 #define FORWARD_DECLARE(kind) class kind##IC;
 IONCACHE_KIND_LIST(FORWARD_DECLARE)
 #undef FORWARD_DECLARE
 
 class IonCacheVisitor
 {
@@ -391,72 +390,16 @@ class BindNameIC : public IonCache
 
     MOZ_MUST_USE bool attachNonGlobal(JSContext* cx, HandleScript outerScript, IonScript* ion,
                                       HandleObject envChain, HandleObject holder);
 
     static JSObject*
     update(JSContext* cx, HandleScript outerScript, size_t cacheIndex, HandleObject envChain);
 };
 
-class NameIC : public IonCache
-{
-  protected:
-    // Registers live after the cache, excluding output registers. The initial
-    // value of these registers must be preserved by the cache.
-    LiveRegisterSet liveRegs_;
-
-    bool typeOf_;
-    Register environmentChain_;
-    PropertyName* name_;
-    TypedOrValueRegister output_;
-
-  public:
-    NameIC(LiveRegisterSet liveRegs, bool typeOf,
-           Register envChain, PropertyName* name,
-           TypedOrValueRegister output)
-      : liveRegs_(liveRegs),
-        typeOf_(typeOf),
-        environmentChain_(envChain),
-        name_(name),
-        output_(output)
-    {
-    }
-
-    CACHE_HEADER(Name)
-
-    Register environmentChainReg() const {
-        return environmentChain_;
-    }
-    HandlePropertyName name() const {
-        return HandlePropertyName::fromMarkedLocation(&name_);
-    }
-    TypedOrValueRegister outputReg() const {
-        return output_;
-    }
-    bool isTypeOf() const {
-        return typeOf_;
-    }
-
-    MOZ_MUST_USE bool attachReadSlot(JSContext* cx, HandleScript outerScript, IonScript* ion,
-                                     HandleObject envChain, HandleObject holderBase,
-                                     HandleNativeObject holder, Handle<PropertyResult> prop);
-
-    MOZ_MUST_USE bool attachCallGetter(JSContext* cx, HandleScript outerScript, IonScript* ion,
-                                       HandleObject envChain, HandleObject obj,
-                                       HandleObject holder, HandleShape shape,
-                                       void* returnAddr);
-
-    MOZ_MUST_USE bool attachTypeOfNoProperty(JSContext* cx, HandleScript outerScript,
-                                             IonScript* ion, HandleObject envChain);
-
-    static MOZ_MUST_USE bool
-    update(JSContext* cx, HandleScript outerScript, size_t cacheIndex, HandleObject envChain,
-           MutableHandleValue vp);
-};
-
 #undef CACHE_HEADER
 
 // Implement cache casts now that the compiler can see the inheritance.
 #define CACHE_CASTS(ickind)                                             \
     ickind##IC& IonCache::to##ickind()                                  \
     {                                                                   \
         MOZ_ASSERT(is##ickind());                                       \
         return *static_cast<ickind##IC*>(this);                        \
--- a/js/src/jit/IonIC.cpp
+++ b/js/src/jit/IonIC.cpp
@@ -40,16 +40,17 @@ IonIC::scratchRegisterForEntryJump()
             return temp;
         TypedOrValueRegister output = asGetPropertyIC()->output();
         return output.hasValue() ? output.valueReg().scratchReg() : output.typedReg().gpr();
       }
       case CacheKind::SetProp:
       case CacheKind::SetElem:
         return asSetPropertyIC()->temp();
       case CacheKind::GetName:
+        return asGetNameIC()->temp();
       case CacheKind::In:
         MOZ_CRASH("Baseline-specific for now");
     }
 
     MOZ_CRASH("Invalid kind");
 }
 
 void
@@ -272,16 +273,59 @@ IonSetPropertyIC::update(JSContext* cx, 
 
         if (!attached && !isTemporarilyUnoptimizable)
             ic->state().trackNotAttached();
     }
 
     return true;
 }
 
+/* static */ bool
+IonGetNameIC::update(JSContext* cx, HandleScript outerScript, IonGetNameIC* ic,
+                     HandleObject envChain, MutableHandleValue res)
+{
+    IonScript* ionScript = outerScript->ionScript();
+
+    if (ic->state().maybeTransition())
+        ic->discardStubs(cx->zone());
+
+    jsbytecode* pc = ic->pc();
+    RootedPropertyName name(cx, ic->script()->getName(pc));
+
+    if (ic->state().canAttachStub()) {
+        bool attached = false;
+        RootedScript script(cx, ic->script());
+        GetNameIRGenerator gen(cx, script, pc, ic->state().mode(), envChain, name);
+        if (gen.tryAttachStub())
+            ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), ionScript, &attached);
+
+        if (!attached)
+            ic->state().trackNotAttached();
+    }
+
+    RootedObject obj(cx);
+    RootedObject holder(cx);
+    Rooted<PropertyResult> prop(cx);
+    if (!LookupName(cx, name, envChain, &obj, &holder, &prop))
+        return false;
+
+    if (*GetNextPc(pc) == JSOP_TYPEOF) {
+        if (!FetchName<GetNameMode::TypeOf>(cx, obj, holder, name, prop, res))
+            return false;
+    } else {
+        if (!FetchName<GetNameMode::Normal>(cx, obj, holder, name, prop, res))
+            return false;
+    }
+
+    // No need to call TypeScript::Monitor, IonBuilder always inserts a type
+    // barrier after GetName ICs.
+
+    return true;
+}
+
 uint8_t*
 IonICStub::stubDataStart()
 {
     return reinterpret_cast<uint8_t*>(this) + stubInfo_->stubDataOffset();
 }
 
 void
 IonIC::attachStub(IonICStub* newStub, JitCode* code)
--- a/js/src/jit/IonIC.h
+++ b/js/src/jit/IonIC.h
@@ -53,16 +53,17 @@ class IonICStub
         nextCodeRaw_ = nullptr;
         next_ = nullptr;
         stubInfo_ = nullptr;
     }
 };
 
 class IonGetPropertyIC;
 class IonSetPropertyIC;
+class IonGetNameIC;
 
 class IonIC
 {
     // This either points at the OOL path for the fallback path, or the code for
     // the first stub.
     uint8_t* codeRaw_;
 
     // The first optimized stub, or nullptr.
@@ -134,16 +135,20 @@ class IonIC
     IonGetPropertyIC* asGetPropertyIC() {
         MOZ_ASSERT(kind_ == CacheKind::GetProp || kind_ == CacheKind::GetElem);
         return (IonGetPropertyIC*)this;
     }
     IonSetPropertyIC* asSetPropertyIC() {
         MOZ_ASSERT(kind_ == CacheKind::SetProp || kind_ == CacheKind::SetElem);
         return (IonSetPropertyIC*)this;
     }
+    IonGetNameIC* asGetNameIC() {
+        MOZ_ASSERT(kind_ == CacheKind::GetName);
+        return (IonGetNameIC*)this;
+    }
 
     void updateBaseAddress(JitCode* code, MacroAssembler& masm);
 
     // Returns the Register to use as scratch when entering IC stubs. This
     // should either be an output register or a temp.
     Register scratchRegisterForEntryJump();
 
     void trace(JSTracer* trc);
@@ -238,12 +243,39 @@ class IonSetPropertyIC : public IonIC
     bool needsPostBarrier() const { return needsPostBarrier_; }
     bool needsTypeBarrier() const { return needsTypeBarrier_; }
     bool guardHoles() const { return guardHoles_; }
 
     static MOZ_MUST_USE bool update(JSContext* cx, HandleScript outerScript, IonSetPropertyIC* ic,
                                     HandleObject obj, HandleValue idVal, HandleValue rhs);
 };
 
+class IonGetNameIC : public IonIC
+{
+    LiveRegisterSet liveRegs_;
+
+    Register environment_;
+    ValueOperand output_;
+    Register temp_;
+
+  public:
+    IonGetNameIC(LiveRegisterSet liveRegs, Register environment, ValueOperand output,
+                 Register temp)
+      : IonIC(CacheKind::GetName),
+        liveRegs_(liveRegs),
+        environment_(environment),
+        output_(output),
+        temp_(temp)
+    { }
+
+    Register environment() const { return environment_; }
+    ValueOperand output() const { return output_; }
+    Register temp() const { return temp_; }
+    LiveRegisterSet liveRegs() const { return liveRegs_; }
+
+    static MOZ_MUST_USE bool update(JSContext* cx, HandleScript outerScript, IonGetNameIC* ic,
+                                    HandleObject envChain, MutableHandleValue res);
+};
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_IonIC_h */
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -3694,17 +3694,17 @@ LIRGenerator::visitGetNameCache(MGetName
 {
     MOZ_ASSERT(ins->envObj()->type() == MIRType::Object);
 
     // Set the performs-call flag so that we don't omit the overrecursed check.
     // This is necessary because the cache can attach a scripted getter stub
     // that calls this script recursively.
     gen->setPerformsCall();
 
-    LGetNameCache* lir = new(alloc()) LGetNameCache(useRegister(ins->envObj()));
+    LGetNameCache* lir = new(alloc()) LGetNameCache(useRegister(ins->envObj()), temp());
     defineBox(lir, ins);
     assignSafepoint(lir, ins);
 }
 
 void
 LIRGenerator::visitCallGetIntrinsicValue(MCallGetIntrinsicValue* ins)
 {
     LCallGetIntrinsicValue* lir = new(alloc()) LCallGetIntrinsicValue();
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -11687,48 +11687,27 @@ class MStoreSlot
 
     ALLOW_CLONE(MStoreSlot)
 };
 
 class MGetNameCache
   : public MUnaryInstruction,
     public SingleObjectPolicy::Data
 {
-  public:
-    enum AccessKind {
-        NAMETYPEOF,
-        NAME
-    };
-
   private:
-    CompilerPropertyName name_;
-    AccessKind kind_;
-
-    MGetNameCache(MDefinition* obj, PropertyName* name, AccessKind kind)
-      : MUnaryInstruction(obj),
-        name_(name),
-        kind_(kind)
+    explicit MGetNameCache(MDefinition* obj)
+      : MUnaryInstruction(obj)
     {
         setResultType(MIRType::Value);
     }
 
   public:
     INSTRUCTION_HEADER(GetNameCache)
     TRIVIAL_NEW_WRAPPERS
     NAMED_OPERANDS((0, envObj))
-
-    PropertyName* name() const {
-        return name_;
-    }
-    AccessKind accessKind() const {
-        return kind_;
-    }
-    bool appendRoots(MRootList& roots) const override {
-        return roots.append(name_);
-    }
 };
 
 class MCallGetIntrinsicValue : public MNullaryInstruction
 {
     CompilerPropertyName name_;
 
     explicit MCallGetIntrinsicValue(PropertyName* name)
       : name_(name)
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -6653,27 +6653,31 @@ class LStoreFixedSlotT : public LInstruc
         return getOperand(0);
     }
     const LAllocation* value() {
         return getOperand(1);
     }
 };
 
 // Note, Name ICs always return a Value. There are no V/T variants.
-class LGetNameCache : public LInstructionHelper<BOX_PIECES, 1, 0>
+class LGetNameCache : public LInstructionHelper<BOX_PIECES, 1, 1>
 {
   public:
     LIR_HEADER(GetNameCache)
 
-    explicit LGetNameCache(const LAllocation& envObj) {
+    LGetNameCache(const LAllocation& envObj, const LDefinition& temp) {
         setOperand(0, envObj);
+        setTemp(0, temp);
     }
     const LAllocation* envObj() {
         return getOperand(0);
     }
+    const LDefinition* temp() {
+        return getTemp(0);
+    }
     const MGetNameCache* mir() const {
         return mir_->toGetNameCache();
     }
 };
 
 class LCallGetIntrinsicValue : public LCallInstructionHelper<BOX_PIECES, 0, 0>
 {
   public:
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -8532,17 +8532,23 @@ LookupAsmJSModuleInCache(JSContext* cx, 
 
     // Due to the hash comparison made by openEntryForRead, this should succeed
     // with high probability.
     ModuleCharsForLookup moduleChars;
     cursor = moduleChars.deserialize(cursor);
     if (!moduleChars.match(parser))
         return true;
 
+    // Don't punish release users by crashing if there is a programmer error
+    // here, just gracefully return with a cache miss.
+#ifdef NIGHTLY_BUILD
     MOZ_RELEASE_ASSERT(cursor == entry.memory + entry.serializedSize);
+#endif
+    if (cursor != entry.memory + entry.serializedSize)
+        return true;
 
     // See AsmJSMetadata comment as well as ModuleValidator::init().
     asmJSMetadata->preludeStart = parser.pc->functionBox()->preludeStart;
     asmJSMetadata->srcStart = parser.pc->functionBox()->functionNode->pn_body->pn_pos.begin;
     asmJSMetadata->srcBodyStart = parser.tokenStream.currentToken().pos.end;
     asmJSMetadata->strict = parser.pc->sc()->strict() && !parser.pc->sc()->hasExplicitUseStrict();
     asmJSMetadata->scriptSource.reset(parser.ss);
 
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -4911,16 +4911,22 @@ pref("dom.w3c_touch_events.enabled", 2);
 
 // W3C draft pointer events
 #if defined(XP_WIN) && defined(NIGHTLY_BUILD)
 pref("dom.w3c_pointer_events.enabled", true);
 #else
 pref("dom.w3c_pointer_events.enabled", false);
 #endif
 
+// Control firing WidgetMouseEvent by handling Windows pointer messages or mouse
+// messages.
+#if defined(XP_WIN)
+pref("dom.w3c_pointer_events.dispatch_by_pointer_messages", false);
+#endif
+
 // W3C pointer events draft
 pref("dom.w3c_pointer_events.implicit_capture", false);
 
 // W3C draft ImageCapture API
 pref("dom.imagecapture.enabled", false);
 
 // W3C MediaDevices devicechange event
 pref("media.ondevicechange.enabled", true);
new file mode 100644
--- /dev/null
+++ b/other-licenses/moz.build
@@ -0,0 +1,32 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+with Files('**'):
+    BUG_COMPONENT = ('Core', 'General')
+
+with Files('7zstub/**'):
+    BUG_COMPONENT = ('Firefox', 'Installer')
+
+with Files('atk-1.0/**'):
+    BUG_COMPONENT = ('Core', 'Disability Access APIs')
+
+with Files('bsdiff/**'):
+    BUG_COMPONENT = ('Core', 'XPCOM')
+
+with Files('ia2/**'):
+    BUG_COMPONENT = ('Core', 'Disability Access APIs')
+
+with Files('nsis/**'):
+    BUG_COMPONENT = ('Firefox', 'Installer')
+
+with Files('ply/**'):
+    BUG_COMPONENT = ('Core', 'JavaScript Engine')
+
+with Files('skia-npapi/**'):
+    BUG_COMPONENT = ('Core', 'Graphics')
+
+with Files('snappy/**'):
+    BUG_COMPONENT = ('Core', 'XPCOM')
--- a/security/sandbox/test/browser_content_sandbox_fs.js
+++ b/security/sandbox/test/browser_content_sandbox_fs.js
@@ -192,26 +192,20 @@ function* createFileInHome() {
   }
 }
 
 // Test if the content process can create a temp file, should pass
 function* createTempFile() {
   let browser = gBrowser.selectedBrowser;
   let path = fileInTempDir().path;
   let fileCreated = yield ContentTask.spawn(browser, path, createFile);
-  if (!fileCreated && isWin()) {
-    // TODO: fix 1329294 and enable this test for Windows.
-    // Not using todo() because this only fails on automation.
-    info("ignoring failure to write to content temp due to 1329294\n");
-  } else {
-    ok(fileCreated == true, "creating a file in content temp is permitted");
-    // now delete the file
-    let fileDeleted = yield ContentTask.spawn(browser, path, deleteFile);
-    ok(fileDeleted == true, "deleting a file in content temp is permitted");
-  }
+  ok(fileCreated == true, "creating a file in content temp is permitted");
+  // now delete the file
+  let fileDeleted = yield ContentTask.spawn(browser, path, deleteFile);
+  ok(fileDeleted == true, "deleting a file in content temp is permitted");
 }
 
 // Test reading files and dirs from web and file content processes.
 function* testFileAccess() {
   // for tests that run in a web content process
   let webBrowser = gBrowser.selectedBrowser;
 
   // For now, we'll only test file access from the file content process if
--- a/taskcluster/ci/test/tests.yml
+++ b/taskcluster/ci/test/tests.yml
@@ -526,17 +526,16 @@ mochitest-browser-chrome:
 mochitest-browser-screenshots:
     description: "Mochitest Browser Screenshots"
     suite: mochitest/browser-chrome-screenshots
     treeherder-symbol: tc-M(ss)
     loopback-video: true
     run-on-projects:
         by-test-platform:
             linux64/opt: ['mozilla-central', 'try']
-            linux64-nightly/opt: ['mozilla-central', 'try']
             default: []
     e10s: true
     max-run-time: 3600
     mozharness:
         mochitest-flavor: browser
         script: desktop_unittest.py
         no-read-buildbot-config: true
         config:
--- a/toolkit/xre/nsXREDirProvider.cpp
+++ b/toolkit/xre/nsXREDirProvider.cpp
@@ -493,16 +493,20 @@ nsXREDirProvider::GetFile(const char* aP
     nsCOMPtr<nsIDirectoryServiceProvider> dirsvc(do_GetService("@mozilla.org/file/directory_service;1", &rv));
     if (NS_FAILED(rv))
       return rv;
     bool unused;
     rv = dirsvc->GetFile("XCurProcD", &unused, getter_AddRefs(file));
   }
 #if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
   else if (!strcmp(aProperty, NS_APP_CONTENT_PROCESS_TEMP_DIR)) {
+    // If we are in the parent and the sandbox is enabled then mContentTempDir
+    // must have been created and set before anything calls for it.
+    MOZ_ASSERT_IF(XRE_IsParentProcess() && !IsContentSandboxDisabled(),
+                  mContentTempDir);
     if (!mContentTempDir && NS_FAILED((rv = LoadContentProcessTempDir()))) {
       return rv;
     }
     rv = mContentTempDir->Clone(getter_AddRefs(file));
   }
 #endif // defined(XP_WIN) && defined(MOZ_CONTENT_SANDBOX)
   else if (NS_SUCCEEDED(GetProfileStartupDir(getter_AddRefs(file)))) {
     // We need to allow component, xpt, and chrome registration to
@@ -1141,16 +1145,25 @@ nsXREDirProvider::DoStartup()
         appStartup->RestartInSafeMode(nsIAppStartup::eForceQuit);
         return NS_OK;
       }
     }
 
     static const char16_t kStartup[] = {'s','t','a','r','t','u','p','\0'};
     obsSvc->NotifyObservers(nullptr, "profile-do-change", kStartup);
 
+#if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
+    // The parent is responsible for creating the sandbox temp dir. It needs to
+    // be called before it is used, which can happen later in this function.
+    if (XRE_IsParentProcess()) {
+      mContentProcessSandboxTempDir = CreateContentProcessSandboxTempDir();
+      mContentTempDir = mContentProcessSandboxTempDir;
+    }
+#endif
+
     // Init the Extension Manager
     nsCOMPtr<nsIObserver> em = do_GetService("@mozilla.org/addons/integration;1");
     if (em) {
       em->Observe(nullptr, "addons-startup", nullptr);
     } else {
       NS_WARNING("Failed to create Addons Manager.");
     }
 
@@ -1195,24 +1208,16 @@ nsXREDirProvider::DoStartup()
         ++count;
       }
 
       mozilla::Telemetry::Accumulate(mozilla::Telemetry::NUMBER_OF_PROFILES,
                                      count);
     }
 
     obsSvc->NotifyObservers(nullptr, "profile-initial-state", nullptr);
-
-#if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
-    // The parent is responsible for creating the sandbox temp dir
-    if (XRE_IsParentProcess()) {
-      mContentProcessSandboxTempDir = CreateContentProcessSandboxTempDir();
-      mContentTempDir = mContentProcessSandboxTempDir;
-    }
-#endif
   }
   return NS_OK;
 }
 
 void
 nsXREDirProvider::DoShutdown()
 {
   PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER);
--- a/tools/moz.build
+++ b/tools/moz.build
@@ -1,7 +1,40 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+with Files("**"):
+    BUG_COMPONENT = ("Core", "General")
+
+with Files("coverity/**"):
+    BUG_COMPONENT = ("Core", "Rewriting and Analysis")
+
+with Files("docs/**"):
+    BUG_COMPONENT = ("Core", "Build Config")
+
+with Files("lint/**"):
+    BUG_COMPONENT = ("Testing", "Lint")
+
+with Files("memory-profiler/**"):
+    BUG_COMPONENT = ("Core", "Gecko Profiler")
+
+with Files("mercurial/**"):
+    BUG_COMPONENT = ("Core", "Build Config")
+
+with Files("profiler/**"):
+    BUG_COMPONENT = ("Core", "Gecko Profiler")
+
+with Files("quitter/**"):
+    BUG_COMPONENT = ("Testing", "General")
+
+with Files("rb/**"):
+    BUG_COMPONENT = ("Core", "XPCOM")
+
+with Files("rewriting/**"):
+    BUG_COMPONENT = ("Core", "Rewriting and Analysis")
+
+with Files("update-packaging/**"):
+    BUG_COMPONENT = ("Release Engineering", "Other")
+
 SPHINX_TREES['lint'] = 'lint/docs'
--- a/widget/windows/WinPointerEvents.cpp
+++ b/widget/windows/WinPointerEvents.cpp
@@ -16,24 +16,28 @@ using namespace mozilla;
 using namespace mozilla::widget;
 
 const wchar_t WinPointerEvents::kPointerLibraryName[] =  L"user32.dll";
 HMODULE WinPointerEvents::sLibraryHandle = nullptr;
 WinPointerEvents::GetPointerTypePtr WinPointerEvents::getPointerType = nullptr;
 WinPointerEvents::GetPointerInfoPtr WinPointerEvents::getPointerInfo = nullptr;
 WinPointerEvents::GetPointerPenInfoPtr WinPointerEvents::getPointerPenInfo = nullptr;
 bool WinPointerEvents::sPointerEventEnabled = true;
+bool WinPointerEvents::sFirePointerEventsByWinPointerMessages = false;
 
 WinPointerEvents::WinPointerEvents()
 {
   InitLibrary();
   static bool addedPointerEventEnabled = false;
   if (!addedPointerEventEnabled) {
     Preferences::AddBoolVarCache(&sPointerEventEnabled,
                                  "dom.w3c_pointer_events.enabled", true);
+    Preferences::AddBoolVarCache(
+      &sFirePointerEventsByWinPointerMessages,
+      "dom.w3c_pointer_events.dispatch_by_pointer_messages", false);
     addedPointerEventEnabled = true;
   }
 }
 
 /* Load and shutdown */
 void
 WinPointerEvents::InitLibrary()
 {
@@ -62,24 +66,27 @@ WinPointerEvents::InitLibrary()
     getPointerType = nullptr;
     getPointerInfo = nullptr;
     getPointerPenInfo = nullptr;
     return;
   }
 }
 
 bool
-WinPointerEvents::ShouldFireCompatibilityMouseEventsForPen(WPARAM aWParam)
+WinPointerEvents::ShouldHandleWinPointerMessages(UINT aMsg, WPARAM aWParam)
 {
+  MOZ_ASSERT(aMsg == WM_POINTERDOWN || aMsg == WM_POINTERUP ||
+             aMsg == WM_POINTERUPDATE || aMsg == WM_POINTERLEAVE);
   if (!sLibraryHandle || !sPointerEventEnabled) {
-    // Firing mouse events by handling Windows WM_POINTER* when preference is on
-    // and the Windows platform supports PointerEvent related interfaces.
     return false;
   }
 
+  // We only handle WM_POINTER* when the input source is pen. This is because
+  // we need some information (e.g. tiltX, tiltY) which can't be retrieved by
+  // WM_*BUTTONDOWN.
   uint32_t pointerId = GetPointerId(aWParam);
   POINTER_INPUT_TYPE pointerType = PT_POINTER;
   if (!GetPointerType(pointerId, &pointerType)) {
     MOZ_ASSERT(false, "cannot find PointerType");
     return false;
   }
   return (pointerType == PT_PEN);
 }
@@ -89,16 +96,24 @@ WinPointerEvents::GetPointerType(uint32_
                                  POINTER_INPUT_TYPE *aPointerType)
 {
   if (!getPointerType) {
     return false;
   }
   return getPointerType(aPointerId, aPointerType);
 }
 
+POINTER_INPUT_TYPE
+WinPointerEvents::GetPointerType(uint32_t aPointerId)
+{
+  POINTER_INPUT_TYPE pointerType = PT_POINTER;
+  Unused << GetPointerType(aPointerId, &pointerType);
+  return pointerType;
+}
+
 bool
 WinPointerEvents::GetPointerInfo(uint32_t aPointerId,
                                  POINTER_INFO *aPointerInfo)
 {
   if (!getPointerInfo) {
     return false;
   }
   return getPointerInfo(aPointerId, aPointerInfo);
@@ -118,14 +133,88 @@ bool
 WinPointerEvents::ShouldEnableInkCollector()
 {
   // We need InkCollector on Win7. For Win8 or later, we handle WM_POINTER* for
   // pen.
   return !IsWin8OrLater();
 }
 
 bool
-WinPointerEvents::ShouldRollupOnPointerEvent(WPARAM aWParam)
+WinPointerEvents::ShouldRollupOnPointerEvent(UINT aMsg, WPARAM aWParam)
 {
+  MOZ_ASSERT(aMsg == WM_POINTERDOWN);
   // Only roll up popups when we handling WM_POINTER* to fire Gecko
   // WidgetMouseEvent and suppress Windows WM_*BUTTONDOWN.
-  return ShouldFireCompatibilityMouseEventsForPen(aWParam);
+  return ShouldHandleWinPointerMessages(aMsg, aWParam) &&
+         ShouldFirePointerEventByWinPointerMessages();
+}
+
+bool
+WinPointerEvents::ShouldFirePointerEventByWinPointerMessages()
+{
+  MOZ_ASSERT(sLibraryHandle && sPointerEventEnabled);
+  return sFirePointerEventsByWinPointerMessages;
+}
+
+WinPointerInfo*
+WinPointerEvents::GetCachedPointerInfo(UINT aMsg, WPARAM aWParam)
+{
+  if (!sLibraryHandle || !sPointerEventEnabled ||
+      MOUSE_INPUT_SOURCE() != nsIDOMMouseEvent::MOZ_SOURCE_PEN ||
+      ShouldFirePointerEventByWinPointerMessages()) {
+    return nullptr;
+  }
+  switch (aMsg) {
+  case WM_LBUTTONDOWN:
+  case WM_MBUTTONDOWN:
+  case WM_RBUTTONDOWN:
+    return &mPenPointerDownInfo;
+  case WM_LBUTTONUP:
+  case WM_MBUTTONUP:
+  case WM_RBUTTONUP:
+    return &mPenPointerDownInfo;
+  case WM_MOUSEMOVE:
+    return &mPenPointerUpdateInfo;
+  default:
+    MOZ_ASSERT(false);
+  }
+  return nullptr;
 }
+
+void
+WinPointerEvents::ConvertAndCachePointerInfo(UINT aMsg, WPARAM aWParam)
+{
+  MOZ_ASSERT(!sFirePointerEventsByWinPointerMessages);
+  // Windows doesn't support chorded buttons for pen, so we can simply keep the
+  // latest information from pen generated pointer messages and use them when
+  // handling mouse messages. Used different pointer info for pointerdown,
+  // pointerupdate, and pointerup because Windows doesn't always interleave
+  // pointer messages and mouse messages.
+  switch (aMsg) {
+  case WM_POINTERDOWN:
+    ConvertAndCachePointerInfo(aWParam, &mPenPointerDownInfo);
+    break;
+  case WM_POINTERUP:
+    ConvertAndCachePointerInfo(aWParam, &mPenPointerUpInfo);
+    break;
+  case WM_POINTERUPDATE:
+    ConvertAndCachePointerInfo(aWParam, &mPenPointerUpdateInfo);
+    break;
+  default:
+    break;
+  }
+}
+
+void
+WinPointerEvents::ConvertAndCachePointerInfo(WPARAM aWParam,
+                                             WinPointerInfo* aInfo)
+{
+  MOZ_ASSERT(!sFirePointerEventsByWinPointerMessages);
+  aInfo->pointerId = GetPointerId(aWParam);
+  MOZ_ASSERT(GetPointerType(aInfo->pointerId) == PT_PEN);
+  POINTER_PEN_INFO penInfo;
+  GetPointerPenInfo(aInfo->pointerId, &penInfo);
+  aInfo->tiltX = penInfo.tiltX;
+  aInfo->tiltY = penInfo.tiltY;
+  // Windows defines the pen pressure is normalized to a range between 0 and
+  // 1024. Convert it to float.
+  aInfo->mPressure = penInfo.pressure ? (float)penInfo.pressure / 1024 : 0;
+}
--- a/widget/windows/WinPointerEvents.h
+++ b/widget/windows/WinPointerEvents.h
@@ -99,16 +99,18 @@ typedef struct tagPOINTER_PEN_INFO {
  * This is a helper class to handle WM_POINTER*. It only supports Win8 or later.
  *
  ******************************************************************************/
 class WinPointerInfo final : public mozilla::WidgetPointerHelper
 {
 public:
   WinPointerInfo()
     : WidgetPointerHelper()
+    , mPressure(0)
+    , mButtons(0)
   {
   }
 
   WinPointerInfo(uint32_t aPointerId, uint32_t aTiltX, uint32_t aTiltY,
                  float aPressure, int16_t aButtons)
     : WidgetPointerHelper(aPointerId, aTiltX, aTiltY)
     , mPressure(aPressure)
     , mButtons(aButtons)
@@ -120,40 +122,50 @@ public:
 };
 
 class WinPointerEvents final
 {
 public:
   explicit WinPointerEvents();
 
 public:
-  bool ShouldFireCompatibilityMouseEventsForPen(WPARAM aWParam);
+  bool ShouldHandleWinPointerMessages(UINT aMsg, WPARAM aWParam);
 
   uint32_t GetPointerId(WPARAM aWParam)
   {
     return GET_POINTERID_WPARAM(aWParam);
   }
   bool GetPointerType(uint32_t aPointerId, POINTER_INPUT_TYPE *aPointerType);
+  POINTER_INPUT_TYPE GetPointerType(uint32_t aPointerId);
   bool GetPointerInfo(uint32_t aPointerId, POINTER_INFO *aPointerInfo);
   bool GetPointerPenInfo(uint32_t aPointerId, POINTER_PEN_INFO *aPenInfo);
   bool ShouldEnableInkCollector();
-  bool ShouldRollupOnPointerEvent(WPARAM aWParam);
+  bool ShouldRollupOnPointerEvent(UINT aMsg, WPARAM aWParam);
+  bool ShouldFirePointerEventByWinPointerMessages();
+  WinPointerInfo* GetCachedPointerInfo(UINT aMsg, WPARAM aWParam);
+  void ConvertAndCachePointerInfo(UINT aMsg, WPARAM aWParam);
+  void ConvertAndCachePointerInfo(WPARAM aWParam, WinPointerInfo* aInfo);
+
 private:
   // Function prototypes
   typedef BOOL (WINAPI* GetPointerTypePtr)(uint32_t aPointerId,
                                            POINTER_INPUT_TYPE *aPointerType);
   typedef BOOL (WINAPI* GetPointerInfoPtr)(uint32_t aPointerId,
                                            POINTER_INFO *aPointerInfo);
   typedef BOOL (WINAPI* GetPointerPenInfoPtr)(uint32_t aPointerId,
                                               POINTER_PEN_INFO *aPenInfo);
 
   void InitLibrary();
 
   static HMODULE sLibraryHandle;
   static const wchar_t kPointerLibraryName[];
   static bool sPointerEventEnabled;
+  static bool WinPointerEvents::sFirePointerEventsByWinPointerMessages;
   // Static function pointers
   static GetPointerTypePtr getPointerType;
   static GetPointerInfoPtr getPointerInfo;
   static GetPointerPenInfoPtr getPointerPenInfo;
+  WinPointerInfo mPenPointerDownInfo;
+  WinPointerInfo mPenPointerUpInfo;
+  WinPointerInfo mPenPointerUpdateInfo;
 };
 
 #endif // #ifndef WinPointerEvents_h__
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -5448,17 +5448,18 @@ nsWindow::ProcessMessage(UINT msg, WPARA
       mp.y      = GET_Y_LPARAM(lParamScreen);
       bool userMovedMouse = false;
       if ((sLastMouseMovePoint.x != mp.x) || (sLastMouseMovePoint.y != mp.y)) {
         userMovedMouse = true;
       }
 
       result = DispatchMouseEvent(eMouseMove, wParam, lParam,
                                   false, WidgetMouseEvent::eLeftButton,
-                                  MOUSE_INPUT_SOURCE());
+                                  MOUSE_INPUT_SOURCE(),
+                                  mPointerEvents.GetCachedPointerInfo(msg, wParam));
       if (userMovedMouse) {
         DispatchPendingEvents();
       }
     }
     break;
 
     case WM_NCMOUSEMOVE:
       // If we receive a mouse move event on non-client chrome, make sure and
@@ -5466,26 +5467,28 @@ nsWindow::ProcessMessage(UINT msg, WPARA
       if (mMousePresent && !sIsInMouseCapture)
         SendMessage(mWnd, WM_MOUSELEAVE, 0, 0);
     break;
 
     case WM_LBUTTONDOWN:
     {
       result = DispatchMouseEvent(eMouseDown, wParam, lParam,
                                   false, WidgetMouseEvent::eLeftButton,
-                                  MOUSE_INPUT_SOURCE());
+                                  MOUSE_INPUT_SOURCE(),
+                                  mPointerEvents.GetCachedPointerInfo(msg, wParam));
       DispatchPendingEvents();
     }
     break;
 
     case WM_LBUTTONUP:
     {
       result = DispatchMouseEvent(eMouseUp, wParam, lParam,
                                   false, WidgetMouseEvent::eLeftButton,
-                                  MOUSE_INPUT_SOURCE());
+                                  MOUSE_INPUT_SOURCE(),
+                                  mPointerEvents.GetCachedPointerInfo(msg, wParam));
       DispatchPendingEvents();
     }
     break;
 
     case WM_MOUSELEAVE:
     {
       if (!mMousePresent)
         break;
@@ -5626,25 +5629,27 @@ nsWindow::ProcessMessage(UINT msg, WPARA
                                   MOUSE_INPUT_SOURCE());
       DispatchPendingEvents();
       break;
 
     case WM_RBUTTONDOWN:
       result = DispatchMouseEvent(eMouseDown, wParam,
                                   lParam, false,
                                   WidgetMouseEvent::eRightButton,
-                                  MOUSE_INPUT_SOURCE());
+                                  MOUSE_INPUT_SOURCE(),
+                                  mPointerEvents.GetCachedPointerInfo(msg, wParam));
       DispatchPendingEvents();
       break;
 
     case WM_RBUTTONUP:
       result = DispatchMouseEvent(eMouseUp, wParam,
                                   lParam, false,
                                   WidgetMouseEvent::eRightButton,
-                                  MOUSE_INPUT_SOURCE());
+                                  MOUSE_INPUT_SOURCE(),
+                                  mPointerEvents.GetCachedPointerInfo(msg, wParam));
       DispatchPendingEvents();
       break;
 
     case WM_RBUTTONDBLCLK:
       result = DispatchMouseEvent(eMouseDoubleClick, wParam,
                                   lParam, false,
                                   WidgetMouseEvent::eRightButton,
                                   MOUSE_INPUT_SOURCE());
@@ -7790,17 +7795,17 @@ nsWindow::DealWithPopups(HWND aWnd, UINT
       if (!EventIsInsideWindow(popupWindow) &&
           GetPopupsToRollup(rollupListener, &popupsToRollup)) {
         break;
       }
       return false;
     case WM_POINTERDOWN:
       {
         WinPointerEvents pointerEvents;
-        if (!pointerEvents.ShouldRollupOnPointerEvent(aWParam)) {
+        if (!pointerEvents.ShouldRollupOnPointerEvent(nativeMessage, aWParam)) {
           return false;
         }
         if (!GetPopupsToRollup(rollupListener, &popupsToRollup)) {
           return false;
         }
         // Can't use EventIsInsideWindow to check whether the event is inside
         // the popup window. It's because EventIsInsideWindow gets message
         // coordinates by GetMessagePos, which returns physical screen
@@ -8206,24 +8211,28 @@ nsWindow::OnWindowedPluginKeyEvent(const
       // keydown or keyup event on the plugin is consumed.  It should be
       // managed in each plugin window rather than top level window.
       return NS_OK;
   }
 }
 
 bool nsWindow::OnPointerEvents(UINT msg, WPARAM aWParam, LPARAM aLParam)
 {
-  if (!mPointerEvents.ShouldFireCompatibilityMouseEventsForPen(aWParam)) {
-    // We only handle WM_POINTER* when the input source is pen. This is because
-    // we need some information (e.g. tiltX, tiltY) which can't be retrieved by
-    // WM_*BUTTONDOWN. So we fire Gecko WidgetMouseEvent when handling
-    // WM_POINTER* and consume WM_POINTER* to stop Windows fire WM_*BUTTONDOWN.
+  if (!mPointerEvents.ShouldHandleWinPointerMessages(msg, aWParam)) {
     return false;
   }
-
+  if (!mPointerEvents.ShouldFirePointerEventByWinPointerMessages()) {
+    // We have to handle WM_POINTER* to fetch and cache pen related information
+    // and fire WidgetMouseEvent with the cached information the WM_*BUTTONDOWN
+    // handler. This is because Windows doesn't support ::DoDragDrop in the touch
+    // or pen message handlers.
+    mPointerEvents.ConvertAndCachePointerInfo(msg, aWParam);
+    // Don't consume the Windows WM_POINTER* messages
+    return false;
+  }
   // When dispatching mouse events with pen, there may be some
   // WM_POINTERUPDATE messages between WM_POINTERDOWN and WM_POINTERUP with
   // small movements. Those events will reset sLastMousePoint and reset
   // sLastClickCount. To prevent that, we keep the last pen down position
   // and compare it with the subsequent WM_POINTERUPDATE. If the movement is
   // smaller than GetSystemMetrics(SM_CXDRAG), then we suppress firing
   // eMouseMove for WM_POINTERUPDATE.
   static POINT sLastPointerDownPoint = {0};