Merge autoland to mozilla-central. a=merge FIREFOX_NIGHTLY_71_END
authorarthur.iakab <aiakab@mozilla.com>
Mon, 21 Oct 2019 12:55:26 +0300
changeset 498398 d42c22627c8dee9e9286fd5d7bbc3d02afbfad44
parent 498397 b34beb21e1cf9d283ef0d855f68f63bcc15e40fb (current diff)
parent 498306 28171f948a0874143e59c9551712b8fa08e50f55 (diff)
child 498399 a62162b09b7a41dfd7163d066e7c87572d9c5b66
push id114157
push usernbeleuzu@mozilla.com
push dateMon, 21 Oct 2019 22:00:13 +0000
treeherdermozilla-inbound@563f437f24b9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone71.0a1
first release with
nightly linux32
d42c22627c8d / 71.0a1 / 20191021095628 / files
nightly linux64
d42c22627c8d / 71.0a1 / 20191021095628 / files
nightly mac
d42c22627c8d / 71.0a1 / 20191021095628 / files
nightly win32
d42c22627c8d / 71.0a1 / 20191021095628 / files
nightly win64
d42c22627c8d / 71.0a1 / 20191021095628 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge autoland to mozilla-central. a=merge
--- a/dom/base/BodyConsumer.cpp
+++ b/dom/base/BodyConsumer.cpp
@@ -235,29 +235,29 @@ class ConsumeBodyDoneObserver final : pu
     if (NS_WARN_IF(!r->Dispatch())) {
       return NS_ERROR_FAILURE;
     }
 
     // We haven't taken ownership of the data.
     return NS_OK;
   }
 
-  virtual void BlobStoreCompleted(MutableBlobStorage* aBlobStorage, Blob* aBlob,
-                                  nsresult aRv) override {
+  virtual void BlobStoreCompleted(MutableBlobStorage* aBlobStorage,
+                                  BlobImpl* aBlobImpl, nsresult aRv) override {
     // On error.
     if (NS_FAILED(aRv)) {
       OnStreamComplete(nullptr, nullptr, aRv, 0, nullptr);
       return;
     }
 
     // The loading is completed. Let's nullify the pump before continuing the
     // consuming of the body.
     mBodyConsumer->NullifyConsumeBodyPump();
 
-    mBodyConsumer->OnBlobResult(aBlob, mWorkerRef);
+    mBodyConsumer->OnBlobResult(aBlobImpl, mWorkerRef);
   }
 
  private:
   ~ConsumeBodyDoneObserver() = default;
 
   RefPtr<BodyConsumer> mBodyConsumer;
   RefPtr<ThreadSafeWorkerRef> mWorkerRef;
 };
@@ -401,17 +401,17 @@ class FileCreationHandler final : public
     }
 
     RefPtr<Blob> blob;
     if (NS_WARN_IF(NS_FAILED(UNWRAP_OBJECT(Blob, &aValue.toObject(), blob)))) {
       mConsumer->OnBlobResult(nullptr, mWorkerRef);
       return;
     }
 
-    mConsumer->OnBlobResult(blob, mWorkerRef);
+    mConsumer->OnBlobResult(blob->Impl(), mWorkerRef);
   }
 
   void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override {
     AssertIsOnMainThread();
 
     mConsumer->OnBlobResult(nullptr, mWorkerRef);
   }
 
@@ -527,18 +527,18 @@ void BodyConsumer::BeginConsumeBodyMainT
     return;
   }
 
   RefPtr<ConsumeBodyDoneObserver> p =
       new ConsumeBodyDoneObserver(this, aWorkerRef);
 
   nsCOMPtr<nsIStreamListener> listener;
   if (mConsumeType == CONSUME_BLOB) {
-    listener = new MutableBlobStreamListener(
-        mBlobStorageType, nullptr, mBodyMimeType, p, mMainThreadEventTarget);
+    listener = new MutableBlobStreamListener(mBlobStorageType, mBodyMimeType, p,
+                                             mMainThreadEventTarget);
   } else {
     nsCOMPtr<nsIStreamLoader> loader;
     rv = NS_NewStreamLoader(getter_AddRefs(loader), p);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return;
     }
 
     listener = loader;
@@ -569,20 +569,21 @@ void BodyConsumer::BeginConsumeBodyMainT
 }
 
 /*
  * OnBlobResult() is called when a blob body is ready to be consumed (when its
  * network transfer completes in BeginConsumeBodyRunnable or its local File has
  * been wrapped by FileCreationHandler). The blob is sent to the target thread
  * and ContinueConsumeBody is called.
  */
-void BodyConsumer::OnBlobResult(Blob* aBlob, ThreadSafeWorkerRef* aWorkerRef) {
+void BodyConsumer::OnBlobResult(BlobImpl* aBlobImpl,
+                                ThreadSafeWorkerRef* aWorkerRef) {
   AssertIsOnMainThread();
 
-  DispatchContinueConsumeBlobBody(aBlob ? aBlob->Impl() : nullptr, aWorkerRef);
+  DispatchContinueConsumeBlobBody(aBlobImpl, aWorkerRef);
 }
 
 void BodyConsumer::DispatchContinueConsumeBlobBody(
     BlobImpl* aBlobImpl, ThreadSafeWorkerRef* aWorkerRef) {
   AssertIsOnMainThread();
 
   // Main-thread.
   if (!aWorkerRef) {
@@ -749,17 +750,20 @@ void BodyConsumer::ContinueConsumeBlobBo
   }
   mBodyConsumed = true;
 
   MOZ_ASSERT(mConsumePromise);
   RefPtr<Promise> localPromise = mConsumePromise.forget();
 
   if (!aShuttingDown) {
     RefPtr<dom::Blob> blob = dom::Blob::Create(mGlobal, aBlobImpl);
-    MOZ_ASSERT(blob);
+    if (NS_WARN_IF(!blob)) {
+      localPromise->MaybeReject(NS_ERROR_FAILURE);
+      return;
+    }
 
     localPromise->MaybeResolve(blob);
   }
 
   ReleaseObject();
 }
 
 void BodyConsumer::ShutDownMainThreadConsuming() {
--- a/dom/base/BodyConsumer.h
+++ b/dom/base/BodyConsumer.h
@@ -65,17 +65,18 @@ class BodyConsumer final : public nsIObs
       const nsAString& aBodyLocalPath, const nsACString& aBodyMimeType,
       MutableBlobStorage::MutableBlobStorageType aBlobStorageType,
       ErrorResult& aRv);
 
   void ReleaseObject();
 
   void BeginConsumeBodyMainThread(ThreadSafeWorkerRef* aWorkerRef);
 
-  void OnBlobResult(Blob* aBlob, ThreadSafeWorkerRef* aWorkerRef = nullptr);
+  void OnBlobResult(BlobImpl* aBlobImpl,
+                    ThreadSafeWorkerRef* aWorkerRef = nullptr);
 
   void ContinueConsumeBody(nsresult aStatus, uint32_t aLength, uint8_t* aResult,
                            bool aShuttingDown = false);
 
   void ContinueConsumeBlobBody(BlobImpl* aBlobImpl, bool aShuttingDown = false);
 
   void DispatchContinueConsumeBlobBody(BlobImpl* aBlobImpl,
                                        ThreadSafeWorkerRef* aWorkerRef);
--- a/dom/base/BodyUtil.cpp
+++ b/dom/base/BodyUtil.cpp
@@ -251,16 +251,20 @@ class MOZ_STACK_CLASS FormDataParser {
         *p++ = *bodyIter++;
       }
       p = nullptr;
 
       RefPtr<Blob> file = File::CreateMemoryFile(
           mParentObject, reinterpret_cast<void*>(copy), body.Length(),
           NS_ConvertUTF8toUTF16(mFilename), NS_ConvertUTF8toUTF16(mContentType),
           /* aLastModifiedDate */ 0);
+      if (NS_WARN_IF(!file)) {
+        return false;
+      }
+
       Optional<nsAString> dummy;
       ErrorResult rv;
       mFormData->Append(name, *file, dummy, rv);
       if (NS_WARN_IF(rv.Failed())) {
         rv.SuppressException();
         return false;
       }
     }
@@ -393,17 +397,17 @@ void BodyUtil::ConsumeArrayBuffer(JSCont
     JS_ClearPendingException(aCx);
     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return;
   }
   aValue.set(arrayBuffer);
 }
 
 // static
