Merge inbound to mozilla-central. a=merge
authorAndreea Pavel <apavel@mozilla.com>
Thu, 24 May 2018 01:00:23 +0300
changeset 419593 47e81ea1ef10189ef210867934bf36e14cf223dc
parent 419560 366a9f1b95f689bebddef872b616235e7f299ba8 (current diff)
parent 419592 8f56e6dd916450dd9c3de12894cf531497c0a71e (diff)
child 419616 538e4b3728c4308aba4d3aa34b910b93cd3bc558
child 419650 e8df6572638a5138cbca0a25bb97b3786864e767
push id34039
push userapavel@mozilla.com
push dateWed, 23 May 2018 22:01:03 +0000
treeherdermozilla-central@47e81ea1ef10 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone62.0a1
first release with
nightly linux32
47e81ea1ef10 / 62.0a1 / 20180523221148 / files
nightly linux64
47e81ea1ef10 / 62.0a1 / 20180523221148 / files
nightly mac
47e81ea1ef10 / 62.0a1 / 20180523221148 / files
nightly win32
47e81ea1ef10 / 62.0a1 / 20180523221148 / files
nightly win64
47e81ea1ef10 / 62.0a1 / 20180523221148 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
mobile/android/fonts/ClearSans-Bold.ttf
mobile/android/fonts/ClearSans-BoldItalic.ttf
mobile/android/fonts/ClearSans-Italic.ttf
mobile/android/fonts/ClearSans-Light.ttf
mobile/android/fonts/ClearSans-Medium.ttf
mobile/android/fonts/ClearSans-MediumItalic.ttf
mobile/android/fonts/ClearSans-Regular.ttf
mobile/android/fonts/ClearSans-Thin.ttf
netwerk/protocol/http/nsHttpChannel.cpp
testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/import-meta/import-meta-url.html.ini
testing/web-platform/meta/shadow-dom/Document-prototype-currentScript.html.ini
--- a/devtools/client/webide/themes/newapp.css
+++ b/devtools/client/webide/themes/newapp.css
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 dialog {
   -moz-appearance: none;
   background-image: linear-gradient(rgb(255, 255, 255), rgb(237, 237, 237) 100px);
-  font-family: "Clear Sans", sans-serif;
+  font-family: sans-serif;
   color: #424E5A;
   overflow-y: scroll;
 }
 
 .header-name {
   font-size: 1.5rem;
   font-weight: normal;
   margin: 15px 0;
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -1015,17 +1015,16 @@ nsDocShell::LoadURI(nsIURI* aURI,
                       referrerPolicy,
                       triggeringPrincipal,
                       principalToInherit,
                       flags,
                       target,
                       nullptr,      // No type hint
                       VoidString(), // No forced download
                       postStream,
-                      -1, // XXXbaku
                       headersStream,
                       loadType,
                       nullptr, // No SHEntry
                       aFirstParty,
                       srcdoc,
                       sourceDocShell,
                       baseURI,
                       nullptr,  // No nsIDocShell
@@ -4927,17 +4926,17 @@ nsDocShell::LoadErrorPage(nsIURI* aURI, 
   nsCOMPtr<nsIURI> errorPageURI;
   nsresult rv = NS_NewURI(getter_AddRefs(errorPageURI), errorPageUrl);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return InternalLoad(errorPageURI, nullptr, Nothing(), false, nullptr,
                       mozilla::net::RP_Unset,
                       nsContentUtils::GetSystemPrincipal(), nullptr,
                       INTERNAL_LOAD_FLAGS_NONE, EmptyString(),
-                      nullptr, VoidString(), nullptr, -1, nullptr,
+                      nullptr, VoidString(), nullptr, nullptr,
                       LOAD_ERROR_PAGE, nullptr, true, VoidString(), this,
                       nullptr, nullptr, nullptr);
 }
 
 NS_IMETHODIMP
 nsDocShell::Reload(uint32_t aReloadFlags)
 {
   if (!IsNavigationAllowed()) {
@@ -5029,17 +5028,16 @@ nsDocShell::Reload(uint32_t aReloadFlags
                       referrerPolicy,
                       triggeringPrincipal,
                       triggeringPrincipal,
                       flags,
                       EmptyString(),   // No window target
                       NS_LossyConvertUTF16toASCII(contentTypeHint).get(),
                       VoidString(),    // No forced download
                       nullptr,         // No post data
-                      -1,              // No post data length
                       nullptr,         // No headers data
                       loadType,        // Load type
                       nullptr,         // No SHEntry
                       true,
                       srcdoc,          // srcdoc argument for iframe
                       this,            // For reloads we are the source
                       baseURI,
                       nullptr,         // No nsIDocShell
@@ -9140,17 +9138,16 @@ public:
                     Maybe<nsCOMPtr<nsIURI>> const& aResultPrincipalURI,
                     bool aLoadReplace,
                     nsIURI* aReferrer, uint32_t aReferrerPolicy,
                     nsIPrincipal* aTriggeringPrincipal,
                     nsIPrincipal* aPrincipalToInherit,
                     uint32_t aFlags,
                     const char* aTypeHint,
                     nsIInputStream* aPostData,
-                    int64_t aPostDataLength,
                     nsIInputStream* aHeadersData,
                     uint32_t aLoadType,
                     nsISHEntry* aSHEntry,
                     bool aFirstParty,
                     const nsAString& aSrcdoc,
                     nsIDocShell* aSourceDocShell,
                     nsIURI* aBaseURI)
     : mozilla::Runnable("InternalLoadEvent")
@@ -9160,17 +9157,16 @@ public:
     , mOriginalURI(aOriginalURI)
     , mResultPrincipalURI(aResultPrincipalURI)
     , mLoadReplace(aLoadReplace)
     , mReferrer(aReferrer)
     , mReferrerPolicy(aReferrerPolicy)
     , mTriggeringPrincipal(aTriggeringPrincipal)
     , mPrincipalToInherit(aPrincipalToInherit)
     , mPostData(aPostData)
-    , mPostDataLength(aPostDataLength)
     , mHeadersData(aHeadersData)
     , mSHEntry(aSHEntry)
     , mFlags(aFlags)
     , mLoadType(aLoadType)
     , mFirstParty(aFirstParty)
     , mSourceDocShell(aSourceDocShell)
     , mBaseURI(aBaseURI)
   {
@@ -9188,17 +9184,17 @@ public:
     return mDocShell->InternalLoad(mURI, mOriginalURI, mResultPrincipalURI,
                                    mLoadReplace,
                                    mReferrer,
                                    mReferrerPolicy,
                                    mTriggeringPrincipal, mPrincipalToInherit,
                                    mFlags, EmptyString(),
                                    mTypeHint.IsVoid() ? nullptr
                                                       : mTypeHint.get(),
-                                   VoidString(), mPostData, mPostDataLength,
+                                   VoidString(), mPostData,
                                    mHeadersData, mLoadType, mSHEntry,
                                    mFirstParty, mSrcdoc, mSourceDocShell,
                                    mBaseURI, nullptr,
                                    nullptr);
   }
 
 private:
   nsCString mTypeHint;
@@ -9209,17 +9205,16 @@ private:
   nsCOMPtr<nsIURI> mOriginalURI;
   Maybe<nsCOMPtr<nsIURI>> mResultPrincipalURI;
   bool mLoadReplace;
   nsCOMPtr<nsIURI> mReferrer;
   uint32_t mReferrerPolicy;
   nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
   nsCOMPtr<nsIPrincipal> mPrincipalToInherit;
   nsCOMPtr<nsIInputStream> mPostData;
-  int64_t mPostDataLength;
   nsCOMPtr<nsIInputStream> mHeadersData;
   nsCOMPtr<nsISHEntry> mSHEntry;
   uint32_t mFlags;
   uint32_t mLoadType;
   bool mFirstParty;
   nsCOMPtr<nsIDocShell> mSourceDocShell;
   nsCOMPtr<nsIURI> mBaseURI;
 };
@@ -9260,17 +9255,16 @@ nsDocShell::InternalLoad(nsIURI* aURI,
                          uint32_t aReferrerPolicy,
                          nsIPrincipal* aTriggeringPrincipal,
                          nsIPrincipal* aPrincipalToInherit,
                          uint32_t aFlags,
                          const nsAString& aWindowTarget,
                          const char* aTypeHint,
                          const nsAString& aFileName,
                          nsIInputStream* aPostData,
-                         int64_t aPostDataLength,
                          nsIInputStream* aHeadersData,
                          uint32_t aLoadType,
                          nsISHEntry* aSHEntry,
                          bool aFirstParty,
                          const nsAString& aSrcdoc,
                          nsIDocShell* aSourceDocShell,
                          nsIURI* aBaseURI,
                          nsIDocShell** aDocShell,
@@ -9612,17 +9606,16 @@ nsDocShell::InternalLoad(nsIURI* aURI,
                                         aReferrerPolicy,
                                         aTriggeringPrincipal,
                                         principalToInherit,
                                         aFlags,
                                         EmptyString(),   // No window target
                                         aTypeHint,
                                         VoidString(),    // No forced download
                                         aPostData,
-                                        aPostDataLength,
                                         aHeadersData,
                                         aLoadType,
                                         aSHEntry,
                                         aFirstParty,
                                         aSrcdoc,
                                         aSourceDocShell,
                                         aBaseURI,
                                         aDocShell,
@@ -9703,17 +9696,17 @@ nsDocShell::InternalLoad(nsIURI* aURI,
         mLoadType = LOAD_NORMAL_REPLACE;
       }
 
       // Do this asynchronously
       nsCOMPtr<nsIRunnable> ev =
         new InternalLoadEvent(this, aURI, aOriginalURI, aResultPrincipalURI,
                               aLoadReplace, aReferrer, aReferrerPolicy,
                               aTriggeringPrincipal, principalToInherit,
-                              aFlags, aTypeHint, aPostData, aPostDataLength,
+                              aFlags, aTypeHint, aPostData,
                               aHeadersData, aLoadType, aSHEntry, aFirstParty,
                               aSrcdoc, aSourceDocShell, aBaseURI);
       return DispatchToTabGroup(TaskCategory::Other, ev.forget());
     }
 
     // Just ignore this load attempt
     return NS_OK;
   }
@@ -10226,17 +10219,17 @@ nsDocShell::InternalLoad(nsIURI* aURI,
   rv = DoURILoad(aURI, aOriginalURI, aResultPrincipalURI, aLoadReplace,
                  loadFromExternal,
                  (aFlags & INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI),
                  (aFlags & INTERNAL_LOAD_FLAGS_ORIGINAL_FRAME_SRC),
                  aReferrer,
                  !(aFlags & INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER),
                  aReferrerPolicy,
                  aTriggeringPrincipal, principalToInherit, aTypeHint,
-                 aFileName, aPostData, aPostDataLength, aHeadersData,
+                 aFileName, aPostData, aHeadersData,
                  aFirstParty, aDocShell, getter_AddRefs(req),
                  (aFlags & INTERNAL_LOAD_FLAGS_FIRST_LOAD) != 0,
                  (aFlags & INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER) != 0,
                  (aFlags & INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES) != 0,
                  srcdoc, aBaseURI, contentType);
   if (req && aRequest) {
     NS_ADDREF(*aRequest = req);
   }
@@ -10370,17 +10363,16 @@ nsDocShell::DoURILoad(nsIURI* aURI,
                       nsIURI* aReferrerURI,
                       bool aSendReferrer,
                       uint32_t aReferrerPolicy,
                       nsIPrincipal* aTriggeringPrincipal,
                       nsIPrincipal* aPrincipalToInherit,
                       const char* aTypeHint,
                       const nsAString& aFileName,
                       nsIInputStream* aPostData,
-                      int64_t aPostDataLength,
                       nsIInputStream* aHeadersData,
                       bool aFirstParty,
                       nsIDocShell** aDocShell,
                       nsIRequest** aRequest,
                       bool aIsNewWindowTarget,
                       bool aBypassClassifier,
                       bool aForceAllowCookies,
                       const nsAString& aSrcdoc,
@@ -10806,17 +10798,17 @@ nsDocShell::DoURILoad(nsIURI* aURI,
       nsCOMPtr<nsISeekableStream> postDataSeekable =
         do_QueryInterface(aPostData);
       if (postDataSeekable) {
         rv = postDataSeekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
         NS_ENSURE_SUCCESS(rv, rv);
       }
 
       // we really need to have a content type associated with this stream!!
-      postChannel->SetUploadStream(aPostData, EmptyCString(), aPostDataLength);
+      postChannel->SetUploadStream(aPostData, EmptyCString(), -1);
     }
 
     /* If there is a valid postdata *and* it is a History Load,
      * set up the cache key on the channel, to retrieve the
      * data *only* from the cache. If it is a normal reload, the
      * cache is free to go to the server for updated postdata.
      */
     if (cacheChannel && cacheKey != 0) {
@@ -12295,17 +12287,16 @@ nsDocShell::LoadHistoryEntry(nsISHEntry*
                     referrerPolicy,
                     triggeringPrincipal,
                     principalToInherit,
                     flags,
                     EmptyString(),      // No window target
                     contentType.get(),  // Type hint
                     VoidString(),       // No forced file download
                     postData,           // Post data stream
-                    -1,                 // Post data stream length
                     nullptr,            // No headers stream
                     aLoadType,          // Load type
                     aEntry,             // SHEntry
                     true,
                     srcdoc,
                     nullptr,            // Source docshell, see comment above
                     baseURI,
                     nullptr,            // No nsIDocShell
@@ -13287,17 +13278,16 @@ nsDocShell::SelectNone(void)
 class OnLinkClickEvent : public Runnable
 {
 public:
   OnLinkClickEvent(nsDocShell* aHandler, nsIContent* aContent,
                    nsIURI* aURI,
                    const char16_t* aTargetSpec,
                    const nsAString& aFileName,
                    nsIInputStream* aPostDataStream,
-                   int64_t aPostDataStreamLength,
                    nsIInputStream* aHeadersDataStream,
                    bool aNoOpenerImplied,
                    bool aIsUserTriggered,
                    bool aIsTrusted,
                    nsIPrincipal* aTriggeringPrincipal);
 
   NS_IMETHOD Run() override
   {
@@ -13307,76 +13297,72 @@ public:
     // OnLinkClickSync we'll eventually end up in nsGlobalWindow::OpenInternal
     // which only does popup blocking if !LegacyIsCallerChromeOrNativeCode().
     // So we need to fake things so that we don't look like native code as far
     // as LegacyIsCallerNativeCode() is concerned.
     AutoJSAPI jsapi;
     if (mIsTrusted || jsapi.Init(mContent->OwnerDoc()->GetScopeObject())) {
       mHandler->OnLinkClickSync(mContent, mURI,
                                 mTargetSpec.get(), mFileName,
-                                mPostDataStream, mPostDataStreamLength,
+                                mPostDataStream,
                                 mHeadersDataStream, mNoOpenerImplied,
                                 nullptr, nullptr, mIsUserTriggered,
                                 mTriggeringPrincipal);
     }
     return NS_OK;
   }
 
 private:
   RefPtr<nsDocShell> mHandler;
   nsCOMPtr<nsIURI> mURI;
   nsString mTargetSpec;
   nsString mFileName;
   nsCOMPtr<nsIInputStream> mPostDataStream;
-  int64_t mPostDataStreamLength;
   nsCOMPtr<nsIInputStream> mHeadersDataStream;
   nsCOMPtr<nsIContent> mContent;
   PopupControlState mPopupState;
   bool mNoOpenerImplied;
   bool mIsUserTriggered;
   bool mIsTrusted;
   nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
 };
 
 OnLinkClickEvent::OnLinkClickEvent(nsDocShell* aHandler,
                                    nsIContent* aContent,
                                    nsIURI* aURI,
                                    const char16_t* aTargetSpec,
                                    const nsAString& aFileName,
                                    nsIInputStream* aPostDataStream,
-                                   int64_t aPostDataStreamLength,
                                    nsIInputStream* aHeadersDataStream,
                                    bool aNoOpenerImplied,
                                    bool aIsUserTriggered,
                                    bool aIsTrusted,
                                    nsIPrincipal* aTriggeringPrincipal)
   : mozilla::Runnable("OnLinkClickEvent")
   , mHandler(aHandler)
   , mURI(aURI)
   , mTargetSpec(aTargetSpec)
   , mFileName(aFileName)
   , mPostDataStream(aPostDataStream)
-  , mPostDataStreamLength(aPostDataStreamLength)
   , mHeadersDataStream(aHeadersDataStream)
   , mContent(aContent)
   , mPopupState(mHandler->mScriptGlobal->GetPopupControlState())
   , mNoOpenerImplied(aNoOpenerImplied)
   , mIsUserTriggered(aIsUserTriggered)
   , mIsTrusted(aIsTrusted)
   , mTriggeringPrincipal(aTriggeringPrincipal)
 {
 }
 
 NS_IMETHODIMP
 nsDocShell::OnLinkClick(nsIContent* aContent,
                         nsIURI* aURI,
                         const char16_t* aTargetSpec,
                         const nsAString& aFileName,
                         nsIInputStream* aPostDataStream,
-                        int64_t aPostDataStreamLength,
                         nsIInputStream* aHeadersDataStream,
                         bool aIsUserTriggered,
                         bool aIsTrusted,
                         nsIPrincipal* aTriggeringPrincipal)
 {
   NS_ASSERTION(NS_IsMainThread(), "wrong thread");
 
   if (!IsNavigationAllowed() || !IsOKToLoadURI(aURI)) {
@@ -13413,18 +13399,17 @@ nsDocShell::OnLinkClick(nsIContent* aCon
   }
 
   if (NS_FAILED(rv)) {
     target = aTargetSpec;
   }
 
   nsCOMPtr<nsIRunnable> ev =
     new OnLinkClickEvent(this, aContent, aURI, target.get(), aFileName,
-                         aPostDataStream, aPostDataStreamLength,
-                         aHeadersDataStream, noOpenerImplied,
+                         aPostDataStream, aHeadersDataStream, noOpenerImplied,
                          aIsUserTriggered, aIsTrusted, aTriggeringPrincipal);
   return DispatchToTabGroup(TaskCategory::UI, ev.forget());
 }
 
 static bool
 IsElementAnchorOrArea(nsIContent* aContent)
 {
   // Make sure we are dealing with either an <A> or <AREA> element in the HTML
@@ -13433,17 +13418,16 @@ IsElementAnchorOrArea(nsIContent* aConte
 }
 
 NS_IMETHODIMP
 nsDocShell::OnLinkClickSync(nsIContent* aContent,
                             nsIURI* aURI,
                             const char16_t* aTargetSpec,
                             const nsAString& aFileName,
                             nsIInputStream* aPostDataStream,
-                            int64_t aPostDataStreamLength,
                             nsIInputStream* aHeadersDataStream,
                             bool aNoOpenerImplied,
                             nsIDocShell** aDocShell,
                             nsIRequest** aRequest,
                             bool aIsUserTriggered,
                             nsIPrincipal* aTriggeringPrincipal)
 {
   // Initialize the DocShell / Request
@@ -13594,17 +13578,16 @@ nsDocShell::OnLinkClickSync(nsIContent* 
                              refererPolicy,             // Referer policy
                              triggeringPrincipal,
                              aContent->NodePrincipal(),
                              flags,
                              target,                    // Window target
                              NS_LossyConvertUTF16toASCII(typeHint).get(),
                              aFileName,                 // Download as file
                              aPostDataStream,           // Post data stream
-                             aPostDataStreamLength,     // Post data stream length
                              aHeadersDataStream,        // Headers stream
                              loadType,                  // Load type
                              nullptr,                   // No SHEntry
                              true,                      // first party site
                              VoidString(),              // No srcdoc
                              this,                      // We are the source
                              nullptr,                   // baseURI not needed
                              aDocShell,                 // DocShell out-param
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -203,27 +203,25 @@ public:
   }
 
   // nsILinkHandler
   NS_IMETHOD OnLinkClick(nsIContent* aContent,
                          nsIURI* aURI,
                          const char16_t* aTargetSpec,
                          const nsAString& aFileName,
                          nsIInputStream* aPostDataStream,
-                         int64_t aPostDataStreamLength,
                          nsIInputStream* aHeadersDataStream,
                          bool aIsUserTriggered,
                          bool aIsTrusted,
                          nsIPrincipal* aTriggeringPrincipal) override;
   NS_IMETHOD OnLinkClickSync(nsIContent* aContent,
                              nsIURI* aURI,
                              const char16_t* aTargetSpec,
                              const nsAString& aFileName,
                              nsIInputStream* aPostDataStream = 0,
-                             int64_t aPostDataStreamLength = -1,
                              nsIInputStream* aHeadersDataStream = 0,
                              bool aNoOpenerImplied = false,
                              nsIDocShell** aDocShell = 0,
                              nsIRequest** aRequest = 0,
                              bool aIsUserTriggered = false,
                              nsIPrincipal* aTriggeringPrincipal = nullptr) override;
   NS_IMETHOD OnOverLink(nsIContent* aContent,
                         nsIURI* aURI,
@@ -528,17 +526,16 @@ private: // member functions
                      nsIURI* aReferrer,
                      bool aSendReferrer,
                      uint32_t aReferrerPolicy,
                      nsIPrincipal* aTriggeringPrincipal,
                      nsIPrincipal* aPrincipalToInherit,
                      const char* aTypeHint,
                      const nsAString& aFileName,
                      nsIInputStream* aPostData,
-                     int64_t aPostDataLength,
                      nsIInputStream* aHeadersData,
                      bool aFirstParty,
                      nsIDocShell** aDocShell,
                      nsIRequest** aRequest,
                      bool aIsNewWindowTarget,
                      bool aBypassClassifier,
                      bool aForceAllowCookies,
                      const nsAString& aSrcdoc,
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -183,18 +183,16 @@ interface nsIDocShell : nsIDocShellTreeI
    * @param aStopActiveDoc       - Flag indicating whether loading the current
    *                               document should be stopped.
    * @param aWindowTarget        - Window target for the load.
    * @param aTypeHint            - A hint as to the content-type of the resulting
    *                               data.  May be null or empty if no hint.
    * @param aFileName            - Non-null when the link should be downloaded as
                                    the given filename.
    * @param aPostDataStream      - Post data stream (if POSTing)
-   * @param aPostDataStreamLength - Post data stream length. Use -1 if the length
-                                    of the stream is unknown.
    * @param aHeadersStream       - Stream containing "extra" request headers...
    * @param aLoadFlags           - Flags to modify load behaviour. Flags are defined
    *                               in nsIWebNavigation.
    * @param aSHEntry             - Active Session History entry (if loading from SH)
    * @param aSrcdoc                When INTERNAL_LOAD_FLAGS_IS_SRCDOC is set, the
    *                               contents of this parameter will be loaded instead
    *                               of aURI.
    * @param aSourceDocShell      - The source browsing context for the navigation.
@@ -210,17 +208,16 @@ interface nsIDocShell : nsIDocShellTreeI
                               in unsigned long aReferrerPolicy,
                               in nsIPrincipal aTriggeringPrincipal,
                               in nsIPrincipal aPrincipalToInherit,
                               in uint32_t aFlags,
                               in AString aWindowTarget,
                               in string aTypeHint,
                               in AString aFileName,
                               in nsIInputStream aPostDataStream,
-                              in long long aPostDataStreamLength,
                               in nsIInputStream aHeadersStream,
                               in unsigned long aLoadFlags,
                               in nsISHEntry aSHEntry,
                               in boolean aFirstParty,
                               in AString aSrcdoc,
                               in nsIDocShell aSourceDocShell,
                               in nsIURI aBaseURI,
                               out nsIDocShell aDocShell,
--- a/docshell/base/nsILinkHandler.h
+++ b/docshell/base/nsILinkHandler.h
@@ -30,29 +30,26 @@ public:
    * Process a click on a link.
    *
    * @param aContent the content for the frame that generated the trigger
    * @param aURI a URI object that defines the destination for the link
    * @param aTargetSpec indicates where the link is targeted (may be an empty
    *        string)
    * @param aFileName non-null when the link should be downloaded as the given file
    * @param aPostDataStream the POST data to send
-   * @param aPostDataStreamLength the POST data length. Use -1 if the length is
-   *        unknown.
    * @param aHeadersDataStream ???
    * @param aIsTrusted false if the triggerer is an untrusted DOM event.
    * @param aTriggeringPrincipal, if not passed explicitly we fall back to
    *        the document's principal.
    */
   NS_IMETHOD OnLinkClick(nsIContent* aContent,
                          nsIURI* aURI,
                          const char16_t* aTargetSpec,
                          const nsAString& aFileName,
                          nsIInputStream* aPostDataStream,
-                         int64_t aPostDataStreamLength,
                          nsIInputStream* aHeadersDataStream,
                          bool aIsUserTriggered,
                          bool aIsTrusted,
                          nsIPrincipal* aTriggeringPrincipal) = 0;
 
   /**
    * Process a click on a link.
    *
@@ -60,30 +57,28 @@ public:
    * through an event.
    *
    * @param aContent the content for the frame that generated the trigger
    * @param aURI a URI obect that defines the destination for the link
    * @param aTargetSpec indicates where the link is targeted (may be an empty
    *        string)
    * @param aFileName non-null when the link should be downloaded as the given file
    * @param aPostDataStream the POST data to send
-   * @param aPostDataStreamLength the POST data length
    * @param aHeadersDataStream ???
    * @param aNoOpenerImplied if the link implies "noopener"
    * @param aDocShell (out-param) the DocShell that the request was opened on
    * @param aRequest the request that was opened
    * @param aTriggeringPrincipal, if not passed explicitly we fall back to
    *        the document's principal.
    */
   NS_IMETHOD OnLinkClickSync(nsIContent* aContent,
                              nsIURI* aURI,
                              const char16_t* aTargetSpec,
                              const nsAString& aFileName,
                              nsIInputStream* aPostDataStream = 0,
-                             int64_t aPostDataStreamLength = -1,
                              nsIInputStream* aHeadersDataStream = 0,
                              bool aNoOpenerImplied = false,
                              nsIDocShell** aDocShell = 0,
                              nsIRequest** aRequest = 0,
                              bool aIsUserTriggered = false,
                              nsIPrincipal* aTriggeringPrincipal = nullptr) = 0;
 
   /**
--- a/dom/base/FormData.cpp
+++ b/dom/base/FormData.cpp
@@ -96,17 +96,16 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 // -------------------------------------------------------------------------
 // HTMLFormSubmission
 nsresult
 FormData::GetEncodedSubmission(nsIURI* aURI,
                                nsIInputStream** aPostDataStream,
-                               int64_t* aPostDataStreamLength,
                                nsCOMPtr<nsIURI>& aOutURI)
 {
   NS_NOTREACHED("Shouldn't call FormData::GetEncodedSubmission");
   return NS_OK;
 }
 
 void
 FormData::Append(const nsAString& aName, const nsAString& aValue,
--- a/dom/base/FormData.h
+++ b/dom/base/FormData.h
@@ -102,17 +102,17 @@ public:
 
   const nsAString& GetKeyAtIndex(uint32_t aIndex) const;
 
   const OwningBlobOrDirectoryOrUSVString& GetValueAtIndex(uint32_t aIndex) const;
 
   // HTMLFormSubmission
   virtual nsresult
   GetEncodedSubmission(nsIURI* aURI, nsIInputStream** aPostDataStream,
-                       int64_t* aPostDataStreamLength, nsCOMPtr<nsIURI>& aOutURI) override;
+                       nsCOMPtr<nsIURI>& aOutURI) override;
 
   virtual nsresult AddNameValuePair(const nsAString& aName,
                                     const nsAString& aValue) override
   {
     FormDataTuple* data = mFormData.AppendElement();
     SetNameValuePair(data, aName, aValue);
     return NS_OK;
   }
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -5533,17 +5533,17 @@ nsContentUtils::TriggerLink(nsIContent *
          !aContent->IsSVGElement(nsGkAtoms::a)) ||
         !aContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::download, fileName) ||
         NS_FAILED(aContent->NodePrincipal()->CheckMayLoad(aLinkURI, false, true))) {
       fileName.SetIsVoid(true); // No actionable download attribute was found.
     }
 
     handler->OnLinkClick(aContent, aLinkURI,
                          fileName.IsVoid() ? aTargetSpec.get() : EmptyString().get(),
-                         fileName, nullptr, -1, nullptr, EventStateManager::IsHandlingUserInput(),
+                         fileName, nullptr, nullptr, EventStateManager::IsHandlingUserInput(),
                          aIsTrusted, aContent->NodePrincipal());
   }
 }
 
 /* static */
 void
 nsContentUtils::GetLinkLocation(Element* aElement, nsString& aLocationString)
 {
--- a/dom/file/ipc/IPCBlobInputStream.cpp
+++ b/dom/file/ipc/IPCBlobInputStream.cpp
@@ -120,24 +120,27 @@ NS_INTERFACE_MAP_BEGIN(IPCBlobInputStrea
   NS_INTERFACE_MAP_ENTRY(nsIInputStream)
   NS_INTERFACE_MAP_ENTRY(nsIAsyncInputStream)
   NS_INTERFACE_MAP_ENTRY(nsIInputStreamCallback)
   NS_INTERFACE_MAP_ENTRY(nsICloneableInputStream)
   NS_INTERFACE_MAP_ENTRY(nsICloneableInputStreamWithRange)
   NS_INTERFACE_MAP_ENTRY(nsIIPCSerializableInputStream)
   NS_INTERFACE_MAP_ENTRY(nsIFileMetadata)
   NS_INTERFACE_MAP_ENTRY(nsIAsyncFileMetadata)
+  NS_INTERFACE_MAP_ENTRY(nsIInputStreamLength)
+  NS_INTERFACE_MAP_ENTRY(nsIAsyncInputStreamLength)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
 NS_INTERFACE_MAP_END
 
 IPCBlobInputStream::IPCBlobInputStream(IPCBlobInputStreamChild* aActor)
   : mActor(aActor)
   , mState(eInit)
   , mStart(0)
   , mLength(0)
+  , mConsumed(false)
   , mMutex("IPCBlobInputStream::mMutex")
 {
   MOZ_ASSERT(aActor);
 
   mLength = aActor->Size();
 
   if (XRE_IsParentProcess()) {
     nsCOMPtr<nsIInputStream> stream;
@@ -213,17 +216,27 @@ IPCBlobInputStream::Read(char* aBuffer, 
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     asyncRemoteStream = mAsyncRemoteStream;
   }
 
   MOZ_ASSERT(asyncRemoteStream);
-  return asyncRemoteStream->Read(aBuffer, aCount, aReadCount);
+  nsresult rv = asyncRemoteStream->Read(aBuffer, aCount, aReadCount);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  {
+    MutexAutoLock lock(mMutex);
+    mConsumed = true;
+  }
+
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 IPCBlobInputStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
                                  uint32_t aCount, uint32_t *aResult)
 {
   nsCOMPtr<nsIAsyncInputStream> asyncRemoteStream;
   {
@@ -245,17 +258,28 @@ IPCBlobInputStream::ReadSegments(nsWrite
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     asyncRemoteStream = mAsyncRemoteStream;
   }
 
   MOZ_ASSERT(asyncRemoteStream);
-  return asyncRemoteStream->ReadSegments(aWriter, aClosure, aCount, aResult);
+  nsresult rv = asyncRemoteStream->ReadSegments(aWriter, aClosure, aCount, aResult);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // If some data has been read, we mark the stream as consumed.
+  if (*aResult != 0) {
+    MutexAutoLock lock(mMutex);
+    mConsumed = true;
+  }
+
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 IPCBlobInputStream::IsNonBlocking(bool* aNonBlocking)
 {
   *aNonBlocking = true;
   return NS_OK;
 }
@@ -760,10 +784,147 @@ IPCBlobInputStream::EnsureAsyncRemoteStr
 
   MOZ_ASSERT(asyncStream);
   mAsyncRemoteStream = asyncStream;
   mRemoteStream = nullptr;
 
   return NS_OK;
 }
 
+// nsIInputStreamLength
+
+NS_IMETHODIMP
+IPCBlobInputStream::Length(int64_t* aLength)
+{
+  MutexAutoLock lock(mMutex);
+
+  if (mState == eClosed) {
+    return NS_BASE_STREAM_CLOSED;
+  }
+
+  if (mConsumed) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  return NS_BASE_STREAM_WOULD_BLOCK;
+}
+
+// nsIAsyncInputStreamLength
+
+NS_IMETHODIMP
+IPCBlobInputStream::AsyncLengthWait(nsIInputStreamLengthCallback* aCallback,
+                                    nsIEventTarget* aEventTarget)
+{
+  MutexAutoLock lock(mMutex);
+
+  if (mState == eClosed) {
+    return NS_BASE_STREAM_CLOSED;
+  }
+
+  if (mConsumed) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  // If we have the callback, we must have the event target.
+  if (NS_WARN_IF(!!aCallback != !!aEventTarget)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  MOZ_ASSERT(mActor);
+
+  mLengthCallback = aCallback;
+  mLengthCallbackEventTarget = aEventTarget;
+
+  if (aCallback) {
+    mActor->LengthNeeded(this, aEventTarget);
+  }
+
+  return NS_OK;
+}
+
+namespace {
+
+class InputStreamLengthCallbackRunnable final : public CancelableRunnable
+{
+public:
+  static void
+  Execute(nsIInputStreamLengthCallback* aCallback,
+          nsIEventTarget* aEventTarget,
+          IPCBlobInputStream* aStream,
+          int64_t aLength)
+  {
+    MOZ_ASSERT(aCallback);
+    MOZ_ASSERT(aEventTarget);
+
+    RefPtr<InputStreamLengthCallbackRunnable> runnable =
+      new InputStreamLengthCallbackRunnable(aCallback, aStream, aLength);
+
+    nsCOMPtr<nsIEventTarget> target = aEventTarget;
+    target->Dispatch(runnable, NS_DISPATCH_NORMAL);
+  }
+
+  NS_IMETHOD
+  Run() override
+  {
+    mCallback->OnInputStreamLengthReady(mStream, mLength);
+    mCallback = nullptr;
+    mStream = nullptr;
+    return NS_OK;
+  }
+
+private:
+  InputStreamLengthCallbackRunnable(nsIInputStreamLengthCallback* aCallback,
+                                    IPCBlobInputStream* aStream,
+                                    int64_t aLength)
+    : CancelableRunnable("dom::InputStreamLengthCallbackRunnable")
+    , mCallback(aCallback)
+    , mStream(aStream)
+    , mLength(aLength)
+  {
+    MOZ_ASSERT(mCallback);
+    MOZ_ASSERT(mStream);
+  }
+
+  nsCOMPtr<nsIInputStreamLengthCallback> mCallback;
+  RefPtr<IPCBlobInputStream> mStream;
+  int64_t mLength;
+};
+
+} // anonymous
+
+void
+IPCBlobInputStream::LengthReady(int64_t aLength)
+{
+  nsCOMPtr<nsIInputStreamLengthCallback> lengthCallback;
+  nsCOMPtr<nsIEventTarget> lengthCallbackEventTarget;
+
+  {
+    MutexAutoLock lock(mMutex);
+
+    // We have been closed in the meantime.
+    if (mState == eClosed || mConsumed) {
+      return;
+    }
+
+    if (mStart > 0) {
+      aLength -= mStart;
+    }
+
+    if (mLength < mActor->Size()) {
+      // If the remote stream must be sliced, we must return here the correct
+      // value.
+      aLength = XPCOM_MIN(aLength, (int64_t)mLength);
+    }
+
+    lengthCallback.swap(mLengthCallback);
+    lengthCallbackEventTarget.swap(mLengthCallbackEventTarget);
+  }
+
+  if (lengthCallback) {
+    InputStreamLengthCallbackRunnable::Execute(lengthCallback,
+                                               lengthCallbackEventTarget,
+                                               this,
+                                               aLength);
+  }
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/file/ipc/IPCBlobInputStream.h
+++ b/dom/file/ipc/IPCBlobInputStream.h
@@ -7,45 +7,53 @@
 #ifndef mozilla_dom_ipc_IPCBlobInputStream_h
 #define mozilla_dom_ipc_IPCBlobInputStream_h
 
 #include "mozilla/Mutex.h"
 #include "nsIAsyncInputStream.h"
 #include "nsICloneableInputStream.h"
 #include "nsIFileStreams.h"
 #include "nsIIPCSerializableInputStream.h"
+#include "nsIInputStreamLength.h"
 #include "nsCOMPtr.h"
 
 namespace mozilla {
 namespace dom {
 
 class IPCBlobInputStreamChild;
 
 class IPCBlobInputStream final : public nsIAsyncInputStream
                                , public nsIInputStreamCallback
                                , public nsICloneableInputStreamWithRange
                                , public nsIIPCSerializableInputStream
                                , public nsIAsyncFileMetadata
+                               , public nsIInputStreamLength
+                               , public nsIAsyncInputStreamLength
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIINPUTSTREAM
   NS_DECL_NSIASYNCINPUTSTREAM
   NS_DECL_NSIINPUTSTREAMCALLBACK
   NS_DECL_NSICLONEABLEINPUTSTREAM
   NS_DECL_NSICLONEABLEINPUTSTREAMWITHRANGE
   NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
   NS_DECL_NSIFILEMETADATA
   NS_DECL_NSIASYNCFILEMETADATA
+  NS_DECL_NSIINPUTSTREAMLENGTH
+  NS_DECL_NSIASYNCINPUTSTREAMLENGTH
 
   explicit IPCBlobInputStream(IPCBlobInputStreamChild* aActor);
 
   void
   StreamReady(already_AddRefed<nsIInputStream> aInputStream);
 
+  void
+  LengthReady(int64_t aLength);
+
 private:
   ~IPCBlobInputStream();
 
   nsresult
   EnsureAsyncRemoteStream(const MutexAutoLock& aProofOfLock);
 
   void
   InitWithExistingRange(uint64_t aStart, uint64_t aLength,
@@ -72,27 +80,35 @@ private:
     // mRemoveStream is released and any method will return
     // NS_BASE_STREAM_CLOSED.
     eClosed,
   } mState;
 
   uint64_t mStart;
   uint64_t mLength;
 
+  // Set to true if the stream is used via Read/ReadSegments or Close.
+  bool mConsumed;
+
   nsCOMPtr<nsIInputStream> mRemoteStream;
   nsCOMPtr<nsIAsyncInputStream> mAsyncRemoteStream;
 
   // These 2 values are set only if mState is ePending.
   nsCOMPtr<nsIInputStreamCallback> mInputStreamCallback;
   nsCOMPtr<nsIEventTarget> mInputStreamCallbackEventTarget;
 
   // These 2 values are set only if mState is ePending.
   nsCOMPtr<nsIFileMetadataCallback> mFileMetadataCallback;
   nsCOMPtr<nsIEventTarget> mFileMetadataCallbackEventTarget;
 
+  // These 2 values are set only when nsIAsyncInputStreamLength::asyncWait() is
+  // called.
+  nsCOMPtr<nsIInputStreamLengthCallback> mLengthCallback;
+  nsCOMPtr<nsIEventTarget> mLengthCallbackEventTarget;
+
   // Any member of this class is protected by mutex because touched on
   // multiple threads.
   Mutex mMutex;
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/file/ipc/IPCBlobInputStreamChild.cpp
+++ b/dom/file/ipc/IPCBlobInputStreamChild.cpp
@@ -84,16 +84,66 @@ public:
     return NS_OK;
   }
 
 private:
   RefPtr<IPCBlobInputStream> mDestinationStream;
   nsCOMPtr<nsIInputStream> mCreatedStream;
 };
 
+// This runnable is used in case LengthNeeded() has been called on a non-owning
+// thread.
+class LengthNeededRunnable final : public CancelableRunnable
+{
+public:
+  explicit LengthNeededRunnable(IPCBlobInputStreamChild* aActor)
+    : CancelableRunnable("dom::LengthNeededRunnable")
+    , mActor(aActor)
+  {}
+
+  NS_IMETHOD
+  Run() override
+  {
+    MOZ_ASSERT(mActor->State() != IPCBlobInputStreamChild::eActiveMigrating &&
+               mActor->State() != IPCBlobInputStreamChild::eInactiveMigrating);
+    if (mActor->State() == IPCBlobInputStreamChild::eActive) {
+      mActor->SendLengthNeeded();
+    }
+    return NS_OK;
+  }
+
+private:
+  RefPtr<IPCBlobInputStreamChild> mActor;
+};
+
+// When the stream has been received from the parent, we inform the
+// IPCBlobInputStream.
+class LengthReadyRunnable final : public CancelableRunnable
+{
+public:
+  LengthReadyRunnable(IPCBlobInputStream* aDestinationStream, int64_t aSize)
+    : CancelableRunnable("dom::LengthReadyRunnable")
+    , mDestinationStream(aDestinationStream)
+    , mSize(aSize)
+  {
+    MOZ_ASSERT(mDestinationStream);
+  }
+
+  NS_IMETHOD
+  Run() override
+  {
+    mDestinationStream->LengthReady(mSize);
+    return NS_OK;
+  }
+
+private:
+  RefPtr<IPCBlobInputStream> mDestinationStream;
+  int64_t mSize;
+};
+
 } // anonymous
 
 IPCBlobInputStreamChild::IPCBlobInputStreamChild(const nsID& aID,
                                                  uint64_t aSize)
   : mMutex("IPCBlobInputStreamChild::mMutex")
   , mID(aID)
   , mSize(aSize)
   , mState(eActive)
@@ -241,16 +291,17 @@ IPCBlobInputStreamChild::StreamNeeded(IP
     return;
   }
 
   MOZ_ASSERT(mStreams.Contains(aStream));
 
   PendingOperation* opt = mPendingOperations.AppendElement();
   opt->mStream = aStream;
   opt->mEventTarget = aEventTarget;
+  opt->mOp = PendingOperation::eStreamNeeded;
 
   if (mState == eActiveMigrating || mState == eInactiveMigrating) {
     // This operation will be continued when the migration is completed.
     return;
   }
 
   MOZ_ASSERT(mState == eActive);
 
@@ -273,16 +324,17 @@ IPCBlobInputStreamChild::RecvStreamReady
 
   {
     MutexAutoLock lock(mMutex);
     MOZ_ASSERT(!mPendingOperations.IsEmpty());
     MOZ_ASSERT(mState == eActive);
 
     pendingStream = mPendingOperations[0].mStream;
     eventTarget = mPendingOperations[0].mEventTarget;
+    MOZ_ASSERT(mPendingOperations[0].mOp == PendingOperation::eStreamNeeded);
 
     mPendingOperations.RemoveElementAt(0);
   }
 
   RefPtr<StreamReadyRunnable> runnable =
     new StreamReadyRunnable(pendingStream, stream.forget());
 
   // If IPCBlobInputStream::AsyncWait() has been executed without passing an
@@ -294,16 +346,75 @@ IPCBlobInputStreamChild::RecvStreamReady
   } else {
     runnable->Run();
   }
 
   return IPC_OK();
 }
 
 void
+IPCBlobInputStreamChild::LengthNeeded(IPCBlobInputStream* aStream,
+                                      nsIEventTarget* aEventTarget)
+{
+  MutexAutoLock lock(mMutex);
+
+  if (mState == eInactive) {
+    return;
+  }
+
+  MOZ_ASSERT(mStreams.Contains(aStream));
+
+  PendingOperation* opt = mPendingOperations.AppendElement();
+  opt->mStream = aStream;
+  opt->mEventTarget = aEventTarget;
+  opt->mOp = PendingOperation::eLengthNeeded;
+
+  if (mState == eActiveMigrating || mState == eInactiveMigrating) {
+    // This operation will be continued when the migration is completed.
+    return;
+  }
+
+  MOZ_ASSERT(mState == eActive);
+
+  if (mOwningEventTarget->IsOnCurrentThread()) {
+    SendLengthNeeded();
+    return;
+  }
+
+  RefPtr<LengthNeededRunnable> runnable = new LengthNeededRunnable(this);
+  mOwningEventTarget->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
+}
+
+mozilla::ipc::IPCResult
+IPCBlobInputStreamChild::RecvLengthReady(const int64_t& aLength)
+{
+  RefPtr<IPCBlobInputStream> pendingStream;
+  nsCOMPtr<nsIEventTarget> eventTarget;
+
+  {
+    MutexAutoLock lock(mMutex);
+    MOZ_ASSERT(!mPendingOperations.IsEmpty());
+    MOZ_ASSERT(mState == eActive);
+
+    pendingStream = mPendingOperations[0].mStream;
+    eventTarget = mPendingOperations[0].mEventTarget;
+    MOZ_ASSERT(mPendingOperations[0].mOp == PendingOperation::eLengthNeeded);
+
+    mPendingOperations.RemoveElementAt(0);
+  }
+
+  RefPtr<LengthReadyRunnable> runnable =
+    new LengthReadyRunnable(pendingStream, aLength);
+
+   MOZ_ASSERT(eventTarget);
+  eventTarget->Dispatch(runnable, NS_DISPATCH_NORMAL);
+
+  return IPC_OK();
+}
+void
 IPCBlobInputStreamChild::Migrated()
 {
   MutexAutoLock lock(mMutex);
   MOZ_ASSERT(mState == eInactiveMigrating);
 
   mWorkerRef = nullptr;
 
   mOwningEventTarget = GetCurrentThreadSerialEventTarget();
@@ -316,14 +427,19 @@ IPCBlobInputStreamChild::Migrated()
     return;
   }
 
   mState = eActive;
 
   // Let's processing the pending operations. We need a stream for each pending
   // operation.
   for (uint32_t i = 0; i < mPendingOperations.Length(); ++i) {
-    SendStreamNeeded();
+    if (mPendingOperations[i].mOp == PendingOperation::eStreamNeeded) {
+      SendStreamNeeded();
+    } else {
+      MOZ_ASSERT(mPendingOperations[i].mOp == PendingOperation::eLengthNeeded);
+      SendLengthNeeded();
+    }
   }
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/file/ipc/IPCBlobInputStreamChild.h
+++ b/dom/file/ipc/IPCBlobInputStreamChild.h
@@ -71,16 +71,23 @@ public:
   void
   StreamNeeded(IPCBlobInputStream* aStream,
                nsIEventTarget* aEventTarget);
 
   mozilla::ipc::IPCResult
   RecvStreamReady(const OptionalIPCStream& aStream) override;
 
   void
+  LengthNeeded(IPCBlobInputStream* aStream,
+               nsIEventTarget* aEventTarget);
+
+  mozilla::ipc::IPCResult
+  RecvLengthReady(const int64_t& aLength) override;
+
+  void
   Shutdown();
 
   void
   Migrated();
 
 private:
   ~IPCBlobInputStreamChild();
 
@@ -97,16 +104,21 @@ private:
 
   ActorState  mState;
 
   // This struct and the array are used for creating streams when needed.
   struct PendingOperation
   {
     RefPtr<IPCBlobInputStream> mStream;
     nsCOMPtr<nsIEventTarget> mEventTarget;
+    enum
+    {
+      eStreamNeeded,
+      eLengthNeeded,
+    } mOp;
   };
   nsTArray<PendingOperation> mPendingOperations;
 
   nsCOMPtr<nsISerialEventTarget> mOwningEventTarget;
 
   RefPtr<ThreadSafeWorkerRef> mWorkerRef;
 };
 
--- a/dom/file/ipc/IPCBlobInputStreamParent.cpp
+++ b/dom/file/ipc/IPCBlobInputStreamParent.cpp
@@ -2,50 +2,53 @@
 /* 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 "IPCBlobInputStreamParent.h"
 #include "IPCBlobInputStreamStorage.h"
 #include "mozilla/ipc/IPCStreamUtils.h"
+#include "mozilla/InputStreamLengthHelper.h"
 #include "nsContentUtils.h"
 
 namespace mozilla {
 namespace dom {
 
 template<typename M>
-/* static */ IPCBlobInputStreamParent*
+/* static */ already_AddRefed<IPCBlobInputStreamParent>
 IPCBlobInputStreamParent::Create(nsIInputStream* aInputStream, uint64_t aSize,
                                  uint64_t aChildID, nsresult* aRv, M* aManager)
 {
   MOZ_ASSERT(aInputStream);
   MOZ_ASSERT(aRv);
 
   nsID id;
   *aRv = nsContentUtils::GenerateUUIDInPlace(id);
   if (NS_WARN_IF(NS_FAILED(*aRv))) {
     return nullptr;
   }
 
   IPCBlobInputStreamStorage::Get()->AddStream(aInputStream, id, aSize, aChildID);
 
-  return new IPCBlobInputStreamParent(id, aSize, aManager);
+  RefPtr<IPCBlobInputStreamParent> parent =
+    new IPCBlobInputStreamParent(id, aSize, aManager);
+  return parent.forget();
 }
 
-/* static */ IPCBlobInputStreamParent*
+/* static */ already_AddRefed<IPCBlobInputStreamParent>
 IPCBlobInputStreamParent::Create(const nsID& aID, uint64_t aSize,
                                  PBackgroundParent* aManager)
 {
-  IPCBlobInputStreamParent* actor =
+  RefPtr<IPCBlobInputStreamParent> actor =
     new IPCBlobInputStreamParent(aID, aSize, aManager);
 
   actor->mCallback = IPCBlobInputStreamStorage::Get()->TakeCallback(aID);
 
-  return actor;
+  return actor.forget();
 }
 
 IPCBlobInputStreamParent::IPCBlobInputStreamParent(const nsID& aID,
                                                    uint64_t aSize,
                                                    nsIContentParent* aManager)
   : mID(aID)
   , mSize(aSize)
   , mContentManager(aManager)
@@ -136,16 +139,47 @@ IPCBlobInputStreamParent::RecvStreamNeed
   if (!SendStreamReady(ipcStream.TakeValue())) {
     return IPC_FAIL(this, "SendStreamReady failed");
   }
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
+IPCBlobInputStreamParent::RecvLengthNeeded()
+{
+  MOZ_ASSERT(mContentManager || mPBackgroundManager);
+
+  nsCOMPtr<nsIInputStream> stream;
+  IPCBlobInputStreamStorage::Get()->GetStream(mID, 0, mSize, getter_AddRefs(stream));
+  if (!stream) {
+    if (!SendLengthReady(-1)) {
+      return IPC_FAIL(this, "SendLengthReady failed");
+    }
+
+    return IPC_OK();
+  }
+
+  int64_t length = -1;
+  if (InputStreamLengthHelper::GetSyncLength(stream, &length)) {
+    Unused << SendLengthReady(length);
+    return IPC_OK();
+  }
+
+  RefPtr<IPCBlobInputStreamParent> self = this;
+  InputStreamLengthHelper::GetAsyncLength(stream, [self](int64_t aLength) {
+    if (self->mContentManager || self->mPBackgroundManager) {
+      Unused << self->SendLengthReady(aLength);
+    }
+  });
+
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
 IPCBlobInputStreamParent::RecvClose()
 {
   MOZ_ASSERT(mContentManager || mPBackgroundManager);
 
   Unused << Send__delete__(this);
   return IPC_OK();
 }
 
--- a/dom/file/ipc/IPCBlobInputStreamParent.h
+++ b/dom/file/ipc/IPCBlobInputStreamParent.h
@@ -26,25 +26,27 @@ protected:
   virtual ~IPCBlobInputStreamParentCallback()
   { }
 };
 
 class IPCBlobInputStreamParent final
   : public mozilla::ipc::PIPCBlobInputStreamParent
 {
 public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(IPCBlobInputStreamParent)
+
   // The size of the inputStream must be passed as argument in order to avoid
   // the use of nsIInputStream::Available() which could open a fileDescriptor in
   // case the stream is a nsFileStream.
   template<typename M>
-  static IPCBlobInputStreamParent*
+  static already_AddRefed<IPCBlobInputStreamParent>
   Create(nsIInputStream* aInputStream, uint64_t aSize,
          uint64_t aChildID, nsresult* aRv, M* aManager);
 
-  static IPCBlobInputStreamParent*
+  static already_AddRefed<IPCBlobInputStreamParent>
   Create(const nsID& aID, uint64_t aSize,
          mozilla::ipc::PBackgroundParent* aManager);
 
   void
   ActorDestroy(IProtocol::ActorDestroyReason aReason) override;
 
   const nsID&
   ID() const
@@ -60,31 +62,36 @@ public:
 
   void
   SetCallback(IPCBlobInputStreamParentCallback* aCallback);
 
   mozilla::ipc::IPCResult
   RecvStreamNeeded() override;
 
   mozilla::ipc::IPCResult
+  RecvLengthNeeded() override;
+
+  mozilla::ipc::IPCResult
   RecvClose() override;
 
   mozilla::ipc::IPCResult
   Recv__delete__() override;
 
   bool
   HasValidStream() const;
 
 private:
   IPCBlobInputStreamParent(const nsID& aID, uint64_t aSize,
                            nsIContentParent* aManager);
 
   IPCBlobInputStreamParent(const nsID& aID, uint64_t aSize,
                            mozilla::ipc::PBackgroundParent* aManager);
 
+  ~IPCBlobInputStreamParent() = default;
+
   const nsID mID;
   const uint64_t mSize;
 
   // Only 1 of these 2 is set. Raw pointer because these 2 managers are keeping
   // the parent actor alive. The pointers will be nullified in ActorDestroyed.
   nsIContentParent* mContentManager;
   mozilla::ipc::PBackgroundParent* mPBackgroundManager;
 
--- a/dom/file/ipc/IPCBlobUtils.cpp
+++ b/dom/file/ipc/IPCBlobUtils.cpp
@@ -85,23 +85,28 @@ template<typename M>
 nsresult
 SerializeInputStreamParent(nsIInputStream* aInputStream, uint64_t aSize,
                            uint64_t aChildID, IPCBlob& aIPCBlob, M* aManager)
 {
   // Parent to Child we always send a IPCBlobInputStream.
   MOZ_ASSERT(XRE_IsParentProcess());
 
   nsresult rv;
-  IPCBlobInputStreamParent* parentActor =
+  RefPtr<IPCBlobInputStreamParent> parentActor =
     IPCBlobInputStreamParent::Create(aInputStream, aSize, aChildID, &rv,
                                      aManager);
   if (!parentActor) {
     return rv;
   }
 
+  // We need manually to increase the reference for this actor because the
+  // IPC allocator method is not triggered. The Release() is called by IPDL
+  // when the actor is deleted.
+  parentActor.get()->AddRef();
+
   if (!aManager->SendPIPCBlobInputStreamConstructor(parentActor,
                                                     parentActor->ID(),
                                                     parentActor->Size())) {
     return NS_ERROR_FAILURE;
   }
 
   aIPCBlob.inputStream() = parentActor;
   return NS_OK;
--- a/dom/file/ipc/PIPCBlobInputStream.ipdl
+++ b/dom/file/ipc/PIPCBlobInputStream.ipdl
@@ -16,23 +16,27 @@ namespace ipc {
 
 protocol PIPCBlobInputStream
 {
   manager PBackground or PContent or PContentBridge;
 
 parent:
   async StreamNeeded();
 
+  async LengthNeeded();
+
   // When this is called, the parent releases the inputStream and sends a
   // __delete__.
   async Close();
 
 child:
   async StreamReady(OptionalIPCStream aStream);
 
+  async LengthReady(int64_t aLength);
+
 both:
   // __delete__ can be called by parent and by child for 2 reasons:
   // - parent->child: This happens after a Close(). The child wants to inform
   //                  the parent that no other messages will be dispatched and
   //                  that the channel can be interrupted.
   // - child->parent: before any operation, the child could start a migration
   //                  from the current thread to a dedicated DOM-File one. The
   //                  reason why a __delete__ is sent from child to parent is
--- a/dom/html/HTMLFormElement.cpp
+++ b/dom/html/HTMLFormElement.cpp
@@ -766,27 +766,25 @@ HTMLFormElement::SubmitSubmission(HTMLFo
   {
     nsAutoPopupStatePusher popupStatePusher(mSubmitPopupState);
 
     AutoHandlingUserInputStatePusher userInpStatePusher(
                                        mSubmitInitiatedFromUserInput,
                                        nullptr, doc);
 
     nsCOMPtr<nsIInputStream> postDataStream;
-    int64_t postDataStreamLength = -1;
     rv = aFormSubmission->GetEncodedSubmission(actionURI,
                                                getter_AddRefs(postDataStream),
-                                               &postDataStreamLength,
                                                actionURI);
     NS_ENSURE_SUBMIT_SUCCESS(rv);
 
     rv = linkHandler->OnLinkClickSync(this, actionURI,
                                       target.get(),
                                       VoidString(),
-                                      postDataStream, postDataStreamLength,
+                                      postDataStream,
                                       nullptr, false,
                                       getter_AddRefs(docShell),
                                       getter_AddRefs(mSubmittingRequest),
                                       EventStateManager::IsHandlingUserInput());
     NS_ENSURE_SUBMIT_SUCCESS(rv);
   }
 
   // Even if the submit succeeds, it's possible for there to be no docshell
--- a/dom/html/HTMLFormSubmission.cpp
+++ b/dom/html/HTMLFormSubmission.cpp
@@ -109,17 +109,17 @@ public:
   virtual nsresult
   AddNameBlobOrNullPair(const nsAString& aName, Blob* aBlob) override;
 
   virtual nsresult
   AddNameDirectoryPair(const nsAString& aName, Directory* aDirectory) override;
 
   virtual nsresult
   GetEncodedSubmission(nsIURI* aURI, nsIInputStream** aPostDataStream,
-                       int64_t* aPostDataStreamLength, nsCOMPtr<nsIURI>& aOutURI) override;
+                       nsCOMPtr<nsIURI>& aOutURI) override;
 
 protected:
 
   /**
    * URL encode a Unicode string by encoding it to bytes, converting linebreaks
    * properly, and then escaping many bytes as %xx.
    *
    * @param aStr the string to encode
@@ -264,24 +264,22 @@ HandleMailtoSubject(nsCString& aPath)
 
     aPath.Append(subjectStrEscaped);
   }
 }
 
 nsresult
 FSURLEncoded::GetEncodedSubmission(nsIURI* aURI,
                                    nsIInputStream** aPostDataStream,
-                                   int64_t* aPostDataStreamLength,
                                    nsCOMPtr<nsIURI>& aOutURI)
 {
   nsresult rv = NS_OK;
   aOutURI = aURI;
 
   *aPostDataStream = nullptr;
-  *aPostDataStreamLength = -1;
 
   if (mMethod == NS_FORM_METHOD_POST) {
 
     bool isMailto = false;
     aURI->SchemeIs("mailto", &isMailto);
     if (isMailto) {
 
       nsAutoCString path;
@@ -297,33 +295,30 @@ FSURLEncoded::GetEncodedSubmission(nsIUR
       }
 
       path += NS_LITERAL_CSTRING("&force-plain-text=Y&body=") + escapedBody;
 
       return NS_MutateURI(aURI)
                .SetPathQueryRef(path)
                .Finalize(aOutURI);
     } else {
-      uint32_t queryStringLength = mQueryString.Length();
       nsCOMPtr<nsIInputStream> dataStream;
       rv = NS_NewCStringInputStream(getter_AddRefs(dataStream), Move(mQueryString));
       NS_ENSURE_SUCCESS(rv, rv);
       mQueryString.Truncate();
 
       nsCOMPtr<nsIMIMEInputStream> mimeStream(
         do_CreateInstance("@mozilla.org/network/mime-input-stream;1", &rv));
       NS_ENSURE_SUCCESS(rv, rv);
 
       mimeStream->AddHeader("Content-Type",
                             "application/x-www-form-urlencoded");
       mimeStream->SetData(dataStream);
 
       mimeStream.forget(aPostDataStream);
-
-      *aPostDataStreamLength = queryStringLength;
     }
 
   } else {
     // Get the full query string
     bool schemeIsJavaScript;
     rv = aURI->SchemeIs("javascript", &schemeIsJavaScript);
     NS_ENSURE_SUCCESS(rv, rv);
     if (schemeIsJavaScript) {
@@ -621,34 +616,32 @@ FSMultipartFormData::AddDataChunk(const 
 
   // CRLF after file
   mPostDataChunk.AppendLiteral(CRLF);
 }
 
 nsresult
 FSMultipartFormData::GetEncodedSubmission(nsIURI* aURI,
                                           nsIInputStream** aPostDataStream,
-                                          int64_t* aPostDataStreamLength,
                                           nsCOMPtr<nsIURI>& aOutURI)
 {
   nsresult rv;
   aOutURI = aURI;
 
   // Make header
   nsCOMPtr<nsIMIMEInputStream> mimeStream
     = do_CreateInstance("@mozilla.org/network/mime-input-stream;1", &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsAutoCString contentType;
   GetContentType(contentType);
   mimeStream->AddHeader("Content-Type", contentType.get());
 
   uint64_t bodySize;
   mimeStream->SetData(GetSubmissionBody(&bodySize));
-  *aPostDataStreamLength = bodySize;
 
   mimeStream.forget(aPostDataStream);
 
   return NS_OK;
 }
 
 nsresult
 FSMultipartFormData::AddPostDataStream()
@@ -688,17 +681,17 @@ public:
   virtual nsresult
   AddNameBlobOrNullPair(const nsAString& aName, Blob* aBlob) override;
 
   virtual nsresult
   AddNameDirectoryPair(const nsAString& aName, Directory* aDirectory) override;
 
   virtual nsresult
   GetEncodedSubmission(nsIURI* aURI, nsIInputStream** aPostDataStream,
-                       int64_t* aPostDataStreaLength, nsCOMPtr<nsIURI>& aOutURI) override;
+                       nsCOMPtr<nsIURI>& aOutURI) override;
 
 private:
   nsString mBody;
 };
 
 nsresult
 FSTextPlain::AddNameValuePair(const nsAString& aName, const nsAString& aValue)
 {
@@ -728,24 +721,22 @@ FSTextPlain::AddNameDirectoryPair(const 
   RetrieveDirectoryName(aDirectory, dirname);
   AddNameValuePair(aName, dirname);
   return NS_OK;
 }
 
 nsresult
 FSTextPlain::GetEncodedSubmission(nsIURI* aURI,
                                   nsIInputStream** aPostDataStream,
-                                  int64_t* aPostDataStreamLength,
                                   nsCOMPtr<nsIURI>& aOutURI)
 {
   nsresult rv = NS_OK;
   aOutURI = aURI;
 
   *aPostDataStream = nullptr;
-  *aPostDataStreamLength = -1;
 
   // XXX HACK We are using the standard URL mechanism to give the body to the
   // mailer instead of passing the post data stream to it, since that sounds
   // hard.
   bool isMailto = false;
   aURI->SchemeIs("mailto", &isMailto);
   if (isMailto) {
     nsAutoCString path;
@@ -774,33 +765,30 @@ FSTextPlain::GetEncodedSubmission(nsIURI
     // values which contains '=' or newlines are potentially ambigiously
     // encoded, but that how text/plain is specced.
     nsCString cbody;
     EncodeVal(mBody, cbody, false);
     cbody.Adopt(nsLinebreakConverter::
                 ConvertLineBreaks(cbody.get(),
                                   nsLinebreakConverter::eLinebreakAny,
                                   nsLinebreakConverter::eLinebreakNet));
-    uint32_t bodyLength = cbody.Length();
     nsCOMPtr<nsIInputStream> bodyStream;
     rv = NS_NewCStringInputStream(getter_AddRefs(bodyStream), Move(cbody));
     if (!bodyStream) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
 
     // Create mime stream with headers and such
     nsCOMPtr<nsIMIMEInputStream> mimeStream
         = do_CreateInstance("@mozilla.org/network/mime-input-stream;1", &rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
     mimeStream->AddHeader("Content-Type", "text/plain");
     mimeStream->SetData(bodyStream);
     CallQueryInterface(mimeStream, aPostDataStream);
-
-    *aPostDataStreamLength = bodyLength;
   }
 
   return rv;
 }
 
 } // anonymous namespace
 
 // --------------------------------------------------------------------------
--- a/dom/html/HTMLFormSubmission.h
+++ b/dom/html/HTMLFormSubmission.h
@@ -79,22 +79,20 @@ public:
                                         Directory* aDirectory) = 0;
 
   /**
    * Given a URI and the current submission, create the final URI and data
    * stream that will be submitted.  Subclasses *must* implement this.
    *
    * @param aURI the URI being submitted to [IN]
    * @param aPostDataStream a data stream for POST data [OUT]
-   * @param aPostDataStreamLength a data stream for POST data length [OUT]
    * @param aOutURI the resulting URI. May be the same as aURI [OUT]
    */
   virtual nsresult
   GetEncodedSubmission(nsIURI* aURI, nsIInputStream** aPostDataStream,
-                       int64_t* aPostDataStreamLength,
                        nsCOMPtr<nsIURI>& aOutURI) = 0;
 
   /**
    * Get the charset that will be used for submission.
    */
   void GetCharset(nsACString& aCharset) { mEncoding->Name(aCharset); }
 
   Element* GetOriginatingElement() const
@@ -165,17 +163,17 @@ public:
   virtual nsresult
   AddNameBlobOrNullPair(const nsAString& aName, Blob* aBlob) override;
 
   virtual nsresult
   AddNameDirectoryPair(const nsAString& aName, Directory* aDirectory) override;
 
   virtual nsresult
   GetEncodedSubmission(nsIURI* aURI, nsIInputStream** aPostDataStream,
-                       int64_t* aPostDataStreamLength, nsCOMPtr<nsIURI>& aOutURI) override;
+                       nsCOMPtr<nsIURI>& aOutURI) override;
 
   void GetContentType(nsACString& aContentType)
   {
     aContentType =
       NS_LITERAL_CSTRING("multipart/form-data; boundary=") + mBoundary;
   }
 
   nsIInputStream* GetSubmissionBody(uint64_t* aContentLength);
--- a/dom/ipc/nsIContentParent.cpp
+++ b/dom/ipc/nsIContentParent.cpp
@@ -231,17 +231,18 @@ nsIContentParent::AllocPIPCBlobInputStre
 {
   MOZ_CRASH("PIPCBlobInputStreamParent actors should be manually constructed!");
   return nullptr;
 }
 
 bool
 nsIContentParent::DeallocPIPCBlobInputStreamParent(PIPCBlobInputStreamParent* aActor)
 {
-  delete aActor;
+  RefPtr<IPCBlobInputStreamParent> actor =
+    dont_AddRef(static_cast<IPCBlobInputStreamParent*>(aActor));
   return true;
 }
 
 mozilla::ipc::IPCResult
 nsIContentParent::RecvSyncMessage(const nsString& aMsg,
                                   const ClonedMessageData& aData,
                                   InfallibleTArray<CpowEntry>&& aCpows,
                                   const IPC::Principal& aPrincipal,
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -480,17 +480,17 @@ NS_IMETHODIMP nsPluginInstanceOwner::Get
   nsCOMPtr<nsIPrincipal> triggeringPrincipal;
   if (!aDoCheckLoadURIChecks) {
     mozilla::OriginAttributes attrs =
       BasePrincipal::Cast(content->NodePrincipal())->OriginAttributesRef();
     triggeringPrincipal = BasePrincipal::CreateCodebasePrincipal(uri, attrs);
   }
 
   rv = lh->OnLinkClick(content, uri, unitarget.get(), VoidString(),
-                       aPostStream, -1, headersDataStream,
+                       aPostStream, headersDataStream,
                        /* isUserTriggered */ false,
                        /* isTrusted */ true, triggeringPrincipal);
 
   return rv;
 }
 
 NS_IMETHODIMP nsPluginInstanceOwner::GetDocument(nsIDocument* *aDocument)
 {
--- a/dom/script/ScriptLoader.cpp
+++ b/dom/script/ScriptLoader.cpp
@@ -780,25 +780,54 @@ HostResolveImportedModule(JSContext* aCx
   MOZ_ASSERT(ms, "Resolved module not found in module map");
 
   MOZ_ASSERT(!ms->HasParseError());
   MOZ_ASSERT(ms->ModuleRecord());
 
   return ms->ModuleRecord();
 }
 
+bool
+HostPopulateImportMeta(JSContext* aCx, JS::Handle<JSObject*> aModule,
+                       JS::Handle<JSObject*> aMetaObject)
+{
+  MOZ_DIAGNOSTIC_ASSERT(aModule);
+
+  JS::Value value = JS::GetModuleHostDefinedField(aModule);
+  if (value.isUndefined()) {
+    JS_ReportErrorASCII(aCx, "Module script not found");
+    return false;
+  }
+
+  auto script = static_cast<ModuleScript*>(value.toPrivate());
+  MOZ_DIAGNOSTIC_ASSERT(script->ModuleRecord() == aModule);
+
+  nsAutoCString url;
+  MOZ_DIAGNOSTIC_ASSERT(script->BaseURL());
+  MOZ_ALWAYS_SUCCEEDS(script->BaseURL()->GetAsciiSpec(url));
+
+  JS::Rooted<JSString*> urlString(aCx, JS_NewStringCopyZ(aCx, url.get()));
+  if (!urlString) {
+    JS_ReportOutOfMemory(aCx);
+    return false;
+  }
+
+  return JS_DefineProperty(aCx, aMetaObject, "url", urlString, JSPROP_ENUMERATE);
+}
+
 static void
 EnsureModuleResolveHook(JSContext* aCx)
 {
   JSRuntime* rt = JS_GetRuntime(aCx);
   if (JS::GetModuleResolveHook(rt)) {
     return;
   }
 
   JS::SetModuleResolveHook(rt, HostResolveImportedModule);
+  JS::SetModuleMetadataHook(rt, HostPopulateImportMeta);
 }
 
 void
 ScriptLoader::CheckModuleDependenciesLoaded(ModuleLoadRequest* aRequest)
 {
   LOG(("ScriptLoadRequest (%p): Check dependencies loaded", aRequest));
 
   RefPtr<ModuleScript> moduleScript = aRequest->mModuleScript;
--- a/dom/script/ScriptLoader.h
+++ b/dom/script/ScriptLoader.h
@@ -52,17 +52,19 @@ class ScriptLoader final : public nsISup
   class MOZ_STACK_CLASS AutoCurrentScriptUpdater
   {
   public:
     AutoCurrentScriptUpdater(ScriptLoader* aScriptLoader,
                              nsIScriptElement* aCurrentScript)
       : mOldScript(aScriptLoader->mCurrentScript)
       , mScriptLoader(aScriptLoader)
     {
-      mScriptLoader->mCurrentScript = aCurrentScript;
+      nsCOMPtr<nsINode> node = do_QueryInterface(aCurrentScript);
+      mScriptLoader->mCurrentScript =
+        node && !node->IsInShadowTree() ? aCurrentScript : nullptr;
     }
 
     ~AutoCurrentScriptUpdater()
     {
       mScriptLoader->mCurrentScript.swap(mOldScript);
     }
 
   private:
--- a/gfx/layers/apz/test/reftest/reftest.list
+++ b/gfx/layers/apz/test/reftest/reftest.list
@@ -1,22 +1,22 @@
 # The following tests test the async positioning of the scrollbars.
 # Basic root-frame scrollbar with async scrolling
-fuzzy-if(Android,43-43,8-8) skip-if(!Android) pref(apz.allow_zooming,true) == async-scrollbar-1-v.html async-scrollbar-1-v-ref.html # bug 1392259
+fuzzy-if(Android,1-1,2-2) skip-if(!Android) pref(apz.allow_zooming,true) == async-scrollbar-1-v.html async-scrollbar-1-v-ref.html # bug 1392259
 fuzzy-if(Android,4,5) skip-if(!Android) pref(apz.allow_zooming,true) == async-scrollbar-1-h.html async-scrollbar-1-h-ref.html
 fuzzy-if(Android,3,5) skip-if(!Android) pref(apz.allow_zooming,true) == async-scrollbar-1-vh.html async-scrollbar-1-vh-ref.html
-fuzzy-if(Android,43-43,8-8) skip-if(!Android) pref(apz.allow_zooming,true) == async-scrollbar-1-v-rtl.html async-scrollbar-1-v-rtl-ref.html # bug 1392259
+fuzzy-if(Android,1-1,2-2) skip-if(!Android) pref(apz.allow_zooming,true) == async-scrollbar-1-v-rtl.html async-scrollbar-1-v-rtl-ref.html # bug 1392259
 fuzzy-if(Android,4,5) skip-if(!Android) pref(apz.allow_zooming,true) == async-scrollbar-1-h-rtl.html async-scrollbar-1-h-rtl-ref.html
 fuzzy-if(Android,3,7) skip-if(!Android) pref(apz.allow_zooming,true) == async-scrollbar-1-vh-rtl.html async-scrollbar-1-vh-rtl-ref.html
 
 # Different async zoom levels. Since the scrollthumb gets async-scaled in the
 # compositor, the border-radius ends of the scrollthumb are going to be a little
 # off, hence the fuzzy-if clauses.
 fuzzy-if(Android,54,20) skip-if(!Android) pref(apz.allow_zooming,true) == async-scrollbar-zoom-1.html async-scrollbar-zoom-1-ref.html
-fuzzy-if(Android,45,22) skip-if(!Android) pref(apz.allow_zooming,true) == async-scrollbar-zoom-2.html async-scrollbar-zoom-2-ref.html
+fuzzy-if(Android,45,26) skip-if(!Android) pref(apz.allow_zooming,true) == async-scrollbar-zoom-2.html async-scrollbar-zoom-2-ref.html
 
 # Test scrollbars working properly with pinch-zooming, i.e. different document resolutions.
 # As above, the end of the scrollthumb won't match perfectly, but the bulk of the scrollbar should be present and identical.
 fuzzy-if(Android,54,14) skip-if(!Android) pref(apz.allow_zooming,true) == scrollbar-zoom-resolution-1.html scrollbar-zoom-resolution-1-ref.html
 fuzzy-if(Android,51,22) skip-if(!Android) pref(apz.allow_zooming,true) == scrollbar-zoom-resolution-2.html scrollbar-zoom-resolution-2-ref.html
 
 # Meta-viewport tag support
 skip-if(!Android) pref(apz.allow_zooming,true) == initial-scale-1.html initial-scale-1-ref.html
--- a/ipc/glue/BackgroundParentImpl.cpp
+++ b/ipc/glue/BackgroundParentImpl.cpp
@@ -349,17 +349,19 @@ BackgroundParentImpl::DeallocPTemporaryI
 
 PIPCBlobInputStreamParent*
 BackgroundParentImpl::AllocPIPCBlobInputStreamParent(const nsID& aID,
                                                      const uint64_t& aSize)
 {
   AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
 
-  return mozilla::dom::IPCBlobInputStreamParent::Create(aID, aSize, this);
+  RefPtr<mozilla::dom::IPCBlobInputStreamParent> actor =
+    mozilla::dom::IPCBlobInputStreamParent::Create(aID, aSize, this);
+  return actor.forget().take();
 }
 
 mozilla::ipc::IPCResult
 BackgroundParentImpl::RecvPIPCBlobInputStreamConstructor(PIPCBlobInputStreamParent* aActor,
                                                          const nsID& aID,
                                                          const uint64_t& aSize)
 {
   if (!static_cast<mozilla::dom::IPCBlobInputStreamParent*>(aActor)->HasValidStream()) {
@@ -371,17 +373,18 @@ BackgroundParentImpl::RecvPIPCBlobInputS
 
 bool
 BackgroundParentImpl::DeallocPIPCBlobInputStreamParent(PIPCBlobInputStreamParent* aActor)
 {
   AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
-  delete aActor;
+  RefPtr<mozilla::dom::IPCBlobInputStreamParent> actor =
+    dont_AddRef(static_cast<mozilla::dom::IPCBlobInputStreamParent*>(aActor));
   return true;
 }
 
 PFileDescriptorSetParent*
 BackgroundParentImpl::AllocPFileDescriptorSetParent(
                                           const FileDescriptor& aFileDescriptor)
 {
   AssertIsInMainProcess();
--- a/js/src/builtin/Array.cpp
+++ b/js/src/builtin/Array.cpp
@@ -1083,17 +1083,17 @@ IsArraySpecies(JSContext* cx, HandleObje
         }
         return false;
     }
 
     // 9.4.2.3 Step 4. Non-array objects always use the default constructor.
     if (!origArray->is<ArrayObject>())
         return true;
 
-    if (cx->compartment()->arraySpeciesLookup.tryOptimizeArray(cx, &origArray->as<ArrayObject>()))
+    if (cx->realm()->arraySpeciesLookup.tryOptimizeArray(cx, &origArray->as<ArrayObject>()))
         return true;
 
     Value ctor;
     if (!GetPropertyPure(cx, origArray, NameToId(cx->names().constructor), &ctor))
         return false;
 
     if (!IsArrayConstructor(ctor))
         return ctor.isUndefined();
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -628,17 +628,17 @@ MapObject::set(JSContext* cx, HandleObje
 
     return true;
 }
 
 MapObject*
 MapObject::create(JSContext* cx, HandleObject proto /* = nullptr */)
 {
     auto map = cx->make_unique<ValueMap>(cx->zone(),
-                                         cx->compartment()->randomHashCodeScrambler());
+                                         cx->realm()->randomHashCodeScrambler());
     if (!map || !map->init()) {
         ReportOutOfMemory(cx);
         return nullptr;
     }
 
     MapObject* mapObj = NewObjectWithClassProto<MapObject>(cx,  proto);
     if (!mapObj)
         return nullptr;
@@ -1311,17 +1311,17 @@ SetObject::add(JSContext* cx, HandleObje
     }
     return true;
 }
 
 SetObject*
 SetObject::create(JSContext* cx, HandleObject proto /* = nullptr */)
 {
     auto set = cx->make_unique<ValueSet>(cx->zone(),
-                                         cx->compartment()->randomHashCodeScrambler());
+                                         cx->realm()->randomHashCodeScrambler());
     if (!set || !set->init()) {
         ReportOutOfMemory(cx);
         return nullptr;
     }
 
     SetObject* obj = NewObjectWithClassProto<SetObject>(cx, proto);
     if (!obj)
         return nullptr;
--- a/js/src/builtin/ModuleObject.cpp
+++ b/js/src/builtin/ModuleObject.cpp
@@ -996,16 +996,35 @@ ModuleObject::hadEvaluationError() const
 
 Value
 ModuleObject::evaluationError() const
 {
     MOZ_ASSERT(hadEvaluationError());
     return getReservedSlot(EvaluationErrorSlot);
 }
 
+JSObject*
+ModuleObject::metaObject() const
+{
+    Value value = getReservedSlot(MetaObjectSlot);
+    if (value.isObject())
+        return &value.toObject();
+
+    MOZ_ASSERT(value.isUndefined());
+    return nullptr;
+}
+
+void
+ModuleObject::setMetaObject(JSObject* obj)
+{
+    MOZ_ASSERT(obj);
+    MOZ_ASSERT(!metaObject());
+    setReservedSlot(MetaObjectSlot, ObjectValue(*obj));
+}
+
 Value
 ModuleObject::hostDefinedField() const
 {
     return getReservedSlot(HostDefinedSlot);
 }
 
 void
 ModuleObject::setHostDefinedField(const JS::Value& value)
@@ -1618,8 +1637,29 @@ ArrayObject* ModuleBuilder::createArray(
     array->setDenseInitializedLength(length);
 
     uint32_t i = 0;
     for (auto r = map.all(); !r.empty(); r.popFront())
         array->initDenseElement(i++, ObjectValue(*r.front().value()));
 
     return array;
 }
+
+JSObject*
+js::GetOrCreateModuleMetaObject(JSContext* cx, HandleObject moduleArg)
+{
+    HandleModuleObject module = moduleArg.as<ModuleObject>();
+    if (JSObject* obj = module->metaObject())
+        return obj;
+
+    RootedObject metaObject(cx, NewObjectWithGivenProto<PlainObject>(cx, nullptr));
+    if (!metaObject)
+        return nullptr;
+
+    JS::ModuleMetadataHook func = cx->runtime()->moduleMetadataHook;
+    MOZ_ASSERT(func);
+    if (!func(cx, module, metaObject))
+        return nullptr;
+
+    module->setMetaObject(metaObject);
+
+    return metaObject;
+}
--- a/js/src/builtin/ModuleObject.h
+++ b/js/src/builtin/ModuleObject.h
@@ -249,16 +249,17 @@ class ModuleObject : public NativeObject
   public:
     enum ModuleSlot
     {
         ScriptSlot = 0,
         EnvironmentSlot,
         NamespaceSlot,
         StatusSlot,
         EvaluationErrorSlot,
+        MetaObjectSlot,
         HostDefinedSlot,
         RequestedModulesSlot,
         ImportEntriesSlot,
         LocalExportEntriesSlot,
         IndirectExportEntriesSlot,
         StarExportEntriesSlot,
         ImportBindingsSlot,
         FunctionDeclarationsSlot,
@@ -299,27 +300,30 @@ class ModuleObject : public NativeObject
     JSScript* script() const;
     Scope* enclosingScope() const;
     ModuleEnvironmentObject& initialEnvironment() const;
     ModuleEnvironmentObject* environment() const;
     ModuleNamespaceObject* namespace_();
     ModuleStatus status() const;
     bool hadEvaluationError() const;
     Value evaluationError() const;
+    JSObject* metaObject() const;
     Value hostDefinedField() const;
     ArrayObject& requestedModules() const;
     ArrayObject& importEntries() const;
     ArrayObject& localExportEntries() const;
     ArrayObject& indirectExportEntries() const;
     ArrayObject& starExportEntries() const;
     IndirectBindingMap& importBindings();
 
     static bool Instantiate(JSContext* cx, HandleModuleObject self);
     static bool Evaluate(JSContext* cx, HandleModuleObject self);
 
+    void setMetaObject(JSObject* obj);
+
     void setHostDefinedField(const JS::Value& value);
 
     // For BytecodeEmitter.
     bool noteFunctionDeclaration(JSContext* cx, HandleAtom name, HandleFunction fun);
 
     // For intrinsic_InstantiateModuleFunctionDeclarations.
     static bool instantiateFunctionDeclarations(JSContext* cx, HandleModuleObject self);
 
@@ -402,16 +406,19 @@ class MOZ_STACK_CLASS ModuleBuilder
     bool maybeAppendRequestedModule(HandleAtom specifier, frontend::ParseNode* node);
 
     template <typename T>
     ArrayObject* createArray(const JS::Rooted<GCVector<T>>& vector);
     template <typename K, typename V>
     ArrayObject* createArray(const JS::Rooted<GCHashMap<K, V>>& map);
 };
 
+JSObject*
+GetOrCreateModuleMetaObject(JSContext* cx, HandleObject module);
+
 } // namespace js
 
 template<>
 inline bool
 JSObject::is<js::ModuleNamespaceObject>() const
 {
     return js::IsDerivedProxyObject(this, &js::ModuleNamespaceObject::proxyHandler);
 }
--- a/js/src/builtin/Promise.cpp
+++ b/js/src/builtin/Promise.cpp
@@ -159,17 +159,17 @@ NewPromiseAllDataHolder(JSContext* cx, H
 namespace {
 // Generator used by PromiseObject::getID.
 mozilla::Atomic<uint64_t> gIDGenerator(0);
 } // namespace
 
 static MOZ_ALWAYS_INLINE bool
 ShouldCaptureDebugInfo(JSContext* cx)
 {
-    return cx->options().asyncStack() || cx->compartment()->isDebuggee();
+    return cx->options().asyncStack() || cx->realm()->isDebuggee();
 }
 
 class PromiseDebugInfo : public NativeObject
 {
   private:
     enum Slots {
         Slot_AllocationSite,
         Slot_ResolutionSite,
@@ -3062,18 +3062,18 @@ IsPromiseThenOrCatchRetValImplicitlyUsed
     // used explicitly in the script, the stack info is observable in devtools
     // and profilers.  We shouldn't apply the optimization not to allocate the
     // returned Promise object if the it's implicitly used by them.
     //
     // FIXME: Once bug 1280819 gets fixed, we can use ShouldCaptureDebugInfo.
     if (!cx->options().asyncStack())
         return false;
 
-    // If devtools is opened, the current compartment will become debuggee.
-    if (cx->compartment()->isDebuggee())
+    // If devtools is opened, the current realm will become debuggee.
+    if (cx->realm()->isDebuggee())
         return true;
 
     // There are 2 profilers, and they can be independently enabled.
     if (cx->runtime()->geckoProfiler().enabled())
         return true;
     if (JS::IsProfileTimelineRecordingEnabled())
         return true;
 
--- a/js/src/builtin/ReflectParse.cpp
+++ b/js/src/builtin/ReflectParse.cpp
@@ -28,22 +28,16 @@
 
 using namespace js;
 using namespace js::frontend;
 
 using JS::AutoValueArray;
 using mozilla::DebugOnly;
 using mozilla::Forward;
 
-enum class ParseTarget
-{
-    Script,
-    Module
-};
-
 enum ASTType {
     AST_ERROR = -1,
 #define ASTDEF(ast, str, method) ast,
 #include "jsast.tbl"
 #undef ASTDEF
     AST_LIMIT
 };
 
@@ -2923,31 +2917,40 @@ ASTSerializer::expression(ParseNode* pn,
         return optExpression(pn->pn_kid, &arg) &&
                builder.yieldExpression(arg, NotDelegating, &pn->pn_pos, dst);
       }
 
       case ParseNodeKind::Class:
         return classDefinition(pn, true, dst);
 
       case ParseNodeKind::NewTarget:
+      case ParseNodeKind::ImportMeta:
       {
         MOZ_ASSERT(pn->pn_left->isKind(ParseNodeKind::PosHolder));
         MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos));
         MOZ_ASSERT(pn->pn_right->isKind(ParseNodeKind::PosHolder));
         MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos));
 
-        RootedValue newIdent(cx);
-        RootedValue targetIdent(cx);
-
-        RootedAtom newStr(cx, cx->names().new_);
-        RootedAtom targetStr(cx, cx->names().target);
-
-        return identifier(newStr, &pn->pn_left->pn_pos, &newIdent) &&
-               identifier(targetStr, &pn->pn_right->pn_pos, &targetIdent) &&
-               builder.metaProperty(newIdent, targetIdent, &pn->pn_pos, dst);
+        RootedValue firstIdent(cx);
+        RootedValue secondIdent(cx);
+
+        RootedAtom firstStr(cx);
+        RootedAtom secondStr(cx);
+
+        if (pn->getKind() == ParseNodeKind::NewTarget) {
+            firstStr = cx->names().new_;
+            secondStr = cx->names().target;
+        } else {
+            firstStr = cx->names().import;
+            secondStr = cx->names().meta;
+        }
+
+        return identifier(firstStr, &pn->pn_left->pn_pos, &firstIdent) &&
+               identifier(secondStr, &pn->pn_right->pn_pos, &secondIdent) &&
+               builder.metaProperty(firstIdent, secondIdent, &pn->pn_pos, dst);
       }
 
       case ParseNodeKind::SetThis:
         // SETTHIS is used to assign the result of a super() call to |this|.
         // It's not part of the original AST, so just forward to the call.
         MOZ_ASSERT(pn->pn_left->isKind(ParseNodeKind::Name));
         return expression(pn->pn_right, dst);
 
@@ -3349,17 +3352,17 @@ reflect_parse(JSContext* cx, uint32_t ar
     RootedString src(cx, ToString<CanGC>(cx, args[0]));
     if (!src)
         return false;
 
     ScopedJSFreePtr<char> filename;
     uint32_t lineno = 1;
     bool loc = true;
     RootedObject builder(cx);
-    ParseTarget target = ParseTarget::Script;
+    ParseGoal target = ParseGoal::Script;
 
     RootedValue arg(cx, args.get(1));
 
     if (!arg.isNullOrUndefined()) {
         if (!arg.isObject()) {
             ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE,
                                   JSDVG_SEARCH_STACK, arg, nullptr,
                                   "not an object", nullptr);
@@ -3437,19 +3440,19 @@ reflect_parse(JSContext* cx, uint32_t ar
         bool isModule = false;
         if (!EqualStrings(cx, stringProp, cx->names().script, &isScript))
             return false;
 
         if (!EqualStrings(cx, stringProp, cx->names().module, &isModule))
             return false;
 
         if (isScript) {
-            target = ParseTarget::Script;
+            target = ParseGoal::Script;
         } else if (isModule) {
-            target = ParseTarget::Module;
+            target = ParseGoal::Module;
         } else {
             JS_ReportErrorASCII(cx, "Bad target value, expected 'script' or 'module'");
             return false;
         }
     }
 
     /* Extract the builder methods first to report errors before parsing. */
     ASTSerializer serialize(cx, loc, filename, lineno);
@@ -3462,38 +3465,38 @@ reflect_parse(JSContext* cx, uint32_t ar
 
     AutoStableStringChars linearChars(cx);
     if (!linearChars.initTwoByte(cx, linear))
         return false;
 
     CompileOptions options(cx);
     options.setFileAndLine(filename, lineno);
     options.setCanLazilyParse(false);
-    options.allowHTMLComments = target == ParseTarget::Script;
+    options.allowHTMLComments = target == ParseGoal::Script;
     mozilla::Range<const char16_t> chars = linearChars.twoByteRange();
     UsedNameTracker usedNames(cx);
     if (!usedNames.init())
         return false;
 
     RootedScriptSourceObject sourceObject(cx, frontend::CreateScriptSourceObject(cx, options,
                                                                                  mozilla::Nothing()));
     if (!sourceObject)
         return false;
 
     Parser<FullParseHandler, char16_t> parser(cx, cx->tempLifoAlloc(), options,
                                               chars.begin().get(), chars.length(),
                                               /* foldConstants = */ false, usedNames, nullptr,
-                                              nullptr, sourceObject);
+                                              nullptr, sourceObject, target);
     if (!parser.checkOptions())
         return false;
 
     serialize.setParser(&parser);
 
     ParseNode* pn;
-    if (target == ParseTarget::Script) {
+    if (target == ParseGoal::Script) {
         pn = parser.parse();
         if (!pn)
             return false;
     } else {
         if (!GlobalObject::ensureModulePrototypesCreated(cx, cx->global()))
             return false;
 
         Rooted<ModuleObject*> module(cx, ModuleObject::create(cx));
--- a/js/src/builtin/SelfHostingDefines.h
+++ b/js/src/builtin/SelfHostingDefines.h
@@ -94,18 +94,18 @@
 #define REGEXP_GLOBAL_FLAG      0x02
 #define REGEXP_MULTILINE_FLAG   0x04
 #define REGEXP_STICKY_FLAG      0x08
 #define REGEXP_UNICODE_FLAG     0x10
 
 #define MODULE_OBJECT_ENVIRONMENT_SLOT        1
 #define MODULE_OBJECT_STATUS_SLOT             3
 #define MODULE_OBJECT_EVALUATION_ERROR_SLOT   4
-#define MODULE_OBJECT_DFS_INDEX_SLOT          13
-#define MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT 14
+#define MODULE_OBJECT_DFS_INDEX_SLOT          14
+#define MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT 15
 
 #define MODULE_STATUS_UNINSTANTIATED  0
 #define MODULE_STATUS_INSTANTIATING   1
 #define MODULE_STATUS_INSTANTIATED    2
 #define MODULE_STATUS_EVALUATING      3
 #define MODULE_STATUS_EVALUATED       4
 #define MODULE_STATUS_EVALUATED_ERROR 5
 
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -1364,17 +1364,17 @@ CaptureFirstSubsumedFrame(JSContext* cx,
 
     RootedObject obj(cx, &args[0].toObject());
     obj = CheckedUnwrap(obj);
     if (!obj) {
         JS_ReportErrorASCII(cx, "Denied permission to object.");
         return false;
     }
 
-    JS::StackCapture capture(JS::FirstSubsumedFrame(cx, obj->compartment()->principals()));
+    JS::StackCapture capture(JS::FirstSubsumedFrame(cx, obj->realm()->principals()));
     if (args.length() > 1)
         capture.as<JS::FirstSubsumedFrame>().ignoreSelfHosted = JS::ToBoolean(args[1]);
 
     JS::RootedObject capturedStack(cx);
     if (!JS::CaptureCurrentStack(cx, &capturedStack, mozilla::Move(capture)))
         return false;
 
     args.rval().setObjectOrNull(capturedStack);
@@ -4423,18 +4423,17 @@ SetRNGState(JSContext* cx, unsigned argc
     uint64_t seed0 = static_cast<uint64_t>(d0);
     uint64_t seed1 = static_cast<uint64_t>(d1);
 
     if (seed0 == 0 && seed1 == 0) {
         JS_ReportErrorASCII(cx, "RNG requires non-zero seed");
         return false;
     }
 
-    cx->compartment()->ensureRandomNumberGenerator();
-    cx->compartment()->randomNumberGenerator.ref().setState(seed0, seed1);
+    cx->realm()->getOrCreateRandomNumberGenerator().setState(seed0, seed1);
 
     args.rval().setUndefined();
     return true;
 }
 #endif
 
 static ModuleEnvironmentObject*
 GetModuleEnvironment(JSContext* cx, HandleModuleObject module)
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -2327,17 +2327,17 @@ TypedObject::create(JSContext* cx, js::g
     if (!obj)
         return cx->alreadyReportedOOM();
 
     TypedObject* tobj = static_cast<TypedObject*>(obj);
     tobj->initGroup(group);
     tobj->initShape(shape);
 
     MOZ_ASSERT(clasp->shouldDelayMetadataBuilder());
-    cx->compartment()->setObjectPendingMetadata(cx, tobj);
+    cx->realm()->setObjectPendingMetadata(cx, tobj);
 
     js::gc::gcTracer.traceCreateObject(tobj);
 
     return tobj;
 }
 
 /******************************************************************************
  * Intrinsics
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -54,18 +54,19 @@ class MOZ_STACK_CLASS BytecodeCompiler
 
     ScriptSourceObject* sourceObjectPtr() const;
 
   private:
     JSScript* compileScript(HandleObject environment, SharedContext* sc);
     bool checkLength();
     bool createScriptSource(const Maybe<uint32_t>& parameterListEnd);
     bool canLazilyParse();
-    bool createParser();
-    bool createSourceAndParser(const Maybe<uint32_t>& parameterListEnd = Nothing());
+    bool createParser(ParseGoal goal);
+    bool createSourceAndParser(ParseGoal goal,
+                               const Maybe<uint32_t>& parameterListEnd = Nothing());
 
     // If toString{Start,End} are not explicitly passed, assume the script's
     // offsets in the source used to parse it are the same as what should be
     // used to compute its Function.prototype.toString() value.
     bool createScript();
     bool createScript(uint32_t toStringStart, uint32_t toStringEnd);
 
     using TokenStreamPosition = frontend::TokenStreamPosition<char16_t>;
@@ -210,42 +211,43 @@ BytecodeCompiler::canLazilyParse()
     return options.canLazilyParse &&
            !cx->realm()->behaviors().disableLazyParsing() &&
            !cx->realm()->behaviors().discardSource() &&
            !options.sourceIsLazy &&
            !cx->lcovEnabled();
 }
 
 bool
-BytecodeCompiler::createParser()
+BytecodeCompiler::createParser(ParseGoal goal)
 {
     usedNames.emplace(cx);
     if (!usedNames->init())
         return false;
 
     if (canLazilyParse()) {
         syntaxParser.emplace(cx, alloc, options, sourceBuffer.get(), sourceBuffer.length(),
                              /* foldConstants = */ false, *usedNames, nullptr, nullptr,
-                             sourceObject);
+                             sourceObject, goal);
         if (!syntaxParser->checkOptions())
             return false;
     }
 
     parser.emplace(cx, alloc, options, sourceBuffer.get(), sourceBuffer.length(),
                    /* foldConstants = */ true, *usedNames, syntaxParser.ptrOr(nullptr), nullptr,
-                   sourceObject);
+                   sourceObject, goal);
     parser->ss = scriptSource;
     return parser->checkOptions();
 }
 
 bool
-BytecodeCompiler::createSourceAndParser(const Maybe<uint32_t>& parameterListEnd /* = Nothing() */)
+BytecodeCompiler::createSourceAndParser(ParseGoal goal,
+                                        const Maybe<uint32_t>& parameterListEnd /* = Nothing() */)
 {
     return createScriptSource(parameterListEnd) &&
-           createParser();
+           createParser(goal);
 }
 
 bool
 BytecodeCompiler::createScript()
 {
     return createScript(0, sourceBuffer.length());
 }
 
@@ -309,17 +311,17 @@ BytecodeCompiler::deoptimizeArgumentsInE
     }
 
     return true;
 }
 
 JSScript*
 BytecodeCompiler::compileScript(HandleObject environment, SharedContext* sc)
 {
-    if (!createSourceAndParser())
+    if (!createSourceAndParser(ParseGoal::Script))
         return nullptr;
 
     TokenStreamPosition startPosition(keepAtoms, parser->tokenStream);
 
     if (!createScript())
         return nullptr;
 
     Maybe<BytecodeEmitter> emitter;
@@ -385,17 +387,17 @@ BytecodeCompiler::compileEvalScript(Hand
     EvalSharedContext evalsc(cx, environment, enclosingScope,
                              directives, options.extraWarningsOption);
     return compileScript(environment, &evalsc);
 }
 
 ModuleObject*
 BytecodeCompiler::compileModule()
 {
-    if (!createSourceAndParser())
+    if (!createSourceAndParser(ParseGoal::Module))
         return nullptr;
 
     Rooted<ModuleObject*> module(cx, ModuleObject::create(cx));
     if (!module)
         return nullptr;
 
     if (!createScript())
         return nullptr;
@@ -444,17 +446,17 @@ bool
 BytecodeCompiler::compileStandaloneFunction(MutableHandleFunction fun,
                                             GeneratorKind generatorKind,
                                             FunctionAsyncKind asyncKind,
                                             const Maybe<uint32_t>& parameterListEnd)
 {
     MOZ_ASSERT(fun);
     MOZ_ASSERT(fun->isTenured());
 
-    if (!createSourceAndParser(parameterListEnd))
+    if (!createSourceAndParser(ParseGoal::Script, parameterListEnd))
         return false;
 
     TokenStreamPosition startPosition(keepAtoms, parser->tokenStream);
 
     // Speculatively parse using the default directives implied by the context.
     // If a directive is encountered (e.g., "use strict") that changes how the
     // function should have been parsed, we backup and reparse with the new set
     // of directives.
@@ -775,17 +777,17 @@ frontend::CompileLazyFunction(JSContext*
 
     UsedNameTracker usedNames(cx);
     if (!usedNames.init())
         return false;
 
     RootedScriptSourceObject sourceObject(cx, &lazy->sourceObject());
     Parser<FullParseHandler, char16_t> parser(cx, cx->tempLifoAlloc(), options, chars, length,
                                               /* foldConstants = */ true, usedNames, nullptr,
-                                              lazy, sourceObject);
+                                              lazy, sourceObject, lazy->parseGoal());
     if (!parser.checkOptions())
         return false;
 
     Rooted<JSFunction*> fun(cx, lazy->functionNonDelazifying());
     ParseNode* pn = parser.standaloneLazyFunction(fun, lazy->toStringStart(),
                                                   lazy->strict(), lazy->generatorKind(),
                                                   lazy->asyncKind());
     if (!pn)
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -3298,16 +3298,17 @@ BytecodeEmitter::checkSideEffects(ParseN
       // functions or eval.
       case ParseNodeKind::This:
         MOZ_ASSERT(pn->isArity(PN_UNARY));
         *answer = sc->needsThisTDZChecks();
         return true;
 
       // Trivial binary nodes with more token pos holders.
       case ParseNodeKind::NewTarget:
+      case ParseNodeKind::ImportMeta:
         MOZ_ASSERT(pn->isArity(PN_BINARY));
         MOZ_ASSERT(pn->pn_left->isKind(ParseNodeKind::PosHolder));
         MOZ_ASSERT(pn->pn_right->isKind(ParseNodeKind::PosHolder));
         *answer = false;
         return true;
 
       case ParseNodeKind::Break:
       case ParseNodeKind::Continue:
@@ -11141,16 +11142,21 @@ BytecodeEmitter::emitTree(ParseNode* pn,
             return false;
         break;
 
       case ParseNodeKind::NewTarget:
         if (!emit1(JSOP_NEWTARGET))
             return false;
         break;
 
+      case ParseNodeKind::ImportMeta:
+        if (!emit1(JSOP_IMPORTMETA))
+            return false;
+        break;
+
       case ParseNodeKind::SetThis:
         if (!emitSetThis(pn))
             return false;
         break;
 
       case ParseNodeKind::PosHolder:
         MOZ_FALLTHROUGH_ASSERT("Should never try to emit ParseNodeKind::PosHolder");
 
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -370,16 +370,17 @@ ContainsHoistedDeclaration(JSContext* cx
       case ParseNodeKind::Catch:
       case ParseNodeKind::ForIn:
       case ParseNodeKind::ForOf:
       case ParseNodeKind::ForHead:
       case ParseNodeKind::ClassMethod:
       case ParseNodeKind::ClassMethodList:
       case ParseNodeKind::ClassNames:
       case ParseNodeKind::NewTarget:
+      case ParseNodeKind::ImportMeta:
       case ParseNodeKind::PosHolder:
       case ParseNodeKind::SuperCall:
       case ParseNodeKind::SuperBase:
       case ParseNodeKind::SetThis:
         MOZ_CRASH("ContainsHoistedDeclaration should have indicated false on "
                   "some parent node without recurring to test this node");
 
       case ParseNodeKind::Pipeline:
@@ -1720,16 +1721,17 @@ Fold(JSContext* cx, ParseNode** pnp, Per
       case ParseNodeKind::ImportSpec:
       case ParseNodeKind::ExportSpec:
       case ParseNodeKind::SetThis:
         MOZ_ASSERT(pn->isArity(PN_BINARY));
         return Fold(cx, &pn->pn_left, parser) &&
                Fold(cx, &pn->pn_right, parser);
 
       case ParseNodeKind::NewTarget:
+      case ParseNodeKind::ImportMeta:
         MOZ_ASSERT(pn->isArity(PN_BINARY));
         MOZ_ASSERT(pn->pn_left->isKind(ParseNodeKind::PosHolder));
         MOZ_ASSERT(pn->pn_right->isKind(ParseNodeKind::PosHolder));
         return true;
 
       case ParseNodeKind::ClassNames:
         MOZ_ASSERT(pn->isArity(PN_BINARY));
         if (ParseNode*& outerBinding = pn->pn_left) {
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -555,16 +555,20 @@ class FullParseHandler
     ParseNode* newExportSpec(ParseNode* bindingName, ParseNode* exportName) {
         return newBinary(ParseNodeKind::ExportSpec, bindingName, exportName);
     }
 
     ParseNode* newExportBatchSpec(const TokenPos& pos) {
         return new_<NullaryNode>(ParseNodeKind::ExportBatchSpec, JSOP_NOP, pos);
     }
 
+    ParseNode* newImportMeta(ParseNode* importHolder, ParseNode* metaHolder) {
+        return new_<BinaryNode>(ParseNodeKind::ImportMeta, JSOP_NOP, importHolder, metaHolder);
+    }
+
     ParseNode* newExprStatement(ParseNode* expr, uint32_t end) {
         MOZ_ASSERT(expr->pn_pos.end <= end);
         return new_<UnaryNode>(ParseNodeKind::ExpressionStatement,
                                TokenPos(expr->pn_pos.begin, end), expr);
     }
 
     ParseNode* newIfStatement(uint32_t begin, ParseNode* cond, ParseNode* thenBranch,
                               ParseNode* elseBranch)
--- a/js/src/frontend/NameAnalysisTypes.h
+++ b/js/src/frontend/NameAnalysisTypes.h
@@ -59,16 +59,22 @@ class EnvironmentCoordinate
 
     bool operator==(const EnvironmentCoordinate& rhs) const {
         return hops() == rhs.hops() && slot() == rhs.slot();
     }
 };
 
 namespace frontend {
 
+enum class ParseGoal : uint8_t
+{
+    Script,
+    Module
+};
+
 // A detailed kind used for tracking declarations in the Parser. Used for
 // specific early error semantics and better error messages.
 enum class DeclarationKind : uint8_t
 {
     PositionalFormalParameter,
     FormalParameter,
     CoverArrowParameter,
     Var,
--- a/js/src/frontend/NameFunctions.cpp
+++ b/js/src/frontend/NameFunctions.cpp
@@ -412,16 +412,17 @@ class NameResolver
           case ParseNodeKind::TypeOfName:
           case ParseNodeKind::SuperBase:
             MOZ_ASSERT(cur->isArity(PN_UNARY));
             MOZ_ASSERT(cur->pn_kid->isKind(ParseNodeKind::Name));
             MOZ_ASSERT(!cur->pn_kid->expr());
             break;
 
           case ParseNodeKind::NewTarget:
+          case ParseNodeKind::ImportMeta:
             MOZ_ASSERT(cur->isArity(PN_BINARY));
             MOZ_ASSERT(cur->pn_left->isKind(ParseNodeKind::PosHolder));
             MOZ_ASSERT(cur->pn_right->isKind(ParseNodeKind::PosHolder));
             break;
 
           // Nodes with a single non-null child requiring name resolution.
           case ParseNodeKind::ExpressionStatement:
           case ParseNodeKind::TypeOfExpr:
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -127,16 +127,17 @@ class ObjectBox;
     F(ClassMethod) \
     F(ClassMethodList) \
     F(ClassNames) \
     F(NewTarget) \
     F(PosHolder) \
     F(SuperBase) \
     F(SuperCall) \
     F(SetThis) \
+    F(ImportMeta) \
     \
     /* Unary operators. */ \
     F(TypeOfName) \
     F(TypeOfExpr) \
     F(Void) \
     F(Not) \
     F(BitNot) \
     F(Await) \
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -795,33 +795,35 @@ ParserBase::errorNoOffset(unsigned error
 
     va_end(args);
 }
 
 ParserBase::ParserBase(JSContext* cx, LifoAlloc& alloc,
                        const ReadOnlyCompileOptions& options,
                        bool foldConstants,
                        UsedNameTracker& usedNames,
-                       ScriptSourceObject* sourceObject)
+                       ScriptSourceObject* sourceObject,
+                       ParseGoal parseGoal)
   : AutoGCRooter(cx, AutoGCRooter::Tag::Parser),
     context(cx),
     alloc(alloc),
     anyChars(cx, options, thisForCtor()),
     traceListHead(nullptr),
     pc(nullptr),
     usedNames(usedNames),
     ss(nullptr),
     sourceObject(cx, sourceObject),
     keepAtoms(cx),
     foldConstants(foldConstants),
 #ifdef DEBUG
     checkOptionsCalled(false),
 #endif
     isUnexpectedEOF_(false),
-    awaitHandling_(AwaitIsName)
+    awaitHandling_(AwaitIsName),
+    parseGoal_(uint8_t(parseGoal))
 {
     cx->frontendCollectionPool().addActiveCompilation();
     tempPoolMark = alloc.mark();
 }
 
 bool
 ParserBase::checkOptions()
 {
@@ -848,33 +850,35 @@ ParserBase::~ParserBase()
     context->frontendCollectionPool().removeActiveCompilation();
 }
 
 template <class ParseHandler>
 PerHandlerParser<ParseHandler>::PerHandlerParser(JSContext* cx, LifoAlloc& alloc,
                                                  const ReadOnlyCompileOptions& options,
                                                  bool foldConstants, UsedNameTracker& usedNames,
                                                  LazyScript* lazyOuterFunction,
-                                                 ScriptSourceObject* sourceObject)
-  : ParserBase(cx, alloc, options, foldConstants, usedNames, sourceObject),
+                                                 ScriptSourceObject* sourceObject,
+                                                 ParseGoal parseGoal)
+  : ParserBase(cx, alloc, options, foldConstants, usedNames, sourceObject, parseGoal),
     handler(cx, alloc, lazyOuterFunction)
 {
 
 }
 
 template <class ParseHandler, typename CharT>
 GeneralParser<ParseHandler, CharT>::GeneralParser(JSContext* cx, LifoAlloc& alloc,
                                                   const ReadOnlyCompileOptions& options,
                                                   const CharT* chars, size_t length,
                                                   bool foldConstants,
                                                   UsedNameTracker& usedNames,
                                                   SyntaxParser* syntaxParser,
                                                   LazyScript* lazyOuterFunction,
-                                                  ScriptSourceObject* sourceObject)
-  : Base(cx, alloc, options, foldConstants, usedNames, lazyOuterFunction, sourceObject),
+                                                  ScriptSourceObject* sourceObject,
+                                                  ParseGoal parseGoal)
+  : Base(cx, alloc, options, foldConstants, usedNames, lazyOuterFunction, sourceObject, parseGoal),
     tokenStream(cx, options, chars, length)
 {
     // The Mozilla specific JSOPTION_EXTRA_WARNINGS option adds extra warnings
     // which are not generated if functions are parsed lazily. Note that the
     // standard "use strict" does not inhibit lazy parsing.
     if (options.extraWarningsOption)
         disableSyntaxParser();
     else
@@ -2546,17 +2550,18 @@ PerHandlerParser<SyntaxParseHandler>::fi
 
     FunctionBox* funbox = pc->functionBox();
     RootedFunction fun(context, funbox->function());
     LazyScript* lazy = LazyScript::Create(context, fun, sourceObject,
                                           pc->closedOverBindingsForLazy(),
                                           pc->innerFunctionsForLazy,
                                           funbox->bufStart, funbox->bufEnd,
                                           funbox->toStringStart,
-                                          funbox->startLine, funbox->startColumn);
+                                          funbox->startLine, funbox->startColumn,
+                                          parseGoal());
     if (!lazy)
         return false;
 
     // Flags that need to be copied into the JSScript when we do the full
     // parse.
     if (pc->sc()->strict())
         lazy->setStrict();
     lazy->setGeneratorKind(funbox->generatorKind());
@@ -5346,16 +5351,32 @@ Parser<SyntaxParseHandler, CharT>::impor
 
 template <class ParseHandler, typename CharT>
 inline typename ParseHandler::Node
 GeneralParser<ParseHandler, CharT>::importDeclaration()
 {
     return asFinalParser()->importDeclaration();
 }
 
+template <class ParseHandler, typename CharT>
+inline typename ParseHandler::Node
+GeneralParser<ParseHandler, CharT>::importDeclarationOrImportMeta(YieldHandling yieldHandling)
+{
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Import));
+
+    TokenKind tt;
+    if (!tokenStream.peekToken(&tt))
+        return null();
+
+    if (tt == TokenKind::Dot)
+        return expressionStatement(yieldHandling);
+
+    return importDeclaration();
+}
+
 template<typename CharT>
 bool
 Parser<FullParseHandler, CharT>::checkExportedName(JSAtom* exportName)
 {
     if (!pc->sc()->asModuleContext()->builder.hasExportedName(exportName))
         return true;
 
     JSAutoByteString str;
@@ -7694,17 +7715,17 @@ GeneralParser<ParseHandler, CharT>::stat
 
       // |class| is also forbidden by lookahead restriction.
       case TokenKind::Class:
         error(JSMSG_FORBIDDEN_AS_STATEMENT, "classes");
         return null();
 
       // ImportDeclaration (only inside modules)
       case TokenKind::Import:
-        return importDeclaration();
+        return importDeclarationOrImportMeta(yieldHandling);
 
       // ExportDeclaration (only inside modules)
       case TokenKind::Export:
         return exportDeclaration();
 
       // Miscellaneous error cases arguably better caught here than elsewhere.
 
       case TokenKind::Catch:
@@ -7887,17 +7908,17 @@ GeneralParser<ParseHandler, CharT>::stat
       //     LetOrConst BindingList[?In, ?Yield]
       case TokenKind::Const:
         // [In] is the default behavior, because for-loops specially parse
         // their heads to handle |in| in this situation.
         return lexicalDeclaration(yieldHandling, DeclarationKind::Const);
 
       // ImportDeclaration (only inside modules)
       case TokenKind::Import:
-        return importDeclaration();
+        return importDeclarationOrImportMeta(yieldHandling);
 
       // ExportDeclaration (only inside modules)
       case TokenKind::Export:
         return exportDeclaration();
 
       // Miscellaneous error cases arguably better caught here than elsewhere.
 
       case TokenKind::Catch:
@@ -8709,16 +8730,20 @@ GeneralParser<ParseHandler, CharT>::memb
         }
     } else if (tt == TokenKind::Super) {
         Node thisName = newThisName();
         if (!thisName)
             return null();
         lhs = handler.newSuperBase(thisName, pos());
         if (!lhs)
             return null();
+    } else if (tt == TokenKind::Import) {
+        lhs = importMeta();
+        if (!lhs)
+            return null();
     } else {
         lhs = primaryExpr(yieldHandling, tripledotHandling, tt, possibleError, invoked);
         if (!lhs)
             return null();
     }
 
     MOZ_ASSERT_IF(handler.isSuperBase(lhs), anyChars.isCurrentTokenType(TokenKind::Super));
 
@@ -9887,16 +9912,55 @@ GeneralParser<ParseHandler, CharT>::tryN
         return false;
 
     newTarget = handler.newNewTarget(newHolder, targetHolder);
     return !!newTarget;
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
+GeneralParser<ParseHandler, CharT>::importMeta()
+{
+    MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Import));
+
+    uint32_t begin = pos().begin;
+
+    if (parseGoal() != ParseGoal::Module) {
+        errorAt(begin, JSMSG_IMPORT_OUTSIDE_MODULE);
+        return null();
+    }
+
+    Node importHolder = handler.newPosHolder(pos());
+    if (!importHolder)
+        return null();
+
+    TokenKind next;
+    if (!tokenStream.getToken(&next))
+        return null();
+    if (next != TokenKind::Dot) {
+        error(JSMSG_UNEXPECTED_TOKEN, "dot", TokenKindToDesc(next));
+        return null();
+    }
+
+    if (!tokenStream.getToken(&next))
+        return null();
+    if (next != TokenKind::Meta) {
+        error(JSMSG_UNEXPECTED_TOKEN, "meta", TokenKindToDesc(next));
+        return null();
+    }
+
+    Node metaHolder = handler.newPosHolder(pos());
+    if (!metaHolder)
+        return null();
+
+    return handler.newImportMeta(importHolder, metaHolder);
+}
+
+template <class ParseHandler, typename CharT>
+typename ParseHandler::Node
 GeneralParser<ParseHandler, CharT>::primaryExpr(YieldHandling yieldHandling,
                                                 TripledotHandling tripledotHandling,
                                                 TokenKind tt, PossibleError* possibleError,
                                                 InvokedPrediction invoked /* = PredictUninvoked */)
 {
     MOZ_ASSERT(anyChars.isCurrentTokenType(tt));
     if (!CheckRecursionLimit(context))
         return null();
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -290,26 +290,32 @@ class MOZ_STACK_CLASS ParserBase
     bool checkOptionsCalled:1;
 #endif
 
     /* Unexpected end of input, i.e. Eof not at top-level. */
     bool isUnexpectedEOF_:1;
 
     /* AwaitHandling */ uint8_t awaitHandling_:2;
 
+    /* ParseGoal */ uint8_t parseGoal_:1;
+
   public:
     bool awaitIsKeyword() const {
       return awaitHandling_ != AwaitIsName;
     }
 
+    ParseGoal parseGoal() const {
+        return ParseGoal(parseGoal_);
+    }
+
     template<class, typename> friend class AutoAwaitIsKeyword;
 
     ParserBase(JSContext* cx, LifoAlloc& alloc, const ReadOnlyCompileOptions& options,
                bool foldConstants, UsedNameTracker& usedNames,
-               ScriptSourceObject* sourceObject);
+               ScriptSourceObject* sourceObject, ParseGoal parseGoal);
     ~ParserBase();
 
     bool checkOptions();
 
     void trace(JSTracer* trc);
 
     const char* getFilename() const { return anyChars.getFilename(); }
     TokenPos pos() const { return anyChars.currentToken().pos; }
@@ -464,17 +470,18 @@ class MOZ_STACK_CLASS PerHandlerParser
     // |GeneralParser<ParseHandler, CharT>| that impose the real type on this
     // field.
     void* internalSyntaxParser_;
 
   protected:
     PerHandlerParser(JSContext* cx, LifoAlloc& alloc, const ReadOnlyCompileOptions& options,
                      bool foldConstants, UsedNameTracker& usedNames,
                      LazyScript* lazyOuterFunction,
-                     ScriptSourceObject* sourceObject);
+                     ScriptSourceObject* sourceObject,
+                     ParseGoal parseGoal);
 
     static Node null() { return ParseHandler::null(); }
 
     Node stringLiteral();
 
     const char* nameIsArgumentsOrEval(Node node);
 
     bool noteDestructuredPositionalFormalParameter(Node fn, Node destruct);
@@ -657,16 +664,17 @@ class MOZ_STACK_CLASS GeneralParser
     using Modifier = TokenStreamShared::Modifier;
     using Position = typename TokenStream::Position;
 
     using Base::PredictUninvoked;
     using Base::PredictInvoked;
 
     using Base::alloc;
     using Base::awaitIsKeyword;
+    using Base::parseGoal;
 #if DEBUG
     using Base::checkOptionsCalled;
 #endif
     using Base::finishFunctionScopes;
     using Base::finishLexicalScope;
     using Base::foldConstants;
     using Base::getFilename;
     using Base::hasUsedFunctionSpecialName;
@@ -867,17 +875,18 @@ class MOZ_STACK_CLASS GeneralParser
   public:
     TokenStream tokenStream;
 
   public:
     GeneralParser(JSContext* cx, LifoAlloc& alloc, const ReadOnlyCompileOptions& options,
                   const CharT* chars, size_t length, bool foldConstants,
                   UsedNameTracker& usedNames, SyntaxParser* syntaxParser,
                   LazyScript* lazyOuterFunction,
-                  ScriptSourceObject* sourceObject);
+                  ScriptSourceObject* sourceObject,
+                  ParseGoal parseGoal);
 
     inline void setAwaitHandling(AwaitHandling awaitHandling);
 
     /*
      * Parse a top-level JS script.
      */
     Node parse();
 
@@ -1008,16 +1017,17 @@ class MOZ_STACK_CLASS GeneralParser
     Node labeledItem(YieldHandling yieldHandling);
 
     Node ifStatement(YieldHandling yieldHandling);
     Node consequentOrAlternative(YieldHandling yieldHandling);
 
     Node lexicalDeclaration(YieldHandling yieldHandling, DeclarationKind kind);
 
     inline Node importDeclaration();
+    Node importDeclarationOrImportMeta(YieldHandling yieldHandling);
 
     Node exportFrom(uint32_t begin, Node specList);
     Node exportBatch(uint32_t begin);
     inline bool checkLocalExportNames(Node node);
     Node exportClause(uint32_t begin);
     Node exportFunctionDeclaration(uint32_t begin, uint32_t toStringStart,
                                    FunctionAsyncKind asyncKind = FunctionAsyncKind::SyncFunction);
     Node exportVariableStatement(uint32_t begin);
@@ -1107,16 +1117,18 @@ class MOZ_STACK_CLASS GeneralParser
     Node primaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
                      TokenKind tt, PossibleError* possibleError,
                      InvokedPrediction invoked = PredictUninvoked);
     Node exprInParens(InHandling inHandling, YieldHandling yieldHandling,
                       TripledotHandling tripledotHandling, PossibleError* possibleError = nullptr);
 
     bool tryNewTarget(Node& newTarget);
 
+    Node importMeta();
+
     Node methodDefinition(uint32_t toStringStart, PropertyType propType, HandleAtom funName);
 
     /*
      * Additional JS parsers.
      */
     bool functionArguments(YieldHandling yieldHandling, FunctionSyntaxKind kind,
                            Node funcpn);
 
--- a/js/src/frontend/ReservedWords.h
+++ b/js/src/frontend/ReservedWords.h
@@ -61,16 +61,17 @@
     \
     /* Contextual keywords. */ \
     macro(as, as, TokenKind::As) \
     macro(async, async, TokenKind::Async) \
     macro(await, await, TokenKind::Await) \
     macro(from, from, TokenKind::From) \
     macro(get, get, TokenKind::Get) \
     macro(let, let, TokenKind::Let) \
+    macro(meta, meta, TokenKind::Meta) \
     macro(of, of, TokenKind::Of) \
     macro(set, set, TokenKind::Set) \
     macro(static, static_, TokenKind::Static) \
     macro(target, target, TokenKind::Target) \
     /* \
      * Yield is a token inside function*.  Outside of a function*, it is a \
      * future reserved word in strict mode, but a keyword in JS1.7 even \
      * when strict.  Punt logic to parser. \
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -292,16 +292,19 @@ class SyntaxParseHandler
         return NodeGeneric;
     }
     Node newExportSpec(Node bindingName, Node exportName) {
         return NodeGeneric;
     }
     Node newExportBatchSpec(const TokenPos& pos) {
         return NodeGeneric;
     }
+    Node newImportMeta(Node importHolder, Node metaHolder) {
+        return NodeGeneric;
+    }
 
     Node newSetThis(Node thisName, Node value) { return value; }
 
     Node newExprStatement(Node expr, uint32_t end) {
         return expr == NodeUnparenthesizedString ? NodeStringExprStatement : NodeGeneric;
     }
 
     Node newIfStatement(uint32_t begin, Node cond, Node then, Node else_) { return NodeGeneric; }
--- a/js/src/frontend/TokenKind.h
+++ b/js/src/frontend/TokenKind.h
@@ -122,16 +122,17 @@
     macro(As,           "'as'") \
     range(ContextualKeywordFirst, As) \
     macro(Async,        "'async'") \
     macro(Await,        "'await'") \
     macro(Each,         "'each'") \
     macro(From,         "'from'") \
     macro(Get,          "'get'") \
     macro(Let,          "'let'") \
+    macro(Meta,         "'meta'") \
     macro(Of,           "'of'") \
     macro(Set,          "'set'") \
     macro(Static,       "'static'") \
     macro(Target,       "'target'") \
     macro(Yield,        "'yield'") \
     range(ContextualKeywordLast, Yield) \
     \
     /* future reserved words */ \
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -747,17 +747,17 @@ TokenStreamAnyChars::fillExcludingContex
 {
     err->isMuted = mutedErrors;
 
     // If this TokenStreamAnyChars doesn't have location information, try to
     // get it from the caller.
     if (!filename_ && !cx->helperThread()) {
         NonBuiltinFrameIter iter(cx,
                                  FrameIter::FOLLOW_DEBUGGER_EVAL_PREV_LINK,
-                                 cx->compartment()->principals());
+                                 cx->realm()->principals());
         if (!iter.done() && iter.filename()) {
             err->filename = iter.filename();
             err->lineNumber = iter.computeLine(&err->columnNumber);
             return false;
         }
     }
 
     // Otherwise use this TokenStreamAnyChars's location information.
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -3840,25 +3840,26 @@ Zone::sweepCompartments(FreeOp* fop, boo
     mozilla::DebugOnly<JSRuntime*> rt = runtimeFromMainThread();
 
     JSCompartment** read = compartments().begin();
     JSCompartment** end = compartments().end();
     JSCompartment** write = read;
     bool foundOne = false;
     while (read < end) {
         JSCompartment* comp = *read++;
-        MOZ_ASSERT(!JS::GetRealmForCompartment(comp)->isAtomsRealm());
+        Realm* realm = JS::GetRealmForCompartment(comp);
+        MOZ_ASSERT(!realm->isAtomsRealm());
 
         /*
-         * Don't delete the last compartment if all the ones before it were
-         * deleted and keepAtleastOne is true.
+         * Don't delete the last compartment and realm if all the ones before
+         * it were deleted and keepAtleastOne is true.
          */
         bool dontDelete = read == end && !foundOne && keepAtleastOne;
-        if ((!comp->marked && !dontDelete) || destroyingRuntime) {
-            JS::GetRealmForCompartment(comp)->destroy(fop);
+        if ((!realm->marked() && !dontDelete) || destroyingRuntime) {
+            realm->destroy(fop);
         } else {
             *write++ = comp;
             foundOne = true;
         }
     }
     compartments().shrinkTo(write - compartments().begin());
     MOZ_ASSERT_IF(keepAtleastOne, !compartments().empty());
 }
@@ -3894,17 +3895,17 @@ GCRuntime::sweepZones(FreeOp* fop, bool 
     Zone** write = read;
 
     while (read < end) {
         Zone* zone = *read++;
 
         if (zone->wasGCStarted()) {
             MOZ_ASSERT(!zone->isQueuedForBackgroundSweep());
             const bool zoneIsDead = zone->arenas.arenaListsAreEmpty() &&
-                                    !zone->hasMarkedCompartments();
+                                    !zone->hasMarkedRealms();
             if (zoneIsDead || destroyingRuntime)
             {
                 // We have just finished sweeping, so we should have freed any
                 // empty arenas back to their Chunk for future allocation.
                 zone->arenas.checkEmptyFreeLists();
 
                 // We are about to delete the Zone; this will leave the Zone*
                 // in the arena header dangling if there are any arenas
@@ -4240,17 +4241,17 @@ GCRuntime::prepareZonesForCollection(JS:
         zone->setPreservingCode(false);
     }
 
     // Discard JIT code more aggressively if the process is approaching its
     // executable code limit.
     bool canAllocateMoreCode = jit::CanLikelyAllocateMoreExecutableMemory();
 
     for (RealmsIter r(rt, WithAtoms); !r.done(); r.next()) {
-        r->marked = false;
+        r->unmark();
         r->scheduledForDestruction = false;
         r->maybeAlive = r->shouldTraceGlobal() || !r->zone()->isGCScheduled();
         if (shouldPreserveJITCode(r, currentTime, reason, canAllocateMoreCode))
             r->zone()->setPreservingCode(true);
     }
 
     if (!cleanUpEverything && canAllocateMoreCode) {
         jit::JitActivationIterator activation(rt->mainContextFromOwnThread());
@@ -8008,23 +8009,23 @@ GCRuntime::mergeCompartments(JSCompartme
 
     AutoTraceSession session(rt);
 
     // Cleanup tables and other state in the source realm/zone that will be
     // meaningless after merging into the target realm/zone.
 
     sourceRealm->clearTables();
     source->zone()->clearTables();
-    source->unsetIsDebuggee();
+    sourceRealm->unsetIsDebuggee();
 
     // The delazification flag indicates the presence of LazyScripts in a
-    // compartment for the Debugger API, so if the source compartment created
-    // LazyScripts, the flag must be propagated to the target compartment.
-    if (source->needsDelazificationForDebugger())
-        target->scheduleDelazificationForDebugger();
+    // realm for the Debugger API, so if the source realm created LazyScripts,
+    // the flag must be propagated to the target realm.
+    if (sourceRealm->needsDelazificationForDebugger())
+        targetRealm->scheduleDelazificationForDebugger();
 
     // Release any relocated arenas which we may be holding on to as they might
     // be in the source zone
     releaseHeldRelocatedArenas();
 
     // Fixup compartment pointers in source to refer to target, and make sure
     // type information generations are in sync.
 
@@ -8101,29 +8102,29 @@ GCRuntime::mergeCompartments(JSCompartme
     // Atoms which are marked in source's zone are now marked in target's zone.
     atomMarking.adoptMarkedAtoms(target->zone(), source->zone());
 
     // Merge script name maps in the target compartment's map.
     if (rt->lcovOutput().isEnabled() && sourceRealm->scriptNameMap) {
         AutoEnterOOMUnsafeRegion oomUnsafe;
 
         if (!targetRealm->scriptNameMap) {
-            targetRealm->scriptNameMap = cx->new_<ScriptNameMap>();
+            targetRealm->scriptNameMap = cx->make_unique<ScriptNameMap>();
 
             if (!targetRealm->scriptNameMap)
                 oomUnsafe.crash("Failed to create a script name map.");
 
             if (!targetRealm->scriptNameMap->init())
                 oomUnsafe.crash("Failed to initialize a script name map.");
         }
 
         for (ScriptNameMap::Range r = sourceRealm->scriptNameMap->all(); !r.empty(); r.popFront()) {
             JSScript* key = r.front().key();
-            const char* value = r.front().value();
-            if (!targetRealm->scriptNameMap->putNew(key, value))
+            auto value = Move(r.front().value());
+            if (!targetRealm->scriptNameMap->putNew(key, Move(value)))
                 oomUnsafe.crash("Failed to add an entry in the script name map.");
         }
 
         sourceRealm->scriptNameMap->clear();
     }
 
     // The source compartment is now completely empty, and is the only
     // compartment in its zone, which is the only zone in its group. Delete
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -1478,17 +1478,17 @@ js::ObjectGroup::traceChildren(JSTracer*
                 TraceEdge(trc, &prop->id, "group_property");
         }
     }
 
     if (proto().isObject())
         TraceEdge(trc, &proto(), "group_proto");
 
     if (trc->isMarkingTracer())
-        compartment()->mark();
+        realm()->mark();
 
     if (JSObject* global = realm()->unsafeUnbarrieredMaybeGlobal())
         TraceManuallyBarrieredEdge(trc, &global, "group_global");
 
 
     if (newScript(sweep))
         newScript(sweep)->trace(trc);
 
@@ -1521,17 +1521,17 @@ js::GCMarker::lazilyMarkChildren(ObjectG
     for (unsigned i = 0; i < count; i++) {
         if (ObjectGroup::Property* prop = group->getProperty(sweep, i))
             traverseEdge(group, prop->id.get());
     }
 
     if (group->proto().isObject())
         traverseEdge(group, group->proto().toObject());
 
-    group->compartment()->mark();
+    group->realm()->mark();
 
     if (GlobalObject* global = group->realm()->unsafeUnbarrieredMaybeGlobal())
         traverseEdge(group, static_cast<JSObject*>(global));
 
     if (group->newScript(sweep))
         group->newScript(sweep)->trace(this);
 
     if (group->maybePreliminaryObjects(sweep))
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -289,20 +289,20 @@ Zone::createJitZone(JSContext* cx)
     if (!jitZone || !jitZone->init(cx))
         return nullptr;
 
     jitZone_ = jitZone.release();
     return jitZone_;
 }
 
 bool
-Zone::hasMarkedCompartments()
+Zone::hasMarkedRealms()
 {
-    for (CompartmentsInZoneIter comp(this); !comp.done(); comp.next()) {
-        if (comp->marked)
+    for (RealmsInZoneIter realm(this); !realm.done(); realm.next()) {
+        if (realm->marked())
             return true;
     }
     return false;
 }
 
 bool
 Zone::canCollect()
 {
--- a/js/src/gc/Zone.h
+++ b/js/src/gc/Zone.h
@@ -215,17 +215,17 @@ struct Zone : public JS::shadow::Zone,
         if (!js::CurrentThreadCanAccessRuntime(runtime_))
             return nullptr;
         return runtimeFromMainThread()->onOutOfMemory(allocFunc, nbytes, reallocPtr);
     }
     void reportAllocationOverflow() { js::ReportAllocationOverflow(nullptr); }
 
     void beginSweepTypes(bool releaseTypes);
 
-    bool hasMarkedCompartments();
+    bool hasMarkedRealms();
 
     void scheduleGC() { MOZ_ASSERT(!CurrentThreadIsHeapBusy()); gcScheduled_ = true; }
     void unscheduleGC() { gcScheduled_ = false; }
     bool isGCScheduled() { return gcScheduled_; }
 
     void setPreservingCode(bool preserving) { gcPreserveCode_ = preserving; }
     bool isPreservingCode() const { return gcPreserveCode_; }
 
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/modules/exportImportMeta.js
@@ -0,0 +1,3 @@
+export default function() {
+    return import.meta;
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/modules/import-meta-expression.js
@@ -0,0 +1,97 @@
+load(libdir + "match.js");
+load(libdir + "asserts.js");
+
+var { Pattern, MatchError } = Match;
+
+program = (elts) => Pattern({
+    type: "Program",
+    body: elts
+});
+expressionStatement = (expression) => Pattern({
+    type: "ExpressionStatement",
+    expression: expression
+});
+assignmentExpression = (left, operator, right) => Pattern({
+    type: "AssignmentExpression",
+    operator: operator,
+    left: left,
+    right: right
+});
+ident = (name) => Pattern({
+    type: "Identifier",
+    name: name
+});
+metaProperty = (meta, property) => Pattern({
+    type: "MetaProperty",
+    meta: meta,
+    property: property
+});
+memberExpression = (object, property) => Pattern({
+    type: "MemberExpression",
+    object: object,
+    property: property
+});
+
+function parseAsModule(source)
+{
+    return Reflect.parse(source, {target: "module"});
+}
+
+program([
+    expressionStatement(
+        metaProperty(
+            ident("import"),
+            ident("meta")
+        )
+    )
+]).assert(parseAsModule("import.meta;"));
+
+program([
+    expressionStatement(
+        assignmentExpression(
+            ident("x"),
+            "=",
+            metaProperty(
+                ident("import"),
+                ident("meta")
+            )
+        )
+    )
+]).assert(parseAsModule("x = import.meta;"));
+
+program([
+    expressionStatement(
+        assignmentExpression(
+            memberExpression(
+                metaProperty(
+                    ident("import"),
+                    ident("meta")
+                ),
+                ident("foo")
+            ),
+            "=",
+            ident("x"),
+        )
+    )
+]).assert(parseAsModule("import.meta.foo = x;"));
+
+function assertModuleParseThrowsSyntaxError(source)
+{
+    assertThrowsInstanceOf(() => parseAsModule(source), SyntaxError);
+}
+
+function assertModuleParseThrowsReferenceError(source)
+{
+    assertThrowsInstanceOf(() => parseAsModule(source), ReferenceError);
+}
+
+assertModuleParseThrowsSyntaxError("import");
+assertModuleParseThrowsSyntaxError("import.");
+assertModuleParseThrowsSyntaxError("import.met");
+assertModuleParseThrowsSyntaxError("import.metaa");
+assertModuleParseThrowsSyntaxError("x = import");
+assertModuleParseThrowsSyntaxError("x = import.");
+assertModuleParseThrowsSyntaxError("x = import.met");
+assertModuleParseThrowsSyntaxError("x = import.metaa");
+
+assertModuleParseThrowsReferenceError("import.meta = x");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/modules/import-meta-oom.js
@@ -0,0 +1,6 @@
+// |jit-test| module
+
+if (typeof oomTest !== "function")
+    quit();
+
+oomTest(() => import.meta);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/modules/import-meta.js
@@ -0,0 +1,58 @@
+// |jit-test| module
+
+// import.meta is an object.
+assertEq(typeof import.meta, "object");
+assertEq(import.meta !== null, true);
+
+// import.meta always returns the same object.
+let obj = import.meta;
+assertEq(import.meta, obj);
+
+// Test calling from lazily compile function.
+function get() {
+    return import.meta;
+}
+assertEq(get(), import.meta);
+
+// import.meta.url: This property will contain the module script's base URL,
+// serialized.
+
+assertEq("url" in import.meta, true);
+assertEq(import.meta.url.endsWith("import-meta.js"), true);
+
+import getOtherMetaObject from "exportImportMeta.js";
+
+let otherImportMeta = getOtherMetaObject();
+assertEq(otherImportMeta.url.endsWith("exportImportMeta.js"), true);
+
+// By default the import.meta object will be extensible, and its properties will
+// be writable, configurable, and enumerable.
+
+assertEq(Object.isExtensible(import.meta), true);
+
+var desc = Object.getOwnPropertyDescriptor(import.meta, "url");
+assertEq(desc.writable, true);
+assertEq(desc.enumerable, true);
+assertEq(desc.configurable, true);
+assertEq(desc.value, import.meta.url);
+
+// The import.meta object's prototype is null.
+assertEq(Object.getPrototypeOf(import.meta), null);
+
+import.meta.url = 0;
+assertEq(import.meta.url, 0);
+
+import.meta.newProp = 42;
+assertEq(import.meta.newProp, 42);
+
+let found = new Set(Reflect.ownKeys(import.meta));
+
+assertEq(found.size, 2);
+assertEq(found.has("url"), true);
+assertEq(found.has("newProp"), true);
+
+delete import.meta.url;
+delete import.meta.newProp;
+
+found = new Set(Reflect.ownKeys(import.meta));
+assertEq(found.size, 0);
--- a/js/src/jit-test/tests/wasm/globals.js
+++ b/js/src/jit-test/tests/wasm/globals.js
@@ -356,22 +356,27 @@ if (typeof WebAssembly.Global === "funct
 
     // These types should not work:
     assertErrorMessage(() => new Global({type: "i64"}),   TypeError, /bad type for a WebAssembly.Global/);
     assertErrorMessage(() => new Global({}),              TypeError, /bad type for a WebAssembly.Global/);
     assertErrorMessage(() => new Global({type: "fnord"}), TypeError, /bad type for a WebAssembly.Global/);
     assertErrorMessage(() => new Global(),                TypeError, /Global requires more than 0 arguments/);
 
     // Coercion of init value; ".value" accessor
-    assertEq((new Global({type: "i32", value: 3.14})).value, 3);
-    assertEq((new Global({type: "f32", value: { valueOf: () => 33.5 }})).value, 33.5);
-    assertEq((new Global({type: "f64", value: "3.25"})).value, 3.25);
+    assertEq((new Global({type: "i32"}, 3.14)).value, 3);
+    assertEq((new Global({type: "f32"}, { valueOf: () => 33.5 })).value, 33.5);
+    assertEq((new Global({type: "f64"}, "3.25")).value, 3.25);
 
     // Nothing special about NaN, it coerces just fine
-    assertEq((new Global({type: "i32", value: NaN})).value, 0);
+    assertEq((new Global({type: "i32"}, NaN)).value, 0);
+
+    // The default init value is zero.
+    assertEq((new Global({type: "i32"})).value, 0);
+    assertEq((new Global({type: "f32"})).value, 0);
+    assertEq((new Global({type: "f64"})).value, 0);
 
     {
         // "value" is enumerable
         let x = new Global({type: "i32"});
         let s = "";
         for ( let i in x )
             s = s + i + ",";
         assertEq(s, "value,");
@@ -382,24 +387,24 @@ if (typeof WebAssembly.Global === "funct
 
     // Can't set the value of an immutable global
     assertErrorMessage(() => (new Global({type: "i32"})).value = 10,
                        TypeError,
                        /can't set value of immutable global/);
 
     {
         // Can set the value of a mutable global
-        let g = new Global({type: "i32", mutable: true, value: 37});
+        let g = new Global({type: "i32", mutable: true}, 37);
         g.value = 10;
         assertEq(g.value, 10);
     }
 
     {
         // Misc internal conversions
-        let g = new Global({type: "i32", value: 42});
+        let g = new Global({type: "i32"}, 42);
 
         // valueOf
         assertEq(g - 5, 37);
 
         // @@toStringTag
         assertEq(g.toString(), "[object WebAssembly.Global]");
     }
 
@@ -503,26 +508,26 @@ if (typeof WebAssembly.Global === "funct
     {
         const mutErr = /imported global mutability mismatch/;
         const i64Err = /cannot pass i64 to or from JS/;
 
         let m1 = new Module(wasmTextToBinary(`(module
                                                (import "m" "g" (global i32)))`));
 
         // Mutable Global matched to immutable import
-        let gm = new Global({type: "i32", value: 42, mutable: true});
+        let gm = new Global({type: "i32", mutable: true}, 42);
         assertErrorMessage(() => new Instance(m1, {m: {g: gm}}),
                            LinkError,
                            mutErr);
 
         let m2 = new Module(wasmTextToBinary(`(module
                                                (import "m" "g" (global (mut i32))))`));
 
         // Immutable Global matched to mutable import
-        let gi = new Global({type: "i32", value: 42, mutable: false});
+        let gi = new Global({type: "i32", mutable: false}, 42);
         assertErrorMessage(() => new Instance(m2, {m: {g: gi}}),
                            LinkError,
                            mutErr);
 
         // Constant value is the same as immutable Global
         assertErrorMessage(() => new Instance(m2, {m: {g: 42}}),
                            LinkError,
                            mutErr);
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -981,17 +981,17 @@ InitFromBailout(JSContext* cx, size_t fr
             // bailing to baseline due to debug mode, we might not have all
             // the stack if we are at the newest frame.
             //
             // For instance, if calling |f()| pushed an Ion frame which threw,
             // the snapshot expects the return value to be pushed, but it's
             // possible nothing was pushed before we threw. We can't drop
             // iterators, however, so read them out. They will be closed by
             // HandleExceptionBaseline.
-            MOZ_ASSERT(cx->compartment()->isDebuggee());
+            MOZ_ASSERT(cx->realm()->isDebuggee());
             if (iter.moreFrames() || HasLiveStackValueAtDepth(script, pc, i + 1)) {
                 v = iter.read();
             } else {
                 iter.skip();
                 v = MagicValue(JS_OPTIMIZED_OUT);
             }
         } else {
             v = iter.read();
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -99,17 +99,17 @@ BaselineCompiler::compile()
 
     AutoKeepTypeScripts keepTypes(cx);
     if (!script->ensureHasTypes(cx, keepTypes) || !script->ensureHasAnalyzedArgsUsage(cx))
         return Method_Error;
 
     // When code coverage is only enabled for optimizations, or when a Debugger
     // set the collectCoverageInfo flag, we have to create the ScriptCounts if
     // they do not exist.
-    if (!script->hasScriptCounts() && cx->compartment()->collectCoverage()) {
+    if (!script->hasScriptCounts() && cx->realm()->collectCoverage()) {
         if (!script->initScriptCounts(cx))
             return Method_Error;
     }
 
     // Pin analysis info during compilation.
     AutoEnterAnalysis autoEnterAnalysis(cx);
 
     MOZ_ASSERT(!script->hasBaselineScript());
@@ -5075,8 +5075,22 @@ BaselineCompiler::emit_JSOP_DERIVEDCONST
     pushArg(ImmGCPtr(script));
     if (!callVM(MakeDefaultConstructorInfo))
         return false;
 
     masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
     frame.push(R0);
     return true;
 }
+
+bool
+BaselineCompiler::emit_JSOP_IMPORTMETA()
+{
+    RootedModuleObject module(cx, GetModuleObjectForScript(script));
+    MOZ_ASSERT(module);
+
+    JSObject* metaObject = GetOrCreateModuleMetaObject(cx, module);
+    if (!metaObject)
+        return false;
+
+    frame.push(ObjectValue(*metaObject));
+    return true;
+}
--- a/js/src/jit/BaselineCompiler.h
+++ b/js/src/jit/BaselineCompiler.h
@@ -251,17 +251,18 @@ namespace jit {
     _(JSOP_IS_CONSTRUCTING)    \
     _(JSOP_TRY_DESTRUCTURING_ITERCLOSE) \
     _(JSOP_CHECKCLASSHERITAGE) \
     _(JSOP_INITHOMEOBJECT)     \
     _(JSOP_BUILTINPROTO)       \
     _(JSOP_OBJWITHPROTO)       \
     _(JSOP_FUNWITHPROTO)       \
     _(JSOP_CLASSCONSTRUCTOR)   \
-    _(JSOP_DERIVEDCONSTRUCTOR)
+    _(JSOP_DERIVEDCONSTRUCTOR) \
+    _(JSOP_IMPORTMETA)
 
 class BaselineCompiler : public BaselineCompilerSpecific
 {
     FixedList<Label>            labels_;
     NonAssertingLabel           return_;
     NonAssertingLabel           postBarrierSlot_;
 
     // Native code offset right before the scope chain is initialized.
--- a/js/src/jit/CompileWrappers.cpp
+++ b/js/src/jit/CompileWrappers.cpp
@@ -252,17 +252,17 @@ CompileRuntime*
 CompileCompartment::runtime()
 {
     return CompileRuntime::get(compartment()->runtimeFromAnyThread());
 }
 
 const void*
 CompileCompartment::addressOfRandomNumberGenerator()
 {
-    return compartment()->randomNumberGenerator.ptr();
+    return JS::GetRealmForCompartment(compartment())->addressOfRandomNumberGenerator();
 }
 
 const JitCompartment*
 CompileCompartment::jitCompartment()
 {
     return compartment()->jitCompartment();
 }
 
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -1214,17 +1214,17 @@ IonBuilder::initEnvironmentChain(MDefini
 void
 IonBuilder::initArgumentsObject()
 {
     JitSpew(JitSpew_IonMIR, "%s:%zu - Emitting code to initialize arguments object! block=%p",
             script()->filename(), script()->lineno(), current);
     MOZ_ASSERT(info().needsArgsObj());
 
     bool mapped = script()->hasMappedArgsObj();
-    ArgumentsObject* templateObj = script()->compartment()->maybeArgumentsTemplateObject(mapped);
+    ArgumentsObject* templateObj = script()->realm()->maybeArgumentsTemplateObject(mapped);
 
     MCreateArgumentsObject* argsObj =
         MCreateArgumentsObject::New(alloc(), current->environmentChain(), templateObj);
     current->add(argsObj);
     current->setArgumentsObject(argsObj);
 }
 
 AbortReasonOr<Ok>
@@ -2371,16 +2371,19 @@ IonBuilder::inspectOpcode(JSOp op)
         // Assuming optimization isn't available doesn't affect correctness.
         // TODO: Investigate dynamic checks.
         MDefinition* arr = current->peek(-1);
         arr->setImplicitlyUsedUnchecked();
         pushConstant(BooleanValue(false));
         return Ok();
       }
 
+      case JSOP_IMPORTMETA:
+          return jsop_importmeta();
+
       // ===== NOT Yet Implemented =====
       // Read below!
 
       // With
       case JSOP_ENTERWITH:
       case JSOP_LEAVEWITH:
 
       // Spread
@@ -13137,16 +13140,31 @@ IonBuilder::jsop_implicitthis(PropertyNa
 
     MImplicitThis* implicitThis = MImplicitThis::New(alloc(), current->environmentChain(), name);
     current->add(implicitThis);
     current->push(implicitThis);
 
     return resumeAfter(implicitThis);
 }
 
+AbortReasonOr<Ok>
+IonBuilder::jsop_importmeta()
+{
+    ModuleObject* module = GetModuleObjectForScript(script());
+    MOZ_ASSERT(module);
+
+    // The object must have been created already when we compiled for baseline.
+    JSObject* metaObject = module->metaObject();
+    MOZ_ASSERT(metaObject);
+
+    pushConstant(ObjectValue(*metaObject));
+
+    return Ok();
+}
+
 MInstruction*
 IonBuilder::addConvertElementsToDoubles(MDefinition* elements)
 {
     MInstruction* convert = MConvertElementsToDoubles::New(alloc(), elements);
     current->add(convert);
     return convert;
 }
 
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -586,16 +586,17 @@ class IonBuilder
     AbortReasonOr<Ok> jsop_setaliasedvar(EnvironmentCoordinate ec);
     AbortReasonOr<Ok> jsop_debugger();
     AbortReasonOr<Ok> jsop_newtarget();
     AbortReasonOr<Ok> jsop_checkisobj(uint8_t kind);
     AbortReasonOr<Ok> jsop_checkiscallable(uint8_t kind);
     AbortReasonOr<Ok> jsop_checkobjcoercible();
     AbortReasonOr<Ok> jsop_pushcallobj();
     AbortReasonOr<Ok> jsop_implicitthis(PropertyName* name);
+    AbortReasonOr<Ok> jsop_importmeta();
 
     /* Inlining. */
 
     enum InliningStatus
     {
         InliningStatus_NotInlined,
         InliningStatus_WarmUpCountTooLow,
         InliningStatus_Inlined
--- a/js/src/jit/JitFrames.cpp
+++ b/js/src/jit/JitFrames.cpp
@@ -169,17 +169,17 @@ class TryNoteIterIon : public TryNoteIte
       : TryNoteIter(cx, frame.script(), frame.pc(), IonFrameStackDepthOp(frame))
     { }
 };
 
 static void
 HandleExceptionIon(JSContext* cx, const InlineFrameIterator& frame, ResumeFromException* rfe,
                    bool* overrecursed)
 {
-    if (cx->compartment()->isDebuggee()) {
+    if (cx->realm()->isDebuggee()) {
         // We need to bail when there is a catchable exception, and we are the
         // debuggee of a Debugger with a live onExceptionUnwind hook, or if a
         // Debugger has observed this frame (e.g., for onPop).
         bool shouldBail = Debugger::hasLiveHook(cx->global(), Debugger::OnExceptionUnwind);
         RematerializedFrame* rematFrame = nullptr;
         if (!shouldBail) {
             JitActivation* act = cx->activation()->asJit();
             rematFrame = act->lookupRematerializedFrame(frame.frame().fp(), frame.frameNo());
@@ -647,17 +647,17 @@ HandleException(ResumeFromException* rfe
             // them.
             InlineFrameIterator frames(cx, &frame);
 
             // Invalidation state will be the same for all inlined scripts in the frame.
             IonScript* ionScript = nullptr;
             bool invalidated = frame.checkInvalidation(&ionScript);
 
 #ifdef JS_TRACE_LOGGING
-            if (logger && cx->compartment()->isDebuggee() && logger->enabled()) {
+            if (logger && cx->realm()->isDebuggee() && logger->enabled()) {
                 logger->disable(/* force = */ true,
                                 "Forcefully disabled tracelogger, due to "
                                 "throwing an exception with an active Debugger "
                                 "in IonMonkey.");
             }
 #endif
 
             for (;;) {
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -1389,17 +1389,17 @@ IonBuilder::inlineMathRandom(CallInfo& c
     }
 
     if (getInlineReturnType() != MIRType::Double)
         return InliningStatus_NotInlined;
 
     // MRandom JIT code directly accesses the RNG. It's (barely) possible to
     // inline Math.random without it having been called yet, so ensure RNG
     // state that isn't guaranteed to be initialized already.
-    script()->compartment()->ensureRandomNumberGenerator();
+    script()->realm()->getOrCreateRandomNumberGenerator();
 
     callInfo.setImplicitlyUsedUnchecked();
 
     MRandom* rand = MRandom::New(alloc());
     current->add(rand);
     current->push(rand);
     return InliningStatus_Inlined;
 }
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -1131,17 +1131,17 @@ OnDebuggerStatement(JSContext* cx, Basel
         MOZ_CRASH("Invalid OnDebuggerStatement resume mode");
     }
 }
 
 bool
 GlobalHasLiveOnDebuggerStatement(JSContext* cx)
 {
     AutoUnsafeCallWithABI unsafe;
-    return cx->compartment()->isDebuggee() &&
+    return cx->realm()->isDebuggee() &&
            Debugger::hasLiveHook(cx->global(), Debugger::OnDebuggerStatement);
 }
 
 bool
 PushLexicalEnv(JSContext* cx, BaselineFrame* frame, Handle<LexicalScope*> scope)
 {
     return frame->pushLexicalEnvironment(cx, scope);
 }
@@ -1186,17 +1186,17 @@ DebugLeaveThenRecreateLexicalEnv(JSConte
     MOZ_ALWAYS_TRUE(DebugLeaveLexicalEnv(cx, frame, pc));
     return frame->recreateLexicalEnvironment(cx);
 }
 
 bool
 DebugLeaveLexicalEnv(JSContext* cx, BaselineFrame* frame, jsbytecode* pc)
 {
     MOZ_ASSERT(frame->script()->baselineScript()->hasDebugInstrumentation());
-    if (cx->compartment()->isDebuggee())
+    if (cx->realm()->isDebuggee())
         DebugEnvironments::onPopLexical(cx, frame, pc);
     return true;
 }
 
 bool
 PushVarEnv(JSContext* cx, BaselineFrame* frame, HandleScope scope)
 {
     return frame->pushVarEnvironment(cx, scope);
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -256,16 +256,17 @@ MSG_DEF(JSMSG_FINALLY_WITHOUT_TRY,     0
 MSG_DEF(JSMSG_FORBIDDEN_AS_STATEMENT,  1, JSEXN_SYNTAXERR, "{0} can't appear in single-statement context")
 MSG_DEF(JSMSG_FOR_AWAIT_OUTSIDE_ASYNC, 0, JSEXN_SYNTAXERR, "for await (... of ...) is only valid in async functions and async generators")
 MSG_DEF(JSMSG_FROM_AFTER_IMPORT_CLAUSE, 0, JSEXN_SYNTAXERR, "missing keyword 'from' after import clause")
 MSG_DEF(JSMSG_FROM_AFTER_EXPORT_STAR,  0, JSEXN_SYNTAXERR, "missing keyword 'from' after export *")
 MSG_DEF(JSMSG_GARBAGE_AFTER_INPUT,     2, JSEXN_SYNTAXERR, "unexpected garbage after {0}, starting with {1}")
 MSG_DEF(JSMSG_IDSTART_AFTER_NUMBER,    0, JSEXN_SYNTAXERR, "identifier starts immediately after numeric literal")
 MSG_DEF(JSMSG_BAD_ESCAPE,              0, JSEXN_SYNTAXERR, "invalid escape sequence")
 MSG_DEF(JSMSG_ILLEGAL_CHARACTER,       0, JSEXN_SYNTAXERR, "illegal character")
+MSG_DEF(JSMSG_IMPORT_OUTSIDE_MODULE,   0, JSEXN_SYNTAXERR, "the import keyword may only appear in a module")
 MSG_DEF(JSMSG_IMPORT_DECL_AT_TOP_LEVEL, 0, JSEXN_SYNTAXERR, "import declarations may only appear at top level of a module")
 MSG_DEF(JSMSG_OF_AFTER_FOR_LOOP_DECL,  0, JSEXN_SYNTAXERR, "a declaration in the head of a for-of loop can't have an initializer")
 MSG_DEF(JSMSG_IN_AFTER_LEXICAL_FOR_DECL,0,JSEXN_SYNTAXERR, "a lexical declaration in the head of a for-in loop can't have an initializer")
 MSG_DEF(JSMSG_INVALID_FOR_IN_DECL_WITH_INIT,0,JSEXN_SYNTAXERR,"for-in loop head declarations may not have initializers")
 MSG_DEF(JSMSG_INVALID_ID,              1, JSEXN_SYNTAXERR, "{0} is an invalid identifier")
 MSG_DEF(JSMSG_LABEL_NOT_FOUND,         0, JSEXN_SYNTAXERR, "label not found")
 MSG_DEF(JSMSG_LEXICAL_DECL_NOT_IN_BLOCK,   1, JSEXN_SYNTAXERR, "{0} declaration not directly within block")
 MSG_DEF(JSMSG_LEXICAL_DECL_LABEL,      1, JSEXN_SYNTAXERR, "{0} declarations cannot be labelled")
--- a/js/src/jsapi-tests/testBinASTReader.cpp
+++ b/js/src/jsapi-tests/testBinASTReader.cpp
@@ -172,17 +172,17 @@ runTestFromPath(JSContext* cx, const cha
         RootedScriptSourceObject sourceObject(cx, frontend::CreateScriptSourceObject(
                                                   cx, txtOptions, mozilla::Nothing()));
         if (!sourceObject)
             MOZ_CRASH("Couldn't initialize ScriptSourceObject");
 
         js::frontend::Parser<js::frontend::FullParseHandler, char16_t> txtParser(
             cx, allocScope.alloc(), txtOptions, txtSource.begin(), txtSource.length(),
             /* foldConstants = */ false, txtUsedNames, nullptr,
-            nullptr, sourceObject);
+            nullptr, sourceObject, frontend::ParseGoal::Script);
         if (!txtParser.checkOptions())
             MOZ_CRASH("Bad options");
 
         auto txtParsed = txtParser.parse(); // Will be deallocated once `parser` goes out of scope.
         RootedValue txtExn(cx);
         if (!txtParsed) {
             // Save exception for more detailed error message, if necessary.
             if (!js::GetAndClearException(cx, &txtExn))
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -4060,21 +4060,21 @@ JS::OwningCompileOptions::setIntroducerF
 }
 
 JS::CompileOptions::CompileOptions(JSContext* cx)
     : ReadOnlyCompileOptions(), elementRoot(cx), elementAttributeNameRoot(cx),
       introductionScriptRoot(cx)
 {
     strictOption = cx->options().strictMode();
     extraWarningsOption = cx->realm()->behaviors().extraWarnings(cx);
-    isProbablySystemCode = cx->compartment()->isProbablySystemCode();
+    isProbablySystemCode = cx->realm()->isProbablySystemCode();
     werrorOption = cx->options().werror();
     if (!cx->options().asmJS())
         asmJSOption = AsmJSOption::Disabled;
-    else if (cx->compartment()->debuggerObservesAsmJS())
+    else if (cx->realm()->debuggerObservesAsmJS())
         asmJSOption = AsmJSOption::DisabledByDebugger;
     else
         asmJSOption = AsmJSOption::Enabled;
     throwOnAsmJSValidationFailureOption = cx->options().throwOnAsmJSValidationFailure();
 }
 
 static bool
 Compile(JSContext* cx, const ReadOnlyCompileOptions& options,
@@ -4474,17 +4474,18 @@ JS_BufferIsCompilableUnit(JSContext* cx,
                                                                                  mozilla::Nothing()));
     if (!sourceObject)
         return false;
 
     frontend::Parser<frontend::FullParseHandler, char16_t> parser(cx, cx->tempLifoAlloc(),
                                                                   options, chars, length,
                                                                   /* foldConstants = */ true,
                                                                   usedNames, nullptr, nullptr,
-                                                                  sourceObject);
+                                                                  sourceObject,
+                                                                  frontend::ParseGoal::Script);
     JS::WarningReporter older = JS::SetWarningReporter(cx, nullptr);
     if (!parser.checkOptions() || !parser.parse()) {
         // We ran into an error. If it was because we ran out of source, we
         // return false so our caller knows to try to collect more buffered
         // source.
         if (parser.isUnexpectedEOF())
             result = false;
 
@@ -4955,16 +4956,30 @@ JS::GetModuleResolveHook(JSRuntime* rt)
 
 JS_PUBLIC_API(void)
 JS::SetModuleResolveHook(JSRuntime* rt, JS::ModuleResolveHook func)
 {
     AssertHeapIsIdle();
     rt->moduleResolveHook = func;
 }
 
+JS_PUBLIC_API(JS::ModuleMetadataHook)
+JS::GetModuleMetadataHook(JSRuntime* rt)
+{
+    AssertHeapIsIdle();
+    return rt->moduleMetadataHook;
+}
+
+JS_PUBLIC_API(void)
+JS::SetModuleMetadataHook(JSRuntime* rt, JS::ModuleMetadataHook func)
+{
+    AssertHeapIsIdle();
+    rt->moduleMetadataHook = func;
+}
+
 JS_PUBLIC_API(bool)
 JS::CompileModule(JSContext* cx, const ReadOnlyCompileOptions& options,
                   SourceBufferHolder& srcBuf, JS::MutableHandleObject module)
 {
     MOZ_ASSERT(!cx->realm()->isAtomsRealm());
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
 
@@ -7493,17 +7508,17 @@ DescribeScriptedCaller(JSContext* cx, Au
     if (lineno)
         *lineno = 0;
     if (column)
         *column = 0;
 
     if (!cx->compartment())
         return false;
 
-    NonBuiltinFrameIter i(cx, cx->compartment()->principals());
+    NonBuiltinFrameIter i(cx, cx->realm()->principals());
     if (i.done())
         return false;
 
     // If the caller is hidden, the embedding wants us to return false here so
     // that it can check its own stack (see HideScriptedCaller).
     if (i.activation()->scriptedCallerIsHidden())
         return false;
 
@@ -7767,17 +7782,17 @@ JS_PUBLIC_API(void)
 JS::SetOutOfMemoryCallback(JSContext* cx, OutOfMemoryCallback cb, void* data)
 {
     cx->runtime()->oomCallback = cb;
     cx->runtime()->oomCallbackData = data;
 }
 
 JS::FirstSubsumedFrame::FirstSubsumedFrame(JSContext* cx,
                                            bool ignoreSelfHostedFrames /* = true */)
-  : JS::FirstSubsumedFrame(cx, cx->compartment()->principals(), ignoreSelfHostedFrames)
+  : JS::FirstSubsumedFrame(cx, cx->realm()->principals(), ignoreSelfHostedFrames)
 { }
 
 JS_PUBLIC_API(bool)
 JS::CaptureCurrentStack(JSContext* cx, JS::MutableHandleObject stackp,
                         JS::StackCapture&& capture /* = JS::StackCapture(JS::AllFrames()) */)
 {
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -3920,16 +3920,31 @@ extern JS_PUBLIC_API(ModuleResolveHook)
 GetModuleResolveHook(JSRuntime* rt);
 
 /**
  * Set the HostResolveImportedModule hook for the runtime to the given function.
  */
 extern JS_PUBLIC_API(void)
 SetModuleResolveHook(JSRuntime* rt, ModuleResolveHook func);
 
+using ModuleMetadataHook = bool (*)(JSContext*, HandleObject, HandleObject);
+
+/**
+ * Get the hook for populating the import.meta metadata object.
+ */
+extern JS_PUBLIC_API(ModuleMetadataHook)
+GetModuleMetadataHook(JSRuntime* rt);
+
+/**
+ * Set the hook for populating the import.meta metadata object to the given
+ * function.
+ */
+extern JS_PUBLIC_API(void)
+SetModuleMetadataHook(JSRuntime* rt, ModuleMetadataHook func);
+
 /**
  * Parse the given source buffer as a module in the scope of the current global
  * of cx and return a source text module record.
  */
 extern JS_PUBLIC_API(bool)
 CompileModule(JSContext* cx, const ReadOnlyCompileOptions& options,
               SourceBufferHolder& srcBuf, JS::MutableHandleObject moduleRecord);
 
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -441,17 +441,17 @@ Error(JSContext* cx, unsigned argc, Valu
     RootedString message(cx, nullptr);
     if (args.hasDefined(0)) {
         message = ToString<CanGC>(cx, args[0]);
         if (!message)
             return false;
     }
 
     /* Find the scripted caller, but only ones we're allowed to know about. */
-    NonBuiltinFrameIter iter(cx, cx->compartment()->principals());
+    NonBuiltinFrameIter iter(cx, cx->realm()->principals());
 
     /* Set the 'fileName' property. */
     RootedString fileName(cx);
     if (args.length() > 1) {
         fileName = ToString<CanGC>(cx, args[1]);
     } else {
         fileName = cx->runtime()->emptyString;
         if (!iter.done()) {
@@ -633,20 +633,20 @@ js::GetErrorTypeName(JSContext* cx, int1
 
 void
 js::ErrorToException(JSContext* cx, JSErrorReport* reportp,
                      JSErrorCallback callback, void* userRef)
 {
     MOZ_ASSERT(reportp);
     MOZ_ASSERT(!JSREPORT_IS_WARNING(reportp->flags));
 
-    // We cannot throw a proper object inside the self-hosting compartment, as
-    // we cannot construct the Error constructor without self-hosted code. Just
+    // We cannot throw a proper object inside the self-hosting realm, as we
+    // cannot construct the Error constructor without self-hosted code. Just
     // print the error to stderr to help debugging.
-    if (cx->runtime()->isSelfHostingCompartment(cx->compartment())) {
+    if (cx->realm()->isSelfHostingRealm()) {
         PrintError(cx, stderr, JS::ConstUTF8CharsZ(), reportp, true);
         return;
     }
 
     // Find the exception index associated with this error.
     JSErrNum errorNumber = static_cast<JSErrNum>(reportp->errorNumber);
     if (!callback)
         callback = GetErrorMessage;
@@ -962,17 +962,17 @@ bool
 ErrorReport::populateUncaughtExceptionReportUTF8VA(JSContext* cx, va_list ap)
 {
     new (&ownedReport) JSErrorReport();
     ownedReport.flags = JSREPORT_ERROR;
     ownedReport.errorNumber = JSMSG_UNCAUGHT_EXCEPTION;
     // XXXbz this assumes the stack we have right now is still
     // related to our exception object.  It would be better if we
     // could accept a passed-in stack of some sort instead.
-    NonBuiltinFrameIter iter(cx, cx->compartment()->principals());
+    NonBuiltinFrameIter iter(cx, cx->realm()->principals());
     if (!iter.done()) {
         ownedReport.filename = iter.filename();
         uint32_t column;
         ownedReport.lineno = iter.computeLine(&column);
         ownedReport.column = FixupColumnForDisplay(column);
         ownedReport.isMuted = iter.mutedErrors();
     }
 
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -150,50 +150,52 @@ JS_FRIEND_API(bool)
 JS_GetIsSecureContext(JSCompartment* compartment)
 {
     return JS::GetRealmForCompartment(compartment)->creationOptions().secureContext();
 }
 
 JS_FRIEND_API(JSPrincipals*)
 JS_GetCompartmentPrincipals(JSCompartment* compartment)
 {
-    return compartment->principals();
+    Realm* realm = JS::GetRealmForCompartment(compartment);
+    return realm->principals();
 }
 
 JS_FRIEND_API(void)
 JS_SetCompartmentPrincipals(JSCompartment* compartment, JSPrincipals* principals)
 {
     // Short circuit if there's no change.
-    if (principals == compartment->principals())
+    Realm* realm = JS::GetRealmForCompartment(compartment);
+    if (principals == realm->principals())
         return;
 
-    // Any compartment with the trusted principals -- and there can be
-    // multiple -- is a system compartment.
-    const JSPrincipals* trusted = compartment->runtimeFromMainThread()->trustedPrincipals();
+    // Any realm with the trusted principals -- and there can be
+    // multiple -- is a system realm.
+    const JSPrincipals* trusted = realm->runtimeFromMainThread()->trustedPrincipals();
     bool isSystem = principals && principals == trusted;
 
     // Clear out the old principals, if any.
-    if (compartment->principals()) {
-        JS_DropPrincipals(TlsContext.get(), compartment->principals());
-        compartment->setPrincipals(nullptr);
+    if (realm->principals()) {
+        JS_DropPrincipals(TlsContext.get(), realm->principals());
+        realm->setPrincipals(nullptr);
         // We'd like to assert that our new principals is always same-origin
         // with the old one, but JSPrincipals doesn't give us a way to do that.
         // But we can at least assert that we're not switching between system
         // and non-system.
-        MOZ_ASSERT(compartment->isSystem() == isSystem);
+        MOZ_ASSERT(realm->isSystem() == isSystem);
     }
 
     // Set up the new principals.
     if (principals) {
         JS_HoldPrincipals(principals);
-        compartment->setPrincipals(principals);
+        realm->setPrincipals(principals);
     }
 
     // Update the system flag.
-    compartment->setIsSystem(isSystem);
+    realm->setIsSystem(isSystem);
 }
 
 JS_FRIEND_API(JSPrincipals*)
 JS_GetScriptPrincipals(JSScript* script)
 {
     return script->principals();
 }
 
@@ -328,17 +330,17 @@ JS_FRIEND_API(JS::Zone*)
 js::GetCompartmentZone(JSCompartment* comp)
 {
     return comp->zone();
 }
 
 JS_FRIEND_API(bool)
 js::IsSystemCompartment(JSCompartment* comp)
 {
-    return comp->isSystem();
+    return JS::GetRealmForCompartment(comp)->isSystem();
 }
 
 JS_FRIEND_API(bool)
 js::IsSystemZone(Zone* zone)
 {
     return zone->isSystem;
 }
 
--- a/js/src/jsmath.cpp
+++ b/js/src/jsmath.cpp
@@ -711,32 +711,32 @@ js::GenerateXorShift128PlusSeed(mozilla:
 {
     // XorShift128PlusRNG must be initialized with a non-zero seed.
     do {
         seed[0] = GenerateRandomSeed();
         seed[1] = GenerateRandomSeed();
     } while (seed[0] == 0 && seed[1] == 0);
 }
 
-void
-JSCompartment::ensureRandomNumberGenerator()
+mozilla::non_crypto::XorShift128PlusRNG&
+Realm::getOrCreateRandomNumberGenerator()
 {
-    if (randomNumberGenerator.isNothing()) {
+    if (randomNumberGenerator_.isNothing()) {
         mozilla::Array<uint64_t, 2> seed;
         GenerateXorShift128PlusSeed(seed);
-        randomNumberGenerator.emplace(seed[0], seed[1]);
+        randomNumberGenerator_.emplace(seed[0], seed[1]);
     }
+
+    return randomNumberGenerator_.ref();
 }
 
 double
 js::math_random_impl(JSContext* cx)
 {
-    JSCompartment* comp = cx->compartment();
-    comp->ensureRandomNumberGenerator();
-    return comp->randomNumberGenerator.ref().nextDouble();
+    return cx->realm()->getOrCreateRandomNumberGenerator().nextDouble();
 }
 
 bool
 js::math_random(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     args.rval().setDouble(math_random_impl(cx));
     return true;
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -4307,16 +4307,34 @@ CallModuleResolveHook(JSContext* cx, Han
          JS_ReportErrorASCII(cx, "Module resolve hook did not return Module object");
          return nullptr;
     }
 
     return &result.toObject();
 }
 
 static bool
+ShellModuleMetadataHook(JSContext* cx, HandleObject module, HandleObject metaObject)
+{
+    // For the shell, just use the script's filename as the base URL.
+    RootedScript script(cx, module->as<ModuleObject>().script());
+    const char* filename = script->scriptSource()->filename();
+    MOZ_ASSERT(filename);
+
+    RootedString url(cx, NewStringCopyZ<CanGC>(cx, filename));
+    if (!url)
+        return false;
+
+    if (!JS_DefineProperty(cx, metaObject, "url", url, JSPROP_ENUMERATE))
+        return false;
+
+    return true;
+}
+
+static bool
 GetModuleLoadPath(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     ShellContext* sc = GetShellContext(cx);
     if (sc->moduleLoadPath) {
         JSString* str = JS_NewStringCopyZ(cx, sc->moduleLoadPath.get());
         if (!str)
@@ -4513,17 +4531,17 @@ Parse(JSContext* cx, unsigned argc, Valu
 
     RootedScriptSourceObject sourceObject(cx, frontend::CreateScriptSourceObject(cx, options,
                                                                                  Nothing()));
     if (!sourceObject)
         return false;
 
     Parser<FullParseHandler, char16_t> parser(cx, cx->tempLifoAlloc(), options, chars, length,
                                               /* foldConstants = */ false, usedNames, nullptr,
-                                              nullptr, sourceObject);
+                                              nullptr, sourceObject, ParseGoal::Script);
     if (!parser.checkOptions())
         return false;
 
     ParseNode* pn = parser.parse(); // Deallocated once `parser` goes out of scope.
     if (!pn)
         return false;
 #ifdef DEBUG
     js::Fprinter out(stderr);
@@ -4571,17 +4589,18 @@ SyntaxParse(JSContext* cx, unsigned argc
     RootedScriptSourceObject sourceObject(cx, frontend::CreateScriptSourceObject(cx, options,
                                                                                  Nothing()));
     if (!sourceObject)
         return false;
 
     Parser<frontend::SyntaxParseHandler, char16_t> parser(cx, cx->tempLifoAlloc(),
                                                           options, chars, length, false,
                                                           usedNames, nullptr, nullptr,
-                                                          sourceObject);
+                                                          sourceObject,
+                                                          frontend::ParseGoal::Script);
     if (!parser.checkOptions())
         return false;
 
     bool succeeded = parser.parse();
     if (cx->isExceptionPending())
         return false;
 
     if (!succeeded && !parser.hadAbortedSyntaxParse()) {
@@ -9310,16 +9329,17 @@ main(int argc, char** argv, char** envp)
         JS_SetGCParameter(cx, JSGC_DYNAMIC_MARK_SLICE, 1);
         JS_SetGCParameter(cx, JSGC_SLICE_TIME_BUDGET, 10);
     }
 #endif
 
     js::SetPreserveWrapperCallback(cx, DummyPreserveWrapperCallback);
 
     JS::SetModuleResolveHook(cx->runtime(), CallModuleResolveHook);
+    JS::SetModuleMetadataHook(cx->runtime(), ShellModuleMetadataHook);
 
     result = Shell(cx, &op, envp);
 
 #ifdef DEBUG
     if (OOM_printAllocationCount)
         printf("OOM max count: %" PRIu64 "\n", js::oom::counter);
 #endif
 
--- a/js/src/vm/ArgumentsObject.cpp
+++ b/js/src/vm/ArgumentsObject.cpp
@@ -234,23 +234,23 @@ ArgumentsObject::createTemplateObject(JS
                                                              shape, group));
 
     ArgumentsObject* obj = &base->as<js::ArgumentsObject>();
     obj->initFixedSlot(ArgumentsObject::DATA_SLOT, PrivateValue(nullptr));
     return obj;
 }
 
 ArgumentsObject*
-JSCompartment::maybeArgumentsTemplateObject(bool mapped) const
+Realm::maybeArgumentsTemplateObject(bool mapped) const
 {
     return mapped ? mappedArgumentsTemplate_ : unmappedArgumentsTemplate_;
 }
 
 ArgumentsObject*
-JSCompartment::getOrCreateArgumentsTemplateObject(JSContext* cx, bool mapped)
+Realm::getOrCreateArgumentsTemplateObject(JSContext* cx, bool mapped)
 {
     ReadBarriered<ArgumentsObject*>& obj =
         mapped ? mappedArgumentsTemplate_ : unmappedArgumentsTemplate_;
 
     ArgumentsObject* templateObj = obj;
     if (templateObj)
         return templateObj;
 
@@ -262,17 +262,17 @@ JSCompartment::getOrCreateArgumentsTempl
     return templateObj;
 }
 
 template <typename CopyArgs>
 /* static */ ArgumentsObject*
 ArgumentsObject::create(JSContext* cx, HandleFunction callee, unsigned numActuals, CopyArgs& copy)
 {
     bool mapped = callee->nonLazyScript()->hasMappedArgsObj();
-    ArgumentsObject* templateObj = cx->compartment()->getOrCreateArgumentsTemplateObject(cx, mapped);
+    ArgumentsObject* templateObj = cx->realm()->getOrCreateArgumentsTemplateObject(cx, mapped);
     if (!templateObj)
         return nullptr;
 
     RootedShape shape(cx, templateObj->lastProperty());
     RootedObjectGroup group(cx, templateObj->group());
 
     unsigned numFormals = callee->nargs();
     unsigned numArgs = Max(numActuals, numFormals);
--- a/js/src/vm/ArrayObject-inl.h
+++ b/js/src/vm/ArrayObject-inl.h
@@ -56,17 +56,17 @@ ArrayObject::createArrayInternal(JSConte
     ArrayObject* aobj = static_cast<ArrayObject*>(obj);
     aobj->initGroup(group);
     aobj->initShape(shape);
     // NOTE: Dynamic slots are created internally by Allocate<JSObject>.
     if (!nDynamicSlots)
         aobj->initSlots(nullptr);
 
     MOZ_ASSERT(clasp->shouldDelayMetadataBuilder());
-    cx->compartment()->setObjectPendingMetadata(cx, aobj);
+    cx->realm()->setObjectPendingMetadata(cx, aobj);
 
     return aobj;
 }
 
 /* static */ inline ArrayObject*
 ArrayObject::finishCreateArray(ArrayObject* obj, HandleShape shape, AutoSetNewObjectMetadata& metadata)
 {
     size_t span = shape->slotSpan();
--- a/js/src/vm/Caches-inl.h
+++ b/js/src/vm/Caches-inl.h
@@ -66,17 +66,17 @@ NewObjectCache::newObjectFromHit(JSConte
                                                                             /* nDynamicSlots = */ 0,
                                                                             heap, group->clasp()));
     if (!obj)
         return nullptr;
 
     copyCachedToObject(obj, templateObj, entry->kind);
 
     if (group->clasp()->shouldDelayMetadataBuilder())
-        cx->compartment()->setObjectPendingMetadata(cx, obj);
+        cx->realm()->setObjectPendingMetadata(cx, obj);
     else
         obj = static_cast<NativeObject*>(SetNewObjectMetadata(cx, obj));
 
     probes::CreateObject(cx, obj);
     gc::gcTracer.traceCreateObject(obj);
     return obj;
 }
 
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -225,16 +225,17 @@
     macro(lookupGetter, lookupGetter, "__lookupGetter__") \
     macro(lookupSetter, lookupSetter, "__lookupSetter__") \
     macro(ltr, ltr, "ltr") \
     macro(MapConstructorInit, MapConstructorInit, "MapConstructorInit") \
     macro(MapIterator, MapIterator, "Map Iterator") \
     macro(maximumFractionDigits, maximumFractionDigits, "maximumFractionDigits") \
     macro(maximumSignificantDigits, maximumSignificantDigits, "maximumSignificantDigits") \
     macro(message, message, "message") \
+    macro(meta, meta, "meta") \
     macro(minDays, minDays, "minDays") \
     macro(minimumFractionDigits, minimumFractionDigits, "minimumFractionDigits") \
     macro(minimumIntegerDigits, minimumIntegerDigits, "minimumIntegerDigits") \
     macro(minimumSignificantDigits, minimumSignificantDigits, "minimumSignificantDigits") \
     macro(minusSign, minusSign, "minusSign") \
     macro(minute, minute, "minute") \
     macro(missingArguments, missingArguments, "missingArguments") \
     macro(mode, mode, "mode") \
--- a/js/src/vm/Debugger-inl.h
+++ b/js/src/vm/Debugger-inl.h
@@ -31,64 +31,64 @@ js::Debugger::fromJSObject(const JSObjec
 {
     MOZ_ASSERT(obj->getClass() == &class_);
     return (Debugger*) obj->as<NativeObject>().getPrivate();
 }
 
 /* static */ inline bool
 js::Debugger::checkNoExecute(JSContext* cx, HandleScript script)
 {
-    if (!cx->compartment()->isDebuggee() || !cx->noExecuteDebuggerTop)
+    if (!cx->realm()->isDebuggee() || !cx->noExecuteDebuggerTop)
         return true;
     return slowPathCheckNoExecute(cx, script);
 }
 
 /* static */ js::ResumeMode
 js::Debugger::onEnterFrame(JSContext* cx, AbstractFramePtr frame)
 {
     MOZ_ASSERT_IF(frame.hasScript() && frame.script()->isDebuggee(), frame.isDebuggee());
     if (!frame.isDebuggee())
         return ResumeMode::Continue;
     return slowPathOnEnterFrame(cx, frame);
 }
 
 /* static */ js::ResumeMode
 js::Debugger::onDebuggerStatement(JSContext* cx, AbstractFramePtr frame)
 {
-    if (!cx->compartment()->isDebuggee())
+    if (!cx->realm()->isDebuggee())
         return ResumeMode::Continue;
     return slowPathOnDebuggerStatement(cx, frame);
 }
 
 /* static */ js::ResumeMode
 js::Debugger::onExceptionUnwind(JSContext* cx, AbstractFramePtr frame)
 {
-    if (!cx->compartment()->isDebuggee())
+    if (!cx->realm()->isDebuggee())
         return ResumeMode::Continue;
     return slowPathOnExceptionUnwind(cx, frame);
 }
 
 /* static */ void
 js::Debugger::onNewWasmInstance(JSContext* cx, Handle<WasmInstanceObject*> wasmInstance)
 {
-    if (cx->compartment()->isDebuggee())
+    if (cx->realm()->isDebuggee())
         slowPathOnNewWasmInstance(cx, wasmInstance);
 }
 
 /* static */ void
 js::Debugger::onNewPromise(JSContext* cx, Handle<PromiseObject*> promise)
 {
-    if (MOZ_UNLIKELY(cx->compartment()->isDebuggee()))
+    if (MOZ_UNLIKELY(cx->realm()->isDebuggee()))
         slowPathPromiseHook(cx, Debugger::OnNewPromise, promise);
 }
 
 /* static */ void
 js::Debugger::onPromiseSettled(JSContext* cx, Handle<PromiseObject*> promise)
 {
-    if (MOZ_UNLIKELY(cx->compartment()->isDebuggee()))
+    if (MOZ_UNLIKELY(cx->realm()->isDebuggee()))
         slowPathPromiseHook(cx, Debugger::OnPromiseSettled, promise);
 }
 
 inline js::Debugger*
 js::DebuggerEnvironment::owner() const
 {
     JSObject* dbgobj = &getReservedSlot(OWNER_SLOT).toObject();
     return Debugger::fromJSObject(dbgobj);
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -273,35 +273,35 @@ ValueToIdentifier(JSContext* cx, HandleV
         ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE,
                               JSDVG_SEARCH_STACK, val, nullptr, "not an identifier",
                               nullptr);
         return false;
     }
     return true;
 }
 
-class AutoRestoreCompartmentDebugMode
-{
-    JSCompartment* comp_;
+class js::AutoRestoreRealmDebugMode
+{
+    Realm* realm_;
     unsigned bits_;
 
   public:
-    explicit AutoRestoreCompartmentDebugMode(JSCompartment* comp)
-      : comp_(comp), bits_(comp->debugModeBits)
+    explicit AutoRestoreRealmDebugMode(Realm* realm)
+      : realm_(realm), bits_(realm->debugModeBits_)
     {
-        MOZ_ASSERT(comp_);
-    }
-
-    ~AutoRestoreCompartmentDebugMode() {
-        if (comp_)
-            comp_->debugModeBits = bits_;
+        MOZ_ASSERT(realm_);
+    }
+
+    ~AutoRestoreRealmDebugMode() {
+        if (realm_)
+            realm_->debugModeBits_ = bits_;
     }
 
     void release() {
-        comp_ = nullptr;
+        realm_ = nullptr;
     }
 };
 
 // Given a Debugger instance dbg, if it is enabled, prevents all its debuggee
 // compartments from executing scripts. Attempts to run script will throw an
 // instance of Debugger.DebuggeeWouldRun from the topmost locked Debugger's
 // compartment.
 class MOZ_RAII js::EnterDebuggeeNoExecute
@@ -413,17 +413,17 @@ class MOZ_RAII js::LeaveDebuggeeNoExecut
             prevLocked_->unlocked_ = nullptr;
         }
     }
 };
 
 /* static */ bool
 Debugger::slowPathCheckNoExecute(JSContext* cx, HandleScript script)
 {
-    MOZ_ASSERT(cx->compartment()->isDebuggee());
+    MOZ_ASSERT(cx->realm()->isDebuggee());
     MOZ_ASSERT(cx->noExecuteDebuggerTop);
     return EnterDebuggeeNoExecute::reportIfFoundInStack(cx, script);
 }
 
 static inline void
 NukeDebuggerWrapper(NativeObject *wrapper)
 {
     // In some OOM failure cases, we need to destroy the edge to the referent,
@@ -2362,45 +2362,46 @@ Debugger::slowPathPromiseHook(JSContext*
     // Promise hooks are infallible and we ignore errors from uncaught
     // exceptions by design.
     MOZ_ASSERT(resumeMode == ResumeMode::Continue);
 }
 
 
 /*** Debugger code invalidation for observing execution ******************************************/
 
-class MOZ_RAII ExecutionObservableCompartments : public Debugger::ExecutionObservableSet
-{
-    HashSet<JSCompartment*> compartments_;
+class MOZ_RAII ExecutionObservableRealms : public Debugger::ExecutionObservableSet
+{
+    HashSet<Realm*> realms_;
     HashSet<Zone*> zones_;
 
   public:
-    explicit ExecutionObservableCompartments(JSContext* cx
-                                             MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-      : compartments_(cx),
+    explicit ExecutionObservableRealms(JSContext* cx
+                                       MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+      : realms_(cx),
         zones_(cx)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     }
 
-    bool init() { return compartments_.init() && zones_.init(); }
-    bool add(JSCompartment* comp) { return compartments_.put(comp) && zones_.put(comp->zone()); }
-
-    typedef HashSet<JSCompartment*>::Range CompartmentRange;
-    const HashSet<JSCompartment*>* compartments() const { return &compartments_; }
+    bool init() { return realms_.init() && zones_.init(); }
+    bool add(Realm* realm) { return realms_.put(realm) && zones_.put(realm->zone()); }
+
+    using RealmRange = HashSet<Realm*>::Range;
+    const HashSet<Realm*>* realms() const { return &realms_; }
 
     const HashSet<Zone*>* zones() const override { return &zones_; }
     bool shouldRecompileOrInvalidate(JSScript* script) const override {
-        return script->hasBaselineScript() && compartments_.has(script->compartment());
+        return script->hasBaselineScript() && realms_.has(script->realm());
     }
     bool shouldMarkAsDebuggee(FrameIter& iter) const override {
         // AbstractFramePtr can't refer to non-remateralized Ion frames or
         // non-debuggee wasm frames, so if iter refers to one such, we know we
         // don't match.
-        return iter.hasUsableAbstractFramePtr() && compartments_.has(iter.compartment());
+        return iter.hasUsableAbstractFramePtr() &&
+               realms_.has(JS::GetRealmForCompartment(iter.compartment()));
     }
 
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 // Given a particular AbstractFramePtr F that has become observable, this
 // represents the stack frames that need to be bailed out or marked as
 // debuggees, and the scripts that need to be recompiled, taking inlining into
@@ -2738,22 +2739,23 @@ Debugger::ensureExecutionObservabilityOf
         return true;
     ExecutionObservableFrame obs(frame);
     return updateExecutionObservabilityOfFrames(cx, obs, Observing);
 }
 
 /* static */ bool
 Debugger::ensureExecutionObservabilityOfCompartment(JSContext* cx, JSCompartment* comp)
 {
-    if (comp->debuggerObservesAllExecution())
+    Realm* realm = JS::GetRealmForCompartment(comp);
+    if (realm->debuggerObservesAllExecution())
         return true;
-    ExecutionObservableCompartments obs(cx);
-    if (!obs.init() || !obs.add(comp))
-        return false;
-    comp->updateDebuggerObservesAllExecution();
+    ExecutionObservableRealms obs(cx);
+    if (!obs.init() || !obs.add(realm))
+        return false;
+    realm->updateDebuggerObservesAllExecution();
     return updateExecutionObservability(cx, obs, Observing);
 }
 
 /* static */ bool
 Debugger::hookObservesAllExecution(Hook which)
 {
     return which == OnEnterFrame;
 }
@@ -2791,61 +2793,61 @@ Debugger::observesCoverage() const
 }
 
 // Toggle whether this Debugger's debuggees observe all execution. This is
 // called when a hook that observes all execution is set or unset. See
 // hookObservesAllExecution.
 bool
 Debugger::updateObservesAllExecutionOnDebuggees(JSContext* cx, IsObserving observing)
 {
-    ExecutionObservableCompartments obs(cx);
+    ExecutionObservableRealms obs(cx);
     if (!obs.init())
         return false;
 
     for (WeakGlobalObjectSet::Range r = debuggees.all(); !r.empty(); r.popFront()) {
         GlobalObject* global = r.front();
-        JSCompartment* comp = global->compartment();
-
-        if (comp->debuggerObservesAllExecution() == observing)
+        JS::Realm* realm = global->realm();
+
+        if (realm->debuggerObservesAllExecution() == observing)
             continue;
 
-        // It's expensive to eagerly invalidate and recompile a compartment,
-        // so add the compartment to the set only if we are observing.
-        if (observing && !obs.add(comp))
+        // It's expensive to eagerly invalidate and recompile a realm,
+        // so add the realm to the set only if we are observing.
+        if (observing && !obs.add(realm))
             return false;
     }
 
     if (!updateExecutionObservability(cx, obs, observing))
         return false;
 
-    typedef ExecutionObservableCompartments::CompartmentRange CompartmentRange;
-    for (CompartmentRange r = obs.compartments()->all(); !r.empty(); r.popFront())
+    using RealmRange = ExecutionObservableRealms::RealmRange;
+    for (RealmRange r = obs.realms()->all(); !r.empty(); r.popFront())
         r.front()->updateDebuggerObservesAllExecution();
 
     return true;
 }
 
 bool
 Debugger::updateObservesCoverageOnDebuggees(JSContext* cx, IsObserving observing)
 {
-    ExecutionObservableCompartments obs(cx);
+    ExecutionObservableRealms obs(cx);
     if (!obs.init())
         return false;
 
     for (WeakGlobalObjectSet::Range r = debuggees.all(); !r.empty(); r.popFront()) {
         GlobalObject* global = r.front();
-        JSCompartment* comp = global->compartment();
-
-        if (comp->debuggerObservesCoverage() == observing)
+        Realm* realm = global->realm();
+
+        if (realm->debuggerObservesCoverage() == observing)
             continue;
 
-        // Invalidate and recompile a compartment to add or remove PCCounts
+        // Invalidate and recompile a realm to add or remove PCCounts
         // increments. We have to eagerly invalidate, as otherwise we might have
         // dangling pointers to freed PCCounts.
-        if (!obs.add(comp))
+        if (!obs.add(realm))
             return false;
     }
 
     // If any frame on the stack belongs to the debuggee, then we cannot update
     // the ScriptCounts, because this would imply to invalidate a Debugger.Frame
     // to recompile it with/without ScriptCount support.
     for (FrameIter iter(cx);
          !iter.done();
@@ -2855,50 +2857,50 @@ Debugger::updateObservesCoverageOnDebugg
             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_NOT_IDLE);
             return false;
         }
     }
 
     if (!updateExecutionObservability(cx, obs, observing))
         return false;
 
-    // All compartments can safely be toggled, and all scripts will be
-    // recompiled. Thus we can update each compartment accordingly.
-    typedef ExecutionObservableCompartments::CompartmentRange CompartmentRange;
-    for (CompartmentRange r = obs.compartments()->all(); !r.empty(); r.popFront())
+    // All realms can safely be toggled, and all scripts will be recompiled.
+    // Thus we can update each realm accordingly.
+    using RealmRange = ExecutionObservableRealms::RealmRange;
+    for (RealmRange r = obs.realms()->all(); !r.empty(); r.popFront())
         r.front()->updateDebuggerObservesCoverage();
 
     return true;
 }
 
 void
 Debugger::updateObservesAsmJSOnDebuggees(IsObserving observing)
 {
     for (WeakGlobalObjectSet::Range r = debuggees.all(); !r.empty(); r.popFront()) {
         GlobalObject* global = r.front();
-        JSCompartment* comp = global->compartment();
-
-        if (comp->debuggerObservesAsmJS() == observing)
+        Realm* realm = global->realm();
+
+        if (realm->debuggerObservesAsmJS() == observing)
             continue;
 
-        comp->updateDebuggerObservesAsmJS();
+        realm->updateDebuggerObservesAsmJS();
     }
 }
 
 void
 Debugger::updateObservesBinarySourceDebuggees(IsObserving observing)
 {
     for (WeakGlobalObjectSet::Range r = debuggees.all(); !r.empty(); r.popFront()) {
         GlobalObject* global = r.front();
-        JSCompartment* comp = global->compartment();
-
-        if (comp->debuggerObservesBinarySource() == observing)
+        Realm* realm = global->realm();
+
+        if (realm->debuggerObservesBinarySource() == observing)
             continue;
 
-        comp->updateDebuggerObservesBinarySource();
+        realm->updateDebuggerObservesBinarySource();
     }
 }
 
 
 /*** Allocations Tracking *************************************************************************/
 
 /* static */ bool
 Debugger::cannotTrackAllocations(const GlobalObject& global)
@@ -3589,18 +3591,18 @@ Debugger::setAllowUnobservedAsmJS(JSCont
 {
     THIS_DEBUGGER(cx, argc, vp, "set allowUnobservedAsmJS", args, dbg);
     if (!args.requireAtLeast(cx, "Debugger.set allowUnobservedAsmJS", 1))
         return false;
     dbg->allowUnobservedAsmJS = ToBoolean(args[0]);
 
     for (WeakGlobalObjectSet::Range r = dbg->debuggees.all(); !r.empty(); r.popFront()) {
         GlobalObject* global = r.front();
-        JSCompartment* comp = global->compartment();
-        comp->updateDebuggerObservesAsmJS();
+        Realm* realm = global->realm();
+        realm->updateDebuggerObservesAsmJS();
     }
 
     args.rval().setUndefined();
     return true;
 }
 
 /* static */ bool
 Debugger::getAllowWasmBinarySource(JSContext* cx, unsigned argc, Value* vp)
@@ -3615,18 +3617,18 @@ Debugger::setAllowWasmBinarySource(JSCon
 {
     THIS_DEBUGGER(cx, argc, vp, "set allowWasmBinarySource", args, dbg);
     if (!args.requireAtLeast(cx, "Debugger.set allowWasmBinarySource", 1))
         return false;
     dbg->allowWasmBinarySource = ToBoolean(args[0]);
 
     for (WeakGlobalObjectSet::Range r = dbg->debuggees.all(); !r.empty(); r.popFront()) {
         GlobalObject* global = r.front();
-        JSCompartment* comp = global->compartment();
-        comp->updateDebuggerObservesBinarySource();
+        Realm* realm = global->realm();
+        realm->updateDebuggerObservesBinarySource();
     }
 
     args.rval().setUndefined();
     return true;
 }
 
 /* static */ bool
 Debugger::getCollectCoverageInfo(JSContext* cx, unsigned argc, Value* vp)
@@ -3764,51 +3766,51 @@ Debugger::removeDebuggee(JSContext* cx, 
     THIS_DEBUGGER(cx, argc, vp, "removeDebuggee", args, dbg);
 
     if (!args.requireAtLeast(cx, "Debugger.removeDebuggee", 1))
         return false;
     Rooted<GlobalObject*> global(cx, dbg->unwrapDebuggeeArgument(cx, args[0]));
     if (!global)
         return false;
 
-    ExecutionObservableCompartments obs(cx);
+    ExecutionObservableRealms obs(cx);
     if (!obs.init())
         return false;
 
     if (dbg->debuggees.has(global)) {
         dbg->removeDebuggeeGlobal(cx->runtime()->defaultFreeOp(), global, nullptr);
 
-        // Only update the compartment 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->compartment()))
+        // 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()))
             return false;
         if (!updateExecutionObservability(cx, obs, NotObserving))
             return false;
     }
 
     args.rval().setUndefined();
     return true;
 }
 
 /* static */ bool
 Debugger::removeAllDebuggees(JSContext* cx, unsigned argc, Value* vp)
 {
     THIS_DEBUGGER(cx, argc, vp, "removeAllDebuggees", args, dbg);
 
-    ExecutionObservableCompartments obs(cx);
+    ExecutionObservableRealms obs(cx);
     if (!obs.init())
         return false;
 
     for (WeakGlobalObjectSet::Enum e(dbg->debuggees); !e.empty(); e.popFront()) {
         Rooted<GlobalObject*> global(cx, e.front());
         dbg->removeDebuggeeGlobal(cx->runtime()->defaultFreeOp(), global, &e);
 
         // See note about adding to the observable set in removeDebuggee.
-        if (global->getDebuggers()->empty() && !obs.add(global->compartment()))
+        if (global->getDebuggers()->empty() && !obs.add(global->realm()))
             return false;
     }
 
     if (!updateExecutionObservability(cx, obs, NotObserving))
         return false;
 
     args.rval().setUndefined();
     return true;
@@ -3883,18 +3885,17 @@ Debugger::getNewestFrame(JSContext* cx, 
     return true;
 }
 
 /* static */ bool
 Debugger::clearAllBreakpoints(JSContext* cx, unsigned argc, Value* vp)
 {
     THIS_DEBUGGER(cx, argc, vp, "clearAllBreakpoints", args, dbg);
     for (WeakGlobalObjectSet::Range r = dbg->debuggees.all(); !r.empty(); r.popFront())
-        r.front()->compartment()->clearBreakpointsIn(cx->runtime()->defaultFreeOp(),
-                                                     dbg, nullptr);
+        r.front()->realm()->clearBreakpointsIn(cx->runtime()->defaultFreeOp(), dbg, nullptr);
     return true;
 }
 
 /* static */ bool
 Debugger::construct(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
@@ -3970,51 +3971,50 @@ Debugger::addDebuggeeGlobal(JSContext* c
     }
 
     /*
      * Check for cycles. If global's realm is reachable from this
      * Debugger object's realm by following debuggee-to-debugger links,
      * then adding global would create a cycle. (Typically nobody is debugging
      * the debugger, in which case we zip through this code without looping.)
      */
-    Vector<JSCompartment*> visited(cx);
-    if (!visited.append(object->compartment()))
+    Vector<Realm*> visited(cx);
+    if (!visited.append(object->realm()))
         return false;
     for (size_t i = 0; i < visited.length(); i++) {
-        JSCompartment* c = visited[i];
-        if (c == debuggeeRealm) {
+        Realm* realm = visited[i];
+        if (realm == debuggeeRealm) {
             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_LOOP);
             return false;
         }
 
         /*
-         * Find all compartments containing debuggers debugging c's global
-         * object. Add those compartments to visited.
+         * Find all realms containing debuggers debugging realm's global object.
+         * Add those realms to visited.
          */
-        if (c->isDebuggee()) {
-            Realm* realm = JS::GetRealmForCompartment(c);
+        if (realm->isDebuggee()) {
             GlobalObject::DebuggerVector* v = realm->maybeGlobal()->getDebuggers();
             for (auto p = v->begin(); p != v->end(); p++) {
-                JSCompartment* next = (*p)->object->compartment();
+                Realm* next = (*p)->object->realm();
                 if (Find(visited, next) == visited.end() && !visited.append(next))
                     return false;
             }
         }
     }
 
     /*
      * For global to become this js::Debugger's debuggee:
      *
      * 1. this js::Debugger must be in global->getDebuggers(),
      * 2. global must be in this->debuggees,
      * 3. it must be in zone->getDebuggers(),
      * 4. the debuggee's zone must be in this->debuggeeZones,
      * 5. if we are tracking allocations, the SavedStacksMetadataBuilder must be
-     *    installed for this compartment, and
-     * 6. JSCompartment::isDebuggee()'s bit must be set.
+     *    installed for this realm, and
+     * 6. Realm::isDebuggee()'s bit must be set.
      *
      * All six indications must be kept consistent.
      */
 
     AutoRealm ar(cx, global);
     Zone* zone = global->zone();
 
     // (1)
@@ -4068,17 +4068,17 @@ Debugger::addDebuggeeGlobal(JSContext* c
         return false;
 
     auto allocationsTrackingGuard = MakeScopeExit([&] {
         if (trackingAllocationSites && enabled)
             Debugger::removeAllocationsTracking(*global);
     });
 
     // (6)
-    AutoRestoreCompartmentDebugMode debugModeGuard(debuggeeRealm);
+    AutoRestoreRealmDebugMode debugModeGuard(debuggeeRealm);
     debuggeeRealm->setIsDebuggee();
     debuggeeRealm->updateDebuggerObservesAsmJS();
     debuggeeRealm->updateDebuggerObservesBinarySource();
     debuggeeRealm->updateDebuggerObservesCoverage();
     if (observesAllExecution() && !ensureExecutionObservabilityOfCompartment(cx, debuggeeRealm))
         return false;
 
     globalDebuggersGuard.release();
@@ -4173,41 +4173,41 @@ Debugger::removeDebuggeeGlobal(FreeOp* f
         zoneDebuggersVector->erase(findDebuggerInVector(this, zoneDebuggersVector));
 
     /* Remove all breakpoints for the debuggee. */
     Breakpoint* nextbp;
     for (Breakpoint* bp = firstBreakpoint(); bp; bp = nextbp) {
         nextbp = bp->nextInDebugger();
         switch (bp->site->type()) {
           case BreakpointSite::Type::JS:
-            if (bp->site->asJS()->script->compartment() == global->compartment())
+            if (bp->site->asJS()->script->realm() == global->realm())
                 bp->destroy(fop);
             break;
           case BreakpointSite::Type::Wasm:
-            if (bp->asWasm()->wasmInstance->compartment() == global->compartment())
+            if (bp->asWasm()->wasmInstance->realm() == global->realm())
                 bp->destroy(fop);
             break;
         }
     }
     MOZ_ASSERT_IF(debuggees.empty(), !firstBreakpoint());
 
     /*
      * If we are tracking allocation sites, we need to remove the object
-     * metadata callback from this global's compartment.
+     * metadata callback from this global's realm.
      */
     if (trackingAllocationSites)
         Debugger::removeAllocationsTracking(*global);
 
     if (global->getDebuggers()->empty()) {
-        global->compartment()->unsetIsDebuggee();
+        global->realm()->unsetIsDebuggee();
     } else {
-        global->compartment()->updateDebuggerObservesAllExecution();
-        global->compartment()->updateDebuggerObservesAsmJS();
-        global->compartment()->updateDebuggerObservesBinarySource();
-        global->compartment()->updateDebuggerObservesCoverage();
+        global->realm()->updateDebuggerObservesAllExecution();
+        global->realm()->updateDebuggerObservesAsmJS();
+        global->realm()->updateDebuggerObservesBinarySource();
+        global->realm()->updateDebuggerObservesCoverage();
     }
 }
 
 
 static inline DebuggerSourceReferent GetSourceReferent(JSObject* obj);
 
 /*
  * A class for parsing 'findScripts' query arguments and searching for
@@ -4579,17 +4579,18 @@ class MOZ_STACK_CLASS Debugger::ScriptQu
         return true;
     }
 
     bool delazifyScripts() {
         // All scripts in debuggee compartments must be visible, so delazify
         // everything.
         for (auto r = compartments.all(); !r.empty(); r.popFront()) {
             JSCompartment* comp = r.front();
-            if (!comp->ensureDelazifyScriptsForDebugger(cx))
+            Realm* realm = JS::GetRealmForCompartment(comp);
+            if (!realm->ensureDelazifyScriptsForDebugger(cx))
                 return false;
         }
         return true;
     }
 
     static void considerScript(JSRuntime* rt, void* data, JSScript* script,
                                const JS::AutoRequireNoGC& nogc) {
         ScriptQuery* self = static_cast<ScriptQuery*>(data);
@@ -5056,17 +5057,18 @@ Debugger::isCompilableUnit(JSContext* cx
     if (!sourceObject)
         return false;
 
     frontend::Parser<frontend::FullParseHandler, char16_t> parser(cx, cx->tempLifoAlloc(),
                                                                   options, chars.twoByteChars(),
                                                                   length,
                                                                   /* foldConstants = */ true,
                                                                   usedNames, nullptr, nullptr,
-                                                                  sourceObject);
+                                                                  sourceObject,
+                                                                  frontend::ParseGoal::Script);
     JS::WarningReporter older = JS::SetWarningReporter(cx, nullptr);
     if (!parser.checkOptions() || !parser.parse()) {
         // We ran into an error. If it was because we ran out of memory we report
         // it in the usual way.
         if (cx->isThrowingOutOfMemory()) {
             JS::SetWarningReporter(cx, older);
             return false;
         }
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -1801,17 +1801,17 @@ Debugger::onNewScript(JSContext* cx, Han
     MOZ_ASSERT_IF(!script->realm()->creationOptions().invisibleToDebugger() &&
                   !script->selfHosted(),
                   script->realm()->firedOnNewGlobalObject);
 
     // The script may not be ready to be interrogated by the debugger.
     if (script->hideScriptFromDebugger())
         return;
 
-    if (script->compartment()->isDebuggee())
+    if (script->realm()->isDebuggee())
         slowPathOnNewScript(cx, script);
 }
 
 /* static */ void
 Debugger::onNewGlobalObject(JSContext* cx, Handle<GlobalObject*> global)
 {
     MOZ_ASSERT(!global->realm()->firedOnNewGlobalObject);
 #ifdef DEBUG
--- a/js/src/vm/EnvironmentObject.cpp
+++ b/js/src/vm/EnvironmentObject.cpp
@@ -2505,17 +2505,17 @@ DebugEnvironments::checkHashTablesAfterM
  * (in particular, JS_GetFrameScopeChain does not require debug mode). Since
  * DebugEnvironments::onPop* are only called in debuggee frames, this means we
  * cannot use any of the maps in DebugEnvironments. This will produce debug scope
  * chains that do not obey the debugger invariants but that is just fine.
  */
 static bool
 CanUseDebugEnvironmentMaps(JSContext* cx)
 {
-    return cx->compartment()->isDebuggee();
+    return cx->realm()->isDebuggee();
 }
 
 DebugEnvironments*
 DebugEnvironments::ensureCompartmentData(JSContext* cx)
 {
     JSCompartment* c = cx->compartment();
     if (c->debugEnvs)
         return c->debugEnvs;
@@ -2888,17 +2888,17 @@ DebugEnvironments::updateLiveEnvironment
                     return false;
                 if (!envs->liveEnvs.put(&ei.environment(), LiveEnvironmentVal(ei)))
                     return false;
             }
         }
 
         if (frame.prevUpToDate())
             return true;
-        MOZ_ASSERT(frame.environmentChain()->compartment()->isDebuggee());
+        MOZ_ASSERT(frame.environmentChain()->realm()->isDebuggee());
         frame.setPrevUpToDate();
     }
 
     return true;
 }
 
 LiveEnvironmentVal*
 DebugEnvironments::hasLiveEnvironment(EnvironmentObject& env)
@@ -3210,19 +3210,29 @@ WithEnvironmentObject::scope() const
 {
     MOZ_ASSERT(isSyntactic());
     return *static_cast<WithScope*>(getReservedSlot(SCOPE_SLOT).toGCThing());
 }
 
 ModuleEnvironmentObject*
 js::GetModuleEnvironmentForScript(JSScript* script)
 {
+    ModuleObject* module = GetModuleObjectForScript(script);
+    if (!module)
+        return nullptr;
+
+    return module->environment();
+}
+
+ModuleObject*
+js::GetModuleObjectForScript(JSScript* script)
+{
     for (ScopeIter si(script); si; si++) {
         if (si.kind() == ScopeKind::Module)
-            return si.scope()->as<ModuleScope>().module()->environment();
+            return si.scope()->as<ModuleScope>().module();
     }
     return nullptr;
 }
 
 bool
 js::GetThisValueForDebuggerMaybeOptimizedOut(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc,
                                              MutableHandleValue res)
 {
--- a/js/src/vm/EnvironmentObject.h
+++ b/js/src/vm/EnvironmentObject.h
@@ -1160,16 +1160,18 @@ IsFrameInitialEnvironment(AbstractFrameP
     return false;
 }
 
 extern bool
 CreateObjectsForEnvironmentChain(JSContext* cx, AutoObjectVector& chain,
                                  HandleObject terminatingEnv,
                                  MutableHandleObject envObj);
 
+ModuleObject* GetModuleObjectForScript(JSScript* script);
+
 ModuleEnvironmentObject* GetModuleEnvironmentForScript(JSScript* script);
 
 MOZ_MUST_USE bool
 GetThisValueForDebuggerMaybeOptimizedOut(JSContext* cx, AbstractFramePtr frame,
                                          jsbytecode* pc, MutableHandleValue res);
 
 MOZ_MUST_USE bool
 CheckVarNameConflict(JSContext* cx, Handle<LexicalEnvironmentObject*> lexicalEnv,
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -17,16 +17,17 @@
 
 #include <string.h>
 
 #include "jslibmath.h"
 #include "jsnum.h"
 
 #include "builtin/Array.h"
 #include "builtin/Eval.h"
+#include "builtin/ModuleObject.h"
 #include "builtin/String.h"
 #include "jit/AtomicOperations.h"
 #include "jit/BaselineJIT.h"
 #include "jit/Ion.h"
 #include "jit/IonAnalysis.h"
 #include "jit/Jit.h"
 #include "util/StringBuffer.h"
 #include "vm/AsyncFunction.h"
@@ -377,17 +378,17 @@ bool
 js::RunScript(JSContext* cx, RunState& state)
 {
     if (!CheckRecursionLimit(cx))
         return false;
 
     // Since any script can conceivably GC, make sure it's safe to do so.
     cx->verifyIsSafeToGC();
 
-    MOZ_DIAGNOSTIC_ASSERT(cx->compartment()->isSystem() ||
+    MOZ_DIAGNOSTIC_ASSERT(cx->realm()->isSystem() ||
                           cx->runtime()->allowContentJS());
 
     MOZ_ASSERT(!cx->enableAccessValidation ||
                cx->compartment()->isAccessValid());
 
     if (!Debugger::checkNoExecute(cx, state.script()))
         return false;
 
@@ -1039,36 +1040,36 @@ static void
 PopEnvironment(JSContext* cx, EnvironmentIter& ei)
 {
     switch (ei.scope().kind()) {
       case ScopeKind::Lexical:
       case ScopeKind::SimpleCatch:
       case ScopeKind::Catch:
       case ScopeKind::NamedLambda:
       case ScopeKind::StrictNamedLambda:
-        if (MOZ_UNLIKELY(cx->compartment()->isDebuggee()))
+        if (MOZ_UNLIKELY(cx->realm()->isDebuggee()))
             DebugEnvironments::onPopLexical(cx, ei);
         if (ei.scope().hasEnvironment())
             ei.initialFrame().popOffEnvironmentChain<LexicalEnvironmentObject>();
         break;
       case ScopeKind::With:
-        if (MOZ_UNLIKELY(cx->compartment()->isDebuggee()))
+        if (MOZ_UNLIKELY(cx->realm()->isDebuggee()))
             DebugEnvironments::onPopWith(ei.initialFrame());
         ei.initialFrame().popOffEnvironmentChain<WithEnvironmentObject>();
         break;
       case ScopeKind::Function:
-        if (MOZ_UNLIKELY(cx->compartment()->isDebuggee()))
+        if (MOZ_UNLIKELY(cx->realm()->isDebuggee()))
             DebugEnvironments::onPopCall(cx, ei.initialFrame());
         if (ei.scope().hasEnvironment())
             ei.initialFrame().popOffEnvironmentChain<CallObject>();
         break;
       case ScopeKind::FunctionBodyVar:
       case ScopeKind::ParameterExpressionVar:
       case ScopeKind::StrictEval:
-        if (MOZ_UNLIKELY(cx->compartment()->isDebuggee()))
+        if (MOZ_UNLIKELY(cx->realm()->isDebuggee()))
             DebugEnvironments::onPopVar(cx, ei);
         if (ei.scope().hasEnvironment())
             ei.initialFrame().popOffEnvironmentChain<VarEnvironmentObject>();
         break;
       case ScopeKind::Eval:
       case ScopeKind::Global:
       case ScopeKind::NonSyntactic:
       case ScopeKind::Module:
@@ -1626,17 +1627,16 @@ GetSuperEnvFunction(JSContext* cx, Inter
                 continue;
 
             return callee;
         }
     }
     MOZ_CRASH("unexpected env chain for GetSuperEnvFunction");
 }
 
-
 /*
  * As an optimization, the interpreter creates a handful of reserved Rooted<T>
  * variables at the beginning, thus inserting them into the Rooted list once
  * upon entry. ReservedRooted "borrows" a reserved Rooted variable and uses it
  * within a local scope, resetting the value to nullptr (or the appropriate
  * equivalent for T) at scope end. This avoids inserting/removing the Rooted
  * from the rooter list, while preventing stale values from being kept alive
  * unnecessarily.
@@ -1810,17 +1810,17 @@ Interpret(JSContext* cx, RunState& state
     JS_END_MACRO
 
     /*
      * Initialize code coverage vectors.
      */
 #define INIT_COVERAGE()                                                       \
     JS_BEGIN_MACRO                                                            \
         if (!script->hasScriptCounts()) {                                     \
-            if (cx->compartment()->collectCoverageForDebug()) {               \
+            if (cx->realm()->collectCoverageForDebug()) {                     \
                 if (!script->initScriptCounts(cx))                            \
                     goto error;                                               \
             }                                                                 \
         }                                                                     \
     JS_END_MACRO
 
     /*
      * Increment the code coverage counter associated with the given pc.
@@ -1930,17 +1930,17 @@ Interpret(JSContext* cx, RunState& state
 
 INTERPRETER_LOOP() {
 
 CASE(EnableInterruptsPseudoOpcode)
 {
     bool moreInterrupts = false;
     jsbytecode op = *REGS.pc;
 
-    if (!script->hasScriptCounts() && cx->compartment()->collectCoverageForDebug()) {
+    if (!script->hasScriptCounts() && cx->realm()->collectCoverageForDebug()) {
         if (!script->initScriptCounts(cx))
             goto error;
     }
 
     if (script->isDebuggee()) {
         if (script->stepModeEnabled()) {
             RootedValue rval(cx);
             ResumeMode mode = Debugger::onSingleStep(cx, &rval);
@@ -3975,51 +3975,51 @@ CASE(JSOP_POPLEXICALENV)
 #ifdef DEBUG
     // Pop block from scope chain.
     Scope* scope = script->lookupScope(REGS.pc);
     MOZ_ASSERT(scope);
     MOZ_ASSERT(scope->is<LexicalScope>());
     MOZ_ASSERT(scope->as<LexicalScope>().hasEnvironment());
 #endif
 
-    if (MOZ_UNLIKELY(cx->compartment()->isDebuggee()))
+    if (MOZ_UNLIKELY(cx->realm()->isDebuggee()))
         DebugEnvironments::onPopLexical(cx, REGS.fp(), REGS.pc);
 
     // Pop block from scope chain.
     REGS.fp()->popOffEnvironmentChain<LexicalEnvironmentObject>();
 }
 END_CASE(JSOP_POPLEXICALENV)
 
 CASE(JSOP_DEBUGLEAVELEXICALENV)
 {
     MOZ_ASSERT(script->lookupScope(REGS.pc));
     MOZ_ASSERT(script->lookupScope(REGS.pc)->is<LexicalScope>());
     MOZ_ASSERT(!script->lookupScope(REGS.pc)->as<LexicalScope>().hasEnvironment());
 
     // FIXME: This opcode should not be necessary.  The debugger shouldn't need
     // help from bytecode to do its job.  See bug 927782.
 
-    if (MOZ_UNLIKELY(cx->compartment()->isDebuggee()))
+    if (MOZ_UNLIKELY(cx->realm()->isDebuggee()))
         DebugEnvironments::onPopLexical(cx, REGS.fp(), REGS.pc);
 }
 END_CASE(JSOP_DEBUGLEAVELEXICALENV)
 
 CASE(JSOP_FRESHENLEXICALENV)
 {
-    if (MOZ_UNLIKELY(cx->compartment()->isDebuggee()))
+    if (MOZ_UNLIKELY(cx->realm()->isDebuggee()))
         DebugEnvironments::onPopLexical(cx, REGS.fp(), REGS.pc);
 
     if (!REGS.fp()->freshenLexicalEnvironment(cx))
         goto error;
 }
 END_CASE(JSOP_FRESHENLEXICALENV)
 
 CASE(JSOP_RECREATELEXICALENV)
 {
-    if (MOZ_UNLIKELY(cx->compartment()->isDebuggee()))
+    if (MOZ_UNLIKELY(cx->realm()->isDebuggee()))
         DebugEnvironments::onPopLexical(cx, REGS.fp(), REGS.pc);
 
     if (!REGS.fp()->recreateLexicalEnvironment(cx))
         goto error;
 }
 END_CASE(JSOP_RECREATELEXICALENV)
 
 CASE(JSOP_PUSHVARENV)
@@ -4035,17 +4035,17 @@ CASE(JSOP_POPVARENV)
 {
 #ifdef DEBUG
     Scope* scope = script->lookupScope(REGS.pc);
     MOZ_ASSERT(scope);
     MOZ_ASSERT(scope->is<VarScope>());
     MOZ_ASSERT(scope->as<VarScope>().hasEnvironment());
 #endif
 
-    if (MOZ_UNLIKELY(cx->compartment()->isDebuggee()))
+    if (MOZ_UNLIKELY(cx->realm()->isDebuggee()))
         DebugEnvironments::onPopVar(cx, REGS.fp(), REGS.pc);
 
     REGS.fp()->popOffEnvironmentChain<VarEnvironmentObject>();
 }
 END_CASE(JSOP_POPVARENV)
 
 CASE(JSOP_GENERATOR)
 {
@@ -4207,16 +4207,29 @@ CASE(JSOP_SUPERBASE)
 }
 END_CASE(JSOP_SUPERBASE)
 
 CASE(JSOP_NEWTARGET)
     PUSH_COPY(REGS.fp()->newTarget());
     MOZ_ASSERT(REGS.sp[-1].isObject() || REGS.sp[-1].isUndefined());
 END_CASE(JSOP_NEWTARGET)
 
+CASE(JSOP_IMPORTMETA)
+{
+    ReservedRooted<JSObject*> module(&rootObject0, GetModuleObjectForScript(script));
+    MOZ_ASSERT(module);
+
+    JSObject* metaObject = GetOrCreateModuleMetaObject(cx, module);
+    if (!metaObject)
+        goto error;
+
+    PUSH_OBJECT(*metaObject);
+}
+END_CASE(JSOP_NEWTARGET)
+
 CASE(JSOP_SUPERFUN)
 {
     ReservedRooted<JSObject*> superEnvFunc(&rootObject0, &GetSuperEnvFunction(cx, REGS));
     ReservedRooted<JSObject*> superFun(&rootObject1);
     superFun = SuperFunOperation(cx, superEnvFunc);
     if (!superFun)
         goto error;
 
--- a/js/src/vm/Iteration.cpp
+++ b/js/src/vm/Iteration.cpp
@@ -925,38 +925,40 @@ js::LookupInIteratorCache(JSContext* cx,
 
 // ES 2017 draft 7.4.7.
 JSObject*
 js::CreateIterResultObject(JSContext* cx, HandleValue value, bool done)
 {
     // Step 1 (implicit).
 
     // Step 2.
-    RootedObject templateObject(cx, cx->compartment()->getOrCreateIterResultTemplateObject(cx));
+    RootedObject templateObject(cx, cx->realm()->getOrCreateIterResultTemplateObject(cx));
     if (!templateObject)
         return nullptr;
 
     NativeObject* resultObj;
     JS_TRY_VAR_OR_RETURN_NULL(cx, resultObj, NativeObject::createWithTemplate(cx, gc::DefaultHeap,
                                                                               templateObject));
 
     // Step 3.
-    resultObj->setSlot(JSCompartment::IterResultObjectValueSlot, value);
+    resultObj->setSlot(Realm::IterResultObjectValueSlot, value);
 
     // Step 4.
-    resultObj->setSlot(JSCompartment::IterResultObjectDoneSlot,
+    resultObj->setSlot(Realm::IterResultObjectDoneSlot,
                        done ? TrueHandleValue : FalseHandleValue);
 
     // Step 5.
     return resultObj;
 }
 
 NativeObject*
-JSCompartment::getOrCreateIterResultTemplateObject(JSContext* cx)
+Realm::getOrCreateIterResultTemplateObject(JSContext* cx)
 {
+    MOZ_ASSERT(cx->realm() == this);
+
     if (iterResultTemplate_)
         return iterResultTemplate_;
 
     // Create template plain object
     RootedNativeObject templateObject(cx, NewBuiltinClassInstance<PlainObject>(cx, TenuredObject));
     if (!templateObject)
         return iterResultTemplate_; // = nullptr
 
@@ -990,19 +992,19 @@ JSCompartment::getOrCreateIterResultTemp
         {
             AutoEnterAnalysis enter(cx);
             types->makeUnknown(sweep, cx);
         }
     }
 
     // Make sure that the properties are in the right slots.
     DebugOnly<Shape*> shape = templateObject->lastProperty();
-    MOZ_ASSERT(shape->previous()->slot() == JSCompartment::IterResultObjectValueSlot &&
+    MOZ_ASSERT(shape->previous()->slot() == Realm::IterResultObjectValueSlot &&
                shape->previous()->propidRef() == NameToId(cx->names().value));
-    MOZ_ASSERT(shape->slot() == JSCompartment::IterResultObjectDoneSlot &&
+    MOZ_ASSERT(shape->slot() == Realm::IterResultObjectDoneSlot &&
                shape->propidRef() == NameToId(cx->names().done));
 
     iterResultTemplate_.set(templateObject);
 
     return iterResultTemplate_;
 }
 
 /*** Iterator objects ****************************************************************************/
--- a/js/src/vm/JSCompartment.cpp
+++ b/js/src/vm/JSCompartment.cpp
@@ -39,60 +39,53 @@ using namespace js;
 using namespace js::gc;
 using namespace js::jit;
 
 using mozilla::PodArrayZero;
 
 JSCompartment::JSCompartment(Zone* zone)
   : zone_(zone),
     runtime_(zone->runtimeFromAnyThread()),
-    principals_(nullptr),
-    isSystem_(false),
-    isSelfHosting(false),
-    marked(true),
-    performanceMonitoring(runtime_),
     data(nullptr),
     regExps(),
-    arraySpeciesLookup(),
     globalWriteBarriered(0),
     detachedTypedObjects(0),
-    objectMetadataState(ImmediateMetadata()),
-    selfHostingScriptSource(nullptr),
     objectMetadataTable(nullptr),
     innerViews(zone),
     lazyArrayBuffers(nullptr),
     nonSyntacticLexicalEnvironments_(nullptr),
     gcIncomingGrayPointers(nullptr),
-    debugModeBits(0),
     validAccessPtr(nullptr),
-    randomKeyGenerator_(runtime_->forkRandomKeyGenerator()),
     debugEnvs(nullptr),
     enumerators(nullptr),
-    scheduledForDestruction(false),
-    maybeAlive(true),
     jitCompartment_(nullptr),
-    mappedArgumentsTemplate_(nullptr),
-    unmappedArgumentsTemplate_(nullptr),
-    iterResultTemplate_(nullptr),
     lcovOutput()
 {
     runtime_->numCompartments++;
 }
 
-JS::Realm::Realm(JS::Zone* zone, const JS::RealmOptions& options)
+Realm::Realm(JS::Zone* zone, const JS::RealmOptions& options)
   : JSCompartment(zone),
     creationOptions_(options.creationOptions()),
     behaviors_(options.behaviors()),
     global_(nullptr),
-    wasm(zone->runtimeFromMainThread())
+    randomKeyGenerator_(runtime_->forkRandomKeyGenerator()),
+    wasm(zone->runtimeFromMainThread()),
+    performanceMonitoring(runtime_)
 {
     MOZ_ASSERT_IF(creationOptions_.mergeable(),
                   creationOptions_.invisibleToDebugger());
 }
 
+Realm::~Realm()
+{
+    // Empty destructor: using the default destructor requires adding various
+    // #includes to other files where we destruct Realms.
+}
+
 JSCompartment::~JSCompartment()
 {
     // Write the code coverage information in a file.
     JSRuntime* rt = runtimeFromMainThread();
     if (rt->lcovOutput().isEnabled())
         rt->lcovOutput().writeLCovResult(lcovOutput);
 
     js_delete(jitCompartment_);
@@ -107,23 +100,16 @@ JSCompartment::~JSCompartment()
     // leaked GC things.
     if (!rt->gc.shutdownCollectedEverything())
         unboxedLayouts.clear();
 #endif
 
     runtime_->numCompartments--;
 }
 
-Realm::~Realm()
-{
-    js_delete(scriptCountsMap);
-    js_delete(scriptNameMap);
-    js_delete(debugScriptMap);
-}
-
 bool
 JSCompartment::init(JSContext* maybecx)
 {
     if (!crossCompartmentWrappers.init(0)) {
         if (maybecx)
             ReportOutOfMemory(maybecx);
         return false;
     }
@@ -660,19 +646,19 @@ Realm::traceGlobal(JSTracer* trc)
     // Atoms are always tenured.
     if (!JS::CurrentThreadIsHeapMinorCollecting())
         varNames_.trace(trc);
 }
 
 void
 Realm::traceRoots(JSTracer* trc, js::gc::GCRuntime::TraceOrMarkRuntime traceOrMark)
 {
-    if (objectMetadataState.is<PendingMetadata>()) {
+    if (objectMetadataState_.is<PendingMetadata>()) {
         TraceRoot(trc,
-                  &objectMetadataState.as<PendingMetadata>(),
+                  &objectMetadataState_.as<PendingMetadata>(),
                   "on-stack object pending metadata");
     }
 
     if (!JS::CurrentThreadIsHeapMinorCollecting()) {
         // The global is never nursery allocated, so we don't need to
         // trace it when doing a minor collection.
         //
         // If a compartment is on-stack, we mark its global so that
@@ -768,17 +754,17 @@ JSCompartment::sweepSavedStacks()
 void
 Realm::sweepGlobalObject()
 {
     if (global_ && IsAboutToBeFinalized(&global_))
         global_.set(nullptr);
 }
 
 void
-JSCompartment::sweepSelfHostingScriptSource()
+Realm::sweepSelfHostingScriptSource()
 {
     if (selfHostingScriptSource.unbarrieredGet() &&
         IsAboutToBeFinalized(&selfHostingScriptSource))
     {
         selfHostingScriptSource.set(nullptr);
     }
 }
 
@@ -860,17 +846,17 @@ CrossCompartmentKey::trace(JSTracer* trc
 bool
 CrossCompartmentKey::needsSweep()
 {
     return applyToWrapped(NeedsSweepUnbarrieredFunctor()) ||
            applyToDebugger(NeedsSweepUnbarrieredFunctor());
 }
 
 void
-JSCompartment::sweepTemplateObjects()
+Realm::sweepTemplateObjects()
 {
     if (mappedArgumentsTemplate_ && IsAboutToBeFinalized(&mappedArgumentsTemplate_))
         mappedArgumentsTemplate_.set(nullptr);
 
     if (unmappedArgumentsTemplate_ && IsAboutToBeFinalized(&unmappedArgumentsTemplate_))
         unmappedArgumentsTemplate_.set(nullptr);
 
     if (iterResultTemplate_ && IsAboutToBeFinalized(&iterResultTemplate_))
@@ -972,17 +958,17 @@ Realm::checkScriptMapsAfterMovingGC()
         }
     }
 
     if (debugScriptMap) {
         for (auto r = debugScriptMap->all(); !r.empty(); r.popFront()) {
             JSScript* script = r.front().key();
             MOZ_ASSERT(script->realm() == this);
             CheckGCThingAfterMovingGC(script);
-            DebugScript* ds = r.front().value();
+            DebugScript* ds = r.front().value().get();
             for (uint32_t i = 0; i < ds->numSites; i++) {
                 BreakpointSite* site = ds->breakpoints[i];
                 if (site && site->type() == BreakpointSite::Type::JS)
                     CheckGCThingAfterMovingGC(site->asJS()->script);
             }
             auto ptr = debugScriptMap->lookup(script);
             MOZ_RELEASE_ASSERT(ptr.found() && &*ptr == &r.front());
         }
@@ -1079,36 +1065,36 @@ AddInnerLazyFunctionsFromScript(JSScript
             if (!lazyFunctions.append(obj))
                 return false;
         }
     }
     return true;
 }
 
 static bool
-AddLazyFunctionsForCompartment(JSContext* cx, AutoObjectVector& lazyFunctions, AllocKind kind)
+AddLazyFunctionsForRealm(JSContext* cx, AutoObjectVector& lazyFunctions, AllocKind kind)
 {
-    // Find all live root lazy functions in the compartment: those which have a
+    // Find all live root lazy functions in the realm: those which have a
     // non-lazy enclosing script, and which do not have an uncompiled enclosing
     // script. The last condition is so that we don't compile lazy scripts
     // whose enclosing scripts failed to compile, indicating that the lazy
     // script did not escape the script.
     //
     // Some LazyScripts have a non-null |JSScript* script| pointer. We still
     // want to delazify in that case: this pointer is weak so the JSScript
     // could be destroyed at the next GC.
 
     for (auto i = cx->zone()->cellIter<JSObject>(kind); !i.done(); i.next()) {
         JSFunction* fun = &i->as<JSFunction>();
 
         // Sweeping is incremental; take care to not delazify functions that
         // are about to be finalized. GC things referenced by objects that are
         // about to be finalized (e.g., in slots) may already be freed.
         if (gc::IsAboutToBeFinalizedUnbarriered(&fun) ||
-            fun->compartment() != cx->compartment())
+            fun->realm() != cx->realm())
         {
             continue;
         }
 
         if (fun->isInterpretedLazy()) {
             LazyScript* lazy = fun->lazyScriptOrNull();
             if (lazy && !lazy->isEnclosingScriptLazy() && !lazy->hasUncompletedEnclosingScript()) {
                 if (!lazyFunctions.append(fun))
@@ -1116,26 +1102,26 @@ AddLazyFunctionsForCompartment(JSContext
             }
         }
     }
 
     return true;
 }
 
 static bool
-CreateLazyScriptsForCompartment(JSContext* cx)
+CreateLazyScriptsForRealm(JSContext* cx)
 {
     AutoObjectVector lazyFunctions(cx);
 
-    if (!AddLazyFunctionsForCompartment(cx, lazyFunctions, AllocKind::FUNCTION))
+    if (!AddLazyFunctionsForRealm(cx, lazyFunctions, AllocKind::FUNCTION))
         return false;
 
     // Methods, for instance {get method() {}}, are extended functions that can
     // be relazified, so we need to handle those as well.
-    if (!AddLazyFunctionsForCompartment(cx, lazyFunctions, AllocKind::FUNCTION_EXTENDED))
+    if (!AddLazyFunctionsForRealm(cx, lazyFunctions, AllocKind::FUNCTION_EXTENDED))
         return false;
 
     // Create scripts for each lazy function, updating the list of functions to
     // process with any newly exposed inner functions in created scripts.
     // A function cannot be delazified until its outer script exists.
     RootedFunction fun(cx);
     for (size_t i = 0; i < lazyFunctions.length(); i++) {
         fun = &lazyFunctions[i]->as<JSFunction>();
@@ -1153,65 +1139,64 @@ CreateLazyScriptsForCompartment(JSContex
         if (lazyScriptHadNoScript && !AddInnerLazyFunctionsFromScript(script, lazyFunctions))
             return false;
     }
 
     return true;
 }
 
 bool
-JSCompartment::ensureDelazifyScriptsForDebugger(JSContext* cx)
+Realm::ensureDelazifyScriptsForDebugger(JSContext* cx)
 {
     AutoRealmUnchecked ar(cx, this);
-    if (needsDelazificationForDebugger() && !CreateLazyScriptsForCompartment(cx))
+    if (needsDelazificationForDebugger() && !CreateLazyScriptsForRealm(cx))
         return false;
-    debugModeBits &= ~DebuggerNeedsDelazification;
+    debugModeBits_ &= ~DebuggerNeedsDelazification;
     return true;
 }
 
 void
-JSCompartment::updateDebuggerObservesFlag(unsigned flag)
+Realm::updateDebuggerObservesFlag(unsigned flag)
 {
     MOZ_ASSERT(isDebuggee());
     MOZ_ASSERT(flag == DebuggerObservesAllExecution ||
                flag == DebuggerObservesCoverage ||
                flag == DebuggerObservesAsmJS ||
                flag == DebuggerObservesBinarySource);
 
-    Realm* realm = JS::GetRealmForCompartment(this);
     GlobalObject* global = zone()->runtimeFromMainThread()->gc.isForegroundSweeping()
-                           ? realm->unsafeUnbarrieredMaybeGlobal()
-                           : realm->maybeGlobal();
+                           ? unsafeUnbarrieredMaybeGlobal()
+                           : maybeGlobal();
     const GlobalObject::DebuggerVector* v = global->getDebuggers();
     for (auto p = v->begin(); p != v->end(); p++) {
         Debugger* dbg = *p;
         if (flag == DebuggerObservesAllExecution ? dbg->observesAllExecution() :
             flag == DebuggerObservesCoverage ? dbg->observesCoverage() :
             flag == DebuggerObservesAsmJS ? dbg->observesAsmJS() :
             dbg->observesBinarySource())
         {
-            debugModeBits |= flag;
+            debugModeBits_ |= flag;
             return;
         }
     }
 
-    debugModeBits &= ~flag;
+    debugModeBits_ &= ~flag;
 }
 
 void
-JSCompartment::unsetIsDebuggee()
+Realm::unsetIsDebuggee()
 {
     if (isDebuggee()) {
-        debugModeBits &= ~DebuggerObservesMask;
+        debugModeBits_ &= ~DebuggerObservesMask;
         DebugEnvironments::onCompartmentUnsetIsDebuggee(this);
     }
 }
 
 void
-JSCompartment::updateDebuggerObservesCoverage()
+Realm::updateDebuggerObservesCoverage()
 {
     bool previousState = debuggerObservesCoverage();
     updateDebuggerObservesFlag(DebuggerObservesCoverage);
     if (previousState == debuggerObservesCoverage())
         return;
 
     if (debuggerObservesCoverage()) {
         // Interrupt any running interpreter frame. The scriptCounts are
@@ -1223,78 +1208,66 @@ JSCompartment::updateDebuggerObservesCov
         }
         return;
     }
 
     // If code coverage is enabled by any other means, keep it.
     if (collectCoverage())
         return;
 
-    Realm* realm = JS::GetRealmForCompartment(this);
-    realm->clearScriptCounts();
-    realm->clearScriptNames();
+    clearScriptCounts();
+    clearScriptNames();
 }
 
 bool
-JSCompartment::collectCoverage() const
+Realm::collectCoverage() const
 {
     return collectCoverageForPGO() ||
            collectCoverageForDebug();
 }
 
 bool
-JSCompartment::collectCoverageForPGO() const
+Realm::collectCoverageForPGO() const
 {
     return !JitOptions.disablePgo;
 }
 
 bool
-JSCompartment::collectCoverageForDebug() const
+Realm::collectCoverageForDebug() const
 {
     return debuggerObservesCoverage() ||
            runtimeFromAnyThread()->profilingScripts ||
            runtimeFromAnyThread()->lcovOutput().isEnabled();
 }
 
 void
 Realm::clearScriptCounts()
 {
     if (!scriptCountsMap)
         return;
 
     // Clear all hasScriptCounts_ flags of JSScript, in order to release all
     // ScriptCounts entries of the current realm.
-    for (ScriptCountsMap::Range r = scriptCountsMap->all(); !r.empty(); r.popFront()) {
-        ScriptCounts* value = r.front().value();
-        r.front().key()->takeOverScriptCountsMapEntry(value);
-        js_delete(value);
-    }
+    for (ScriptCountsMap::Range r = scriptCountsMap->all(); !r.empty(); r.popFront())
+        r.front().key()->clearHasScriptCounts();
 
-    js_delete(scriptCountsMap);
-    scriptCountsMap = nullptr;
+    scriptCountsMap.reset();
 }
 
 void
 Realm::clearScriptNames()
 {
-    if (!scriptNameMap)
-        return;
-
-    for (ScriptNameMap::Range r = scriptNameMap->all(); !r.empty(); r.popFront())
-        js_delete(r.front().value());
-
-    js_delete(scriptNameMap);
-    scriptNameMap = nullptr;
+    scriptNameMap.reset();
 }
 
 void
-JSCompartment::clearBreakpointsIn(FreeOp* fop, js::Debugger* dbg, HandleObject handler)
+Realm::clearBreakpointsIn(FreeOp* fop, js::Debugger* dbg, HandleObject handler)
 {
     for (auto script = zone()->cellIter<JSScript>(); !script.done(); script.next()) {
-        if (script->compartment() == this && script->hasAnyBreakpointsOrStepMode())
+        if (script->realm() == this && script->hasAnyBreakpointsOrStepMode())
             script->clearBreakpointsIn(fop, dbg, handler);
     }
 }
 
 void
 JSCompartment::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,
                                       size_t* crossCompartmentWrappersArg)
 {
@@ -1352,64 +1325,63 @@ Realm::addSizeOfIncludingThis(mozilla::M
         *scriptCountsMapArg += scriptCountsMap->sizeOfIncludingThis(mallocSizeOf);
         for (auto r = scriptCountsMap->all(); !r.empty(); r.popFront()) {
             *scriptCountsMapArg += r.front().value()->sizeOfIncludingThis(mallocSizeOf);
         }
     }
 }
 
 HashNumber
-JSCompartment::randomHashCode()
+Realm::randomHashCode()
 {
-    ensureRandomNumberGenerator();
-    return HashNumber(randomNumberGenerator.ref().next());
+    return HashNumber(getOrCreateRandomNumberGenerator().next());
 }
 
 mozilla::HashCodeScrambler
-JSCompartment::randomHashCodeScrambler()
+Realm::randomHashCodeScrambler()
 {
     return mozilla::HashCodeScrambler(randomKeyGenerator_.next(),
                                       randomKeyGenerator_.next());
 }
 
 AutoSetNewObjectMetadata::AutoSetNewObjectMetadata(JSContext* cx
                                                    MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
     : CustomAutoRooter(cx)
     , cx_(cx->helperThread() ? nullptr : cx)
-    , prevState_(cx->compartment()->objectMetadataState)
+    , prevState_(cx->realm()->objectMetadataState_)
 {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     if (cx_)
-        cx_->compartment()->objectMetadataState = NewObjectMetadataState(DelayMetadata());
+        cx_->realm()->objectMetadataState_ = NewObjectMetadataState(DelayMetadata());
 }
 
 AutoSetNewObjectMetadata::~AutoSetNewObjectMetadata()
 {
     // If we don't have a cx, we didn't change the metadata state, so no need to
     // reset it here.
     if (!cx_)
         return;
 
-    if (!cx_->isExceptionPending() && cx_->compartment()->hasObjectPendingMetadata()) {
+    if (!cx_->isExceptionPending() && cx_->realm()->hasObjectPendingMetadata()) {
         // This destructor often runs upon exit from a function that is
         // returning an unrooted pointer to a Cell. The allocation metadata
         // callback often allocates; if it causes a GC, then the Cell pointer
         // being returned won't be traced or relocated.
         //
         // The only extant callbacks are those internal to SpiderMonkey that
         // capture the JS stack. In fact, we're considering removing general
         // callbacks altogther in bug 1236748. Since it's not running arbitrary
         // code, it's adequate to simply suppress GC while we run the callback.
         AutoSuppressGC autoSuppressGC(cx_);
 
-        JSObject* obj = cx_->compartment()->objectMetadataState.as<PendingMetadata>();
+        JSObject* obj = cx_->realm()->objectMetadataState_.as<PendingMetadata>();
 
         // Make sure to restore the previous state before setting the object's
         // metadata. SetNewObjectMetadata asserts that the state is not
         // PendingMetadata in order to ensure that metadata callbacks are called
         // in order.
-        cx_->compartment()->objectMetadataState = prevState_;
+        cx_->realm()->objectMetadataState_ = prevState_;
 
         obj = SetNewObjectMetadata(cx_, obj);
     } else {
-        cx_->compartment()->objectMetadataState = prevState_;
+        cx_->realm()->objectMetadataState_ = prevState_;
     }
 }
--- a/js/src/vm/JSCompartment.h
+++ b/js/src/vm/JSCompartment.h
@@ -14,16 +14,17 @@
 #include "mozilla/Variant.h"
 #include "mozilla/XorShift128PlusRNG.h"
 
 #include <stddef.h>
 
 #include "gc/Barrier.h"
 #include "gc/NurseryAwareHashMap.h"
 #include "gc/Zone.h"
+#include "js/UniquePtr.h"
 #include "vm/ArrayBufferObject.h"
 #include "vm/GlobalObject.h"
 #include "vm/ReceiverGuard.h"
 #include "vm/RegExpShared.h"
 #include "vm/SavedStacks.h"
 #include "vm/Time.h"
 #include "wasm/WasmRealm.h"
 
@@ -32,16 +33,17 @@ namespace js {
 namespace jit {
 class JitCompartment;
 } // namespace jit
 
 namespace gc {
 template <typename Node, typename Derived> class ComponentFinder;
 } // namespace gc
 
+class AutoRestoreRealmDebugMode;
 class GlobalObject;
 class LexicalEnvironmentObject;
 class MapObject;
 class ScriptSourceObject;
 class SetObject;
 struct NativeIterator;
 
 /*
@@ -549,79 +551,21 @@ class WeakMapBase;
 } // namespace js
 
 struct JSCompartment
 {
   protected:
     JS::Zone*                    zone_;
     JSRuntime*                   runtime_;
 
-  public:
-    /*
-     * The principals associated with this compartment. Note that the
-     * same several compartments may share the same principals and
-     * that a compartment may change principals during its lifetime
-     * (e.g. in case of lazy parsing).
-     */
-    inline JSPrincipals* principals() {
-        return principals_;
-    }
-    inline void setPrincipals(JSPrincipals* principals) {
-        if (principals_ == principals)
-            return;
-
-        // If we change principals, we need to unlink immediately this
-        // compartment from its PerformanceGroup. For one thing, the
-        // performance data we collect should not be improperly associated
-        // with a group to which we do not belong anymore. For another thing,
-        // we use `principals()` as part of the key to map compartments
-        // to a `PerformanceGroup`, so if we do not unlink now, this will
-        // be too late once we have updated `principals_`.
-        performanceMonitoring.unlink();
-        principals_ = principals;
-    }
-    inline bool isSystem() const {
-        return isSystem_;
-    }
-    inline void setIsSystem(bool isSystem) {
-        if (isSystem_ == isSystem)
-            return;
-
-        // If we change `isSystem*(`, we need to unlink immediately this
-        // compartment from its PerformanceGroup. For one thing, the
-        // performance data we collect should not be improperly associated
-        // to a group to which we do not belong anymore. For another thing,
-        // we use `isSystem()` as part of the key to map compartments
-        // to a `PerformanceGroup`, so if we do not unlink now, this will
-        // be too late once we have updated `isSystem_`.
-        performanceMonitoring.unlink();
-        isSystem_ = isSystem;
-    }
-
-    // Used to approximate non-content code when reporting telemetry.
-    inline bool isProbablySystemCode() const {
-        return isSystem_;
-    }
-  private:
-    JSPrincipals*                principals_;
-    bool                         isSystem_;
-
-  public:
-    bool                         isSelfHosting;
-    bool                         marked;
-
-    void mark() { marked = true; }
-
   private:
     friend struct JSRuntime;
     friend struct JSContext;
 
   public:
-    js::PerformanceGroupHolder performanceMonitoring;
-
     JS::Zone* zone() { return zone_; }
     const JS::Zone* zone() const { return zone_; }
 
     JSRuntime* runtimeFromMainThread() const {
         MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(runtime_));
         return runtime_;
     }
 
@@ -643,18 +587,16 @@ struct JSCompartment
   public:
     void assertNoCrossCompartmentWrappers() {
         MOZ_ASSERT(crossCompartmentWrappers.empty());
     }
 
   public:
     js::RegExpCompartment        regExps;
 
-    js::ArraySpeciesLookup       arraySpeciesLookup;
-
     using IteratorCache = js::HashSet<js::PropertyIteratorObject*,
                                       js::IteratorHashPolicy,
                                       js::SystemAllocPolicy>;
     IteratorCache iteratorCache;
 
     /*
      * For generational GC, record whether a write barrier has added this
      * compartment's global to the store buffer since the last minor GC.
@@ -663,54 +605,33 @@ struct JSCompartment
      * written to a property of the global.
      */
     uint32_t                     globalWriteBarriered;
 
     // Non-zero if the storage underlying any typed object in this compartment
     // might be detached.
     int32_t                      detachedTypedObjects;
 
-  protected:
-    friend class js::AutoSetNewObjectMetadata;
-    js::NewObjectMetadataState objectMetadataState;
-
-  public:
     // Recompute the probability with which this compartment should record
     // profiling data (stack traces, allocations log, etc.) about each
     // allocation. We consult the probabilities requested by the Debugger
     // instances observing us, if any.
     void chooseAllocationSamplingProbability() { savedStacks_.chooseSamplingProbability(this); }
 
-    bool hasObjectPendingMetadata() const { return objectMetadataState.is<js::PendingMetadata>(); }
-
-    void setObjectPendingMetadata(JSContext* cx, JSObject* obj) {
-        if (!cx->helperThread()) {
-            MOZ_ASSERT(objectMetadataState.is<js::DelayMetadata>());
-            objectMetadataState = js::NewObjectMetadataState(js::PendingMetadata(obj));
-        }
-    }
-
   protected:
     void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,
                                 size_t* crossCompartmentWrappersArg);
 
   public:
     // Object group tables and other state in the compartment.
     js::ObjectGroupCompartment   objectGroups;
 
 #ifdef JSGC_HASH_TABLE_CHECKS
     void checkWrapperMapAfterMovingGC();
 #endif
-
-    /*
-     * Lazily initialized script source object to use for scripts cloned
-     * from the self-hosting global.
-     */
-    js::ReadBarrieredScriptSourceObject selfHostingScriptSource;
-
     // Keep track of the metadata objects which can be associated with each JS
     // object. Both keys and values are in this compartment.
     js::ObjectWeakMap* objectMetadataTable;
 
     // Map from array buffers to views sharing that storage.
     JS::WeakCache<js::InnerViewTable> innerViews;
 
     // Inline transparent typed objects do not initially have an array buffer,
@@ -733,36 +654,16 @@ struct JSCompartment
      *
      * The objects in the list are either cross-compartment wrappers, or
      * debugger wrapper objects.  The list link is either in the second extra
      * slot for the former, or a special slot for the latter.
      */
     JSObject*                    gcIncomingGrayPointers;
 
   private:
-    enum {
-        IsDebuggee = 1 << 0,
-        DebuggerObservesAllExecution = 1 << 1,
-        DebuggerObservesAsmJS = 1 << 2,
-        DebuggerObservesCoverage = 1 << 3,
-        DebuggerObservesBinarySource = 1 << 4,
-        DebuggerNeedsDelazification = 1 << 5
-    };
-
-    unsigned debugModeBits;
-    friend class AutoRestoreCompartmentDebugMode;
-
-    static const unsigned DebuggerObservesMask = IsDebuggee |
-                                                 DebuggerObservesAllExecution |
-                                                 DebuggerObservesCoverage |
-                                                 DebuggerObservesAsmJS |
-                                                 DebuggerObservesBinarySource;
-
-    void updateDebuggerObservesFlag(unsigned flag);
-
     bool getNonWrapperObjectForCurrentCompartment(JSContext* cx, js::MutableHandleObject obj);
     bool getOrCreateWrapper(JSContext* cx, js::HandleObject existing, js::MutableHandleObject obj);
 
   private:
     // This pointer is controlled by the embedder. If it is non-null, and if
     // cx->enableAccessValidation is true, then we assert that *validAccessPtr
     // is true before running any code in this compartment.
     bool* validAccessPtr;
@@ -831,197 +732,59 @@ struct JSCompartment
      */
     void traceOutgoingCrossCompartmentWrappers(JSTracer* trc);
     static void traceIncomingCrossCompartmentEdgesForZoneGC(JSTracer* trc);
 
     void sweepAfterMinorGC(JSTracer* trc);
 
     void sweepCrossCompartmentWrappers();
     void sweepSavedStacks();
-    void sweepSelfHostingScriptSource();
     void sweepJitCompartment();
     void sweepRegExps();
     void sweepDebugEnvironments();
     void sweepNativeIterators();
-    void sweepTemplateObjects();
 
     static void fixupCrossCompartmentWrappersAfterMovingGC(JSTracer* trc);
     void fixupAfterMovingGC();
 
     js::SavedStacks& savedStacks() { return savedStacks_; }
 
     void findOutgoingEdges(js::gc::ZoneComponentFinder& finder);
 
-    // Random number generator for Math.random().
-    mozilla::Maybe<mozilla::non_crypto::XorShift128PlusRNG> randomNumberGenerator;
-
-    // Initialize randomNumberGenerator if needed.
-    void ensureRandomNumberGenerator();
-
-  private:
-    mozilla::non_crypto::XorShift128PlusRNG randomKeyGenerator_;
-
-  public:
-    js::HashNumber randomHashCode();
-
-    mozilla::HashCodeScrambler randomHashCodeScrambler();
-
     static size_t offsetOfRegExps() {
         return offsetof(JSCompartment, regExps);
     }
 
-    //
-    // The Debugger observes execution on a frame-by-frame basis. The
-    // invariants of JSCompartment's debug mode bits, JSScript::isDebuggee,
-    // InterpreterFrame::isDebuggee, and BaselineFrame::isDebuggee are
-    // enumerated below.
-    //
-    // 1. When a compartment's isDebuggee() == true, relazification and lazy
-    //    parsing are disabled.
-    //
-    //    Whether AOT wasm is disabled is togglable by the Debugger API. By
-    //    default it is disabled. See debuggerObservesAsmJS below.
-    //
-    // 2. When a compartment's debuggerObservesAllExecution() == true, all of
-    //    the compartment's scripts are considered debuggee scripts.
-    //
-    // 3. A script is considered a debuggee script either when, per above, its
-    //    compartment is observing all execution, or if it has breakpoints set.
-    //
-    // 4. A debuggee script always pushes a debuggee frame.
-    //
-    // 5. A debuggee frame calls all slow path Debugger hooks in the
-    //    Interpreter and Baseline. A debuggee frame implies that its script's
-    //    BaselineScript, if extant, has been compiled with debug hook calls.
-    //
-    // 6. A debuggee script or a debuggee frame (i.e., during OSR) ensures
-    //    that the compiled BaselineScript is compiled with debug hook calls
-    //    when attempting to enter Baseline.
-    //
-    // 7. A debuggee script or a debuggee frame (i.e., during OSR) does not
-    //    attempt to enter Ion.
-    //
-    // Note that a debuggee frame may exist without its script being a
-    // debuggee script. e.g., Debugger.Frame.prototype.eval only marks the
-    // frame in which it is evaluating as a debuggee frame.
-    //
-
-    // True if this compartment's global is a debuggee of some Debugger
-    // object.
-    bool isDebuggee() const { return !!(debugModeBits & IsDebuggee); }
-    void setIsDebuggee() { debugModeBits |= IsDebuggee; }
-    void unsetIsDebuggee();
-
-    // 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() {
-        updateDebuggerObservesFlag(DebuggerObservesAllExecution);
-    }
-
-    // True if this compartment's global is a debuggee of some Debugger object
-    // whose allowUnobservedAsmJS flag is false.
-    //
-    // Note that since AOT wasm functions cannot bail out, this flag really
-    // means "observe wasm from this point forward". We cannot make
-    // already-compiled wasm code observable to Debugger.
-    bool debuggerObservesAsmJS() const {
-        static const unsigned Mask = IsDebuggee | DebuggerObservesAsmJS;
-        return (debugModeBits & Mask) == Mask;
-    }
-    void updateDebuggerObservesAsmJS() {
-        updateDebuggerObservesFlag(DebuggerObservesAsmJS);
-    }
-
-    bool debuggerObservesBinarySource() const {
-        static const unsigned Mask = IsDebuggee | DebuggerObservesBinarySource;
-        return (debugModeBits & Mask) == Mask;
-    }
-
-    void updateDebuggerObservesBinarySource() {
-        updateDebuggerObservesFlag(DebuggerObservesBinarySource);
-    }
-
-    // True if this compartment's global is a debuggee of some Debugger object
-    // whose collectCoverageInfo flag is true.
-    bool debuggerObservesCoverage() const {
-        static const unsigned Mask = DebuggerObservesCoverage;
-        return (debugModeBits & Mask) == Mask;
-    }
-    void updateDebuggerObservesCoverage();
-
-    // The code coverage can be enabled either for each compartment, with the
-    // Debugger API, or for the entire runtime.
-    bool collectCoverage() const;
-    bool collectCoverageForDebug() const;
-    bool collectCoverageForPGO() const;
-
-    bool needsDelazificationForDebugger() const {
-        return debugModeBits & DebuggerNeedsDelazification;
-    }
-
-    /*
-     * Schedule the compartment to be delazified. Called from
-     * LazyScript::Create.
-     */
-    void scheduleDelazificationForDebugger() { debugModeBits |= DebuggerNeedsDelazification; }
-
-    /*
-     * If we scheduled delazification for turning on debug mode, delazify all
-     * scripts.
-     */
-    bool ensureDelazifyScriptsForDebugger(JSContext* cx);
-
-    void clearBreakpointsIn(js::FreeOp* fop, js::Debugger* dbg, JS::HandleObject handler);
-
-  private:
-    void sweepBreakpoints(js::FreeOp* fop);
-
-  public:
     /* Bookkeeping information for debug scope objects. */
     js::DebugEnvironments* debugEnvs;
 
     /*
      * List of potentially active iterators that may need deleted property
      * suppression.
      */
     js::NativeIterator* enumerators;
 
     MOZ_ALWAYS_INLINE bool objectMaybeInIteration(JSObject* obj);
 
     // These flags help us to discover if a compartment that shouldn't be alive
-    // manages to outlive a GC.
-    bool scheduledForDestruction;
-    bool maybeAlive;
+    // manages to outlive a GC. Note that these flags have to be on the
+    // compartment, not the realm, because same-compartment realms can have
+    // cross-realm pointers without wrappers.
+    bool scheduledForDestruction = false;
+    bool maybeAlive = true;
 
   protected:
     js::jit::JitCompartment* jitCompartment_;
 
-    js::ReadBarriered<js::ArgumentsObject*> mappedArgumentsTemplate_;
-    js::ReadBarriered<js::ArgumentsObject*> unmappedArgumentsTemplate_;
-    js::ReadBarriered<js::NativeObject*> iterResultTemplate_;
-
   public:
     bool ensureJitCompartmentExists(JSContext* cx);
     js::jit::JitCompartment* jitCompartment() {
         return jitCompartment_;
     }
 
-    js::ArgumentsObject* getOrCreateArgumentsTemplateObject(JSContext* cx, bool mapped);
-
-    js::ArgumentsObject* maybeArgumentsTemplateObject(bool mapped) const;
-
-    static const size_t IterResultObjectValueSlot = 0;
-    static const size_t IterResultObjectDoneSlot = 1;
-    js::NativeObject* getOrCreateIterResultTemplateObject(JSContext* cx);
-
     // Aggregated output used to collect JSScript hit counts when code coverage
     // is enabled.
     js::coverage::LCovCompartment lcovOutput;
 };
 
 class JS::Realm : public JSCompartment
 {
     const JS::RealmCreationOptions creationOptions_;
@@ -1035,45 +798,92 @@ class JS::Realm : public JSCompartment
     // VariableDeclaration declarations in global code in this realm.
     // Names are only removed from this list by a |delete IdentifierReference|
     // that successfully removes that global property.
     using VarNamesSet = JS::GCHashSet<JSAtom*,
                                       js::DefaultHasher<JSAtom*>,
                                       js::SystemAllocPolicy>;
     VarNamesSet varNames_;
 
+    friend class js::AutoSetNewObjectMetadata;
+    js::NewObjectMetadataState objectMetadataState_ { js::ImmediateMetadata() };
+
+    // Random number generator for Math.random().
+    mozilla::Maybe<mozilla::non_crypto::XorShift128PlusRNG> randomNumberGenerator_;
+
+    // Random number generator for randomHashCodeScrambler().
+    mozilla::non_crypto::XorShift128PlusRNG randomKeyGenerator_;
+
+    JSPrincipals* principals_ = nullptr;
+
     // Used by memory reporters and invalid otherwise.
     JS::RealmStats* realmStats_ = nullptr;
 
     const js::AllocationMetadataBuilder* allocationMetadataBuilder_ = nullptr;
     void* realmPrivate_ = nullptr;
 
+    js::ReadBarriered<js::ArgumentsObject*> mappedArgumentsTemplate_ { nullptr };
+    js::ReadBarriered<js::ArgumentsObject*> unmappedArgumentsTemplate_ { nullptr };
+    js::ReadBarriered<js::NativeObject*> iterResultTemplate_ { nullptr };
+
     unsigned enterRealmDepth_ = 0;
 
+    enum {
+        IsDebuggee = 1 << 0,
+        DebuggerObservesAllExecution = 1 << 1,
+        DebuggerObservesAsmJS = 1 << 2,
+        DebuggerObservesCoverage = 1 << 3,
+        DebuggerObservesBinarySource = 1 << 4,
+        DebuggerNeedsDelazification = 1 << 5
+    };
+    static const unsigned DebuggerObservesMask = IsDebuggee |
+                                                 DebuggerObservesAllExecution |
+                                                 DebuggerObservesCoverage |
+                                                 DebuggerObservesAsmJS |
+                                                 DebuggerObservesBinarySource;
+    unsigned debugModeBits_ = 0;
+    friend class js::AutoRestoreRealmDebugMode;
+
     bool isAtomsRealm_ = false;
+    bool isSelfHostingRealm_ = false;
+    bool marked_ = true;
+    bool isSystem_ = false;
 
   public:
     // WebAssembly state for the realm.
     js::wasm::Realm wasm;
 
     js::DtoaCache dtoaCache;
     js::NewProxyCache newProxyCache;
+    js::ArraySpeciesLookup arraySpeciesLookup;
 
-    js::ScriptCountsMap* scriptCountsMap = nullptr;
-    js::ScriptNameMap* scriptNameMap = nullptr;
-    js::DebugScriptMap* debugScriptMap = nullptr;
+    js::PerformanceGroupHolder performanceMonitoring;
+
+    js::UniquePtr<js::ScriptCountsMap> scriptCountsMap;
+    js::UniquePtr<js::ScriptNameMap> scriptNameMap;
+    js::UniquePtr<js::DebugScriptMap> debugScriptMap;
+
+    /*
+     * Lazily initialized script source object to use for scripts cloned
+     * from the self-hosting global.
+     */
+    js::ReadBarrieredScriptSourceObject selfHostingScriptSource { nullptr };
 
     // Last time at which an animation was played for this realm.
     int64_t lastAnimationTime = 0;
 
     uint32_t warnedAboutStringGenericsMethods = 0;
 #ifdef DEBUG
     bool firedOnNewGlobalObject = false;
 #endif
 
+  private:
+    void updateDebuggerObservesFlag(unsigned flag);
+
+  public:
     Realm(JS::Zone* zone, const JS::RealmOptions& options);
     ~Realm();
 
     MOZ_MUST_USE bool init(JSContext* maybecx);
     void destroy(js::FreeOp* fop);
     void clearTables();
 
     void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
@@ -1102,16 +912,23 @@ class JS::Realm : public JSCompartment
 
     bool isAtomsRealm() const {
         return isAtomsRealm_;
     }
     void setIsAtomsRealm() {
         isAtomsRealm_ = true;
     }
 
+    bool isSelfHostingRealm() const {
+        return isSelfHostingRealm_;
+    }
+    void setIsSelfHostingRealm() {
+        isSelfHostingRealm_ = true;
+    }
+
     /* The global object for this realm.
      *
      * This returns nullptr if this is the atoms realm.  (The global_ field is
      * also null briefly during GC, after the global object is collected; but
      * when that happens the Realm is destroyed during the same GC.)
      *
      * In contrast, JSObject::global() is infallible because marking a JSObject
      * always marks its global as well.
@@ -1140,16 +957,19 @@ class JS::Realm : public JSCompartment
      * regardless of whether the realm's global is still live.
      */
     void traceRoots(JSTracer* trc, js::gc::GCRuntime::TraceOrMarkRuntime traceOrMark);
     /*
      * This method clears out tables of roots in preparation for the final GC.
      */
     void finishRoots();
 
+    void sweepSelfHostingScriptSource();
+    void sweepTemplateObjects();
+
     void clearScriptCounts();
     void clearScriptNames();
 
     void purge();
 
     void fixupScriptMapsAfterMovingGC();
 
 #ifdef JSGC_HASH_TABLE_CHECKS
@@ -1191,16 +1011,26 @@ class JS::Realm : public JSCompartment
     const void* addressOfMetadataBuilder() const {
         return &allocationMetadataBuilder_;
     }
     void setAllocationMetadataBuilder(const js::AllocationMetadataBuilder* builder);
     void forgetAllocationMetadataBuilder();
     void setNewObjectMetadata(JSContext* cx, JS::HandleObject obj);
     void clearObjectMetadata();
 
+    bool hasObjectPendingMetadata() const {
+        return objectMetadataState_.is<js::PendingMetadata>();
+    }
+    void setObjectPendingMetadata(JSContext* cx, JSObject* obj) {
+        if (!cx->helperThread()) {
+            MOZ_ASSERT(objectMetadataState_.is<js::DelayMetadata>());
+            objectMetadataState_ = js::NewObjectMetadataState(js::PendingMetadata(obj));
+        }
+    }
+
     void* realmPrivate() const {
         return realmPrivate_;
     }
     void setRealmPrivate(void* p) {
         realmPrivate_ = p;
     }
 
     // This should only be called when it is non-null, i.e. during memory
@@ -1214,16 +1044,194 @@ class JS::Realm : public JSCompartment
     void nullRealmStats() {
         MOZ_ASSERT(realmStats_);
         realmStats_ = nullptr;
     }
     void setRealmStats(JS::RealmStats* newStats) {
         MOZ_ASSERT(!realmStats_ && newStats);
         realmStats_ = newStats;
     }
+
+    bool marked() const {
+        return marked_;
+    }
+    void mark() {
+        marked_ = true;
+    }
+    void unmark() {
+        marked_ = false;
+    }
+
+    /*
+     * The principals associated with this realm. Note that the same several
+     * realms may share the same principals and that a realm may change
+     * principals during its lifetime (e.g. in case of lazy parsing).
+     */
+    JSPrincipals* principals() {
+        return principals_;
+    }
+    void setPrincipals(JSPrincipals* principals) {
+        if (principals_ == principals)
+            return;
+
+        // If we change principals, we need to unlink immediately this
+        // realm from its PerformanceGroup. For one thing, the performance data
+        // we collect should not be improperly associated with a group to which
+        // we do not belong anymore. For another thing, we use `principals()` as
+        // part of the key to map realms to a `PerformanceGroup`, so if we do
+        // not unlink now, this will be too late once we have updated
+        // `principals_`.
+        performanceMonitoring.unlink();
+        principals_ = principals;
+    }
+
+    bool isSystem() const {
+        return isSystem_;
+    }
+    void setIsSystem(bool isSystem) {
+        if (isSystem_ == isSystem)
+            return;
+
+        // If we change `isSystem*(`, we need to unlink immediately this realm
+        // from its PerformanceGroup. For one thing, the performance data we
+        // collect should not be improperly associated to a group to which we
+        // do not belong anymore. For another thing, we use `isSystem()` as part
+        // of the key to map realms to a `PerformanceGroup`, so if we do not
+        // unlink now, this will be too late once we have updated `isSystem_`.
+        performanceMonitoring.unlink();
+        isSystem_ = isSystem;
+    }
+
+    // Used to approximate non-content code when reporting telemetry.
+    bool isProbablySystemCode() const {
+        return isSystem_;
+    }
+
+    static const size_t IterResultObjectValueSlot = 0;
+    static const size_t IterResultObjectDoneSlot = 1;
+    js::NativeObject* getOrCreateIterResultTemplateObject(JSContext* cx);
+
+    js::ArgumentsObject* getOrCreateArgumentsTemplateObject(JSContext* cx, bool mapped);
+    js::ArgumentsObject* maybeArgumentsTemplateObject(bool mapped) const;
+
+    //
+    // The Debugger observes execution on a frame-by-frame basis. The
+    // invariants of Realm's debug mode bits, JSScript::isDebuggee,
+    // InterpreterFrame::isDebuggee, and BaselineFrame::isDebuggee are
+    // enumerated below.
+    //
+    // 1. When a realm's isDebuggee() == true, relazification and lazy
+    //    parsing are disabled.
+    //
+    //    Whether AOT wasm is disabled is togglable by the Debugger API. By
+    //    default it is disabled. See debuggerObservesAsmJS below.
+    //
+    // 2. When a realm's debuggerObservesAllExecution() == true, all of
+    //    the realm's scripts are considered debuggee scripts.
+    //
+    // 3. A script is considered a debuggee script either when, per above, its
+    //    realm is observing all execution, or if it has breakpoints set.
+    //
+    // 4. A debuggee script always pushes a debuggee frame.
+    //
+    // 5. A debuggee frame calls all slow path Debugger hooks in the
+    //    Interpreter and Baseline. A debuggee frame implies that its script's
+    //    BaselineScript, if extant, has been compiled with debug hook calls.
+    //
+    // 6. A debuggee script or a debuggee frame (i.e., during OSR) ensures
+    //    that the compiled BaselineScript is compiled with debug hook calls
+    //    when attempting to enter Baseline.
+    //
+    // 7. A debuggee script or a debuggee frame (i.e., during OSR) does not
+    //    attempt to enter Ion.
+    //
+    // Note that a debuggee frame may exist without its script being a
+    // debuggee script. e.g., Debugger.Frame.prototype.eval only marks the
+    // frame in which it is evaluating as a debuggee frame.
+    //
+
+    // True if this realm's global is a debuggee of some Debugger
+    // object.
+    bool isDebuggee() const { return !!(debugModeBits_ & IsDebuggee); }
+    void setIsDebuggee() { debugModeBits_ |= IsDebuggee; }
+    void unsetIsDebuggee();
+
+    // 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() {
+        updateDebuggerObservesFlag(DebuggerObservesAllExecution);
+    }
+
+    // True if this realm's global is a debuggee of some Debugger object
+    // whose allowUnobservedAsmJS flag is false.
+    //
+    // Note that since AOT wasm functions cannot bail out, this flag really
+    // means "observe wasm from this point forward". We cannot make
+    // already-compiled wasm code observable to Debugger.
+    bool debuggerObservesAsmJS() const {
+        static const unsigned Mask = IsDebuggee | DebuggerObservesAsmJS;
+        return (debugModeBits_ & Mask) == Mask;
+    }
+    void updateDebuggerObservesAsmJS() {
+        updateDebuggerObservesFlag(DebuggerObservesAsmJS);
+    }
+
+    bool debuggerObservesBinarySource() const {
+        static const unsigned Mask = IsDebuggee | DebuggerObservesBinarySource;
+        return (debugModeBits_ & Mask) == Mask;
+    }
+
+    void updateDebuggerObservesBinarySource() {
+        updateDebuggerObservesFlag(DebuggerObservesBinarySource);
+    }
+
+    // True if this realm's global is a debuggee of some Debugger object
+    // whose collectCoverageInfo flag is true.
+    bool debuggerObservesCoverage() const {
+        static const unsigned Mask = DebuggerObservesCoverage;
+        return (debugModeBits_ & Mask) == Mask;
+    }
+    void updateDebuggerObservesCoverage();
+
+    // The code coverage can be enabled either for each realm, with the
+    // Debugger API, or for the entire runtime.
+    bool collectCoverage() const;
+    bool collectCoverageForDebug() const;
+    bool collectCoverageForPGO() const;
+
+    bool needsDelazificationForDebugger() const {
+        return debugModeBits_ & DebuggerNeedsDelazification;
+    }
+
+    // Schedule the realm to be delazified. Called from LazyScript::Create.
+    void scheduleDelazificationForDebugger() {
+        debugModeBits_ |= DebuggerNeedsDelazification;
+    }
+
+    // If we scheduled delazification for turning on debug mode, delazify all
+    // scripts.
+    bool ensureDelazifyScriptsForDebugger(JSContext* cx);
+
+    void clearBreakpointsIn(js::FreeOp* fop, js::Debugger* dbg, JS::HandleObject handler);
+
+    // Initializes randomNumberGenerator if needed.
+    mozilla::non_crypto::XorShift128PlusRNG& getOrCreateRandomNumberGenerator();
+
+    const void* addressOfRandomNumberGenerator() const {
+        return randomNumberGenerator_.ptr();
+    }
+
+    js::HashNumber randomHashCode();
+
+    mozilla::HashCodeScrambler randomHashCodeScrambler();
 };
 
 namespace js {
 
 // We only set the maybeAlive flag for objects and scripts. It's assumed that,
 // if a compartment is alive, then it will have at least some live object or
 // script it in. Even if we get this wrong, the worst that will happen is that
 // scheduledForDestruction will be set on the compartment, which will cause
--- a/js/src/vm/JSContext-inl.h
+++ b/js/src/vm/JSContext-inl.h
@@ -455,17 +455,17 @@ JSContext::setPendingException(const js:
     // We don't use assertSameCompartment here to allow
     // js::SetPendingExceptionCrossContext to work.
     MOZ_ASSERT_IF(v.isObject(), v.toObject().compartment() == compartment());
 }
 
 inline bool
 JSContext::runningWithTrustedPrincipals()
 {
-    return !compartment() || compartment()->principals() == runtime()->trustedPrincipals();
+    return !realm() || realm()->principals() == runtime()->trustedPrincipals();
 }
 
 inline void
 JSContext::enterNonAtomsRealm(JS::Realm* realm)
 {
     enterRealmDepth_++;
 
     MOZ_ASSERT(!realm->zone()->isAtomsZone());
--- a/js/src/vm/JSContext.cpp
+++ b/js/src/vm/JSContext.cpp
@@ -251,25 +251,25 @@ ReportError(JSContext* cx, JSErrorReport
 
 /*
  * The given JSErrorReport object have been zeroed and must not outlive
  * cx->fp() (otherwise owned fields may become invalid).
  */
 static void
 PopulateReportBlame(JSContext* cx, JSErrorReport* report)
 {
-    JSCompartment* compartment = cx->compartment();
-    if (!compartment)
+    JS::Realm* realm = cx->realm();
+    if (!realm)
         return;
 
     /*
      * Walk stack until we find a frame that is associated with a non-builtin
      * rather than a builtin frame and which we're allowed to know about.
      */
-    NonBuiltinFrameIter iter(cx, compartment->principals());
+    NonBuiltinFrameIter iter(cx, realm->principals());
     if (iter.done())
         return;
 
     report->filename = iter.filename();
     uint32_t column;
     report->lineno = iter.computeLine(&column);
     report->column = FixupColumnForDisplay(column);
     report->isMuted = iter.mutedErrors();
--- a/js/src/vm/JSFunction.cpp
+++ b/js/src/vm/JSFunction.cpp
@@ -1683,17 +1683,17 @@ JSFunction::maybeRelazify(JSRuntime* rt)
 
     // Don't relazify functions in realms that are active.
     Realm* realm = this->realm();
     if (realm->hasBeenEntered() && !rt->allowRelazificationForTesting)
         return;
 
     // The caller should have checked we're not in the self-hosting zone (it's
     // shared with worker runtimes so relazifying functions in it will race).
-    MOZ_ASSERT(!realm->isSelfHosting);
+    MOZ_ASSERT(!realm->isSelfHostingRealm());
 
     // Don't relazify if the realm is being debugged.
     if (realm->isDebuggee())
         return;
 
     // Don't relazify if the realm and/or runtime is instrumented to
     // collect code coverage for analysis.
     if (realm->collectCoverageForDebug())
@@ -2283,17 +2283,17 @@ js::CloneAsmJSModuleFunction(JSContext* 
     clone->setGroup(fun->group());
     return clone;
 }
 
 JSFunction*
 js::CloneSelfHostingIntrinsic(JSContext* cx, HandleFunction fun)
 {
     MOZ_ASSERT(fun->isNative());
-    MOZ_ASSERT(fun->compartment()->isSelfHosting);
+    MOZ_ASSERT(fun->realm()->isSelfHostingRealm());
     MOZ_ASSERT(!fun->isExtended());
     MOZ_ASSERT(cx->compartment() != fun->compartment());
 
     JSFunction* clone = NewFunctionClone(cx, fun, SingletonObject, AllocKind::FUNCTION,
                                          /* proto = */ nullptr);
     if (!clone)
         return nullptr;
 
--- a/js/src/vm/JSObject.cpp
+++ b/js/src/vm/JSObject.cpp
@@ -4161,17 +4161,17 @@ JSObject::debugCheckNewObject(ObjectGrou
         MOZ_ASSERT(finalizeFlags == 0);
     }
 
     MOZ_ASSERT_IF(clasp->hasFinalize(), heap == gc::TenuredHeap ||
                                         CanNurseryAllocateFinalizedClass(clasp) ||
                                         clasp->isProxy());
     MOZ_ASSERT_IF(group->hasUnanalyzedPreliminaryObjects(), heap == gc::TenuredHeap);
 
-    MOZ_ASSERT(!group->compartment()->hasObjectPendingMetadata());
+    MOZ_ASSERT(!group->realm()->hasObjectPendingMetadata());
 
     // Non-native classes manage their own data and slots, so numFixedSlots and
     // slotSpan are always 0. Note that proxy classes can have reserved slots
     // but they're also not included in numFixedSlots/slotSpan.
     if (!clasp->isNative()) {
         MOZ_ASSERT_IF(!clasp->isProxy(), JSCLASS_RESERVED_SLOTS(clasp) == 0);
         MOZ_ASSERT(!clasp->hasPrivate());
         MOZ_ASSERT_IF(shape, shape->numFixedSlots() == 0);
--- a/js/src/vm/JSScript-inl.h
+++ b/js/src/vm/JSScript-inl.h
@@ -174,17 +174,17 @@ JSScript::initialEnvironmentShape() cons
         return scope->environmentShape();
     }
     return nullptr;
 }
 
 inline JSPrincipals*
 JSScript::principals()
 {
-    return compartment()->principals();
+    return realm()->principals();
 }
 
 inline void
 JSScript::setBaselineScript(JSRuntime* rt, js::jit::BaselineScript* baselineScript)
 {
     if (hasBaselineScript())
         js::jit::BaselineScript::writeBarrierPre(zone(), baseline);
     MOZ_ASSERT(!ion || ion == ION_DISABLED_SCRIPT);
--- a/js/src/vm/JSScript.cpp
+++ b/js/src/vm/JSScript.cpp
@@ -1047,94 +1047,81 @@ JSScript::initScriptCounts(JSContext* cx
         ReportOutOfMemory(cx);
         return false;
     }
 
     for (size_t i = 0; i < jumpTargets.length(); i++)
         base.infallibleEmplaceBack(pcToOffset(jumpTargets[i]));
 
     // Create realm's scriptCountsMap if necessary.
-    ScriptCountsMap* map = realm()->scriptCountsMap;
-    if (!map) {
-        map = cx->new_<ScriptCountsMap>();
-        if (!map) {
+    if (!realm()->scriptCountsMap) {
+        auto map = cx->make_unique<ScriptCountsMap>();
+        if (!map || !map->init()) {
             ReportOutOfMemory(cx);
             return false;
         }
 
-        if (!map->init()) {
-            js_delete(map);
-            ReportOutOfMemory(cx);
-            return false;
-        }
-
-        realm()->scriptCountsMap = map;
+        realm()->scriptCountsMap = Move(map);
     }
 
     // Allocate the ScriptCounts.
-    ScriptCounts* sc = cx->new_<ScriptCounts>(Move(base));
+    UniqueScriptCounts sc = cx->make_unique<ScriptCounts>(Move(base));
     if (!sc) {
         ReportOutOfMemory(cx);
         return false;
     }
-    auto guardScriptCounts = mozilla::MakeScopeExit([&] () {
-        js_delete(sc);
-    });
-
-    // Register the current ScriptCounts in the compartment's map.
-    if (!map->putNew(this, sc)) {
+
+    // Register the current ScriptCounts in the realm's map.
+    if (!realm()->scriptCountsMap->putNew(this, Move(sc))) {
         ReportOutOfMemory(cx);
         return false;
     }
 
     // safe to set this;  we can't fail after this point.
     bitFields_.hasScriptCounts_ = true;
-    guardScriptCounts.release();
 
     // Enable interrupts in any interpreter frames running on this script. This
     // is used to let the interpreter increment the PCCounts, if present.
     for (ActivationIterator iter(cx); !iter.done(); ++iter) {
         if (iter->isInterpreter())
             iter->asInterpreter()->enableInterruptsIfRunning(this);
     }
 
     return true;
 }
 
 static inline ScriptCountsMap::Ptr
 GetScriptCountsMapEntry(JSScript* script)
 {
     MOZ_ASSERT(script->hasScriptCounts());
-    ScriptCountsMap* map = script->realm()->scriptCountsMap;
-    ScriptCountsMap::Ptr p = map->lookup(script);
+    ScriptCountsMap::Ptr p = script->realm()->scriptCountsMap->lookup(script);
     MOZ_ASSERT(p);
     return p;
 }
 
 static inline ScriptNameMap::Ptr
 GetScriptNameMapEntry(JSScript* script)
 {
-    ScriptNameMap* map = script->realm()->scriptNameMap;
-    auto p = map->lookup(script);
+    auto p = script->realm()->scriptNameMap->lookup(script);
     MOZ_ASSERT(p);
     return p;
 }
 
 ScriptCounts&
 JSScript::getScriptCounts()
 {
     ScriptCountsMap::Ptr p = GetScriptCountsMapEntry(this);
     return *p->value();
 }
 
 const char*
 JSScript::getScriptName()
 {
     auto p = GetScriptNameMapEntry(this);
-    return p->value();
+    return p->value().get();
 }
 
 js::PCCounts*
 ScriptCounts::maybeGetPCCounts(size_t offset) {
     PCCounts searched = PCCounts(offset);
     PCCounts* elem = std::lower_bound(pcCounts_.begin(), pcCounts_.end(), searched);
     if (elem == pcCounts_.end() || elem->pcOffset() != offset)
         return nullptr;
@@ -1288,31 +1275,26 @@ JSScript::addIonCounts(jit::IonScriptCou
 
 jit::IonScriptCounts*
 JSScript::getIonCounts()
 {
     return getScriptCounts().ionCounts_;
 }
 
 void
-JSScript::takeOverScriptCountsMapEntry(ScriptCounts* entryValue)
+JSScript::clearHasScriptCounts()
 {
-#ifdef DEBUG
-    ScriptCountsMap::Ptr p = GetScriptCountsMapEntry(this);
-    MOZ_ASSERT(entryValue == p->value());
-#endif
     bitFields_.hasScriptCounts_ = false;
 }
 
 void
 JSScript::releaseScriptCounts(ScriptCounts* counts)
 {
     ScriptCountsMap::Ptr p = GetScriptCountsMapEntry(this);
-    *counts = Move(*p->value());
-    js_delete(p->value());
+    *counts = Move(*p->value().get());
     realm()->scriptCountsMap->remove(p);
     bitFields_.hasScriptCounts_ = false;
 }
 
 void
 JSScript::destroyScriptCounts()
 {
     if (hasScriptCounts()) {
@@ -1320,17 +1302,16 @@ JSScript::destroyScriptCounts()
         releaseScriptCounts(&scriptCounts);
     }
 }
 
 void
 JSScript::destroyScriptName()
 {
     auto p = GetScriptNameMapEntry(this);
-    js_delete(p->value());
     realm()->scriptNameMap->remove(p);
 }
 
 bool
 JSScript::hasScriptName()
 {
     if (!realm()->scriptNameMap)
         return false;
@@ -2705,42 +2686,34 @@ bool
 JSScript::initScriptName(JSContext* cx)
 {
     MOZ_ASSERT(!hasScriptName());
 
     if (!filename())
         return true;
 
     // Create realm's scriptNameMap if necessary.
-    ScriptNameMap* map = realm()->scriptNameMap;
-    if (!map) {
-        map = cx->new_<ScriptNameMap>();
-        if (!map) {
+    if (!realm()->scriptNameMap) {
+        auto map = cx->make_unique<ScriptNameMap>();
+        if (!map || !map->init()) {
             ReportOutOfMemory(cx);
             return false;
         }
 
-        if (!map->init()) {
-            js_delete(map);
-            ReportOutOfMemory(cx);
-            return false;
-        }
-
-        realm()->scriptNameMap = map;
+        realm()->scriptNameMap = Move(map);
     }
 
-    char* name = js_strdup(filename());
+    UniqueChars name(js_strdup(filename()));
     if (!name) {
         ReportOutOfMemory(cx);
         return false;
     }
 
-    // Register the script name in the compartment's map.
-    if (!map->putNew(this, name)) {
-        js_delete(name);
+    // Register the script name in the realm's map.
+    if (!realm()->scriptNameMap->putNew(this, Move(name))) {
         ReportOutOfMemory(cx);
         return false;
     }
 
     return true;
 }
 
 static inline uint8_t*
@@ -3365,17 +3338,17 @@ js::DescribeScriptedCallerForCompilation
 
         *file = maybeScript->filename();
         *linenop = GET_UINT32(nextpc);
         *pcOffset = pc - maybeScript->code();
         *mutedErrors = maybeScript->mutedErrors();
         return;
     }
 
-    NonBuiltinFrameIter iter(cx, cx->compartment()->principals());
+    NonBuiltinFrameIter iter(cx, cx->realm()->principals());
 
     if (iter.done()) {
         maybeScript.set(nullptr);
         *file = nullptr;
         *linenop = 0;
         *pcOffset = 0;
         *mutedErrors = false;
         return;
@@ -3621,27 +3594,27 @@ static JSScript*
 CreateEmptyScriptForClone(JSContext* cx, HandleScript src)
 {
     /*
      * Wrap the script source object as needed. Self-hosted scripts may be
      * in another runtime, so lazily create a new script source object to
      * use for them.
      */
     RootedObject sourceObject(cx);
-    if (cx->runtime()->isSelfHostingCompartment(src->compartment())) {
-        if (!cx->compartment()->selfHostingScriptSource) {
+    if (src->realm()->isSelfHostingRealm()) {
+        if (!cx->realm()->selfHostingScriptSource) {
             CompileOptions options(cx);
             FillSelfHostingCompileOptions(options);
 
             ScriptSourceObject* obj = frontend::CreateScriptSourceObject(cx, options);
             if (!obj)
                 return nullptr;
-            cx->compartment()->selfHostingScriptSource.set(obj);
+            cx->realm()->selfHostingScriptSource.set(obj);
         }
-        sourceObject = cx->compartment()->selfHostingScriptSource;
+        sourceObject = cx->realm()->selfHostingScriptSource;
     } else {
         sourceObject = src->sourceObject();
         if (!cx->compartment()->wrap(cx, &sourceObject))
             return nullptr;
     }
 
     CompileOptions options(cx);
     options.setMutedErrors(src->mutedErrors())
@@ -3724,32 +3697,32 @@ js::CloneScriptIntoFunction(JSContext* c
 
     return dst;
 }
 
 DebugScript*
 JSScript::debugScript()
 {
     MOZ_ASSERT(bitFields_.hasDebugScript_);
-    DebugScriptMap* map = realm()->debugScriptMap;
+    DebugScriptMap* map = realm()->debugScriptMap.get();
     MOZ_ASSERT(map);
     DebugScriptMap::Ptr p = map->lookup(this);
     MOZ_ASSERT(p);
-    return p->value();
+    return p->value().get();
 }
 
 DebugScript*
 JSScript::releaseDebugScript()
 {
     MOZ_ASSERT(bitFields_.hasDebugScript_);
-    DebugScriptMap* map = realm()->debugScriptMap;
+    DebugScriptMap* map = realm()->debugScriptMap.get();
     MOZ_ASSERT(map);
     DebugScriptMap::Ptr p = map->lookup(this);
     MOZ_ASSERT(p);
-    DebugScript* debug = p->value();
+    DebugScript* debug = p->value().release();
     map->remove(p);
     bitFields_.hasDebugScript_ = false;
     return debug;
 }
 
 void
 JSScript::destroyDebugScript(FreeOp* fop)
 {
@@ -3769,36 +3742,32 @@ JSScript::destroyDebugScript(FreeOp* fop
 
 bool
 JSScript::ensureHasDebugScript(JSContext* cx)
 {
     if (bitFields_.hasDebugScript_)
         return true;
 
     size_t nbytes = offsetof(DebugScript, breakpoints) + length() * sizeof(BreakpointSite*);
-    DebugScript* debug = (DebugScript*) zone()->pod_calloc<uint8_t>(nbytes);
+    UniqueDebugScript debug(reinterpret_cast<DebugScript*>(zone()->pod_calloc<uint8_t>(nbytes)));
     if (!debug)
         return false;
 
     /* Create realm's debugScriptMap if necessary. */
-    DebugScriptMap* map = realm()->debugScriptMap;
-    if (!map) {
-        map = cx->new_<DebugScriptMap>();
-        if (!map || !map->init()) {
-            js_free(debug);
-            js_delete(map);
+    if (!realm()->debugScriptMap) {
+        auto map = cx->make_unique<DebugScriptMap>();
+        if (!map || !map->init())
             return false;
-        }
-        realm()->debugScriptMap = map;
+
+        realm()->debugScriptMap = Move(map);
     }
 
-    if (!map->putNew(this, debug)) {
-        js_free(debug);
+    if (!realm()->debugScriptMap->putNew(this, Move(debug)))
         return false;
-    }
+
     bitFields_.hasDebugScript_ = true; // safe to set this;  we can't fail after this point
 
     /*
      * Ensure that any Interpret() instances running on this script have
      * interrupts enabled. The interrupts must stay enabled until the
      * debug state is destroyed.
      */
     for (ActivationIterator iter(cx); !iter.done(); ++iter) {
@@ -3824,17 +3793,17 @@ JSScript::setNewStepMode(FreeOp* fop, ui
             fop->free_(releaseDebugScript());
     }
 }
 
 bool
 JSScript::incrementStepModeCount(JSContext* cx)
 {
     assertSameCompartment(cx, this);
-    MOZ_ASSERT(cx->compartment()->isDebuggee());
+    MOZ_ASSERT(cx->realm()->isDebuggee());
 
     if (!ensureHasDebugScript(cx))
         return false;
 
     DebugScript* debug = debugScript();
     uint32_t count = debug->stepMode;
     setNewStepMode(cx->runtime()->defaultFreeOp(), count + 1);
     return true;
@@ -3951,17 +3920,17 @@ JSScript::traceChildren(JSTracer* trc)
 
     MOZ_ASSERT_IF(sourceObject(), MaybeForwarded(sourceObject())->compartment() == compartment());
     TraceNullableEdge(trc, &sourceObject_, "sourceObject");
 
     if (maybeLazyScript())
         TraceManuallyBarrieredEdge(trc, &lazyScript, "lazyScript");
 
     if (trc->isMarkingTracer())
-        compartment()->mark();
+        realm()->mark();
 
     jit::TraceJitScripts(trc, this);
 }
 
 void
 LazyScript::finalize(FreeOp* fop)
 {
     fop->free_(table_);
@@ -4285,29 +4254,30 @@ LazyScript::CreateRaw(JSContext* cx, Han
         ReportOutOfMemory(cx);
         return nullptr;
     }
 
     LazyScript* res = Allocate<LazyScript>(cx);
     if (!res)
         return nullptr;
 
-    cx->compartment()->scheduleDelazificationForDebugger();
+    cx->realm()->scheduleDelazificationForDebugger();
 
     return new (res) LazyScript(fun, *sourceObject, table.forget(), packed, sourceStart, sourceEnd,
                                 toStringStart, lineno, column);
 }
 
 /* static */ LazyScript*
 LazyScript::Create(JSContext* cx, HandleFunction fun,
                    HandleScriptSourceObject sourceObject,
                    const frontend::AtomVector& closedOverBindings,
                    Handle<GCVector<JSFunction*, 8>> innerFunctions,
                    uint32_t sourceStart, uint32_t sourceEnd,
-                   uint32_t toStringStart, uint32_t lineno, uint32_t column)
+                   uint32_t toStringStart, uint32_t lineno, uint32_t column,
+                   frontend::ParseGoal parseGoal)
 {
     union {
         PackedView p;
         uint64_t packedFields;
     };
 
     p.shouldDeclareArguments = false;
     p.hasThisBinding = false;
@@ -4318,16 +4288,17 @@ LazyScript::Create(JSContext* cx, Handle
     p.isGenerator = false;
     p.strict = false;
     p.bindingsAccessedDynamically = false;
     p.hasDebuggerStatement = false;
     p.hasDirectEval = false;
     p.isLikelyConstructorWrapper = false;
     p.isDerivedClassConstructor = false;
     p.needsHomeObject = false;
+    p.parseGoal = uint32_t(parseGoal);
 
     LazyScript* res = LazyScript::CreateRaw(cx, fun, sourceObject, packedFields,
                                             sourceStart, sourceEnd,
                                             toStringStart, lineno, column);
     if (!res)
         return nullptr;
 
     JSAtom** resClosedOverBindings = res->closedOverBindings();
@@ -4459,39 +4430,39 @@ JSScript::mayReadFrameArgsDirectly()
 {
     return argumentsHasVarBinding() || hasRest();
 }
 
 void
 JSScript::AutoDelazify::holdScript(JS::HandleFunction fun)
 {
     if (fun) {
-        if (fun->compartment()->isSelfHosting) {
-            // The self-hosting compartment is shared across runtimes, so we
-            // can't use JSAutoRealm: it could cause races. Functions in the
-            // self-hosting compartment will never be lazy, so we can safely
-            // assume we don't have to delazify.
+        if (fun->realm()->isSelfHostingRealm()) {
+            // The self-hosting realm is shared across runtimes, so we can't use
+            // JSAutoRealm: it could cause races. Functions in the self-hosting
+            // realm will never be lazy, so we can safely assume we don't have
+            // to delazify.
             script_ = fun->nonLazyScript();
         } else {
             JSAutoRealm ar(cx_, fun);
             script_ = JSFunction::getOrCreateScript(cx_, fun);
             if (script_) {
                 oldDoNotRelazify_ = script_->bitFields_.doNotRelazify_;
                 script_->setDoNotRelazify(true);
             }
         }
     }
 }
 
 void
 JSScript::AutoDelazify::dropScript()
 {
-    // Don't touch script_ if it's in the self-hosting compartment, see the
-    // comment in holdScript.
-    if (script_ && !script_->compartment()->isSelfHosting)
+    // Don't touch script_ if it's in the self-hosting realm, see the comment
+    // in holdScript.
+    if (script_ && !script_->realm()->isSelfHostingRealm())
         script_->setDoNotRelazify(oldDoNotRelazify_);
     script_ = nullptr;
 }
 
 JS::ubi::Node::Size
 JS::ubi::Concrete<JSScript>::size(mozilla::MallocSizeOf mallocSizeOf) const
 {
     Size size = Arena::thingSize(get().asTenured().getAllocKind());
--- a/js/src/vm/JSScript.h
+++ b/js/src/vm/JSScript.h
@@ -230,27 +230,29 @@ class ScriptCounts
     PCCountsVector throwCounts_;
 
     // Information about any Ion compilations for the script.
     jit::IonScriptCounts* ionCounts_;
 };
 
 // Note: The key of this hash map is a weak reference to a JSScript.  We do not
 // use the WeakMap implementation provided in gc/WeakMap.h because it would be
-// collected at the beginning of the sweeping of the compartment, thus before
-// the calls to the JSScript::finalize function which are used to aggregate
-// code coverage results on the compartment.
-typedef HashMap<JSScript*,
-                ScriptCounts*,
-                DefaultHasher<JSScript*>,
-                SystemAllocPolicy> ScriptCountsMap;
-typedef HashMap<JSScript*,
-                const char*,
-                DefaultHasher<JSScript*>,
-                SystemAllocPolicy> ScriptNameMap;
+// collected at the beginning of the sweeping of the realm, thus before the
+// calls to the JSScript::finalize function which are used to aggregate code
+// coverage results on the realm.
+using UniqueScriptCounts = js::UniquePtr<ScriptCounts>;
+using ScriptCountsMap = HashMap<JSScript*,
+                                UniqueScriptCounts,
+                                DefaultHasher<JSScript*>,
+                                SystemAllocPolicy>;
+
+using ScriptNameMap = HashMap<JSScript*,
+                              JS::UniqueChars,
+                              DefaultHasher<JSScript*>,
+                              SystemAllocPolicy>;
 
 class DebugScript
 {
     friend class ::JSScript;
     friend class JS::Realm;
 
     /*
      * When non-zero, compile script in single-step mode. The top bit is set and
@@ -271,20 +273,21 @@ class DebugScript
      * Breakpoints set in our script. For speed and simplicity, this array is
      * parallel to script->code(): the BreakpointSite for the opcode at
      * script->code()[offset] is debugScript->breakpoints[offset]. Naturally,
      * this array's true length is script->length().
      */
     BreakpointSite* breakpoints[1];
 };
 
-typedef HashMap<JSScript*,
-                DebugScript*,
-                DefaultHasher<JSScript*>,
-                SystemAllocPolicy> DebugScriptMap;
+using UniqueDebugScript = js::UniquePtr<DebugScript, JS::FreePolicy>;
+using DebugScriptMap = HashMap<JSScript*,
+                               UniqueDebugScript,
+                               DefaultHasher<JSScript*>,
+                               SystemAllocPolicy>;
 
 class ScriptSource;
 
 struct ScriptSourceChunk
 {
     ScriptSource* ss;
     uint32_t chunk;
 
@@ -1843,18 +1846,17 @@ class JSScript : public js::gc::TenuredC
     js::PCCounts* getThrowCounts(jsbytecode* pc);
     uint64_t getHitCount(jsbytecode* pc);
     void incHitCount(jsbytecode* pc); // Used when we bailout out of Ion.
     void addIonCounts(js::jit::IonScriptCounts* ionCounts);
     js::jit::IonScriptCounts* getIonCounts();
     void releaseScriptCounts(js::ScriptCounts* counts);
     void destroyScriptCounts();
     void destroyScriptName();
-    // The entry should be removed after using this function.
-    void takeOverScriptCountsMapEntry(js::ScriptCounts* entryValue);
+    void clearHasScriptCounts();
 
     jsbytecode* main() const {
         return code() + mainOffset();
     }
 
     /*
      * computedSizeOfData() is the in-use size of all the data sections.
      * sizeOfData() is the size of the block allocated to hold all the data
@@ -2182,16 +2184,17 @@ class LazyScript : public gc::TenuredCel
         uint32_t hasDebuggerStatement : 1;
         uint32_t hasDirectEval : 1;
         uint32_t isLikelyConstructorWrapper : 1;
         uint32_t hasBeenCloned : 1;
         uint32_t treatAsRunOnce : 1;
         uint32_t isDerivedClassConstructor : 1;
         uint32_t needsHomeObject : 1;
         uint32_t hasRest : 1;
+        uint32_t parseGoal : 1;
     };
 
     union {
         PackedView p_;
         uint64_t packedFields_;
     };
 
     // Source location for the script.
@@ -2224,17 +2227,18 @@ class LazyScript : public gc::TenuredCel
 
     // Create a LazyScript and initialize closedOverBindings and innerFunctions
     // with the provided vectors.
     static LazyScript* Create(JSContext* cx, HandleFunction fun,
                               HandleScriptSourceObject sourceObject,
                               const frontend::AtomVector& closedOverBindings,
                               Handle<GCVector<JSFunction*, 8>> innerFunctions,
                               uint32_t begin, uint32_t end,
-                              uint32_t toStringStart, uint32_t lineno, uint32_t column);
+                              uint32_t toStringStart, uint32_t lineno, uint32_t column,
+                              frontend::ParseGoal parseGoal);
 
     // Create a LazyScript and initialize the closedOverBindings and the
     // innerFunctions with dummy values to be replaced in a later initialization
     // phase.
     //
     // The "script" argument to this function can be null.  If it's non-null,
     // then this LazyScript should be associated with the given JSScript.
     //
@@ -2321,16 +2325,20 @@ class LazyScript : public gc::TenuredCel
 
     bool hasRest() const {
         return p_.hasRest;
     }
     void setHasRest() {
         p_.hasRest = true;
     }
 
+    frontend::ParseGoal parseGoal() const {
+        return frontend::ParseGoal(p_.parseGoal);
+    }
+
     bool strict() const {
         return p_.strict;
     }
     void setStrict() {
         p_.strict = true;
     }
 
     bool bindingsAccessedDynamically() const {
--- a/js/src/vm/MemoryMetrics.cpp
+++ b/js/src/vm/MemoryMetrics.cpp
@@ -871,29 +871,29 @@ JS::CollectRuntimeStats(JSContext* cx, R
 {
     return CollectRuntimeStatsHelper(cx, rtStats, opv, anonymize, StatsCellCallback<FineGrained>);
 }
 
 JS_PUBLIC_API(size_t)
 JS::SystemRealmCount(JSContext* cx)
 {
     size_t n = 0;
-    for (CompartmentsIter comp(cx->runtime(), WithAtoms); !comp.done(); comp.next()) {
-        if (comp->isSystem())
+    for (RealmsIter realm(cx->runtime(), WithAtoms); !realm.done(); realm.next()) {
+        if (realm->isSystem())
             ++n;
     }
     return n;
 }
 
 JS_PUBLIC_API(size_t)
 JS::UserRealmCount(JSContext* cx)
 {
     size_t n = 0;
-    for (CompartmentsIter comp(cx->runtime(), WithAtoms); !comp.done(); comp.next()) {
-        if (!comp->isSystem())
+    for (RealmsIter realm(cx->runtime(), WithAtoms); !realm.done(); realm.next()) {
+        if (!realm->isSystem())
             ++n;
     }
     return n;
 }
 
 JS_PUBLIC_API(size_t)
 JS::PeakSizeOfTemporary(const JSContext* cx)
 {
--- a/js/src/vm/NativeObject-inl.h
+++ b/js/src/vm/NativeObject-inl.h
@@ -548,17 +548,17 @@ NativeObject::create(JSContext* cx, js::
 
     if (clasp->hasPrivate())
         nobj->initPrivate(nullptr);
 
     if (size_t span = shape->slotSpan())
         nobj->initializeSlotRange(0, span);
 
     if (clasp->shouldDelayMetadataBuilder())
-        cx->compartment()->setObjectPendingMetadata(cx, nobj);
+        cx->realm()->setObjectPendingMetadata(cx, nobj);
     else
         nobj = SetNewObjectMetadata(cx, nobj);
 
     js::gc::gcTracer.traceCreateObject(nobj);
 
     return nobj;
 }
 
--- a/js/src/vm/Opcodes.h
+++ b/js/src/vm/Opcodes.h
@@ -58,16 +58,17 @@ 1234567890123456789012345678901234567890
  *     Local Variables
  *     Aliased Variables
  *     Intrinsics
  *     Block-local Scope
  *     This
  *     Super
  *     Arguments
  *     Var Scope
+ *     Modules
  *   [Operators]
  *     Comparison Operators
  *     Arithmetic Operators
  *     Bitwise Logical Operators
  *     Bitwise Shift Operators
  *     Logical Operators
  *     Special Operators
  *     Stack Operations
@@ -2339,24 +2340,32 @@ 1234567890123456789012345678901234567890
      * Like JSOP_CALL, but tells the function that the return value is ignored.
      * stack.
      *   Category: Statements
      *   Type: Function
      *   Operands: uint16_t argc
      *   Stack: callee, this, args[0], ..., args[argc-1] => rval
      *   nuses: (argc+2)
      */ \
-    macro(JSOP_CALL_IGNORES_RV, 231, "call-ignores-rv", NULL, 3, -1, 1, JOF_UINT16|JOF_INVOKE|JOF_TYPESET)
+    macro(JSOP_CALL_IGNORES_RV, 231, "call-ignores-rv", NULL, 3, -1, 1, JOF_UINT16|JOF_INVOKE|JOF_TYPESET) \
+    /*
+     * Push "import.meta"
+     *
+     *   Category: Variables and Scopes
+     *   Type: Modules
+     *   Operands:
+     *   Stack: => import.meta
+     */ \
+    macro(JSOP_IMPORTMETA,    232, "importmeta", NULL,      1,  0,  1,  JOF_BYTE)
 
 /*
  * In certain circumstances it may be useful to "pad out" the opcode space to
  * a power of two.  Use this macro to do so.
  */
 #define FOR_EACH_TRAILING_UNUSED_OPCODE(macro) \
-    macro(232) \
     macro(233) \
     macro(234) \
     macro(235) \
     macro(236) \
     macro(237) \
     macro(238) \
     macro(239) \
     macro(240) \
--- a/js/src/vm/ProxyObject.cpp
+++ b/js/src/vm/ProxyObject.cpp
@@ -185,17 +185,17 @@ ProxyObject::create(JSContext* cx, const
     if (!obj)
         return cx->alreadyReportedOOM();
 
     ProxyObject* pobj = static_cast<ProxyObject*>(obj);
     pobj->initGroup(group);
     pobj->initShape(shape);
 
     MOZ_ASSERT(clasp->shouldDelayMetadataBuilder());
-    cx->compartment()->setObjectPendingMetadata(cx, pobj);
+    cx->realm()->setObjectPendingMetadata(cx, pobj);
 
     js::gc::gcTracer.traceCreateObject(pobj);
 
     if (newKind == SingletonObject) {
         Rooted<ProxyObject*> pobjRoot(cx, pobj);
         if (!JSObject::setSingleton(cx, pobjRoot))
             return cx->alreadyReportedOOM();
         pobj = pobjRoot;
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -170,17 +170,18 @@ JSRuntime::JSRuntime(JSRuntime* parentRu
     autoWritableJitCodeActive_(false),
     oomCallback(nullptr),
     debuggerMallocSizeOf(ReturnZeroSize),
     lastAnimationTime(0),
     performanceMonitoring_(),
     stackFormat_(parentRuntime ? js::StackFormat::Default
                                : js::StackFormat::SpiderMonkey),
     wasmInstances(mutexid::WasmRuntimeInstances),
-    moduleResolveHook()
+    moduleResolveHook(),
+    moduleMetadataHook()
 {
     JS_COUNT_CTOR(JSRuntime);
     liveRuntimesCount++;
 
     lcovOutput().init();
 }
 
 JSRuntime::~JSRuntime()
@@ -454,17 +455,17 @@ HandleInterrupt(JSContext* cx, bool invo
     for (JSInterruptCallback cb : cx->interruptCallbacks()) {
         if (!cb(cx))
             stop = true;
     }
 
     if (!stop) {
         // Debugger treats invoking the interrupt callback as a "step", so
         // invoke the onStep handler.
-        if (cx->compartment()->isDebuggee()) {
+        if (cx->realm()->isDebuggee()) {
             ScriptFrameIter iter(cx);
             if (!iter.done() &&
                 cx->compartment() == iter.compartment() &&
                 iter.script()->stepModeEnabled())
             {
                 RootedValue rval(cx);
                 switch (Debugger::onSingleStep(cx, &rval)) {
                   case ResumeMode::Terminate:
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -581,17 +581,16 @@ struct JSRuntime : public js::MallocProv
     }
 
     bool initSelfHosting(JSContext* cx);
     void finishSelfHosting();
     void traceSelfHostingGlobal(JSTracer* trc);
     bool isSelfHostingGlobal(JSObject* global) {
         return global == selfHostingGlobal_;
     }
-    bool isSelfHostingCompartment(JSCompartment* comp) const;
     bool isSelfHostingZone(const JS::Zone* zone) const;
     bool createLazySelfHostedFunctionClone(JSContext* cx, js::HandlePropertyName selfHostedName,
                                            js::HandleAtom name, unsigned nargs,
                                            js::HandleObject proto,
                                            js::NewObjectKind newKind,
                                            js::MutableHandleFunction fun);
     bool cloneSelfHostedFunctionScript(JSContext* cx, js::Handle<js::PropertyName*> name,
                                        js::Handle<JSFunction*> targetFun);
@@ -938,16 +937,20 @@ struct JSRuntime : public js::MallocProv
     // List of all the live wasm::Instances in the runtime. Equal to the union
     // of all instances registered in all JSCompartments. Accessed from watchdog
     // threads for purposes of wasm::InterruptRunningCode().
     js::ExclusiveData<js::wasm::InstanceVector> wasmInstances;
 
     // The implementation-defined abstract operation HostResolveImportedModule.
     js::MainThreadData<JS::ModuleResolveHook> moduleResolveHook;
 
+    // A hook that implements the abstract operations
+    // HostGetImportMetaProperties and HostFinalizeImportMeta.
+    js::MainThreadData<JS::ModuleMetadataHook> moduleMetadataHook;
+
   public:
 #if defined(JS_BUILD_BINAST)
     js::BinaryASTSupport& binast() {
         return binast_;
     }
   private:
     js::BinaryASTSupport binast_;
 #endif // defined(JS_BUILD_BINAST)
--- a/js/src/vm/SavedStacks.cpp
+++ b/js/src/vm/SavedStacks.cpp
@@ -609,29 +609,29 @@ SavedFrame::construct(JSContext* cx, uns
 
 static bool
 SavedFrameSubsumedByCaller(JSContext* cx, HandleSavedFrame frame)
 {
     auto subsumes = cx->runtime()->securityCallbacks->subsumes;
     if (!subsumes)
         return true;
 
-    auto currentCompartmentPrincipals = cx->compartment()->principals();
-    MOZ_ASSERT(!ReconstructedSavedFramePrincipals::is(currentCompartmentPrincipals));
+    auto currentRealmPrincipals = cx->realm()->principals();
+    MOZ_ASSERT(!ReconstructedSavedFramePrincipals::is(currentRealmPrincipals));
 
     auto framePrincipals = frame->getPrincipals();
 
     // Handle SavedFrames that have been reconstructed from stacks in a heap
     // snapshot.
     if (framePrincipals == &ReconstructedSavedFramePrincipals::IsSystem)
         return cx->runningWithTrustedPrincipals();
     if (framePrincipals == &ReconstructedSavedFramePrincipals::IsNotSystem)
         return true;
 
-    return subsumes(currentCompartmentPrincipals, framePrincipals);
+    return subsumes(currentRealmPrincipals, framePrincipals);
 }
 
 // Return the first SavedFrame in the chain that starts with |frame| whose
 // for which the given match function returns true. If there is no such frame,
 // return nullptr. |skippedAsync| is set to true if any of the skipped frames
 // had the |asyncCause| property set, otherwise it is explicitly set to false.
 template<typename Matcher>
 static SavedFrame*
@@ -766,39 +766,39 @@ namespace {
 // stack) but we're working with an SavedFrame object that was captured in
 // unprivileged code.  If so, drop privileges down to its level.  The idea is
 // that this way devtools code that's asking an exception object for a stack to
 // display will end up with the stack the web developer would see via doing
 // .stack in a web page, with Firefox implementation details excluded.
 //
 // We want callers to pass us the object they were actually passed, not an
 // unwrapped form of it.  That way Xray access to SavedFrame objects should not
-// be affected by AutoMaybeEnterFrameCompartment and the only things that will
+// be affected by AutoMaybeEnterFrameRealm and the only things that will
 // be affected will be cases in which privileged code works with some C++ object
 // that then pokes at an unprivileged StackFrame it has on hand.
-class MOZ_STACK_CLASS AutoMaybeEnterFrameCompartment
+class MOZ_STACK_CLASS AutoMaybeEnterFrameRealm
 {
 public:
-    AutoMaybeEnterFrameCompartment(JSContext* cx,
-                                   HandleObject obj
-                                   MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+    AutoMaybeEnterFrameRealm(JSContext* cx,
+                             HandleObject obj
+                             MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
 
-        MOZ_RELEASE_ASSERT(cx->compartment());
+        MOZ_RELEASE_ASSERT(cx->realm());
         if (obj)
-            MOZ_RELEASE_ASSERT(obj->compartment());
+            MOZ_RELEASE_ASSERT(obj->realm());
 
         // Note that obj might be null here, since we're doing this before
         // UnwrapSavedFrame.
-        if (obj && cx->compartment() != obj->compartment())
+        if (obj && cx->realm() != obj->realm())
         {
             JSSubsumesOp subsumes = cx->runtime()->securityCallbacks->subsumes;
-            if (subsumes && subsumes(cx->compartment()->principals(),
-                                     obj->compartment()->principals()))
+            if (subsumes && subsumes(cx->realm()->principals(),
+                                     obj->realm()->principals()))
             {
                 ar_.emplace(cx, obj);
             }
         }
     }
 
  private:
     Maybe<JSAutoRealm> ar_;
@@ -827,17 +827,17 @@ JS_PUBLIC_API(SavedFrameResult)
 GetSavedFrameSource(JSContext* cx, HandleObject savedFrame, MutableHandleString sourcep,
                     SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */)
 {
     js::AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     MOZ_RELEASE_ASSERT(cx->compartment());
 
     {
-        AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
+        AutoMaybeEnterFrameRealm ar(cx, savedFrame);
         bool skippedAsync;
         js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, selfHosted, skippedAsync));
         if (!frame) {
             sourcep.set(cx->runtime()->emptyString);
             return SavedFrameResult::AccessDenied;
         }
         sourcep.set(frame->getSource());
     }
@@ -850,17 +850,17 @@ JS_PUBLIC_API(SavedFrameResult)
 GetSavedFrameLine(JSContext* cx, HandleObject savedFrame, uint32_t* linep,
                   SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */)
 {
     js::AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     MOZ_RELEASE_ASSERT(cx->compartment());
     MOZ_ASSERT(linep);
 
-    AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
+    AutoMaybeEnterFrameRealm ar(cx, savedFrame);
     bool skippedAsync;
     js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, selfHosted, skippedAsync));
     if (!frame) {
         *linep = 0;
         return SavedFrameResult::AccessDenied;
     }
     *linep = frame->getLine();
     return SavedFrameResult::Ok;
@@ -870,17 +870,17 @@ JS_PUBLIC_API(SavedFrameResult)
 GetSavedFrameColumn(JSContext* cx, HandleObject savedFrame, uint32_t* columnp,
                     SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */)
 {
     js::AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     MOZ_RELEASE_ASSERT(cx->compartment());
     MOZ_ASSERT(columnp);
 
-    AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
+    AutoMaybeEnterFrameRealm ar(cx, savedFrame);
     bool skippedAsync;
     js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, selfHosted, skippedAsync));
     if (!frame) {
         *columnp = 0;
         return SavedFrameResult::AccessDenied;
     }
     *columnp = frame->getColumn();
     return SavedFrameResult::Ok;
@@ -890,17 +890,17 @@ JS_PUBLIC_API(SavedFrameResult)
 GetSavedFrameFunctionDisplayName(JSContext* cx, HandleObject savedFrame, MutableHandleString namep,
                                  SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */)
 {
     js::AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     MOZ_RELEASE_ASSERT(cx->compartment());
 
     {
-        AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
+        AutoMaybeEnterFrameRealm ar(cx, savedFrame);
         bool skippedAsync;
         js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, selfHosted, skippedAsync));
         if (!frame) {
             namep.set(nullptr);
             return SavedFrameResult::AccessDenied;
         }
         namep.set(frame->getFunctionDisplayName());
     }
@@ -913,17 +913,17 @@ JS_PUBLIC_API(SavedFrameResult)
 GetSavedFrameAsyncCause(JSContext* cx, HandleObject savedFrame, MutableHandleString asyncCausep,
                         SavedFrameSelfHosted unused_ /* = SavedFrameSelfHosted::Include */)
 {
     js::AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     MOZ_RELEASE_ASSERT(cx->compartment());
 
     {
-        AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
+        AutoMaybeEnterFrameRealm ar(cx, savedFrame);
         bool skippedAsync;
         // This function is always called with self-hosted frames excluded by
         // GetValueIfNotCached in dom/bindings/Exceptions.cpp. However, we want
         // to include them because our Promise implementation causes us to have
         // the async cause on a self-hosted frame. So we just ignore the
         // parameter and always include self-hosted frames.
         js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, SavedFrameSelfHosted::Include,
                                                         skippedAsync));
@@ -943,17 +943,17 @@ GetSavedFrameAsyncCause(JSContext* cx, H
 JS_PUBLIC_API(SavedFrameResult)
 GetSavedFrameAsyncParent(JSContext* cx, HandleObject savedFrame, MutableHandleObject asyncParentp,
                          SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */)
 {
     js::AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     MOZ_RELEASE_ASSERT(cx->compartment());
 
-    AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
+    AutoMaybeEnterFrameRealm ar(cx, savedFrame);
     bool skippedAsync;
     js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, selfHosted, skippedAsync));
     if (!frame) {
         asyncParentp.set(nullptr);
         return SavedFrameResult::AccessDenied;
     }
     js::RootedSavedFrame parent(cx, frame->getParent());
 
@@ -976,17 +976,17 @@ GetSavedFrameAsyncParent(JSContext* cx, 
 JS_PUBLIC_API(SavedFrameResult)
 GetSavedFrameParent(JSContext* cx, HandleObject savedFrame, MutableHandleObject parentp,
                     SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */)
 {
     js::AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     MOZ_RELEASE_ASSERT(cx->compartment());
 
-    AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
+    AutoMaybeEnterFrameRealm ar(cx, savedFrame);
     bool skippedAsync;
     js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, selfHosted, skippedAsync));
     if (!frame) {
         parentp.set(nullptr);
         return SavedFrameResult::AccessDenied;
     }
     js::RootedSavedFrame parent(cx, frame->getParent());
 
@@ -1089,21 +1089,21 @@ BuildStackString(JSContext* cx, HandleOb
 
     js::StringBuffer sb(cx);
 
     if (format == js::StackFormat::Default)
         format = cx->runtime()->stackFormat();
     MOZ_ASSERT(format != js::StackFormat::Default);
 
     // Enter a new block to constrain the scope of possibly entering the stack's
-    // compartment. This ensures that when we finish the StringBuffer, we are
-    // back in the cx's original compartment, and fulfill our contract with
-    // callers to place the output string in the cx's current compartment.
+    // realm. This ensures that when we finish the StringBuffer, we are back in
+    // the cx's original compartment, and fulfill our contract with callers to
+    // place the output string in the cx's current realm.
     {
-        AutoMaybeEnterFrameCompartment ac(cx, stack);
+        AutoMaybeEnterFrameRealm ar(cx, stack);
         bool skippedAsync;
         js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, stack, SavedFrameSelfHosted::Exclude,
                                                         skippedAsync));
         if (!frame) {
             stringp.set(cx->runtime()->emptyString);
             return true;
         }
 
@@ -1476,17 +1476,17 @@ SavedStacks::insertFrames(JSContext* cx,
         {
             AutoRealmUnchecked ar(cx, iter.compartment());
             if (!cx->compartment()->savedStacks().getLocation(cx, iter, &location))
                 return false;
         }
 
         RootedAtom displayAtom(cx, iter.maybeFunctionDisplayAtom());
 
-        auto principals = iter.compartment()->principals();
+        auto principals = JS::GetRealmForCompartment(iter.compartment())->principals();
         MOZ_ASSERT_IF(framePtr && !iter.isWasm(), iter.pc());
 
         if (!stackChain->emplaceBack(location.source(),
                                      location.line(),
                                      location.column(),
                                      displayAtom,
                                      nullptr, // asyncCause
                                      nullptr, // parent (not known yet)
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -1990,17 +1990,17 @@ intrinsic_AddContentTelemetry(JSContext*
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 2);
 
     int id = args[0].toInt32();
     MOZ_ASSERT(id < JS_TELEMETRY_END);
     MOZ_ASSERT(id >= 0);
 
-    if (!cx->compartment()->isProbablySystemCode())
+    if (!cx->realm()->isProbablySystemCode())
         cx->runtime()->addTelemetry(id, args[1].toInt32());
 
     args.rval().setUndefined();
     return true;
 }
 
 static bool
 intrinsic_WarnDeprecatedStringMethod(JSContext* cx, unsigned argc, Value* vp)
@@ -2790,36 +2790,38 @@ JSRuntime::createSelfHostingGlobal(JSCon
     JS::RealmOptions options;
     options.creationOptions().setNewZone();
     options.behaviors().setDiscardSource(true);
 
     JSCompartment* compartment = NewCompartment(cx, nullptr, options);
     if (!compartment)
         return nullptr;
 
+    JS::Realm* realm = JS::GetRealmForCompartment(compartment);
+
     static const ClassOps shgClassOps = {
         nullptr, nullptr, nullptr, nullptr,
         nullptr, nullptr, nullptr, nullptr,
         nullptr, nullptr,
         JS_GlobalObjectTraceHook
     };
 
     static const Class shgClass = {
         "self-hosting-global", JSCLASS_GLOBAL_FLAGS,
         &shgClassOps
     };
 
-    AutoRealmUnchecked ar(cx, compartment);
+    AutoRealmUnchecked ar(cx, realm);
     Rooted<GlobalObject*> shg(cx, GlobalObject::createInternal(cx, &shgClass));
     if (!shg)
         return nullptr;
 
     cx->runtime()->selfHostingGlobal_ = shg;
-    compartment->isSelfHosting = true;
-    compartment->setIsSystem(true);
+    realm->setIsSelfHostingRealm();
+    realm->setIsSystem(true);
 
     if (!GlobalObject::initSelfHostingBuiltins(cx, shg, intrinsic_functions))
         return nullptr;
 
     JS_FireOnNewGlobalObject(cx, shg);
 
     return shg;
 }
@@ -2980,22 +2982,16 @@ JSRuntime::finishSelfHosting()
 void
 JSRuntime::traceSelfHostingGlobal(JSTracer* trc)
 {
     if (selfHostingGlobal_ && !parentRuntime)
         TraceRoot(trc, const_cast<NativeObject**>(&selfHostingGlobal_.ref()), "self-hosting global");
 }
 
 bool
-JSRuntime::isSelfHostingCompartment(JSCompartment* comp) const
-{
-    return selfHostingGlobal_->compartment() == comp;
-}
-
-bool
 JSRuntime::isSelfHostingZone(const JS::Zone* zone) const
 {
     return selfHostingGlobal_ && selfHostingGlobal_->zoneFromAnyThread() == zone;
 }
 
 static bool
 CloneValue(JSContext* cx, HandleValue selfHostedValue, MutableHandleValue vp);
 
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -220,17 +220,17 @@ InterpreterStack::purge(JSRuntime* rt)
 {
     rt->gc.freeUnusedLifoBlocksAfterSweeping(&allocator_);
 }
 
 uint8_t*
 InterpreterStack::allocateFrame(JSContext* cx, size_t size)
 {
     size_t maxFrames;
-    if (cx->compartment()->principals() == cx->runtime()->trustedPrincipals())
+    if (cx->realm()->principals() == cx->runtime()->trustedPrincipals())
         maxFrames = MAX_FRAMES_TRUSTED;
     else
         maxFrames = MAX_FRAMES;
 
     if (MOZ_UNLIKELY(frameCount_ >= maxFrames)) {
         ReportOverRecursed(cx);
         return nullptr;
     }
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -668,22 +668,24 @@ FrameIter::settleOnActivation()
     while (true) {
         if (data_.activations_.done()) {
             data_.state_ = DONE;
             return;
         }
 
         Activation* activation = data_.activations_.activation();
 
-        // If the caller supplied principals, only show activations which are subsumed (of the same
-        // origin or of an origin accessible) by these principals.
+        // If the caller supplied principals, only show activations which are
+        // subsumed (of the same origin or of an origin accessible) by these
+        // principals.
         if (data_.principals_) {
             JSContext* cx = data_.cx_;
             if (JSSubsumesOp subsumes = cx->runtime()->securityCallbacks->subsumes) {
-                if (!subsumes(data_.principals_, activation->compartment()->principals())) {
+                JS::Realm* realm = JS::GetRealmForCompartment(activation->compartment());
+                if (!subsumes(data_.principals_, realm->principals())) {
                     ++data_.activations_;
                     continue;
                 }
             }
         }
 
         if (activation->isJit()) {
             data_.jitFrames_ = JitFrameIter(activation->asJit());
@@ -1650,17 +1652,17 @@ jit::JitActivation::lookupRematerialized
 }
 
 void
 jit::JitActivation::removeRematerializedFramesFromDebugger(JSContext* cx, uint8_t* top)
 {
     // Ion bailout can fail due to overrecursion and OOM. In such cases we
     // cannot honor any further Debugger hooks on the frame, and need to
     // ensure that its Debugger.Frame entry is cleaned up.
-    if (!cx->compartment()->isDebuggee() || !rematerializedFrames_)
+    if (!cx->realm()->isDebuggee() || !rematerializedFrames_)
         return;
     if (RematerializedFrameTable::Ptr p = rematerializedFrames_->lookup(top)) {
         for (uint32_t i = 0; i < p->value().length(); i++)
             Debugger::handleUnrecoverableIonBailoutError(cx, p->value()[i]);
     }
 }
 
 void
--- a/js/src/vm/Stopwatch.cpp
+++ b/js/src/vm/Stopwatch.cpp
@@ -189,19 +189,18 @@ PerformanceMonitoring::monotonicReadTime
     return 0;
 #endif // defined(MOZ_HAVE_RDTSC)
 }
 
 void
 PerformanceMonitoring::dispose(JSRuntime* rt)
 {
     reset();
-    for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
-        c->performanceMonitoring.unlink();
-    }
+    for (RealmsIter r(rt, SkipAtoms); !r.done(); r.next())
+        r->performanceMonitoring.unlink();
 }
 
 PerformanceGroupHolder::~PerformanceGroupHolder()
 {
     unlink();
 }
 
 void
@@ -239,17 +238,17 @@ AutoStopwatch::AutoStopwatch(JSContext* 
 
     JSCompartment* compartment = cx_->compartment();
     if (MOZ_UNLIKELY(compartment->scheduledForDestruction))
         return;
 
     JSRuntime* runtime = cx_->runtime();
     iteration_ = runtime->performanceMonitoring().iteration();
 
-    const PerformanceGroupVector* groups = compartment->performanceMonitoring.getGroups(cx);
+    const PerformanceGroupVector* groups = cx_->realm()->performanceMonitoring.getGroups(cx);
     if (!groups) {
       // Either the embedding has not provided any performance
       // monitoring logistics or there was an error that prevents
       // performance monitoring.
       return;
     }
     for (auto group = groups->begin(); group < groups->end(); group++) {
       auto acquired = acquireGroup(*group);
--- a/js/src/vm/SymbolType.cpp
+++ b/js/src/vm/SymbolType.cpp
@@ -44,17 +44,17 @@ Symbol::new_(JSContext* cx, JS::SymbolCo
     }
 
     // Lock to allocate. If symbol allocation becomes a bottleneck, this can
     // probably be replaced with an assertion that we're on the main thread.
     AutoLockForExclusiveAccess lock(cx);
     Symbol* sym;
     {
         AutoAtomsRealm ar(cx, lock);
-        sym = newInternal(cx, code, cx->compartment()->randomHashCode(), atom, lock);
+        sym = newInternal(cx, code, cx->realm()->randomHashCode(), atom, lock);
     }
     if (sym)
         cx->markAtom(sym);
     return sym;
 }
 
 Symbol*
 Symbol::for_(JSContext* cx, HandleString description)
--- a/js/src/vm/UnboxedObject.cpp
+++ b/js/src/vm/UnboxedObject.cpp
@@ -774,17 +774,17 @@ UnboxedObject::createInternal(JSContext*
     JSObject* obj = js::Allocate<JSObject>(cx, kind, /* nDynamicSlots = */ 0, heap, clasp);
     if (!obj)
         return cx->alreadyReportedOOM();
 
     UnboxedObject* uobj = static_cast<UnboxedObject*>(obj);
     uobj->initGroup(group);
 
     MOZ_ASSERT(clasp->shouldDelayMetadataBuilder());
-    cx->compartment()->setObjectPendingMetadata(cx, uobj);
+    cx->realm()->setObjectPendingMetadata(cx, uobj);
 
     js::gc::gcTracer.traceCreateObject(uobj);
 
     return uobj;
 }
 
 /* static */
 UnboxedPlainObject*
--- a/js/src/wasm/WasmCompile.cpp
+++ b/js/src/wasm/WasmCompile.cpp
@@ -98,17 +98,17 @@ CompileArgs::initFromContext(JSContext* 
     sharedMemoryEnabled = cx->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled();
     gcTypesEnabled = gcEnabled ? HasGcTypes::True : HasGcTypes::False;
     testTiering = (cx->options().testWasmAwaitTier2() || JitOptions.wasmDelayTier2) && !gcEnabled;
 
     // Debug information such as source view or debug traps will require
     // additional memory and permanently stay in baseline code, so we try to
     // only enable it when a developer actually cares: when the debugger tab
     // is open.
-    debugEnabled = cx->compartment()->debuggerObservesAsmJS();
+    debugEnabled = cx->realm()->debuggerObservesAsmJS();
 
     this->scriptedCaller = Move(scriptedCaller);
     return assumptions.initBuildIdFromContext(cx);
 }
 
 // Classify the current system as one of a set of recognizable classes.  This
 // really needs to get our tier-1 systems right.
 //
--- a/js/src/wasm/WasmJS.cpp
+++ b/js/src/wasm/WasmJS.cpp
@@ -2191,23 +2191,32 @@ WasmGlobalObject::construct(JSContext* c
     }
 
     RootedValue mutableVal(cx);
     if (!JS_GetProperty(cx, obj, "mutable", &mutableVal))
         return false;
 
     bool isMutable = ToBoolean(mutableVal);
 
-    RootedValue valueVal(cx);
-    if (!JS_GetProperty(cx, obj, "value", &valueVal))
-        return false;
-
-    Val globalVal;
-    if (!ToWebAssemblyValue(cx, globalType, valueVal, &globalVal))
-        return false;
+    // Extract the initial value, or provide a suitable default.
+    // Guard against control flow mistakes below failing to set |globalVal|.
+    Val globalVal = Val(uint32_t(0));
+    if (args.length() >= 2) {
+        RootedValue valueVal(cx, args.get(1));
+
+        if (!ToWebAssemblyValue(cx, globalType, valueVal, &globalVal))
+            return false;
+    } else {
+        switch (globalType) {
+          case ValType::I32: /* set above */ break;
+          case ValType::F32: globalVal = Val(float(0.0));  break;
+          case ValType::F64: globalVal = Val(double(0.0)); break;
+          default: MOZ_CRASH();
+        }
+    }
 
     WasmGlobalObject* global = WasmGlobalObject::create(cx, globalVal, isMutable);
     if (!global)
         return false;
 
     args.rval().setObject(*global);
     return true;
 }
--- a/js/src/wasm/WasmModule.cpp
+++ b/js/src/wasm/WasmModule.cpp
@@ -1276,33 +1276,33 @@ Module::instantiate(JSContext* cx,
 
             code = debugCode;
         }
     }
 
     // To support viewing the source of an instance (Instance::createText), the
     // instance must hold onto a ref of the bytecode (keeping it alive). This
     // wastes memory for most users, so we try to only save the source when a
-    // developer actually cares: when the compartment is debuggable (which is
-    // true when the web console is open), has code compiled with debug flag
+    // developer actually cares: when the realm is debuggable (which is true
+    // when the web console is open), has code compiled with debug flag
     // enabled or a names section is present (since this going to be stripped
     // for non-developer builds).
 
     const ShareableBytes* maybeBytecode = nullptr;
-    if (cx->compartment()->isDebuggee() || metadata().debugEnabled ||
+    if (cx->realm()->isDebuggee() || metadata().debugEnabled ||
         !metadata().funcNames.empty())
     {
         maybeBytecode = bytecode_.get();
     }
 
     // The debug object must be present even when debugging is not enabled: It
     // provides the lazily created source text for the program, even if that
     // text is a placeholder message when debugging is not enabled.
 
-    bool binarySource = cx->compartment()->debuggerObservesBinarySource();
+    bool binarySource = cx->realm()->debuggerObservesBinarySource();
     auto debug = cx->make_unique<DebugState>(code, maybeBytecode, binarySource);
     if (!debug)
         return false;
 
     instance.set(WasmInstanceObject::create(cx,
                                             code,
                                             Move(debug),
                                             Move(tlsData),
--- a/layout/base/tests/test_getBoxQuads_convertPointRectQuad.html
+++ b/layout/base/tests/test_getBoxQuads_convertPointRectQuad.html
@@ -17,17 +17,23 @@ var comment;
 var fragment;
 var openedWindow;
 var zeroPoint;
 var zeroRect;
 var zeroQuad;
 var notInDocument = document.createElement('div');
 
 function isEval(expr, b) {
-  is(eval(expr), b, expr);
+  // we ignore an insignificant discrepancy in floating-point values
+  var exprVal = eval(expr);
+  if (exprVal != b && Math.abs(exprVal - b) < 0.0001) {
+    ok(true, expr + " (" + exprVal + " within 0.0001 of " + b + ")");
+    return;
+  }
+  is(exprVal, b, expr);
 }
 
 function isApprox(a, b, msg, options) {
   if (a != b && 'tolerance' in options &&
       Math.abs(a - b) < options.tolerance) {
     ok(true, msg + "(" + a + " within " + options.tolerance + " of " + b + ")");
     return;
   }
@@ -452,17 +458,17 @@ function runTest() {
   isEval("ibSplit.getBoxQuads()[0].bounds.width", 100);
   isEval("ibSplit.getBoxQuads()[1].bounds.width", 110);
   isEval("ibSplit.getBoxQuads()[2].bounds.width", 130);
   isEval("table.getBoxQuads().length", 2);
   isEval("table.getBoxQuads()[0].bounds.height", 50);
   isEval("table.getBoxQuads()[1].bounds.height", 40);
 
   // Test that we skip anonymous boxes when finding the right box to be relative to.
-  checkQuadIsRect("d", {toStr:"ibSplit"},
+  checkQuadIsRect("d", {toStr:"ibSplit", tolerance:0.0001},
                   dX - ibSplitPart1X, dY - ibSplitY, dW, dH);
   var tableX = table.getClientRects()[0].left;
   var tableY = table.getClientRects()[0].top;
   checkQuadIsRect("d", {toStr:"table"},
                   dX - tableX, dY - tableY, dW, dH);
   isEval("ibSplit.convertPointFromNode(zeroPoint,d).x", dX - ibSplitPart1X);
   isEval("table.convertPointFromNode(zeroPoint,d).x", dX - table.getClientRects()[0].left);
 
--- a/layout/base/tests/transformed_scrolling_repaints_3_window.html
+++ b/layout/base/tests/transformed_scrolling_repaints_3_window.html
@@ -15,44 +15,33 @@ var SimpleTest = window.opener.SimpleTes
 var SpecialPowers = window.opener.SpecialPowers;
 var is = window.opener.is;
 var t, e, utils, iterations;
 var smoothScrollPref = "general.smoothScroll";
 
 function startTest() {
   SpecialPowers.pushPrefEnv({"set":[[smoothScrollPref, false]]}, runTest);
 }
-function runTest() {
-    t = document.getElementById("t");
-    e = t.contentDocument.getElementById("e");
-    t.contentWindow.scrollTo(0,0);
-    utils = SpecialPowers.getDOMWindowUtils(window);
-    iterations = 0;
+
+async function runTest() {
+  let t = document.getElementById("t");
+  let e = t.contentDocument.getElementById("e");
+  t.contentWindow.scrollTo(0,0);
+  let utils = SpecialPowers.getDOMWindowUtils(window);
 
-  // Do a couple of scrolls to ensure we've triggered activity heuristics.
-    waitForAllPaintsFlushed(function () {
-      t.contentWindow.scrollByLines(1);
-      waitForAllPaintsFlushed(function () {
-        t.contentWindow.scrollByLines(1);
-        waitForAllPaintsFlushed(function () {
-          // Clear paint state now and scroll again.
-          utils.checkAndClearPaintedState(e);
-          t.contentWindow.scrollByLines(1);
-          waitForAllPaintsFlushed(nextIteration);
-        });
-      });
-    });
-}
-function nextIteration() {
-  var painted = utils.checkAndClearPaintedState(e);
-  is(painted, false, "Fully-visible scrolled element should not have been painted");
-  if (++iterations == 10) {
-    SimpleTest.finish();
-    window.close();
-  } else {
+  for (let i = 0; i < 15; i++) {
+    let painted = utils.checkAndClearPaintedState(e);
+    // We ignore the first few scrolls, to ensure we have triggered activity
+    // heuristics.
+    if (i >= 5) {
+      is(painted, false,
+         "Fully-visible scrolled element should not have been painted");
+    }
     t.contentWindow.scrollByLines(1);
-    waitForAllPaintsFlushed(nextIteration);
+    await promiseAllPaintsDone(null, true);
   }
+  SimpleTest.finish();
+  window.close();
 }
 </script>
 </pre>
 </body>
 </html>
--- a/layout/reftests/abs-pos/reftest.list
+++ b/layout/reftests/abs-pos/reftest.list
@@ -45,17 +45,17 @@ fuzzy-if(/^Windows\x20NT\x206\.1/.test(h
 == table-footer-group-4.html table-internal-4-ref.html
 == table-footer-group-5.html table-internal-5-ref.html
 == table-footer-group-6.html table-internal-6-ref.html
 == table-footer-group-7.html table-internal-7-ref.html
 == continuation-positioned-inline-1.html continuation-positioned-inline-ref.html
 == continuation-positioned-inline-2.html continuation-positioned-inline-ref.html
 == scrollframe-1.html scrollframe-1-ref.html
 fuzzy-if(gtkWidget,1,1) fuzzy-if(Android,9,185) fuzzy-if(asyncPan&&!layersGPUAccelerated,140,144) == scrollframe-2.html scrollframe-2-ref.html #bug 756530
-fuzzy-if(gtkWidget,1,8) == select-1.html select-1-ref.html
+fuzzy-if(gtkWidget,1,8) fuzzy-if(Android,4,2) == select-1.html select-1-ref.html
 fuzzy-if(gtkWidget,1,8) == select-1-dynamic.html select-1-ref.html
 == select-2.html select-2-ref.html
 fuzzy-if(gtkWidget,1,19) fuzzy-if(Android,17,726) fuzzy-if(asyncPan&&!layersGPUAccelerated,110,114) fuzzy-if(browserIsRemote&&winWidget,143,114) == select-3.html select-3-ref.html
 == multi-column-1.html multi-column-1-ref.html
 == button-1.html button-1-ref.html
 == button-2.html button-2-ref.html
 == relative-row-animation-1.html relative-row-animation-1-ref.html
 fuzzy-if(Android,12,50) == fixed-pos-auto-offset-1a.html fixed-pos-auto-offset-1-ref.html
--- a/layout/reftests/async-scrolling/reftest.list
+++ b/layout/reftests/async-scrolling/reftest.list
@@ -60,17 +60,17 @@ skip-if(Android||!asyncPan) != opaque-fr
 fuzzy-if(Android,6,4) skip-if(!asyncPan) == fixed-pos-scrolled-clip-1.html fixed-pos-scrolled-clip-1-ref.html
 fuzzy-if(Android,6,8) skip-if(!asyncPan) == fixed-pos-scrolled-clip-2.html fixed-pos-scrolled-clip-2-ref.html
 fuzzy-if(Android,6,8) skip-if(!asyncPan) == fixed-pos-scrolled-clip-3.html fixed-pos-scrolled-clip-3-ref.html
 fuzzy-if(Android,6,8) skip-if(!asyncPan) == fixed-pos-scrolled-clip-4.html fixed-pos-scrolled-clip-4-ref.html
 skip-if(!asyncPan) == fixed-pos-scrolled-clip-5.html fixed-pos-scrolled-clip-5-ref.html
 skip-if(!asyncPan) == position-sticky-bug1434250.html position-sticky-bug1434250-ref.html
 fuzzy-if(Android,6,4) skip-if(!asyncPan) == position-sticky-scrolled-clip-1.html position-sticky-scrolled-clip-1-ref.html
 fuzzy-if(Android,6,4) skip == position-sticky-scrolled-clip-2.html position-sticky-scrolled-clip-2-ref.html # bug ?????? - incorrectly applying clip to sticky contents
-skip-if(!asyncPan) == curtain-effect-1.html curtain-effect-1-ref.html
+fuzzy-if(Android,2,4) skip-if(!asyncPan) == curtain-effect-1.html curtain-effect-1-ref.html
 
 # for the following tests, we want to disable the low-precision buffer
 # as it will expand the displayport beyond what the test specifies in
 # its reftest-displayport attributes, and interfere with where we expect
 # checkerboarding to occur
 default-preferences pref(layers.low-precision-buffer,false)
 skip-if(!asyncPan) == checkerboard-1.html checkerboard-1-ref.html
 skip-if(!asyncPan) == checkerboard-2.html checkerboard-2-ref.html
--- a/layout/reftests/bidi/reftest.list
+++ b/layout/reftests/bidi/reftest.list
@@ -9,17 +9,17 @@ include numeral/reftest.list
 == bidi-003.html bidi-003-ref.html
 fuzzy-if(gtkWidget,255,17) == bidi-004.html bidi-004-ref.html # inconsistency in the Hebrew font that gets used
 fuzzy-if(gtkWidget,255,17) == bidi-004-j.html bidi-004-ref.html # inconsistency in the Hebrew font that gets used
 == bidi-005.html bidi-005-ref.html
 random-if(cocoaWidget) == bidi-006.html bidi-006-ref.html # bug 734313
 random-if(cocoaWidget) == bidi-006-j.html bidi-006-ref.html # bug 734313
 == bidiSVG-01.svg bidiSVG-01-ref.svg
 == bidiSVG-02.svg bidiSVG-02-ref.svg
-== bidiSVG-03.svg bidiSVG-03-ref.svg
+fuzzy-if(Android,253,76) == bidiSVG-03.svg bidiSVG-03-ref.svg
 fuzzy-if(skiaContent,1,1) == bidiSVG-04.svg bidiSVG-04-ref.svg
 == bidiSVG-05.svg bidiSVG-05-ref.svg
 == bidiMirroring.svg bidiMirroring-ref.svg
 fuzzy-if(Android,9,134) random-if(layersGPUAccelerated) == visualmarquee.html marquee-ref.html
 fuzzy-if(Android,9,134) random-if(layersGPUAccelerated) == logicalmarquee.html marquee-ref.html
 == visualmarquee.html logicalmarquee.html
 # test for glyph mirroring in right-to-left text
 == mirroring-01.html mirroring-01-ref.html
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -268,17 +268,17 @@ skip-if(Android&&asyncPan) == 243519-1.h
 == 243519-9c.html 243519-9-ref.html
 == 243519-9d.html 243519-9-ref.html
 == 243519-9e.html 243519-9-ref.html
 == 243519-9f.html 243519-9-ref.html
 == 244135-1.html 244135-1-ref.html
 == 244135-2.html 244135-2-ref.html
 == 244932-1.html 244932-1-ref.html
 == 246669-1.html 246669-1-ref.html
-== 249141.xul 249141-ref.xul
+fails-if(Android) == 249141.xul 249141-ref.xul # dependent on width of '…' and '*' in the default font
 == 249982-1.html 249982-1-ref.html
 == 252920-1.html 252920-1-ref.html
 == 253701-1.html 253701-1-ref.html
 fuzzy-if(skiaContent,1,5) == 255820-1.html 255820-1-ref.html
 == 260406-1.html 260406-1-ref.html
 == 261826-1.xul 261826-1-ref.xul
 == 262151-1.html 262151-1-ref.html
 fuzzy-if(skiaContent,1,5) == 262998-1.html 262998-1-ref.html
--- a/layout/reftests/css-ruby/reftest.list
+++ b/layout/reftests/css-ruby/reftest.list
@@ -41,17 +41,17 @@ test-pref(font.minimum-size.ja,16) == mi
 load nested-ruby-1.html
 == no-transform.html no-transform-ref.html
 == relative-positioning-1.html relative-positioning-1-ref.html
 == relative-positioning-2.html relative-positioning-2-ref.html
 == ruby-align-1.html ruby-align-1-ref.html
 == ruby-align-1a.html ruby-align-1-ref.html
 == ruby-align-2.html ruby-align-2-ref.html
 == ruby-align-2a.html ruby-align-2-ref.html
-pref(layout.css.ruby.intercharacter.enabled,true) == ruby-intercharacter-1.htm ruby-intercharacter-1-ref.htm
+pref(layout.css.ruby.intercharacter.enabled,true) fuzzy-if(Android,198,70) == ruby-intercharacter-1.htm ruby-intercharacter-1-ref.htm
 pref(layout.css.ruby.intercharacter.enabled,false) != ruby-intercharacter-1.htm ruby-intercharacter-1-ref.htm
 pref(layout.css.ruby.intercharacter.enabled,true) == ruby-intercharacter-2.htm ruby-intercharacter-2-ref.htm
 pref(layout.css.ruby.intercharacter.enabled,false) != ruby-intercharacter-2.htm ruby-intercharacter-2-ref.htm
 == ruby-position-horizontal.html ruby-position-horizontal-ref.html
 == ruby-position-vertical-lr.html ruby-position-vertical-lr-ref.html
 == ruby-position-vertical-rl.html ruby-position-vertical-rl-ref.html
 != ruby-reflow-1-opaqueruby.html ruby-reflow-1-noruby.html
 fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu),13,1) == ruby-reflow-1-transparentruby.html ruby-reflow-1-noruby.html
--- a/layout/reftests/forms/legend/reftest.list
+++ b/layout/reftests/forms/legend/reftest.list
@@ -1,3 +1,3 @@
 == legend.html legend-ref.html
-== 1273433.html 1273433-ref.html
+fuzzy-if(Android,255,41) == 1273433.html 1273433-ref.html
 fails == 1339287.html 1339287-ref.html # bug 1383868
--- a/layout/reftests/mathml/reftest.list
+++ b/layout/reftests/mathml/reftest.list
@@ -109,17 +109,17 @@ fails-if(webrender&&winWidget) == table-
 != embellished-op-3-3.html embellished-op-3-3-ref.html
 != embellished-op-3-4.html embellished-op-3-4-ref.html
 != embellished-op-3-5.html embellished-op-3-5-ref.html
 == embellished-op-4-1.html embellished-op-4-1-ref.html
 == embellished-op-4-2.html embellished-op-4-2-ref.html
 == embellished-op-4-3.html embellished-op-4-3-ref.html
 == embellished-op-5-1.html embellished-op-5-ref.html
 == embellished-op-5-2.html embellished-op-5-ref.html
-fails-if(gtkWidget) random-if(winWidget) == semantics-1.xhtml semantics-1-ref.xhtml # bug 1309429, bug 1328771
+fails-if(gtkWidget||Android) random-if(winWidget) == semantics-1.xhtml semantics-1-ref.xhtml # bug 1309429, bug 1328771
 == semantics-2.html semantics-2-ref.html
 == semantics-3.html semantics-3-ref.html
 == semantics-4.html semantics-4-ref.html
 != mathcolor-1.xml mathcolor-1-ref.xml
 != mathcolor-2.xml mathcolor-2-ref.xml
 != mathcolor-3.xml mathcolor-3-ref.xml
 == mathcolor-4.xml mathcolor-4-ref.xml
 != mathbackground-1.xml mathbackground-1-ref.xml
--- a/layout/reftests/svg/text/reftest.list
+++ b/layout/reftests/svg/text/reftest.list
@@ -76,19 +76,19 @@ fuzzy-if(webrender&&!gtkWidget,132-134,2
 == multiple-x-percentages-3.html multiple-x-percentages-3-ref.html
 
 == multiple-x-white-space.svg multiple-x-white-space-ref.svg
 
 == multiple-chunks-bidi.svg multiple-chunks-bidi-ref.svg
 == multiple-chunks-different-anchor-bidi.svg multiple-chunks-different-anchor-bidi-ref.svg
 == multiple-chunks-different-anchor-rtl.svg multiple-chunks-different-anchor-rtl-ref.svg
 == multiple-chunks-different-anchor.svg multiple-chunks-different-anchor-ref.svg
-== multiple-chunks-directions-and-anchor.svg multiple-chunks-directions-and-anchor-ref.svg
+fuzzy-if(Android,242,81) == multiple-chunks-directions-and-anchor.svg multiple-chunks-directions-and-anchor-ref.svg
 == multiple-chunks-directions-and-anchor-dx.svg multiple-chunks-directions-and-anchor-dx-ref.svg
-== multiple-chunks-directions-and-anchor-multiple-dx.svg multiple-chunks-directions-and-anchor-multiple-dx-ref.svg
+fuzzy-if(Android,242,81) == multiple-chunks-directions-and-anchor-multiple-dx.svg multiple-chunks-directions-and-anchor-multiple-dx-ref.svg
 == multiple-chunks-dx-bidi.svg multiple-chunks-dx-bidi-ref.svg
 == multiple-chunks-dx-different-anchor-bidi.svg multiple-chunks-dx-different-anchor-bidi-ref.svg
 == multiple-chunks-dx-different-anchor-rtl.svg multiple-chunks-dx-different-anchor-rtl-ref.svg
 == multiple-chunks-dx-different-anchor.svg multiple-chunks-dx-different-anchor-ref.svg
 == multiple-chunks-dx-rtl.svg multiple-chunks-dx-rtl-ref.svg
 == multiple-chunks-dx.svg multiple-chunks-dx-ref.svg
 == multiple-chunks-fill-color.svg multiple-chunks-fill-color-ref.svg
 == multiple-chunks-multiple-dx-bidi.svg multiple-chunks-multiple-dx-bidi-ref.svg
--- a/layout/reftests/w3c-css/failures.list
+++ b/layout/reftests/w3c-css/failures.list
@@ -66,20 +66,16 @@ fails-if(OSX||winWidget||Android) css-wr
 fuzzy-if(winWidget,255,648-713) css-writing-modes/sizing-orthog-vlr-in-htb-008.xht
 fuzzy-if(winWidget,255,648-713) css-writing-modes/sizing-orthog-vlr-in-htb-020.xht
 fuzzy-if(winWidget,255,648-713) css-writing-modes/sizing-orthog-vrl-in-htb-008.xht
 fuzzy-if(winWidget,255,648-713) css-writing-modes/sizing-orthog-vrl-in-htb-020.xht
 fails-if(Android) css-writing-modes/sizing-orthog-htb-in-vlr-003.xht
 fails-if(Android) css-writing-modes/sizing-orthog-htb-in-vlr-009.xht
 fails-if(Android) css-writing-modes/sizing-orthog-htb-in-vlr-015.xht
 fails-if(Android) css-writing-modes/sizing-orthog-htb-in-vlr-021.xht
-fails-if(Android) css-writing-modes/sizing-orthog-htb-in-vrl-003.xht
-fails-if(Android) css-writing-modes/sizing-orthog-htb-in-vrl-009.xht
-fails-if(Android) css-writing-modes/sizing-orthog-htb-in-vrl-015.xht
-fails-if(Android) css-writing-modes/sizing-orthog-htb-in-vrl-021.xht
 fails-if(Android) css-writing-modes/sizing-orthog-vlr-in-htb-003.xht
 fails-if(Android) css-writing-modes/sizing-orthog-vlr-in-htb-009.xht
 fails-if(Android) css-writing-modes/sizing-orthog-vlr-in-htb-015.xht
 fails-if(Android) css-writing-modes/sizing-orthog-vlr-in-htb-021.xht
 fails-if(Android) css-writing-modes/sizing-orthog-vrl-in-htb-003.xht
 fails-if(Android) css-writing-modes/sizing-orthog-vrl-in-htb-009.xht
 fails-if(Android) css-writing-modes/sizing-orthog-vrl-in-htb-015.xht
 fails-if(Android) css-writing-modes/sizing-orthog-vrl-in-htb-021.xht
@@ -201,33 +197,33 @@ fuzzy(87,180) css-multicol/multicol-colu
 fuzzy(87,180) css-multicol/multicol-columns-007.xht
 fuzzy(204,930) fuzzy-if(skiaContent,208,930) fails-if(webrender&&cocoaWidget) css-multicol/multicol-columns-invalid-001.xht
 fails-if(OSX||winWidget) css-multicol/multicol-columns-invalid-002.xht
 fuzzy(204,930) fuzzy-if(skiaContent,208,930) fails-if(webrender&&cocoaWidget) css-multicol/multicol-columns-toolong-001.xht
 fuzzy(135,530) fails-if(webrender&&cocoaWidget) css-multicol/multicol-containing-001.xht
 fuzzy(215,241) css-multicol/multicol-containing-002.xht
 fuzzy(87,180) css-multicol/multicol-count-001.xht
 fails css-multicol/multicol-count-002.xht
-fails-if(winWidget||OSX||Android) css-multicol/multicol-count-computed-003.xht
-fuzzy-if(winWidget||OSX||gtkWidget,112,861) fails-if(Android) fails-if(webrender&&cocoaWidget) css-multicol/multicol-count-computed-004.xht
-fails-if(winWidget||OSX||Android) css-multicol/multicol-count-computed-005.xht
+fails-if(winWidget||OSX) css-multicol/multicol-count-computed-003.xht
+fuzzy-if(winWidget||OSX||gtkWidget,112,861) fails-if(webrender&&cocoaWidget) css-multicol/multicol-count-computed-004.xht
+fails-if(winWidget||OSX) css-multicol/multicol-count-computed-005.xht
 fuzzy(204,930) fuzzy-if(skiaContent,208,930) fails-if(webrender&&cocoaWidget) css-multicol/multicol-count-negative-001.xht
 fuzzy(204,930) fuzzy-if(skiaContent,208,930) fails-if(webrender&&cocoaWidget) css-multicol/multicol-count-negative-002.xht
 fuzzy(204,930) fuzzy-if(skiaContent,208,930) fails-if(webrender&&cocoaWidget) css-multicol/multicol-count-non-integer-001.xht
 fuzzy(204,930) fuzzy-if(skiaContent,208,930) fails-if(webrender&&cocoaWidget) css-multicol/multicol-count-non-integer-002.xht
 fuzzy(204,930) fuzzy-if(skiaContent,208,930) fails-if(webrender&&cocoaWidget) css-multicol/multicol-count-non-integer-003.xht
 fuzzy(135,80) css-multicol/multicol-fill-auto-002.xht
 fuzzy(135,3270) css-multicol/multicol-fill-auto-003.xht
 fuzzy(135,80) css-multicol/multicol-fill-balance-001.xht
 fuzzy(135,821) css-multicol/multicol-gap-000.xht
 fuzzy(255,329) css-multicol/multicol-gap-001.xht
 fuzzy(204,930) fuzzy-if(skiaContent,208,930) fails-if(webrender&&cocoaWidget) css-multicol/multicol-gap-002.xht
 fuzzy(204,930) fuzzy-if(skiaContent,208,930) fails-if(webrender&&cocoaWidget) css-multicol/multicol-gap-003.xht
 fuzzy(107,1823) fails-if(webrender&&cocoaWidget) css-multicol/multicol-gap-fraction-001.xht
-fuzzy-if(winWidget||OSX||gtkWidget,204,1048) fuzzy-if(skiaContent,208,1048) fails-if(Android) css-multicol/multicol-gap-large-001.xht
+fuzzy-if(winWidget||OSX||gtkWidget,204,1048) fuzzy-if(skiaContent,208,1048) css-multicol/multicol-gap-large-001.xht
 fuzzy(225,920) fails-if(webrender&&cocoaWidget) css-multicol/multicol-gap-large-002.xht
 fuzzy(204,930) fuzzy-if(skiaContent,208,930) fails-if(webrender&&cocoaWidget) css-multicol/multicol-gap-negative-001.xht
 fails css-multicol/multicol-height-block-child-001.xht
 fuzzy(255,3762) css-multicol/multicol-inherit-001.xht
 fuzzy(135,1893) fails-if(webrender&&cocoaWidget) css-multicol/multicol-inherit-002.xht
 fails css-multicol/multicol-inherit-003.xht
 fuzzy(97,264) css-multicol/multicol-list-item-001.xht
 fuzzy(73,1200) css-multicol/multicol-margin-001.xht
--- a/layout/reftests/w3c-css/received/reftest.list
+++ b/layout/reftests/w3c-css/received/reftest.list
@@ -66,19 +66,19 @@ fuzzy(87,180) == css-multicol/multicol-c
 fuzzy(87,180) == css-multicol/multicol-columns-007.xht css-multicol/multicol-columns-001-ref.xht
 fuzzy(204,930) fuzzy-if(skiaContent,208,930) fails-if(webrender&&cocoaWidget) == css-multicol/multicol-columns-invalid-001.xht css-multicol/multicol-columns-invalid-001-ref.xht
 fails-if(OSX||winWidget) == css-multicol/multicol-columns-invalid-002.xht css-multicol/multicol-columns-invalid-001-ref.xht
 fuzzy(204,930) fuzzy-if(skiaContent,208,930) fails-if(webrender&&cocoaWidget) == css-multicol/multicol-columns-toolong-001.xht css-multicol/multicol-columns-invalid-001-ref.xht
 fuzzy(135,530) fails-if(webrender&&cocoaWidget) == css-multicol/multicol-containing-001.xht css-multicol/multicol-containing-001-ref.xht
 fuzzy(215,241) == css-multicol/multicol-containing-002.xht css-multicol/multicol-containing-002-ref.xht
 fuzzy(87,180) == css-multicol/multicol-count-001.xht css-multicol/multicol-columns-001-ref.xht
 fails == css-multicol/multicol-count-002.xht css-multicol/multicol-count-002-ref.xht
-fails-if(winWidget||OSX||Android) == css-multicol/multicol-count-computed-003.xht css-multicol/multicol-count-computed-003-ref.xht
-fuzzy-if(winWidget||OSX||gtkWidget,112,861) fails-if(Android) fails-if(webrender&&cocoaWidget) == css-multicol/multicol-count-computed-004.xht css-multicol/multicol-count-computed-004-ref.xht
-fails-if(winWidget||OSX||Android) == css-multicol/multicol-count-computed-005.xht css-multicol/multicol-count-computed-003-ref.xht
+fails-if(winWidget||OSX) == css-multicol/multicol-count-computed-003.xht css-multicol/multicol-count-computed-003-ref.xht
+fuzzy-if(winWidget||OSX||gtkWidget,112,861) fails-if(webrender&&cocoaWidget) == css-multicol/multicol-count-computed-004.xht css-multicol/multicol-count-computed-004-ref.xht
+fails-if(winWidget||OSX) == css-multicol/multicol-count-computed-005.xht css-multicol/multicol-count-computed-003-ref.xht
 fuzzy(204,930) fuzzy-if(skiaContent,208,930) fails-if(webrender&&cocoaWidget) == css-multicol/multicol-count-negative-001.xht css-multicol/multicol-columns-invalid-001-ref.xht
 fuzzy(204,930) fuzzy-if(skiaContent,208,930) fails-if(webrender&&cocoaWidget) == css-multicol/multicol-count-negative-002.xht css-multicol/multicol-columns-invalid-001-ref.xht
 fuzzy(204,930) fuzzy-if(skiaContent,208,930) fails-if(webrender&&cocoaWidget) == css-multicol/multicol-count-non-integer-001.xht css-multicol/multicol-columns-invalid-001-ref.xht
 fuzzy(204,930) fuzzy-if(skiaContent,208,930) fails-if(webrender&&cocoaWidget) == css-multicol/multicol-count-non-integer-002.xht css-multicol/multicol-columns-invalid-001-ref.xht
 fuzzy(204,930) fuzzy-if(skiaContent,208,930) fails-if(webrender&&cocoaWidget) == css-multicol/multicol-count-non-integer-003.xht css-multicol/multicol-columns-invalid-001-ref.xht
 == css-multicol/multicol-fill-000.xht css-multicol/multicol-fill-000-ref.xht
 == css-multicol/multicol-fill-001.xht css-multicol/multicol-fill-001-ref.xht
 == css-multicol/multicol-fill-auto-001.xht css-multicol/multicol-fill-auto-001-ref.xht
@@ -89,17 +89,17 @@ fails == css-multicol/multicol-fill-auto
 fuzzy(135,80) == css-multicol/multicol-fill-balance-001.xht css-multicol/multicol-fill-balance-001-ref.xht
 skip == css-multicol/multicol-fill-balance-002.html reference/ref-filled-green-100px-square-only.html
 fuzzy(135,821) == css-multicol/multicol-gap-000.xht css-multicol/multicol-gap-000-ref.xht
 fuzzy(255,329) == css-multicol/multicol-gap-001.xht css-multicol/multicol-gap-001-ref.xht
 fuzzy(204,930) fuzzy-if(skiaContent,208,930) fails-if(webrender&&cocoaWidget) == css-multicol/multicol-gap-002.xht css-multicol/multicol-gap-002-ref.xht
 fuzzy(204,930) fuzzy-if(skiaContent,208,930) fails-if(webrender&&cocoaWidget) == css-multicol/multicol-gap-003.xht css-multicol/multicol-gap-002-ref.xht
 fuzzy(107,1823) fails-if(webrender&&cocoaWidget) == css-multicol/multicol-gap-fraction-001.xht css-multicol/multicol-gap-fraction-001-ref.xht
 skip == css-multicol/multicol-gap-fraction-002.html reference/nothing.html
-fuzzy-if(winWidget||OSX||gtkWidget,204,1048) fuzzy-if(skiaContent,208,1048) fails-if(Android) == css-multicol/multicol-gap-large-001.xht css-multicol/multicol-gap-large-001-ref.xht
+fuzzy-if(winWidget||OSX||gtkWidget,204,1048) fuzzy-if(skiaContent,208,1048) == css-multicol/multicol-gap-large-001.xht css-multicol/multicol-gap-large-001-ref.xht
 fuzzy(225,920) fails-if(webrender&&cocoaWidget) == css-multicol/multicol-gap-large-002.xht css-multicol/multicol-gap-large-002-ref.xht
 fuzzy(204,930) fuzzy-if(skiaContent,208,930) fails-if(webrender&&cocoaWidget) == css-multicol/multicol-gap-negative-001.xht css-multicol/multicol-gap-002-ref.xht
 == css-multicol/multicol-height-001.xht css-multicol/multicol-height-001-ref.xht
 fails == css-multicol/multicol-height-block-child-001.xht css-multicol/multicol-height-block-child-001-ref.xht
 fuzzy(255,3762) == css-multicol/multicol-inherit-001.xht css-multicol/multicol-inherit-001-ref.xht
 fuzzy(135,1893) fails-if(webrender&&cocoaWidget) == css-multicol/multicol-inherit-002.xht css-multicol/multicol-inherit-002-ref.xht
 fails == css-multicol/multicol-inherit-003.xht css-multicol/multicol-inherit-3-ref.xht
 fuzzy(97,264) == css-multicol/multicol-list-item-001.xht css-multicol/multicol-list-item-001-ref.xht
@@ -932,32 +932,32 @@ fails-if(Android) == css-writing-modes/s
 == css-writing-modes/sizing-orthog-htb-in-vlr-018.xht css-writing-modes/sizing-orthog-htb-in-vlr-018-ref.xht
 == css-writing-modes/sizing-orthog-htb-in-vlr-019.xht css-writing-modes/sizing-orthog-htb-in-vlr-019-ref.xht
 fails-if(OSX||winWidget||Android) == css-writing-modes/sizing-orthog-htb-in-vlr-020.xht css-writing-modes/sizing-orthog-htb-in-vlr-020-ref.xht
 fails-if(Android) == css-writing-modes/sizing-orthog-htb-in-vlr-021.xht css-writing-modes/sizing-orthog-htb-in-vlr-015-ref.xht
 == css-writing-modes/sizing-orthog-htb-in-vlr-022.xht css-writing-modes/sizing-orthog-htb-in-vlr-022-ref.xht
 == css-writing-modes/sizing-orthog-htb-in-vlr-023.xht css-writing-modes/sizing-orthog-htb-in-vlr-023-ref.xht
 == css-writing-modes/sizing-orthog-htb-in-vlr-024.xht css-writing-modes/sizing-orthog-htb-in-vlr-018-ref.xht
 fails == css-writing-modes/sizing-orthog-htb-in-vrl-001.xht css-writing-modes/sizing-orthog-htb-in-vrl-001-ref.xht
-fails-if(Android) == css-writing-modes/sizing-orthog-htb-in-vrl-003.xht css-writing-modes/sizing-orthog-htb-in-vrl-003-ref.xht
+== css-writing-modes/sizing-orthog-htb-in-vrl-003.xht css-writing-modes/sizing-orthog-htb-in-vrl-003-ref.xht
 fails == css-writing-modes/sizing-orthog-htb-in-vrl-004.xht css-writing-modes/sizing-orthog-htb-in-vlr-004-ref.xht
 == css-writing-modes/sizing-orthog-htb-in-vrl-006.xht css-writing-modes/sizing-orthog-htb-in-vrl-006-ref.xht
 == css-writing-modes/sizing-orthog-htb-in-vrl-007.xht css-writing-modes/sizing-orthog-htb-in-vrl-007-ref.xht
 fails-if(OSX||winWidget||Android) == css-writing-modes/sizing-orthog-htb-in-vrl-008.xht css-writing-modes/sizing-orthog-htb-in-vrl-008-ref.xht
-fails-if(Android) == css-writing-modes/sizing-orthog-htb-in-vrl-009.xht css-writing-modes/sizing-orthog-htb-in-vrl-003-ref.xht
+== css-writing-modes/sizing-orthog-htb-in-vrl-009.xht css-writing-modes/sizing-orthog-htb-in-vrl-003-ref.xht
 == css-writing-modes/sizing-orthog-htb-in-vrl-010.xht css-writing-modes/sizing-orthog-htb-in-vrl-010-ref.xht
 == css-writing-modes/sizing-orthog-htb-in-vrl-011.xht css-writing-modes/sizing-orthog-htb-in-vrl-011-ref.xht
 == css-writing-modes/sizing-orthog-htb-in-vrl-012.xht css-writing-modes/sizing-orthog-htb-in-vrl-006-ref.xht
 fails == css-writing-modes/sizing-orthog-htb-in-vrl-013.xht css-writing-modes/sizing-orthog-htb-in-vrl-013-ref.xht
-fails-if(Android) == css-writing-modes/sizing-orthog-htb-in-vrl-015.xht css-writing-modes/sizing-orthog-htb-in-vrl-015-ref.xht
+== css-writing-modes/sizing-orthog-htb-in-vrl-015.xht css-writing-modes/sizing-orthog-htb-in-vrl-015-ref.xht
 == css-writing-modes/sizing-orthog-htb-in-vrl-016.xht css-writing-modes/sizing-orthog-htb-in-vlr-016-ref.xht
 == css-writing-modes/sizing-orthog-htb-in-vrl-018.xht css-writing-modes/sizing-orthog-htb-in-vrl-018-ref.xht
 == css-writing-modes/sizing-orthog-htb-in-vrl-019.xht css-writing-modes/sizing-orthog-htb-in-vrl-019-ref.xht
 fails-if(OSX||winWidget||Android) == css-writing-modes/sizing-orthog-htb-in-vrl-020.xht css-writing-modes/sizing-orthog-htb-in-vrl-020-ref.xht
-fails-if(Android) == css-writing-modes/sizing-orthog-htb-in-vrl-021.xht css-writing-modes/sizing-orthog-htb-in-vrl-015-ref.xht
+== css-writing-modes/sizing-orthog-htb-in-vrl-021.xht css-writing-modes/sizing-orthog-htb-in-vrl-015-ref.xht
 == css-writing-modes/sizing-orthog-htb-in-vrl-022.xht css-writing-modes/sizing-orthog-htb-in-vrl-022-ref.xht
 == css-writing-modes/sizing-orthog-htb-in-vrl-023.xht css-writing-modes/sizing-orthog-htb-in-vrl-023-ref.xht
 == css-writing-modes/sizing-orthog-htb-in-vrl-024.xht css-writing-modes/sizing-orthog-htb-in-vrl-018-ref.xht
 == css-writing-modes/sizing-orthog-prct-htb-in-vlr-001.xht css-writing-modes/sizing-orthog-prct-htb-in-vlr-001-ref.xht
 == css-writing-modes/sizing-orthog-prct-htb-in-vlr-002.xht css-writing-modes/sizing-orthog-prct-htb-in-vlr-002-ref.xht
 == css-writing-modes/sizing-orthog-prct-htb-in-vlr-003.xht css-writing-modes/sizing-orthog-prct-htb-in-vlr-003-ref.xht
 == css-writing-modes/sizing-orthog-prct-htb-in-vlr-004.xht css-writing-modes/sizing-orthog-prct-htb-in-vlr-004-ref.xht
 == css-writing-modes/sizing-orthog-prct-htb-in-vlr-005.xht css-writing-modes/sizing-orthog-prct-htb-in-vlr-005-ref.xht
--- a/layout/reftests/w3c-css/submitted/flexbox/flexbox-anonymous-items-001-ref.html
+++ b/layout/reftests/w3c-css/submitted/flexbox/flexbox-anonymous-items-001-ref.html
@@ -5,11 +5,11 @@
 -->
 <html>
 <head>
   <title>CSS Reftest Reference</title>
   <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
   <meta charset="utf-8">
 </head>
 <body>
-  a ab bx x
+  a a<div style="display:inline-block">b b</div>x x
 </body>
 </html>
--- a/layout/reftests/w3c-css/submitted/flexbox/reftest.list
+++ b/layout/reftests/w3c-css/submitted/flexbox/reftest.list
@@ -1,15 +1,15 @@
 # Tests for absolutely-positioned children of a flex container
 == flexbox-abspos-child-001a.html flexbox-abspos-child-001-ref.html
 == flexbox-abspos-child-001b.html flexbox-abspos-child-001-ref.html
 == flexbox-abspos-child-002.html flexbox-abspos-child-002-ref.html
 
 # Tests for handling anonymous flex items
-== flexbox-anonymous-items-001.html flexbox-anonymous-items-001-ref.html
+fuzzy-if(Android,225,116) == flexbox-anonymous-items-001.html flexbox-anonymous-items-001-ref.html
 
 # Tests for alignment of flex lines (align-content property)
 == flexbox-align-content-horiz-001a.xhtml flexbox-align-content-horiz-001-ref.xhtml
 == flexbox-align-content-horiz-001b.xhtml flexbox-align-content-horiz-001-ref.xhtml
 == flexbox-align-content-vert-001a.xhtml  flexbox-align-content-vert-001-ref.xhtml
 == flexbox-align-content-vert-001b.xhtml  flexbox-align-content-vert-001-ref.xhtml
 
 # Tests for cross-axis alignment (align-self / align-items properties)
--- a/layout/reftests/writing-mode/reftest.list
+++ b/layout/reftests/writing-mode/reftest.list
@@ -144,17 +144,17 @@ test-pref(dom.meta-viewport.enabled,true
 skip-if(winWidget&&/^Windows\x20NT\x206\.1/.test(http.oscpu)) == 1175789-underline-overline-1.html 1175789-underline-overline-1-ref.html # bug 1442637
 == 1188061-1-nsChangeHint_ClearAncestorIntrinsics.html 1188061-1-nsChangeHint_ClearAncestorIntrinsics-ref.html
 == 1188061-2-nsChangeHint_UpdateComputedBSize.html 1188061-2-nsChangeHint_UpdateComputedBSize-ref.html
 
 # tests involving sideways-lr mode
 == 1193519-sideways-lr-1.html 1193519-sideways-lr-1-ref.html
 == 1193519-sideways-lr-2.html 1193519-sideways-lr-2-ref.html
 fuzzy-if(winWidget,3,84) fails-if(webrender&&!gtkWidget) == 1193519-sideways-lr-3.html 1193519-sideways-lr-3-ref.html
-fuzzy-if(winWidget,3,112) fails-if(webrender) == 1193519-sideways-lr-4.html 1193519-sideways-lr-4-ref.html # see bug 1366692. Rounding error with WR enabled.
+fuzzy-if(winWidget,3,112) fails-if(webrender||Android) == 1193519-sideways-lr-4.html 1193519-sideways-lr-4-ref.html # see bug 1366692. Rounding error with WR enabled, and on Android.
 fuzzy-if(gtkWidget,255,6) fuzzy-if(cocoaWidget,65,69) fuzzy-if(webrender&&winWidget,255-255,608-608) fails-if(webrender&&cocoaWidget) == 1193519-sideways-lr-decoration-1.html 1193519-sideways-lr-decoration-1-ref.html
 
 == 1196887-1-computed-display-inline-block.html 1196887-1-computed-display-inline-block-ref.html
 == 1205787-legacy-svg-values-1.html 1205787-legacy-svg-values-1-ref.html
 
 == 1216747-1.html 1216747-1-ref.html
 != 1216747-1.html 1216747-1-notref.html
 
--- a/layout/reftests/writing-mode/tables/reftest.list
+++ b/layout/reftests/writing-mode/tables/reftest.list
@@ -19,18 +19,18 @@ fuzzy-if(webrender&&cocoaWidget,18-18,3-
 fuzzy-if(webrender&&cocoaWidget,18-18,3-3) == fixed-table-layout-007-vlr.html fixed-table-layout-007-ref.html
 == fixed-table-layout-009-vlr.html fixed-table-layout-009-ref.html
 fuzzy-if(Android,255,400) == fixed-table-layout-010-vlr.html fixed-table-layout-010-ref.html
 fuzzy-if(webrender&&cocoaWidget,18-18,3-3) == fixed-table-layout-012-vlr.html fixed-table-layout-012-ref.html
 == fixed-table-layout-013-vlr.html fixed-table-layout-002-ref.html
 == fixed-table-layout-014-vlr.html fixed-table-layout-002-ref.html
 == fixed-table-layout-015-vlr.html fixed-table-layout-002-ref.html
 == fixed-table-layout-016-vlr.html fixed-table-layout-002-ref.html
-fuzzy-if(skiaContent,1,50) fuzzy-if(webrender&&cocoaWidget,23-23,6-6) == fixed-table-layout-017-vlr.html fixed-table-layout-017-ref.html
-fuzzy-if(skiaContent,1,50) fuzzy-if(webrender&&cocoaWidget,23-23,6-6) == fixed-table-layout-018-vlr.html fixed-table-layout-017-ref.html
+fuzzy-if(skiaContent,1,50) fuzzy-if(Android,1,63) fuzzy-if(webrender&&cocoaWidget,23-23,6-6) == fixed-table-layout-017-vlr.html fixed-table-layout-017-ref.html
+fuzzy-if(skiaContent,1,50) fuzzy-if(Android,1,63) fuzzy-if(webrender&&cocoaWidget,23-23,6-6) == fixed-table-layout-018-vlr.html fixed-table-layout-017-ref.html
 fuzzy-if(skiaContent,1,90) fuzzy-if(webrender&&cocoaWidget,28-28,9-9) == fixed-table-layout-021-vlr.html fixed-table-layout-021-ref.html
 fuzzy-if(skiaContent,1,90) fuzzy-if(webrender&&cocoaWidget,28-28,12-12) == fixed-table-layout-022-vlr.html fixed-table-layout-022-ref.html
 fuzzy-if(skiaContent,1,90) fuzzy-if(webrender&&cocoaWidget,28-28,12-12) == fixed-table-layout-023-vlr.html fixed-table-layout-023-ref.html
 == fixed-table-layout-025-vlr.html fixed-table-layout-025-ref.html
 == fixed-table-layout-026-vlr.html fixed-table-layout-025-ref.html
 == fixed-table-layout-027-vlr.html fixed-table-layout-025-ref.html
 == fixed-table-layout-028-vlr.html fixed-table-layout-025-ref.html
 == fixed-table-layout-029-vlr.html fixed-table-layout-025-ref.html
@@ -45,18 +45,18 @@ fuzzy-if(webrender&&cocoaWidget,18-18,3-
 fuzzy-if(webrender&&cocoaWidget,18-18,3-3) == fixed-table-layout-007-vrl.html fixed-table-layout-007-ref.html
 == fixed-table-layout-009-vrl.html fixed-table-layout-009-ref.html
 fuzzy-if(Android,255,400) == fixed-table-layout-010-vrl.html fixed-table-layout-010-ref.html
 fuzzy-if(webrender&&cocoaWidget,18-18,3-3) == fixed-table-layout-012-vrl.html fixed-table-layout-012-ref.html
 == fixed-table-layout-013-vrl.html fixed-table-layout-002-ref.html
 == fixed-table-layout-014-vrl.html fixed-table-layout-002-ref.html
 == fixed-table-layout-015-vrl.html fixed-table-layout-002-ref.html
 == fixed-table-layout-016-vrl.html fixed-table-layout-002-ref.html
-fuzzy-if(skiaContent,1,50) fuzzy-if(webrender&&cocoaWidget,23-23,6-6) == fixed-table-layout-017-vrl.html fixed-table-layout-017-ref.html
-fuzzy-if(skiaContent,1,50) fuzzy-if(webrender&&cocoaWidget,23-23,6-6) == fixed-table-layout-018-vrl.html fixed-table-layout-017-ref.html
+fuzzy-if(skiaContent,1,50) fuzzy-if(Android,1,63) fuzzy-if(webrender&&cocoaWidget,23-23,6-6) == fixed-table-layout-017-vrl.html fixed-table-layout-017-ref.html
+fuzzy-if(skiaContent,1,50) fuzzy-if(Android,1,63) fuzzy-if(webrender&&cocoaWidget,23-23,6-6) == fixed-table-layout-018-vrl.html fixed-table-layout-017-ref.html
 fuzzy-if(skiaContent,1,90) fuzzy-if(webrender&&cocoaWidget,28-28,9-9) == fixed-table-layout-021-vrl.html fixed-table-layout-021-ref.html
 fuzzy-if(skiaContent,1,90) fuzzy-if(webrender&&cocoaWidget,28-28,12-12) == fixed-table-layout-022-vrl.html fixed-table-layout-022-ref.html
 fuzzy-if(skiaContent,1,90) fuzzy-if(webrender&&cocoaWidget,28-28,12-12) == fixed-table-layout-023-vrl.html fixed-table-layout-023-ref.html
 == fixed-table-layout-025-vrl.html fixed-table-layout-025-ref.html
 == fixed-table-layout-026-vrl.html fixed-table-layout-025-ref.html
 == fixed-table-layout-027-vrl.html fixed-table-layout-025-ref.html
 == fixed-table-layout-028-vrl.html fixed-table-layout-025-ref.html
 == fixed-table-layout-029-vrl.html fixed-table-layout-025-ref.html
deleted file mode 100644
index b06f84dccdb1255b29b4c05221e5f467bfd96a65..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index debf5560711d2e93d17a9a85a4696231ef01bf96..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index c47df80eeb3ade63adeed4d17b0ef7d6e2350e91..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index c87978a1e90563315a1a268537a25ddc1b94bb6b..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 40dfd826813472e92ca45826ab8a2c9a29456751..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 952bcb5751709fee10a8a1c0b1088c8e66250cda..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index fe686f8d2a1343973005977cf21b00b8a388d56a..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 6f25224f5df7414be0a66433493252c9a0f31ddf..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/mobile/android/fonts/moz.build
+++ b/mobile/android/fonts/moz.build
@@ -8,17 +8,9 @@ with Files('**'):
     BUG_COMPONENT = ('Firefox for Android', 'General')
 
 if not CONFIG['MOZ_ANDROID_EXCLUDE_FONTS']:
     RESOURCE_FILES.fonts += [
         'CharisSILCompact-B.ttf',
         'CharisSILCompact-BI.ttf',
         'CharisSILCompact-I.ttf',
         'CharisSILCompact-R.ttf',
-        'ClearSans-Bold.ttf',
-        'ClearSans-BoldItalic.ttf',
-        'ClearSans-Italic.ttf',
-        'ClearSans-Light.ttf',
-        'ClearSans-Medium.ttf',
-        'ClearSans-MediumItalic.ttf',
-        'ClearSans-Regular.ttf',
-        'ClearSans-Thin.ttf',
     ]
--- a/mobile/android/themes/core/about.css
+++ b/mobile/android/themes/core/about.css
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 html {
   background: #f0f0f0;
   padding: 0 1em;
-  font-family: "Clear Sans", sans-serif !important;
+  font-family: sans-serif !important;
   font-size: 100% !important;
 }
 
 body {
   color: black;
   position: relative;
   min-width: 330px;
   max-width: 50em;
--- a/mobile/android/themes/core/aboutBase.css
+++ b/mobile/android/themes/core/aboutBase.css
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 @import "defines.css";
 
 html {
-  font-family: "Clear Sans",sans-serif;
+  font-family: sans-serif;
   font-size: 14px;
   background-color: var(--color_about_background);
   -moz-text-size-adjust: none;
   -moz-user-select: none;
   --icon-size: 1.8em;
   --icon-margin: 1.35em;
 }
 
--- a/mobile/android/themes/core/aboutPage.css
+++ b/mobile/android/themes/core/aboutPage.css
@@ -1,15 +1,15 @@
 /* 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/. */
 
 body {
   -moz-text-size-adjust: none;
-  font-family: "Clear Sans",sans-serif;
+  font-family: sans-serif;
   font-size: 23px;
   color: #222222;
   background-color: #ced7de;
 }
 
 #header {
   height: 80px;
 }
--- a/mobile/android/themes/core/aboutPrivateBrowsing.css
+++ b/mobile/android/themes/core/aboutPrivateBrowsing.css
@@ -1,14 +1,14 @@
 /* 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/. */
 
 body {
-  font-family: "Clear Sans",sans-serif;
+  font-family: sans-serif;
   font-size: 16px;
   text-align: center;
   padding: 0 30px 0;
 }
 
 body.normal  .showPrivate,
 body.private .showNormal {
   display: none;
--- a/mobile/android/themes/core/config.css
+++ b/mobile/android/themes/core/config.css
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 html,
 body {
     margin: 0;
     padding: 0;
     background-color: #ced7de;
     -moz-user-select: none;
-    font-family: "Clear Sans",sans-serif;
+    font-family: sans-serif;
     -moz-text-size-adjust: none;
 }
 
 .toolbar {
     width: 100%;
     min-height: 3em;
     display: flow-root;
     position: sticky;
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -4384,70 +4384,70 @@ pref("font.size.fixed.x-unicode", 12);
 
 pref("font.default.x-western", "sans-serif");
 pref("font.size.fixed.x-western", 12);
 
 # ANDROID
 #endif
 
 #if defined(ANDROID)
-// We use the bundled fonts for Firefox for Android
+// We use the bundled Charis SIL Compact as serif font for Firefox for Android
 
 pref("font.name-list.emoji", "Noto Color Emoji");
 
 pref("font.name-list.serif.ar", "Noto Naskh Arabic, Noto Serif, Droid Serif");
-pref("font.name-list.sans-serif.ar", "Noto Naskh Arabic, Clear Sans, Roboto, Droid Sans");
+pref("font.name-list.sans-serif.ar", "Noto Naskh Arabic, Roboto, Droid Sans");
 pref("font.name-list.monospace.ar", "Noto Naskh Arabic");
 
 pref("font.name-list.serif.el", "Droid Serif, Noto Serif"); // not Charis SIL Compact, only has a few Greek chars
-pref("font.name-list.sans-serif.el", "Clear Sans, Roboto, Droid Sans");
+pref("font.name-list.sans-serif.el", "Roboto, Droid Sans");
 pref("font.name-list.monospace.el", "Droid Sans Mono");
 
 pref("font.name-list.serif.he", "Droid Serif, Noto Serif");
-pref("font.name-list.sans-serif.he", "Clear Sans, Droid Sans Hebrew, Droid Sans, Arial");
+pref("font.name-list.sans-serif.he", "Roboto, Droid Sans Hebrew, Droid Sans, Arial");
 pref("font.name-list.monospace.he", "Droid Sans Mono");
 
 pref("font.name-list.serif.ja", "Charis SIL Compact, Noto Serif, Droid Serif");
-pref("font.name-list.sans-serif.ja", "Clear Sans, Roboto, Droid Sans, MotoyaLMaru, MotoyaLCedar, Noto Sans JP, Noto Sans CJK JP, Droid Sans Japanese");
+pref("font.name-list.sans-serif.ja", "Roboto, Droid Sans, MotoyaLMaru, MotoyaLCedar, Noto Sans JP, Noto Sans CJK JP, Droid Sans Japanese");
 pref("font.name-list.monospace.ja", "MotoyaLMaru, MotoyaLCedar, Droid Sans Mono CJK JP, Droid Sans Mono");
 
 pref("font.name-list.serif.ko", "Charis SIL Compact, Noto Serif, Droid Serif, HYSerif");
-pref("font.name-list.sans-serif.ko", "Clear Sans, SmartGothic, NanumGothic, Noto Sans KR, Noto Sans CJK KR, DroidSansFallback, Droid Sans Fallback");
+pref("font.name-list.sans-serif.ko", "Roboto, SmartGothic, NanumGothic, Noto Sans KR, Noto Sans CJK KR, DroidSansFallback, Droid Sans Fallback");
 pref("font.name-list.monospace.ko", "Droid Sans Mono, Noto Sans Mono CJK KR");
 
 pref("font.name-list.serif.th", "Charis SIL Compact, Noto Serif, Droid Serif");
-pref("font.name-list.sans-serif.th", "Clear Sans, Droid Sans Thai, Droid Sans");
+pref("font.name-list.sans-serif.th", "Roboto, Droid Sans Thai, Droid Sans");
 pref("font.name-list.monospace.th", "Droid Sans Mono");
 
 pref("font.name-list.serif.x-cyrillic", "Charis SIL Compact, Noto Serif, Droid Serif");
-pref("font.name-list.sans-serif.x-cyrillic", "Clear Sans, Roboto, Droid Sans");
+pref("font.name-list.sans-serif.x-cyrillic", "Roboto, Droid Sans");
 pref("font.name-list.monospace.x-cyrillic", "Droid Sans Mono");
 
 pref("font.name-list.serif.x-unicode", "Charis SIL Compact, Noto Serif, Droid Serif");
-pref("font.name-list.sans-serif.x-unicode", "Clear Sans, Roboto, Droid Sans");
+pref("font.name-list.sans-serif.x-unicode", "Roboto, Droid Sans");
 pref("font.name-list.monospace.x-unicode", "Droid Sans Mono");
 
 pref("font.name-list.serif.x-western", "Charis SIL Compact, Noto Serif, Droid Serif");
-pref("font.name-list.sans-serif.x-western", "Clear Sans, Roboto, Droid Sans");
+pref("font.name-list.sans-serif.x-western", "Roboto, Droid Sans");
 pref("font.name-list.monospace.x-western", "Droid Sans Mono");
 
 pref("font.name-list.serif.zh-CN", "Charis SIL Compact, Noto Serif, Droid Serif, Droid Sans Fallback");
-pref("font.name-list.sans-serif.zh-CN", "Clear Sans, Roboto, Droid Sans, Noto Sans SC, Noto Sans CJK SC, Droid Sans Fallback");
+pref("font.name-list.sans-serif.zh-CN", "Roboto, Droid Sans, Noto Sans SC, Noto Sans CJK SC, Droid Sans Fallback");
 pref("font.name-list.monospace.zh-CN", "Droid Sans Mono, Noto Sans Mono CJK SC, Droid Sans Fallback");
 
 pref("font.name-list.serif.zh-HK", "Charis SIL Compact, Noto Serif, Droid Serif, Droid Sans Fallback");
-pref("font.name-list.sans-serif.zh-HK", "Clear Sans, Roboto, Droid Sans, Noto Sans TC, Noto Sans SC, Noto Sans CJK TC, Droid Sans Fallback");
+pref("font.name-list.sans-serif.zh-HK", "Roboto, Droid Sans, Noto Sans TC, Noto Sans SC, Noto Sans CJK TC, Droid Sans Fallback");
 pref("font.name-list.monospace.zh-HK", "Droid Sans Mono, Noto Sans Mono CJK TC, Droid Sans Fallback");
 
 pref("font.name-list.serif.zh-TW", "Charis SIL Compact, Noto Serif, Droid Serif, Droid Sans Fallback");
-pref("font.name-list.sans-serif.zh-TW", "Clear Sans, Roboto, Droid Sans, Noto Sans TC, Noto Sans SC, Noto Sans CJK TC, Droid Sans Fallback");
+pref("font.name-list.sans-serif.zh-TW", "Roboto, Droid Sans, Noto Sans TC, Noto Sans SC, Noto Sans CJK TC, Droid Sans Fallback");
 pref("font.name-list.monospace.zh-TW", "Droid Sans Mono, Noto Sans Mono CJK TC, Droid Sans Fallback");
 
 pref("font.name-list.serif.x-math", "Latin Modern Math, STIX Two Math, XITS Math, Cambria Math, Libertinus Math, DejaVu Math TeX Gyre, TeX Gyre Bonum Math, TeX Gyre Pagella Math, TeX Gyre Schola, TeX Gyre Termes Math, STIX Math, Asana Math, STIXGeneral, DejaVu Serif, DejaVu Sans, Charis SIL Compact");
-pref("font.name-list.sans-serif.x-math", "Clear Sans");
+pref("font.name-list.sans-serif.x-math", "Roboto");
 pref("font.name-list.monospace.x-math", "Droid Sans Mono");
 
 #endif
 
 #if OS_ARCH==AIX
 
 // Override default Japanese fonts
 pref("font.name-list.serif.ja", "dt-interface system-jisx0208.1983-0");
--- a/netwerk/base/PartiallySeekableInputStream.cpp
+++ b/netwerk/base/PartiallySeekableInputStream.cpp
@@ -20,25 +20,33 @@ NS_INTERFACE_MAP_BEGIN(PartiallySeekable
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsICloneableInputStream,
                                      mWeakCloneableInputStream)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIIPCSerializableInputStream,
                                      mWeakIPCSerializableInputStream)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAsyncInputStream,
                                      mWeakAsyncInputStream)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIInputStreamCallback,
                                      mWeakAsyncInputStream)
+  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIInputStreamLength,
+                                     mWeakInputStreamLength)
+  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAsyncInputStreamLength,
+                                     mWeakAsyncInputStreamLength)
+  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIInputStreamLengthCallback,
+                                     mWeakAsyncInputStreamLength)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
 NS_INTERFACE_MAP_END
 
 PartiallySeekableInputStream::PartiallySeekableInputStream(already_AddRefed<nsIInputStream> aInputStream,
                                                            uint64_t aBufferSize)
   : mInputStream(Move(aInputStream))
   , mWeakCloneableInputStream(nullptr)
   , mWeakIPCSerializableInputStream(nullptr)
   , mWeakAsyncInputStream(nullptr)
+  , mWeakInputStreamLength(nullptr)
+  , mWeakAsyncInputStreamLength(nullptr)
   , mBufferSize(aBufferSize)
   , mPos(0)
   , mClosed(false)
   , mMutex("PartiallySeekableInputStream::mMutex")
 {
   Init();
 }
 
@@ -80,16 +88,29 @@ PartiallySeekableInputStream::Init()
     mWeakIPCSerializableInputStream = serializableStream;
   }
 
   nsCOMPtr<nsIAsyncInputStream> asyncInputStream =
     do_QueryInterface(mInputStream);
   if (asyncInputStream && SameCOMIdentity(mInputStream, asyncInputStream)) {
     mWeakAsyncInputStream = asyncInputStream;
   }
+
+  nsCOMPtr<nsIInputStreamLength> inputStreamLength =
+    do_QueryInterface(mInputStream);
+  if (inputStreamLength && SameCOMIdentity(mInputStream, inputStreamLength)) {
+    mWeakInputStreamLength = inputStreamLength;
+  }
+
+  nsCOMPtr<nsIAsyncInputStreamLength> asyncInputStreamLength =
+    do_QueryInterface(mInputStream);
+  if (asyncInputStreamLength &&
+      SameCOMIdentity(mInputStream, asyncInputStreamLength)) {
+    mWeakAsyncInputStreamLength = asyncInputStreamLength;
+  }
 }
 
 NS_IMETHODIMP
 PartiallySeekableInputStream::Close()
 {
   mInputStream->Close();
   mCachedBuffer.Clear();
   mPos = 0;
@@ -347,10 +368,54 @@ PartiallySeekableInputStream::Tell(int64
 }
 
 NS_IMETHODIMP
 PartiallySeekableInputStream::SetEOF()
 {
   return Close();
 }
 
+// nsIInputStreamLength
+
+NS_IMETHODIMP
+PartiallySeekableInputStream::Length(int64_t* aLength)
+{
+  NS_ENSURE_STATE(mWeakInputStreamLength);
+  return mWeakInputStreamLength->Length(aLength);
+}
+
+// nsIAsyncInputStreamLength
+
+NS_IMETHODIMP
+PartiallySeekableInputStream::AsyncLengthWait(nsIInputStreamLengthCallback* aCallback,
+                                              nsIEventTarget* aEventTarget)
+{
+  NS_ENSURE_STATE(mWeakAsyncInputStreamLength);
+
+  nsCOMPtr<nsIInputStreamLengthCallback> callback = aCallback ? this : nullptr;
+  {
+    MutexAutoLock lock(mMutex);
+    mAsyncInputStreamLengthCallback = aCallback;
+  }
+
+  return mWeakAsyncInputStreamLength->AsyncLengthWait(callback, aEventTarget);
+}
+
+NS_IMETHODIMP
+PartiallySeekableInputStream::OnInputStreamLengthReady(nsIAsyncInputStreamLength* aStream,
+                                                       int64_t aLength)
+{
+  nsCOMPtr<nsIInputStreamLengthCallback> callback;
+  {
+    MutexAutoLock lock(mMutex);
+    // We have been canceled in the meanwhile.
+    if (!mAsyncInputStreamLengthCallback) {
+      return NS_OK;
+    }
+
+    callback.swap(mAsyncInputStreamLengthCallback);
+  }
+
+  return callback->OnInputStreamLengthReady(this, aLength);
+}
+
 } // net namespace
 } // mozilla namespace
--- a/netwerk/base/PartiallySeekableInputStream.h
+++ b/netwerk/base/PartiallySeekableInputStream.h
@@ -6,39 +6,46 @@
 #ifndef PartiallySeekableInputStream_h
 #define PartiallySeekableInputStream_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/Mutex.h"
 #include "nsCOMPtr.h"
 #include "nsIAsyncInputStream.h"
 #include "nsICloneableInputStream.h"
+#include "nsIInputStreamLength.h"
 #include "nsIIPCSerializableInputStream.h"
 #include "nsISeekableStream.h"
 
 namespace mozilla {
 namespace net {
 
 // A wrapper for making a stream seekable for the first |aBufferSize| bytes.
 // Note that this object takes the ownership of the underlying stream.
 
 class PartiallySeekableInputStream final : public nsISeekableStream
                                          , public nsIAsyncInputStream
                                          , public nsICloneableInputStream
                                          , public nsIIPCSerializableInputStream
                                          , public nsIInputStreamCallback
+                                         , public nsIInputStreamLength
+                                         , public nsIAsyncInputStreamLength
+                                         , public nsIInputStreamLengthCallback
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIINPUTSTREAM
   NS_DECL_NSISEEKABLESTREAM
   NS_DECL_NSIASYNCINPUTSTREAM
   NS_DECL_NSICLONEABLEINPUTSTREAM
   NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
   NS_DECL_NSIINPUTSTREAMCALLBACK
+  NS_DECL_NSIINPUTSTREAMLENGTH
+  NS_DECL_NSIASYNCINPUTSTREAMLENGTH
+  NS_DECL_NSIINPUTSTREAMLENGTHCALLBACK
 
   explicit PartiallySeekableInputStream(already_AddRefed<nsIInputStream> aInputStream,
                                         uint64_t aBufferSize = 4096);
 
 private:
   PartiallySeekableInputStream(already_AddRefed<nsIInputStream> aClonedBaseStream,
                                PartiallySeekableInputStream* aClonedFrom);
 
@@ -48,20 +55,25 @@ private:
   Init();
 
   nsCOMPtr<nsIInputStream> mInputStream;
 
   // Raw pointers because these are just QI of mInputStream.
   nsICloneableInputStream* mWeakCloneableInputStream;
   nsIIPCSerializableInputStream* mWeakIPCSerializableInputStream;
   nsIAsyncInputStream* mWeakAsyncInputStream;
+  nsIInputStreamLength* mWeakInputStreamLength;
+  nsIAsyncInputStreamLength* mWeakAsyncInputStreamLength;
 
   // Protected by mutex.
   nsCOMPtr<nsIInputStreamCallback> mAsyncWaitCallback;
 
+  // Protected by mutex.
+  nsCOMPtr<nsIInputStreamLengthCallback> mAsyncInputStreamLengthCallback;
+
   nsTArray<char> mCachedBuffer;
 
   uint64_t mBufferSize;
   uint64_t mPos;
   bool mClosed;
 
   Mutex mMutex;
 };
--- a/netwerk/base/nsBufferedStreams.cpp
+++ b/netwerk/base/nsBufferedStreams.cpp
@@ -289,31 +289,36 @@ NS_IMPL_CLASSINFO(nsBufferedInputStream,
 NS_INTERFACE_MAP_BEGIN(nsBufferedInputStream)
     NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIInputStream, nsIBufferedInputStream)
     NS_INTERFACE_MAP_ENTRY(nsIBufferedInputStream)
     NS_INTERFACE_MAP_ENTRY(nsIStreamBufferAccess)
     NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIIPCSerializableInputStream, mIsIPCSerializable)
     NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAsyncInputStream, mIsAsyncInputStream)
     NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIInputStreamCallback, mIsAsyncInputStream)
     NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsICloneableInputStream, mIsCloneableInputStream)
+    NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIInputStreamLength, mIsInputStreamLength)
+    NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAsyncInputStreamLength, mIsAsyncInputStreamLength)
+    NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIInputStreamLengthCallback, mIsAsyncInputStreamLength)
     NS_IMPL_QUERY_CLASSINFO(nsBufferedInputStream)
 NS_INTERFACE_MAP_END_INHERITING(nsBufferedStream)
 
 NS_IMPL_CI_INTERFACE_GETTER(nsBufferedInputStream,
                             nsIInputStream,
                             nsIBufferedInputStream,
                             nsISeekableStream,
                             nsIStreamBufferAccess)
 
 nsBufferedInputStream::nsBufferedInputStream()
    : nsBufferedStream()
    , mMutex("nsBufferedInputStream::mMutex")
    , mIsIPCSerializable(true)
    , mIsAsyncInputStream(false)
    , mIsCloneableInputStream(false)
+   , mIsInputStreamLength(false)
+   , mIsAsyncInputStreamLength(false)
 {}
 
 nsresult
 nsBufferedInputStream::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
 {
     NS_ENSURE_NO_AGGREGATION(aOuter);
 
     nsBufferedInputStream* stream = new nsBufferedInputStream();
@@ -342,16 +347,26 @@ nsBufferedInputStream::Init(nsIInputStre
         mIsAsyncInputStream = !!stream;
     }
 
     {
         nsCOMPtr<nsICloneableInputStream> stream = do_QueryInterface(mStream);
         mIsCloneableInputStream = !!stream;
     }
 
+    {
+        nsCOMPtr<nsIInputStreamLength> stream = do_QueryInterface(mStream);
+        mIsInputStreamLength = !!stream;
+    }
+
+    {
+        nsCOMPtr<nsIAsyncInputStreamLength> stream = do_QueryInterface(mStream);
+        mIsAsyncInputStreamLength = !!stream;
+    }
+
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsBufferedInputStream::Close()
 {
     nsresult rv1 = NS_OK, rv2;
     if (mStream) {
@@ -762,16 +777,69 @@ nsBufferedInputStream::Clone(nsIInputStr
   nsCOMPtr<nsIBufferedInputStream> bis = new nsBufferedInputStream();
   rv = bis->Init(clonedStream, mBufferSize);
   NS_ENSURE_SUCCESS(rv, rv);
 
   bis.forget(aResult);
   return NS_OK;
 }
 
+// nsIInputStreamLength
+
+NS_IMETHODIMP
+nsBufferedInputStream::Length(int64_t* aLength)
+{
+  nsCOMPtr<nsIInputStreamLength> stream = do_QueryInterface(mStream);
+  NS_ENSURE_TRUE(stream, NS_ERROR_FAILURE);
+
+  return stream->Length(aLength);
+}
+
+// nsIAsyncInputStreamLength
+
+NS_IMETHODIMP
+nsBufferedInputStream::AsyncLengthWait(nsIInputStreamLengthCallback* aCallback,
+                                       nsIEventTarget* aEventTarget)
+{
+  nsCOMPtr<nsIAsyncInputStreamLength> stream = do_QueryInterface(mStream);
+  if (!stream) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<nsIInputStreamLengthCallback> callback = aCallback ? this : nullptr;
+  {
+    MutexAutoLock lock(mMutex);
+    mAsyncInputStreamLengthCallback = aCallback;
+  }
+
+  MOZ_ASSERT(stream);
+  return stream->AsyncLengthWait(callback, aEventTarget);
+}
+
+// nsIInputStreamLengthCallback
+
+NS_IMETHODIMP
+nsBufferedInputStream::OnInputStreamLengthReady(nsIAsyncInputStreamLength* aStream,
+                                                int64_t aLength)
+{
+  nsCOMPtr<nsIInputStreamLengthCallback> callback;
+  {
+    MutexAutoLock lock(mMutex);
+    // We have been canceled in the meanwhile.
+    if (!mAsyncInputStreamLengthCallback) {
+      return NS_OK;
+    }
+
+    callback.swap(mAsyncInputStreamLengthCallback);
+  }
+
+  MOZ_ASSERT(callback);
+  return callback->OnInputStreamLengthReady(this, aLength);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // nsBufferedOutputStream
 
 NS_IMPL_ADDREF_INHERITED(nsBufferedOutputStream, nsBufferedStream)
 NS_IMPL_RELEASE_INHERITED(nsBufferedOutputStream, nsBufferedStream)
 // This QI uses NS_INTERFACE_MAP_ENTRY_CONDITIONAL to check for
 // non-nullness of mSafeStream.
 NS_INTERFACE_MAP_BEGIN(nsBufferedOutputStream)
--- a/netwerk/base/nsBufferedStreams.h
+++ b/netwerk/base/nsBufferedStreams.h
@@ -11,16 +11,17 @@
 #include "nsIOutputStream.h"
 #include "nsISafeOutputStream.h"
 #include "nsISeekableStream.h"
 #include "nsIStreamBufferAccess.h"
 #include "nsCOMPtr.h"
 #include "nsIIPCSerializableInputStream.h"
 #include "nsIAsyncInputStream.h"
 #include "nsICloneableInputStream.h"
+#include "nsIInputStreamLength.h"
 #include "mozilla/Mutex.h"
 
 ////////////////////////////////////////////////////////////////////////////////
 
 class nsBufferedStream : public nsISeekableStream
 {
 public:
     NS_DECL_THREADSAFE_ISUPPORTS
@@ -64,27 +65,33 @@ protected:
 
 class nsBufferedInputStream final
     : public nsBufferedStream,
       public nsIBufferedInputStream,
       public nsIStreamBufferAccess,
       public nsIIPCSerializableInputStream,
       public nsIAsyncInputStream,
       public nsIInputStreamCallback,
-      public nsICloneableInputStream
+      public nsICloneableInputStream,
+      public nsIInputStreamLength,
+      public nsIAsyncInputStreamLength,
+      public nsIInputStreamLengthCallback
 {
 public:
     NS_DECL_ISUPPORTS_INHERITED
     NS_DECL_NSIINPUTSTREAM
     NS_DECL_NSIBUFFEREDINPUTSTREAM
     NS_DECL_NSISTREAMBUFFERACCESS
     NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
     NS_DECL_NSIASYNCINPUTSTREAM
     NS_DECL_NSIINPUTSTREAMCALLBACK
     NS_DECL_NSICLONEABLEINPUTSTREAM
+    NS_DECL_NSIINPUTSTREAMLENGTH
+    NS_DECL_NSIASYNCINPUTSTREAMLENGTH
+    NS_DECL_NSIINPUTSTREAMLENGTHCALLBACK
 
     nsBufferedInputStream();
 
     static nsresult
     Create(nsISupports *aOuter, REFNSIID aIID, void **aResult);
 
     nsIInputStream* Source() {
         return (nsIInputStream*)mStream;
@@ -96,19 +103,24 @@ protected:
     NS_IMETHOD Fill() override;
     NS_IMETHOD Flush() override { return NS_OK; } // no-op for input streams
 
     mozilla::Mutex mMutex;
 
     // This value is protected by mutex.
     nsCOMPtr<nsIInputStreamCallback> mAsyncWaitCallback;
 
+    // This value is protected by mutex.
+    nsCOMPtr<nsIInputStreamLengthCallback> mAsyncInputStreamLengthCallback;
+
     bool mIsIPCSerializable;
     bool mIsAsyncInputStream;
     bool mIsCloneableInputStream;
+    bool mIsInputStreamLength;
+    bool mIsAsyncInputStreamLength;
 };
 
 ////////////////////////////////////////////////////////////////////////////////
 
 class nsBufferedOutputStream  : public nsBufferedStream,
                                 public nsISafeOutputStream,
                                 public nsIBufferedOutputStream,
                                 public nsIStreamBufferAccess
--- a/netwerk/base/nsMIMEInputStream.cpp
+++ b/netwerk/base/nsMIMEInputStream.cpp
@@ -8,16 +8,17 @@
  * automatic creation of the content-length header.
  */
 
 #include "ipc/IPCMessageUtils.h"
 
 #include "nsCOMPtr.h"
 #include "nsComponentManagerUtils.h"
 #include "nsIAsyncInputStream.h"
+#include "nsIInputStreamLength.h"
 #include "nsIHttpHeaderVisitor.h"
 #include "nsIMIMEInputStream.h"
 #include "nsISeekableStream.h"
 #include "nsString.h"
 #include "nsMIMEInputStream.h"
 #include "nsIClassInfoImpl.h"
 #include "nsIIPCSerializableInputStream.h"
 #include "mozilla/Move.h"
@@ -27,56 +28,67 @@
 using namespace mozilla::ipc;
 using mozilla::Maybe;
 using mozilla::Move;
 
 class nsMIMEInputStream : public nsIMIMEInputStream,
                           public nsISeekableStream,
                           public nsIIPCSerializableInputStream,
                           public nsIAsyncInputStream,
-                          public nsIInputStreamCallback
+                          public nsIInputStreamCallback,
+                          public nsIInputStreamLength,
+                          public nsIAsyncInputStreamLength,
+                          public nsIInputStreamLengthCallback
 {
     virtual ~nsMIMEInputStream() = default;
 
 public:
     nsMIMEInputStream();
 
     NS_DECL_THREADSAFE_ISUPPORTS
     NS_DECL_NSIINPUTSTREAM
     NS_DECL_NSIMIMEINPUTSTREAM
     NS_DECL_NSISEEKABLESTREAM
     NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
     NS_DECL_NSIASYNCINPUTSTREAM
     NS_DECL_NSIINPUTSTREAMCALLBACK
+    NS_DECL_NSIINPUTSTREAMLENGTH
+    NS_DECL_NSIASYNCINPUTSTREAMLENGTH
+    NS_DECL_NSIINPUTSTREAMLENGTHCALLBACK
 
 private:
 
     void InitStreams();
 
     struct MOZ_STACK_CLASS ReadSegmentsState {
         nsCOMPtr<nsIInputStream> mThisStream;
         nsWriteSegmentFun mWriter;
         void* mClosure;
     };
     static nsresult ReadSegCb(nsIInputStream* aIn, void* aClosure,
                               const char* aFromRawSegment, uint32_t aToOffset,
                               uint32_t aCount, uint32_t *aWriteCount);
 
     bool IsAsyncInputStream() const;
     bool IsIPCSerializable() const;
+    bool IsInputStreamLength() const;
+    bool IsAsyncInputStreamLength() const;
 
     nsTArray<HeaderEntry> mHeaders;
 
     nsCOMPtr<nsIInputStream> mStream;
     bool mStartedReading;
 
     mozilla::Mutex mMutex;
 
     // This is protected by mutex.
     nsCOMPtr<nsIInputStreamCallback> mAsyncWaitCallback;
+
+    // This is protected by mutex.
+    nsCOMPtr<nsIInputStreamLengthCallback> mAsyncInputStreamLengthCallback;
 };
 
 NS_IMPL_ADDREF(nsMIMEInputStream)
 NS_IMPL_RELEASE(nsMIMEInputStream)
 
 NS_IMPL_CLASSINFO(nsMIMEInputStream, nullptr, nsIClassInfo::THREADSAFE,
                   NS_MIMEINPUTSTREAM_CID)
 
@@ -86,16 +98,22 @@ NS_INTERFACE_MAP_BEGIN(nsMIMEInputStream
   NS_INTERFACE_MAP_ENTRY(nsISeekableStream)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIIPCSerializableInputStream,
                                      IsIPCSerializable())
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAsyncInputStream,
                                      IsAsyncInputStream())
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIInputStreamCallback,
                                      IsAsyncInputStream())
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIMIMEInputStream)
+  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIInputStreamLength,
+                                     IsInputStreamLength())
+  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAsyncInputStreamLength,
+                                     IsAsyncInputStreamLength())
+  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIInputStreamLengthCallback,
+                                     IsAsyncInputStreamLength())
   NS_IMPL_QUERY_CLASSINFO(nsMIMEInputStream)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CI_INTERFACE_GETTER(nsMIMEInputStream,
                             nsIMIMEInputStream,
                             nsIAsyncInputStream,
                             nsIInputStream,
                             nsISeekableStream)
@@ -387,16 +405,64 @@ nsMIMEInputStream::Deserialize(const Inp
 
 Maybe<uint64_t>
 nsMIMEInputStream::ExpectedSerializedLength()
 {
     nsCOMPtr<nsIIPCSerializableInputStream> serializable = do_QueryInterface(mStream);
     return serializable ? serializable->ExpectedSerializedLength() : Nothing();
 }
 
+NS_IMETHODIMP
+nsMIMEInputStream::Length(int64_t* aLength)
+{
+    nsCOMPtr<nsIInputStreamLength> stream = do_QueryInterface(mStream);
+    if (NS_WARN_IF(!stream)) {
+        return NS_ERROR_FAILURE;
+    }
+
+    return stream->Length(aLength);
+}
+
+NS_IMETHODIMP
+nsMIMEInputStream::AsyncLengthWait(nsIInputStreamLengthCallback* aCallback,
+                                   nsIEventTarget* aEventTarget)
+{
+    nsCOMPtr<nsIAsyncInputStreamLength> stream = do_QueryInterface(mStream);
+    if (NS_WARN_IF(!stream)) {
+        return NS_ERROR_FAILURE;
+    }
+
+    nsCOMPtr<nsIInputStreamLengthCallback> callback = aCallback ? this : nullptr;
+    {
+        MutexAutoLock lock(mMutex);
+        mAsyncInputStreamLengthCallback = aCallback;
+    }
+
+    return stream->AsyncLengthWait(callback, aEventTarget);
+}
+
+NS_IMETHODIMP
+nsMIMEInputStream::OnInputStreamLengthReady(nsIAsyncInputStreamLength* aStream,
+                                            int64_t aLength)
+{
+    nsCOMPtr<nsIInputStreamLengthCallback> callback;
+    {
+        MutexAutoLock lock(mMutex);
+        // We have been canceled in the meanwhile.
+        if (!mAsyncInputStreamLengthCallback) {
+            return NS_OK;
+        }
+
+        callback.swap(mAsyncInputStreamLengthCallback);
+    }
+
+    MOZ_ASSERT(callback);
+    return callback->OnInputStreamLengthReady(this, aLength);
+}
+
 bool
 nsMIMEInputStream::IsAsyncInputStream() const
 {
     nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(mStream);
     return !!asyncStream;
 }
 
 bool
@@ -405,8 +471,22 @@ nsMIMEInputStream::IsIPCSerializable() c
     // If SetData() or Deserialize() has not be called yet, mStream is null.
     if (!mStream) {
       return true;
     }
 
     nsCOMPtr<nsIIPCSerializableInputStream> serializable = do_QueryInterface(mStream);
     return !!serializable;
 }
+
+bool
+nsMIMEInputStream::IsInputStreamLength() const
+{
+    nsCOMPtr<nsIInputStreamLength> stream = do_QueryInterface(mStream);
+    return !!stream;
+}
+
+bool
+nsMIMEInputStream::IsAsyncInputStreamLength() const
+{
+    nsCOMPtr<nsIAsyncInputStreamLength> stream = do_QueryInterface(mStream);
+    return !!stream;
+}
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -54,16 +54,17 @@
 #include "nsISSLSocketControl.h"
 #include "mozilla/Telemetry.h"
 #include "nsIURL.h"
 #include "nsIConsoleService.h"
 #include "mozilla/BinarySearch.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/Move.h"
 #include "mozilla/net/PartiallySeekableInputStream.h"
+#include "mozilla/InputStreamLengthHelper.h"
 #include "nsIHttpHeaderVisitor.h"
 #include "nsIMIMEInputStream.h"
 #include "nsIXULRuntime.h"
 #include "nsICacheInfoChannel.h"
 #include "nsIDOMWindowUtils.h"
 #include "nsHttpChannel.h"
 #include "nsRedirectHistoryEntry.h"
 #include "nsServerTiming.h"
@@ -183,16 +184,17 @@ HttpBaseChannel::HttpBaseChannel()
   , mTRR(false)
   , mResponseTimeoutEnabled(true)
   , mAllRedirectsSameOrigin(true)
   , mAllRedirectsPassTimingAllowCheck(true)
   , mResponseCouldBeSynthesized(false)
   , mBlockAuthPrompt(false)
   , mAllowStaleCacheContent(false)
   , mAddedAsNonTailRequest(false)
+  , mAsyncOpenWaitingForStreamLength(false)
   , mTlsFlags(0)
   , mSuspendCount(0)
   , mInitialRwin(0)
   , mProxyResolveFlags(0)
   , mContentDispositionHint(UINT32_MAX)
   , mHttpHandler(gHttpHandler)
   , mReferrerPolicy(NS_GetDefaultReferrerPolicy())
   , mRedirectCount(0)
@@ -214,17 +216,17 @@ HttpBaseChannel::HttpBaseChannel()
   , mRequireCORSPreflight(false)
   , mReportCollector(new ConsoleReportCollector())
   , mAltDataLength(0)
   , mAltDataForChild(false)
   , mForceMainDocumentChannel(false)
   , mIsTrackingResource(false)
   , mLastRedirectFlags(0)
   , mReqContentLength(0U)
-  , mReqContentLengthDetermined(false)
+  , mPendingInputStreamLengthOperation(false)
 {
   LOG(("Creating HttpBaseChannel @%p\n", this));
 
   // Subfields of unions cannot be targeted in an initializer list.
 #ifdef MOZ_VALGRIND
   // Zero the entire unions so that Valgrind doesn't complain when we send them
   // to another process.
   memset(&mSelfAddr, 0, sizeof(NetAddr));
@@ -1006,22 +1008,17 @@ HttpBaseChannel::CloneUploadStream(int64
   }
 
   nsCOMPtr<nsIInputStream> clonedStream;
   nsresult rv = NS_CloneInputStream(mUploadStream, getter_AddRefs(clonedStream));
   NS_ENSURE_SUCCESS(rv, rv);
 
   clonedStream.forget(aClonedStream);
 
-  if (mReqContentLengthDetermined) {
-    *aContentLength = mReqContentLength;
-  } else {
-    *aContentLength = -1;
-  }
-
+  *aContentLength = mReqContentLength;
   return NS_OK;
 }
 
 
 //-----------------------------------------------------------------------------
 // HttpBaseChannel::nsIUploadChannel2
 //-----------------------------------------------------------------------------
 
@@ -1037,70 +1034,144 @@ HttpBaseChannel::ExplicitSetUploadStream
 
   {
     DebugOnly<nsCOMPtr<nsIMIMEInputStream>> mimeStream;
     MOZ_ASSERT(!aStreamHasHeaders ||
                NS_FAILED(CallQueryInterface(aStream, getter_AddRefs(mimeStream.value))),
                "nsIMIMEInputStream should not include headers");
   }
 
-  if (aContentLength < 0 && !aStreamHasHeaders) {
-    nsresult rv = aStream->Available(reinterpret_cast<uint64_t*>(&aContentLength));
-    if (NS_FAILED(rv) || aContentLength < 0) {
-      NS_ERROR("unable to determine content length");
-      return NS_ERROR_FAILURE;
-    }
-  }
-
   nsresult rv = SetRequestMethod(aMethod);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  if (!aStreamHasHeaders) {
-    // SetRequestHeader propagates headers to chrome if HttpChannelChild
-    nsAutoCString contentLengthStr;
-    contentLengthStr.AppendInt(aContentLength);
-    SetRequestHeader(NS_LITERAL_CSTRING("Content-Length"), contentLengthStr,
-                     false);
-    if (!aContentType.IsVoid()) {
-      if (aContentType.IsEmpty()) {
-        SetEmptyRequestHeader(NS_LITERAL_CSTRING("Content-Type"));
-      } else {
-        SetRequestHeader(NS_LITERAL_CSTRING("Content-Type"), aContentType,
-                         false);
-      }
+  if (!aStreamHasHeaders && !aContentType.IsVoid()) {
+    if (aContentType.IsEmpty()) {
+      SetEmptyRequestHeader(NS_LITERAL_CSTRING("Content-Type"));
+    } else {
+      SetRequestHeader(NS_LITERAL_CSTRING("Content-Type"), aContentType,
+                       false);
     }
   }
 
   mUploadStreamHasHeaders = aStreamHasHeaders;
 
-  // We already have the content length. We don't need to determinate it.
-  if (aContentLength > 0) {
-    mReqContentLength = aContentLength;
-    mReqContentLengthDetermined = true;
-  }
-
   nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(aStream);
   if (!seekable) {
     nsCOMPtr<nsIInputStream> stream = aStream;
-    aStream = new PartiallySeekableInputStream(stream.forget());
+    seekable = new PartiallySeekableInputStream(stream.forget());
+  }
+
+  mUploadStream = do_QueryInterface(seekable);
+
+  if (aContentLength >= 0) {
+    ExplicitSetUploadStreamLength(aContentLength, aStreamHasHeaders);
+    return NS_OK;
+  }
+
+  // Sync access to the stream length.
+  int64_t length;
+  if (InputStreamLengthHelper::GetSyncLength(aStream, &length)) {
+    ExplicitSetUploadStreamLength(length >= 0 ? length : 0,
+                                  aStreamHasHeaders);
+    return NS_OK;
   }
 
-  mUploadStream = aStream;
+  // Let's resolve the size of the stream.
+  RefPtr<HttpBaseChannel> self = this;
+  InputStreamLengthHelper::GetAsyncLength(aStream,
+    [self, aStreamHasHeaders](int64_t aLength) {
+      self->mPendingInputStreamLengthOperation = false;
+      self->ExplicitSetUploadStreamLength(aLength >= 0 ? aLength : 0,
+                                          aStreamHasHeaders);
+      self->MaybeResumeAsyncOpen();
+    });
+  mPendingInputStreamLengthOperation = true;
+  return NS_OK;
+}
+
+nsresult
+HttpBaseChannel::ExplicitSetUploadStreamLength(uint64_t aContentLength,
+                                               bool aStreamHasHeaders)
+{
+  // We already have the content length. We don't need to determinate it.
+  mReqContentLength = aContentLength;
+
+  if (aStreamHasHeaders) {
+    return NS_OK;
+  }
+
+  nsAutoCString header;
+  header.AssignLiteral("Content-Length");
+
+  // Maybe the content-length header has been already set.
+  nsAutoCString value;
+  nsresult rv = GetRequestHeader(header, value);
+  if (NS_SUCCEEDED(rv) && !value.IsEmpty()) {
+    return NS_OK;
+  }
+
+  // SetRequestHeader propagates headers to chrome if HttpChannelChild
+  MOZ_ASSERT(!mWasOpened);
+  nsAutoCString contentLengthStr;
+  contentLengthStr.AppendInt(aContentLength);
+  SetRequestHeader(header, contentLengthStr, false);
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HttpBaseChannel::GetUploadStreamHasHeaders(bool *hasHeaders)
 {
   NS_ENSURE_ARG(hasHeaders);
 
   *hasHeaders = mUploadStreamHasHeaders;
   return NS_OK;
 }
 
+bool
+HttpBaseChannel::MaybeWaitForUploadStreamLength(nsIStreamListener *aListener,
+                                                nsISupports *aContext)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(!mAsyncOpenWaitingForStreamLength, "AsyncOpen() called twice?");
+
+  if (!mPendingInputStreamLengthOperation) {
+    return false;
+  }
+
+  mListener = aListener;
+  mListenerContext = aContext;
+  mAsyncOpenWaitingForStreamLength = true;
+  return true;
+}
+
+void
+HttpBaseChannel::MaybeResumeAsyncOpen()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(!mPendingInputStreamLengthOperation);
+
+  if (!mAsyncOpenWaitingForStreamLength) {
+    return;
+  }
+
+  nsCOMPtr<nsIStreamListener> listener;
+  listener.swap(mListener);
+
+  nsCOMPtr<nsISupports> context;
+  context.swap(mListenerContext);
+
+  mAsyncOpenWaitingForStreamLength = false;
+
+  nsresult rv = AsyncOpen(listener, context);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    DoAsyncAbort(rv);
+  }
+}
+
 //-----------------------------------------------------------------------------
 // HttpBaseChannel::nsIEncodedChannel
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 HttpBaseChannel::GetApplyConversion(bool *value)
 {
   *value = mApplyConversion;
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -382,16 +382,21 @@ public: /* Necko internal use only... */
       return mChannelId;
     }
 
     void InternalSetUploadStream(nsIInputStream *uploadStream)
     {
       mUploadStream = uploadStream;
     }
 
+    void InternalSetUploadStreamLength(uint64_t aLength)
+    {
+      mReqContentLength = aLength;
+    }
+
     void SetUploadStreamHasHeaders(bool hasHeaders)
     {
       mUploadStreamHasHeaders = hasHeaders;
     }
 
     MOZ_MUST_USE nsresult
     SetReferrerWithPolicyInternal(nsIURI *referrer, uint32_t referrerPolicy)
     {
@@ -415,16 +420,20 @@ public: /* Necko internal use only... */
 protected:
   // Handle notifying listener, removing from loadgroup if request failed.
   void     DoNotifyListener();
   virtual void DoNotifyListenerCleanup() = 0;
 
   // drop reference to listener, its callbacks, and the progress sink
   virtual void ReleaseListeners();
 
+  // Call AsyncAbort().
+  virtual void
+  DoAsyncAbort(nsresult aStatus) = 0;
+
   // This is fired only when a cookie is created due to the presence of
   // Set-Cookie header in the response header of any network request.
   // This notification will come only after the "http-on-examine-response"
   // was fired.
   void NotifySetCookie(char const *aCookie);
 
   mozilla::dom::PerformanceStorage* GetPerformanceStorage();
   void MaybeReportTimingData();
@@ -477,16 +486,20 @@ protected:
   already_AddRefed<nsILoadInfo> CloneLoadInfoForRedirect(nsIURI *newURI, uint32_t redirectFlags);
 
   static void CallTypeSniffers(void *aClosure, const uint8_t *aData,
                                uint32_t aCount);
 
   nsresult
   CheckRedirectLimit(uint32_t aRedirectFlags) const;
 
+  bool
+  MaybeWaitForUploadStreamLength(nsIStreamListener *aListener,
+                                 nsISupports *aContext);
+
   friend class PrivateBrowsingChannel<HttpBaseChannel>;
   friend class InterceptFailedOnStop;
 
 protected:
   // this section is for main-thread-only object
   // all the references need to be proxy released on main thread.
   nsCOMPtr<nsIURI> mURI;
   nsCOMPtr<nsIURI> mOriginalURI;
@@ -507,16 +520,23 @@ protected:
   nsCOMPtr<nsIStreamListener> mCompressListener;
 
 private:
   // Proxy release all members above on main thread.
   void ReleaseMainThreadOnlyReferences();
 
   bool IsCrossOriginWithReferrer();
 
+  nsresult
+  ExplicitSetUploadStreamLength(uint64_t aContentLength,
+                                bool aStreamHasHeaders);
+
+  void
+  MaybeResumeAsyncOpen();
+
 protected:
   // Use Release-Acquire ordering to ensure the OMT ODA is ignored while channel
   // is canceled on main thread.
   Atomic<bool, ReleaseAcquire> mCanceled;
 
   nsTArray<Pair<nsString, nsString>> mSecurityConsoleMessages;
 
   nsCOMPtr<nsISupports>             mOwner;
@@ -593,16 +613,21 @@ protected:
   // If true, we behave as if the LOAD_FROM_CACHE flag has been set.
   // Used to enforce that flag's behavior but not expose it externally.
   uint32_t                          mAllowStaleCacheContent : 1;
 
   // True iff this request has been calculated in its request context as
   // a non tail request.  We must remove it again when this channel is done.
   uint32_t                          mAddedAsNonTailRequest : 1;
 
+  // True if AsyncOpen() is called when the stream length is still unknown.
+  // AsyncOpen() will be retriggered when InputStreamLengthHelper execs the
+  // callback, passing the stream length value.
+  uint32_t                          mAsyncOpenWaitingForStreamLength : 1;
+
   // An opaque flags for non-standard behavior of the TLS system.
   // It is unlikely this will need to be set outside of telemetry studies
   // relating to the TLS implementation.
   uint32_t                          mTlsFlags;
 
   // Current suspension depth for this channel object
   uint32_t                          mSuspendCount;
 
@@ -711,24 +736,27 @@ protected:
   uint64_t mChannelId;
 
   // If this channel was created as the result of a redirect, then this value
   // will reflect the redirect flags passed to the SetupReplacementChannel()
   // method.
   uint32_t mLastRedirectFlags;
 
   uint64_t mReqContentLength;
-  bool mReqContentLengthDetermined;
 
   nsString mIntegrityMetadata;
 
   // Classified channel's matched information
   nsCString mMatchedList;
   nsCString mMatchedProvider;
   nsCString mMatchedFullHash;
+
+  // This is set true if the channel is waiting for the
+  // InputStreamLengthHelper::GetAsyncLength callback.
+  bool mPendingInputStreamLengthOperation;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(HttpBaseChannel, HTTP_BASE_CHANNEL_IID)
 
 // Share some code while working around C++'s absurd inability to handle casting
 // of member functions between base/derived types.
 // - We want to store member function pointer to call at resume time, but one
 //   such function--HandleAsyncAbort--we want to share between the
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -1506,16 +1506,22 @@ HttpChannelChild::DoNotifyListenerCleanu
   if (mInterceptListener) {
     mInterceptListener->Cleanup();
     mInterceptListener = nullptr;
   }
 
   MaybeCallSynthesizedCallback();
 }
 
+void
+HttpChannelChild::DoAsyncAbort(nsresult aStatus)
+{
+  Unused << AsyncAbort(aStatus);
+}
+
 class DeleteSelfEvent : public NeckoTargetChannelEvent<HttpChannelChild>
 {
  public:
   explicit DeleteSelfEvent(HttpChannelChild* child)
   : NeckoTargetChannelEvent<HttpChannelChild>(child) {}
   void Run() override { mChild->DeleteSelf(); }
 };
 
@@ -2481,16 +2487,20 @@ HttpChannelChild::AsyncOpen(nsIStreamLis
   if (mCanceled)
     return mStatus;
 
   NS_ENSURE_TRUE(gNeckoChild != nullptr, NS_ERROR_FAILURE);
   NS_ENSURE_ARG_POINTER(listener);
   NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
   NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
 
+  if (MaybeWaitForUploadStreamLength(listener, aContext)) {
+    return NS_OK;
+  }
+
   mAsyncOpenTime = TimeStamp::Now();
 #ifdef MOZ_TASK_TRACER
   if (tasktracer::IsStartLogging()) {
     nsCOMPtr<nsIURI> uri;
     GetURI(getter_AddRefs(uri));
     nsAutoCString urispec;
     uri->GetSpec(urispec);
     tasktracer::AddLabel("HttpChannelChild::AsyncOpen %s", urispec.get());
--- a/netwerk/protocol/http/HttpChannelChild.h
+++ b/netwerk/protocol/http/HttpChannelChild.h
@@ -176,16 +176,18 @@ protected:
   mozilla::ipc::IPCResult RecvCancelDiversion() override;
 
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
 
   MOZ_MUST_USE bool
   GetAssociatedContentSecurity(nsIAssociatedContentSecurity** res = nullptr);
   virtual void DoNotifyListenerCleanup() override;
 
+  virtual void DoAsyncAbort(nsresult aStatus) override;
+
   NS_IMETHOD GetResponseSynthesized(bool* aSynthesized) override;
 
   nsresult
   AsyncCall(void (HttpChannelChild::*funcPtr)(),
             nsRunnableMethod<HttpChannelChild> **retval = nullptr) override;
 
   // Get event target for processing network events.
   already_AddRefed<nsIEventTarget> GetNeckoTarget() override;
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -10,16 +10,17 @@
 #include "mozilla/ipc/FileDescriptorSetParent.h"
 #include "mozilla/ipc/IPCStreamUtils.h"
 #include "mozilla/net/HttpChannelParent.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/ServiceWorkerUtils.h"
 #include "mozilla/dom/TabParent.h"
 #include "mozilla/net/NeckoParent.h"
+#include "mozilla/InputStreamLengthHelper.h"
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/Unused.h"
 #include "HttpBackgroundChannelParent.h"
 #include "HttpChannelParentListener.h"
 #include "nsHttpHandler.h"
 #include "nsNetCID.h"
 #include "nsNetUtil.h"
@@ -396,57 +397,16 @@ HttpChannelParent::InvokeAsyncOpen(nsres
   else {
     rv = mChannel->AsyncOpen(mParentListener, nullptr);
   }
   if (NS_FAILED(rv)) {
     AsyncOpenFailed(rv);
   }
 }
 
-namespace {
-class InvokeAsyncOpen : public Runnable
-{
-  nsMainThreadPtrHandle<nsIInterfaceRequestor> mChannel;
-  nsresult mStatus;
-public:
-  InvokeAsyncOpen(const nsMainThreadPtrHandle<nsIInterfaceRequestor>& aChannel,
-                  nsresult aStatus)
-    : Runnable("net::InvokeAsyncOpen")
-    , mChannel(aChannel)
-    , mStatus(aStatus)
-  {
-  }
-
-  NS_IMETHOD Run() override
-  {
-    RefPtr<HttpChannelParent> channel = do_QueryObject(mChannel.get());
-    channel->TryInvokeAsyncOpen(mStatus);
-    return NS_OK;
-  }
-};
-
-struct UploadStreamClosure {
-  nsMainThreadPtrHandle<nsIInterfaceRequestor> mChannel;
-
-  explicit UploadStreamClosure(const nsMainThreadPtrHandle<nsIInterfaceRequestor>& aChannel)
-  : mChannel(aChannel)
-  {
-  }
-};
-
-void
-UploadCopyComplete(void* aClosure, nsresult aStatus) {
-  // Called on the Stream Transport Service thread by NS_AsyncCopy
-  MOZ_ASSERT(!NS_IsMainThread());
-  UniquePtr<UploadStreamClosure> closure(static_cast<UploadStreamClosure*>(aClosure));
-  nsCOMPtr<nsIRunnable> event = new InvokeAsyncOpen(closure->mChannel, aStatus);
-  NS_DispatchToMainThread(event);
-}
-} // anonymous namespace
-
 bool
 HttpChannelParent::DoAsyncOpen(  const URIParams&           aURI,
                                  const OptionalURIParams&   aOriginalURI,
                                  const OptionalURIParams&   aDocURI,
                                  const OptionalURIParams&   aReferrerURI,
                                  const uint32_t&            aReferrerPolicy,
                                  const OptionalURIParams&   aAPIRedirectToURI,
                                  const OptionalURIParams&   aTopWindowURI,
@@ -597,72 +557,34 @@ HttpChannelParent::DoAsyncOpen(  const U
 
   if (aCorsPreflightArgs.type() == OptionalCorsPreflightArgs::TCorsPreflightArgs) {
     const CorsPreflightArgs& args = aCorsPreflightArgs.get_CorsPreflightArgs();
     httpChannel->SetCorsPreflightParameters(args.unsafeHeaders());
   }
 
   nsCOMPtr<nsIInputStream> stream = DeserializeIPCStream(uploadStream);
   if (stream) {
-    // FIXME: The fast path of using the existing stream currently only applies to streams
-    //   that have had their entire contents serialized from the child at this point.
-    //   Once bug 1294446 and bug 1294450 are fixed it is worth revisiting this heuristic.
-    nsCOMPtr<nsIIPCSerializableInputStream> completeStream = do_QueryInterface(stream);
-    if (!completeStream) {
-      // Wait for completion of async copying IPC upload stream to a local input stream.
+    int64_t length;
+    if (InputStreamLengthHelper::GetSyncLength(stream, &length)) {
+      httpChannel->InternalSetUploadStreamLength(length >= 0 ? length : 0);
+    } else {
+      // Wait for the nputStreamLengthHelper::GetAsyncLength callback.
       ++mAsyncOpenBarrier;
 
-      // buffer size matches PChildToParentStream transfer size.
-      const uint32_t kBufferSize = 32768;
-
-      nsCOMPtr<nsIStorageStream> storageStream;
-      nsresult rv = NS_NewStorageStream(kBufferSize, UINT32_MAX,
-                                        getter_AddRefs(storageStream));
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return SendFailedAsyncOpen(rv);
-      }
-
-      nsCOMPtr<nsIInputStream> newUploadStream;
-      rv = storageStream->NewInputStream(0, getter_AddRefs(newUploadStream));
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return SendFailedAsyncOpen(rv);
-      }
-
-      nsCOMPtr<nsIOutputStream> sink;
-      rv = storageStream->GetOutputStream(0, getter_AddRefs(sink));
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return SendFailedAsyncOpen(rv);
-      }
+      // Let's resolve the size of the stream. The following operation is always
+      // async.
+      RefPtr<HttpChannelParent> self = this;
+      InputStreamLengthHelper::GetAsyncLength(stream,
+        [self, httpChannel](int64_t aLength) {
+          httpChannel->InternalSetUploadStreamLength(aLength >= 0 ? aLength : 0);
+          self->TryInvokeAsyncOpen(NS_OK);
+        });
+    }
 
-      nsCOMPtr<nsIEventTarget> target =
-          do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
-      if (NS_FAILED(rv) || !target) {
-        return SendFailedAsyncOpen(rv);
-      }
-
-      nsCOMPtr<nsIInterfaceRequestor> iir = static_cast<nsIInterfaceRequestor*>(this);
-      nsMainThreadPtrHandle<nsIInterfaceRequestor> handle =
-          nsMainThreadPtrHandle<nsIInterfaceRequestor>(
-              new nsMainThreadPtrHolder<nsIInterfaceRequestor>(
-                "nsIInterfaceRequestor", iir));
-      UniquePtr<UploadStreamClosure> closure(new UploadStreamClosure(handle));
-
-      // Accumulate the stream contents as the child sends it. We will continue with
-      // the AsyncOpen process once the full stream has been received.
-      rv = NS_AsyncCopy(stream, sink, target, NS_ASYNCCOPY_VIA_READSEGMENTS,
-                        kBufferSize, // copy segment size
-                        UploadCopyComplete, closure.release());
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return SendFailedAsyncOpen(rv);
-      }
-
-      httpChannel->InternalSetUploadStream(newUploadStream);
-    } else {
-      httpChannel->InternalSetUploadStream(stream);
-    }
+    httpChannel->InternalSetUploadStream(stream);
     httpChannel->SetUploadStreamHasHeaders(uploadStreamHasHeaders);
   }
 
   if (aSynthesizedResponseHead.type() == OptionalHttpResponseHead::TnsHttpResponseHead) {
     parentListener->SetupInterception(aSynthesizedResponseHead.get_nsHttpResponseHead());
     mWillSynthesizeResponse = true;
     httpChannelImpl->SetCouldBeSynthesized();
 
--- a/netwerk/protocol/http/InterceptedHttpChannel.cpp
+++ b/netwerk/protocol/http/InterceptedHttpChannel.cpp
@@ -645,16 +645,22 @@ InterceptedHttpChannel::ResumeAt(uint64_
 
 void
 InterceptedHttpChannel::DoNotifyListenerCleanup()
 {
   // Prefer to cleanup in ReleaseListeners() as it seems to be called
   // more consistently in necko.
 }
 
+void
+InterceptedHttpChannel::DoAsyncAbort(nsresult aStatus)
+{
+  Unused << AsyncAbort(aStatus);
+}
+
 
 NS_IMETHODIMP
 InterceptedHttpChannel::ResetInterception(void)
 {
   if (mCanceled) {
     return mStatus;
   }
 
--- a/netwerk/protocol/http/InterceptedHttpChannel.h
+++ b/netwerk/protocol/http/InterceptedHttpChannel.h
@@ -188,14 +188,17 @@ public:
   NS_IMETHOD
   AddClassFlags(uint32_t flags) override;
 
   NS_IMETHOD
   ResumeAt(uint64_t startPos, const nsACString & entityID) override;
 
   void
   DoNotifyListenerCleanup() override;
+
+  void
+  DoAsyncAbort(nsresult aStatus) override;
 };
 
 } // namespace net
 } // namespace mozilla
 
 #endif // mozilla_net_InterceptedHttpChannel_h
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -633,83 +633,19 @@ nsHttpChannel::ConnectOnTailUnblock()
     // cache has won the race.
     if (mRaceCacheWithNetwork && mCachedContentIsValid) {
         Unused << ReadFromCache(true);
     }
 
     return TriggerNetwork();
 }
 
-// nsIInputAvailableCallback (nsIStreamTransportService.idl)
-NS_IMETHODIMP
-nsHttpChannel::OnInputAvailableComplete(uint64_t size, nsresult status)
-{
-    MOZ_ASSERT(NS_IsMainThread(), "Wrong thread.");
-    LOG(("nsHttpChannel::OnInputAvailableComplete %p %" PRIx32 "\n",
-         this, static_cast<uint32_t>(status)));
-    if (NS_SUCCEEDED(status)) {
-        mReqContentLength = size;
-    } else {
-        // fall back to synchronous on the error path. should not happen.
-        if (NS_SUCCEEDED(mUploadStream->Available(&size))) {
-            mReqContentLength = size;
-        }
-    }
-
-    LOG(("nsHttpChannel::DetermineContentLength %p from sts\n", this));
-    mReqContentLengthDetermined = true;
-    nsresult rv = mCanceled ? mStatus : ContinueConnect();
-    if (NS_FAILED(rv)) {
-        CloseCacheEntry(false);
-        Unused << AsyncAbort(rv);
-    }
-    return NS_OK;
-}
-
-void
-nsHttpChannel::DetermineContentLength()
-{
-    nsCOMPtr<nsIStreamTransportService> sts(services::GetStreamTransportService());
-
-    if (!mUploadStream || !sts) {
-        LOG(("nsHttpChannel::DetermineContentLength %p no body\n", this));
-        mReqContentLength = 0U;
-        mReqContentLengthDetermined = true;
-        return;
-    }
-
-    // If this is a stream is blocking, it needs to be sent to a worker thread
-    // to do Available() as it may cause disk/IO.
-    bool nonBlocking = false;
-    if (NS_FAILED(mUploadStream->IsNonBlocking(&nonBlocking)) || nonBlocking) {
-        mUploadStream->Available(&mReqContentLength);
-        LOG(("nsHttpChannel::DetermineContentLength %p from mem\n", this));
-        mReqContentLengthDetermined = true;
-        return;
-    }
-
-    LOG(("nsHttpChannel::DetermineContentLength Async [this=%p]\n", this));
-    sts->InputAvailable(mUploadStream, this);
-}
-
 nsresult
 nsHttpChannel::ContinueConnect()
 {
-    // If we have a request body that is going to require bouncing to the STS
-    // in order to determine the content-length as doing it on the main thread
-    // will incur file IO some of the time.
-    if (!mReqContentLengthDetermined) {
-        // C-L might be determined sync or async. Sync will set
-        // mReqContentLengthDetermined to true in DetermineContentLength()
-        DetermineContentLength();
-    }
-    if (!mReqContentLengthDetermined) {
-        return NS_OK;
-    }
-
     // If we need to start a CORS preflight, do it now!
     // Note that it is important to do this before the early returns below.
     if (!mIsCorsPreflightDone && mRequireCORSPreflight) {
         MOZ_ASSERT(!mPreflightChannel);
         nsresult rv =
             nsCORSListenerProxy::StartCORSPreflight(this, this,
                                                     mUnsafeHeaders,
                                                     getter_AddRefs(mPreflightChannel));
@@ -830,16 +766,22 @@ void
 nsHttpChannel::ReleaseListeners()
 {
     HttpBaseChannel::ReleaseListeners();
     mChannelClassifier = nullptr;
     mWarningReporter = nullptr;
 }
 
 void
+nsHttpChannel::DoAsyncAbort(nsresult aStatus)
+{
+    Unused << AsyncAbort(aStatus);
+}
+
+void
 nsHttpChannel::HandleAsyncRedirect()
 {
     MOZ_ASSERT(!mCallOnResume, "How did that happen?");
 
     if (mSuspendCount) {
         LOG(("Waiting until resume to do async redirect [this=%p]\n", this));
         mCallOnResume = &nsHttpChannel::HandleAsyncRedirect;
         return;
@@ -5832,17 +5774,16 @@ NS_INTERFACE_MAP_BEGIN(nsHttpChannel)
     NS_INTERFACE_MAP_ENTRY(nsIFormPOSTActionChannel)
     NS_INTERFACE_MAP_ENTRY(nsIUploadChannel2)
     NS_INTERFACE_MAP_ENTRY(nsICacheEntryOpenCallback)
     NS_INTERFACE_MAP_ENTRY(nsIHttpChannelInternal)
     NS_INTERFACE_MAP_ENTRY(nsIResumableChannel)
     NS_INTERFACE_MAP_ENTRY(nsITransportEventSink)
     NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
     NS_INTERFACE_MAP_ENTRY(nsIProtocolProxyCallback)
-    NS_INTERFACE_MAP_ENTRY(nsIInputAvailableCallback)
     NS_INTERFACE_MAP_ENTRY(nsIProxiedChannel)
     NS_INTERFACE_MAP_ENTRY(nsIHttpAuthenticableChannel)
     NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheContainer)
     NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheChannel)
     NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback)
     NS_INTERFACE_MAP_ENTRY(nsIThreadRetargetableRequest)
     NS_INTERFACE_MAP_ENTRY(nsIThreadRetargetableStreamListener)
     NS_INTERFACE_MAP_ENTRY(nsIDNSListener)
@@ -5984,16 +5925,20 @@ nsHttpChannel::AsyncOpen(nsIStreamListen
 #ifdef DEBUG
     AssertPrivateBrowsingId();
 #endif
 
     NS_ENSURE_ARG_POINTER(listener);
     NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
     NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
 
+    if (MaybeWaitForUploadStreamLength(listener, context)) {
+        return NS_OK;
+    }
+
     nsresult rv;
 
     MOZ_ASSERT(NS_IsMainThread());
 
     if (!gHttpHandler->Active()) {
         LOG(("  after HTTP shutdown..."));
         ReleaseListeners();
         return NS_ERROR_NOT_AVAILABLE;
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
@@ -68,17 +68,16 @@ public:
 
 class nsHttpChannel final : public HttpBaseChannel
                           , public HttpAsyncAborter<nsHttpChannel>
                           , public nsIStreamListener
                           , public nsICachingChannel
                           , public nsICacheEntryOpenCallback
                           , public nsITransportEventSink
                           , public nsIProtocolProxyCallback
-                          , public nsIInputAvailableCallback
                           , public nsIHttpAuthenticableChannel
                           , public nsIApplicationCacheChannel
                           , public nsIAsyncVerifyRedirectCallback
                           , public nsIThreadRetargetableRequest
                           , public nsIThreadRetargetableStreamListener
                           , public nsIDNSListener
                           , public nsSupportsWeakReference
                           , public nsICorsPreflightCallback
@@ -92,17 +91,16 @@ public:
     NS_DECL_NSIREQUESTOBSERVER
     NS_DECL_NSISTREAMLISTENER
     NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
     NS_DECL_NSICACHEINFOCHANNEL
     NS_DECL_NSICACHINGCHANNEL
     NS_DECL_NSICACHEENTRYOPENCALLBACK
     NS_DECL_NSITRANSPORTEVENTSINK
     NS_DECL_NSIPROTOCOLPROXYCALLBACK
-    NS_DECL_NSIINPUTAVAILABLECALLBACK
     NS_DECL_NSIPROXIEDCHANNEL
     NS_DECL_NSIAPPLICATIONCACHECONTAINER
     NS_DECL_NSIAPPLICATIONCACHECHANNEL
     NS_DECL_NSIASYNCVERIFYREDIRECTCALLBACK
     NS_DECL_NSITHREADRETARGETABLEREQUEST
     NS_DECL_NSIDNSLISTENER
     NS_DECL_NSICHANNELWITHDIVERTABLEPARENTLISTENER
     NS_DECLARE_STATIC_IID_ACCESSOR(NS_HTTPCHANNEL_IID)
@@ -394,18 +392,16 @@ private:
 
     MOZ_MUST_USE nsresult DoAuthRetry(nsAHttpConnection *);
 
     void     HandleAsyncRedirectChannelToHttps();
     MOZ_MUST_USE nsresult StartRedirectChannelToHttps();
     MOZ_MUST_USE nsresult ContinueAsyncRedirectChannelToURI(nsresult rv);
     MOZ_MUST_USE nsresult OpenRedirectChannel(nsresult rv);
 
-    void DetermineContentLength();
-
     /**
      * A function that takes care of reading STS and PKP headers and enforcing
      * STS and PKP load rules. After a secure channel is erected, STS and PKP
      * requires the channel to be trusted or any STS or PKP header data on
      * the channel is ignored. This is called from ProcessResponse.
      */
     MOZ_MUST_USE nsresult ProcessSecurityHeaders();
 
@@ -711,16 +707,18 @@ private:
 
 protected:
     virtual void DoNotifyListenerCleanup() override;
 
     // Override ReleaseListeners() because mChannelClassifier only exists
     // in nsHttpChannel and it will be released in ReleaseListeners().
     virtual void ReleaseListeners() override;
 
+    virtual void DoAsyncAbort(nsresult aStatus) override;
+
 private: // cache telemetry
     bool mDidReval;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsHttpChannel, NS_HTTPCHANNEL_IID)
 } // namespace net
 } // namespace mozilla
 
new file mode 100644
--- /dev/null