-already_AddRefed<Blob> BodyUtil::ConsumeBlob(nsISupports* aParent,
+already_AddRefed<Blob> BodyUtil::ConsumeBlob(nsIGlobalObject* aParent,
                                              const nsString& aMimeType,
                                              uint32_t aInputLength,
                                              uint8_t* aInput,
                                              ErrorResult& aRv) {
   RefPtr<Blob> blob = Blob::CreateMemoryBlob(
       aParent, reinterpret_cast<void*>(aInput), aInputLength, aMimeType);
 
   if (!blob) {
--- a/dom/base/BodyUtil.h
+++ b/dom/base/BodyUtil.h
@@ -31,17 +31,17 @@ class BodyUtil final {
                                  JS::MutableHandle<JSObject*> aValue,
                                  uint32_t aInputLength, uint8_t* aInput,
                                  ErrorResult& aRv);
 
   /**
    * Creates an in-memory blob from an array. The blob takes ownership of
    * |aInput|, which must be allocated by |malloc|.
    */
-  static already_AddRefed<Blob> ConsumeBlob(nsISupports* aParent,
+  static already_AddRefed<Blob> ConsumeBlob(nsIGlobalObject* aParent,
                                             const nsString& aMimeType,
                                             uint32_t aInputLength,
                                             uint8_t* aInput, ErrorResult& aRv);
 
   /**
    * Creates a form data object from a UTF-8 encoded |aStr|. Returns |nullptr|
    * and sets |aRv| to MSG_BAD_FORMDATA if |aStr| contains invalid data.
    */
--- a/dom/base/ImageEncoder.cpp
+++ b/dom/base/ImageEncoder.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ImageEncoder.h"
 #include "mozilla/dom/CanvasRenderingContext2D.h"
+#include "mozilla/dom/MemoryBlobImpl.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/DataSurfaceHelpers.h"
 #include "mozilla/layers/AsyncCanvasRenderer.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/SyncRunnable.h"
 #include "mozilla/Unused.h"
 #include "gfxUtils.h"
@@ -95,24 +96,20 @@ class EncodingCompleteEvent : public Can
   // bug 1535398.
   MOZ_CAN_RUN_SCRIPT_BOUNDARY
   NS_IMETHOD Run() override {
     nsresult rv = NS_OK;
 
     // We want to null out mEncodeCompleteCallback no matter what.
     RefPtr<EncodeCompleteCallback> callback(mEncodeCompleteCallback.forget());
     if (!mFailed) {
-      // The correct parentObject has to be set by the mEncodeCompleteCallback.
-      RefPtr<Blob> blob =
-          Blob::CreateMemoryBlob(nullptr, mImgData, mImgSize, mType);
-      MOZ_ASSERT(blob);
-
-      rv = callback->ReceiveBlob(blob.forget());
+      RefPtr<BlobImpl> blobImpl = new MemoryBlobImpl(mImgData, mImgSize, mType);
+      rv = callback->ReceiveBlobImpl(blobImpl.forget());
     } else {
-      rv = callback->ReceiveBlob(nullptr);
+      rv = callback->ReceiveBlobImpl(nullptr);
     }
 
     return rv;
   }
 
   void SetMembers(void* aImgData, uint64_t aImgSize,
                   const nsAutoString& aType) {
     mImgData = aImgData;
--- a/dom/base/ImageEncoder.h
+++ b/dom/base/ImageEncoder.h
@@ -97,25 +97,25 @@ class ImageEncoder {
   static already_AddRefed<imgIEncoder> GetImageEncoder(nsAString& aType);
 
   friend class EncodingRunnable;
   friend class EncoderThreadPoolTerminator;
 };
 
 /**
  *  The callback interface of ExtractDataAsync and
- * ExtractDataFromLayersImageAsync. ReceiveBlob() is called on main thread when
- * encoding is complete.
+ * ExtractDataFromLayersImageAsync. ReceiveBlobImpl() is called on main thread
+ * when encoding is complete.
  */
 class EncodeCompleteCallback {
  public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(EncodeCompleteCallback)
 
   MOZ_CAN_RUN_SCRIPT
-  virtual nsresult ReceiveBlob(already_AddRefed<Blob> aBlob) = 0;
+  virtual nsresult ReceiveBlobImpl(already_AddRefed<BlobImpl> aBlobImpl) = 0;
 
  protected:
   virtual ~EncodeCompleteCallback() {}
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
--- a/dom/base/PostMessageEvent.cpp
+++ b/dom/base/PostMessageEvent.cpp
@@ -159,17 +159,17 @@ PostMessageEvent::Run() {
 
   IgnoredErrorResult rv;
   JS::Rooted<JS::Value> messageData(cx);
   nsCOMPtr<mozilla::dom::EventTarget> eventTarget =
       do_QueryObject(targetWindow);
 
   StructuredCloneHolder* holder;
   if (mHolder.constructed<StructuredCloneHolder>()) {
-    mHolder.ref<StructuredCloneHolder>().Read(ToSupports(targetWindow), cx,
+    mHolder.ref<StructuredCloneHolder>().Read(targetWindow->AsGlobal(), cx,
                                               &messageData, rv);
     holder = &mHolder.ref<StructuredCloneHolder>();
   } else {
     MOZ_ASSERT(mHolder.constructed<ipc::StructuredCloneData>());
     mHolder.ref<ipc::StructuredCloneData>().Read(cx, &messageData, rv);
     holder = &mHolder.ref<ipc::StructuredCloneData>();
   }
   if (NS_WARN_IF(rv.Failed())) {
--- a/dom/base/StructuredCloneHolder.cpp
+++ b/dom/base/StructuredCloneHolder.cpp
@@ -228,17 +228,17 @@ bool StructuredCloneHolderBase::CustomCa
 // StructuredCloneHolder class
 
 StructuredCloneHolder::StructuredCloneHolder(
     CloningSupport aSupportsCloning, TransferringSupport aSupportsTransferring,
     StructuredCloneScope aScope)
     : StructuredCloneHolderBase(aScope),
       mSupportsCloning(aSupportsCloning == CloningSupported),
       mSupportsTransferring(aSupportsTransferring == TransferringSupported),
-      mParent(nullptr)
+      mGlobal(nullptr)
 #ifdef DEBUG
       ,
       mCreationEventTarget(GetCurrentThreadEventTarget())
 #endif
 {
 }
 
 StructuredCloneHolder::~StructuredCloneHolder() {
@@ -261,26 +261,26 @@ void StructuredCloneHolder::Write(JSCont
 
   if (!StructuredCloneHolderBase::Write(aCx, aValue, aTransfer,
                                         cloneDataPolicy)) {
     aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
     return;
   }
 }
 
-void StructuredCloneHolder::Read(nsISupports* aParent, JSContext* aCx,
+void StructuredCloneHolder::Read(nsIGlobalObject* aGlobal, JSContext* aCx,
                                  JS::MutableHandle<JS::Value> aValue,
                                  ErrorResult& aRv) {
   MOZ_ASSERT_IF(
       mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread,
       mCreationEventTarget->IsOnCurrentThread());
-  MOZ_ASSERT(aParent);
+  MOZ_ASSERT(aGlobal);
 
-  mozilla::AutoRestore<nsISupports*> guard(mParent);
-  mParent = aParent;
+  mozilla::AutoRestore<nsIGlobalObject*> guard(mGlobal);
+  mGlobal = aGlobal;
 
   if (!StructuredCloneHolderBase::Read(aCx, aValue)) {
     JS_ClearPendingException(aCx);
     aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
     return;
   }
 
   // If we are tranferring something, we cannot call 'Read()' more than once.
@@ -288,37 +288,39 @@ void StructuredCloneHolder::Read(nsISupp
     mBlobImplArray.Clear();
     mWasmModuleArray.Clear();
     mClonedSurfaces.Clear();
     mInputStreamArray.Clear();
     Clear();
   }
 }
 
-void StructuredCloneHolder::ReadFromBuffer(nsISupports* aParent, JSContext* aCx,
+void StructuredCloneHolder::ReadFromBuffer(nsIGlobalObject* aGlobal,
+                                           JSContext* aCx,
                                            JSStructuredCloneData& aBuffer,
                                            JS::MutableHandle<JS::Value> aValue,
                                            ErrorResult& aRv) {
-  ReadFromBuffer(aParent, aCx, aBuffer, JS_STRUCTURED_CLONE_VERSION, aValue,
+  ReadFromBuffer(aGlobal, aCx, aBuffer, JS_STRUCTURED_CLONE_VERSION, aValue,
                  aRv);
 }
 
-void StructuredCloneHolder::ReadFromBuffer(nsISupports* aParent, JSContext* aCx,
+void StructuredCloneHolder::ReadFromBuffer(nsIGlobalObject* aGlobal,
+                                           JSContext* aCx,
                                            JSStructuredCloneData& aBuffer,
                                            uint32_t aAlgorithmVersion,
                                            JS::MutableHandle<JS::Value> aValue,
                                            ErrorResult& aRv) {
   MOZ_ASSERT_IF(
       mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread,
       mCreationEventTarget->IsOnCurrentThread());
 
   MOZ_ASSERT(!mBuffer, "ReadFromBuffer() must be called without a Write().");
 
-  mozilla::AutoRestore<nsISupports*> guard(mParent);
-  mParent = aParent;
+  mozilla::AutoRestore<nsIGlobalObject*> guard(mGlobal);
+  mGlobal = aGlobal;
 
   if (!JS_ReadStructuredClone(aCx, aBuffer, aAlgorithmVersion,
                               mStructuredCloneScope, aValue, &sCallbacks,
                               this)) {
     JS_ClearPendingException(aCx);
     aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
   }
 }
@@ -348,20 +350,20 @@ JSObject* StructuredCloneHolder::ReadFul
     }
 
     JS::RootedValue result(aCx);
     {
       // nsJSPrincipals::ReadKnownPrincipalType addrefs for us, but because of
       // the casting between JSPrincipals* and nsIPrincipal* we can't use
       // getter_AddRefs above and have to already_AddRefed here.
       nsCOMPtr<nsIPrincipal> principal =
-        already_AddRefed<nsIPrincipal>(nsJSPrincipals::get(prin));
+          already_AddRefed<nsIPrincipal>(nsJSPrincipals::get(prin));
 
       nsresult rv = nsContentUtils::WrapNative(
-        aCx, principal, &NS_GET_IID(nsIPrincipal), &result);
+          aCx, principal, &NS_GET_IID(nsIPrincipal), &result);
       if (NS_FAILED(rv)) {
         xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
         return nullptr;
       }
     }
     return result.toObjectOrNull();
   }
 
@@ -444,17 +446,21 @@ JSObject* ReadBlob(JSContext* aCx, uint3
     // toObject() is called because the static analysis thinks releasing XPCOM
     // objects can GC (because in some cases it can!), and a return statement
     // with a JSObject* type means that JSObject* is on the stack as a raw
     // pointer while destructors are running.
     RefPtr<BlobImpl> blobImpl = aHolder->BlobImpls()[aIndex];
 
     MOZ_ALWAYS_SUCCEEDS(blobImpl->SetMutable(false));
 
-    RefPtr<Blob> blob = Blob::Create(aHolder->ParentDuringRead(), blobImpl);
+    RefPtr<Blob> blob = Blob::Create(aHolder->GlobalDuringRead(), blobImpl);
+    if (NS_WARN_IF(!blob)) {
+      return nullptr;
+    }
+
     if (!ToJSValue(aCx, blob, &val)) {
       return nullptr;
     }
   }
 
   return &val.toObject();
 }
 
@@ -516,17 +522,17 @@ already_AddRefed<Directory> ReadDirector
 
   nsCOMPtr<nsIFile> file;
   nsresult rv = NS_NewLocalFile(path, true, getter_AddRefs(file));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return nullptr;
   }
 
   RefPtr<Directory> directory =
-      Directory::Create(aHolder->ParentDuringRead(), file);
+      Directory::Create(aHolder->GlobalDuringRead(), file);
   return directory.forget();
 }
 
 JSObject* ReadDirectory(JSContext* aCx, JSStructuredCloneReader* aReader,
                         uint32_t aPathLength, StructuredCloneHolder* aHolder) {
   MOZ_ASSERT(aCx);
   MOZ_ASSERT(aReader);
   MOZ_ASSERT(aHolder);
@@ -555,17 +561,17 @@ JSObject* ReadDirectory(JSContext* aCx, 
 // Read the WriteFileList for the format.
 JSObject* ReadFileList(JSContext* aCx, JSStructuredCloneReader* aReader,
                        uint32_t aCount, StructuredCloneHolder* aHolder) {
   MOZ_ASSERT(aCx);
   MOZ_ASSERT(aReader);
 
   JS::Rooted<JS::Value> val(aCx);
   {
-    RefPtr<FileList> fileList = new FileList(aHolder->ParentDuringRead());
+    RefPtr<FileList> fileList = new FileList(aHolder->GlobalDuringRead());
 
     uint32_t zero, index;
     // |index| is the index of the first blobImpl.
     if (!JS_ReadUint32Pair(aReader, &zero, &index)) {
       return nullptr;
     }
 
     MOZ_ASSERT(zero == 0);
@@ -580,17 +586,21 @@ JSObject* ReadFileList(JSContext* aCx, J
 #endif
       MOZ_ASSERT(pos < aHolder->BlobImpls().Length());
 
       RefPtr<BlobImpl> blobImpl = aHolder->BlobImpls()[pos];
       MOZ_ASSERT(blobImpl->IsFile());
 
       MOZ_ALWAYS_SUCCEEDS(blobImpl->SetMutable(false));
 
-      RefPtr<File> file = File::Create(aHolder->ParentDuringRead(), blobImpl);
+      RefPtr<File> file = File::Create(aHolder->GlobalDuringRead(), blobImpl);
+      if (NS_WARN_IF(!file)) {
+        return nullptr;
+      }
+
       if (!fileList->Append(file)) {
         return nullptr;
       }
     }
 
     if (!ToJSValue(aCx, fileList, &val)) {
       return nullptr;
     }
@@ -633,17 +643,17 @@ JSObject* ReadFormData(JSContext* aCx, J
                        uint32_t aCount, StructuredCloneHolder* aHolder) {
   MOZ_ASSERT(aCx);
   MOZ_ASSERT(aReader);
   MOZ_ASSERT(aHolder);
 
   // See the serialization of the FormData for the format.
   JS::Rooted<JS::Value> val(aCx);
   {
-    RefPtr<FormData> formData = new FormData(aHolder->ParentDuringRead());
+    RefPtr<FormData> formData = new FormData(aHolder->GlobalDuringRead());
 
     Optional<nsAString> thirdArg;
     for (uint32_t i = 0; i < aCount; ++i) {
       nsAutoString name;
       if (!StructuredCloneHolder::ReadString(aReader, name)) {
         return nullptr;
       }
 
@@ -658,18 +668,20 @@ JSObject* ReadFormData(JSContext* aCx, J
           return nullptr;
         }
 #endif
         MOZ_ASSERT(indexOrLengthOfString < aHolder->BlobImpls().Length());
 
         RefPtr<BlobImpl> blobImpl = aHolder->BlobImpls()[indexOrLengthOfString];
         MOZ_ALWAYS_SUCCEEDS(blobImpl->SetMutable(false));
 
-        RefPtr<Blob> blob = Blob::Create(aHolder->ParentDuringRead(), blobImpl);
-        MOZ_ASSERT(blob);
+        RefPtr<Blob> blob = Blob::Create(aHolder->GlobalDuringRead(), blobImpl);
+        if (NS_WARN_IF(!blob)) {
+          return nullptr;
+        }
 
         ErrorResult rv;
         formData->Append(name, *blob, thirdArg, rv);
         if (NS_WARN_IF(rv.Failed())) {
           rv.SuppressException();
           return nullptr;
         }
 
@@ -833,17 +845,17 @@ JSObject* ReadInputStream(JSContext* aCx
   }
 #endif
   MOZ_ASSERT(aIndex < aHolder->InputStreams().Length());
   JS::RootedValue result(aCx);
   {
     nsCOMPtr<nsIInputStream> inputStream = aHolder->InputStreams()[aIndex];
 
     nsresult rv = nsContentUtils::WrapNative(
-      aCx, inputStream, &NS_GET_IID(nsIInputStream), &result);
+        aCx, inputStream, &NS_GET_IID(nsIInputStream), &result);
     if (NS_FAILED(rv)) {
       return nullptr;
     }
   }
 
   return &result.toObject();
 }
 
@@ -890,19 +902,18 @@ JSObject* StructuredCloneHolder::CustomR
   if (aTag == SCTAG_DOM_IMAGEBITMAP &&
       (mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
        mStructuredCloneScope ==
            StructuredCloneScope::SameProcessDifferentThread)) {
     // Get the current global object.
     // This can be null.
     JS::RootedObject result(aCx);
     {
-      nsCOMPtr<nsIGlobalObject> parent = do_QueryInterface(mParent);
       // aIndex is the index of the cloned image.
-      result = ImageBitmap::ReadStructuredClone(aCx, aReader, parent,
+      result = ImageBitmap::ReadStructuredClone(aCx, aReader, mGlobal,
                                                 GetSurfaces(), aIndex);
     }
     return result;
   }
 
   if (aTag == SCTAG_DOM_STRUCTURED_CLONE_HOLDER) {
     return StructuredCloneBlob::ReadStructuredClone(aCx, aReader, this);
   }
@@ -1027,20 +1038,18 @@ bool StructuredCloneHolder::CustomReadTr
 #ifdef FUZZING
     if (aExtraData >= mPortIdentifiers.Length()) {
       return false;
     }
 #endif
     MOZ_ASSERT(aExtraData < mPortIdentifiers.Length());
     const MessagePortIdentifier& portIdentifier = mPortIdentifiers[aExtraData];
 
-    nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(mParent);
-
     ErrorResult rv;
-    RefPtr<MessagePort> port = MessagePort::Create(global, portIdentifier, rv);
+    RefPtr<MessagePort> port = MessagePort::Create(mGlobal, portIdentifier, rv);
     if (NS_WARN_IF(rv.Failed())) {
       rv.SuppressException();
       return false;
     }
 
     mTransferredPorts.AppendElement(port);
 
     JS::Rooted<JS::Value> value(aCx);
@@ -1055,19 +1064,18 @@ bool StructuredCloneHolder::CustomReadTr
 
   if (aTag == SCTAG_DOM_CANVAS &&
       (mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
        mStructuredCloneScope ==
            StructuredCloneScope::SameProcessDifferentThread)) {
     MOZ_ASSERT(aContent);
     OffscreenCanvasCloneData* data =
         static_cast<OffscreenCanvasCloneData*>(aContent);
-    nsCOMPtr<nsIGlobalObject> parent = do_QueryInterface(mParent);
     RefPtr<OffscreenCanvas> canvas =
-        OffscreenCanvas::CreateFromCloneData(parent, data);
+        OffscreenCanvas::CreateFromCloneData(mGlobal, data);
     delete data;
 
     JS::Rooted<JS::Value> value(aCx);
     if (!GetOrCreateDOMReflector(aCx, canvas, &value)) {
       JS_ClearPendingException(aCx);
       return false;
     }
 
@@ -1076,18 +1084,18 @@ bool StructuredCloneHolder::CustomReadTr
   }
 
   if (aTag == SCTAG_DOM_IMAGEBITMAP &&
       (mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread ||
        mStructuredCloneScope ==
            StructuredCloneScope::SameProcessDifferentThread)) {
     MOZ_ASSERT(aContent);
     ImageBitmapCloneData* data = static_cast<ImageBitmapCloneData*>(aContent);
-    nsCOMPtr<nsIGlobalObject> parent = do_QueryInterface(mParent);
-    RefPtr<ImageBitmap> bitmap = ImageBitmap::CreateFromCloneData(parent, data);
+    RefPtr<ImageBitmap> bitmap =
+        ImageBitmap::CreateFromCloneData(mGlobal, data);
     delete data;
 
     JS::Rooted<JS::Value> value(aCx);
     if (!GetOrCreateDOMReflector(aCx, bitmap, &value)) {
       JS_ClearPendingException(aCx);
       return false;
     }
 
--- a/dom/base/StructuredCloneHolder.h
+++ b/dom/base/StructuredCloneHolder.h
@@ -8,23 +8,23 @@
 #define mozilla_dom_StructuredCloneHolder_h
 
 #include "jsapi.h"
 #include "js/StructuredClone.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Move.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/dom/BindingDeclarations.h"
-#include "nsISupports.h"
 #include "nsTArray.h"
 
 #ifdef DEBUG
 #  include "nsIThread.h"
 #endif
 
+class nsIGlobalObject;
 class nsIInputStream;
 
 namespace mozilla {
 class ErrorResult;
 namespace layers {
 class Image;
 }
 
@@ -157,17 +157,17 @@ class StructuredCloneHolder : public Str
   // Normally you should just use Write() and Read().
 
   void Write(JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv);
 
   void Write(JSContext* aCx, JS::Handle<JS::Value> aValue,
              JS::Handle<JS::Value> aTransfer,
              JS::CloneDataPolicy cloneDataPolicy, ErrorResult& aRv);
 
-  void Read(nsISupports* aParent, JSContext* aCx,
+  void Read(nsIGlobalObject* aGlobal, JSContext* aCx,
             JS::MutableHandle<JS::Value> aValue, ErrorResult& aRv);
 
   // Call this method to know if this object is keeping some DOM object alive.
   bool HasClonedDOMObjects() const {
     return !mBlobImplArray.IsEmpty() || !mWasmModuleArray.IsEmpty() ||
            !mClonedSurfaces.IsEmpty() || !mInputStreamArray.IsEmpty();
   }
 
@@ -186,19 +186,19 @@ class StructuredCloneHolder : public Str
   nsTArray<nsCOMPtr<nsIInputStream>>& InputStreams() {
     MOZ_ASSERT(mSupportsCloning,
                "InputStreams cannot be taken/set if cloning is not supported.");
     return mInputStreamArray;
   }
 
   StructuredCloneScope CloneScope() const { return mStructuredCloneScope; }
 
-  // The parent object is set internally just during the Read(). This method
+  // The global object is set internally just during the Read(). This method
   // can be used by read functions to retrieve it.
-  nsISupports* ParentDuringRead() const { return mParent; }
+  nsIGlobalObject* GlobalDuringRead() const { return mGlobal; }
 
   // This must be called if the transferring has ports generated by Read().
   // MessagePorts are not thread-safe and they must be retrieved in the thread
   // where they are created.
   nsTArray<RefPtr<MessagePort>>&& TakeTransferredPorts() {
     MOZ_ASSERT(mSupportsTransferring);
     return std::move(mTransferredPorts);
   }
@@ -265,21 +265,21 @@ class StructuredCloneHolder : public Str
                           const nsString& aString);
 
   static const JSStructuredCloneCallbacks sCallbacks;
 
  protected:
   // If you receive a buffer from IPC, you can use this method to retrieve a
   // JS::Value. It can happen that you want to pre-populate the array of Blobs
   // and/or the PortIdentifiers.
-  void ReadFromBuffer(nsISupports* aParent, JSContext* aCx,
+  void ReadFromBuffer(nsIGlobalObject* aGlobal, JSContext* aCx,
                       JSStructuredCloneData& aBuffer,
                       JS::MutableHandle<JS::Value> aValue, ErrorResult& aRv);
 
-  void ReadFromBuffer(nsISupports* aParent, JSContext* aCx,
+  void ReadFromBuffer(nsIGlobalObject* aGlobal, JSContext* aCx,
                       JSStructuredCloneData& aBuffer,
                       uint32_t aAlgorithmVersion,
                       JS::MutableHandle<JS::Value> aValue, ErrorResult& aRv);
 
   bool mSupportsCloning;
   bool mSupportsTransferring;
 
   // SizeOfExcludingThis is inherited from StructuredCloneHolderBase. It doesn't
@@ -299,17 +299,17 @@ class StructuredCloneHolder : public Str
 
   // This is used for sharing the backend of ImageBitmaps.
   // The DataSourceSurface object must be thread-safely reference-counted.
   // The DataSourceSurface object will not be written ever via any ImageBitmap
   // instance, so no race condition will occur.
   nsTArray<RefPtr<gfx::DataSourceSurface>> mClonedSurfaces;
 
   // This raw pointer is only set within ::Read() and is unset by the end.
-  nsISupports* MOZ_NON_OWNING_REF mParent;
+  nsIGlobalObject* MOZ_NON_OWNING_REF mGlobal;
 
   // This array contains the ports once we've finished the reading. It's
   // generated from the mPortIdentifiers array.
   nsTArray<RefPtr<MessagePort>> mTransferredPorts;
 
   // This array contains the identifiers of the MessagePorts. Based on these we
   // are able to reconnect the new transferred ports with the other
   // MessageChannel ports.
--- a/dom/base/nsDOMDataChannel.cpp
+++ b/dom/base/nsDOMDataChannel.cpp
@@ -279,18 +279,20 @@ nsresult nsDOMDataChannel::DoOnMessageAv
   }
   JSContext* cx = jsapi.cx();
 
   JS::Rooted<JS::Value> jsData(cx);
 
   if (aBinary) {
     if (mBinaryType == DC_BINARY_TYPE_BLOB) {
       RefPtr<Blob> blob =
-          Blob::CreateStringBlob(GetOwner(), aData, EmptyString());
-      MOZ_ASSERT(blob);
+          Blob::CreateStringBlob(GetOwnerGlobal(), aData, EmptyString());
+      if (NS_WARN_IF(!blob)) {
+        return NS_ERROR_FAILURE;
+      }
 
       if (!ToJSValue(cx, blob, &jsData)) {
         return NS_ERROR_FAILURE;
       }
     } else if (mBinaryType == DC_BINARY_TYPE_ARRAYBUFFER) {
       JS::Rooted<JSObject*> arrayBuf(cx);
       rv = nsContentUtils::CreateArrayBuffer(cx, aData, arrayBuf.address());
       NS_ENSURE_SUCCESS(rv, rv);
--- a/dom/canvas/CanvasRenderingContextHelper.cpp
+++ b/dom/canvas/CanvasRenderingContextHelper.cpp
@@ -27,29 +27,29 @@ void CanvasRenderingContextHelper::ToBlo
   // Encoder callback when encoding is complete.
   class EncodeCallback : public EncodeCompleteCallback {
    public:
     EncodeCallback(nsIGlobalObject* aGlobal, BlobCallback* aCallback)
         : mGlobal(aGlobal), mBlobCallback(aCallback) {}
 
     // This is called on main thread.
     MOZ_CAN_RUN_SCRIPT
-    nsresult ReceiveBlob(already_AddRefed<Blob> aBlob) override {
-      RefPtr<Blob> blob = aBlob;
+    nsresult ReceiveBlobImpl(already_AddRefed<BlobImpl> aBlobImpl) override {
+      RefPtr<BlobImpl> blobImpl = aBlobImpl;
 
-      RefPtr<Blob> newBlob;
+      RefPtr<Blob> blob;
 
-      if (blob) {
-        newBlob = Blob::Create(mGlobal, blob->Impl());
+      if (blobImpl) {
+        blob = Blob::Create(mGlobal, blobImpl);
       }
 
       RefPtr<BlobCallback> callback(mBlobCallback.forget());
       ErrorResult rv;
 
-      callback->Call(newBlob, rv);
+      callback->Call(blob, rv);
 
       mGlobal = nullptr;
       MOZ_ASSERT(!mBlobCallback);
 
       return rv.StealNSResult();
     }
 
     nsCOMPtr<nsIGlobalObject> mGlobal;
--- a/dom/canvas/OffscreenCanvas.cpp
+++ b/dom/canvas/OffscreenCanvas.cpp
@@ -224,22 +224,26 @@ already_AddRefed<Promise> OffscreenCanva
 
   // Encoder callback when encoding is complete.
   class EncodeCallback : public EncodeCompleteCallback {
    public:
     EncodeCallback(nsIGlobalObject* aGlobal, Promise* aPromise)
         : mGlobal(aGlobal), mPromise(aPromise) {}
 
     // This is called on main thread.
-    nsresult ReceiveBlob(already_AddRefed<Blob> aBlob) override {
-      RefPtr<Blob> blob = aBlob;
+    nsresult ReceiveBlobImpl(already_AddRefed<BlobImpl> aBlobImpl) override {
+      RefPtr<BlobImpl> blobImpl = aBlobImpl;
 
       if (mPromise) {
-        RefPtr<Blob> newBlob = Blob::Create(mGlobal, blob->Impl());
-        mPromise->MaybeResolve(newBlob);
+        RefPtr<Blob> blob = Blob::Create(mGlobal, blobImpl);
+        if (NS_WARN_IF(!blob)) {
+          mPromise->MaybeReject(NS_ERROR_FAILURE);
+        } else {
+          mPromise->MaybeResolve(blob);
+        }
       }
 
       mGlobal = nullptr;
       mPromise = nullptr;
 
       return NS_OK;
     }
 
--- a/dom/console/Console.cpp
+++ b/dom/console/Console.cpp
@@ -67,17 +67,17 @@
 #define STORAGE_MAX_EVENTS 1000
 
 using namespace mozilla::dom::exceptions;
 
 namespace mozilla {
 namespace dom {
 
 struct ConsoleStructuredCloneData {
-  nsCOMPtr<nsISupports> mParent;
+  nsCOMPtr<nsIGlobalObject> mGlobal;
   nsTArray<RefPtr<BlobImpl>> mBlobs;
 };
 
 /**
  * Console API in workers uses the Structured Clone Algorithm to move any value
  * from the worker thread to the main-thread. Some object cannot be moved and,
  * in these cases, we convert them to strings.
  * It's not the best, but at least we are able to show something.
@@ -286,18 +286,19 @@ class ConsoleRunnable : public Structure
                               uint32_t aTag, uint32_t aIndex) override {
     AssertIsOnMainThread();
 
     if (aTag == CONSOLE_TAG_BLOB) {
       MOZ_ASSERT(mClonedData.mBlobs.Length() > aIndex);
 
       JS::Rooted<JS::Value> val(aCx);
       {
-        RefPtr<Blob> blob = Blob::Create(mClonedData.mParent,
-                                         mClonedData.mBlobs.ElementAt(aIndex));
+        nsCOMPtr<nsIGlobalObject> global = mClonedData.mGlobal;
+        RefPtr<Blob> blob =
+            Blob::Create(global, mClonedData.mBlobs.ElementAt(aIndex));
         if (!ToJSValue(aCx, blob, &val)) {
           return nullptr;
         }
       }
 
       return &val.toObject();
     }
 
@@ -446,17 +447,17 @@ class ConsoleRunnable : public Structure
   void ProcessProfileData(JSContext* aCx, Console::MethodName aMethodName,
                           const nsAString& aAction) {
     AssertIsOnMainThread();
 
     ConsoleCommon::ClearException ce(aCx);
 
     JS::Rooted<JS::Value> argumentsValue(aCx);
     bool ok = Read(aCx, &argumentsValue);
-    mClonedData.mParent = nullptr;
+    mClonedData.mGlobal = nullptr;
 
     if (!ok) {
       return;
     }
 
     MOZ_ASSERT(argumentsValue.isObject());
     JS::Rooted<JSObject*> argumentsObj(aCx, &argumentsValue.toObject());
     if (NS_WARN_IF(!argumentsObj)) {
@@ -652,17 +653,18 @@ class ConsoleWorkerRunnable : public Wor
       return;
     }
 
     nsCOMPtr<nsPIDOMWindowOuter> outerWindow = aWindow->GetOuterWindow();
     if (NS_WARN_IF(!outerWindow)) {
       return;
     }
 
-    RunConsole(jsapi.cx(), aWorkerPrivate, outerWindow, aWindow);
+    RunConsole(jsapi.cx(), aWindow->AsGlobal(), aWorkerPrivate, outerWindow,
+               aWindow);
   }
 
   void RunWindowless(WorkerPrivate* aWorkerPrivate) {
     MOZ_ASSERT(aWorkerPrivate);
     AssertIsOnMainThread();
 
     WorkerPrivate* wp = aWorkerPrivate;
     while (wp->GetParent()) {
@@ -683,31 +685,37 @@ class ConsoleWorkerRunnable : public Wor
     }
 
     // The GetOrCreateSandbox call returns a proxy to the actual sandbox object.
     // We don't need a proxy here.
     global = js::UncheckedUnwrap(global);
 
     JSAutoRealm ar(cx, global);
 
-    RunConsole(cx, aWorkerPrivate, nullptr, nullptr);
+    nsCOMPtr<nsIGlobalObject> globalObject = xpc::NativeGlobal(global);
+    if (NS_WARN_IF(!globalObject)) {
+      return;
+    }
+
+    RunConsole(cx, globalObject, aWorkerPrivate, nullptr, nullptr);
   }
 
   void RunBackOnWorkerThreadForCleanup(WorkerPrivate* aWorkerPrivate) override {
     MOZ_ASSERT(aWorkerPrivate);
     aWorkerPrivate->AssertIsOnWorkerThread();
     ReleaseData();
     mConsole = nullptr;
   }
 
   // This method is called in the owning thread of the Console object.
   virtual bool PreDispatch(JSContext* aCx) = 0;
 
   // This method is called in the main-thread.
-  virtual void RunConsole(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
+  virtual void RunConsole(JSContext* aCx, nsIGlobalObject* aGlobal,
+                          WorkerPrivate* aWorkerPrivate,
                           nsPIDOMWindowOuter* aOuterWindow,
                           nsPIDOMWindowInner* aInnerWindow) = 0;
 
   // This method is called in the owning thread of the Console object.
   virtual void ReleaseData() = 0;
 
   bool ForMessaging() const override { return true; }
 
@@ -730,19 +738,21 @@ class ConsoleCallDataWorkerRunnable fina
 
  private:
   ~ConsoleCallDataWorkerRunnable() override { MOZ_ASSERT(!mCallData); }
 
   bool PreDispatch(JSContext* aCx) override {
     return StoreConsoleData(aCx, mCallData);
   }
 
-  void RunConsole(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
+  void RunConsole(JSContext* aCx, nsIGlobalObject* aGlobal,
+                  WorkerPrivate* aWorkerPrivate,
                   nsPIDOMWindowOuter* aOuterWindow,
                   nsPIDOMWindowInner* aInnerWindow) override {
+    MOZ_ASSERT(aGlobal);
     MOZ_ASSERT(aWorkerPrivate);
     AssertIsOnMainThread();
 
     // The windows have to run in parallel.
     MOZ_ASSERT(!!aOuterWindow == !!aInnerWindow);
 
     if (aOuterWindow) {
       mCallData->SetIDs(aOuterWindow->WindowID(), aInnerWindow->WindowID());
@@ -763,22 +773,21 @@ class ConsoleCallDataWorkerRunnable fina
         CopyASCIItoUTF16(aWorkerPrivate->ServiceWorkerScope(), id);
       } else {
         innerID = NS_LITERAL_STRING("Worker");
       }
 
       mCallData->SetIDs(id, innerID);
     }
 
-    // Now we could have the correct window (if we are not window-less).
-    mClonedData.mParent = aInnerWindow;
+    mClonedData.mGlobal = aGlobal;
 
     ProcessCallData(aCx, mConsole, mCallData);
 
-    mClonedData.mParent = nullptr;
+    mClonedData.mGlobal = nullptr;
   }
 
   virtual void ReleaseData() override {
     ReleaseCallData(mConsole, mCallData);
     mCallData = nullptr;
   }
 
   RefPtr<ConsoleCallData> mCallData;
@@ -851,27 +860,28 @@ class ConsoleProfileWorkerRunnable final
     MOZ_ASSERT(aConsole);
   }
 
  private:
   bool PreDispatch(JSContext* aCx) override {
     return StoreProfileData(aCx, mArguments);
   }
 
-  void RunConsole(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
+  void RunConsole(JSContext* aCx, nsIGlobalObject* aGlobal,
+                  WorkerPrivate* aWorkerPrivate,
                   nsPIDOMWindowOuter* aOuterWindow,
                   nsPIDOMWindowInner* aInnerWindow) override {
     AssertIsOnMainThread();
-
-    // Now we could have the correct window (if we are not window-less).
-    mClonedData.mParent = aInnerWindow;
+    MOZ_ASSERT(aGlobal);
+
+    mClonedData.mGlobal = aGlobal;
 
     ProcessProfileData(aCx, mName, mAction);
 
-    mClonedData.mParent = nullptr;
+    mClonedData.mGlobal = nullptr;
   }
 
   virtual void ReleaseData() override {}
 
   Console::MethodName mName;
   nsString mAction;
 
   // This is a reference of the sequence of arguments we receive from the DOM
--- a/dom/events/DataTransferItem.cpp
+++ b/dom/events/DataTransferItem.cpp
@@ -281,52 +281,53 @@ already_AddRefed<File> DataTransferItem:
     MOZ_ASSERT(!aRv.Failed() && supports,
                "File objects should be stored as nsISupports variants");
     if (aRv.Failed() || !supports) {
       return nullptr;
     }
 
     if (RefPtr<Blob> blob = do_QueryObject(supports)) {
       mCachedFile = blob->ToFile();
-    } else if (nsCOMPtr<BlobImpl> blobImpl = do_QueryInterface(supports)) {
-      MOZ_ASSERT(blobImpl->IsFile());
-      mCachedFile = File::Create(mDataTransfer, blobImpl);
-    } else if (nsCOMPtr<nsIFile> ifile = do_QueryInterface(supports)) {
-      mCachedFile = File::CreateFromFile(mDataTransfer, ifile);
     } else {
-      MOZ_ASSERT(false, "One of the above code paths should be taken");
-      return nullptr;
+      nsCOMPtr<nsIGlobalObject> global = GetGlobalFromDataTransfer();
+      if (NS_WARN_IF(!global)) {
+        return nullptr;
+      }
+
+      if (nsCOMPtr<BlobImpl> blobImpl = do_QueryInterface(supports)) {
+        MOZ_ASSERT(blobImpl->IsFile());
+        mCachedFile = File::Create(global, blobImpl);
+        if (NS_WARN_IF(!mCachedFile)) {
+          return nullptr;
+        }
+      } else if (nsCOMPtr<nsIFile> ifile = do_QueryInterface(supports)) {
+        mCachedFile = File::CreateFromFile(global, ifile);
+        if (NS_WARN_IF(!mCachedFile)) {
+          return nullptr;
+        }
+      } else {
+        MOZ_ASSERT(false, "One of the above code paths should be taken");
+        return nullptr;
+      }
     }
   }
 
   RefPtr<File> file = mCachedFile;
   return file.forget();
 }
 
 already_AddRefed<FileSystemEntry> DataTransferItem::GetAsEntry(
     nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
   RefPtr<File> file = GetAsFile(aSubjectPrincipal, aRv);
   if (NS_WARN_IF(aRv.Failed()) || !file) {
     return nullptr;
   }
 
-  nsCOMPtr<nsIGlobalObject> global;
-  // This is annoying, but DataTransfer may have various things as parent.
-  nsCOMPtr<EventTarget> target =
-      do_QueryInterface(mDataTransfer->GetParentObject());
-  if (target) {
-    global = target->GetOwnerGlobal();
-  } else {
-    RefPtr<Event> event = do_QueryObject(mDataTransfer->GetParentObject());
-    if (event) {
-      global = event->GetParentObject();
-    }
-  }
-
-  if (!global) {
+  nsCOMPtr<nsIGlobalObject> global = GetGlobalFromDataTransfer();
+  if (NS_WARN_IF(!global)) {
     return nullptr;
   }
 
   RefPtr<FileSystem> fs = FileSystem::Create(global);
   RefPtr<FileSystemEntry> entry;
   BlobImpl* impl = file->Impl();
   MOZ_ASSERT(impl);
 
@@ -385,17 +386,22 @@ already_AddRefed<File> DataTransferItem:
 
   uint64_t available;
   void* data = nullptr;
   rv = NS_ReadInputStreamToBuffer(aStream, &data, -1, &available);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return nullptr;
   }
 
-  return File::CreateMemoryFile(mDataTransfer, data, available, fileName, mType,
+  nsCOMPtr<nsIGlobalObject> global = GetGlobalFromDataTransfer();
+  if (NS_WARN_IF(!global)) {
+    return nullptr;
+  }
+
+  return File::CreateMemoryFile(global, data, available, fileName, mType,
                                 PR_Now());
 }
 
 void DataTransferItem::GetAsString(FunctionStringCallback* aCallback,
                                    nsIPrincipal& aSubjectPrincipal,
                                    ErrorResult& aRv) {
   if (!aCallback) {
     return;
@@ -546,10 +552,28 @@ already_AddRefed<nsIVariant> DataTransfe
         return nullptr;
       }
     }
   }
 
   return variant.forget();
 }
 
+already_AddRefed<nsIGlobalObject>
+DataTransferItem::GetGlobalFromDataTransfer() {
+  nsCOMPtr<nsIGlobalObject> global;
+  // This is annoying, but DataTransfer may have various things as parent.
+  nsCOMPtr<EventTarget> target =
+      do_QueryInterface(mDataTransfer->GetParentObject());
+  if (target) {
+    global = target->GetOwnerGlobal();
+  } else {
+    RefPtr<Event> event = do_QueryObject(mDataTransfer->GetParentObject());
+    if (event) {
+      global = event->GetParentObject();
+    }
+  }
+
+  return global.forget();
+}
+
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/events/DataTransferItem.h
+++ b/dom/events/DataTransferItem.h
@@ -100,16 +100,18 @@ class DataTransferItem final : public ns
   void SetChromeOnly(bool aChromeOnly) { mChromeOnly = aChromeOnly; }
 
   static eKind KindFromData(nsIVariant* aData);
 
  private:
   ~DataTransferItem() {}
   already_AddRefed<File> CreateFileFromInputStream(nsIInputStream* aStream);
 
+  already_AddRefed<nsIGlobalObject> GetGlobalFromDataTransfer();
+
   // The index in the 2d mIndexedItems array
   uint32_t mIndex;
 
   bool mChromeOnly;
   eKind mKind;
   const nsString mType;
   nsCOMPtr<nsIVariant> mData;
   nsCOMPtr<nsIPrincipal> mPrincipal;
--- a/dom/file/Blob.cpp
+++ b/dom/file/Blob.cpp
@@ -8,34 +8,35 @@
 #include "EmptyBlobImpl.h"
 #include "File.h"
 #include "MemoryBlobImpl.h"
 #include "mozilla/dom/BlobBinding.h"
 #include "mozilla/dom/BodyStream.h"
 #include "mozilla/dom/WorkerCommon.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "MultipartBlobImpl.h"
+#include "nsIGlobalObject.h"
 #include "nsIInputStream.h"
 #include "nsPIDOMWindow.h"
 #include "StreamBlobImpl.h"
 #include "StringBlobImpl.h"
 #include "js/GCAPI.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(Blob)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Blob)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Blob)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Blob)
   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Blob)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
@@ -62,54 +63,62 @@ void Blob::MakeValidBlobType(nsAString& 
 
     if (c >= 'A' && c <= 'Z') {
       *iter = c + ('a' - 'A');
     }
   }
 }
 
 /* static */
-Blob* Blob::Create(nsISupports* aParent, BlobImpl* aImpl) {
+Blob* Blob::Create(nsIGlobalObject* aGlobal, BlobImpl* aImpl) {
   MOZ_ASSERT(aImpl);
 
-  return aImpl->IsFile() ? new File(aParent, aImpl) : new Blob(aParent, aImpl);
+  MOZ_ASSERT(aGlobal);
+  if (NS_WARN_IF(!aGlobal)) {
+    return nullptr;
+  }
+
+  return aImpl->IsFile() ? new File(aGlobal, aImpl) : new Blob(aGlobal, aImpl);
 }
 
 /* static */
-already_AddRefed<Blob> Blob::CreateEmptyBlob(nsISupports* aParent,
-                                             const nsAString& aContentType) {
-  RefPtr<Blob> blob = Blob::Create(aParent, new EmptyBlobImpl(aContentType));
+already_AddRefed<Blob> Blob::CreateStringBlob(nsIGlobalObject* aGlobal,
+                                              const nsACString& aData,
+                                              const nsAString& aContentType) {
+  MOZ_ASSERT(aGlobal);
+  if (NS_WARN_IF(!aGlobal)) {
+    return nullptr;
+  }
+
+  RefPtr<BlobImpl> blobImpl = StringBlobImpl::Create(aData, aContentType);
+  RefPtr<Blob> blob = Blob::Create(aGlobal, blobImpl);
   MOZ_ASSERT(!blob->mImpl->IsFile());
   return blob.forget();
 }
 
 /* static */
-already_AddRefed<Blob> Blob::CreateStringBlob(nsISupports* aParent,
-                                              const nsACString& aData,
+already_AddRefed<Blob> Blob::CreateMemoryBlob(nsIGlobalObject* aGlobal,
+                                              void* aMemoryBuffer,
+                                              uint64_t aLength,
                                               const nsAString& aContentType) {
-  RefPtr<BlobImpl> blobImpl = StringBlobImpl::Create(aData, aContentType);
-  RefPtr<Blob> blob = Blob::Create(aParent, blobImpl);
+  MOZ_ASSERT(aGlobal);
+  if (NS_WARN_IF(!aGlobal)) {
+    return nullptr;
+  }
+
+  RefPtr<Blob> blob = Blob::Create(
+      aGlobal, new MemoryBlobImpl(aMemoryBuffer, aLength, aContentType));
   MOZ_ASSERT(!blob->mImpl->IsFile());
   return blob.forget();
 }
 
-/* static */
-already_AddRefed<Blob> Blob::CreateMemoryBlob(nsISupports* aParent,
-                                              void* aMemoryBuffer,
-                                              uint64_t aLength,
-                                              const nsAString& aContentType) {
-  RefPtr<Blob> blob = Blob::Create(
-      aParent, new MemoryBlobImpl(aMemoryBuffer, aLength, aContentType));
-  MOZ_ASSERT(!blob->mImpl->IsFile());
-  return blob.forget();
-}
-
-Blob::Blob(nsISupports* aParent, BlobImpl* aImpl)
-    : mImpl(aImpl), mParent(aParent) {
+Blob::Blob(nsIGlobalObject* aGlobal, BlobImpl* aImpl)
+    : mImpl(aImpl), mGlobal(aGlobal) {
   MOZ_ASSERT(mImpl);
+  MOZ_ASSERT(mGlobal);
 }
 
 Blob::~Blob() = default;
 
 bool Blob::IsFile() const { return mImpl->IsFile(); }
 
 const nsTArray<RefPtr<BlobImpl>>* Blob::GetSubBlobImpls() const {
   return mImpl->GetSubBlobImpls();
@@ -119,17 +128,17 @@ already_AddRefed<File> Blob::ToFile() {
   if (!mImpl->IsFile()) {
     return nullptr;
   }
 
   RefPtr<File> file;
   if (HasFileInterface()) {
     file = static_cast<File*>(this);
   } else {
-    file = new File(mParent, mImpl);
+    file = new File(mGlobal, mImpl);
   }
 
   return file.forget();
 }
 
 already_AddRefed<File> Blob::ToFile(const nsAString& aName,
                                     ErrorResult& aRv) const {
   AutoTArray<RefPtr<BlobImpl>, 1> blobImpls({mImpl});
@@ -138,30 +147,30 @@ already_AddRefed<File> Blob::ToFile(cons
   mImpl->GetType(contentType);
 
   RefPtr<MultipartBlobImpl> impl =
       MultipartBlobImpl::Create(std::move(blobImpls), aName, contentType, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
-  RefPtr<File> file = new File(mParent, impl);
+  RefPtr<File> file = new File(mGlobal, impl);
   return file.forget();
 }
 
 already_AddRefed<Blob> Blob::CreateSlice(uint64_t aStart, uint64_t aLength,
                                          const nsAString& aContentType,
                                          ErrorResult& aRv) {
   RefPtr<BlobImpl> impl =
       mImpl->CreateSlice(aStart, aLength, aContentType, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
 
-  RefPtr<Blob> blob = Blob::Create(mParent, impl);
+  RefPtr<Blob> blob = Blob::Create(mGlobal, impl);
   return blob.forget();
 }
 
 uint64_t Blob::GetSize(ErrorResult& aRv) { return mImpl->GetSize(aRv); }
 
 void Blob::GetType(nsAString& aType) { mImpl->GetType(aType); }
 
 void Blob::GetBlobImplType(nsAString& aBlobImplType) {
@@ -177,17 +186,17 @@ already_AddRefed<Blob> Blob::Slice(const
     contentType = aContentType.Value();
   }
 
   RefPtr<BlobImpl> impl = mImpl->Slice(aStart, aEnd, contentType, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
 
-  RefPtr<Blob> blob = Blob::Create(mParent, impl);
+  RefPtr<Blob> blob = Blob::Create(mGlobal, impl);
   return blob.forget();
 }
 
 size_t Blob::GetAllocationSize() const { return mImpl->GetAllocationSize(); }
 
 // contentTypeWithCharset can be set to the contentType or
 // contentType+charset based on what the spec says.
 // See: https://fetch.spec.whatwg.org/#concept-bodyinit-extract
@@ -223,17 +232,20 @@ already_AddRefed<Blob> Blob::Constructor
   }
 
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   MOZ_ASSERT(!impl->IsFile());
 
-  RefPtr<Blob> blob = Blob::Create(aGlobal.GetAsSupports(), impl);
+  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
+  MOZ_ASSERT(global);
+
+  RefPtr<Blob> blob = Blob::Create(global, impl);
   return blob.forget();
 }
 
 int64_t Blob::GetFileId() { return mImpl->GetFileId(); }
 
 bool Blob::IsMemoryFile() const { return mImpl->IsMemoryFile(); }
 
 void Blob::CreateInputStream(nsIInputStream** aStream, ErrorResult& aRv) {
@@ -256,40 +268,39 @@ already_AddRefed<Promise> Blob::Text(Err
 }
 
 already_AddRefed<Promise> Blob::ArrayBuffer(ErrorResult& aRv) {
   return ConsumeBody(BodyConsumer::CONSUME_ARRAYBUFFER, aRv);
 }
 
 already_AddRefed<Promise> Blob::ConsumeBody(
     BodyConsumer::ConsumeType aConsumeType, ErrorResult& aRv) {
-  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(mParent);
-  if (NS_WARN_IF(!global)) {
+  if (NS_WARN_IF(!mGlobal)) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   nsCOMPtr<nsIEventTarget> mainThreadEventTarget;
   if (!NS_IsMainThread()) {
     WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
     MOZ_ASSERT(workerPrivate);
     mainThreadEventTarget = workerPrivate->MainThreadEventTarget();
   } else {
-    mainThreadEventTarget = global->EventTargetFor(TaskCategory::Other);
+    mainThreadEventTarget = mGlobal->EventTargetFor(TaskCategory::Other);
   }
 
   MOZ_ASSERT(mainThreadEventTarget);
 
   nsCOMPtr<nsIInputStream> inputStream;
   CreateInputStream(getter_AddRefs(inputStream), aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
-  return BodyConsumer::Create(global, mainThreadEventTarget, inputStream,
+  return BodyConsumer::Create(mGlobal, mainThreadEventTarget, inputStream,
                               nullptr, aConsumeType, VoidCString(),
                               VoidString(), VoidCString(),
                               MutableBlobStorage::eOnlyInMemory, aRv);
 }
 
 namespace {
 
 class BlobBodyStreamHolder final : public BodyStreamHolder {
@@ -348,25 +359,24 @@ NS_INTERFACE_MAP_END_INHERITING(BodyStre
 void Blob::Stream(JSContext* aCx, JS::MutableHandle<JSObject*> aStream,
                   ErrorResult& aRv) {
   nsCOMPtr<nsIInputStream> stream;
   CreateInputStream(getter_AddRefs(stream), aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
 
-  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
-  if (NS_WARN_IF(!global)) {
+  if (NS_WARN_IF(!mGlobal)) {
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
 
   RefPtr<BlobBodyStreamHolder> holder = new BlobBodyStreamHolder();
 
-  BodyStream::Create(aCx, holder, global, stream, aRv);
+  BodyStream::Create(aCx, holder, mGlobal, stream, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
 
   aStream.set(holder->GetReadableStreamBody());
 }
 
 }  // namespace dom
--- a/dom/file/Blob.h
+++ b/dom/file/Blob.h
@@ -13,16 +13,17 @@
 #include "mozilla/dom/BlobImpl.h"
 #include "mozilla/dom/BodyConsumer.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsCOMPtr.h"
 #include "nsIMutable.h"
 #include "nsWrapperCache.h"
 #include "nsWeakReference.h"
 
+class nsIGlobalObject;
 class nsIInputStream;
 
 namespace mozilla {
 namespace dom {
 
 struct BlobPropertyBag;
 class File;
 class OwningArrayBufferViewOrArrayBufferOrBlobOrUSVString;
@@ -43,29 +44,26 @@ class Blob : public nsIMutable,
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS_FINAL
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(Blob, nsIMutable)
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_DOM_BLOB_IID)
 
   typedef OwningArrayBufferViewOrArrayBufferOrBlobOrUSVString BlobPart;
 
   // This creates a Blob or a File based on the type of BlobImpl.
-  static Blob* Create(nsISupports* aParent, BlobImpl* aImpl);
+  static Blob* Create(nsIGlobalObject* aGlobal, BlobImpl* aImpl);
 
-  static already_AddRefed<Blob> CreateEmptyBlob(nsISupports* aParent,
-                                                const nsAString& aContentType);
-
-  static already_AddRefed<Blob> CreateStringBlob(nsISupports* aParent,
+  static already_AddRefed<Blob> CreateStringBlob(nsIGlobalObject* aGlobal,
                                                  const nsACString& aData,
                                                  const nsAString& aContentType);
 
   // The returned Blob takes ownership of aMemoryBuffer. aMemoryBuffer will be
   // freed by free so it must be allocated by malloc or something
   // compatible with it.
-  static already_AddRefed<Blob> CreateMemoryBlob(nsISupports* aParent,
+  static already_AddRefed<Blob> CreateMemoryBlob(nsIGlobalObject* aGlobal,
                                                  void* aMemoryBuffer,
                                                  uint64_t aLength,
                                                  const nsAString& aContentType);
 
   BlobImpl* Impl() const { return mImpl; }
 
   bool IsFile() const;
 
@@ -92,17 +90,17 @@ class Blob : public nsIMutable,
   // blob: no codepoints outside the ASCII range (otherwise type becomes empty)
   // and lowercase ASCII only.  We can't just use our existing nsContentUtils
   // ASCII-related helpers because we need the "outside ASCII range" check, and
   // we can't use NS_IsAscii because its definition of "ASCII" (chars all <=
   // 0x7E) differs from the file API definition (which excludes control chars).
   static void MakeValidBlobType(nsAString& aType);
 
   // WebIDL methods
-  nsISupports* GetParentObject() const { return mParent; }
+  nsIGlobalObject* GetParentObject() const { return mGlobal; }
 
   bool IsMemoryFile() const;
 
   // Blob constructor
   static already_AddRefed<Blob> Constructor(
       const GlobalObject& aGlobal, const Optional<Sequence<BlobPart>>& aData,
       const BlobPropertyBag& aBag, ErrorResult& aRv);
 
@@ -127,32 +125,32 @@ class Blob : public nsIMutable,
 
   void Stream(JSContext* aCx, JS::MutableHandle<JSObject*> aStream,
               ErrorResult& aRv);
   already_AddRefed<Promise> Text(ErrorResult& aRv);
   already_AddRefed<Promise> ArrayBuffer(ErrorResult& aRv);
 
  protected:
   // File constructor should never be used directly. Use Blob::Create instead.
-  Blob(nsISupports* aParent, BlobImpl* aImpl);
+  Blob(nsIGlobalObject* aGlobal, BlobImpl* aImpl);
   virtual ~Blob();
 
   virtual bool HasFileInterface() const { return false; }
 
   already_AddRefed<Promise> ConsumeBody(BodyConsumer::ConsumeType aConsumeType,
                                         ErrorResult& aRv);
 
   // The member is the real backend implementation of this File/Blob.
   // It's thread-safe and not CC-able and it's the only element that is moved
   // between threads.
   // Note: we should not store any other state in this class!
   RefPtr<BlobImpl> mImpl;
 
  private:
-  nsCOMPtr<nsISupports> mParent;
+  nsCOMPtr<nsIGlobalObject> mGlobal;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(Blob, NS_DOM_BLOB_IID)
 
 // Override BindingJSObjectMallocBytes for blobs to tell the JS GC how much
 // memory is held live by the binding object.
 size_t BindingJSObjectMallocBytes(Blob* aBlob);
 
--- a/dom/file/File.cpp
+++ b/dom/file/File.cpp
@@ -13,71 +13,98 @@
 #include "mozilla/dom/FileCreatorHelper.h"
 #include "mozilla/dom/FileSystemUtils.h"
 #include "mozilla/dom/Promise.h"
 #include "nsXULAppAPI.h"
 
 namespace mozilla {
 namespace dom {
 
-File::File(nsISupports* aParent, BlobImpl* aImpl) : Blob(aParent, aImpl) {
+File::File(nsIGlobalObject* aGlobal, BlobImpl* aImpl) : Blob(aGlobal, aImpl) {
   MOZ_ASSERT(aImpl->IsFile());
 }
 
 File::~File() {}
 
 /* static */
-File* File::Create(nsISupports* aParent, BlobImpl* aImpl) {
+File* File::Create(nsIGlobalObject* aGlobal, BlobImpl* aImpl) {
   MOZ_ASSERT(aImpl);
   MOZ_ASSERT(aImpl->IsFile());
 
-  return new File(aParent, aImpl);
+  MOZ_ASSERT(aGlobal);
+  if (NS_WARN_IF(!aGlobal)) {
+    return nullptr;
+  }
+
+  return new File(aGlobal, aImpl);
 }
 
 /* static */
-already_AddRefed<File> File::Create(nsISupports* aParent,
+already_AddRefed<File> File::Create(nsIGlobalObject* aGlobal,
                                     const nsAString& aName,
                                     const nsAString& aContentType,
                                     uint64_t aLength,
                                     int64_t aLastModifiedDate) {
+  MOZ_ASSERT(aGlobal);
+  if (NS_WARN_IF(!aGlobal)) {
+    return nullptr;
+  }
+
   RefPtr<File> file = new File(
-      aParent, new BaseBlobImpl(NS_LITERAL_STRING("BaseBlobImpl"), aName,
+      aGlobal, new BaseBlobImpl(NS_LITERAL_STRING("BaseBlobImpl"), aName,
                                 aContentType, aLength, aLastModifiedDate));
   return file.forget();
 }
 
 /* static */
-already_AddRefed<File> File::CreateMemoryFile(nsISupports* aParent,
+already_AddRefed<File> File::CreateMemoryFile(nsIGlobalObject* aGlobal,
                                               void* aMemoryBuffer,
                                               uint64_t aLength,
                                               const nsAString& aName,
                                               const nsAString& aContentType,
                                               int64_t aLastModifiedDate) {
+  MOZ_ASSERT(aGlobal);
+  if (NS_WARN_IF(!aGlobal)) {
+    return nullptr;
+  }
+
   RefPtr<File> file =
-      new File(aParent, new MemoryBlobImpl(aMemoryBuffer, aLength, aName,
+      new File(aGlobal, new MemoryBlobImpl(aMemoryBuffer, aLength, aName,
                                            aContentType, aLastModifiedDate));
   return file.forget();
 }
 
 /* static */
-already_AddRefed<File> File::CreateFromFile(nsISupports* aParent,
+already_AddRefed<File> File::CreateFromFile(nsIGlobalObject* aGlobal,
                                             nsIFile* aFile) {
   MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
-  RefPtr<File> file = new File(aParent, new FileBlobImpl(aFile));
+
+  MOZ_ASSERT(aGlobal);
+  if (NS_WARN_IF(!aGlobal)) {
+    return nullptr;
+  }
+
+  RefPtr<File> file = new File(aGlobal, new FileBlobImpl(aFile));
   return file.forget();
 }
 
 /* static */
-already_AddRefed<File> File::CreateFromFile(nsISupports* aParent,
+already_AddRefed<File> File::CreateFromFile(nsIGlobalObject* aGlobal,
                                             nsIFile* aFile,
                                             const nsAString& aName,
                                             const nsAString& aContentType) {
   MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
+
+  MOZ_ASSERT(aGlobal);
+  if (NS_WARN_IF(!aGlobal)) {
+    return nullptr;
+  }
+
   RefPtr<File> file =
-      new File(aParent, new FileBlobImpl(aFile, aName, aContentType));
+      new File(aGlobal, new FileBlobImpl(aFile, aName, aContentType));
   return file.forget();
 }
 
 JSObject* File::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
   return File_Binding::Wrap(aCx, this, aGivenProto);
 }
 
 void File::GetName(nsAString& aFileName) const { mImpl->GetName(aFileName); }
@@ -127,27 +154,36 @@ already_AddRefed<File> File::Constructor
     return nullptr;
   }
   MOZ_ASSERT(impl->IsFile());
 
   if (aBag.mLastModified.WasPassed()) {
     impl->SetLastModified(aBag.mLastModified.Value());
   }
 
-  RefPtr<File> file = new File(aGlobal.GetAsSupports(), impl);
+  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
+  MOZ_ASSERT(global);
+
+  RefPtr<File> file = new File(global, impl);
   return file.forget();
 }
 
 /* static */
 already_AddRefed<Promise> File::CreateFromNsIFile(
     const GlobalObject& aGlobal, nsIFile* aData,
     const ChromeFilePropertyBag& aBag, SystemCallerGuarantee aGuarantee,
     ErrorResult& aRv) {
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
 
+  MOZ_ASSERT(global);
+  if (NS_WARN_IF(!global)) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+
   RefPtr<Promise> promise =
       FileCreatorHelper::CreateFile(global, aData, aBag, true, aRv);
   return promise.forget();
 }
 
 /* static */
 already_AddRefed<Promise> File::CreateFromFileName(
     const GlobalObject& aGlobal, const nsAString& aPath,
@@ -156,15 +192,21 @@ already_AddRefed<Promise> File::CreateFr
   nsCOMPtr<nsIFile> file;
   aRv = NS_NewLocalFile(aPath, false, getter_AddRefs(file));
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
 
+  MOZ_ASSERT(global);
+  if (NS_WARN_IF(!global)) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+
   RefPtr<Promise> promise =
       FileCreatorHelper::CreateFile(global, file, aBag, false, aRv);
   return promise.forget();
 }
 
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/file/File.h
+++ b/dom/file/File.h
@@ -20,44 +20,44 @@ struct FilePropertyBag;
 class Promise;
 
 class File final : public Blob {
   friend class Blob;
 
  public:
   // Note: BlobImpl must be a File in order to use this method.
   // Check impl->IsFile().
-  static File* Create(nsISupports* aParent, BlobImpl* aImpl);
+  static File* Create(nsIGlobalObject* aGlobal, BlobImpl* aImpl);
 
-  static already_AddRefed<File> Create(nsISupports* aParent,
+  static already_AddRefed<File> Create(nsIGlobalObject* aGlobal,
                                        const nsAString& aName,
                                        const nsAString& aContentType,
                                        uint64_t aLength,
                                        int64_t aLastModifiedDate);
 
   // The returned File takes ownership of aMemoryBuffer. aMemoryBuffer will be
   // freed by free so it must be allocated by malloc or something
   // compatible with it.
-  static already_AddRefed<File> CreateMemoryFile(nsISupports* aParent,
+  static already_AddRefed<File> CreateMemoryFile(nsIGlobalObject* aGlobal,
                                                  void* aMemoryBuffer,
                                                  uint64_t aLength,
                                                  const nsAString& aName,
                                                  const nsAString& aContentType,
                                                  int64_t aLastModifiedDate);
 
   // This method creates a BlobFileImpl for the new File object. This is
   // thread-safe, cross-process, cross-thread as any other BlobImpl, but, when
   // GetType() is called, it must dispatch a runnable to the main-thread in
   // order to use nsIMIMEService.
   // Would be nice if we try to avoid to use this method outside the
   // main-thread to avoid extra runnables.
-  static already_AddRefed<File> CreateFromFile(nsISupports* aParent,
+  static already_AddRefed<File> CreateFromFile(nsIGlobalObject* aGlobal,
                                                nsIFile* aFile);
 
-  static already_AddRefed<File> CreateFromFile(nsISupports* aParent,
+  static already_AddRefed<File> CreateFromFile(nsIGlobalObject* aGlobal,
                                                nsIFile* aFile,
                                                const nsAString& aName,
                                                const nsAString& aContentType);
 
   // WebIDL methods
 
   virtual JSObject* WrapObject(JSContext* cx,
                                JS::Handle<JSObject*> aGivenProto) override;
@@ -93,16 +93,16 @@ class File final : public Blob {
   void GetMozFullPathInternal(nsAString& aName, ErrorResult& aRv);
 
  protected:
   virtual bool HasFileInterface() const override { return true; }
 
  private:
   // File constructor should never be used directly. Use Blob::Create or
   // File::Create.
-  File(nsISupports* aParent, BlobImpl* aImpl);
+  File(nsIGlobalObject* aGlobal, BlobImpl* aImpl);
   ~File();
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // mozilla_dom_File_h
--- a/dom/file/MutableBlobStorage.cpp
+++ b/dom/file/MutableBlobStorage.cpp
@@ -26,52 +26,51 @@ namespace dom {
 
 namespace {
 
 // This class uses the callback to inform when the Blob is created or when the
 // error must be propagated.
 class BlobCreationDoneRunnable final : public Runnable {
  public:
   BlobCreationDoneRunnable(MutableBlobStorage* aBlobStorage,
-                           MutableBlobStorageCallback* aCallback, Blob* aBlob,
-                           nsresult aRv)
+                           MutableBlobStorageCallback* aCallback,
+                           BlobImpl* aBlobImpl, nsresult aRv)
       : Runnable("dom::BlobCreationDoneRunnable"),
         mBlobStorage(aBlobStorage),
         mCallback(aCallback),
-        mBlob(aBlob),
+        mBlobImpl(aBlobImpl),
         mRv(aRv) {
     MOZ_ASSERT(aBlobStorage);
     MOZ_ASSERT(aCallback);
-    MOZ_ASSERT((NS_FAILED(aRv) && !aBlob) || (NS_SUCCEEDED(aRv) && aBlob));
+    MOZ_ASSERT((NS_FAILED(aRv) && !aBlobImpl) ||
+               (NS_SUCCEEDED(aRv) && aBlobImpl));
   }
 
   NS_IMETHOD
   Run() override {
     MOZ_ASSERT(NS_IsMainThread());
     MOZ_ASSERT(mBlobStorage);
-    mCallback->BlobStoreCompleted(mBlobStorage, mBlob, mRv);
+    mCallback->BlobStoreCompleted(mBlobStorage, mBlobImpl, mRv);
     mCallback = nullptr;
-    mBlob = nullptr;
+    mBlobImpl = nullptr;
     return NS_OK;
   }
 
  private:
   ~BlobCreationDoneRunnable() {
     MOZ_ASSERT(mBlobStorage);
     // If something when wrong, we still have to release these objects in the
     // correct thread.
     NS_ProxyRelease("BlobCreationDoneRunnable::mCallback",
                     mBlobStorage->EventTarget(), mCallback.forget());
-    NS_ProxyRelease("BlobCreationDoneRunnable::mBlob",
-                    mBlobStorage->EventTarget(), mBlob.forget());
   }
 
   RefPtr<MutableBlobStorage> mBlobStorage;
   RefPtr<MutableBlobStorageCallback> mCallback;
-  RefPtr<Blob> mBlob;
+  RefPtr<BlobImpl> mBlobImpl;
   nsresult mRv;
 };
 
 // Simple runnable to propagate the error to the BlobStorage.
 class ErrorPropagationRunnable final : public Runnable {
  public:
   ErrorPropagationRunnable(MutableBlobStorage* aBlobStorage, nsresult aRv)
       : Runnable("dom::ErrorPropagationRunnable"),
@@ -186,107 +185,94 @@ class CloseFileRunnable final : public R
 class CreateBlobRunnable final : public Runnable,
                                  public TemporaryIPCBlobChildCallback {
  public:
   // We need to always declare refcounting because
   // TemporaryIPCBlobChildCallback has pure-virtual refcounting.
   NS_DECL_ISUPPORTS_INHERITED
 
   CreateBlobRunnable(MutableBlobStorage* aBlobStorage,
-                     already_AddRefed<nsISupports> aParent,
                      const nsACString& aContentType,
                      already_AddRefed<MutableBlobStorageCallback> aCallback)
       : Runnable("dom::CreateBlobRunnable"),
         mBlobStorage(aBlobStorage),
-        mParent(aParent),
         mContentType(aContentType),
         mCallback(aCallback) {
     MOZ_ASSERT(!NS_IsMainThread());
     MOZ_ASSERT(aBlobStorage);
   }
 
   NS_IMETHOD
   Run() override {
     MOZ_ASSERT(NS_IsMainThread());
     MOZ_ASSERT(mBlobStorage);
     mBlobStorage->AskForBlob(this, mContentType);
     return NS_OK;
   }
 
   void OperationSucceeded(BlobImpl* aBlobImpl) override {
-    nsCOMPtr<nsISupports> parent(std::move(mParent));
     RefPtr<MutableBlobStorageCallback> callback(std::move(mCallback));
-
-    RefPtr<Blob> blob = Blob::Create(parent, aBlobImpl);
-    callback->BlobStoreCompleted(mBlobStorage, blob, NS_OK);
+    callback->BlobStoreCompleted(mBlobStorage, aBlobImpl, NS_OK);
   }
 
   void OperationFailed(nsresult aRv) override {
     RefPtr<MutableBlobStorageCallback> callback(std::move(mCallback));
     callback->BlobStoreCompleted(mBlobStorage, nullptr, aRv);
   }
 
  private:
   ~CreateBlobRunnable() {
     MOZ_ASSERT(mBlobStorage);
     // If something when wrong, we still have to release data in the correct
     // thread.
-    NS_ProxyRelease("CreateBlobRunnable::mParent", mBlobStorage->EventTarget(),
-                    mParent.forget());
     NS_ProxyRelease("CreateBlobRunnable::mCallback",
                     mBlobStorage->EventTarget(), mCallback.forget());
   }
 
   RefPtr<MutableBlobStorage> mBlobStorage;
-  nsCOMPtr<nsISupports> mParent;
   nsCString mContentType;
   RefPtr<MutableBlobStorageCallback> mCallback;
 };
 
 NS_IMPL_ISUPPORTS_INHERITED0(CreateBlobRunnable, Runnable)
 
 // This task is used to know when the writing is completed. From the IO thread
 // it dispatches a CreateBlobRunnable to the main-thread.
 class LastRunnable final : public Runnable {
  public:
-  LastRunnable(MutableBlobStorage* aBlobStorage, nsISupports* aParent,
-               const nsACString& aContentType,
+  LastRunnable(MutableBlobStorage* aBlobStorage, const nsACString& aContentType,
                MutableBlobStorageCallback* aCallback)
       : Runnable("dom::LastRunnable"),
         mBlobStorage(aBlobStorage),
-        mParent(aParent),
         mContentType(aContentType),
         mCallback(aCallback) {
     MOZ_ASSERT(NS_IsMainThread());
     MOZ_ASSERT(mBlobStorage);
     MOZ_ASSERT(aCallback);
   }
 
   NS_IMETHOD
   Run() override {
     MOZ_ASSERT(!NS_IsMainThread());
 
-    RefPtr<Runnable> runnable = new CreateBlobRunnable(
-        mBlobStorage, mParent.forget(), mContentType, mCallback.forget());
+    RefPtr<Runnable> runnable =
+        new CreateBlobRunnable(mBlobStorage, mContentType, mCallback.forget());
     return mBlobStorage->EventTarget()->Dispatch(runnable, NS_DISPATCH_NORMAL);
   }
 
  private:
   ~LastRunnable() {
     MOZ_ASSERT(mBlobStorage);
     // If something when wrong, we still have to release data in the correct
     // thread.
-    NS_ProxyRelease("LastRunnable::mParent", mBlobStorage->EventTarget(),
-                    mParent.forget());
     NS_ProxyRelease("LastRunnable::mCallback", mBlobStorage->EventTarget(),
                     mCallback.forget());
   }
 
   RefPtr<MutableBlobStorage> mBlobStorage;
-  nsCOMPtr<nsISupports> mParent;
   nsCString mContentType;
   RefPtr<MutableBlobStorageCallback> mCallback;
 };
 
 }  // anonymous namespace
 
 MutableBlobStorage::MutableBlobStorage(MutableBlobStorageType aType,
                                        nsIEventTarget* aEventTarget,
@@ -327,19 +313,18 @@ MutableBlobStorage::~MutableBlobStorage(
   }
 
   if (mActor) {
     NS_ProxyRelease("MutableBlobStorage::mActor", EventTarget(),
                     mActor.forget());
   }
 }
 
-void MutableBlobStorage::GetBlobWhenReady(
-    nsISupports* aParent, const nsACString& aContentType,
-    MutableBlobStorageCallback* aCallback) {
+void MutableBlobStorage::GetBlobImplWhenReady(
+    const nsACString& aContentType, MutableBlobStorageCallback* aCallback) {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aCallback);
 
   MutexAutoLock lock(mMutex);
 
   // GetBlob can be called just once.
   MOZ_ASSERT(mStorageState != eClosed);
   StorageState previousState = mStorageState;
@@ -356,28 +341,26 @@ void MutableBlobStorage::GetBlobWhenRead
     }
 
     MOZ_ASSERT(mActor);
 
     // We want to wait until all the WriteRunnable are completed. The way we do
     // this is to go to the I/O thread and then we come back: the runnables are
     // executed in order and this LastRunnable will be... the last one.
     // This Runnable will also close the FD on the I/O thread.
-    RefPtr<Runnable> runnable =
-        new LastRunnable(this, aParent, aContentType, aCallback);
+    RefPtr<Runnable> runnable = new LastRunnable(this, aContentType, aCallback);
 
     // If the dispatching fails, we are shutting down and it's fine to do not
     // run the callback.
     Unused << DispatchToIOThread(runnable.forget());
     return;
   }
 
   // If we are waiting for the temporary file, it's better to wait...
   if (previousState == eWaitingForTemporaryFile) {
-    mPendingParent = aParent;
     mPendingContentType = aContentType;
     mPendingCallback = aCallback;
     return;
   }
 
   RefPtr<BlobImpl> blobImpl;
 
   if (mData) {
@@ -386,19 +369,18 @@ void MutableBlobStorage::GetBlobWhenRead
 
     mData = nullptr;  // The MemoryBlobImpl takes ownership of the buffer
     mDataLen = 0;
     mDataBufferLen = 0;
   } else {
     blobImpl = new EmptyBlobImpl(NS_ConvertUTF8toUTF16(aContentType));
   }
 
-  RefPtr<Blob> blob = Blob::Create(aParent, blobImpl);
   RefPtr<BlobCreationDoneRunnable> runnable =
-      new BlobCreationDoneRunnable(this, aCallback, blob, NS_OK);
+      new BlobCreationDoneRunnable(this, aCallback, blobImpl, NS_OK);
 
   nsresult error =
       EventTarget()->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
   if (NS_WARN_IF(NS_FAILED(error))) {
     return;
   }
 }
 
@@ -586,29 +568,28 @@ void MutableBlobStorage::TemporaryFileCr
   mData = nullptr;
 
   nsresult rv = DispatchToIOThread(runnable.forget());
   if (NS_WARN_IF(NS_FAILED(rv))) {
     // Shutting down, we cannot continue.
     return;
   }
 
-  // If we are closed, it means that GetBlobWhenReady() has been called when we
-  // were already waiting for a temporary file-descriptor. Finally we are here,
-  // AdoptBuffer runnable is going to write the current buffer into this file.
-  // After that, there is nothing else to write, and we dispatch LastRunnable
-  // which ends up calling mPendingCallback via CreateBlobRunnable.
+  // If we are closed, it means that GetBlobImplWhenReady() has been called when
+  // we were already waiting for a temporary file-descriptor. Finally we are
+  // here, AdoptBuffer runnable is going to write the current buffer into this
+  // file. After that, there is nothing else to write, and we dispatch
+  // LastRunnable which ends up calling mPendingCallback via CreateBlobRunnable.
   if (mStorageState == eClosed) {
     MOZ_ASSERT(mPendingCallback);
 
-    RefPtr<Runnable> runnable = new LastRunnable(
-        this, mPendingParent, mPendingContentType, mPendingCallback);
+    RefPtr<Runnable> runnable =
+        new LastRunnable(this, mPendingContentType, mPendingCallback);
     Unused << DispatchToIOThread(runnable.forget());
 
-    mPendingParent = nullptr;
     mPendingCallback = nullptr;
   }
 }
 
 void MutableBlobStorage::AskForBlob(TemporaryIPCBlobChildCallback* aCallback,
                                     const nsACString& aContentType) {
   MOZ_ASSERT(NS_IsMainThread());
 
--- a/dom/file/MutableBlobStorage.h
+++ b/dom/file/MutableBlobStorage.h
@@ -26,18 +26,18 @@ class BlobImpl;
 class MutableBlobStorage;
 class TemporaryIPCBlobChild;
 class TemporaryIPCBlobChildCallback;
 
 class MutableBlobStorageCallback {
  public:
   NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
 
-  virtual void BlobStoreCompleted(MutableBlobStorage* aBlobStorage, Blob* aBlob,
-                                  nsresult aRv) = 0;
+  virtual void BlobStoreCompleted(MutableBlobStorage* aBlobStorage,
+                                  BlobImpl* aBlob, nsresult aRv) = 0;
 };
 
 // This class is must be created and used on main-thread, except for Append()
 // that can be called on any thread.
 class MutableBlobStorage final {
  public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MutableBlobStorage)
 
@@ -48,19 +48,19 @@ class MutableBlobStorage final {
 
   explicit MutableBlobStorage(MutableBlobStorageType aType,
                               nsIEventTarget* aEventTarget = nullptr,
                               uint32_t aMaxMemory = 0);
 
   nsresult Append(const void* aData, uint32_t aLength);
 
   // This method can be called just once.
-  // The callback will be called when the Blob is ready.
-  void GetBlobWhenReady(nsISupports* aParent, const nsACString& aContentType,
-                        MutableBlobStorageCallback* aCallback);
+  // The callback will be called when the BlobImpl is ready.
+  void GetBlobImplWhenReady(const nsACString& aContentType,
+                            MutableBlobStorageCallback* aCallback);
 
   void TemporaryFileCreated(PRFileDesc* aFD);
 
   void AskForBlob(TemporaryIPCBlobChildCallback* aCallback,
                   const nsACString& aContentType);
 
   void ErrorPropagated(nsresult aRv);
 
@@ -112,17 +112,16 @@ class MutableBlobStorage final {
 
   PRFileDesc* mFD;
 
   nsresult mErrorResult;
 
   RefPtr<TaskQueue> mTaskQueue;
   nsCOMPtr<nsIEventTarget> mEventTarget;
 
-  nsCOMPtr<nsISupports> mPendingParent;
   nsCString mPendingContentType;
   RefPtr<MutableBlobStorageCallback> mPendingCallback;
 
   RefPtr<TemporaryIPCBlobChild> mActor;
 
   // This value is used when we go from eInMemory to eWaitingForTemporaryFile
   // and eventually eInTemporaryFile. If the size of the buffer is >=
   // mMaxMemory, the creation of the temporary file will start.
--- a/dom/file/MutableBlobStreamListener.cpp
+++ b/dom/file/MutableBlobStreamListener.cpp
@@ -9,20 +9,19 @@
 #include "nsIInputStream.h"
 #include "nsThreadUtils.h"
 
 namespace mozilla {
 namespace dom {
 
 MutableBlobStreamListener::MutableBlobStreamListener(
     MutableBlobStorage::MutableBlobStorageType aStorageType,
-    nsISupports* aParent, const nsACString& aContentType,
-    MutableBlobStorageCallback* aCallback, nsIEventTarget* aEventTarget)
+    const nsACString& aContentType, MutableBlobStorageCallback* aCallback,
+    nsIEventTarget* aEventTarget)
     : mCallback(aCallback),
-      mParent(aParent),
       mStorageType(aStorageType),
       mContentType(aContentType),
       mEventTarget(aEventTarget) {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aCallback);
 
   if (!mEventTarget) {
     mEventTarget = GetMainThreadEventTarget();
@@ -59,17 +58,17 @@ MutableBlobStreamListener::OnStopRequest
   storage.swap(mStorage);
 
   // Let's propagate the error simulating a failure of the storage.
   if (NS_FAILED(aStatus)) {
     mCallback->BlobStoreCompleted(storage, nullptr, aStatus);
     return NS_OK;
   }
 
-  storage->GetBlobWhenReady(mParent, mContentType, mCallback);
+  storage->GetBlobImplWhenReady(mContentType, mCallback);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 MutableBlobStreamListener::OnDataAvailable(nsIRequest* aRequest,
                                            nsIInputStream* aStream,
                                            uint64_t aSourceOffset,
                                            uint32_t aCount) {
--- a/dom/file/MutableBlobStreamListener.h
+++ b/dom/file/MutableBlobStreamListener.h
@@ -21,32 +21,30 @@ class MutableBlobStreamListener final
       public nsIThreadRetargetableStreamListener {
  public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSISTREAMLISTENER
   NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
   NS_DECL_NSIREQUESTOBSERVER
 
   MutableBlobStreamListener(MutableBlobStorage::MutableBlobStorageType aType,
-                            nsISupports* aParent,
                             const nsACString& aContentType,
                             MutableBlobStorageCallback* aCallback,
                             nsIEventTarget* aEventTarget = nullptr);
 
  private:
   ~MutableBlobStreamListener();
 
   static nsresult WriteSegmentFun(nsIInputStream* aWriter, void* aClosure,
                                   const char* aFromSegment, uint32_t aOffset,
                                   uint32_t aCount, uint32_t* aWriteCount);
 
   RefPtr<MutableBlobStorage> mStorage;
   RefPtr<MutableBlobStorageCallback> mCallback;
 
-  nsCOMPtr<nsISupports> mParent;
   MutableBlobStorage::MutableBlobStorageType mStorageType;
   nsCString mContentType;
   nsCOMPtr<nsIEventTarget> mEventTarget;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
--- a/dom/file/ipc/FileCreatorChild.cpp
+++ b/dom/file/ipc/FileCreatorChild.cpp
@@ -35,16 +35,21 @@ mozilla::ipc::IPCResult FileCreatorChild
   }
 
   MOZ_ASSERT(aResult.type() == FileCreationResult::TFileCreationSuccessResult);
 
   RefPtr<dom::BlobImpl> impl = dom::IPCBlobUtils::Deserialize(
       aResult.get_FileCreationSuccessResult().blob());
 
   RefPtr<File> file = File::Create(promise->GetParentObject(), impl);
+  if (NS_WARN_IF(!file)) {
+    promise->MaybeReject(NS_ERROR_FAILURE);
+    return IPC_OK();
+  }
+
   promise->MaybeResolve(file);
   return IPC_OK();
 }
 
 void FileCreatorChild::ActorDestroy(ActorDestroyReason aWhy) {
   if (mPromise) {
     mPromise->MaybeReject(NS_ERROR_FAILURE);
     mPromise = nullptr;
--- a/dom/file/moz.build
+++ b/dom/file/moz.build
@@ -9,16 +9,17 @@ with Files("**"):
 
 DIRS += ['ipc', 'uri' ]
 
 EXPORTS.mozilla.dom += [
     'BaseBlobImpl.h',
     'Blob.h',
     'BlobImpl.h',
     'BlobSet.h',
+    'EmptyBlobImpl.h',
     'File.h',
     'FileBlobImpl.h',
     'FileCreatorHelper.h',
     'FileList.h',
     'FileReader.h',
     'FileReaderSync.h',
     'MemoryBlobImpl.h',
     'MultipartBlobImpl.h',
--- a/dom/filesystem/Directory.cpp
+++ b/dom/filesystem/Directory.cpp
@@ -22,25 +22,25 @@ namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(Directory)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Directory)
   if (tmp->mFileSystem) {
     tmp->mFileSystem->Unlink();
     tmp->mFileSystem = nullptr;
   }
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Directory)
   if (tmp->mFileSystem) {
     tmp->mFileSystem->Traverse(cb);
   }
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(Directory)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(Directory)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(Directory)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Directory)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
@@ -52,48 +52,54 @@ already_AddRefed<Directory> Directory::C
                                                    const nsAString& aRealPath,
                                                    ErrorResult& aRv) {
   nsCOMPtr<nsIFile> path;
   aRv = NS_NewLocalFile(aRealPath, true, getter_AddRefs(path));
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
-  return Create(aGlobal.GetAsSupports(), path);
+  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
+  if (NS_WARN_IF(!global)) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+
+  return Create(global, path);
 }
 
 /* static */
-already_AddRefed<Directory> Directory::Create(nsISupports* aParent,
+already_AddRefed<Directory> Directory::Create(nsIGlobalObject* aGlobal,
                                               nsIFile* aFile,
                                               FileSystemBase* aFileSystem) {
-  MOZ_ASSERT(aParent);
+  MOZ_ASSERT(aGlobal);
   MOZ_ASSERT(aFile);
 
-  RefPtr<Directory> directory = new Directory(aParent, aFile, aFileSystem);
+  RefPtr<Directory> directory = new Directory(aGlobal, aFile, aFileSystem);
   return directory.forget();
 }
 
-Directory::Directory(nsISupports* aParent, nsIFile* aFile,
+Directory::Directory(nsIGlobalObject* aGlobal, nsIFile* aFile,
                      FileSystemBase* aFileSystem)
-    : mParent(aParent), mFile(aFile) {
+    : mGlobal(aGlobal), mFile(aFile) {
   MOZ_ASSERT(aFile);
 
   // aFileSystem can be null. In this case we create a OSFileSystem when needed.
   if (aFileSystem) {
     // More likely, this is a OSFileSystem. This object keeps a reference of
-    // mParent but it's not cycle collectable and to avoid manual
+    // mGlobal but it's not cycle collectable and to avoid manual
     // addref/release, it's better to have 1 object per directory. For this
     // reason we clone it here.
     mFileSystem = aFileSystem->Clone();
   }
 }
 
 Directory::~Directory() {}
 
-nsISupports* Directory::GetParentObject() const { return mParent; }
+nsIGlobalObject* Directory::GetParentObject() const { return mGlobal; }
 
 JSObject* Directory::WrapObject(JSContext* aCx,
                                 JS::Handle<JSObject*> aGivenProto) {
   return Directory_Binding::Wrap(aCx, this, aGivenProto);
 }
 
 void Directory::GetName(nsAString& aRetval, ErrorResult& aRv) {
   aRetval.Truncate();
@@ -178,17 +184,17 @@ FileSystemBase* Directory::GetFileSystem
   if (!mFileSystem) {
     nsAutoString path;
     aRv = mFile->GetPath(path);
     if (NS_WARN_IF(aRv.Failed())) {
       return nullptr;
     }
 
     RefPtr<OSFileSystem> fs = new OSFileSystem(path);
-    fs->Init(mParent);
+    fs->Init(mGlobal);
 
     mFileSystem = fs;
   }
 
   return mFileSystem;
 }
 
 }  // namespace dom
--- a/dom/filesystem/Directory.h
+++ b/dom/filesystem/Directory.h
@@ -25,23 +25,23 @@ class Directory final : public nsISuppor
  public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Directory)
 
   static already_AddRefed<Directory> Constructor(const GlobalObject& aGlobal,
                                                  const nsAString& aRealPath,
                                                  ErrorResult& aRv);
 
-  static already_AddRefed<Directory> Create(nsISupports* aParent,
+  static already_AddRefed<Directory> Create(nsIGlobalObject* aGlobal,
                                             nsIFile* aDirectory,
                                             FileSystemBase* aFileSystem = 0);
 
   // ========= Begin WebIDL bindings. ===========
 
-  nsISupports* GetParentObject() const;
+  nsIGlobalObject* GetParentObject() const;
 
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aGivenProto) override;
 
   void GetName(nsAString& aRetval, ErrorResult& aRv);
 
   // From
   // https://microsoftedge.github.io/directory-upload/proposal.html#directory-interface
@@ -81,26 +81,26 @@ class Directory final : public nsISuppor
    */
   void SetContentFilters(const nsAString& aFilters);
 
   FileSystemBase* GetFileSystem(ErrorResult& aRv);
 
   nsIFile* GetInternalNsIFile() const { return mFile; }
 
  private:
-  Directory(nsISupports* aParent, nsIFile* aFile,
+  Directory(nsIGlobalObject* aGlobal, nsIFile* aFile,
             FileSystemBase* aFileSystem = nullptr);
   ~Directory();
 
   /*
    * Convert relative DOM path to the absolute real path.
    */
   nsresult DOMPathToRealPath(const nsAString& aPath, nsIFile** aFile) const;
 
-  nsCOMPtr<nsISupports> mParent;
+  nsCOMPtr<nsIGlobalObject> mGlobal;
   RefPtr<FileSystemBase> mFileSystem;
   nsCOMPtr<nsIFile> mFile;
 
   nsString mFilters;
   nsString mPath;
 };
 
 }  // namespace dom
--- a/dom/filesystem/FileSystemBase.cpp
+++ b/dom/filesystem/FileSystemBase.cpp
@@ -16,17 +16,17 @@ FileSystemBase::FileSystemBase() : mShut
 
 FileSystemBase::~FileSystemBase() { AssertIsOnOwningThread(); }
 
 void FileSystemBase::Shutdown() {
   AssertIsOnOwningThread();
   mShutdown = true;
 }
 
-nsISupports* FileSystemBase::GetParentObject() const {
+nsIGlobalObject* FileSystemBase::GetParentObject() const {
   AssertIsOnOwningThread();
   return nullptr;
 }
 
 bool FileSystemBase::GetRealPath(BlobImpl* aFile, nsIFile** aPath) const {
   AssertIsOnOwningThread();
   MOZ_ASSERT(aFile, "aFile Should not be null.");
   MOZ_ASSERT(aPath);
--- a/dom/filesystem/FileSystemBase.h
+++ b/dom/filesystem/FileSystemBase.h
@@ -25,17 +25,17 @@ class FileSystemBase {
 
   // SerializeDOMPath the FileSystem to string.
   virtual void SerializeDOMPath(nsAString& aOutput) const = 0;
 
   virtual already_AddRefed<FileSystemBase> Clone() = 0;
 
   virtual bool ShouldCreateDirectory() = 0;
 
-  virtual nsISupports* GetParentObject() const;
+  virtual nsIGlobalObject* GetParentObject() const;
 
   virtual void GetDirectoryName(nsIFile* aFile, nsAString& aRetval,
                                 ErrorResult& aRv) const;
 
   void GetDOMPath(nsIFile* aFile, nsAString& aRetval, ErrorResult& aRv) const;
 
   /*
    * Return the local root path of the FileSystem implementation.
--- a/dom/filesystem/GetDirectoryListingTask.cpp
+++ b/dom/filesystem/GetDirectoryListingTask.cpp
@@ -32,22 +32,18 @@ GetDirectoryListingTaskChild::Create(Fil
                                      Directory* aDirectory,
                                      nsIFile* aTargetPath,
                                      const nsAString& aFilters,
                                      ErrorResult& aRv) {
   MOZ_ASSERT(aFileSystem);
   MOZ_ASSERT(aDirectory);
   aFileSystem->AssertIsOnOwningThread();
 
-  nsCOMPtr<nsIGlobalObject> globalObject =
-      do_QueryInterface(aFileSystem->GetParentObject());
-  if (NS_WARN_IF(!globalObject)) {
-    aRv.Throw(NS_ERROR_FAILURE);
-    return nullptr;
-  }
+  nsCOMPtr<nsIGlobalObject> globalObject = aFileSystem->GetParentObject();
+  MOZ_ASSERT(globalObject);
 
   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())) {
@@ -118,18 +114,20 @@ void GetDirectoryListingTaskChild::SetSu
     if (data.type() == FileSystemDirectoryListingResponseData::
                            TFileSystemDirectoryListingResponseFile) {
       const FileSystemDirectoryListingResponseFile& d =
           data.get_FileSystemDirectoryListingResponseFile();
 
       RefPtr<BlobImpl> blobImpl = IPCBlobUtils::Deserialize(d.blob());
       MOZ_ASSERT(blobImpl);
 
-      RefPtr<File> file =
-          File::Create(mFileSystem->GetParentObject(), blobImpl);
+      nsCOMPtr<nsIGlobalObject> globalObject = mFileSystem->GetParentObject();
+      MOZ_ASSERT(globalObject);
+
+      RefPtr<File> file = File::Create(globalObject, blobImpl);
       MOZ_ASSERT(file);
 
       ofd->SetAsFile() = file;
     } else {
       MOZ_ASSERT(data.type() ==
                  FileSystemDirectoryListingResponseData::
                      TFileSystemDirectoryListingResponseDirectory);
       const FileSystemDirectoryListingResponseDirectory& d =
--- a/dom/filesystem/GetFileOrDirectoryTask.cpp
+++ b/dom/filesystem/GetFileOrDirectoryTask.cpp
@@ -25,18 +25,17 @@ 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);
 
-  nsCOMPtr<nsIGlobalObject> globalObject =
-      do_QueryInterface(aFileSystem->GetParentObject());
+  nsCOMPtr<nsIGlobalObject> globalObject = aFileSystem->GetParentObject();
   if (NS_WARN_IF(!globalObject)) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   RefPtr<GetFileOrDirectoryTaskChild> task =
       new GetFileOrDirectoryTaskChild(globalObject, aFileSystem, aTargetPath);
 
@@ -86,18 +85,23 @@ void GetFileOrDirectoryTaskChild::SetSuc
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   switch (aValue.type()) {
     case FileSystemResponseValue::TFileSystemFileResponse: {
       FileSystemFileResponse r = aValue;
 
       RefPtr<BlobImpl> blobImpl = IPCBlobUtils::Deserialize(r.blob());
       MOZ_ASSERT(blobImpl);
 
-      mResultFile = File::Create(mFileSystem->GetParentObject(), blobImpl);
-      MOZ_ASSERT(mResultFile);
+      nsCOMPtr<nsIGlobalObject> globalObject = mFileSystem->GetParentObject();
+      MOZ_ASSERT(globalObject);
+
+      mResultFile = File::Create(globalObject, blobImpl);
+      if (NS_WARN_IF(!mResultFile)) {
+        aRv.Throw(NS_ERROR_FAILURE);
+      }
       break;
     }
     case FileSystemResponseValue::TFileSystemDirectoryResponse: {
       FileSystemDirectoryResponse r = aValue;
 
       nsCOMPtr<nsIFile> file;
       aRv = NS_NewLocalFile(r.realPath(), true, getter_AddRefs(file));
       if (NS_WARN_IF(aRv.Failed())) {
--- a/dom/filesystem/GetFilesHelper.cpp
+++ b/dom/filesystem/GetFilesHelper.cpp
@@ -19,82 +19,71 @@ 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:
   static void MaybeReleaseOnMainThread(
       nsTArray<RefPtr<Promise>>& aPromises,
-      nsTArray<RefPtr<GetFilesCallback>>& aCallbacks,
-      Sequence<RefPtr<File>>& aFiles,
-      already_AddRefed<nsIGlobalObject> aGlobal) {
-    nsCOMPtr<nsIGlobalObject> global(aGlobal);
+      nsTArray<RefPtr<GetFilesCallback>>& aCallbacks) {
     if (NS_IsMainThread()) {
       return;
     }
 
     RefPtr<ReleaseRunnable> runnable =
-        new ReleaseRunnable(aPromises, aCallbacks, aFiles, global.forget());
+        new ReleaseRunnable(aPromises, aCallbacks);
     FileSystemUtils::DispatchRunnable(nullptr, runnable.forget());
   }
 
   NS_IMETHOD
   Run() override {
     MOZ_ASSERT(NS_IsMainThread());
 
     mPromises.Clear();
     mCallbacks.Clear();
-    mFiles.Clear();
-    mGlobal = nullptr;
 
     return NS_OK;
   }
 
  private:
   ReleaseRunnable(nsTArray<RefPtr<Promise>>& aPromises,
-                  nsTArray<RefPtr<GetFilesCallback>>& aCallbacks,
-                  Sequence<RefPtr<File>>& aFiles,
-                  already_AddRefed<nsIGlobalObject> aGlobal)
+                  nsTArray<RefPtr<GetFilesCallback>>& aCallbacks)
       : Runnable("dom::ReleaseRunnable") {
     mPromises.SwapElements(aPromises);
     mCallbacks.SwapElements(aCallbacks);
-    mFiles.SwapElements(aFiles);
-    mGlobal = aGlobal;
   }
 
   nsTArray<RefPtr<Promise>> mPromises;
   nsTArray<RefPtr<GetFilesCallback>> mCallbacks;
-  Sequence<RefPtr<File>> mFiles;
-  nsCOMPtr<nsIGlobalObject> mGlobal;
 };
 
 }  // namespace
 
 ///////////////////////////////////////////////////////////////////////////////
 // GetFilesHelper Base class
 
 already_AddRefed<GetFilesHelper> GetFilesHelper::Create(
-    nsIGlobalObject* aGlobal,
     const nsTArray<OwningFileOrDirectory>& aFilesOrDirectory,
     bool aRecursiveFlag, ErrorResult& aRv) {
   RefPtr<GetFilesHelper> helper;
 
   if (XRE_IsParentProcess()) {
-    helper = new GetFilesHelper(aGlobal, aRecursiveFlag);
+    helper = new GetFilesHelper(aRecursiveFlag);
   } else {
-    helper = new GetFilesHelperChild(aGlobal, aRecursiveFlag);
+    helper = new GetFilesHelperChild(aRecursiveFlag);
   }
 
   nsAutoString directoryPath;
 
   for (uint32_t i = 0; i < aFilesOrDirectory.Length(); ++i) {
     const OwningFileOrDirectory& data = aFilesOrDirectory[i];
     if (data.IsFile()) {
-      if (!helper->mFiles.AppendElement(data.GetAsFile(), fallible)) {
+      if (!helper->mTargetBlobImplArray.AppendElement(data.GetAsFile()->Impl(),
+                                                      fallible)) {
         aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
         return nullptr;
       }
     } else {
       MOZ_ASSERT(data.IsDirectory());
 
       // We support the upload of only 1 top-level directory from our
       // directory picker. This means that we cannot have more than 1
@@ -112,39 +101,37 @@ already_AddRefed<GetFilesHelper> GetFile
   }
 
   // No directories to explore.
   if (directoryPath.IsEmpty()) {
     helper->mListingCompleted = true;
     return helper.forget();
   }
 
-  MOZ_ASSERT(helper->mFiles.IsEmpty());
+  MOZ_ASSERT(helper->mTargetBlobImplArray.IsEmpty());
   helper->SetDirectoryPath(directoryPath);
 
   helper->Work(aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   return helper.forget();
 }
 
-GetFilesHelper::GetFilesHelper(nsIGlobalObject* aGlobal, bool aRecursiveFlag)
+GetFilesHelper::GetFilesHelper(bool aRecursiveFlag)
     : Runnable("GetFilesHelper"),
       GetFilesHelperBase(aRecursiveFlag),
-      mGlobal(aGlobal),
       mListingCompleted(false),
       mErrorResult(NS_OK),
       mMutex("GetFilesHelper::mMutex"),
       mCanceled(false) {}
 
 GetFilesHelper::~GetFilesHelper() {
-  ReleaseRunnable::MaybeReleaseOnMainThread(mPromises, mCallbacks, mFiles,
-                                            mGlobal.forget());
+  ReleaseRunnable::MaybeReleaseOnMainThread(mPromises, mCallbacks);
 }
 
 void GetFilesHelper::AddPromise(Promise* aPromise) {
   MOZ_ASSERT(aPromise);
 
   // Still working.
   if (!mListingCompleted) {
     mPromises.AppendElement(aPromise);
@@ -164,33 +151,29 @@ void GetFilesHelper::AddCallback(GetFile
     return;
   }
 
   MOZ_ASSERT(mCallbacks.IsEmpty());
   RunCallback(aCallback);
 }
 
 void GetFilesHelper::Unlink() {
-  mGlobal = nullptr;
-  mFiles.Clear();
   mPromises.Clear();
   mCallbacks.Clear();
 
   {
     MutexAutoLock lock(mMutex);
     mCanceled = true;
   }
 
   Cancel();
 }
 
 void GetFilesHelper::Traverse(nsCycleCollectionTraversalCallback& cb) {
   GetFilesHelper* tmp = this;
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal);
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFiles);
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromises);
 }
 
 void GetFilesHelper::Work(ErrorResult& aRv) {
   nsCOMPtr<nsIEventTarget> target =
       do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
   MOZ_ASSERT(target);
 
@@ -218,18 +201,16 @@ GetFilesHelper::Run() {
   }
 
   // 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;
   }
 
-  RunMainThread();
-
   OperationCompleted();
   return NS_OK;
 }
 
 void GetFilesHelper::OperationCompleted() {
   // We mark the operation as completed here.
   mListingCompleted = true;
 
@@ -269,39 +250,16 @@ void GetFilesHelper::RunIO() {
 
   nsAutoString domPath;
   domPath.AssignLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL);
   domPath.Append(leafName);
 
   mErrorResult = ExploreDirectory(domPath, file);
 }
 
-void GetFilesHelper::RunMainThread() {
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(!mDirectoryPath.IsEmpty());
-  MOZ_ASSERT(!mListingCompleted);
-
-  // If there is an error, do nothing.
-  if (NS_FAILED(mErrorResult)) {
-    return;
-  }
-
-  // Create the sequence of Files.
-  for (uint32_t i = 0; i < mTargetBlobImplArray.Length(); ++i) {
-    RefPtr<File> domFile = File::Create(mGlobal, mTargetBlobImplArray[i]);
-    MOZ_ASSERT(domFile);
-
-    if (!mFiles.AppendElement(domFile, fallible)) {
-      mErrorResult = NS_ERROR_OUT_OF_MEMORY;
-      mFiles.Clear();
-      return;
-    }
-  }
-}
-
 nsresult GetFilesHelperBase::ExploreDirectory(const nsAString& aDOMPath,
                                               nsIFile* aFile) {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(aFile);
 
   // We check if this operation has to be terminated at each recursion.
   if (IsCanceled()) {
     return NS_OK;
@@ -436,31 +394,51 @@ bool GetFilesHelperBase::ShouldFollowSym
   return !mExploredDirectories.Contains(targetPath);
 }
 
 void GetFilesHelper::ResolveOrRejectPromise(Promise* aPromise) {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mListingCompleted);
   MOZ_ASSERT(aPromise);
 
+  Sequence<RefPtr<File>> files;
+
+  if (NS_SUCCEEDED(mErrorResult)) {
+    for (uint32_t i = 0; i < mTargetBlobImplArray.Length(); ++i) {
+      RefPtr<File> domFile =
+          File::Create(aPromise->GetParentObject(), mTargetBlobImplArray[i]);
+      if (NS_WARN_IF(!domFile)) {
+        mErrorResult = NS_ERROR_FAILURE;
+        files.Clear();
+        break;
+      }
+
+      if (!files.AppendElement(domFile, fallible)) {
+        mErrorResult = NS_ERROR_OUT_OF_MEMORY;
+        files.Clear();
+        break;
+      }
+    }
+  }
+
   // Error propagation.
   if (NS_FAILED(mErrorResult)) {
     aPromise->MaybeReject(mErrorResult);
     return;
   }
 
-  aPromise->MaybeResolve(mFiles);
+  aPromise->MaybeResolve(files);
 }
 
 void GetFilesHelper::RunCallback(GetFilesCallback* aCallback) {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mListingCompleted);
   MOZ_ASSERT(aCallback);
 
-  aCallback->Callback(mErrorResult, mFiles);
+  aCallback->Callback(mErrorResult, mTargetBlobImplArray);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 // GetFilesHelperChild class
 
 void GetFilesHelperChild::Work(ErrorResult& aRv) {
   ContentChild* cc = ContentChild::GetSingleton();
   if (NS_WARN_IF(!cc)) {
@@ -491,20 +469,17 @@ void GetFilesHelperChild::Cancel() {
   cc->DeleteGetFilesRequest(mUUID, this);
 }
 
 bool GetFilesHelperChild::AppendBlobImpl(BlobImpl* aBlobImpl) {
   MOZ_ASSERT(mPendingOperation);
   MOZ_ASSERT(aBlobImpl);
   MOZ_ASSERT(aBlobImpl->IsFile());
 
-  RefPtr<File> file = File::Create(mGlobal, aBlobImpl);
-  MOZ_ASSERT(file);
-
-  return mFiles.AppendElement(file, fallible);
+  return mTargetBlobImplArray.AppendElement(aBlobImpl, fallible);
 }
 
 void GetFilesHelperChild::Finished(nsresult aError) {
   MOZ_ASSERT(mPendingOperation);
   MOZ_ASSERT(NS_SUCCEEDED(mErrorResult));
 
   mPendingOperation = false;
   mErrorResult = aError;
@@ -518,31 +493,31 @@ void GetFilesHelperChild::Finished(nsres
 class GetFilesHelperParentCallback final : public GetFilesCallback {
  public:
   explicit GetFilesHelperParentCallback(GetFilesHelperParent* aParent)
       : mParent(aParent) {
     MOZ_ASSERT(aParent);
   }
 
   void Callback(nsresult aStatus,
-                const Sequence<RefPtr<File>>& aFiles) override {
+                const FallibleTArray<RefPtr<BlobImpl>>& aBlobImpls) override {
     if (NS_FAILED(aStatus)) {
       mParent->mContentParent->SendGetFilesResponseAndForget(
           mParent->mUUID, GetFilesResponseFailure(aStatus));
       return;
     }
 
     GetFilesResponseSuccess success;
 
     nsTArray<IPCBlob>& ipcBlobs = success.blobs();
-    ipcBlobs.SetLength(aFiles.Length());
+    ipcBlobs.SetLength(aBlobImpls.Length());
 
-    for (uint32_t i = 0; i < aFiles.Length(); ++i) {
+    for (uint32_t i = 0; i < aBlobImpls.Length(); ++i) {
       nsresult rv = IPCBlobUtils::Serialize(
-          aFiles[i]->Impl(), mParent->mContentParent, ipcBlobs[i]);
+          aBlobImpls[i], mParent->mContentParent, ipcBlobs[i]);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         mParent->mContentParent->SendGetFilesResponseAndForget(
             mParent->mUUID, GetFilesResponseFailure(NS_ERROR_OUT_OF_MEMORY));
         return;
       }
     }
 
     mParent->mContentParent->SendGetFilesResponseAndForget(mParent->mUUID,
@@ -552,17 +527,17 @@ class GetFilesHelperParentCallback final
  private:
   // Raw pointer because this callback is kept alive by this parent object.
   GetFilesHelperParent* mParent;
 };
 
 GetFilesHelperParent::GetFilesHelperParent(const nsID& aUUID,
                                            ContentParent* aContentParent,
                                            bool aRecursiveFlag)
-    : GetFilesHelper(nullptr, aRecursiveFlag),
+    : GetFilesHelper(aRecursiveFlag),
       mContentParent(aContentParent),
       mUUID(aUUID) {}
 
 GetFilesHelperParent::~GetFilesHelperParent() {
   NS_ReleaseOnMainThreadSystemGroup("GetFilesHelperParent::mContentParent",
                                     mContentParent.forget());
 }
 
--- a/dom/filesystem/GetFilesHelper.h
+++ b/dom/filesystem/GetFilesHelper.h
@@ -25,17 +25,17 @@ class GetFilesHelperParent;
 class OwningFileOrDirectory;
 class Promise;
 
 class GetFilesCallback {
  public:
   NS_INLINE_DECL_REFCOUNTING(GetFilesCallback);
 
   virtual void Callback(nsresult aStatus,
-                        const Sequence<RefPtr<File>>& aFiles) = 0;
+                        const FallibleTArray<RefPtr<BlobImpl>>& aBlobImpls) = 0;
 
  protected:
   virtual ~GetFilesCallback() {}
 };
 
 class GetFilesHelperBase {
  protected:
   explicit GetFilesHelperBase(bool aRecursiveFlag)
@@ -60,30 +60,29 @@ class GetFilesHelperBase {
 
 // Retrieving the list of files can be very time/IO consuming. We use this
 // helper class to do it just once.
 class GetFilesHelper : public Runnable, public GetFilesHelperBase {
   friend class GetFilesHelperParent;
 
  public:
   static already_AddRefed<GetFilesHelper> Create(
-      nsIGlobalObject* aGlobal,
       const nsTArray<OwningFileOrDirectory>& aFilesOrDirectory,
       bool aRecursiveFlag, ErrorResult& aRv);
 
   void AddPromise(Promise* aPromise);
 
   void AddCallback(GetFilesCallback* aCallback);
 
   // CC methods
   void Unlink();
   void Traverse(nsCycleCollectionTraversalCallback& cb);
 
  protected:
-  GetFilesHelper(nsIGlobalObject* aGlobal, bool aRecursiveFlag);
+  explicit GetFilesHelper(bool aRecursiveFlag);
 
   virtual ~GetFilesHelper();
 
   void SetDirectoryPath(const nsAString& aDirectoryPath) {
     mDirectoryPath = aDirectoryPath;
   }
 
   virtual bool IsCanceled() override {
@@ -95,48 +94,41 @@ class GetFilesHelper : public Runnable, 
 
   virtual void Cancel(){};
 
   NS_IMETHOD
   Run() override;
 
   void RunIO();
 
-  void RunMainThread();
-
   void OperationCompleted();
 
   void ResolveOrRejectPromise(Promise* aPromise);
 
   void RunCallback(GetFilesCallback* aCallback);
 
-  nsCOMPtr<nsIGlobalObject> mGlobal;
-
   bool mListingCompleted;
   nsString mDirectoryPath;
 
-  // This is the real File sequence that we expose via Promises.
-  Sequence<RefPtr<File>> mFiles;
-
   // Error code to propagate.
   nsresult mErrorResult;
 
   nsTArray<RefPtr<Promise>> mPromises;
   nsTArray<RefPtr<GetFilesCallback>> mCallbacks;
 
   Mutex mMutex;
 
   // This variable is protected by mutex.
   bool mCanceled;
 };
 
 class GetFilesHelperChild final : public GetFilesHelper {
  public:
-  GetFilesHelperChild(nsIGlobalObject* aGlobal, bool aRecursiveFlag)
-      : GetFilesHelper(aGlobal, aRecursiveFlag), mPendingOperation(false) {}
+  explicit GetFilesHelperChild(bool aRecursiveFlag)
+      : GetFilesHelper(aRecursiveFlag), mPendingOperation(false) {}
 
   virtual void Work(ErrorResult& aRv) override;
 
   virtual void Cancel() override;
 
   bool AppendBlobImpl(BlobImpl* aBlobImpl);
 
   void Finished(nsresult aResult);
--- a/dom/filesystem/GetFilesTask.cpp
+++ b/dom/filesystem/GetFilesTask.cpp
@@ -27,18 +27,17 @@ namespace dom {
 /* static */
 already_AddRefed<GetFilesTaskChild> GetFilesTaskChild::Create(
     FileSystemBase* aFileSystem, Directory* aDirectory, nsIFile* aTargetPath,
     bool aRecursiveFlag, ErrorResult& aRv) {
   MOZ_ASSERT(aFileSystem);
   MOZ_ASSERT(aDirectory);
   aFileSystem->AssertIsOnOwningThread();
 
-  nsCOMPtr<nsIGlobalObject> globalObject =
-      do_QueryInterface(aFileSystem->GetParentObject());
+  nsCOMPtr<nsIGlobalObject> globalObject = aFileSystem->GetParentObject();
   if (NS_WARN_IF(!globalObject)) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   RefPtr<GetFilesTaskChild> task = new GetFilesTaskChild(
       globalObject, aFileSystem, aDirectory, aTargetPath, aRecursiveFlag);
 
@@ -102,22 +101,29 @@ void GetFilesTaskChild::SetSuccessReques
 
   FileSystemFilesResponse r = aValue;
 
   if (!mTargetData.SetLength(r.data().Length(), mozilla::fallible_t())) {
     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return;
   }
 
+  nsCOMPtr<nsIGlobalObject> globalObject = mFileSystem->GetParentObject();
+  MOZ_ASSERT(globalObject);
+
   for (uint32_t i = 0; i < r.data().Length(); ++i) {
     const FileSystemFileResponse& data = r.data()[i];
     RefPtr<BlobImpl> blobImpl = IPCBlobUtils::Deserialize(data.blob());
     MOZ_ASSERT(blobImpl);
 
-    mTargetData[i] = File::Create(mFileSystem->GetParentObject(), blobImpl);
+    mTargetData[i] = File::Create(globalObject, blobImpl);
+    if (NS_WARN_IF(!mTargetData[i])) {
+      aRv.Throw(NS_ERROR_FAILURE);
+      return;
+    }
   }
 }
 
 void GetFilesTaskChild::HandlerCallback() {
   mFileSystem->AssertIsOnOwningThread();
   if (mFileSystem->IsShutdown()) {
     mPromise = nullptr;
     return;
--- a/dom/filesystem/OSFileSystem.cpp
+++ b/dom/filesystem/OSFileSystem.cpp
@@ -20,39 +20,34 @@ namespace dom {
 OSFileSystem::OSFileSystem(const nsAString& aRootDir) {
   mLocalRootPath = aRootDir;
 }
 
 already_AddRefed<FileSystemBase> OSFileSystem::Clone() {
   AssertIsOnOwningThread();
 
   RefPtr<OSFileSystem> fs = new OSFileSystem(mLocalRootPath);
-  if (mParent) {
-    fs->Init(mParent);
+  if (mGlobal) {
+    fs->Init(mGlobal);
   }
 
   return fs.forget();
 }
 
-void OSFileSystem::Init(nsISupports* aParent) {
+void OSFileSystem::Init(nsIGlobalObject* aGlobal) {
   AssertIsOnOwningThread();
-  MOZ_ASSERT(!mParent, "No duple Init() calls");
-  MOZ_ASSERT(aParent);
+  MOZ_ASSERT(!mGlobal, "No duple Init() calls");
+  MOZ_ASSERT(aGlobal);
 
-  mParent = aParent;
-
-#ifdef DEBUG
-  nsCOMPtr<nsIGlobalObject> obj = do_QueryInterface(aParent);
-  MOZ_ASSERT(obj);
-#endif
+  mGlobal = aGlobal;
 }
 
-nsISupports* OSFileSystem::GetParentObject() const {
+nsIGlobalObject* OSFileSystem::GetParentObject() const {
   AssertIsOnOwningThread();
-  return mParent;
+  return mGlobal;
 }
 
 bool OSFileSystem::IsSafeFile(nsIFile* aFile) const {
   // The concept of "safe files" is specific to the Device Storage API where
   // files are only "safe" if they're of a type that is appropriate for the
   // area of device storage that is being used.
   MOZ_CRASH("Don't use OSFileSystem with the Device Storage API");
   return true;
@@ -63,24 +58,24 @@ bool OSFileSystem::IsSafeDirectory(Direc
   // where a directory is only "safe" if it belongs to the area of device
   // storage that it is being used with.
   MOZ_CRASH("Don't use OSFileSystem with the Device Storage API");
   return true;
 }
 
 void OSFileSystem::Unlink() {
   AssertIsOnOwningThread();
-  mParent = nullptr;
+  mGlobal = nullptr;
 }
 
 void OSFileSystem::Traverse(nsCycleCollectionTraversalCallback& cb) {
   AssertIsOnOwningThread();
 
   OSFileSystem* tmp = this;
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent);
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal);
 }
 
 void OSFileSystem::SerializeDOMPath(nsAString& aOutput) const {
   AssertIsOnOwningThread();
   aOutput = mLocalRootPath;
 }
 
 /**
--- a/dom/filesystem/OSFileSystem.h
+++ b/dom/filesystem/OSFileSystem.h
@@ -11,61 +11,61 @@
 
 namespace mozilla {
 namespace dom {
 
 class OSFileSystem final : public FileSystemBase {
  public:
   explicit OSFileSystem(const nsAString& aRootDir);
 
-  void Init(nsISupports* aParent);
+  void Init(nsIGlobalObject* aGlobal);
 
   // Overrides FileSystemBase
 
   virtual already_AddRefed<FileSystemBase> Clone() override;
 
   virtual bool ShouldCreateDirectory() override {
     MOZ_CRASH("This should not be called.");
     // Because OSFileSystem should not be used when the creation of directories
     // is needed. For that we have OSFileSystemParent.
     return false;
   }
 
-  virtual nsISupports* GetParentObject() const override;
+  virtual nsIGlobalObject* GetParentObject() const override;
 
   virtual bool IsSafeFile(nsIFile* aFile) const override;
 
   virtual bool IsSafeDirectory(Directory* aDir) const override;
 
   virtual void SerializeDOMPath(nsAString& aOutput) const override;
 
   // CC methods
   virtual void Unlink() override;
   virtual void Traverse(nsCycleCollectionTraversalCallback& cb) override;
 
  private:
   virtual ~OSFileSystem() {}
 
-  nsCOMPtr<nsISupports> mParent;
+  nsCOMPtr<nsIGlobalObject> mGlobal;
 };
 
 class OSFileSystemParent final : public FileSystemBase {
  public:
   explicit OSFileSystemParent(const nsAString& aRootDir);
 
   // Overrides FileSystemBase
 
   virtual already_AddRefed<FileSystemBase> Clone() override {
     MOZ_CRASH("This should not be called on the PBackground thread.");
     return nullptr;
   }
 
   virtual bool ShouldCreateDirectory() override { return false; }
 
-  virtual nsISupports* GetParentObject() const override {
+  virtual nsIGlobalObject* GetParentObject() const override {
     MOZ_CRASH("This should not be called on the PBackground thread.");
     return nullptr;
   }
 
   virtual void GetDirectoryName(nsIFile* aFile, nsAString& aRetval,
                                 ErrorResult& aRv) const override {
     MOZ_CRASH("This should not be called on the PBackground thread.");
   }
--- a/dom/filesystem/compat/FileSystemFileEntry.cpp
+++ b/dom/filesystem/compat/FileSystemFileEntry.cpp
@@ -25,18 +25,16 @@ class FileCallbackRunnable final : publi
   }
 
   // MOZ_CAN_RUN_SCRIPT_BOUNDARY until Runnable::Run is MOZ_CAN_RUN_SCRIPT.  See
   // bug 1535398.
   MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD Run() override {
     // Here we clone the File object.
 
     RefPtr<File> file = File::Create(mFile->GetParentObject(), mFile->Impl());
-    MOZ_ASSERT(file);
-
     mCallback->Call(*file);
     return NS_OK;
   }
 
  private:
   const RefPtr<FileCallback> mCallback;
   RefPtr<File> mFile;
 };
--- a/dom/html/HTMLCanvasElement.cpp
+++ b/dom/html/HTMLCanvasElement.cpp
@@ -878,18 +878,21 @@ nsresult HTMLCanvasElement::MozGetAsFile
   void* imgData = nullptr;
   rv = NS_ReadInputStreamToBuffer(stream, &imgData, -1, &imgSize);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsPIDOMWindowInner> win =
       do_QueryInterface(OwnerDoc()->GetScopeObject());
 
   // The File takes ownership of the buffer
-  RefPtr<File> file =
-      File::CreateMemoryFile(win, imgData, imgSize, aName, type, PR_Now());
+  RefPtr<File> file = File::CreateMemoryFile(win->AsGlobal(), imgData, imgSize,
+                                             aName, type, PR_Now());
+  if (NS_WARN_IF(!file)) {
+    return NS_ERROR_FAILURE;
+  }
 
   file.forget(aResult);
   return NS_OK;
 }
 
 nsresult HTMLCanvasElement::GetContext(const nsAString& aContextId,
                                        nsISupports** aContext) {
   ErrorResult rv;
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -238,22 +238,29 @@ const double HTMLInputElement::kMsPerDay
 // events at the end of the exploration of the directories.
 class DispatchChangeEventCallback final : public GetFilesCallback {
  public:
   explicit DispatchChangeEventCallback(HTMLInputElement* aInputElement)
       : mInputElement(aInputElement) {
     MOZ_ASSERT(aInputElement);
   }
 
-  virtual void Callback(nsresult aStatus,
-                        const Sequence<RefPtr<File>>& aFiles) override {
+  virtual void Callback(
+      nsresult aStatus,
+      const FallibleTArray<RefPtr<BlobImpl>>& aBlobImpls) override {
     nsTArray<OwningFileOrDirectory> array;
-    for (uint32_t i = 0; i < aFiles.Length(); ++i) {
+    for (uint32_t i = 0; i < aBlobImpls.Length(); ++i) {
       OwningFileOrDirectory* element = array.AppendElement();
-      element->SetAsFile() = aFiles[i];
+      RefPtr<File> file =
+          File::Create(mInputElement->GetOwnerGlobal(), aBlobImpls[i]);
+      if (NS_WARN_IF(!file)) {
+        break;
+      }
+
+      element->SetAsFile() = file;
     }
 
     mInputElement->SetFilesOrDirectories(array, true);
     Unused << NS_WARN_IF(NS_FAILED(DispatchEvents()));
   }
 
   MOZ_CAN_RUN_SCRIPT_BOUNDARY
   nsresult DispatchEvents() {
@@ -1997,17 +2004,19 @@ void HTMLInputElement::MozSetFileArray(
   MOZ_ASSERT(global);
   if (!global) {
     return;
   }
 
   nsTArray<OwningFileOrDirectory> files;
   for (uint32_t i = 0; i < aFiles.Length(); ++i) {
     RefPtr<File> file = File::Create(global, aFiles[i].get()->Impl());
-    MOZ_ASSERT(file);
+    if (NS_WARN_IF(!file)) {
+      return;
+    }
 
     OwningFileOrDirectory* element = files.AppendElement();
     element->SetAsFile() = file;
   }
 
   SetFilesOrDirectories(files, true);
 }
 
@@ -2045,16 +2054,20 @@ void HTMLInputElement::MozSetFileNameArr
 
     nsCOMPtr<nsIGlobalObject> global = OwnerDoc()->GetScopeObject();
     if (!global) {
       aRv.Throw(NS_ERROR_FAILURE);
       return;
     }
 
     RefPtr<File> domFile = File::CreateFromFile(global, file);
+    if (NS_WARN_IF(!domFile)) {
+      aRv.Throw(NS_ERROR_FAILURE);
+      return;
+    }
 
     OwningFileOrDirectory* element = files.AppendElement();
     element->SetAsFile() = domFile;
   }
 
   SetFilesOrDirectories(files, true);
 }
 
@@ -2071,17 +2084,17 @@ void HTMLInputElement::MozSetDirectory(c
   }
 
   nsPIDOMWindowInner* window = OwnerDoc()->GetInnerWindow();
   if (NS_WARN_IF(!window)) {
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
 
-  RefPtr<Directory> directory = Directory::Create(window, file);
+  RefPtr<Directory> directory = Directory::Create(window->AsGlobal(), file);
   MOZ_ASSERT(directory);
 
   nsTArray<OwningFileOrDirectory> array;
   OwningFileOrDirectory* element = array.AppendElement();
   element->SetAsDirectory() = directory;
 
   SetFilesOrDirectories(array, true);
 }
@@ -6107,31 +6120,34 @@ static nsTArray<OwningFileOrDirectory> R
   nsTArray<OwningFileOrDirectory> res(aData.Length());
   for (auto& it : aData) {
     if (it.type() == FileContentData::TBlobImpl) {
       if (!it.get_BlobImpl()) {
         // Serialization failed, skip this file.
         continue;
       }
 
-      RefPtr<File> file = File::Create(aWindow, it.get_BlobImpl());
-      MOZ_ASSERT(file);
+      RefPtr<File> file = File::Create(aWindow->AsGlobal(), it.get_BlobImpl());
+      if (NS_WARN_IF(!file)) {
+        continue;
+      }
 
       OwningFileOrDirectory* element = res.AppendElement();
       element->SetAsFile() = file;
     } else {
       MOZ_ASSERT(it.type() == FileContentData::TnsString);
       nsCOMPtr<nsIFile> file;
       nsresult rv =
           NS_NewLocalFile(it.get_nsString(), true, getter_AddRefs(file));
       if (NS_WARN_IF(NS_FAILED(rv))) {
         continue;
       }
 
-      RefPtr<Directory> directory = Directory::Create(aWindow, file);
+      RefPtr<Directory> directory =
+          Directory::Create(aWindow->AsGlobal(), file);
       MOZ_ASSERT(directory);
 
       OwningFileOrDirectory* element = res.AppendElement();
       element->SetAsDirectory() = directory;
     }
   }
   return res;
 }
@@ -7129,38 +7145,31 @@ JSObject* HTMLInputElement::WrapNode(JSC
                                      JS::Handle<JSObject*> aGivenProto) {
   return HTMLInputElement_Binding::Wrap(aCx, this, aGivenProto);
 }
 
 GetFilesHelper* HTMLInputElement::GetOrCreateGetFilesHelper(bool aRecursiveFlag,
                                                             ErrorResult& aRv) {
   MOZ_ASSERT(mFileData);
 
-  nsCOMPtr<nsIGlobalObject> global = OwnerDoc()->GetScopeObject();
-  MOZ_ASSERT(global);
-  if (!global) {
-    aRv.Throw(NS_ERROR_FAILURE);
-    return nullptr;
-  }
-
   if (aRecursiveFlag) {
     if (!mFileData->mGetFilesRecursiveHelper) {
       mFileData->mGetFilesRecursiveHelper = GetFilesHelper::Create(
-          global, GetFilesOrDirectoriesInternal(), aRecursiveFlag, aRv);
+          GetFilesOrDirectoriesInternal(), aRecursiveFlag, aRv);
       if (NS_WARN_IF(aRv.Failed())) {
         return nullptr;
       }
     }
 
     return mFileData->mGetFilesRecursiveHelper;
   }
 
   if (!mFileData->mGetFilesNonRecursiveHelper) {
     mFileData->mGetFilesNonRecursiveHelper = GetFilesHelper::Create(
-        global, GetFilesOrDirectoriesInternal(), aRecursiveFlag, aRv);
+        GetFilesOrDirectoriesInternal(), aRecursiveFlag, aRv);
     if (NS_WARN_IF(aRv.Failed())) {
       return nullptr;
     }
   }
 
   return mFileData->mGetFilesNonRecursiveHelper;
 }
 
--- a/dom/indexedDB/ActorsChild.cpp
+++ b/dom/indexedDB/ActorsChild.cpp
@@ -531,16 +531,17 @@ void DeserializeStructuredCloneFiles(
 
           const IPCBlob& ipcBlob = blobOrMutableFile.get_IPCBlob();
 
           const RefPtr<BlobImpl> blobImpl = IPCBlobUtils::Deserialize(ipcBlob);
           MOZ_ASSERT(blobImpl);
 
           RefPtr<Blob> blob =
               Blob::Create(aDatabase->GetOwnerGlobal(), blobImpl);
+          MOZ_ASSERT(blob);
 
           StructuredCloneFile* const file = aFiles.AppendElement();
           MOZ_ASSERT(file);
 
           file->mType = StructuredCloneFile::eBlob;
           file->mBlob.swap(blob);
 
           break;
@@ -597,16 +598,17 @@ void DeserializeStructuredCloneFiles(
             const IPCBlob& ipcBlob = blobOrMutableFile.get_IPCBlob();
 
             const RefPtr<BlobImpl> blobImpl =
                 IPCBlobUtils::Deserialize(ipcBlob);
             MOZ_ASSERT(blobImpl);
 
             RefPtr<Blob> blob =
                 Blob::Create(aDatabase->GetOwnerGlobal(), blobImpl);
+            MOZ_ASSERT(blob);
 
             StructuredCloneFile* const file = aFiles.AppendElement();
             MOZ_ASSERT(file);
 
             file->mType = StructuredCloneFile::eStructuredClone;
             file->mBlob.swap(blob);
           } else {
             MOZ_ASSERT(blobOrMutableFile.type() == BlobOrMutableFile::Tnull_t);
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -621,69 +621,76 @@ class ValueDeserializationHelper {
     MOZ_ASSERT(aCx);
     MOZ_ASSERT(aData.tag == SCTAG_DOM_FILE ||
                aData.tag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE ||
                aData.tag == SCTAG_DOM_BLOB);
     MOZ_ASSERT(aFile.mType == StructuredCloneFile::eBlob);
 
     RefPtr<Blob> blob = aFile.mBlob;
 
+    // It can happen that this IDB is chrome code, so there is no parent, but
+    // still we want to set a correct parent for the new File object.
+    nsCOMPtr<nsIGlobalObject> global;
+    if (NS_IsMainThread()) {
+      if (aDatabase && aDatabase->GetParentObject()) {
+        global = aDatabase->GetParentObject();
+      } else {
+        global = xpc::CurrentNativeGlobal(aCx);
+      }
+    } else {
+      WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+      MOZ_ASSERT(workerPrivate);
+
+      WorkerGlobalScope* globalScope = workerPrivate->GlobalScope();
+      MOZ_ASSERT(globalScope);
+
+      global = do_QueryObject(globalScope);
+    }
+
+    MOZ_ASSERT(global);
+
     /* If we are creating an index, we do not have an mBlob but do have an
      * mInfo.  Unlike other index or upgrade cases, we do need a real-looking
      * Blob/File instance because the index's key path can reference their
      * properties.  Rather than create a fake-looking object, create a real
      * Blob. */
     if (!blob) {
       MOZ_ASSERT(aFile.mFileInfo);
       const nsCOMPtr<nsIFile> file =
           FileInfo::GetFileForFileInfo(aFile.mFileInfo);
       if (!file) {
         return false;
       }
 
       const RefPtr<FileBlobImpl> impl = new FileBlobImpl(file);
       impl->SetFileId(aFile.mFileInfo->Id());
-      blob = File::Create(nullptr, impl);
-    }
-
-    // It can happen that this IDB is chrome code, so there is no parent, but
-    // still we want to set a correct parent for the new File object.
-    nsCOMPtr<nsISupports> parent;
-    if (NS_IsMainThread()) {
-      if (aDatabase && aDatabase->GetParentObject()) {
-        parent = aDatabase->GetParentObject();
-      } else {
-        parent = xpc::CurrentNativeGlobal(aCx);
+      blob = File::Create(global, impl);
+      if (NS_WARN_IF(!blob)) {
+        return false;
       }
-    } else {
-      WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
-      MOZ_ASSERT(workerPrivate);
-
-      WorkerGlobalScope* globalScope = workerPrivate->GlobalScope();
-      MOZ_ASSERT(globalScope);
-
-      parent = do_QueryObject(globalScope);
     }
 
-    MOZ_ASSERT(parent);
-
     if (aData.tag == SCTAG_DOM_BLOB) {
       blob->Impl()->SetLazyData(VoidString(), aData.type, aData.size,
                                 INT64_MAX);
       MOZ_ASSERT(!blob->IsFile());
 
       // ActorsParent sends here a kind of half blob and half file wrapped into
       // a DOM File object. DOM File and DOM Blob are a WebIDL wrapper around a
       // BlobImpl object. SetLazyData() has just changed the BlobImpl to be a
       // Blob (see the previous assert), but 'blob' still has the WebIDL DOM
       // File wrapping.
       // Before exposing it to content, we must recreate a DOM Blob object.
 
       const RefPtr<Blob> exposedBlob =
           Blob::Create(blob->GetParentObject(), blob->Impl());
+      if (NS_WARN_IF(!exposedBlob)) {
+        return false;
+      }
+
       MOZ_ASSERT(exposedBlob);
       JS::Rooted<JS::Value> wrappedBlob(aCx);
       if (!ToJSValue(aCx, exposedBlob, &wrappedBlob)) {
         return false;
       }
 
       aResult.set(&wrappedBlob.toObject());
       return true;
--- a/dom/media/MediaRecorder.cpp
+++ b/dom/media/MediaRecorder.cpp
@@ -12,16 +12,17 @@
 #include "GeckoProfiler.h"
 #include "MediaDecoder.h"
 #include "MediaEncoder.h"
 #include "MediaTrackGraphImpl.h"
 #include "VideoUtils.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/dom/AudioStreamTrack.h"
 #include "mozilla/dom/BlobEvent.h"
+#include "mozilla/dom/EmptyBlobImpl.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/MediaRecorderErrorEvent.h"
 #include "mozilla/dom/MutableBlobStorage.h"
 #include "mozilla/dom/VideoStreamTrack.h"
 #include "mozilla/media/MediaUtils.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/StaticPtr.h"
@@ -836,50 +837,47 @@ class MediaRecorder::Session : public Pr
   void MaybeCreateMutableBlobStorage() {
     if (!mMutableBlobStorage) {
       mMutableBlobStorage = new MutableBlobStorage(
           MutableBlobStorage::eCouldBeInTemporaryFile, nullptr, mMaxMemory);
     }
   }
 
   static const bool IsExclusive = false;
-  using BlobPromise =
-      MozPromise<nsMainThreadPtrHandle<Blob>, nsresult, IsExclusive>;
+  using BlobPromise = MozPromise<RefPtr<BlobImpl>, nsresult, IsExclusive>;
   class BlobStorer : public MutableBlobStorageCallback {
     MozPromiseHolder<BlobPromise> mHolder;
 
     virtual ~BlobStorer() = default;
 
    public:
     BlobStorer() = default;
 
     NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BlobStorer, override)
 
-    void BlobStoreCompleted(MutableBlobStorage*, Blob* aBlob,
+    void BlobStoreCompleted(MutableBlobStorage*, BlobImpl* aBlobImpl,
                             nsresult aRv) override {
       MOZ_ASSERT(NS_IsMainThread());
       if (NS_FAILED(aRv)) {
         mHolder.Reject(aRv, __func__);
-      } else {
-        mHolder.Resolve(nsMainThreadPtrHandle<Blob>(
-                            MakeAndAddRef<nsMainThreadPtrHolder<Blob>>(
-                                "BlobStorer::ResolveBlob", aBlob)),
-                        __func__);
+        return;
       }
+
+      mHolder.Resolve(aBlobImpl, __func__);
     }
 
     RefPtr<BlobPromise> Promise() { return mHolder.Ensure(__func__); }
   };
 
  protected:
   RefPtr<BlobPromise> GatherBlobImpl() {
     RefPtr<BlobStorer> storer = MakeAndAddRef<BlobStorer>();
     MaybeCreateMutableBlobStorage();
-    mMutableBlobStorage->GetBlobWhenReady(
-        mRecorder->GetOwner(), NS_ConvertUTF16toUTF8(mMimeType), storer);
+    mMutableBlobStorage->GetBlobImplWhenReady(NS_ConvertUTF16toUTF8(mMimeType),
+                                              storer);
     mMutableBlobStorage = nullptr;
 
     storer->Promise()->Then(
         mMainThread, __func__,
         [self = RefPtr<Session>(this), p = storer->Promise()] {
           if (self->mBlobPromise == p) {
             // Reset BlobPromise.
             self->mBlobPromise = nullptr;
@@ -1106,62 +1104,62 @@ class MediaRecorder::Session : public Pr
 
     if (rv == NS_OK) {
       mRunningState = RunningState::Stopped;
     } else {
       mRunningState = Err(rv);
     }
 
     GatherBlob()
-        ->Then(mMainThread, __func__,
-               [this, self = RefPtr<Session>(this), rv, needsStartEvent](
-                   const BlobPromise::ResolveOrRejectValue& aResult) {
-                 if (mRecorder->mSessions.LastElement() == this) {
-                   // Set state to inactive, but only if the recorder is not
-                   // controlled by another session already.
-                   mRecorder->Inactivate();
-                 }
+        ->Then(
+            mMainThread, __func__,
+            [this, self = RefPtr<Session>(this), rv, needsStartEvent](
+                const BlobPromise::ResolveOrRejectValue& aResult) {
+              if (mRecorder->mSessions.LastElement() == this) {
+                // Set state to inactive, but only if the recorder is not
+                // controlled by another session already.
+                mRecorder->Inactivate();
+              }
 
-                 if (needsStartEvent) {
-                   mRecorder->DispatchSimpleEvent(NS_LITERAL_STRING("start"));
-                 }
+              if (needsStartEvent) {
+                mRecorder->DispatchSimpleEvent(NS_LITERAL_STRING("start"));
+              }
 
-                 // If there was an error, Fire the appropriate one
-                 if (NS_FAILED(rv)) {
-                   mRecorder->NotifyError(rv);
-                 }
+              // If there was an error, Fire the appropriate one
+              if (NS_FAILED(rv)) {
+                mRecorder->NotifyError(rv);
+              }
 
-                 // Fire a blob event named dataavailable
-                 RefPtr<Blob> blob;
-                 if (rv == NS_ERROR_DOM_SECURITY_ERR || aResult.IsReject()) {
-                   // In case of SecurityError, the blob data must be discarded.
-                   // We create a new empty one and throw the blob with its data
-                   // away.
-                   // In case we failed to gather blob data, we create an empty
-                   // memory blob instead.
-                   blob = Blob::CreateEmptyBlob(mRecorder->GetParentObject(),
-                                                mMimeType);
-                 } else {
-                   blob = aResult.ResolveValue();
-                 }
-                 if (NS_FAILED(mRecorder->CreateAndDispatchBlobEvent(blob))) {
-                   // Failed to dispatch blob event. That's unexpected. It's
-                   // probably all right to fire an error event if we haven't
-                   // already.
-                   if (NS_SUCCEEDED(rv)) {
-                     mRecorder->NotifyError(NS_ERROR_FAILURE);
-                   }
-                 }
+              // Fire a blob event named dataavailable
+              RefPtr<BlobImpl> blobImpl;
+              if (rv == NS_ERROR_DOM_SECURITY_ERR || aResult.IsReject()) {
+                // In case of SecurityError, the blob data must be discarded.
+                // We create a new empty one and throw the blob with its data
+                // away.
+                // In case we failed to gather blob data, we create an empty
+                // memory blob instead.
+                blobImpl = new EmptyBlobImpl(mMimeType);
+              } else {
+                blobImpl = aResult.ResolveValue();
+              }
+              if (NS_FAILED(mRecorder->CreateAndDispatchBlobEvent(blobImpl))) {
+                // Failed to dispatch blob event. That's unexpected. It's
+                // probably all right to fire an error event if we haven't
+                // already.
+                if (NS_SUCCEEDED(rv)) {
+                  mRecorder->NotifyError(NS_ERROR_FAILURE);
+                }
+              }
 
-                 // Fire an event named stop
-                 mRecorder->DispatchSimpleEvent(NS_LITERAL_STRING("stop"));
+              // Fire an event named stop
+              mRecorder->DispatchSimpleEvent(NS_LITERAL_STRING("stop"));
 
-                 // And finally, Shutdown and destroy the Session
-                 return Shutdown();
-               })
+              // And finally, Shutdown and destroy the Session
+              return Shutdown();
+            })
         ->Then(mMainThread, __func__, [this, self = RefPtr<Session>(this)] {
           GetShutdownBarrier()->RemoveBlocker(mShutdownBlocker);
           mShutdownBlocker = nullptr;
         });
   }
 
   void MediaEncoderInitialized() {
     MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
@@ -1897,23 +1895,33 @@ bool MediaRecorder::IsTypeSupported(Glob
   return MediaRecorder::IsTypeSupported(aMIMEType);
 }
 
 /* static */
 bool MediaRecorder::IsTypeSupported(const nsAString& aMIMEType) {
   return IsTypeSupportedImpl(aMIMEType) == TypeSupport::Supported;
 }
 
-nsresult MediaRecorder::CreateAndDispatchBlobEvent(Blob* aBlob) {
+nsresult MediaRecorder::CreateAndDispatchBlobEvent(BlobImpl* aBlobImpl) {
   MOZ_ASSERT(NS_IsMainThread(), "Not running on main thread");
 
+  if (!GetOwnerGlobal()) {
+    // This MediaRecorder has been disconnected in the meantime.
+    return NS_ERROR_FAILURE;
+  }
+
+  RefPtr<Blob> blob = Blob::Create(GetOwnerGlobal(), aBlobImpl);
+  if (NS_WARN_IF(!blob)) {
+    return NS_ERROR_FAILURE;
+  }
+
   BlobEventInit init;
   init.mBubbles = false;
   init.mCancelable = false;
-  init.mData = aBlob;
+  init.mData = blob;
 
   RefPtr<BlobEvent> event =
       BlobEvent::Constructor(this, NS_LITERAL_STRING("dataavailable"), init);
   event->SetTrusted(true);
   ErrorResult rv;
   DispatchEvent(*event, rv);
   return rv.StealNSResult();
 }
--- a/dom/media/MediaRecorder.h
+++ b/dom/media/MediaRecorder.h
@@ -116,17 +116,17 @@ class MediaRecorder final : public DOMEv
   uint32_t AudioBitsPerSecond() const { return mAudioBitsPerSecond; }
   uint32_t VideoBitsPerSecond() const { return mVideoBitsPerSecond; }
 
  protected:
   virtual ~MediaRecorder();
 
   MediaRecorder& operator=(const MediaRecorder& x) = delete;
   // Create dataavailable event with Blob data and it runs in main thread
-  nsresult CreateAndDispatchBlobEvent(Blob* aBlob);
+  nsresult CreateAndDispatchBlobEvent(BlobImpl* aBlobImpl);
   // Creating a simple event to notify UA simple event.
   void DispatchSimpleEvent(const nsAString& aStr);
   // Creating a error event with message.
   void NotifyError(nsresult aRv);
 
   MediaRecorder(const MediaRecorder& x) = delete;  // prevent bad usage
   // Remove session pointer.
   void RemoveSession(Session* aSession);
--- a/dom/media/imagecapture/CaptureTask.cpp
+++ b/dom/media/imagecapture/CaptureTask.cpp
@@ -31,29 +31,33 @@ class CaptureTask::MediaTrackEventListen
 };
 
 CaptureTask::CaptureTask(dom::ImageCapture* aImageCapture)
     : mImageCapture(aImageCapture),
       mEventListener(new MediaTrackEventListener(this)),
       mImageGrabbedOrTrackEnd(false),
       mPrincipalChanged(false) {}
 
-nsresult CaptureTask::TaskComplete(already_AddRefed<dom::Blob> aBlob,
+nsresult CaptureTask::TaskComplete(already_AddRefed<dom::BlobImpl> aBlobImpl,
                                    nsresult aRv) {
   MOZ_ASSERT(NS_IsMainThread());
 
   DetachTrack();
 
   nsresult rv;
-  RefPtr<dom::Blob> blob(aBlob);
+  RefPtr<dom::BlobImpl> blobImpl(aBlobImpl);
 
   // We have to set the parent because the blob has been generated with a valid
   // one.
-  if (blob) {
-    blob = dom::Blob::Create(mImageCapture->GetParentObject(), blob->Impl());
+  RefPtr<dom::Blob> blob;
+  if (blobImpl) {
+    blob = dom::Blob::Create(mImageCapture->GetOwnerGlobal(), blobImpl);
+    if (NS_WARN_IF(!blob)) {
+      return NS_ERROR_FAILURE;
+    }
   }
 
   if (mPrincipalChanged) {
     aRv = NS_ERROR_DOM_SECURITY_ERR;
     IC_LOG("MediaStream principal should not change during TakePhoto().");
   }
 
   if (NS_SUCCEEDED(aRv)) {
@@ -99,19 +103,20 @@ void CaptureTask::NotifyRealtimeTrackDat
   MOZ_ASSERT(aMedia.GetType() == MediaSegment::VIDEO);
   const VideoSegment& video = static_cast<const VideoSegment&>(aMedia);
 
   // Callback for encoding complete, it calls on main thread.
   class EncodeComplete : public dom::EncodeCompleteCallback {
    public:
     explicit EncodeComplete(CaptureTask* aTask) : mTask(aTask) {}
 
-    nsresult ReceiveBlob(already_AddRefed<dom::Blob> aBlob) override {
-      RefPtr<dom::Blob> blob(aBlob);
-      mTask->TaskComplete(blob.forget(), NS_OK);
+    nsresult ReceiveBlobImpl(
+        already_AddRefed<dom::BlobImpl> aBlobImpl) override {
+      RefPtr<dom::BlobImpl> blobImpl(aBlobImpl);
+      mTask->TaskComplete(blobImpl.forget(), NS_OK);
       mTask = nullptr;
       return NS_OK;
     }
 
    protected:
     RefPtr<CaptureTask> mTask;
   };
 
--- a/dom/media/imagecapture/CaptureTask.h
+++ b/dom/media/imagecapture/CaptureTask.h
@@ -9,17 +9,17 @@
 
 #include "MediaTrackGraph.h"
 #include "MediaTrackListener.h"
 #include "PrincipalChangeObserver.h"
 
 namespace mozilla {
 
 namespace dom {
-class Blob;
+class BlobImpl;
 class ImageCapture;
 class MediaStreamTrack;
 }  // namespace dom
 
 /**
  * CaptureTask retrieves image from MediaTrack and encodes the image to jpeg in
  * ImageEncoder. The whole procedures start at AttachTrack(), it will add this
  * class into MediaTrack and retrieves an image in MediaTrackGraph thread.
@@ -43,17 +43,18 @@ class CaptureTask : public DirectMediaTr
 
   // CaptureTask methods.
 
   // It is called when aBlob is ready to post back to script in company with
   // aRv == NS_OK. If aRv is not NS_OK, it will post an error event to script.
   //
   // Note:
   //   this function should be called on main thread.
-  nsresult TaskComplete(already_AddRefed<dom::Blob> aBlob, nsresult aRv);
+  nsresult TaskComplete(already_AddRefed<dom::BlobImpl> aBlobImpl,
+                        nsresult aRv);
 
   // Add listeners into MediaStreamTrack and PrincipalChangeObserver.
   // It should be on main thread only.
   void AttachTrack();
 
   // Remove listeners from MediaStreamTrack and PrincipalChangeObserver.
   // It should be on main thread only.
   void DetachTrack();
--- a/dom/presentation/PresentationConnection.cpp
+++ b/dom/presentation/PresentationConnection.cpp
@@ -507,18 +507,20 @@ nsresult PresentationConnection::DoRecei
   }
   JSContext* cx = jsapi.cx();
   JS::Rooted<JS::Value> jsData(cx);
 
   nsresult rv;
   if (aIsBinary) {
     if (mBinaryType == PresentationConnectionBinaryType::Blob) {
       RefPtr<Blob> blob =
-          Blob::CreateStringBlob(GetOwner(), aData, EmptyString());
-      MOZ_ASSERT(blob);
+          Blob::CreateStringBlob(GetOwnerGlobal(), aData, EmptyString());
+      if (NS_WARN_IF(!blob)) {
+        return NS_ERROR_FAILURE;
+      }
 
       if (!ToJSValue(cx, blob, &jsData)) {
         return NS_ERROR_FAILURE;
       }
     } else if (mBinaryType == PresentationConnectionBinaryType::Arraybuffer) {
       JS::Rooted<JSObject*> arrayBuf(cx);
       rv = nsContentUtils::CreateArrayBuffer(cx, aData, arrayBuf.address());
       if (NS_WARN_IF(NS_FAILED(rv))) {
--- a/dom/serviceworkers/ServiceWorkerEvents.cpp
+++ b/dom/serviceworkers/ServiceWorkerEvents.cpp
@@ -1046,17 +1046,17 @@ nsresult ExtractBytesFromData(
   if (aDataInit.IsUSVString()) {
     return ExtractBytesFromUSVString(aDataInit.GetAsUSVString(), aBytes);
   }
   MOZ_ASSERT_UNREACHABLE("Unexpected push message data");
   return NS_ERROR_FAILURE;
 }
 }  // namespace
 
-PushMessageData::PushMessageData(nsISupports* aOwner,
+PushMessageData::PushMessageData(nsIGlobalObject* aOwner,
                                  nsTArray<uint8_t>&& aBytes)
     : mOwner(aOwner), mBytes(std::move(aBytes)) {}
 
 PushMessageData::~PushMessageData() {}
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PushMessageData, mOwner)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(PushMessageData)
@@ -1144,17 +1144,17 @@ already_AddRefed<PushEvent> PushEvent::C
   e->SetComposed(aOptions.mComposed);
   if (aOptions.mData.WasPassed()) {
     nsTArray<uint8_t> bytes;
     nsresult rv = ExtractBytesFromData(aOptions.mData.Value(), bytes);
     if (NS_FAILED(rv)) {
       aRv.Throw(rv);
       return nullptr;
     }
-    e->mData = new PushMessageData(aOwner, std::move(bytes));
+    e->mData = new PushMessageData(aOwner->GetOwnerGlobal(), std::move(bytes));
   }
   return e.forget();
 }
 
 NS_IMPL_ADDREF_INHERITED(PushEvent, ExtendableEvent)
 NS_IMPL_RELEASE_INHERITED(PushEvent, ExtendableEvent)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PushEvent)
--- a/dom/serviceworkers/ServiceWorkerEvents.h
+++ b/dom/serviceworkers/ServiceWorkerEvents.h
@@ -178,29 +178,29 @@ class FetchEvent final : public Extendab
 class PushMessageData final : public nsISupports, public nsWrapperCache {
  public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(PushMessageData)
 
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aGivenProto) override;
 
-  nsISupports* GetParentObject() const { return mOwner; }
+  nsIGlobalObject* GetParentObject() const { return mOwner; }
 
   void Json(JSContext* cx, JS::MutableHandle<JS::Value> aRetval,
             ErrorResult& aRv);
   void Text(nsAString& aData);
   void ArrayBuffer(JSContext* cx, JS::MutableHandle<JSObject*> aRetval,
                    ErrorResult& aRv);
   already_AddRefed<mozilla::dom::Blob> Blob(ErrorResult& aRv);
 
-  PushMessageData(nsISupports* aOwner, nsTArray<uint8_t>&& aBytes);
+  PushMessageData(nsIGlobalObject* aOwner, nsTArray<uint8_t>&& aBytes);
 
  private:
-  nsCOMPtr<nsISupports> mOwner;
+  nsCOMPtr<nsIGlobalObject> mOwner;
   nsTArray<uint8_t> mBytes;
   nsString mDecodedText;
   ~PushMessageData();
 
   nsresult EnsureDecodedText();
   uint8_t* GetContentsCopy();
 };
 
--- a/dom/websocket/WebSocket.cpp
+++ b/dom/websocket/WebSocket.cpp
@@ -1839,17 +1839,19 @@ nsresult WebSocket::CreateAndDispatchMes
   // Create appropriate JS object for message
   JS::Rooted<JS::Value> jsData(cx);
   if (aIsBinary) {
     if (mBinaryType == dom::BinaryType::Blob) {
       messageType = nsIWebSocketEventListener::TYPE_BLOB;
 
       RefPtr<Blob> blob =
           Blob::CreateStringBlob(GetOwnerGlobal(), aData, EmptyString());
-      MOZ_ASSERT(blob);
+      if (NS_WARN_IF(!blob)) {
+        return NS_ERROR_FAILURE;
+      }
 
       if (!ToJSValue(cx, blob, &jsData)) {
         return NS_ERROR_FAILURE;
       }
 
     } else if (mBinaryType == dom::BinaryType::Arraybuffer) {
       messageType = nsIWebSocketEventListener::TYPE_ARRAYBUFFER;
 
--- a/dom/xhr/XMLHttpRequestMainThread.cpp
+++ b/dom/xhr/XMLHttpRequestMainThread.cpp
@@ -1696,17 +1696,17 @@ XMLHttpRequestMainThread::OnDataAvailabl
     nsCOMPtr<nsIFile> localFile;
     nsCOMPtr<nsIURI> blobURI;
     GetBlobURIFromChannel(request, getter_AddRefs(blobURI));
     if (blobURI) {
       RefPtr<BlobImpl> blobImpl;
       rv = NS_GetBlobForBlobURI(blobURI, getter_AddRefs(blobImpl));
       if (NS_SUCCEEDED(rv)) {
         if (blobImpl) {
-          mResponseBlob = Blob::Create(GetOwner(), blobImpl);
+          mResponseBlob = Blob::Create(GetOwnerGlobal(), blobImpl);
         }
         if (!mResponseBlob) {
           rv = NS_ERROR_FILE_NOT_FOUND;
         }
       }
     } else {
       rv = GetLocalFileFromChannel(request, getter_AddRefs(localFile));
     }
@@ -2138,17 +2138,17 @@ XMLHttpRequestMainThread::OnStopRequest(
       contentType.Assign(NS_ConvertUTF16toUTF8(mOverrideMimeType));
     } else {
       mChannel->GetContentType(contentType);
     }
 
     // mBlobStorage can be null if the channel is non-file non-cacheable
     // and if the response length is zero.
     MaybeCreateBlobStorage();
-    mBlobStorage->GetBlobWhenReady(GetOwner(), contentType, this);
+    mBlobStorage->GetBlobImplWhenReady(contentType, this);
     waitingForBlobCreation = true;
 
     NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty");
     NS_ASSERTION(mResponseText.IsEmpty(), "mResponseText should be empty");
   } else if (NS_SUCCEEDED(status) && !mIsMappedArrayBuffer &&
              mResponseType == XMLHttpRequestResponseType::Arraybuffer) {
     // set the capacity down to the actual length, to realloc back
     // down to the actual size
@@ -3578,25 +3578,25 @@ void XMLHttpRequestMainThread::MaybeCrea
   if (nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal()) {
     eventTarget = global->EventTargetFor(TaskCategory::Other);
   }
 
   mBlobStorage = new MutableBlobStorage(storageType, eventTarget);
 }
 
 void XMLHttpRequestMainThread::BlobStoreCompleted(
-    MutableBlobStorage* aBlobStorage, Blob* aBlob, nsresult aRv) {
+    MutableBlobStorage* aBlobStorage, BlobImpl* aBlobImpl, nsresult aRv) {
   // Ok, the state is changed...
   if (mBlobStorage != aBlobStorage || NS_FAILED(aRv)) {
     return;
   }
 
   MOZ_ASSERT(mState != XMLHttpRequest_Binding::DONE);
 
-  mResponseBlob = aBlob;
+  mResponseBlob = Blob::Create(GetOwnerGlobal(), aBlobImpl);
   mBlobStorage = nullptr;
 
   ChangeStateToDone(mFlagSyncLooping);
 }
 
 NS_IMETHODIMP
 XMLHttpRequestMainThread::GetName(nsACString& aName) {
   aName.AssignLiteral("XMLHttpRequest");
--- a/dom/xhr/XMLHttpRequestMainThread.h
+++ b/dom/xhr/XMLHttpRequestMainThread.h
@@ -428,17 +428,17 @@ class XMLHttpRequestMainThread final : p
   static void SetDontWarnAboutSyncXHR(bool aVal) {
     sDontWarnAboutSyncXHR = aVal;
   }
   static bool DontWarnAboutSyncXHR() { return sDontWarnAboutSyncXHR; }
 
   virtual void SetOriginAttributes(
       const mozilla::dom::OriginAttributesDictionary& aAttrs) override;
 
-  void BlobStoreCompleted(MutableBlobStorage* aBlobStorage, Blob* aBlob,
+  void BlobStoreCompleted(MutableBlobStorage* aBlobStorage, BlobImpl* aBlobImpl,
                           nsresult aResult) override;
 
   void LocalFileToBlobCompleted(Blob* aBlob);
 
  protected:
   nsresult DetectCharset();
   nsresult AppendToResponseText(Span<const uint8_t> aBuffer,
                                 bool aLast = false);
--- a/editor/libeditor/HTMLEditSubActionHandler.cpp
+++ b/editor/libeditor/HTMLEditSubActionHandler.cpp
@@ -4965,16 +4965,110 @@ EditActionResult HTMLEditor::IndentAsSub
 
   nsresult rv = MaybeInsertPaddingBRElementForEmptyLastLineAtSelection();
   NS_WARNING_ASSERTION(
       NS_SUCCEEDED(rv),
       "MaybeInsertPaddingBRElementForEmptyLastLineAtSelection() failed");
   return result.SetResult(rv);
 }
 
+// Helper for Handle[CSS|HTML]IndentAtSelectionInternal
+nsresult HTMLEditor::IndentListChild(RefPtr<Element>* aCurList,
+                                     const EditorDOMPoint& aCurPoint,
+                                     OwningNonNull<nsINode>& aCurNode) {
+  MOZ_ASSERT(HTMLEditUtils::IsList(aCurPoint.GetContainer()),
+             "unexpected container");
+  MOZ_ASSERT(IsTopLevelEditSubActionDataAvailable());
+
+  // some logic for putting list items into nested lists...
+
+  // Check for whether we should join a list that follows aCurNode.
+  // We do this if the next element is a list, and the list is of the
+  // same type (li/ol) as aCurNode was a part it.
+  if (nsIContent* nextEditableSibling =
+          GetNextHTMLSibling(aCurNode, SkipWhitespace::Yes)) {
+    if (HTMLEditUtils::IsList(nextEditableSibling) &&
+        aCurPoint.GetContainer()->NodeInfo()->NameAtom() ==
+            nextEditableSibling->NodeInfo()->NameAtom() &&
+        aCurPoint.GetContainer()->NodeInfo()->NamespaceID() ==
+            nextEditableSibling->NodeInfo()->NamespaceID()) {
+      nsresult rv =
+          MoveNodeWithTransaction(MOZ_KnownLive(*aCurNode->AsContent()),
+                                  EditorDOMPoint(nextEditableSibling, 0));
+      if (NS_WARN_IF(Destroyed())) {
+        return NS_ERROR_EDITOR_DESTROYED;
+      }
+      NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
+                           "MoveNodeWithTransaction() failed");
+      return rv;
+    }
+  }
+
+  // Check for whether we should join a list that preceeds aCurNode.
+  // We do this if the previous element is a list, and the list is of
+  // the same type (li/ol) as aCurNode was a part of.
+  if (nsCOMPtr<nsIContent> previousEditableSibling =
+          GetPriorHTMLSibling(aCurNode, SkipWhitespace::Yes)) {
+    if (HTMLEditUtils::IsList(previousEditableSibling) &&
+        aCurPoint.GetContainer()->NodeInfo()->NameAtom() ==
+            previousEditableSibling->NodeInfo()->NameAtom() &&
+        aCurPoint.GetContainer()->NodeInfo()->NamespaceID() ==
+            previousEditableSibling->NodeInfo()->NamespaceID()) {
+      nsresult rv =
+          MoveNodeToEndWithTransaction(MOZ_KnownLive(*aCurNode->AsContent()),
+                                       *previousEditableSibling);
+      if (NS_WARN_IF(Destroyed())) {
+        return NS_ERROR_EDITOR_DESTROYED;
+      }
+      NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
+                           "MoveNodeToEndWithTransaction() failed");
+      return rv;
+    }
+  }
+
+  // check to see if aCurList is still appropriate.  Which it is if
+  // aCurNode is still right after it in the same list.
+  nsIContent* previousEditableSibling =
+      *aCurList ? GetPriorHTMLSibling(aCurNode, SkipWhitespace::Yes) : nullptr;
+  if (!*aCurList ||
+      (previousEditableSibling && previousEditableSibling != *aCurList)) {
+    nsAtom* containerName =
+        aCurPoint.GetContainer()->NodeInfo()->NameAtom();
+    // Create a new nested list of correct type.
+    SplitNodeResult splitNodeResult =
+        MaybeSplitAncestorsForInsertWithTransaction(
+            MOZ_KnownLive(*containerName), aCurPoint);
+    if (NS_WARN_IF(splitNodeResult.Failed())) {
+      return splitNodeResult.Rv();
+    }
+    *aCurList = CreateNodeWithTransaction(MOZ_KnownLive(*containerName),
+                                          splitNodeResult.SplitPoint());
+    if (NS_WARN_IF(Destroyed())) {
+      return NS_ERROR_EDITOR_DESTROYED;
+    }
+    if (NS_WARN_IF(!*aCurList)) {
+      return NS_ERROR_FAILURE;
+    }
+    // aCurList is now the correct thing to put aCurNode in
+    // remember our new block for postprocessing
+    TopLevelEditSubActionDataRef().mNewBlockElement = *aCurList;
+  }
+  // tuck the node into the end of the active list
+  RefPtr<nsINode> container = *aCurList;
+  nsresult rv =
+      MoveNodeToEndWithTransaction(MOZ_KnownLive(*aCurNode->AsContent()),
+                                   *container);
+  if (NS_WARN_IF(Destroyed())) {
+    return NS_ERROR_EDITOR_DESTROYED;
+  }
+  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
+                       "MoveNodeToEndWithTransaction() failed");
+  return rv;
+}
+
 EditActionResult HTMLEditor::HandleIndentAtSelection() {
   MOZ_ASSERT(IsEditActionDataAvailable());
 
   nsresult rv = EnsureNoPaddingBRElementForEmptyEditor();
   if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
     return EditActionResult(NS_ERROR_EDITOR_DESTROYED);
   }
   NS_WARNING_ASSERTION(
@@ -5139,98 +5233,19 @@ nsresult HTMLEditor::HandleCSSIndentAtSe
     }
 
     // Ignore all non-editable nodes.  Leave them be.
     // XXX We ignore non-editable nodes here, but not so in the above block.
     if (!IsEditable(curNode)) {
       continue;
     }
 
-    // some logic for putting list items into nested lists...
     if (HTMLEditUtils::IsList(atCurNode.GetContainer())) {
-      // Check for whether we should join a list that follows curNode.
-      // We do this if the next element is a list, and the list is of the
-      // same type (li/ol) as curNode was a part it.
-      // XXX We also check namespace of the element here, but we don't do
-      //     that in other places.
-      if (nsIContent* nextEditableSibling = GetNextHTMLSibling(curNode)) {
-        if (HTMLEditUtils::IsList(nextEditableSibling) &&
-            atCurNode.GetContainer()->NodeInfo()->NameAtom() ==
-                nextEditableSibling->NodeInfo()->NameAtom() &&
-            atCurNode.GetContainer()->NodeInfo()->NamespaceID() ==
-                nextEditableSibling->NodeInfo()->NamespaceID()) {
-          nsresult rv =
-              MoveNodeWithTransaction(MOZ_KnownLive(*curNode->AsContent()),
-                                      EditorDOMPoint(nextEditableSibling, 0));
-          if (NS_WARN_IF(Destroyed())) {
-            return NS_ERROR_EDITOR_DESTROYED;
-          }
-          if (NS_WARN_IF(NS_FAILED(rv))) {
-            return rv;
-          }
-          continue;
-        }
-      }
-
-      // Check for whether we should join a list that preceeds curNode.
-      // We do this if the previous element is a list, and the list is of
-      // the same type (li/ol) as curNode was a part of.
-      if (nsCOMPtr<nsIContent> previousEditableSibling =
-              GetPriorHTMLSibling(curNode)) {
-        if (HTMLEditUtils::IsList(previousEditableSibling) &&
-            atCurNode.GetContainer()->NodeInfo()->NameAtom() ==
-                previousEditableSibling->NodeInfo()->NameAtom() &&
-            atCurNode.GetContainer()->NodeInfo()->NamespaceID() ==
-                previousEditableSibling->NodeInfo()->NamespaceID()) {
-          nsresult rv = MoveNodeToEndWithTransaction(
-              MOZ_KnownLive(*curNode->AsContent()), *previousEditableSibling);
-          if (NS_WARN_IF(Destroyed())) {
-            return NS_ERROR_EDITOR_DESTROYED;
-          }
-          if (NS_WARN_IF(NS_FAILED(rv))) {
-            return rv;
-          }
-          continue;
-        }
-      }
-
-      // check to see if curList is still appropriate.  Which it is if
-      // curNode is still right after it in the same list.
-      nsIContent* previousEditableSibling =
-          curList ? GetPriorHTMLSibling(curNode) : nullptr;
-      if (!curList ||
-          (previousEditableSibling && previousEditableSibling != curList)) {
-        nsAtom* containerName =
-            atCurNode.GetContainer()->NodeInfo()->NameAtom();
-        // Create a new nested list of correct type.
-        SplitNodeResult splitNodeResult =
-            MaybeSplitAncestorsForInsertWithTransaction(
-                MOZ_KnownLive(*containerName), atCurNode);
-        if (NS_WARN_IF(splitNodeResult.Failed())) {
-          return splitNodeResult.Rv();
-        }
-        curList = CreateNodeWithTransaction(MOZ_KnownLive(*containerName),
-                                            splitNodeResult.SplitPoint());
-        if (NS_WARN_IF(Destroyed())) {
-          return NS_ERROR_EDITOR_DESTROYED;
-        }
-        if (NS_WARN_IF(!curList)) {
-          return NS_ERROR_FAILURE;
-        }
-        // curList is now the correct thing to put curNode in
-        // remember our new block for postprocessing
-        TopLevelEditSubActionDataRef().mNewBlockElement = curList;
-      }
-      // tuck the node into the end of the active list
-      nsresult rv = MoveNodeToEndWithTransaction(
-          MOZ_KnownLive(*curNode->AsContent()), *curList);
-      if (NS_WARN_IF(Destroyed())) {
-        return NS_ERROR_EDITOR_DESTROYED;
-      }
-      if (NS_WARN_IF(NS_FAILED(rv))) {
+      nsresult rv = IndentListChild(&curList, atCurNode, curNode);
+      if (NS_FAILED(rv)) {
         return rv;
       }
       continue;
     }
 
     // Not a list item.
 
     if (HTMLEditor::NodeIsBlockStatic(*curNode)) {
@@ -5404,100 +5419,23 @@ nsresult HTMLEditor::HandleHTMLIndentAtS
     }
 
     // Ignore all non-editable nodes.  Leave them be.
     // XXX We ignore non-editable nodes here, but not so in the above block.
     if (!IsEditable(curNode)) {
       continue;
     }
 
-    // some logic for putting list items into nested lists...
     if (HTMLEditUtils::IsList(atCurNode.GetContainer())) {
-      // Check for whether we should join a list that follows curNode.
-      // We do this if the next element is a list, and the list is of the
-      // same type (li/ol) as curNode was a part it.
-      if (nsIContent* nextEditableSibling = GetNextHTMLSibling(curNode)) {
-        if (HTMLEditUtils::IsList(nextEditableSibling) &&
-            atCurNode.GetContainer()->NodeInfo()->NameAtom() ==
-                nextEditableSibling->NodeInfo()->NameAtom() &&
-            atCurNode.GetContainer()->NodeInfo()->NamespaceID() ==
-                nextEditableSibling->NodeInfo()->NamespaceID()) {
-          rv = MoveNodeWithTransaction(MOZ_KnownLive(*curNode->AsContent()),
-                                       EditorDOMPoint(nextEditableSibling, 0));
-          if (NS_WARN_IF(Destroyed())) {
-            return NS_ERROR_EDITOR_DESTROYED;
-          }
-          if (NS_WARN_IF(NS_FAILED(rv))) {
-            return rv;
-          }
-          continue;
-        }
-      }
-
-      // Check for whether we should join a list that preceeds curNode.
-      // We do this if the previous element is a list, and the list is of
-      // the same type (li/ol) as curNode was a part of.
-      if (nsCOMPtr<nsIContent> previousEditableSibling =
-              GetPriorHTMLSibling(curNode)) {
-        if (HTMLEditUtils::IsList(previousEditableSibling) &&
-            atCurNode.GetContainer()->NodeInfo()->NameAtom() ==
-                previousEditableSibling->NodeInfo()->NameAtom() &&
-            atCurNode.GetContainer()->NodeInfo()->NamespaceID() ==
-                previousEditableSibling->NodeInfo()->NamespaceID()) {
-          rv = MoveNodeToEndWithTransaction(
-              MOZ_KnownLive(*curNode->AsContent()), *previousEditableSibling);
-          if (NS_WARN_IF(Destroyed())) {
-            return NS_ERROR_EDITOR_DESTROYED;
-          }
-          if (NS_WARN_IF(NS_FAILED(rv))) {
-            return rv;
-          }
-          continue;
-        }
-      }
-
-      // check to see if curList is still appropriate.  Which it is if
-      // curNode is still right after it in the same list.
-      nsIContent* previousEditableSibling =
-          curList ? GetPriorHTMLSibling(curNode) : nullptr;
-      if (!curList ||
-          (previousEditableSibling && previousEditableSibling != curList)) {
-        nsAtom* containerName =
-            atCurNode.GetContainer()->NodeInfo()->NameAtom();
-        // Create a new nested list of correct type.
-        SplitNodeResult splitNodeResult =
-            MaybeSplitAncestorsForInsertWithTransaction(
-                MOZ_KnownLive(*containerName), atCurNode);
-        if (NS_WARN_IF(splitNodeResult.Failed())) {
-          return splitNodeResult.Rv();
-        }
-        curList = CreateNodeWithTransaction(MOZ_KnownLive(*containerName),
-                                            splitNodeResult.SplitPoint());
-        if (NS_WARN_IF(Destroyed())) {
-          return NS_ERROR_EDITOR_DESTROYED;
-        }
-        if (NS_WARN_IF(!curList)) {
-          return NS_ERROR_FAILURE;
-        }
-        // curList is now the correct thing to put curNode in
-        // remember our new block for postprocessing
-        TopLevelEditSubActionDataRef().mNewBlockElement = curList;
-      }
-      // tuck the node into the end of the active list
-      rv = MoveNodeToEndWithTransaction(MOZ_KnownLive(*curNode->AsContent()),
-                                        *curList);
-      if (NS_WARN_IF(Destroyed())) {
-        return NS_ERROR_EDITOR_DESTROYED;
-      }
-      if (NS_WARN_IF(NS_FAILED(rv))) {
+      nsresult rv = IndentListChild(&curList, atCurNode, curNode);
+      if (NS_FAILED(rv)) {
         return rv;
       }
       // forget curQuote, if any
       curQuote = nullptr;
-
       continue;
     }
 
     // Not a list item, use blockquote?
 
     // if we are inside a list item, we don't want to blockquote, we want
     // to sublist the list item.  We may have several nodes listed in the
     // array of nodes to act on, that are in the same list item.  Since
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -3784,40 +3784,32 @@ nsresult HTMLEditor::RemoveBlockContaine
   // Now remove container
   nsresult rv = RemoveContainerWithTransaction(aElement);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
 
-/**
- * GetPriorHTMLSibling() returns the previous editable sibling, if there is
- * one within the parent.
- */
-nsIContent* HTMLEditor::GetPriorHTMLSibling(nsINode* aNode) {
+nsIContent* HTMLEditor::GetPriorHTMLSibling(nsINode* aNode, SkipWhitespace aSkipWS) {
   MOZ_ASSERT(aNode);
 
   nsIContent* node = aNode->GetPreviousSibling();
-  while (node && !IsEditable(node)) {
+  while (node && (!IsEditable(node) || SkippableWhitespace(node, aSkipWS))) {
     node = node->GetPreviousSibling();
   }
 
   return node;
 }
 
-/**
- * GetNextHTMLSibling() returns the next editable sibling, if there is
- * one within the parent.
- */
-nsIContent* HTMLEditor::GetNextHTMLSibling(nsINode* aNode) {
+nsIContent* HTMLEditor::GetNextHTMLSibling(nsINode* aNode, SkipWhitespace aSkipWS) {
   MOZ_ASSERT(aNode);
 
   nsIContent* node = aNode->GetNextSibling();
-  while (node && !IsEditable(node)) {
+  while (node && (!IsEditable(node) || SkippableWhitespace(node, aSkipWS))) {
     node = node->GetNextSibling();
   }
 
   return node;
 }
 
 nsIContent* HTMLEditor::GetPreviousHTMLElementOrTextInternal(
     nsINode& aNode, bool aNoBlockCrossing) {
--- a/editor/libeditor/HTMLEditor.h
+++ b/editor/libeditor/HTMLEditor.h
@@ -1010,19 +1010,36 @@ class HTMLEditor final : public TextEdit
    *                            with topmost split element.  If this didn't
    *                            find inline elements to be split, Handled()
    *                            returns false.
    */
   MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE SplitNodeResult
   SplitAncestorStyledInlineElementsAt(const EditorDOMPoint& aPointToSplit,
                                       nsAtom* aProperty, nsAtom* aAttribute);
 
-  nsIContent* GetPriorHTMLSibling(nsINode* aNode);
-
-  nsIContent* GetNextHTMLSibling(nsINode* aNode);
+  /**
+   * GetPriorHTMLSibling() returns the previous editable sibling, if there is
+   * one within the parent, optionally skipping text nodes that are only
+   * whitespace.
+   */
+  enum class SkipWhitespace { Yes, No };
+  nsIContent* GetPriorHTMLSibling(nsINode* aNode, SkipWhitespace = SkipWhitespace::No);
+
+  /**
+   * GetNextHTMLSibling() returns the next editable sibling, if there is
+   * one within the parent, optionally skipping text nodes that are only
+   * whitespace.
+   */
+  nsIContent* GetNextHTMLSibling(nsINode* aNode, SkipWhitespace = SkipWhitespace::No);
+
+  // Helper for GetPriorHTMLSibling/GetNextHTMLSibling.
+  static bool SkippableWhitespace(nsINode* aNode, SkipWhitespace aSkipWS) {
+    return aSkipWS == SkipWhitespace::Yes &&
+      aNode->IsText() && aNode->AsText()->TextIsOnlyWhitespace();
+  }
 
   /**
    * GetPreviousHTMLElementOrText*() methods are similar to
    * EditorBase::GetPreviousElementOrText*() but this won't return nodes
    * outside active editing host.
    */
   nsIContent* GetPreviousHTMLElementOrText(nsINode& aNode) {
     return GetPreviousHTMLElementOrTextInternal(aNode, false);
@@ -4306,16 +4323,22 @@ class HTMLEditor final : public TextEdit
   already_AddRefed<nsRange> GetChangedRangeForTopLevelEditSubAction() const {
     if (!mChangedRangeForTopLevelEditSubAction) {
       mChangedRangeForTopLevelEditSubAction = new nsRange(GetDocument());
     }
     return do_AddRef(mChangedRangeForTopLevelEditSubAction);
   }
 
  protected:
+  // Helper for Handle[CSS|HTML]IndentAtSelectionInternal
+  MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE nsresult
+  IndentListChild(RefPtr<Element>* aCurList,
+                  const EditorDOMPoint& aCurPoint,
+                  OwningNonNull<nsINode>& aCurNode);
+
   RefPtr<TypeInState> mTypeInState;
   RefPtr<ComposerCommandsUpdater> mComposerCommandsUpdater;
 
   // Used by TopLevelEditSubActionData::mSelectedRange.
   mutable RefPtr<RangeItem> mSelectedRangeForTopLevelEditSubAction;
   // Used by TopLevelEditSubActionData::mChangedRange.
   mutable RefPtr<nsRange> mChangedRangeForTopLevelEditSubAction;
 
new file mode 100644
--- /dev/null
+++ b/editor/reftests/exec-command-indent-ws-ref.html
@@ -0,0 +1,61 @@
+<!DOCTYPE HTML>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+  <meta charset="utf-8">
+  <title>Reference for bug </title>
+<style>
+html,body {
+  color:black; background-color:white; font:10px/1 monospace; padding:0; margin:0;
+}
+
+li::before { content: " list-item counter:" counters(list-item,".") " "; }
+ol,ul { border:1px solid; margin: 0; }
+div > ul { counter-reset: list-item 7; }
+</style>
+</head>
+<body>
+
+<div contenteditable>
+<ol start=8>
+  <li>A</li>
+  <ol><li class="indent">B</li></ol>
+  <li>C</li>
+</ol>
+</div>
+
+<div contenteditable>
+<ol start=8>
+  <li>A</li>
+  <ol><li class="indent">B</li></ol>
+  <li>C</li>
+</ol>
+</div>
+
+<div contenteditable>
+<ul>
+  <li>A</li>
+  <ul><li class="indent">B</li></ul>
+  <li>C</li>
+</ul>
+</div>
+
+<div contenteditable>
+<ul>
+  <li>A</li>
+  <ul><li class="indent">B</li></ul>
+  <li>C</li>
+</ul>
+</div>
+
+<!-- now the same as above without whitespace: -->
+
+<div contenteditable><ol start=8><li>A</li><ol><li class="indent">B</li></ol><li>C</li></ol></div>
+<div contenteditable><ol start=8><li>A</li><ol><li class="indent">B</li></ol><li>C</li></ol></div>
+<div contenteditable><ul><li>A</li><ul><li class="indent">B</li></ul><li>C</li></ul></div>
+<div contenteditable><ul><li>A</li><ul><li class="indent">B</li></ul><li>C</li></ul></div>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/editor/reftests/exec-command-indent-ws.html
@@ -0,0 +1,81 @@
+<!DOCTYPE HTML>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+  <meta charset="utf-8">
+  <title>Testcase for bug </title>
+<style>
+html,body {
+  color:black; background-color:white; font:10px/1 monospace; padding:0; margin:0;
+}
+
+li::before { content: " list-item counter:" counters(list-item,".") " "; }
+ol,ul { border:1px solid; margin: 0; }
+div > ul { counter-reset: list-item 7; }
+</style>
+</head>
+<body>
+
+<div contenteditable>
+<ol start=8>
+  <li>A</li>
+  <ol></ol>
+  <li class="indent">B</li>
+  <li>C</li>
+</ol>
+</div>
+
+<div contenteditable>
+<ol start=8>
+  <li>A</li>
+  <li class="indent">B</li>
+  <ol></ol>
+  <li>C</li>
+</ol>
+</div>
+
+<div contenteditable>
+<ul>
+  <li>A</li>
+  <ul></ul>
+  <li class="indent">B</li>
+  <li>C</li>
+</ul>
+</div>
+
+<div contenteditable>
+<ul>
+  <li>A</li>
+  <li class="indent">B</li>
+  <ul></ul>
+  <li>C</li>
+</ul>
+</div>
+
+<!-- now the same as above without whitespace: -->
+
+<div contenteditable><ol start=8><li>A</li><ol></ol><li class="indent">B</li><li>C</li></ol></div>
+<div contenteditable><ol start=8><li>A</li><li class="indent">B</li><ol></ol><li>C</li></ol></div>
+<div contenteditable><ul><li>A</li><ul></ul><li class="indent">B</li><li>C</li></ul></div>
+<div contenteditable><ul><li>A</li><li class="indent">B</li><ul></ul><li>C</li></ul></div>
+
+<script>
+function test() {
+  [...document.querySelectorAll('.indent')].forEach(function(elm) {
+    var r = document.createRange();
+    r.setStart(elm.firstChild,0)
+    r.setEnd(elm.firstChild,0)
+    window.getSelection().addRange(r);
+    document.execCommand("indent");
+    window.getSelection().removeAllRanges();
+  });
+}
+
+test();
+document.activeElement.blur();
+</script>
+
+</body>
+</html>
--- a/editor/reftests/reftest.list
+++ b/editor/reftests/reftest.list
@@ -145,8 +145,9 @@ needs-focus == spellcheck-contenteditabl
 == 911201.html 911201-ref.html
 needs-focus == 969773.html 969773-ref.html
 fuzzy-if(skiaContent,0-1,0-220) == 997805.html 997805-ref.html
 fuzzy-if(skiaContent,0-1,0-220) skip-if(verify&&OSX) == 1088158.html 1088158-ref.html
 fuzzy-if(Android,0-1,0-1) needs-focus == 1443902-1.html 1443902-1-ref.html
 fuzzy-if(Android,0-1,0-1) needs-focus == 1443902-2.html 1443902-2-ref.html
 fuzzy-if(Android,0-1,0-1) needs-focus == 1443902-3.html 1443902-3-ref.html
 fuzzy-if(Android,0-1,0-1) needs-focus == 1443902-4.html 1443902-4-ref.html
+== exec-command-indent-ws.html exec-command-indent-ws-ref.html
--- a/ipc/glue/IdleSchedulerChild.cpp
+++ b/ipc/glue/IdleSchedulerChild.cpp
@@ -2,54 +2,54 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/ipc/IdleSchedulerChild.h"
 #include "mozilla/ipc/IdleSchedulerParent.h"
 #include "mozilla/Atomics.h"
-#include "mozilla/PrioritizedEventQueue.h"
+#include "mozilla/IdlePeriodState.h"
 #include "BackgroundChild.h"
 
 namespace mozilla {
 namespace ipc {
 
 static IdleSchedulerChild* sMainThreadIdleScheduler = nullptr;
 
 IdleSchedulerChild::~IdleSchedulerChild() {
   if (sMainThreadIdleScheduler == this) {
     sMainThreadIdleScheduler = nullptr;
   }
-  MOZ_ASSERT(!mEventQueue);
+  MOZ_ASSERT(!mIdlePeriodState);
 }
 
-void IdleSchedulerChild::Init(PrioritizedEventQueue* aEventQueue) {
-  mEventQueue = aEventQueue;
+void IdleSchedulerChild::Init(IdlePeriodState* aIdlePeriodState) {
+  mIdlePeriodState = aIdlePeriodState;
 
   RefPtr<IdleSchedulerChild> scheduler = this;
   auto resolve =
       [&](Tuple<mozilla::Maybe<SharedMemoryHandle>, uint32_t>&& aResult) {
         if (Get<0>(aResult)) {
           mActiveCounter.SetHandle(*Get<0>(aResult), false);
           mActiveCounter.Map(sizeof(int32_t));
           mChildId = Get<1>(aResult);
-          if (mChildId && mEventQueue && mEventQueue->IsActive()) {
+          if (mChildId && mIdlePeriodState && mIdlePeriodState->IsActive()) {
             SetActive();
           }
         }
       };
 
   auto reject = [&](ResponseRejectReason) {};
   SendInitForIdleUse(std::move(resolve), std::move(reject));
 }
 
 IPCResult IdleSchedulerChild::RecvIdleTime(uint64_t aId, TimeDuration aBudget) {
-  if (mEventQueue) {
-    mEventQueue->SetIdleToken(aId, aBudget);
+  if (mIdlePeriodState) {
+    mIdlePeriodState->SetIdleToken(aId, aBudget);
   }
   return IPC_OK();
 }
 
 void IdleSchedulerChild::SetActive() {
   if (mChildId && CanSend() && mActiveCounter.memory()) {
     ++(static_cast<Atomic<int32_t>*>(
         mActiveCounter.memory())[NS_IDLE_SCHEDULER_INDEX_OF_ACTIVITY_COUNTER]);
--- a/ipc/glue/IdleSchedulerChild.h
+++ b/ipc/glue/IdleSchedulerChild.h
@@ -9,50 +9,50 @@
 
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/ipc/PIdleSchedulerChild.h"
 
 class nsIIdlePeriod;
 
 namespace mozilla {
-class PrioritizedEventQueue;
+class IdlePeriodState;
 
 namespace ipc {
 
 class BackgroundChildImpl;
 
 class IdleSchedulerChild final : public PIdleSchedulerChild {
  public:
   IdleSchedulerChild() = default;
 
   NS_INLINE_DECL_REFCOUNTING(IdleSchedulerChild)
 
   IPCResult RecvIdleTime(uint64_t aId, TimeDuration aBudget);
 
-  void Init(PrioritizedEventQueue* aEventQueue);
+  void Init(IdlePeriodState* aIdlePeriodState);
 
-  void Disconnect() { mEventQueue = nullptr; }
+  void Disconnect() { mIdlePeriodState = nullptr; }
 
   // See similar methods on PrioritizedEventQueue.
   void SetActive();
   // Returns true if activity state dropped below cpu count.
   bool SetPaused();
 
   static IdleSchedulerChild* GetMainThreadIdleScheduler();
 
  private:
   ~IdleSchedulerChild();
 
   friend class BackgroundChildImpl;
 
   // See IdleScheduleParent::sActiveChildCounter
   base::SharedMemory mActiveCounter;
 
-  PrioritizedEventQueue* mEventQueue = nullptr;
+  IdlePeriodState* mIdlePeriodState = nullptr;
 
   uint32_t mChildId = 0;
 };
 
 }  // namespace ipc
 }  // namespace mozilla
 
 #endif  // mozilla_ipc_IdleSchedulerChild_h__
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -741,17 +741,17 @@ static const uint32_t JSCLASS_FOREGROUND
 // with the following flags. Failure to use JSCLASS_GLOBAL_FLAGS was
 // previously allowed, but is now an ES5 violation and thus unsupported.
 //
 // JSCLASS_GLOBAL_APPLICATION_SLOTS is the number of slots reserved at
 // the beginning of every global object's slots for use by the
 // application.
 static const uint32_t JSCLASS_GLOBAL_APPLICATION_SLOTS = 5;
 static const uint32_t JSCLASS_GLOBAL_SLOT_COUNT =
-    JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 26;
+    JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 25;
 
 static constexpr uint32_t JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(uint32_t n) {
   return JSCLASS_IS_GLOBAL |
          JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + n);
 }
 
 static constexpr uint32_t JSCLASS_GLOBAL_FLAGS =
     JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(0);
--- a/js/src/debugger/DebugAPI-inl.h
+++ b/js/src/debugger/DebugAPI-inl.h
@@ -53,32 +53,32 @@ void DebugAPI::onNewGlobalObject(JSConte
   if (!cx->runtime()->onNewGlobalObjectWatchers().isEmpty()) {
     slowPathOnNewGlobalObject(cx, global);
   }
 }
 
 /* static */
 void DebugAPI::notifyParticipatesInGC(GlobalObject* global,
                                       uint64_t majorGCNumber) {
-  GlobalObject::DebuggerVector* dbgs = global->getDebuggers();
-  if (dbgs && !dbgs->empty()) {
-    slowPathNotifyParticipatesInGC(majorGCNumber, *dbgs);
+  Realm::DebuggerVector& dbgs = global->getDebuggers();
+  if (!dbgs.empty()) {
+    slowPathNotifyParticipatesInGC(majorGCNumber, dbgs);
   }
 }
 
 /* static */
 bool DebugAPI::onLogAllocationSite(JSContext* cx, JSObject* obj,
                                    HandleSavedFrame frame,
                                    mozilla::TimeStamp when) {
-  GlobalObject::DebuggerVector* dbgs = cx->global()->getDebuggers();
-  if (!dbgs || dbgs->empty()) {
+  Realm::DebuggerVector& dbgs = cx->global()->getDebuggers();
+  if (dbgs.empty()) {
     return true;
   }
   RootedObject hobj(cx, obj);
-  return slowPathOnLogAllocationSite(cx, hobj, frame, when, *dbgs);
+  return slowPathOnLogAllocationSite(cx, hobj, frame, when, dbgs);
 }
 
 /* static */
 bool DebugAPI::onLeaveFrame(JSContext* cx, AbstractFramePtr frame,
                             jsbytecode* pc, bool ok) {
   MOZ_ASSERT_IF(frame.isInterpreterFrame(),
                 frame.asInterpreterFrame() == cx->interpreterFrame());
   MOZ_ASSERT_IF(frame.hasScript() && frame.script()->isDebuggee(),
--- a/js/src/debugger/DebugAPI.h
+++ b/js/src/debugger/DebugAPI.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef debugger_DebugAPI_h
 #define debugger_DebugAPI_h
 
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
 #include "vm/JSContext.h"
+#include "vm/Realm.h"
 
 namespace js {
 
 // This file contains the API which SpiderMonkey should use to interact with any
 // active Debuggers.
 
 class AbstractGeneratorObject;
 class PromiseObject;
@@ -332,43 +333,36 @@ class DebugAPI {
                                                       HandleSavedFrame frame,
                                                       mozilla::TimeStamp when);
 
   // Announce to the debugger that a global object is being collected by the
   // specified major GC.
   static inline void notifyParticipatesInGC(GlobalObject* global,
                                             uint64_t majorGCNumber);
 
-  // Allocate an object which holds a GlobalObject::DebuggerVector.
-  static JSObject* newGlobalDebuggersHolder(JSContext* cx);
-
-  // Get the GlobalObject::DebuggerVector for an object allocated by
-  // newGlobalDebuggersObject.
-  static GlobalObject::DebuggerVector* getGlobalDebuggers(JSObject* holder);
-
   /*
    * Get any instrumentation ID which has been associated with a script using
    * the specified debugger object.
    */
   static bool getScriptInstrumentationId(JSContext* cx, HandleObject dbgObject,
                                          HandleScript script,
                                          MutableHandleValue rval);
 
  private:
   static bool stepModeEnabledSlow(JSScript* script);
   static bool hasBreakpointsAtSlow(JSScript* script, jsbytecode* pc);
   static void sweepBreakpointsSlow(JSFreeOp* fop, JSScript* script);
   static void slowPathOnNewScript(JSContext* cx, HandleScript script);
   static void slowPathOnNewGlobalObject(JSContext* cx,
                                         Handle<GlobalObject*> global);
-  static void slowPathNotifyParticipatesInGC(
-      uint64_t majorGCNumber, GlobalObject::DebuggerVector& dbgs);
+  static void slowPathNotifyParticipatesInGC(uint64_t majorGCNumber,
+                                             JS::Realm::DebuggerVector& dbgs);
   static MOZ_MUST_USE bool slowPathOnLogAllocationSite(
       JSContext* cx, HandleObject obj, HandleSavedFrame frame,
-      mozilla::TimeStamp when, GlobalObject::DebuggerVector& dbgs);
+      mozilla::TimeStamp when, JS::Realm::DebuggerVector& dbgs);
   static MOZ_MUST_USE bool slowPathOnLeaveFrame(JSContext* cx,
                                                 AbstractFramePtr frame,
                                                 jsbytecode* pc, bool ok);
   static MOZ_MUST_USE bool slowPathOnNewGenerator(
       JSContext* cx, AbstractFramePtr frame,
       Handle<AbstractGeneratorObject*> genObj);
   static MOZ_MUST_USE bool slowPathCheckNoExecute(JSContext* cx,
                                                   HandleScript script);
--- a/js/src/debugger/Debugger.cpp
+++ b/js/src/debugger/Debugger.cpp
@@ -513,63 +513,16 @@ bool Debugger::hasMemory() const {
 
 DebuggerMemory& Debugger::memory() const {
   MOZ_ASSERT(hasMemory());
   return object->getReservedSlot(JSSLOT_DEBUG_MEMORY_INSTANCE)
       .toObject()
       .as<DebuggerMemory>();
 }
 
-/*** DebuggerVectorHolder *****************************************************/
-
-static void GlobalDebuggerVectorHolder_finalize(JSFreeOp* fop, JSObject* obj) {
-  MOZ_ASSERT(fop->maybeOnHelperThread());
-  void* ptr = obj->as<NativeObject>().getPrivate();
-  auto debuggers = static_cast<GlobalObject::DebuggerVector*>(ptr);
-  fop->delete_(obj, debuggers, MemoryUse::GlobalDebuggerVector);
-}
-
-static const JSClassOps GlobalDebuggerVectorHolder_classOps = {
-    nullptr,
-    nullptr,
-    nullptr,
-    nullptr,
-    nullptr,
-    nullptr,
-    GlobalDebuggerVectorHolder_finalize};
-
-static const JSClass GlobalDebuggerVectorHolder_class = {
-    "GlobalDebuggerVectorHolder",
-    JSCLASS_HAS_PRIVATE | JSCLASS_BACKGROUND_FINALIZE,
-    &GlobalDebuggerVectorHolder_classOps};
-
-/* static */
-JSObject* DebugAPI::newGlobalDebuggersHolder(JSContext* cx) {
-  NativeObject* obj = NewNativeObjectWithGivenProto(
-      cx, &GlobalDebuggerVectorHolder_class, nullptr);
-  if (!obj) {
-    return nullptr;
-  }
-
-  GlobalObject::DebuggerVector* debuggers =
-      cx->new_<GlobalObject::DebuggerVector>(cx->zone());
-  if (!debuggers) {
-    return nullptr;
-  }
-
-  InitObjectPrivate(obj, debuggers, MemoryUse::GlobalDebuggerVector);
-  return obj;
-}
-
-/* static */
-GlobalObject::DebuggerVector* DebugAPI::getGlobalDebuggers(JSObject* holder) {
-  MOZ_ASSERT(holder->getClass() == &GlobalDebuggerVectorHolder_class);
-  return (GlobalObject::DebuggerVector*)holder->as<NativeObject>().getPrivate();
-}
-
 /*** Debugger accessors *******************************************************/
 
 bool Debugger::getFrame(JSContext* cx, const FrameIter& iter,
                         MutableHandleValue vp) {
   RootedDebuggerFrame result(cx);
   if (!Debugger::getFrame(cx, iter, &result)) {
     return false;
   }
@@ -660,23 +613,22 @@ bool Debugger::getFrame(JSContext* cx, c
 }
 
 static bool DebuggerExists(
     GlobalObject* global, const std::function<bool(Debugger* dbg)>& predicate) {
   // The GC analysis can't determine that the predicate can't GC, so let it know
   // explicitly.
   JS::AutoSuppressGCAnalysis nogc;
 
-  if (GlobalObject::DebuggerVector* debuggers = global->getDebuggers()) {
-    for (auto p = debuggers->begin(); p != debuggers->end(); p++) {
-      // Callbacks should not create new references to the debugger, so don't
-      // use a barrier. This allows this method to be called during GC.
-      if (predicate(p->unbarrieredGet())) {
-        return true;
-      }
+  Realm::DebuggerVector& debuggers = global->getDebuggers();
+  for (auto p = debuggers.begin(); p != debuggers.end(); p++) {
+    // Callbacks should not create new references to the debugger, so don't
+    // use a barrier. This allows this method to be called during GC.
+    if (predicate(p->unbarrieredGet())) {
+      return true;
     }
   }
   return false;
 }
 
 /* static */
 bool Debugger::hasLiveHook(GlobalObject* global, Hook which) {
   return DebuggerExists(global,
@@ -810,36 +762,34 @@ ResumeMode DebugAPI::slowPathOnResumeFra
 
   Rooted<AbstractGeneratorObject*> genObj(
       cx, GetGeneratorObjectForFrame(cx, frame));
   MOZ_ASSERT(genObj);
 
   // For each debugger, if there is an existing Debugger.Frame object for the
   // resumed `frame`, update it with the new frame pointer and make sure the
   // frame is observable.
-  if (GlobalObject::DebuggerVector* debuggers =
-          frame.global()->getDebuggers()) {
-    for (Debugger* dbg : *debuggers) {
-      if (Debugger::GeneratorWeakMap::Ptr entry =
-              dbg->generatorFrames.lookup(genObj)) {
-        DebuggerFrame* frameObj = &entry->value()->as<DebuggerFrame>();
-        MOZ_ASSERT(&frameObj->unwrappedGenerator() == genObj);
-        if (!dbg->frames.putNew(frame, frameObj)) {
-          ReportOutOfMemory(cx);
-          return ResumeMode::Throw;
-        }
-
-        FrameIter iter(cx);
-        MOZ_ASSERT(iter.abstractFramePtr() == frame);
-        if (!frameObj->resume(iter)) {
-          return ResumeMode::Throw;
-        }
-        if (!Debugger::ensureExecutionObservabilityOfFrame(cx, frame)) {
-          return ResumeMode::Throw;
-        }
+  Realm::DebuggerVector& debuggers = frame.global()->getDebuggers();
+  for (Debugger* dbg : debuggers) {
+    if (Debugger::GeneratorWeakMap::Ptr entry =
+            dbg->generatorFrames.lookup(genObj)) {
+      DebuggerFrame* frameObj = &entry->value()->as<DebuggerFrame>();
+      MOZ_ASSERT(&frameObj->unwrappedGenerator() == genObj);
+      if (!dbg->frames.putNew(frame, frameObj)) {
+        ReportOutOfMemory(cx);
+        return ResumeMode::Throw;
+      }
+
+      FrameIter iter(cx);
+      MOZ_ASSERT(iter.abstractFramePtr() == frame);
+      if (!frameObj->resume(iter)) {
+        return ResumeMode::Throw;
+      }
+      if (!Debugger::ensureExecutionObservabilityOfFrame(cx, frame)) {
+        return ResumeMode::Throw;
       }
     }
   }
 
   return slowPathOnEnterFrame(cx, frame);
 }
 
 /* static */
@@ -2287,23 +2237,22 @@ ResumeMode Debugger::dispatchHook(JSCont
   // Determine which debuggers will receive this event, and in what order.
   // Make a copy of the list, since the original is mutable and we will be
   // calling into arbitrary JS.
   //
   // Note: In the general case, 'triggered' contains references to objects in
   // different compartments--every compartment *except* this one.
   RootedValueVector triggered(cx);
   Handle<GlobalObject*> global = cx->global();
-  if (GlobalObject::DebuggerVector* debuggers = global->getDebuggers()) {
-    for (auto p = debuggers->begin(); p != debuggers->end(); p++) {
-      Debugger* dbg = *p;
-      if (hookIsEnabled(dbg)) {
-        if (!triggered.append(ObjectValue(*dbg->toJSObject()))) {
-          return ResumeMode::Terminate;
-        }
+  Realm::DebuggerVector& debuggers = global->getDebuggers();
+  for (auto p = debuggers.begin(); p != debuggers.end(); p++) {
+    Debugger* dbg = *p;
+    if (hookIsEnabled(dbg)) {
+      if (!triggered.append(ObjectValue(*dbg->toJSObject()))) {
+        return ResumeMode::Terminate;
       }
     }
   }
 
   // Preserve the debuggee's microtask event queue while we run the hooks, so
   // the debugger's microtask checkpoints don't run from the debuggee's
   // microtasks, and vice versa.
   JS::AutoDebuggerJobQueueInterruption adjqi;
@@ -2559,56 +2508,55 @@ ResumeMode DebugAPI::onSingleStep(JSCont
   //
   // The converse --- ensuring that we do receive traps when we should --- can
   // be done with unit tests.
   if (iter.hasScript()) {
     uint32_t liveStepperCount = 0;
     uint32_t suspendedStepperCount = 0;
     JSScript* trappingScript = iter.script();
     GlobalObject* global = cx->global();
-    if (GlobalObject::DebuggerVector* debuggers = global->getDebuggers()) {
-      for (auto p = debuggers->begin(); p != debuggers->end(); p++) {
-        Debugger* dbg = *p;
-        for (Debugger::FrameMap::Range r = dbg->frames.all(); !r.empty();
-             r.popFront()) {
-          AbstractFramePtr frame = r.front().key();
-          NativeObject* frameobj = r.front().value();
-          if (frame.isWasmDebugFrame()) {
-            continue;
-          }
-          if (frame.script() == trappingScript &&
-              !frameobj->getReservedSlot(DebuggerFrame::ONSTEP_HANDLER_SLOT)
-                   .isUndefined()) {
-            liveStepperCount++;
-          }
+    Realm::DebuggerVector& debuggers = global->getDebuggers();
+    for (auto p = debuggers.begin(); p != debuggers.end(); p++) {
+      Debugger* dbg = *p;
+      for (Debugger::FrameMap::Range r = dbg->frames.all(); !r.empty();
+           r.popFront()) {
+        AbstractFramePtr frame = r.front().key();
+        NativeObject* frameobj = r.front().value();
+        if (frame.isWasmDebugFrame()) {
+          continue;
+        }
+        if (frame.script() == trappingScript &&
+            !frameobj->getReservedSlot(DebuggerFrame::ONSTEP_HANDLER_SLOT)
+                 .isUndefined()) {
+          liveStepperCount++;
         }
-
-        // Also count hooks set on suspended generator frames.
-        for (Debugger::GeneratorWeakMap::Range r = dbg->generatorFrames.all();
-             !r.empty(); r.popFront()) {
-          AbstractGeneratorObject& genObj =
-              r.front().key()->as<AbstractGeneratorObject>();
-          DebuggerFrame& frameObj = r.front().value()->as<DebuggerFrame>();
-          MOZ_ASSERT(&frameObj.unwrappedGenerator() == &genObj);
-
-          // Live Debugger.Frames were already counted in dbg->frames loop.
-          if (frameObj.isLive()) {
-            continue;
-          }
-
-          // If a frame isn't live, but it has an entry in generatorFrames,
-          // it had better be suspended.
-          MOZ_ASSERT(genObj.isSuspended());
-
-          if (!genObj.callee().isInterpretedLazy() &&
-              genObj.callee().nonLazyScript() == trappingScript &&
-              !frameObj.getReservedSlot(DebuggerFrame::ONSTEP_HANDLER_SLOT)
-                   .isUndefined()) {
-            suspendedStepperCount++;
-          }
+      }
+
+      // Also count hooks set on suspended generator frames.
+      for (Debugger::GeneratorWeakMap::Range r = dbg->generatorFrames.all();
+           !r.empty(); r.popFront()) {
+        AbstractGeneratorObject& genObj =
+            r.front().key()->as<AbstractGeneratorObject>();
+        DebuggerFrame& frameObj = r.front().value()->as<DebuggerFrame>();
+        MOZ_ASSERT(&frameObj.unwrappedGenerator() == &genObj);
+
+        // Live Debugger.Frames were already counted in dbg->frames loop.
+        if (frameObj.isLive()) {
+          continue;
+        }
+
+        // If a frame isn't live, but it has an entry in generatorFrames,
+        // it had better be suspended.
+        MOZ_ASSERT(genObj.isSuspended());
+
+        if (!genObj.callee().isInterpretedLazy() &&
+            genObj.callee().nonLazyScript() == trappingScript &&
+            !frameObj.getReservedSlot(DebuggerFrame::ONSTEP_HANDLER_SLOT)
+                 .isUndefined()) {
+          suspendedStepperCount++;
         }
       }
     }
 
     MOZ_ASSERT(liveStepperCount + suspendedStepperCount ==
                DebugScript::getStepperCount(trappingScript));
   }
 #endif
@@ -2747,47 +2695,46 @@ void DebugAPI::slowPathOnNewGlobalObject
         break;
       }
     }
   }
   MOZ_ASSERT(!cx->isExceptionPending());
 }
 
 /* static */
-void DebugAPI::slowPathNotifyParticipatesInGC(
-    uint64_t majorGCNumber, GlobalObject::DebuggerVector& dbgs) {
-  for (GlobalObject::DebuggerVector::Range r = dbgs.all(); !r.empty();
-       r.popFront()) {
+void DebugAPI::slowPathNotifyParticipatesInGC(uint64_t majorGCNumber,
+                                              Realm::DebuggerVector& dbgs) {
+  for (Realm::DebuggerVector::Range r = dbgs.all(); !r.empty(); r.popFront()) {
     if (!r.front().unbarrieredGet()->debuggeeIsBeingCollected(majorGCNumber)) {
 #ifdef DEBUG
       fprintf(stderr,
               "OOM while notifying observing Debuggers of a GC: The "
               "onGarbageCollection\n"
               "hook will not be fired for this GC for some Debuggers!\n");
 #endif
       return;
     }
   }
 }
 
 /* static */
 Maybe<double> DebugAPI::allocationSamplingProbability(GlobalObject* global) {
-  GlobalObject::DebuggerVector* dbgs = global->getDebuggers();
-  if (!dbgs || dbgs->empty()) {
+  Realm::DebuggerVector& dbgs = global->getDebuggers();
+  if (dbgs.empty()) {
     return Nothing();
   }
 
-  DebugOnly<WeakHeapPtr<Debugger*>*> begin = dbgs->begin();
+  DebugOnly<WeakHeapPtr<Debugger*>*> begin = dbgs.begin();
 
   double probability = 0;
   bool foundAnyDebuggers = false;
-  for (auto p = dbgs->begin(); p < dbgs->end(); p++) {
+  for (auto p = dbgs.begin(); p < dbgs.end(); p++) {
     // The set of debuggers had better not change while we're iterating,
     // such that the vector gets reallocated.
-    MOZ_ASSERT(dbgs->begin() == begin);
+    MOZ_ASSERT(dbgs.begin() == begin);
     // Use unbarrieredGet() to prevent triggering read barrier while collecting,
     // this is safe as long as dbgp does not escape.
     Debugger* dbgp = p->unbarrieredGet();
 
     if (dbgp->trackingAllocationSites) {
       foundAnyDebuggers = true;
       probability = std::max(dbgp->allocationSamplingProbability, probability);
     }
@@ -2795,17 +2742,17 @@ Maybe<double> DebugAPI::allocationSampli
 
   return foundAnyDebuggers ? Some(probability) : Nothing();
 }
 
 /* static */
 bool DebugAPI::slowPathOnLogAllocationSite(JSContext* cx, HandleObject obj,
                                            HandleSavedFrame frame,
                                            mozilla::TimeStamp when,
-                                           GlobalObject::DebuggerVector& dbgs) {
+                                           Realm::DebuggerVector& dbgs) {
   MOZ_ASSERT(!dbgs.empty());
   mozilla::DebugOnly<WeakHeapPtr<Debugger*>*> begin = dbgs.begin();
 
   // Root all the Debuggers while we're iterating over them;
   // appendAllocationSite calls Compartment::wrap, and thus can GC.
   //
   // SpiderMonkey protocol is generally for the caller to prove that it has
   // rooted the stuff it's asking you to operate on (i.e. by passing a
@@ -3272,22 +3219,21 @@ bool Debugger::updateExecutionObservabil
 
   return true;
 }
 
 template <typename FrameFn>
 /* static */
 void Debugger::forEachDebuggerFrame(AbstractFramePtr frame, FrameFn fn) {
   GlobalObject* global = frame.global();
-  if (GlobalObject::DebuggerVector* debuggers = global->getDebuggers()) {
-    for (auto p = debuggers->begin(); p != debuggers->end(); p++) {
-      Debugger* dbg = *p;
-      if (FrameMap::Ptr entry = dbg->frames.lookup(frame)) {
-        fn(entry->value());
-      }
+  Realm::DebuggerVector& debuggers = global->getDebuggers();
+  for (auto p = debuggers.begin(); p != debuggers.end(); p++) {
+    Debugger* dbg = *p;
+    if (FrameMap::Ptr entry = dbg->frames.lookup(frame)) {
+      fn(entry->value());
     }
   }
 }
 
 /* static */
 bool Debugger::getDebuggerFrames(AbstractFramePtr frame,
                                  MutableHandle<DebuggerFrameVector> frames) {
   bool hadOOM = false;
@@ -3496,24 +3442,23 @@ void Debugger::updateObservesAsmJSOnDebu
 bool Debugger::cannotTrackAllocations(const GlobalObject& global) {
   auto existingCallback = global.realm()->getAllocationMetadataBuilder();
   return existingCallback && existingCallback != &SavedStacks::metadataBuilder;
 }
 
 /* static */
 bool DebugAPI::isObservedByDebuggerTrackingAllocations(
     const GlobalObject& debuggee) {
-  if (auto* v = debuggee.getDebuggers()) {
-    for (auto p = v->begin(); p != v->end(); p++) {
-      // Use unbarrieredGet() to prevent triggering read barrier while
-      // collecting, this is safe as long as dbg does not escape.
-      Debugger* dbg = p->unbarrieredGet();
-      if (dbg->trackingAllocationSites) {
-        return true;
-      }
+  auto& v = debuggee.getDebuggers();
+  for (auto p = v.begin(); p != v.end(); p++) {
+    // Use unbarrieredGet() to prevent triggering read barrier while
+    // collecting, this is safe as long as dbg does not escape.
+    Debugger* dbg = p->unbarrieredGet();
+    if (dbg->trackingAllocationSites) {
+      return true;
     }
   }
 
   return false;
 }
 
 /* static */
 bool Debugger::addAllocationsTracking(JSContext* cx,
@@ -3740,21 +3685,18 @@ bool DebugAPI::markIteratively(GCMarker*
   JSRuntime* rt = marker->runtime();
   for (RealmsIter r(rt); !r.done(); r.next()) {
     if (r->isDebuggee()) {
       GlobalObject* global = r->unsafeUnbarrieredMaybeGlobal();
       if (!IsMarkedUnbarriered(rt, &global)) {
         continue;
       }
 
-      // Every debuggee has at least one debugger, so in this case
-      // getDebuggers can't return nullptr.
-      const GlobalObject::DebuggerVector* debuggers = global->getDebuggers();
-      MOZ_ASSERT(debuggers);
-      for (auto p = debuggers->begin(); p != debuggers->end(); p++) {
+      const Realm::DebuggerVector& debuggers = global->getDebuggers();
+      for (auto p = debuggers.begin(); p != debuggers.end(); p++) {
         Debugger* dbg = p->unbarrieredGet();
 
         // dbg is a Debugger with at least one debuggee. Check three things:
         //   - dbg is actually in a compartment that is being marked
         //   - it isn't already marked
         //   - it actually has hooks that might be called
         GCPtrNativeObject& dbgobj = dbg->toJSObjectRef();
         if (!dbgobj->zone()->isGCMarking()) {
@@ -3915,21 +3857,21 @@ void DebugAPI::sweepAll(JSFreeOp* fop) {
 
     dbg = next;
   }
 }
 
 /* static */
 void Debugger::detachAllDebuggersFromGlobal(JSFreeOp* fop,
                                             GlobalObject* global) {
-  const GlobalObject::DebuggerVector* debuggers = global->getDebuggers();
-  MOZ_ASSERT(!debuggers->empty());
-  while (!debuggers->empty()) {
-    debuggers->back()->removeDebuggeeGlobal(fop, global, nullptr,
-                                            Debugger::FromSweep::No);
+  const Realm::DebuggerVector& debuggers = global->getDebuggers();
+  MOZ_ASSERT(!debuggers.empty());
+  while (!debuggers.empty()) {
+    debuggers.back()->removeDebuggeeGlobal(fop, global, nullptr,
+                                           Debugger::FromSweep::No);
   }
 }
 
 static inline bool SweepZonesInSameGroup(Zone* a, Zone* b) {
   // Ensure two zones are swept in the same sweep group by adding an edge
   // between them in each direction.
   return a->addSweepGroupEdgeTo(b) && b->addSweepGroupEdgeTo(a);
 }
@@ -4393,17 +4335,17 @@ bool Debugger::CallData::removeDebuggee(
 
   if (dbg->debuggees.has(global)) {
     dbg->removeDebuggeeGlobal(cx->runtime()->defaultFreeOp(), global, nullptr,
                               FromSweep::No);
 
     // Only update the realm if there are no Debuggers left, as it's
     // expensive to check if no other Debugger has a live script or frame
     // hook on any of the current on-stack debuggee frames.
-    if (global->getDebuggers()->empty() && !obs.add(global->realm())) {
+    if (global->getDebuggers().empty() && !obs.add(global->realm())) {
       return false;
     }
     if (!updateExecutionObservability(cx, obs, NotObserving)) {
       return false;
     }
   }
 
   args.rval().setUndefined();
@@ -4414,17 +4356,17 @@ bool Debugger::CallData::removeAllDebugg
   ExecutionObservableRealms obs(cx);
 
   for (WeakGlobalObjectSet::Enum e(dbg->debuggees); !e.empty(); e.popFront()) {
     Rooted<GlobalObject*> global(cx, e.front());
     dbg->removeDebuggeeGlobal(cx->runtime()->defaultFreeOp(), global, &e,
                               FromSweep::No);
 
     // See note about adding to the observable set in removeDebuggee.
-    if (global->getDebuggers()->empty() && !obs.add(global->realm())) {
+    if (global->getDebuggers().empty() && !obs.add(global->realm())) {
       return false;
     }
   }
 
   if (!updateExecutionObservability(cx, obs, NotObserving)) {
     return false;
   }
 
@@ -4612,18 +4554,18 @@ bool Debugger::addDebuggeeGlobal(JSConte
     if (realm == debuggeeRealm) {
       JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_LOOP);
       return false;
     }
 
     // Find all realms containing debuggers debugging realm's global object.
     // Add those realms to visited.
     if (realm->isDebuggee()) {
-      GlobalObject::DebuggerVector* v = realm->maybeGlobal()->getDebuggers();
-      for (auto p = v->begin(); p != v->end(); p++) {
+      Realm::DebuggerVector& v = realm->getDebuggers();
+      for (auto p = v.begin(); p != v.end(); p++) {
         Realm* next = (*p)->object->realm();
         if (Find(visited, next) == visited.end() && !visited.append(next)) {
           return false;
         }
       }
     }
   }
 
@@ -4637,26 +4579,22 @@ bool Debugger::addDebuggeeGlobal(JSConte
   // 5. Realm::isDebuggee()'s bit must be set.
   //
   // All five indications must be kept consistent.
 
   AutoRealm ar(cx, global);
   Zone* zone = global->zone();
 
   // (1)
-  auto* globalDebuggers = GlobalObject::getOrCreateDebuggers(cx, global);
-  if (!globalDebuggers) {
-    return false;
-  }
-  if (!globalDebuggers->append(this)) {
+  auto& globalDebuggers = global->getDebuggers();
+  if (!globalDebuggers.append(this)) {
     ReportOutOfMemory(cx);
     return false;
   }
-  auto globalDebuggersGuard =
-      MakeScopeExit([&] { globalDebuggers->popBack(); });
+  auto globalDebuggersGuard = MakeScopeExit([&] { globalDebuggers.popBack(); });
 
   // (2)
   if (!debuggees.put(global)) {
     ReportOutOfMemory(cx);
     return false;
   }
   auto debuggeesGuard = MakeScopeExit([&] { debuggees.remove(global); });
 
@@ -4724,24 +4662,24 @@ static T* findDebuggerInVector(Debugger*
   MOZ_ASSERT(p != vec->end());
   return p;
 }
 
 // a WeakHeapPtr version for findDebuggerInVector
 // TODO: Bug 1515934 - findDebuggerInVector<T> triggers read barriers.
 template <typename AP>
 static WeakHeapPtr<Debugger*>* findDebuggerInVector(
-    Debugger* dbg, Vector<WeakHeapPtr<Debugger*>, 0, AP>* vec) {
+    Debugger* dbg, Vector<WeakHeapPtr<Debugger*>, 0, AP>& vec) {
   WeakHeapPtr<Debugger*>* p;
-  for (p = vec->begin(); p != vec->end(); p++) {
+  for (p = vec.begin(); p != vec.end(); p++) {
     if (p->unbarrieredGet() == dbg) {
       break;
     }
   }
-  MOZ_ASSERT(p != vec->end());
+  MOZ_ASSERT(p != vec.end());
   return p;
 }
 
 void Debugger::removeDebuggeeGlobal(JSFreeOp* fop, GlobalObject* global,
                                     WeakGlobalObjectSet::Enum* debugEnum,
                                     FromSweep fromSweep) {
   // The caller might have found global by enumerating this->debuggees; if
   // so, use HashSet::Enum::removeFront rather than HashSet::remove below,
@@ -4784,28 +4722,28 @@ void Debugger::removeDebuggeeGlobal(JSFr
       auto& genObj = e.front().key()->as<AbstractGeneratorObject>();
       auto& frameObj = e.front().value()->as<DebuggerFrame>();
       if (genObj.isClosed() || &genObj.callee().global() == global) {
         frameObj.clearGenerator(fop, this, &e);
       }
     }
   }
 
-  auto* globalDebuggersVector = global->getDebuggers();
+  auto& globalDebuggersVector = global->getDebuggers();
 
   // The relation must be removed from up to three places:
   // globalDebuggersVector and debuggees for sure, and possibly the
   // compartment's debuggee set.
   //
   // The debuggee zone set is recomputed on demand. This avoids refcounting
   // and in practice we have relatively few debuggees that tend to all be in
   // the same zone. If after recomputing the debuggee zone set, this global's
   // zone is not in the set, then we must remove ourselves from the zone's
   // vector of observing debuggers.
-  globalDebuggersVector->erase(
+  globalDebuggersVector.erase(
       findDebuggerInVector(this, globalDebuggersVector));
 
   if (debugEnum) {
     debugEnum->removeFront();
   } else {
     debuggees.remove(global);
   }
 
@@ -4833,17 +4771,17 @@ void Debugger::removeDebuggeeGlobal(JSFr
   MOZ_ASSERT_IF(debuggees.empty(), !firstBreakpoint());
 
   // If we are tracking allocation sites, we need to remove the object
   // metadata callback from this global's realm.
   if (trackingAllocationSites) {
     Debugger::removeAllocationsTracking(*global);
   }
 
-  if (global->getDebuggers()->empty()) {
+  if (global->realm()->getDebuggers().empty()) {
     global->realm()->unsetIsDebuggee();
   } else {
     global->realm()->updateDebuggerObservesAllExecution();
     global->realm()->updateDebuggerObservesAsmJS();
     global->realm()->updateDebuggerObservesCoverage();
   }
 }
 
--- a/js/src/gc/GCEnum.h
+++ b/js/src/gc/GCEnum.h
@@ -132,17 +132,16 @@ enum class ZealMode {
   _(WasmModule)                            \
   _(WasmTableTable)                        \
   _(FileObjectFile)                        \
   _(Debugger)                              \
   _(DebuggerFrameGeneratorInfo)            \
   _(DebuggerFrameIterData)                 \
   _(DebuggerOnStepHandler)                 \
   _(DebuggerOnPopHandler)                  \
-  _(GlobalDebuggerVector)                  \
   _(RealmInstrumentation)
 
 #define JS_FOR_EACH_MEMORY_USE(_)  \
   JS_FOR_EACH_PUBLIC_MEMORY_USE(_) \
   JS_FOR_EACH_INTERNAL_MEMORY_USE(_)
 
 enum class MemoryUse : uint8_t {
 #define DEFINE_MEMORY_USE(Name) Name,
--- a/js/src/gc/Zone.h
+++ b/js/src/gc/Zone.h
@@ -310,18 +310,16 @@ class Zone : public js::ZoneAllocator, p
 #endif
 
   void sweepAfterMinorGC(JSTracer* trc);
   void sweepBreakpoints(JSFreeOp* fop);
   void sweepUniqueIds();
   void sweepWeakMaps();
   void sweepCompartments(JSFreeOp* fop, bool keepAtleastOne, bool lastGC);
 
-  using DebuggerVector = js::Vector<js::Debugger*, 0, js::SystemAllocPolicy>;
-
  private:
   js::jit::JitZone* createJitZone(JSContext* cx);
 
   bool isQueuedForBackgroundSweep() { return isOnList(); }
 
   // Side map for storing a unique ids for cells, independent of address.
   js::ZoneOrGCTaskData<js::gc::UniqueIdMap> uniqueIds_;
 
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug1584027.js
@@ -0,0 +1,3 @@
+// |jit-test| error:unsafe filename: (invalid UTF-8 filename)
+setTestFilenameValidationCallback();
+evaluate("throw 2", {fileName: "\uDEFF"});
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -853,41 +853,16 @@ bool js::DefinePropertiesAndFunctions(JS
 
 bool js::DefineToStringTag(JSContext* cx, HandleObject obj, JSAtom* tag) {
   RootedId toStringTagId(cx,
                          SYMBOL_TO_JSID(cx->wellKnownSymbols().toStringTag));
   RootedValue tagString(cx, StringValue(tag));
   return DefineDataProperty(cx, obj, toStringTagId, tagString, JSPROP_READONLY);
 }
 
-GlobalObject::DebuggerVector* GlobalObject::getDebuggers() const {
-  Value debuggers = getReservedSlot(DEBUGGERS);
-  if (debuggers.isUndefined()) {
-    return nullptr;
-  }
-  return DebugAPI::getGlobalDebuggers(&debuggers.toObject());
-}
-
-/* static */ GlobalObject::DebuggerVector* GlobalObject::getOrCreateDebuggers(
-    JSContext* cx, Handle<GlobalObject*> global) {
-  cx->check(global);
-  DebuggerVector* debuggers = global->getDebuggers();
-  if (debuggers) {
-    return debuggers;
-  }
-
-  JSObject* obj = DebugAPI::newGlobalDebuggersHolder(cx);
-  if (!obj) {
-    return nullptr;
-  }
-
-  global->setReservedSlot(DEBUGGERS, ObjectValue(*obj));
-  return global->getDebuggers();
-}
-
 /* static */
 NativeObject* GlobalObject::getOrCreateForOfPICObject(
     JSContext* cx, Handle<GlobalObject*> global) {
   cx->check(global);
   NativeObject* forOfPIC = global->getForOfPICObject();
   if (forOfPIC) {
     return forOfPIC;
   }
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -16,17 +16,16 @@
 #include "vm/ArrayBufferObject.h"
 #include "vm/ErrorObject.h"
 #include "vm/JSFunction.h"
 #include "vm/Realm.h"
 #include "vm/Runtime.h"
 
 namespace js {
 
-class Debugger;
 class TypedObjectModuleObject;
 class LexicalEnvironmentObject;
 class RegExpStatics;
 
 enum class ReferenceType;
 
 /*
  * Global object slots are reserved as follows:
@@ -83,17 +82,16 @@ class GlobalObject : public NativeObject
     MAP_ITERATOR_PROTO,
     SET_ITERATOR_PROTO,
     MODULE_PROTO,
     IMPORT_ENTRY_PROTO,
     EXPORT_ENTRY_PROTO,
     REQUESTED_MODULE_PROTO,
     REGEXP_STATICS,
     RUNTIME_CODEGEN_ENABLED,
-    DEBUGGERS,
     INTRINSICS,
     FOR_OF_PIC_CHAIN,
     WINDOW_PROXY,
     GLOBAL_THIS_RESOLVED,
     INSTRUMENTATION,
     SOURCE_URLS,
 
     /* Total reserved-slot count for global objects. */
@@ -816,30 +814,19 @@ class GlobalObject : public NativeObject
   static bool initTypedObjectModule(JSContext* cx,
                                     Handle<GlobalObject*> global);
 
   static bool initStandardClasses(JSContext* cx, Handle<GlobalObject*> global);
   static bool initSelfHostingBuiltins(JSContext* cx,
                                       Handle<GlobalObject*> global,
                                       const JSFunctionSpec* builtins);
 
-  using DebuggerVector = Vector<WeakHeapPtr<Debugger*>, 0, ZoneAllocPolicy>;
-
-  /*
-   * The collection of Debugger objects debugging this global. If this global
-   * is not a debuggee, this returns either nullptr or an empty vector.
-   */
-  DebuggerVector* getDebuggers() const;
-
-  /*
-   * The same, but create the empty vector if one does not already
-   * exist. Returns nullptr only on OOM.
-   */
-  static DebuggerVector* getOrCreateDebuggers(JSContext* cx,
-                                              Handle<GlobalObject*> global);
+  Realm::DebuggerVector& getDebuggers() const {
+    return realm()->getDebuggers();
+  }
 
   inline NativeObject* getForOfPICObject() {
     Value forOfPIC = getReservedSlot(FOR_OF_PIC_CHAIN);
     if (forOfPIC.isUndefined()) {
       return nullptr;
     }
     return &forOfPIC.toObject().as<NativeObject>();
   }
--- a/js/src/vm/JSScript.cpp
+++ b/js/src/vm/JSScript.cpp
@@ -12,16 +12,17 @@
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/ScopeExit.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/Unused.h"
+#include "mozilla/Utf8.h"
 #include "mozilla/Vector.h"
 
 #include <algorithm>
 #include <new>
 #include <string.h>
 #include <type_traits>
 #include <utility>
 
@@ -1720,18 +1721,24 @@ static MOZ_MUST_USE bool MaybeValidateFi
   if (!filename || options.skipFilenameValidation()) {
     return true;
   }
 
   if (gFilenameValidationCallback(filename, cx->realm()->isSystem())) {
     return true;
   }
 
+  const char* utf8Filename;
+  if (mozilla::IsUtf8(mozilla::MakeStringSpan(filename))) {
+    utf8Filename = filename;
+  } else {
+    utf8Filename = "(invalid UTF-8 filename)";
+  }
   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_UNSAFE_FILENAME,
-                           filename);
+                           utf8Filename);
   return false;
 }
 
 /* static */
 bool ScriptSourceObject::initFromOptions(
     JSContext* cx, HandleScriptSourceObject source,
     const ReadOnlyCompileOptions& options) {
   cx->releaseCheck(source);
--- a/js/src/vm/Realm.cpp
+++ b/js/src/vm/Realm.cpp
@@ -8,16 +8,17 @@
 
 #include "mozilla/MemoryReporting.h"
 
 #include <stddef.h>
 
 #include "jsfriendapi.h"
 
 #include "debugger/DebugAPI.h"
+#include "debugger/Debugger.h"
 #include "gc/Policy.h"
 #include "gc/PublicIterators.h"
 #include "jit/JitOptions.h"
 #include "jit/JitRealm.h"
 #include "js/Date.h"
 #include "js/Proxy.h"
 #include "js/RootingAPI.h"
 #include "js/Wrapper.h"
@@ -48,16 +49,17 @@ Realm::Realm(Compartment* comp, const JS
     : JS::shadow::Realm(comp),
       zone_(comp->zone()),
       runtime_(comp->runtimeFromMainThread()),
       creationOptions_(options.creationOptions()),
       behaviors_(options.behaviors()),
       objects_(zone_),
       varNames_(zone_),
       randomKeyGenerator_(runtime_->forkRandomKeyGenerator()),
+      debuggers_(zone_),
       wasm(runtime_) {
   MOZ_ASSERT_IF(creationOptions_.mergeable(),
                 creationOptions_.invisibleToDebugger());
 
   runtime_->numRealms++;
 }
 
 Realm::~Realm() {
--- a/js/src/vm/Realm.h
+++ b/js/src/vm/Realm.h
@@ -385,16 +385,23 @@ class JS::Realm : public JS::shadow::Rea
   //
   // An important invariant is that the JIT can only switch to a different
   // realm within the same compartment, so whenever that happens there must
   // always be a same-compartment realm with enterRealmDepthIgnoringJit_ > 0.
   // This lets us set Compartment::hasEnteredRealm without walking the
   // stack.
   unsigned enterRealmDepthIgnoringJit_ = 0;
 
+ public:
+  using DebuggerVector =
+      js::Vector<js::WeakHeapPtr<js::Debugger*>, 0, js::ZoneAllocPolicy>;
+
+ private:
+  DebuggerVector debuggers_;
+
   enum {
     IsDebuggee = 1 << 0,
     DebuggerObservesAllExecution = 1 << 1,
     DebuggerObservesAsmJS = 1 << 2,
     DebuggerObservesCoverage = 1 << 3,
     DebuggerNeedsDelazification = 1 << 4
   };
   static const unsigned DebuggerObservesMask =
@@ -701,16 +708,18 @@ class JS::Realm : public JS::shadow::Rea
 
   // True if this realm's global is a debuggee of some Debugger
   // object.
   bool isDebuggee() const { return !!(debugModeBits_ & IsDebuggee); }
 
   void setIsDebuggee();
   void unsetIsDebuggee();
 
+  DebuggerVector& getDebuggers() { return debuggers_; };
+
   // True if this compartment's global is a debuggee of some Debugger
   // object with a live hook that observes all execution; e.g.,
   // onEnterFrame.
   bool debuggerObservesAllExecution() const {
     static const unsigned Mask = IsDebuggee | DebuggerObservesAllExecution;
     return (debugModeBits_ & Mask) == Mask;
   }
   void updateDebuggerObservesAllExecution() {
--- a/js/xpconnect/src/ExportHelpers.cpp
+++ b/js/xpconnect/src/ExportHelpers.cpp
@@ -111,16 +111,20 @@ class MOZ_STACK_CLASS StackScopedCloneDa
       MOZ_ASSERT(global);
 
       // RefPtr<File> needs to go out of scope before toObjectOrNull() is called
       // because otherwise the static analysis thinks it can gc the JSObject via
       // the stack.
       JS::Rooted<JS::Value> val(aCx);
       {
         RefPtr<Blob> blob = Blob::Create(global, mBlobImpls[idx]);
+        if (NS_WARN_IF(!blob)) {
+          return nullptr;
+        }
+
         if (!ToJSValue(aCx, blob, &val)) {
           return nullptr;
         }
       }
 
       return val.toObjectOrNull();
     }
 
--- a/layout/forms/nsFileControlFrame.cpp
+++ b/layout/forms/nsFileControlFrame.cpp
@@ -326,17 +326,17 @@ static void AppendBlobImplAsDirectory(ns
     return;
   }
 
   nsPIDOMWindowInner* inner = aContent->OwnerDoc()->GetInnerWindow();
   if (!inner || !inner->IsCurrentInnerWindow()) {
     return;
   }
 
-  RefPtr<Directory> directory = Directory::Create(inner, file);
+  RefPtr<Directory> directory = Directory::Create(inner->AsGlobal(), file);
   MOZ_ASSERT(directory);
 
   OwningFileOrDirectory* element = aArray.AppendElement();
   element->SetAsDirectory() = directory;
 }
 
 /**
  * This is called when we receive a drop or a dragover.
--- a/widget/cocoa/nsTouchBar.mm
+++ b/widget/cocoa/nsTouchBar.mm
@@ -58,17 +58,17 @@ static const uint32_t kInputIconSize = 1
     mTouchBarHelper = do_GetService(NS_TOUCHBARHELPER_CID);
     if (!mTouchBarHelper) {
       NS_ERROR("Unable to create Touch Bar Helper.");
       return nil;
     }
 
     self.delegate = self;
     self.mappedLayoutItems = [NSMutableDictionary dictionary];
-    self.customizationAllowedItemIdentifiers = nil;
+    self.customizationAllowedItemIdentifiers = @[];
 
     if (!aInputs) {
       // This customization identifier is how users' custom layouts are saved by macOS.
       // If this changes, all users' layouts would be reset to the default layout.
       self.customizationIdentifier = [BaseIdentifier stringByAppendingPathExtension:@"defaultbar"];
       nsCOMPtr<nsIArray> allItems;
 
       nsresult rv = mTouchBarHelper->GetAllItems(getter_AddRefs(allItems));
@@ -121,17 +121,16 @@ static const uint32_t kInputIconSize = 1
 
 - (void)dealloc {
   for (NSTouchBarItemIdentifier identifier in self.mappedLayoutItems) {
     NSTouchBarItem* item = [self itemForIdentifier:identifier];
     if (!item) {
       continue;
     }
     if ([item isKindOfClass:[NSPopoverTouchBarItem class]]) {
-      [(NSPopoverTouchBarItem*)item setCollapsedRepresentation:nil];
       [(NSPopoverTouchBarItem*)item setCollapsedRepresentationImage:nil];
       [(nsTouchBar*)[(NSPopoverTouchBarItem*)item popoverTouchBar] release];
     } else if ([[item view] isKindOfClass:[NSScrollView class]]) {
       [[(NSScrollView*)[item view] documentView] release];
       [(NSScrollView*)[item view] release];
     }
 
     [item release];
@@ -351,18 +350,16 @@ static const uint32_t kInputIconSize = 1
     RefPtr<nsTouchBarInputIcon> icon = [aInput icon];
     if (!icon) {
       icon = new nsTouchBarInputIcon([aInput document], nil, nil, aPopoverItem);
       [aInput setIcon:icon];
     }
     icon->SetupIcon([aInput imageURI]);
   } else if ([aInput title]) {
     aPopoverItem.collapsedRepresentationLabel = [aInput title];
-  } else {
-    aPopoverItem.collapsedRepresentation = nil;
   }
 
   // Special handling to show/hide the search popover if the Urlbar is focused.
   if ([[aInput nativeIdentifier] isEqualToString:SearchPopoverIdentifier]) {
     // We can reach this code during window shutdown. We only want to toggle
     // showPopover if we are in a normal running state.
     if (!mTouchBarHelper) {
       return;
--- a/widget/nsBaseFilePicker.cpp
+++ b/widget/nsBaseFilePicker.cpp
@@ -32,31 +32,37 @@ using namespace mozilla::dom;
 #define FILEPICKER_TITLES "chrome://global/locale/filepicker.properties"
 #define FILEPICKER_FILTERS "chrome://global/content/filepicker.properties"
 
 namespace {
 
 nsresult LocalFileToDirectoryOrBlob(nsPIDOMWindowInner* aWindow,
                                     bool aIsDirectory, nsIFile* aFile,
                                     nsISupports** aResult) {
+  MOZ_ASSERT(aWindow);
+
   if (aIsDirectory) {
 #ifdef DEBUG
     bool isDir;
     aFile->IsDirectory(&isDir);
     MOZ_ASSERT(isDir);
 #endif
 
-    RefPtr<Directory> directory = Directory::Create(aWindow, aFile);
+    RefPtr<Directory> directory = Directory::Create(aWindow->AsGlobal(), aFile);
     MOZ_ASSERT(directory);
 
     directory.forget(aResult);
     return NS_OK;
   }
 
-  RefPtr<File> file = File::CreateFromFile(aWindow, aFile);
+  RefPtr<File> file = File::CreateFromFile(aWindow->AsGlobal(), aFile);
+  if (NS_WARN_IF(!file)) {
+    return NS_ERROR_FAILURE;
+  }
+
   file.forget(aResult);
   return NS_OK;
 }
 
 }  // anonymous namespace
 
 /**
  * A runnable to dispatch from the main thread to the main thread to display
@@ -114,16 +120,20 @@ class nsBaseFilePickerEnumerator : publi
       return NS_OK;
     }
 
     nsCOMPtr<nsIFile> localFile = do_QueryInterface(tmp);
     if (!localFile) {
       return NS_ERROR_FAILURE;
     }
 
+    if (!mParent) {
+      return NS_ERROR_FAILURE;
+    }
+
     return LocalFileToDirectoryOrBlob(
         mParent, mMode == nsIFilePicker::modeGetFolder, localFile, aResult);
   }
 
   NS_IMETHOD
   HasMoreElements(bool* aResult) override {
     return mIterator->HasMoreElements(aResult);
   }
@@ -373,16 +383,20 @@ nsBaseFilePicker::GetDomFileOrDirectory(
 
   if (!localFile) {
     *aValue = nullptr;
     return NS_OK;
   }
 
   auto* innerParent = mParent ? mParent->GetCurrentInnerWindow() : nullptr;
 
+  if (!innerParent) {
+    return NS_ERROR_FAILURE;
+  }
+
   return LocalFileToDirectoryOrBlob(
       innerParent, mMode == nsIFilePicker::modeGetFolder, localFile, aValue);
 }
 
 NS_IMETHODIMP
 nsBaseFilePicker::GetDomFileOrDirectoryEnumerator(
     nsISimpleEnumerator** aValue) {
   nsCOMPtr<nsISimpleEnumerator> iter;
--- a/widget/nsFilePickerProxy.cpp
+++ b/widget/nsFilePickerProxy.cpp
@@ -141,44 +141,50 @@ nsFilePickerProxy::Open(nsIFilePickerSho
            mFilters, mFilterNames, mRawFilters, displayDirectory,
            mDisplaySpecialDirectory, mOkButtonLabel, mCapture);
 
   return NS_OK;
 }
 
 mozilla::ipc::IPCResult nsFilePickerProxy::Recv__delete__(
     const MaybeInputData& aData, const int16_t& aResult) {
+  nsPIDOMWindowInner* inner =
+      mParent ? mParent->GetCurrentInnerWindow() : nullptr;
+
+  if (NS_WARN_IF(!inner)) {
+    return IPC_OK();
+  }
+
   if (aData.type() == MaybeInputData::TInputBlobs) {
     const nsTArray<IPCBlob>& blobs = aData.get_InputBlobs().blobs();
     for (uint32_t i = 0; i < blobs.Length(); ++i) {
       RefPtr<BlobImpl> blobImpl = IPCBlobUtils::Deserialize(blobs[i]);
       NS_ENSURE_TRUE(blobImpl, IPC_OK());
 
       if (!blobImpl->IsFile()) {
         return IPC_OK();
       }
 
-      nsPIDOMWindowInner* inner =
-          mParent ? mParent->GetCurrentInnerWindow() : nullptr;
-      RefPtr<File> file = File::Create(inner, blobImpl);
-      MOZ_ASSERT(file);
+      RefPtr<File> file = File::Create(inner->AsGlobal(), blobImpl);
+      if (NS_WARN_IF(!file)) {
+        return IPC_OK();
+      }
 
       OwningFileOrDirectory* element = mFilesOrDirectories.AppendElement();
       element->SetAsFile() = file;
     }
   } else if (aData.type() == MaybeInputData::TInputDirectory) {
     nsCOMPtr<nsIFile> file;
     const nsAString& path(aData.get_InputDirectory().directoryPath());
     nsresult rv = NS_NewLocalFile(path, true, getter_AddRefs(file));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return IPC_OK();
     }
 
-    RefPtr<Directory> directory =
-        Directory::Create(mParent->GetCurrentInnerWindow(), file);
+    RefPtr<Directory> directory = Directory::Create(inner->AsGlobal(), file);
     MOZ_ASSERT(directory);
 
     OwningFileOrDirectory* element = mFilesOrDirectories.AppendElement();
     element->SetAsDirectory() = directory;
   }
 
   if (mCallback) {
     mCallback->Done(aResult);
copy from xpcom/threads/PrioritizedEventQueue.cpp
copy to xpcom/threads/IdlePeriodState.cpp
--- a/xpcom/threads/PrioritizedEventQueue.cpp
+++ b/xpcom/threads/IdlePeriodState.cpp
@@ -1,86 +1,111 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "PrioritizedEventQueue.h"
-#include "mozilla/EventQueue.h"
-#include "mozilla/ScopeExit.h"
+#include "mozilla/IdlePeriodState.h"
 #include "mozilla/StaticPrefs_idle_period.h"
-#include "mozilla/StaticPrefs_threads.h"
 #include "mozilla/ipc/IdleSchedulerChild.h"
+#include "nsIIdlePeriod.h"
 #include "nsThreadManager.h"
-#include "nsXPCOMPrivate.h"  // for gXPCOMThreadsShutDown
-#include "InputEventStatistics.h"
-
-using namespace mozilla;
+#include "nsThreadUtils.h"
+#include "nsXPCOM.h"
+#include "nsXULAppAPI.h"
 
 static uint64_t sIdleRequestCounter = 0;
 
-PrioritizedEventQueue::PrioritizedEventQueue(
-    already_AddRefed<nsIIdlePeriod> aIdlePeriod)
-    : mHighQueue(MakeUnique<EventQueue>(EventQueuePriority::High)),
-      mInputQueue(MakeUnique<EventQueue>(EventQueuePriority::Input)),
-      mMediumHighQueue(MakeUnique<EventQueue>(EventQueuePriority::MediumHigh)),
-      mNormalQueue(MakeUnique<EventQueue>(EventQueuePriority::Normal)),
-      mDeferredTimersQueue(
-          MakeUnique<EventQueue>(EventQueuePriority::DeferredTimers)),
-      mIdleQueue(MakeUnique<EventQueue>(EventQueuePriority::Idle)),
-      mIdlePeriod(aIdlePeriod) {}
+namespace mozilla {
 
-PrioritizedEventQueue::~PrioritizedEventQueue() {
+IdlePeriodState::IdlePeriodState(already_AddRefed<nsIIdlePeriod>&& aIdlePeriod)
+    : mIdlePeriod(aIdlePeriod) {
+  MOZ_ASSERT(NS_IsMainThread(),
+             "Why are we touching idle state off the main thread?");
+}
+
+IdlePeriodState::~IdlePeriodState() {
+  MOZ_ASSERT(NS_IsMainThread(),
+             "Why are we touching idle state off the main thread?");
   if (mIdleScheduler) {
     mIdleScheduler->Disconnect();
   }
 }
 
-void PrioritizedEventQueue::PutEvent(already_AddRefed<nsIRunnable>&& aEvent,
-                                     EventQueuePriority aPriority,
-                                     const MutexAutoLock& aProofOfLock) {
-  // Double check the priority with a QI.
-  RefPtr<nsIRunnable> event(aEvent);
-  EventQueuePriority priority = aPriority;
-
-  if (priority == EventQueuePriority::Input &&
-      mInputQueueState == STATE_DISABLED) {
-    priority = EventQueuePriority::Normal;
-  } else if (priority == EventQueuePriority::MediumHigh &&
-             !StaticPrefs::threads_medium_high_event_queue_enabled()) {
-    priority = EventQueuePriority::Normal;
+size_t IdlePeriodState::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
+  size_t n = 0;
+  if (mIdlePeriod) {
+    n += aMallocSizeOf(mIdlePeriod);
   }
 
-  switch (priority) {
-    case EventQueuePriority::High:
-      mHighQueue->PutEvent(event.forget(), priority, aProofOfLock);
-      break;
-    case EventQueuePriority::Input:
-      mInputQueue->PutEvent(event.forget(), priority, aProofOfLock);
-      break;
-    case EventQueuePriority::MediumHigh:
-      mMediumHighQueue->PutEvent(event.forget(), priority, aProofOfLock);
-      break;
-    case EventQueuePriority::Normal:
-      mNormalQueue->PutEvent(event.forget(), priority, aProofOfLock);
-      break;
-    case EventQueuePriority::DeferredTimers:
-      mDeferredTimersQueue->PutEvent(event.forget(), priority, aProofOfLock);
-      break;
-    case EventQueuePriority::Idle:
-      mIdleQueue->PutEvent(event.forget(), priority, aProofOfLock);
-      break;
-    case EventQueuePriority::Count:
-      MOZ_CRASH("EventQueuePriority::Count isn't a valid priority");
-      break;
+  return n;
+}
+
+void IdlePeriodState::FlagNotIdle(Mutex& aMutexToUnlock) {
+  MOZ_ASSERT(NS_IsMainThread(),
+             "Why are we touching idle state off the main thread?");
+
+  EnsureIsActive();
+  if (mIdleToken && mIdleToken < TimeStamp::Now()) {
+    ClearIdleToken(aMutexToUnlock);
   }
 }
 
-TimeStamp PrioritizedEventQueue::GetLocalIdleDeadline(bool& aShuttingDown) {
+void IdlePeriodState::RanOutOfTasks(Mutex& aMutexToUnlock) {
+  MOZ_ASSERT(NS_IsMainThread(),
+             "Why are we touching idle state off the main thread?");
+  MOZ_ASSERT(!mHasPendingEventsPromisedIdleEvent);
+  EnsureIsPaused(aMutexToUnlock);
+  ClearIdleToken(aMutexToUnlock);
+}
+
+TimeStamp IdlePeriodState::GetIdleDeadlineInternal(bool aIsPeek,
+                                                   Mutex& aMutexToUnlock) {
+  MOZ_ASSERT(NS_IsMainThread(),
+             "Why are we touching idle state off the main thread?");
+
+  bool shuttingDown;
+  TimeStamp localIdleDeadline =
+      GetLocalIdleDeadline(shuttingDown, aMutexToUnlock);
+  if (!localIdleDeadline) {
+    if (!aIsPeek) {
+      EnsureIsPaused(aMutexToUnlock);
+      ClearIdleToken(aMutexToUnlock);
+    }
+    return TimeStamp();
+  }
+
+  TimeStamp idleDeadline =
+      mHasPendingEventsPromisedIdleEvent || shuttingDown
+          ? localIdleDeadline
+          : GetIdleToken(localIdleDeadline, aMutexToUnlock);
+  if (!idleDeadline) {
+    if (!aIsPeek) {
+      EnsureIsPaused(aMutexToUnlock);
+
+      // Don't call ClearIdleToken() here, since we may have a pending
+      // request already.
+      // RequestIdleToken can do all sorts of IPC stuff that might take mutexes.
+      MutexAutoUnlock unlock(aMutexToUnlock);
+      RequestIdleToken(localIdleDeadline);
+    }
+    return TimeStamp();
+  }
+
+  if (!aIsPeek) {
+    EnsureIsActive();
+  }
+  return idleDeadline;
+}
+
+TimeStamp IdlePeriodState::GetLocalIdleDeadline(bool& aShuttingDown,
+                                                Mutex& aMutexToUnlock) {
+  MOZ_ASSERT(NS_IsMainThread(),
+             "Why are we touching idle state off the main thread?");
   // If we are shutting down, we won't honor the idle period, and we will
   // always process idle runnables.  This will ensure that the idle queue
   // gets exhausted at shutdown time to prevent intermittently leaking
   // some runnables inside that queue and even worse potentially leaving
   // some important cleanup work unfinished.
   if (gXPCOMThreadsShutDown ||
       nsThreadManager::get().GetCurrentThread()->ShuttingDown()) {
     aShuttingDown = true;
@@ -89,17 +114,17 @@ TimeStamp PrioritizedEventQueue::GetLoca
 
   aShuttingDown = false;
   TimeStamp idleDeadline;
   {
     // Releasing the lock temporarily since getting the idle period
     // might need to lock the timer thread. Unlocking here might make
     // us receive an event on the main queue, but we've committed to
     // run an idle event anyhow.
-    MutexAutoUnlock unlock(*mMutex);
+    MutexAutoUnlock unlock(aMutexToUnlock);
     mIdlePeriod->GetIdlePeriodHint(&idleDeadline);
   }
 
   // If HasPendingEvents() has been called and it has returned true because of
   // pending idle events, there is a risk that we may decide here that we aren't
   // idle and return null, in which case HasPendingEvents() has effectively
   // lied.  Since we can't go back and fix the past, we have to adjust what we
   // do here and forcefully pick the idle queue task here.  Note that this means
@@ -117,320 +142,45 @@ TimeStamp PrioritizedEventQueue::GetLoca
     // If HasPendingEvents() has been called and it has returned true, but we're
     // no longer in the idle period, we must return a valid timestamp to pretend
     // that we are still in the idle period.
     return TimeStamp::Now();
   }
   return idleDeadline;
 }
 
-EventQueuePriority PrioritizedEventQueue::SelectQueue(
-    bool aUpdateState, const MutexAutoLock& aProofOfLock) {
-  size_t inputCount = mInputQueue->Count(aProofOfLock);
-
-  if (aUpdateState && mInputQueueState == STATE_ENABLED &&
-      mInputHandlingStartTime.IsNull() && inputCount > 0) {
-    mInputHandlingStartTime =
-        InputEventStatistics::Get().GetInputHandlingStartTime(inputCount);
-  }
-
-  // We check the different queues in the following order. The conditions we use
-  // are meant to avoid starvation and to ensure that we don't process an event
-  // at the wrong time.
-  //
-  // HIGH: if mProcessHighPriorityQueue
-  // INPUT: if inputCount > 0 && TimeStamp::Now() > mInputHandlingStartTime
-  // MEDIUMHIGH: if medium high pending
-  // NORMAL: if normal pending
-  //
-  // If we still don't have an event, then we take events from the queues
-  // in the following order:
-  //
-  // HIGH
-  // INPUT
-  // DEFERREDTIMERS: if GetLocalIdleDeadline()
-  // IDLE: if GetLocalIdleDeadline()
-  //
-  // If we don't get an event in this pass, then we return null since no events
-  // are ready.
-
-  // This variable determines which queue we will take an event from.
-  EventQueuePriority queue;
-  bool highPending = !mHighQueue->IsEmpty(aProofOfLock);
-
-  if (mProcessHighPriorityQueue) {
-    queue = EventQueuePriority::High;
-  } else if (inputCount > 0 && (mInputQueueState == STATE_FLUSHING ||
-                                (mInputQueueState == STATE_ENABLED &&
-                                 !mInputHandlingStartTime.IsNull() &&
-                                 TimeStamp::Now() > mInputHandlingStartTime))) {
-    queue = EventQueuePriority::Input;
-  } else if (!mMediumHighQueue->IsEmpty(aProofOfLock)) {
-    MOZ_ASSERT(
-        mInputQueueState != STATE_FLUSHING,
-        "Shouldn't consume medium high event when flushing input events");
-    queue = EventQueuePriority::MediumHigh;
-  } else if (!mNormalQueue->IsEmpty(aProofOfLock)) {
-    MOZ_ASSERT(mInputQueueState != STATE_FLUSHING,
-               "Shouldn't consume normal event when flushing input events");
-    queue = EventQueuePriority::Normal;
-  } else if (highPending) {
-    queue = EventQueuePriority::High;
-  } else if (inputCount > 0 && mInputQueueState != STATE_SUSPEND) {
-    MOZ_ASSERT(
-        mInputQueueState != STATE_DISABLED,
-        "Shouldn't consume input events when the input queue is disabled");
-    queue = EventQueuePriority::Input;
-  } else if (!mDeferredTimersQueue->IsEmpty(aProofOfLock)) {
-    // We may not actually return an idle event in this case.
-    queue = EventQueuePriority::DeferredTimers;
-  } else {
-    // We may not actually return an idle event in this case.
-    queue = EventQueuePriority::Idle;
-  }
-
-  MOZ_ASSERT_IF(
-      queue == EventQueuePriority::Input,
-      mInputQueueState != STATE_DISABLED && mInputQueueState != STATE_SUSPEND);
-
-  if (aUpdateState) {
-    mProcessHighPriorityQueue = highPending;
-  }
-
-  return queue;
-}
-
-already_AddRefed<nsIRunnable> PrioritizedEventQueue::GetEvent(
-    EventQueuePriority* aPriority, const MutexAutoLock& aProofOfLock) {
-#ifndef RELEASE_OR_BETA
-  // Clear mNextIdleDeadline so that it is possible to determine that
-  // we're running an idle runnable in ProcessNextEvent.
-  *mNextIdleDeadline = TimeStamp();
-#endif
-
-  EventQueuePriority queue = SelectQueue(true, aProofOfLock);
-  auto guard = MakeScopeExit([&] {
-    mHasPendingEventsPromisedIdleEvent = false;
-    if (queue != EventQueuePriority::Idle &&
-        queue != EventQueuePriority::DeferredTimers) {
-      EnsureIsActive();
-      if (mIdleToken && mIdleToken < TimeStamp::Now()) {
-        ClearIdleToken();
-      }
-    }
-  });
-
-  if (aPriority) {
-    *aPriority = queue;
-  }
-
-  if (queue == EventQueuePriority::High) {
-    nsCOMPtr<nsIRunnable> event = mHighQueue->GetEvent(aPriority, aProofOfLock);
-    MOZ_ASSERT(event);
-    mInputHandlingStartTime = TimeStamp();
-    mProcessHighPriorityQueue = false;
-    return event.forget();
-  }
-
-  if (queue == EventQueuePriority::Input) {
-    nsCOMPtr<nsIRunnable> event =
-        mInputQueue->GetEvent(aPriority, aProofOfLock);
-    MOZ_ASSERT(event);
-    return event.forget();
-  }
-
-  if (queue == EventQueuePriority::MediumHigh) {
-    nsCOMPtr<nsIRunnable> event =
-        mMediumHighQueue->GetEvent(aPriority, aProofOfLock);
-    return event.forget();
-  }
-
-  if (queue == EventQueuePriority::Normal) {
-    nsCOMPtr<nsIRunnable> event =
-        mNormalQueue->GetEvent(aPriority, aProofOfLock);
-    return event.forget();
-  }
-
-  // If we get here, then all queues except deferredtimers and idle are empty.
-  MOZ_ASSERT(queue == EventQueuePriority::Idle ||
-             queue == EventQueuePriority::DeferredTimers);
-
-  if (mIdleQueue->IsEmpty(aProofOfLock) &&
-      mDeferredTimersQueue->IsEmpty(aProofOfLock)) {
-    MOZ_ASSERT(!mHasPendingEventsPromisedIdleEvent);
-    EnsureIsPaused();
-    ClearIdleToken();
-    return nullptr;
-  }
+TimeStamp IdlePeriodState::GetIdleToken(TimeStamp aLocalIdlePeriodHint,
+                                        Mutex& aMutexToUnlock) {
+  MOZ_ASSERT(NS_IsMainThread(),
+             "Why are we touching idle state off the main thread?");
 
-  bool shuttingDown;
-  TimeStamp localIdleDeadline = GetLocalIdleDeadline(shuttingDown);
-  if (!localIdleDeadline) {
-    EnsureIsPaused();
-    ClearIdleToken();
-    return nullptr;
-  }
-
-  TimeStamp idleDeadline = mHasPendingEventsPromisedIdleEvent || shuttingDown
-                               ? localIdleDeadline
-                               : GetIdleToken(localIdleDeadline);
-  if (!idleDeadline) {
-    EnsureIsPaused();
-
-    // Don't call ClearIdleToken() here, since we may have a pending
-    // request already.
-    MutexAutoUnlock unlock(*mMutex);
-    RequestIdleToken(localIdleDeadline);
-    return nullptr;
-  }
-
-  nsCOMPtr<nsIRunnable> event =
-      mDeferredTimersQueue->GetEvent(aPriority, aProofOfLock);
-  if (!event) {
-    event = mIdleQueue->GetEvent(aPriority, aProofOfLock);
-  }
-  if (event) {
-    nsCOMPtr<nsIIdleRunnable> idleEvent = do_QueryInterface(event);
-    if (idleEvent) {
-      idleEvent->SetDeadline(idleDeadline);
-    }
-
-#ifndef RELEASE_OR_BETA
-    // Store the next idle deadline to be able to determine budget use
-    // in ProcessNextEvent.
-    *mNextIdleDeadline = idleDeadline;
-#endif
-  }
-
-  EnsureIsActive();
-  return event.forget();
-}
-
-void PrioritizedEventQueue::DidRunEvent(const MutexAutoLock& aProofOfLock) {
-  if (IsEmpty(aProofOfLock)) {
-    if (IsActive()) {
-      SetPaused();
-    }
-    ClearIdleToken();
-  }
-}
-
-bool PrioritizedEventQueue::IsEmpty(const MutexAutoLock& aProofOfLock) {
-  // Just check IsEmpty() on the sub-queues. Don't bother checking the idle
-  // deadline since that only determines whether an idle event is ready or not.
-  return mHighQueue->IsEmpty(aProofOfLock) &&
-         mInputQueue->IsEmpty(aProofOfLock) &&
-         mMediumHighQueue->IsEmpty(aProofOfLock) &&
-         mNormalQueue->IsEmpty(aProofOfLock) &&
-         mDeferredTimersQueue->IsEmpty(aProofOfLock) &&
-         mIdleQueue->IsEmpty(aProofOfLock);
-}
-
-bool PrioritizedEventQueue::HasReadyEvent(const MutexAutoLock& aProofOfLock) {
-  mHasPendingEventsPromisedIdleEvent = false;
-
-  EventQueuePriority queue = SelectQueue(false, aProofOfLock);
-
-  if (queue == EventQueuePriority::High) {
-    return mHighQueue->HasReadyEvent(aProofOfLock);
-  } else if (queue == EventQueuePriority::Input) {
-    return mInputQueue->HasReadyEvent(aProofOfLock);
-  } else if (queue == EventQueuePriority::MediumHigh) {
-    return mMediumHighQueue->HasReadyEvent(aProofOfLock);
-  } else if (queue == EventQueuePriority::Normal) {
-    return mNormalQueue->HasReadyEvent(aProofOfLock);
-  }
-
-  MOZ_ASSERT(queue == EventQueuePriority::Idle ||
-             queue == EventQueuePriority::DeferredTimers);
-
-  // If we get here, then both the high and normal queues are empty.
-
-  if (mDeferredTimersQueue->IsEmpty(aProofOfLock) &&
-      mIdleQueue->IsEmpty(aProofOfLock)) {
-    return false;
-  }
-
-  bool shuttingDown;
-  TimeStamp localIdleDeadline = GetLocalIdleDeadline(shuttingDown);
-  if (localIdleDeadline) {
-    TimeStamp idleDeadline = mHasPendingEventsPromisedIdleEvent || shuttingDown
-                                 ? localIdleDeadline
-                                 : GetIdleToken(localIdleDeadline);
-    if (idleDeadline && (mDeferredTimersQueue->HasReadyEvent(aProofOfLock) ||
-                         mIdleQueue->HasReadyEvent(aProofOfLock))) {
-      mHasPendingEventsPromisedIdleEvent = true;
-      return true;
-    }
-  }
-
-  return false;
-}
-
-bool PrioritizedEventQueue::HasPendingHighPriorityEvents(
-    const MutexAutoLock& aProofOfLock) {
-  return !mHighQueue->IsEmpty(aProofOfLock);
-}
-
-size_t PrioritizedEventQueue::Count(const MutexAutoLock& aProofOfLock) const {
-  MOZ_CRASH("unimplemented");
-}
-
-void PrioritizedEventQueue::EnableInputEventPrioritization(
-    const MutexAutoLock& aProofOfLock) {
-  MOZ_ASSERT(mInputQueueState == STATE_DISABLED);
-  mInputQueueState = STATE_ENABLED;
-  mInputHandlingStartTime = TimeStamp();
-}
-
-void PrioritizedEventQueue::FlushInputEventPrioritization(
-    const MutexAutoLock& aProofOfLock) {
-  MOZ_ASSERT(mInputQueueState == STATE_ENABLED ||
-             mInputQueueState == STATE_SUSPEND);
-  mInputQueueState =
-      mInputQueueState == STATE_ENABLED ? STATE_FLUSHING : STATE_SUSPEND;
-}
-
-void PrioritizedEventQueue::SuspendInputEventPrioritization(
-    const MutexAutoLock& aProofOfLock) {
-  MOZ_ASSERT(mInputQueueState == STATE_ENABLED ||
-             mInputQueueState == STATE_FLUSHING);
-  mInputQueueState = STATE_SUSPEND;
-}
-
-void PrioritizedEventQueue::ResumeInputEventPrioritization(
-    const MutexAutoLock& aProofOfLock) {
-  MOZ_ASSERT(mInputQueueState == STATE_SUSPEND);
-  mInputQueueState = STATE_ENABLED;
-}
-
-mozilla::TimeStamp PrioritizedEventQueue::GetIdleToken(
-    TimeStamp aLocalIdlePeriodHint) {
   if (XRE_IsParentProcess()) {
     return aLocalIdlePeriodHint;
   }
   if (mIdleToken) {
     TimeStamp now = TimeStamp::Now();
     if (mIdleToken < now) {
-      ClearIdleToken();
+      ClearIdleToken(aMutexToUnlock);
       return mIdleToken;
     }
     return mIdleToken < aLocalIdlePeriodHint ? mIdleToken
                                              : aLocalIdlePeriodHint;
   }
   return TimeStamp();
 }
 
-void PrioritizedEventQueue::RequestIdleToken(TimeStamp aLocalIdlePeriodHint) {
+void IdlePeriodState::RequestIdleToken(TimeStamp aLocalIdlePeriodHint) {
+  MOZ_ASSERT(NS_IsMainThread(),
+             "Why are we touching idle state off the main thread?");
   MOZ_ASSERT(!mActive);
 
   if (!mIdleSchedulerInitialized) {
     mIdleSchedulerInitialized = true;
     if (StaticPrefs::idle_period_cross_process_scheduling() &&
-        XRE_IsContentProcess() && NS_IsMainThread() &&
+        XRE_IsContentProcess() &&
         // Disable when recording/replaying, as IdleSchedulerChild uses mutable
         // shared memory which needs special handling.
         !recordreplay::IsRecordingOrReplaying()) {
       // For now cross-process idle scheduler is supported only on the main
       // threads of the child processes.
       mIdleScheduler = ipc::IdleSchedulerChild::GetMainThreadIdleScheduler();
       if (mIdleScheduler) {
         mIdleScheduler->Init(this);
@@ -445,44 +195,60 @@ void PrioritizedEventQueue::RequestIdleT
     }
 
     mIdleRequestId = ++sIdleRequestCounter;
     mIdleScheduler->SendRequestIdleTime(mIdleRequestId,
                                         aLocalIdlePeriodHint - now);
   }
 }
 
-void PrioritizedEventQueue::SetIdleToken(uint64_t aId, TimeDuration aDuration) {
+void IdlePeriodState::SetIdleToken(uint64_t aId, TimeDuration aDuration) {
+  MOZ_ASSERT(NS_IsMainThread(),
+             "Why are we touching idle state off the main thread?");
   if (mIdleRequestId == aId) {
     mIdleToken = TimeStamp::Now() + aDuration;
   }
 }
 
-void PrioritizedEventQueue::SetActive() {
+void IdlePeriodState::SetActive() {
+  MOZ_ASSERT(NS_IsMainThread(),
+             "Why are we touching idle state off the main thread?");
   MOZ_ASSERT(!mActive);
   if (mIdleScheduler) {
     mIdleScheduler->SetActive();
   }
   mActive = true;
 }
 
-void PrioritizedEventQueue::SetPaused() {
+void IdlePeriodState::SetPaused(Mutex& aMutexToUnlock) {
+  MOZ_ASSERT(NS_IsMainThread(),
+             "Why are we touching idle state off the main thread?");
   MOZ_ASSERT(mActive);
   if (mIdleScheduler && mIdleScheduler->SetPaused()) {
-    MutexAutoUnlock unlock(*mMutex);
+    MutexAutoUnlock unlock(aMutexToUnlock);
     // We may have gotten a free cpu core for running idle tasks.
     // We don't try to catch the case when there are prioritized processes
     // running.
+
+    // This SendSchedule call is why we need to MutexAutoUnlock here, because
+    // IPC can do weird things with mutexes.
     mIdleScheduler->SendSchedule();
   }
   mActive = false;
 }
 
-void PrioritizedEventQueue::ClearIdleToken() {
+void IdlePeriodState::ClearIdleToken(Mutex& aMutexToUnlock) {
+  MOZ_ASSERT(NS_IsMainThread(),
+             "Why are we touching idle state off the main thread?");
+
   if (mIdleRequestId) {
     if (mIdleScheduler) {
-      MutexAutoUnlock unlock(*mMutex);
+      // This SendIdleTimeUsed call is why we need to MutexAutoUnlock here,
+      // because IPC can do weird things with mutexes.
+      MutexAutoUnlock unlock(aMutexToUnlock);
       mIdleScheduler->SendIdleTimeUsed(mIdleRequestId);
     }
     mIdleRequestId = 0;
     mIdleToken = TimeStamp();
   }
 }
+
+}  // namespace mozilla
copy from xpcom/threads/PrioritizedEventQueue.h
copy to xpcom/threads/IdlePeriodState.h
--- a/xpcom/threads/PrioritizedEventQueue.h
+++ b/xpcom/threads/IdlePeriodState.h
@@ -1,207 +1,183 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef mozilla_PrioritizedEventQueue_h
-#define mozilla_PrioritizedEventQueue_h
+#ifndef mozilla_IdlePeriodState_h
+#define mozilla_IdlePeriodState_h
 
-#include "mozilla/AbstractEventQueue.h"
-#include "mozilla/EventQueue.h"
+/**
+ * A class for tracking the state of our idle period.  This includes keeping
+ * track of both the state of our process-local idle period estimate and, for
+ * content processes, managing communication with the parent process for
+ * cross-pprocess idle detection.
+ */
+
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/RefPtr.h"
 #include "mozilla/TimeStamp.h"
-#include "mozilla/TypeTraits.h"
-#include "mozilla/UniquePtr.h"
 #include "nsCOMPtr.h"
-#include "nsIIdlePeriod.h"
 
-class nsIRunnable;
+#include <stdint.h>
+
+class nsIIdlePeriod;
 
 namespace mozilla {
 namespace ipc {
 class IdleSchedulerChild;
-}
+}  // namespace ipc
+
+class IdlePeriodState {
+ public:
+  explicit IdlePeriodState(already_AddRefed<nsIIdlePeriod>&& aIdlePeriod);
+
+  ~IdlePeriodState();
+
+  // Integration with memory reporting.
+  size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const;
 
-// This AbstractEventQueue implementation has one queue for each
-// EventQueuePriority. The type of queue used for each priority is determined by
-// the template parameter.
-//
-// When an event is pushed, its priority is determined by QIing the runnable to
-// nsIRunnablePriority, or by falling back to the aPriority parameter if the QI
-// fails.
-//
-// When an event is popped, a queue is selected based on heuristics that
-// optimize for performance. Roughly, events are selected from the highest
-// priority queue that is non-empty. However, there are a few exceptions:
-// - We try to avoid processing too many high-priority events in a row so
-//   that the normal priority queue is not starved. When there are high-
-//   and normal-priority events available, we interleave popping from the
-//   normal and high queues.
-// - We do not select events from the idle queue if the current idle period
-//   is almost over.
-class PrioritizedEventQueue final : public AbstractEventQueue {
- public:
-  static const bool SupportsPrioritization = true;
+  // Notification that whoever we are tracking idle state for has found a
+  // non-idle task to process.
+  //
+  // aMutexToUnlock is the mutex to unlock if we do anything that might lock
+  // other mutexes.
+  void FlagNotIdle(Mutex& aMutexToUnlock);
 
-  explicit PrioritizedEventQueue(already_AddRefed<nsIIdlePeriod> aIdlePeriod);
-
-  virtual ~PrioritizedEventQueue();
-
-  void PutEvent(already_AddRefed<nsIRunnable>&& aEvent,
-                EventQueuePriority aPriority,
-                const MutexAutoLock& aProofOfLock) final;
-  already_AddRefed<nsIRunnable> GetEvent(
-      EventQueuePriority* aPriority, const MutexAutoLock& aProofOfLock) final;
-  void DidRunEvent(const MutexAutoLock& aProofOfLock);
+  // Notification that whoever we are tracking idle state for has no more
+  // tasks (idle or not) to process.
+  //
+  // aMutexToUnlock is the mutex to unlock if we do anything that might lock
+  // other mutexes.
+  void RanOutOfTasks(Mutex& aMutexToUnlock);
 
-  bool IsEmpty(const MutexAutoLock& aProofOfLock) final;
-  size_t Count(const MutexAutoLock& aProofOfLock) const final;
-  bool HasReadyEvent(const MutexAutoLock& aProofOfLock) final;
-  bool HasPendingHighPriorityEvents(const MutexAutoLock& aProofOfLock) final;
+  // Notification that whoever we are tracking idle state has idle tasks that
+  // they are considering ready to run and that we should keep claiming they are
+  // ready to run until they call ForgetPendingTaskGuarantee().
+  void EnforcePendingTaskGuarantee() {
+    mHasPendingEventsPromisedIdleEvent = true;
+  }
 
-  // When checking the idle deadline, we need to drop whatever mutex protects
-  // this queue. This method allows that mutex to be stored so that we can drop
-  // it and reacquire it when checking the idle deadline. The mutex must live at
-  // least as long as the queue.
-  void SetMutexRef(Mutex& aMutex) { mMutex = &aMutex; }
-
-#ifndef RELEASE_OR_BETA
-  // nsThread.cpp sends telemetry containing the most recently computed idle
-  // deadline. We store a reference to a field in nsThread where this deadline
-  // will be stored so that it can be fetched quickly for telemetry.
-  void SetNextIdleDeadlineRef(TimeStamp& aDeadline) {
-    mNextIdleDeadline = &aDeadline;
+  // Notification that whoever we are tracking idle state for is done with our
+  // "we have an idle event ready to run" guarantee.  When this happens, we can
+  // reset mHasPendingEventsPromisedIdleEvent to false, because we have
+  // fulfilled our contract.
+  void ForgetPendingTaskGuarantee() {
+    mHasPendingEventsPromisedIdleEvent = false;
   }
-#endif
 
-  void EnableInputEventPrioritization(const MutexAutoLock& aProofOfLock) final;
-  void FlushInputEventPrioritization(const MutexAutoLock& aProofOfLock) final;
-  void SuspendInputEventPrioritization(const MutexAutoLock& aProofOfLock) final;
-  void ResumeInputEventPrioritization(const MutexAutoLock& aProofOfLock) final;
-
-  size_t SizeOfExcludingThis(
-      mozilla::MallocSizeOf aMallocSizeOf) const override {
-    size_t n = 0;
+  // Get our current idle deadline so we can run an idle task with that
+  // deadline.  This can return a null timestamp (which means we are not idle
+  // right now), and it can also queue up queries to our parent process, if
+  // we're a content process, to find out whether we're idle.  This should only
+  // be called when there is an actual idle task that might run.
+  //
+  // aMutexToUnlock is the mutex to unlock if we do anything that might lock
+  // other mutexes.
+  TimeStamp GetDeadlineForIdleTask(Mutex& aMutexToUnlock) {
+    return GetIdleDeadlineInternal(false, aMutexToUnlock);
+  }
 
-    n += mHighQueue->SizeOfIncludingThis(aMallocSizeOf);
-    n += mInputQueue->SizeOfIncludingThis(aMallocSizeOf);
-    n += mMediumHighQueue->SizeOfIncludingThis(aMallocSizeOf);
-    n += mNormalQueue->SizeOfIncludingThis(aMallocSizeOf);
-    n += mDeferredTimersQueue->SizeOfIncludingThis(aMallocSizeOf);
-    n += mIdleQueue->SizeOfIncludingThis(aMallocSizeOf);
-
-    if (mIdlePeriod) {
-      n += aMallocSizeOf(mIdlePeriod);
-    }
-
-    return n;
+  // Peek our current idle deadline.  This can return a null timestamp (which
+  // means we are not idle right now).  This method does not have any
+  // side-effects on our state, apart from guaranteeing that if it returns
+  // non-null then GetDeadlineForIdleTask will return non-null until
+  // ForgetPendingTaskGuarantee() is called.
+  //
+  // aMutexToUnlock is the mutex to unlock if we do anything that might lock
+  // other mutexes.
+  TimeStamp PeekIdleDeadline(Mutex& aMutexToUnlock) {
+    return GetIdleDeadlineInternal(true, aMutexToUnlock);
   }
 
   void SetIdleToken(uint64_t aId, TimeDuration aDuration);
 
   bool IsActive() { return mActive; }
 
+ protected:
   void EnsureIsActive() {
     if (!mActive) {
       SetActive();
     }
   }
 
-  void EnsureIsPaused() {
+  void EnsureIsPaused(Mutex& aMutexToUnlock) {
     if (mActive) {
-      SetPaused();
+      SetPaused(aMutexToUnlock);
     }
   }
 
- private:
-  EventQueuePriority SelectQueue(bool aUpdateState,
-                                 const MutexAutoLock& aProofOfLock);
+  // Returns a null TimeStamp if we're not in the idle period.
+  TimeStamp GetLocalIdleDeadline(bool& aShuttingDown, Mutex& aMutexToUnlock);
 
-  // Returns a null TimeStamp if we're not in the idle period.
-  mozilla::TimeStamp GetLocalIdleDeadline(bool& aShuttingDown);
+  // Gets the idle token, which is the end time of the idle period.
+  //
+  // aMutexToUnlock is the mutex to unlock if we do anything that might lock
+  // other mutexes.
+  TimeStamp GetIdleToken(TimeStamp aLocalIdlePeriodHint, Mutex& aMutexToUnlock);
+
+  // In case of child processes, requests idle time from the cross-process
+  // idle scheduler.
+  void RequestIdleToken(TimeStamp aLocalIdlePeriodHint);
+
+  // Mark that we don't have idle time to use, nor are expecting to get an idle
+  // token from the idle scheduler.
+  void ClearIdleToken(Mutex& aMutexToUnlock);
 
   // SetActive should be called when the event queue is running any type of
   // tasks.
   void SetActive();
   // SetPaused should be called once the event queue doesn't have more
   // tasks to process, or is waiting for the idle token.
-  void SetPaused();
-
-  // Gets the idle token, which is the end time of the idle period.
-  TimeStamp GetIdleToken(TimeStamp aLocalIdlePeriodHint);
-
-  // In case of child processes, requests idle time from the cross-process
-  // idle scheduler.
-  void RequestIdleToken(TimeStamp aLocalIdlePeriodHint);
-
-  // Returns true if the event queue either is waiting for an idle token
-  // from the idle scheduler or has one.
-  bool HasIdleRequest() { return mIdleRequestId != 0; }
-
-  // Mark that the event queue doesn't have idle time to use, nor is expecting
-  // to get idle token from the idle scheduler.
-  void ClearIdleToken();
+  //
+  // aMutexToUnlock is the mutex to unlock if we do anything that might lock
+  // other mutexes.
+  void SetPaused(Mutex& aMutexToUnlock);
 
-  UniquePtr<EventQueue> mHighQueue;
-  UniquePtr<EventQueue> mInputQueue;
-  UniquePtr<EventQueue> mMediumHighQueue;
-  UniquePtr<EventQueue> mNormalQueue;
-  UniquePtr<EventQueue> mDeferredTimersQueue;
-  UniquePtr<EventQueue> mIdleQueue;
-
-  // We need to drop the queue mutex when checking the idle deadline, so we keep
-  // a pointer to it here.
-  Mutex* mMutex = nullptr;
+  // Get or peek our idle deadline.  When peeking, we generally don't change any
+  // of our internal state.  When getting, we may request an idle token as
+  // needed.
+  //
+  // aMutexToUnlock is the mutex to unlock if we do anything that might lock
+  // other mutexes.
+  TimeStamp GetIdleDeadlineInternal(bool aIsPeek, Mutex& aMutexToUnlock);
 
-#ifndef RELEASE_OR_BETA
-  // Pointer to a place where the most recently computed idle deadline is
-  // stored.
-  TimeStamp* mNextIdleDeadline = nullptr;
-#endif
+  // Set to true if we have claimed we have a ready-to-run idle task when asked.
+  // In that case, we will ensure that we allow at least one task to run when
+  // someone tries to run a task, even if we have run out of idle period at that
+  // point.  This ensures that we never fail to produce a task to run if we
+  // claim we have a task ready to run.
+  bool mHasPendingEventsPromisedIdleEvent = false;
 
-  // Try to process one high priority runnable after each normal
-  // priority runnable. This gives the processing model HTML spec has for
-  // 'Update the rendering' in the case only vsync messages are in the
-  // secondary queue and prevents starving the normal queue.
-  bool mProcessHighPriorityQueue = false;
-
-  // mIdlePeriod keeps track of the current idle period. If at any
-  // time the main event queue is empty, calling
+  // mIdlePeriod keeps track of the current idle period. Calling
   // mIdlePeriod->GetIdlePeriodHint() will give an estimate of when
   // the current idle period will end.
   nsCOMPtr<nsIIdlePeriod> mIdlePeriod;
 
-  // Set to true if HasPendingEvents() has been called and returned true because
-  // of a pending idle event.  This is used to remember to return that idle
-  // event from GetIdleEvent() to ensure that HasPendingEvents() never lies.
-  bool mHasPendingEventsPromisedIdleEvent = false;
-
-  TimeStamp mInputHandlingStartTime;
-
-  enum InputEventQueueState {
-    STATE_DISABLED,
-    STATE_FLUSHING,
-    STATE_SUSPEND,
-    STATE_ENABLED
-  };
-  InputEventQueueState mInputQueueState = STATE_DISABLED;
-
-  // If non-null, tells the end time of the idle period.
-  // Idle period starts when we get idle token from the parent process and
-  // ends when either there are no runnables in the event queues or
-  // mIdleToken < TimeStamp::Now()
+  // If non-null, this timestamp represents the end time of the idle period.  An
+  // idle period starts when we get the idle token from the parent process and
+  // ends when either there are no more things we want to run at idle priority
+  // or mIdleToken < TimeStamp::Now(), so we have reached our idle deadline.
   TimeStamp mIdleToken;
 
   // The id of the last idle request to the cross-process idle scheduler.
   uint64_t mIdleRequestId = 0;
 
+  // If we're in a content process, we use mIdleScheduler to communicate with
+  // the parent process for purposes of cross-process idle tracking.
   RefPtr<ipc::IdleSchedulerChild> mIdleScheduler;
+
+  // mIdleSchedulerInitialized is true if our mIdleScheduler has been
+  // initialized.  It may be null even after initialiazation, in various
+  // situations.
   bool mIdleSchedulerInitialized = false;
 
-  // mActive tells whether the event queue is running tasks.
+  // mActive is true when the PrioritizedEventQueue or TaskController we are
+  // associated with is running tasks.
   bool mActive = true;
 };
 
 }  // namespace mozilla
 
-#endif  // mozilla_PrioritizedEventQueue_h
+#endif  // mozilla_IdlePeriodState_h
--- a/xpcom/threads/PrioritizedEventQueue.cpp
+++ b/xpcom/threads/PrioritizedEventQueue.cpp
@@ -2,43 +2,36 @@
 /* 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 "PrioritizedEventQueue.h"
 #include "mozilla/EventQueue.h"
 #include "mozilla/ScopeExit.h"
-#include "mozilla/StaticPrefs_idle_period.h"
 #include "mozilla/StaticPrefs_threads.h"
 #include "mozilla/ipc/IdleSchedulerChild.h"
 #include "nsThreadManager.h"
 #include "nsXPCOMPrivate.h"  // for gXPCOMThreadsShutDown
 #include "InputEventStatistics.h"
 
 using namespace mozilla;
 
-static uint64_t sIdleRequestCounter = 0;
-
 PrioritizedEventQueue::PrioritizedEventQueue(
-    already_AddRefed<nsIIdlePeriod> aIdlePeriod)
+    already_AddRefed<nsIIdlePeriod>&& aIdlePeriod)
     : mHighQueue(MakeUnique<EventQueue>(EventQueuePriority::High)),
       mInputQueue(MakeUnique<EventQueue>(EventQueuePriority::Input)),
       mMediumHighQueue(MakeUnique<EventQueue>(EventQueuePriority::MediumHigh)),
       mNormalQueue(MakeUnique<EventQueue>(EventQueuePriority::Normal)),
       mDeferredTimersQueue(
           MakeUnique<EventQueue>(EventQueuePriority::DeferredTimers)),
       mIdleQueue(MakeUnique<EventQueue>(EventQueuePriority::Idle)),
-      mIdlePeriod(aIdlePeriod) {}
+      mIdlePeriodState(std::move(aIdlePeriod)) {}
 
-PrioritizedEventQueue::~PrioritizedEventQueue() {
-  if (mIdleScheduler) {
-    mIdleScheduler->Disconnect();
-  }
-}
+PrioritizedEventQueue::~PrioritizedEventQueue() = default;
 
 void PrioritizedEventQueue::PutEvent(already_AddRefed<nsIRunnable>&& aEvent,
                                      EventQueuePriority aPriority,
                                      const MutexAutoLock& aProofOfLock) {
   // Double check the priority with a QI.
   RefPtr<nsIRunnable> event(aEvent);
   EventQueuePriority priority = aPriority;
 
@@ -70,63 +63,16 @@ void PrioritizedEventQueue::PutEvent(alr
       mIdleQueue->PutEvent(event.forget(), priority, aProofOfLock);
       break;
     case EventQueuePriority::Count:
       MOZ_CRASH("EventQueuePriority::Count isn't a valid priority");
       break;
   }
 }
 
-TimeStamp PrioritizedEventQueue::GetLocalIdleDeadline(bool& aShuttingDown) {
-  // If we are shutting down, we won't honor the idle period, and we will
-  // always process idle runnables.  This will ensure that the idle queue
-  // gets exhausted at shutdown time to prevent intermittently leaking
-  // some runnables inside that queue and even worse potentially leaving
-  // some important cleanup work unfinished.
-  if (gXPCOMThreadsShutDown ||
-      nsThreadManager::get().GetCurrentThread()->ShuttingDown()) {
-    aShuttingDown = true;
-    return TimeStamp::Now();
-  }
-
-  aShuttingDown = false;
-  TimeStamp idleDeadline;
-  {
-    // Releasing the lock temporarily since getting the idle period
-    // might need to lock the timer thread. Unlocking here might make
-    // us receive an event on the main queue, but we've committed to
-    // run an idle event anyhow.
-    MutexAutoUnlock unlock(*mMutex);
-    mIdlePeriod->GetIdlePeriodHint(&idleDeadline);
-  }
-
-  // If HasPendingEvents() has been called and it has returned true because of
-  // pending idle events, there is a risk that we may decide here that we aren't
-  // idle and return null, in which case HasPendingEvents() has effectively
-  // lied.  Since we can't go back and fix the past, we have to adjust what we
-  // do here and forcefully pick the idle queue task here.  Note that this means
-  // that we are choosing to run a task from the idle queue when we would
-  // normally decide that we aren't in an idle period, but this can only happen
-  // if we fall out of the idle period in between the call to HasPendingEvents()
-  // and here, which should hopefully be quite rare.  We are effectively
-  // choosing to prioritize the sanity of our API semantics over the optimal
-  // scheduling.
-  if (!mHasPendingEventsPromisedIdleEvent &&
-      (!idleDeadline || idleDeadline < TimeStamp::Now())) {
-    return TimeStamp();
-  }
-  if (mHasPendingEventsPromisedIdleEvent && !idleDeadline) {
-    // If HasPendingEvents() has been called and it has returned true, but we're
-    // no longer in the idle period, we must return a valid timestamp to pretend
-    // that we are still in the idle period.
-    return TimeStamp::Now();
-  }
-  return idleDeadline;
-}
-
 EventQueuePriority PrioritizedEventQueue::SelectQueue(
     bool aUpdateState, const MutexAutoLock& aProofOfLock) {
   size_t inputCount = mInputQueue->Count(aProofOfLock);
 
   if (aUpdateState && mInputQueueState == STATE_ENABLED &&
       mInputHandlingStartTime.IsNull() && inputCount > 0) {
     mInputHandlingStartTime =
         InputEventStatistics::Get().GetInputHandlingStartTime(inputCount);
@@ -203,23 +149,20 @@ already_AddRefed<nsIRunnable> Prioritize
 #ifndef RELEASE_OR_BETA
   // Clear mNextIdleDeadline so that it is possible to determine that
   // we're running an idle runnable in ProcessNextEvent.
   *mNextIdleDeadline = TimeStamp();
 #endif
 
   EventQueuePriority queue = SelectQueue(true, aProofOfLock);
   auto guard = MakeScopeExit([&] {
-    mHasPendingEventsPromisedIdleEvent = false;
+    mIdlePeriodState.ForgetPendingTaskGuarantee();
     if (queue != EventQueuePriority::Idle &&
         queue != EventQueuePriority::DeferredTimers) {
-      EnsureIsActive();
-      if (mIdleToken && mIdleToken < TimeStamp::Now()) {
-        ClearIdleToken();
-      }
+      mIdlePeriodState.FlagNotIdle(*mMutex);
     }
   });
 
   if (aPriority) {
     *aPriority = queue;
   }
 
   if (queue == EventQueuePriority::High) {
@@ -250,40 +193,22 @@ already_AddRefed<nsIRunnable> Prioritize
   }
 
   // If we get here, then all queues except deferredtimers and idle are empty.
   MOZ_ASSERT(queue == EventQueuePriority::Idle ||
              queue == EventQueuePriority::DeferredTimers);
 
   if (mIdleQueue->IsEmpty(aProofOfLock) &&
       mDeferredTimersQueue->IsEmpty(aProofOfLock)) {
-    MOZ_ASSERT(!mHasPendingEventsPromisedIdleEvent);
-    EnsureIsPaused();
-    ClearIdleToken();
+    mIdlePeriodState.RanOutOfTasks(*mMutex);
     return nullptr;
   }
 
-  bool shuttingDown;
-  TimeStamp localIdleDeadline = GetLocalIdleDeadline(shuttingDown);
-  if (!localIdleDeadline) {
-    EnsureIsPaused();
-    ClearIdleToken();
-    return nullptr;
-  }
-
-  TimeStamp idleDeadline = mHasPendingEventsPromisedIdleEvent || shuttingDown
-                               ? localIdleDeadline
-                               : GetIdleToken(localIdleDeadline);
+  TimeStamp idleDeadline = mIdlePeriodState.GetDeadlineForIdleTask(*mMutex);
   if (!idleDeadline) {
-    EnsureIsPaused();
-
-    // Don't call ClearIdleToken() here, since we may have a pending
-    // request already.
-    MutexAutoUnlock unlock(*mMutex);
-    RequestIdleToken(localIdleDeadline);
     return nullptr;
   }
 
   nsCOMPtr<nsIRunnable> event =
       mDeferredTimersQueue->GetEvent(aPriority, aProofOfLock);
   if (!event) {
     event = mIdleQueue->GetEvent(aPriority, aProofOfLock);
   }
@@ -295,42 +220,39 @@ already_AddRefed<nsIRunnable> Prioritize
 
 #ifndef RELEASE_OR_BETA
     // Store the next idle deadline to be able to determine budget use
     // in ProcessNextEvent.
     *mNextIdleDeadline = idleDeadline;
 #endif
   }
 
-  EnsureIsActive();
   return event.forget();
 }
 
 void PrioritizedEventQueue::DidRunEvent(const MutexAutoLock& aProofOfLock) {
   if (IsEmpty(aProofOfLock)) {
-    if (IsActive()) {
-      SetPaused();
-    }
-    ClearIdleToken();
+    // Certainly no more idle tasks.
+    mIdlePeriodState.RanOutOfTasks(*mMutex);
   }
 }
 
 bool PrioritizedEventQueue::IsEmpty(const MutexAutoLock& aProofOfLock) {
   // Just check IsEmpty() on the sub-queues. Don't bother checking the idle
   // deadline since that only determines whether an idle event is ready or not.
   return mHighQueue->IsEmpty(aProofOfLock) &&
          mInputQueue->IsEmpty(aProofOfLock) &&
          mMediumHighQueue->IsEmpty(aProofOfLock) &&
          mNormalQueue->IsEmpty(aProofOfLock) &&
          mDeferredTimersQueue->IsEmpty(aProofOfLock) &&
          mIdleQueue->IsEmpty(aProofOfLock);
 }
 
 bool PrioritizedEventQueue::HasReadyEvent(const MutexAutoLock& aProofOfLock) {
-  mHasPendingEventsPromisedIdleEvent = false;
+  mIdlePeriodState.ForgetPendingTaskGuarantee();
 
   EventQueuePriority queue = SelectQueue(false, aProofOfLock);
 
   if (queue == EventQueuePriority::High) {
     return mHighQueue->HasReadyEvent(aProofOfLock);
   } else if (queue == EventQueuePriority::Input) {
     return mInputQueue->HasReadyEvent(aProofOfLock);
   } else if (queue == EventQueuePriority::MediumHigh) {
@@ -344,27 +266,21 @@ bool PrioritizedEventQueue::HasReadyEven
 
   // If we get here, then both the high and normal queues are empty.
 
   if (mDeferredTimersQueue->IsEmpty(aProofOfLock) &&
       mIdleQueue->IsEmpty(aProofOfLock)) {
     return false;
   }
 
-  bool shuttingDown;
-  TimeStamp localIdleDeadline = GetLocalIdleDeadline(shuttingDown);
-  if (localIdleDeadline) {
-    TimeStamp idleDeadline = mHasPendingEventsPromisedIdleEvent || shuttingDown
-                                 ? localIdleDeadline
-                                 : GetIdleToken(localIdleDeadline);
-    if (idleDeadline && (mDeferredTimersQueue->HasReadyEvent(aProofOfLock) ||
-                         mIdleQueue->HasReadyEvent(aProofOfLock))) {
-      mHasPendingEventsPromisedIdleEvent = true;
-      return true;
-    }
+  TimeStamp idleDeadline = mIdlePeriodState.PeekIdleDeadline(*mMutex);
+  if (idleDeadline && (mDeferredTimersQueue->HasReadyEvent(aProofOfLock) ||
+                       mIdleQueue->HasReadyEvent(aProofOfLock))) {
+    mIdlePeriodState.EnforcePendingTaskGuarantee();
+    return true;
   }
 
   return false;
 }
 
 bool PrioritizedEventQueue::HasPendingHighPriorityEvents(
     const MutexAutoLock& aProofOfLock) {
   return !mHighQueue->IsEmpty(aProofOfLock);
@@ -396,93 +312,8 @@ void PrioritizedEventQueue::SuspendInput
   mInputQueueState = STATE_SUSPEND;
 }
 
 void PrioritizedEventQueue::ResumeInputEventPrioritization(
     const MutexAutoLock& aProofOfLock) {
   MOZ_ASSERT(mInputQueueState == STATE_SUSPEND);
   mInputQueueState = STATE_ENABLED;
 }
-
-mozilla::TimeStamp PrioritizedEventQueue::GetIdleToken(
-    TimeStamp aLocalIdlePeriodHint) {
-  if (XRE_IsParentProcess()) {
-    return aLocalIdlePeriodHint;
-  }
-  if (mIdleToken) {
-    TimeStamp now = TimeStamp::Now();
-    if (mIdleToken < now) {
-      ClearIdleToken();
-      return mIdleToken;
-    }
-    return mIdleToken < aLocalIdlePeriodHint ? mIdleToken
-                                             : aLocalIdlePeriodHint;
-  }
-  return TimeStamp();
-}
-
-void PrioritizedEventQueue::RequestIdleToken(TimeStamp aLocalIdlePeriodHint) {
-  MOZ_ASSERT(!mActive);
-
-  if (!mIdleSchedulerInitialized) {
-    mIdleSchedulerInitialized = true;
-    if (StaticPrefs::idle_period_cross_process_scheduling() &&
-        XRE_IsContentProcess() && NS_IsMainThread() &&
-        // Disable when recording/replaying, as IdleSchedulerChild uses mutable
-        // shared memory which needs special handling.
-        !recordreplay::IsRecordingOrReplaying()) {
-      // For now cross-process idle scheduler is supported only on the main
-      // threads of the child processes.
-      mIdleScheduler = ipc::IdleSchedulerChild::GetMainThreadIdleScheduler();
-      if (mIdleScheduler) {
-        mIdleScheduler->Init(this);
-      }
-    }
-  }
-
-  if (mIdleScheduler && !mIdleRequestId) {
-    TimeStamp now = TimeStamp::Now();
-    if (aLocalIdlePeriodHint <= now) {
-      return;
-    }
-
-    mIdleRequestId = ++sIdleRequestCounter;
-    mIdleScheduler->SendRequestIdleTime(mIdleRequestId,
-                                        aLocalIdlePeriodHint - now);
-  }
-}
-
-void PrioritizedEventQueue::SetIdleToken(uint64_t aId, TimeDuration aDuration) {
-  if (mIdleRequestId == aId) {
-    mIdleToken = TimeStamp::Now() + aDuration;
-  }
-}
-
-void PrioritizedEventQueue::SetActive() {
-  MOZ_ASSERT(!mActive);
-  if (mIdleScheduler) {
-    mIdleScheduler->SetActive();
-  }
-  mActive = true;
-}
-
-void PrioritizedEventQueue::SetPaused() {
-  MOZ_ASSERT(mActive);
-  if (mIdleScheduler && mIdleScheduler->SetPaused()) {
-    MutexAutoUnlock unlock(*mMutex);
-    // We may have gotten a free cpu core for running idle tasks.
-    // We don't try to catch the case when there are prioritized processes
-    // running.
-    mIdleScheduler->SendSchedule();
-  }
-  mActive = false;
-}
-
-void PrioritizedEventQueue::ClearIdleToken() {
-  if (mIdleRequestId) {
-    if (mIdleScheduler) {
-      MutexAutoUnlock unlock(*mMutex);
-      mIdleScheduler->SendIdleTimeUsed(mIdleRequestId);
-    }
-    mIdleRequestId = 0;
-    mIdleToken = TimeStamp();
-  }
-}
--- a/xpcom/threads/PrioritizedEventQueue.h
+++ b/xpcom/threads/PrioritizedEventQueue.h
@@ -4,22 +4,23 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_PrioritizedEventQueue_h
 #define mozilla_PrioritizedEventQueue_h
 
 #include "mozilla/AbstractEventQueue.h"
 #include "mozilla/EventQueue.h"
+#include "mozilla/IdlePeriodState.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/TypeTraits.h"
 #include "mozilla/UniquePtr.h"
 #include "nsCOMPtr.h"
-#include "nsIIdlePeriod.h"
 
+class nsIIdlePeriod;
 class nsIRunnable;
 
 namespace mozilla {
 namespace ipc {
 class IdleSchedulerChild;
 }
 
 // This AbstractEventQueue implementation has one queue for each
@@ -38,17 +39,17 @@ class IdleSchedulerChild;
 //   and normal-priority events available, we interleave popping from the
 //   normal and high queues.
 // - We do not select events from the idle queue if the current idle period
 //   is almost over.
 class PrioritizedEventQueue final : public AbstractEventQueue {
  public:
   static const bool SupportsPrioritization = true;
 
-  explicit PrioritizedEventQueue(already_AddRefed<nsIIdlePeriod> aIdlePeriod);
+  explicit PrioritizedEventQueue(already_AddRefed<nsIIdlePeriod>&& aIdlePeriod);
 
   virtual ~PrioritizedEventQueue();
 
   void PutEvent(already_AddRefed<nsIRunnable>&& aEvent,
                 EventQueuePriority aPriority,
                 const MutexAutoLock& aProofOfLock) final;
   already_AddRefed<nsIRunnable> GetEvent(
       EventQueuePriority* aPriority, const MutexAutoLock& aProofOfLock) final;
@@ -85,68 +86,25 @@ class PrioritizedEventQueue final : publ
 
     n += mHighQueue->SizeOfIncludingThis(aMallocSizeOf);
     n += mInputQueue->SizeOfIncludingThis(aMallocSizeOf);
     n += mMediumHighQueue->SizeOfIncludingThis(aMallocSizeOf);
     n += mNormalQueue->SizeOfIncludingThis(aMallocSizeOf);
     n += mDeferredTimersQueue->SizeOfIncludingThis(aMallocSizeOf);
     n += mIdleQueue->SizeOfIncludingThis(aMallocSizeOf);
 
-    if (mIdlePeriod) {
-      n += aMallocSizeOf(mIdlePeriod);
-    }
+    n += mIdlePeriodState.SizeOfExcludingThis(aMallocSizeOf);
 
     return n;
   }
 
-  void SetIdleToken(uint64_t aId, TimeDuration aDuration);
-
-  bool IsActive() { return mActive; }
-
-  void EnsureIsActive() {
-    if (!mActive) {
-      SetActive();
-    }
-  }
-
-  void EnsureIsPaused() {
-    if (mActive) {
-      SetPaused();
-    }
-  }
-
  private:
   EventQueuePriority SelectQueue(bool aUpdateState,
                                  const MutexAutoLock& aProofOfLock);
 
-  // Returns a null TimeStamp if we're not in the idle period.
-  mozilla::TimeStamp GetLocalIdleDeadline(bool& aShuttingDown);
-
-  // SetActive should be called when the event queue is running any type of
-  // tasks.
-  void SetActive();
-  // SetPaused should be called once the event queue doesn't have more
-  // tasks to process, or is waiting for the idle token.
-  void SetPaused();
-
-  // Gets the idle token, which is the end time of the idle period.
-  TimeStamp GetIdleToken(TimeStamp aLocalIdlePeriodHint);
-
-  // In case of child processes, requests idle time from the cross-process
-  // idle scheduler.
-  void RequestIdleToken(TimeStamp aLocalIdlePeriodHint);
-
-  // Returns true if the event queue either is waiting for an idle token
-  // from the idle scheduler or has one.
-  bool HasIdleRequest() { return mIdleRequestId != 0; }
-
-  // Mark that the event queue doesn't have idle time to use, nor is expecting
-  // to get idle token from the idle scheduler.
-  void ClearIdleToken();
-
   UniquePtr<EventQueue> mHighQueue;
   UniquePtr<EventQueue> mInputQueue;
   UniquePtr<EventQueue> mMediumHighQueue;
   UniquePtr<EventQueue> mNormalQueue;
   UniquePtr<EventQueue> mDeferredTimersQueue;
   UniquePtr<EventQueue> mIdleQueue;
 
   // We need to drop the queue mutex when checking the idle deadline, so we keep
@@ -160,48 +118,25 @@ class PrioritizedEventQueue final : publ
 #endif
 
   // Try to process one high priority runnable after each normal
   // priority runnable. This gives the processing model HTML spec has for
   // 'Update the rendering' in the case only vsync messages are in the
   // secondary queue and prevents starving the normal queue.
   bool mProcessHighPriorityQueue = false;
 
-  // mIdlePeriod keeps track of the current idle period. If at any
-  // time the main event queue is empty, calling
-  // mIdlePeriod->GetIdlePeriodHint() will give an estimate of when
-  // the current idle period will end.
-  nsCOMPtr<nsIIdlePeriod> mIdlePeriod;
-
-  // Set to true if HasPendingEvents() has been called and returned true because
-  // of a pending idle event.  This is used to remember to return that idle
-  // event from GetIdleEvent() to ensure that HasPendingEvents() never lies.
-  bool mHasPendingEventsPromisedIdleEvent = false;
-
   TimeStamp mInputHandlingStartTime;
 
   enum InputEventQueueState {
     STATE_DISABLED,
     STATE_FLUSHING,
     STATE_SUSPEND,
     STATE_ENABLED
   };
   InputEventQueueState mInputQueueState = STATE_DISABLED;
 
-  // If non-null, tells the end time of the idle period.
-  // Idle period starts when we get idle token from the parent process and
-  // ends when either there are no runnables in the event queues or
-  // mIdleToken < TimeStamp::Now()
-  TimeStamp mIdleToken;
-
-  // The id of the last idle request to the cross-process idle scheduler.
-  uint64_t mIdleRequestId = 0;
-
-  RefPtr<ipc::IdleSchedulerChild> mIdleScheduler;
-  bool mIdleSchedulerInitialized = false;
-
-  // mActive tells whether the event queue is running tasks.
-  bool mActive = true;
+  // Tracking of our idle state of various sorts.
+  IdlePeriodState mIdlePeriodState;
 };
 
 }  // namespace mozilla
 
 #endif  // mozilla_PrioritizedEventQueue_h
--- a/xpcom/threads/moz.build
+++ b/xpcom/threads/moz.build
@@ -43,16 +43,17 @@ EXPORTS.mozilla += [
     'AbstractEventQueue.h',
     'AbstractThread.h',
     'BlockingResourceBase.h',
     'CondVar.h',
     'CPUUsageWatcher.h',
     'DataMutex.h',
     'DeadlockDetector.h',
     'EventQueue.h',
+    'IdlePeriodState.h',
     'IdleTaskRunner.h',
     'LazyIdleThread.h',
     'MainThreadIdlePeriod.h',
     'Monitor.h',
     'MozPromise.h',
     'MozPromiseInlines.h',
     'Mutex.h',
     'PerformanceCounter.h',
@@ -81,16 +82,17 @@ SOURCES += [
     'ThreadDelay.cpp',
 ]
 
 UNIFIED_SOURCES += [
     'AbstractThread.cpp',
     'BlockingResourceBase.cpp',
     'CPUUsageWatcher.cpp',
     'EventQueue.cpp',
+    'IdlePeriodState.cpp',
     'InputEventStatistics.cpp',
     'LazyIdleThread.cpp',
     'MainThreadIdlePeriod.cpp',
     'nsEnvironment.cpp',
     'nsMemoryPressure.cpp',
     'nsProcessCommon.cpp',
     'nsProxyRelease.cpp',
     'nsThread.cpp',