Bug 1444956 - Support BinAST decoding in the script loader r=baku
authorJon Coppeard <jcoppeard@mozilla.com>
Wed, 16 May 2018 15:58:13 +0100
changeset 418577 f0433be07dc594b15ad488cb235bbf60b99c004f
parent 418522 eac1b2073bf4985fa4d521bb952cc1305813d6f3
child 418578 96819f2142ae4142d3661a49e0423c7791be6433
push id34007
push usercsabou@mozilla.com
push dateThu, 17 May 2018 09:47:02 +0000
treeherdermozilla-central@8fb36531f7d0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku
bugs1444956
milestone62.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1444956 - Support BinAST decoding in the script loader r=baku
dom/base/DOMPrefsInternal.h
dom/base/nsJSUtils.cpp
dom/base/nsJSUtils.h
dom/script/ScriptLoadHandler.cpp
dom/script/ScriptLoadRequest.cpp
dom/script/ScriptLoadRequest.h
dom/script/ScriptLoader.cpp
dom/script/ScriptLoader.h
js/src/jsapi.cpp
js/src/jsapi.h
modules/libpref/init/all.js
netwerk/mime/nsMimeTypes.h
testing/web-platform/meta/MANIFEST.json
testing/web-platform/mozilla/meta/MANIFEST.json
testing/web-platform/mozilla/meta/binast/__dir__.ini
testing/web-platform/mozilla/meta/binast/large.https.html.ini
testing/web-platform/mozilla/meta/binast/small.https.html.ini
testing/web-platform/mozilla/tests/binast/insecure.html
testing/web-platform/mozilla/tests/binast/large.binjs
testing/web-platform/mozilla/tests/binast/large.https.html
testing/web-platform/mozilla/tests/binast/large.js
testing/web-platform/mozilla/tests/binast/serve.py
testing/web-platform/mozilla/tests/binast/small.binjs
testing/web-platform/mozilla/tests/binast/small.https.html
testing/web-platform/mozilla/tests/binast/small.js
--- a/dom/base/DOMPrefsInternal.h
+++ b/dom/base/DOMPrefsInternal.h
@@ -37,16 +37,19 @@ DOM_PREF(OffscreenCanvasEnabled, "gfx.of
 DOM_PREF(WebkitBlinkDirectoryPickerEnabled, "dom.webkitBlink.dirPicker.enabled")
 DOM_PREF(NetworkInformationEnabled, "dom.netinfo.enabled")
 DOM_PREF(FetchObserverEnabled, "dom.fetchObserver.enabled")
 DOM_PREF(ResistFingerprintingEnabled, "privacy.resistFingerprinting")
 DOM_PREF(EnableAutoDeclineCanvasPrompts, "privacy.resistFingerprinting.autoDeclineNoUserInputCanvasPrompts")
 DOM_PREF(DevToolsEnabled, "devtools.enabled")
 DOM_PREF(PerformanceObserverEnabled, "dom.enable_performance_observer")
 DOM_PREF(IndexedDBStorageOptionsEnabled, "dom.indexedDB.storageOption.enabled")
+#ifdef JS_BUILD_BINAST
+DOM_PREF(BinASTEncodingEnabled, "dom.script_loader.binast_encoding.enabled")
+#endif
 
 DOM_WEBIDL_PREF(ImageBitmapExtensionsEnabled)
 DOM_WEBIDL_PREF(DOMCachesEnabled)
 DOM_WEBIDL_PREF(NotificationEnabledInServiceWorkers)
 DOM_WEBIDL_PREF(NotificationRIEnabled)
 DOM_WEBIDL_PREF(ServiceWorkersEnabled)
 DOM_WEBIDL_PREF(StorageManagerEnabled)
 DOM_WEBIDL_PREF(PromiseRejectionEventsEnabled)
--- a/dom/base/nsJSUtils.cpp
+++ b/dom/base/nsJSUtils.cpp
@@ -340,16 +340,101 @@ nsJSUtils::ExecutionContext::DecodeJoinA
     mRv = EvaluationExceptionToNSResult(mCx);
     return mRv;
   }
 
   return NS_OK;
 }
 
 nsresult
+nsJSUtils::ExecutionContext::DecodeBinASTJoinAndExec(JS::OffThreadToken** aOffThreadToken,
+                                                     JS::MutableHandle<JSScript*> aScript)
+{
+#ifdef JS_BUILD_BINAST
+  if (mSkip) {
+    return mRv;
+  }
+
+  MOZ_ASSERT(!mWantsReturnValue);
+  MOZ_ASSERT(!mExpectScopeChain);
+
+  aScript.set(JS::FinishOffThreadBinASTDecode(mCx, *aOffThreadToken));
+  *aOffThreadToken = nullptr; // Mark the token as having been finished.
+
+  if (!aScript) {
+    mSkip = true;
+    mRv = EvaluationExceptionToNSResult(mCx);
+    return mRv;
+  }
+
+  if (mEncodeBytecode && !StartIncrementalEncoding(mCx, aScript)) {
+    mSkip = true;
+    mRv = EvaluationExceptionToNSResult(mCx);
+    return mRv;
+  }
+
+  if (!JS_ExecuteScript(mCx, mScopeChain, aScript)) {
+    mSkip = true;
+    mRv = EvaluationExceptionToNSResult(mCx);
+    return mRv;
+  }
+
+  return NS_OK;
+#else
+  return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+nsresult
+nsJSUtils::ExecutionContext::DecodeBinASTAndExec(JS::CompileOptions& aCompileOptions,
+                                                 const uint8_t* aBuf, size_t aLength,
+                                                 JS::MutableHandle<JSScript*> aScript)
+{
+#ifdef JS_BUILD_BINAST
+  MOZ_ASSERT(mScopeChain.length() == 0,
+             "BinAST decoding is not supported in non-syntactic scopes");
+
+  if (mSkip) {
+    return mRv;
+  }
+
+  MOZ_ASSERT(aBuf);
+  MOZ_ASSERT(mRetValue.isUndefined());
+#ifdef DEBUG
+  mWantsReturnValue = !aCompileOptions.noScriptRval;
+#endif
+
+  aScript.set(JS::DecodeBinAST(mCx, aCompileOptions, aBuf, aLength));
+
+  if (!aScript) {
+    mSkip = true;
+    mRv = EvaluationExceptionToNSResult(mCx);
+    return mRv;
+  }
+
+  if (mEncodeBytecode && !StartIncrementalEncoding(mCx, aScript)) {
+    mSkip = true;
+    mRv = EvaluationExceptionToNSResult(mCx);
+    return mRv;
+  }
+
+  MOZ_ASSERT(!mCoerceToString || mWantsReturnValue);
+  if (!JS_ExecuteScript(mCx, mScopeChain, aScript, &mRetValue)) {
+    mSkip = true;
+    mRv = EvaluationExceptionToNSResult(mCx);
+    return mRv;
+  }
+
+  return NS_OK;
+#else
+  return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+nsresult
 nsJSUtils::ExecutionContext::ExtractReturnValue(JS::MutableHandle<JS::Value> aRetValue)
 {
   MOZ_ASSERT(aRetValue.isUndefined());
   if (mSkip) {
     // Repeat earlier result, as NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW are not
     // failures cases.
 #ifdef DEBUG
     mWantsReturnValue = false;
--- a/dom/base/nsJSUtils.h
+++ b/dom/base/nsJSUtils.h
@@ -174,16 +174,24 @@ public:
     MOZ_MUST_USE nsresult DecodeAndExec(JS::CompileOptions& aCompileOptions,
                                         mozilla::Vector<uint8_t>& aBytecodeBuf,
                                         size_t aBytecodeIndex);
 
     // After getting a notification that an off-thread decoding terminated, this
     // function will get the result of the decoder by moving it to the main
     // thread before starting the execution of the script.
     MOZ_MUST_USE nsresult DecodeJoinAndExec(JS::OffThreadToken** aOffThreadToken);
+
+    MOZ_MUST_USE nsresult DecodeBinASTJoinAndExec(JS::OffThreadToken** aOffThreadToken,
+                                                  JS::MutableHandle<JSScript*> aScript);
+
+    // Decode a BinAST encoded script contained in a buffer, and execute it.
+    nsresult DecodeBinASTAndExec(JS::CompileOptions& aCompileOptions,
+                                 const uint8_t* aBuf, size_t aLength,
+                                 JS::MutableHandle<JSScript*> aScript);
   };
 
   static nsresult CompileModule(JSContext* aCx,
                                 JS::SourceBufferHolder& aSrcBuf,
                                 JS::Handle<JSObject*> aEvaluationGlobal,
                                 JS::CompileOptions &aCompileOptions,
                                 JS::MutableHandle<JSObject*> aModule);
 
--- a/dom/script/ScriptLoadHandler.cpp
+++ b/dom/script/ScriptLoadHandler.cpp
@@ -4,16 +4,19 @@
  * 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 "ScriptLoadHandler.h"
 #include "ScriptLoader.h"
 #include "ScriptTrace.h"
 
 #include "nsContentUtils.h"
+#include "nsIEncodedChannel.h"
+#include "nsIStringEnumerator.h"
+#include "nsMimeTypes.h"
 
 #include "mozilla/Telemetry.h"
 
 namespace mozilla {
 namespace dom {
 
 #undef LOG
 #define LOG(args) \
@@ -54,33 +57,45 @@ ScriptLoadHandler::OnIncrementalData(nsI
   }
 
   nsresult rv = NS_OK;
   if (mRequest->IsUnknownDataType()) {
     rv = EnsureKnownDataType(aLoader);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
-  if (mRequest->IsSource()) {
+  if (mRequest->IsTextSource()) {
     if (!EnsureDecoder(aLoader, aData, aDataLength,
                        /* aEndOfStream = */ false)) {
       return NS_OK;
     }
 
     // Below we will/shall consume entire data chunk.
     *aConsumedLength = aDataLength;
 
     // Decoder has already been initialized. -- trying to decode all loaded bytes.
     rv = DecodeRawData(aData, aDataLength, /* aEndOfStream = */ false);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // If SRI is required for this load, appending new bytes to the hash.
     if (mSRIDataVerifier && NS_SUCCEEDED(mSRIStatus)) {
       mSRIStatus = mSRIDataVerifier->Update(aDataLength, aData);
     }
+  } else if (mRequest->IsBinASTSource()) {
+    if (!mRequest->ScriptBinASTData().append(aData, aDataLength)) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+
+    // Below we will/shall consume entire data chunk.
+    *aConsumedLength = aDataLength;
+
+    // If SRI is required for this load, appending new bytes to the hash.
+    if (mSRIDataVerifier && NS_SUCCEEDED(mSRIStatus)) {
+      mSRIStatus = mSRIDataVerifier->Update(aDataLength, aData);
+    }
   } else {
     MOZ_ASSERT(mRequest->IsBytecode());
     if (!mRequest->mScriptBytecode.append(aData, aDataLength)) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
 
     *aConsumedLength = aDataLength;
     rv = MaybeDecodeSRI();
@@ -99,41 +114,41 @@ ScriptLoadHandler::DecodeRawData(const u
                                  uint32_t aDataLength,
                                  bool aEndOfStream)
 {
   CheckedInt<size_t> needed = mDecoder->MaxUTF16BufferLength(aDataLength);
   if (!needed.isValid()) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
-  uint32_t haveRead = mRequest->mScriptText.length();
+  uint32_t haveRead = mRequest->ScriptText().length();
 
   CheckedInt<uint32_t> capacity = haveRead;
   capacity += needed.value();
 
-  if (!capacity.isValid() || !mRequest->mScriptText.reserve(capacity.value())) {
+  if (!capacity.isValid() || !mRequest->ScriptText().reserve(capacity.value())) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   uint32_t result;
   size_t read;
   size_t written;
   bool hadErrors;
   Tie(result, read, written, hadErrors) = mDecoder->DecodeToUTF16(
     MakeSpan(aData, aDataLength),
-    MakeSpan(mRequest->mScriptText.begin() + haveRead, needed.value()),
+    MakeSpan(mRequest->ScriptText().begin() + haveRead, needed.value()),
     aEndOfStream);
   MOZ_ASSERT(result == kInputEmpty);
   MOZ_ASSERT(read == aDataLength);
   MOZ_ASSERT(written <= needed.value());
   Unused << hadErrors;
 
   haveRead += written;
   MOZ_ASSERT(haveRead <= capacity.value(), "mDecoder produced more data than expected");
-  MOZ_ALWAYS_TRUE(mRequest->mScriptText.resizeUninitialized(haveRead));
+  MOZ_ALWAYS_TRUE(mRequest->ScriptText().resizeUninitialized(haveRead));
 
   return NS_OK;
 }
 
 bool
 ScriptLoadHandler::EnsureDecoder(nsIIncrementalStreamLoader* aLoader,
                                  const uint8_t* aData,
                                  uint32_t aDataLength,
@@ -268,43 +283,62 @@ ScriptLoadHandler::MaybeDecodeSRI()
   return NS_OK;
 }
 
 nsresult
 ScriptLoadHandler::EnsureKnownDataType(nsIIncrementalStreamLoader* aLoader)
 {
   MOZ_ASSERT(mRequest->IsUnknownDataType());
   MOZ_ASSERT(mRequest->IsLoading());
-  if (mRequest->IsLoadingSource()) {
-    mRequest->mDataType = ScriptLoadRequest::DataType::eSource;
-    TRACE_FOR_TEST(mRequest->mElement, "scriptloader_load_source");
-    return NS_OK;
-  }
 
   nsCOMPtr<nsIRequest> req;
   nsresult rv = aLoader->GetRequest(getter_AddRefs(req));
   MOZ_ASSERT(req, "StreamLoader's request went away prematurely");
   NS_ENSURE_SUCCESS(rv, rv);
 
+  if (ScriptLoader::BinASTEncodingEnabled()) {
+    nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(req);
+    if (httpChannel) {
+      nsAutoCString mimeType;
+      httpChannel->GetContentType(mimeType);
+      if (mimeType.LowerCaseEqualsASCII(APPLICATION_JAVASCRIPT_BINAST)) {
+        if (mRequest->ShouldAcceptBinASTEncoding()) {
+          mRequest->SetBinASTSource();
+          TRACE_FOR_TEST(mRequest->mElement, "scriptloader_load_source");
+          return NS_OK;
+        } else {
+          return NS_ERROR_FAILURE;
+        }
+      }
+    }
+  }
+
+  if (mRequest->IsLoadingSource()) {
+    mRequest->SetTextSource();
+    TRACE_FOR_TEST(mRequest->mElement, "scriptloader_load_source");
+    return NS_OK;
+  }
+
   nsCOMPtr<nsICacheInfoChannel> cic(do_QueryInterface(req));
   if (cic) {
     nsAutoCString altDataType;
     cic->GetAlternativeDataType(altDataType);
     if (altDataType.Equals(nsContentUtils::JSBytecodeMimeType())) {
-      mRequest->mDataType = ScriptLoadRequest::DataType::eBytecode;
+      mRequest->SetBytecode();
       TRACE_FOR_TEST(mRequest->mElement, "scriptloader_load_bytecode");
     } else {
       MOZ_ASSERT(altDataType.IsEmpty());
-      mRequest->mDataType = ScriptLoadRequest::DataType::eSource;
+      mRequest->SetTextSource();
       TRACE_FOR_TEST(mRequest->mElement, "scriptloader_load_source");
     }
   } else {
-    mRequest->mDataType = ScriptLoadRequest::DataType::eSource;
+    mRequest->SetTextSource();
     TRACE_FOR_TEST(mRequest->mElement, "scriptloader_load_source");
   }
+
   MOZ_ASSERT(!mRequest->IsUnknownDataType());
   MOZ_ASSERT(mRequest->IsLoading());
   return NS_OK;
 }
 
 NS_IMETHODIMP
 ScriptLoadHandler::OnStreamComplete(nsIIncrementalStreamLoader* aLoader,
                                     nsISupports* aContext,
@@ -324,25 +358,34 @@ ScriptLoadHandler::OnStreamComplete(nsII
   aLoader->GetRequest(getter_AddRefs(channelRequest));
 
   if (!mRequest->IsCanceled()) {
     if (mRequest->IsUnknownDataType()) {
       rv = EnsureKnownDataType(aLoader);
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
-    if (mRequest->IsSource()) {
+    if (mRequest->IsTextSource()) {
       DebugOnly<bool> encoderSet =
         EnsureDecoder(aLoader, aData, aDataLength, /* aEndOfStream = */ true);
       MOZ_ASSERT(encoderSet);
       rv = DecodeRawData(aData, aDataLength, /* aEndOfStream = */ true);
       NS_ENSURE_SUCCESS(rv, rv);
 
       LOG(("ScriptLoadRequest (%p): Source length = %u",
-           mRequest.get(), unsigned(mRequest->mScriptText.length())));
+           mRequest.get(), unsigned(mRequest->ScriptText().length())));
+
+      // If SRI is required for this load, appending new bytes to the hash.
+      if (mSRIDataVerifier && NS_SUCCEEDED(mSRIStatus)) {
+        mSRIStatus = mSRIDataVerifier->Update(aDataLength, aData);
+      }
+    } else if (mRequest->IsBinASTSource()) {
+      if (!mRequest->ScriptBinASTData().append(aData, aDataLength)) {
+        return NS_ERROR_OUT_OF_MEMORY;
+      }
 
       // If SRI is required for this load, appending new bytes to the hash.
       if (mSRIDataVerifier && NS_SUCCEEDED(mSRIStatus)) {
         mSRIStatus = mSRIDataVerifier->Update(aDataLength, aData);
       }
     } else {
       MOZ_ASSERT(mRequest->IsBytecode());
       if (!mRequest->mScriptBytecode.append(aData, aDataLength)) {
--- a/dom/script/ScriptLoadRequest.cpp
+++ b/dom/script/ScriptLoadRequest.cpp
@@ -1,16 +1,19 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ModuleLoadRequest.h"
+
 #include "mozilla/HoldDropJSObjects.h"
+#include "mozilla/Unused.h"
+
 #include "nsICacheInfoChannel.h"
 #include "ScriptLoadRequest.h"
 #include "ScriptSettings.h"
 
 namespace mozilla {
 namespace dom {
 
 //////////////////////////////////////////////////////////////
@@ -58,17 +61,16 @@ ScriptLoadRequest::ScriptLoadRequest(Scr
   , mInDeferList(false)
   , mInAsyncList(false)
   , mIsNonAsyncScriptInserted(false)
   , mIsXSLT(false)
   , mIsCanceled(false)
   , mWasCompiledOMT(false)
   , mIsTracking(false)
   , mOffThreadToken(nullptr)
-  , mScriptText()
   , mScriptBytecode()
   , mBytecodeOffset(0)
   , mURI(aURI)
   , mLineNo(1)
   , mCORSMode(aCORSMode)
   , mIntegrity(aIntegrity)
   , mReferrer(aReferrer)
   , mReferrerPolicy(aReferrerPolicy)
@@ -147,16 +149,77 @@ ScriptLoadRequest::SetScriptMode(bool aD
     mScriptMode = ScriptMode::eAsync;
   } else if (aDeferAttr || IsModuleRequest()) {
     mScriptMode = ScriptMode::eDeferred;
   } else {
     mScriptMode = ScriptMode::eBlocking;
   }
 }
 
+void
+ScriptLoadRequest::SetUnknownDataType()
+{
+  mDataType = DataType::eUnknown;
+  mScriptData.reset();
+}
+
+void
+ScriptLoadRequest::SetTextSource()
+{
+  MOZ_ASSERT(IsUnknownDataType());
+  mDataType = DataType::eTextSource;
+  mScriptData.emplace(VariantType<Vector<char16_t>>());
+}
+
+void
+ScriptLoadRequest::SetBinASTSource()
+{
+#ifdef JS_BUILD_BINAST
+  MOZ_ASSERT(IsUnknownDataType());
+  mDataType = DataType::eBinASTSource;
+  mScriptData.emplace(VariantType<Vector<uint8_t>>());
+#else
+  MOZ_CRASH("BinAST not supported");
+#endif
+}
+
+void
+ScriptLoadRequest::SetBytecode()
+{
+  MOZ_ASSERT(IsUnknownDataType());
+  mDataType = DataType::eBytecode;
+}
+
+bool
+ScriptLoadRequest::ShouldAcceptBinASTEncoding() const
+{
+#ifdef JS_BUILD_BINAST
+  // We accept the BinAST encoding if we're using a secure connection.
+
+  bool isHTTPS = false;
+  nsresult rv = mURI->SchemeIs("https", &isHTTPS);
+  MOZ_ASSERT(NS_SUCCEEDED(rv));
+  Unused << rv;
+
+  return isHTTPS;
+#else
+  MOZ_CRASH("BinAST not supported");
+#endif
+}
+
+void
+ScriptLoadRequest::ClearScriptSource()
+{
+  if (IsTextSource()) {
+    ScriptText().clearAndFree();
+  } else if (IsBinASTSource()) {
+    ScriptBinASTData().clearAndFree();
+  }
+}
+
 //////////////////////////////////////////////////////////////
 // ScriptLoadRequestList
 //////////////////////////////////////////////////////////////
 
 ScriptLoadRequestList::~ScriptLoadRequestList()
 {
   Clear();
 }
--- a/dom/script/ScriptLoadRequest.h
+++ b/dom/script/ScriptLoadRequest.h
@@ -5,17 +5,19 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_ScriptLoadRequest_h
 #define mozilla_dom_ScriptLoadRequest_h
 
 #include "mozilla/CORSMode.h"
 #include "mozilla/dom/SRIMetadata.h"
 #include "mozilla/LinkedList.h"
+#include "mozilla/Maybe.h"
 #include "mozilla/net/ReferrerPolicy.h"
+#include "mozilla/Variant.h"
 #include "mozilla/Vector.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIScriptElement.h"
 
 class nsICacheInfoChannel;
 
 namespace mozilla {
@@ -129,33 +131,68 @@ public:
   {
     return mProgress == Progress::eCompiling ||
            (IsReadyToRun() && mWasCompiledOMT);
   }
 
   // Type of data provided by the nsChannel.
   enum class DataType : uint8_t {
     eUnknown,
-    eSource,
+    eTextSource,
+    eBinASTSource,
     eBytecode
   };
 
   bool IsUnknownDataType() const
   {
     return mDataType == DataType::eUnknown;
   }
+  bool IsTextSource() const
+  {
+    return mDataType == DataType::eTextSource;
+  }
+  bool IsBinASTSource() const
+  {
+#ifdef JS_BUILD_BINAST
+    return mDataType == DataType::eBinASTSource;
+#else
+    return false;
+#endif
+  }
   bool IsSource() const
   {
-    return mDataType == DataType::eSource;
+    return IsTextSource() || IsBinASTSource();
   }
   bool IsBytecode() const
   {
     return mDataType == DataType::eBytecode;
   }
 
+  void SetUnknownDataType();
+  void SetTextSource();
+  void SetBinASTSource();
+  void SetBytecode();
+
+  const Vector<char16_t>& ScriptText() const {
+    MOZ_ASSERT(IsTextSource());
+    return mScriptData->as<Vector<char16_t>>();
+  }
+  Vector<char16_t>& ScriptText() {
+    MOZ_ASSERT(IsTextSource());
+    return mScriptData->as<Vector<char16_t>>();
+  }
+  const Vector<uint8_t>& ScriptBinASTData() const {
+    MOZ_ASSERT(IsBinASTSource());
+    return mScriptData->as<Vector<uint8_t>>();
+  }
+  Vector<uint8_t>& ScriptBinASTData() {
+    MOZ_ASSERT(IsBinASTSource());
+    return mScriptData->as<Vector<uint8_t>>();
+  }
+
   enum class ScriptMode : uint8_t {
     eBlocking,
     eDeferred,
     eAsync
   };
 
   void SetScriptMode(bool aDeferAttr, bool aAsyncAttr);
 
@@ -175,16 +212,19 @@ public:
   }
 
   virtual bool IsTopLevel() const
   {
     // Classic scripts are always top level.
     return true;
   }
 
+  bool ShouldAcceptBinASTEncoding() const;
+
+  void ClearScriptSource();
 
   void MaybeCancelOffThreadScript();
   void DropBytecodeCacheReferences();
 
   using super::getNext;
   using super::isInList;
 
   const ScriptKind mKind;
@@ -204,19 +244,20 @@ public:
   bool mIsTracking;       // True if the script comes from a source on our tracking protection list.
   JS::OffThreadToken* mOffThreadToken; // Off-thread parsing token.
   nsString mSourceMapURL; // Holds source map url for loaded scripts
 
   // Holds the top-level JSScript that corresponds to the current source, once
   // it is parsed, and planned to be saved in the bytecode cache.
   JS::Heap<JSScript*> mScript;
 
-  // Holds script text for non-inline scripts. Don't use nsString so we can give
-  // ownership to jsapi.
-  mozilla::Vector<char16_t> mScriptText;
+  // Holds script source data for non-inline scripts. Don't use nsString so we
+  // can give ownership to jsapi. Holds either char16_t source text characters
+  // or BinAST encoded bytes depending on mSourceEncoding.
+  Maybe<Variant<Vector<char16_t>, Vector<uint8_t>>> mScriptData;
 
   // Holds the SRI serialized hash and the script bytecode for non-inline
   // scripts.
   mozilla::Vector<uint8_t> mScriptBytecode;
   uint32_t mBytecodeOffset; // Offset of the bytecode in mScriptBytecode
 
   const nsCOMPtr<nsIURI> mURI;
   nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
--- a/dom/script/ScriptLoader.cpp
+++ b/dom/script/ScriptLoader.cpp
@@ -48,16 +48,17 @@
 #include "nsIContentSecurityPolicy.h"
 #include "mozilla/Logging.h"
 #include "nsCRT.h"
 #include "nsContentCreatorFunctions.h"
 #include "nsProxyRelease.h"
 #include "nsSandboxFlags.h"
 #include "nsContentTypeParser.h"
 #include "nsINetworkPredictor.h"
+#include "nsMimeTypes.h"
 #include "mozilla/ConsoleReportCollector.h"
 #include "mozilla/LoadInfo.h"
 
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Unused.h"
@@ -211,30 +212,31 @@ CollectScriptTelemetry(nsIIncrementalStr
 
   // Report the type of source, as well as the size of the source.
   if (aRequest->IsLoadingSource()) {
     if (aRequest->mIsInline) {
       AccumulateCategorical(LABELS_DOM_SCRIPT_LOADING_SOURCE::Inline);
       nsAutoString inlineData;
       aRequest->mElement->GetScriptText(inlineData);
       Accumulate(DOM_SCRIPT_INLINE_SIZE, inlineData.Length());
-    } else {
+    } else if (aRequest->IsTextSource()) {
       AccumulateCategorical(LABELS_DOM_SCRIPT_LOADING_SOURCE::SourceFallback);
-      Accumulate(DOM_SCRIPT_SOURCE_SIZE, aRequest->mScriptText.length());
+      Accumulate(DOM_SCRIPT_SOURCE_SIZE, aRequest->ScriptText().length());
     }
+    // TODO: Add telemetry for BinAST encoded source.
   } else {
     MOZ_ASSERT(aRequest->IsLoading());
-    if (aRequest->IsSource()) {
+    if (aRequest->IsTextSource()) {
       AccumulateCategorical(LABELS_DOM_SCRIPT_LOADING_SOURCE::Source);
-      Accumulate(DOM_SCRIPT_SOURCE_SIZE, aRequest->mScriptText.length());
-    } else {
-      MOZ_ASSERT(aRequest->IsBytecode());
+      Accumulate(DOM_SCRIPT_SOURCE_SIZE, aRequest->ScriptText().length());
+    } else if (aRequest->IsBytecode()) {
       AccumulateCategorical(LABELS_DOM_SCRIPT_LOADING_SOURCE::AltData);
       Accumulate(DOM_SCRIPT_BYTECODE_SIZE, aRequest->mScriptBytecode.length());
     }
+    // TODO: Add telemetry for BinAST encoded source.
   }
 
   // Skip if we do not have any cache information for the given script.
   if (!aLoader) {
     return;
   }
   nsCOMPtr<nsIRequest> channel;
   aLoader->GetRequest(getter_AddRefs(channel));
@@ -442,17 +444,17 @@ ScriptLoader::GetFetchedModule(nsIURI* a
 nsresult
 ScriptLoader::ProcessFetchedModuleSource(ModuleLoadRequest* aRequest)
 {
   MOZ_ASSERT(!aRequest->mModuleScript);
 
   nsresult rv = CreateModuleScript(aRequest);
   MOZ_ASSERT(NS_FAILED(rv) == !aRequest->mModuleScript);
 
-  aRequest->mScriptText.clearAndFree();
+  aRequest->ClearScriptSource();
 
   if (NS_FAILED(rv)) {
     aRequest->LoadFailed();
     return rv;
   }
 
   if (!aRequest->mIsInline) {
     SetModuleFetchFinishedAndResumeWaitingRequests(aRequest, rv);
@@ -971,17 +973,17 @@ ScriptLoader::RestartLoad(ScriptLoadRequ
   return NS_BINDING_RETARGETED;
 }
 
 nsresult
 ScriptLoader::StartLoad(ScriptLoadRequest* aRequest)
 {
   MOZ_ASSERT(aRequest->IsLoading());
   NS_ENSURE_TRUE(mDocument, NS_ERROR_NULL_POINTER);
-  aRequest->mDataType = ScriptLoadRequest::DataType::eUnknown;
+  aRequest->SetUnknownDataType();
 
   // If this document is sandboxed without 'allow-scripts', abort.
   if (mDocument->HasScriptsBlockedBySandbox()) {
     return NS_OK;
   }
 
   if (LOG_ENABLED()) {
     nsAutoCString url;
@@ -1116,20 +1118,24 @@ ScriptLoader::StartLoad(ScriptLoadReques
         cos->AddClassFlags(nsIClassOfService::TailAllowed);
       }
     }
   }
 
   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
   if (httpChannel) {
     // HTTP content negotation has little value in this context.
+    nsAutoCString acceptTypes("*/*");
+    if (BinASTEncodingEnabled() && aRequest->ShouldAcceptBinASTEncoding()) {
+      acceptTypes = APPLICATION_JAVASCRIPT_BINAST ", */*";
+    }
     rv = httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
-                                       NS_LITERAL_CSTRING("*/*"),
-                                       false);
+                                       acceptTypes, false);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
+
     rv = httpChannel->SetReferrerWithPolicy(aRequest->mReferrer,
                                             aRequest->mReferrerPolicy);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
 
     nsCOMPtr<nsIHttpChannelInternal> internalChannel(do_QueryInterface(httpChannel));
     if (internalChannel) {
       rv = internalChannel->SetIntegrityMetadata(aRequest->mIntegrity.GetIntegrityString());
       MOZ_ASSERT(NS_SUCCEEDED(rv));
@@ -1506,17 +1512,17 @@ ScriptLoader::ProcessInlineScript(nsIScr
     CreateLoadRequest(aScriptKind, mDocument->GetDocumentURI(), aElement,
                       corsMode,
                       SRIMetadata(), // SRI doesn't apply
                       mDocument->GetReferrerPolicy());
   request->mIsInline = true;
   request->mTriggeringPrincipal = mDocument->NodePrincipal();
   request->mLineNo = aElement->GetScriptLineNumber();
   request->mProgress = ScriptLoadRequest::Progress::eLoading_Source;
-  request->mDataType = ScriptLoadRequest::DataType::eSource;
+  request->SetTextSource();
   TRACE_FOR_TEST_BOOL(request->mElement, "scriptloader_load_source");
   CollectScriptTelemetry(nullptr, request);
 
   // Only the 'async' attribute is heeded on an inline module script and
   // inline classic scripts ignore both these attributes.
   MOZ_ASSERT(!aElement->GetScriptDeferred());
   MOZ_ASSERT_IF(!request->IsModuleRequest(), !aElement->GetScriptAsync());
   request->SetScriptMode(false, aElement->GetScriptAsync());
@@ -1777,56 +1783,74 @@ ScriptLoader::AttemptAsyncScriptCompile(
   JS::Rooted<JSObject*> global(cx, globalObject->GetGlobalJSObject());
   JS::CompileOptions options(cx);
 
   nsresult rv = FillCompileOptionsForRequest(jsapi, aRequest, global, &options);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  if (aRequest->IsSource()) {
-    if (!JS::CanCompileOffThread(cx, options, aRequest->mScriptText.length())) {
+  if (aRequest->IsTextSource()) {
+    if (!JS::CanCompileOffThread(cx, options, aRequest->ScriptText().length())) {
       return NS_ERROR_FAILURE;
     }
+#ifdef JS_BUILD_BINAST
+  } else if (aRequest->IsBinASTSource()) {
+    if (!JS::CanDecodeBinASTOffThread(cx, options, aRequest->ScriptBinASTData().length())) {
+      return NS_ERROR_FAILURE;
+    }
+#endif
   } else {
+    MOZ_ASSERT(aRequest->IsBytecode());
     size_t length = aRequest->mScriptBytecode.length() - aRequest->mBytecodeOffset;
     if (!JS::CanDecodeOffThread(cx, options, length)) {
       return NS_ERROR_FAILURE;
     }
   }
 
   RefPtr<NotifyOffThreadScriptLoadCompletedRunnable> runnable =
     new NotifyOffThreadScriptLoadCompletedRunnable(aRequest, this);
 
   if (aRequest->IsModuleRequest()) {
-    MOZ_ASSERT(aRequest->IsSource());
+    MOZ_ASSERT(aRequest->IsTextSource());
     if (!JS::CompileOffThreadModule(cx, options,
-                                    aRequest->mScriptText.begin(),
-                                    aRequest->mScriptText.length(),
+                                    aRequest->ScriptText().begin(),
+                                    aRequest->ScriptText().length(),
                                     OffThreadScriptLoaderCallback,
                                     static_cast<void*>(runnable))) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
-  } else if (aRequest->IsSource()) {
-    if (!JS::CompileOffThread(cx, options,
-                              aRequest->mScriptText.begin(),
-                              aRequest->mScriptText.length(),
-                              OffThreadScriptLoaderCallback,
-                              static_cast<void*>(runnable))) {
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-  } else {
-    MOZ_ASSERT(aRequest->IsBytecode());
+  } else if (aRequest->IsBytecode()) {
     if (!JS::DecodeOffThreadScript(cx, options,
                                    aRequest->mScriptBytecode,
                                    aRequest->mBytecodeOffset,
                                    OffThreadScriptLoaderCallback,
                                    static_cast<void*>(runnable))) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
+#ifdef JS_BUILD_BINAST
+  } else if (aRequest->IsBinASTSource()) {
+    MOZ_ASSERT(aRequest->IsSource());
+    if (!JS::DecodeBinASTOffThread(cx, options,
+                                   aRequest->ScriptBinASTData().begin(),
+                                   aRequest->ScriptBinASTData().length(),
+                                   OffThreadScriptLoaderCallback,
+                                   static_cast<void*>(runnable))) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+#endif
+  } else {
+    MOZ_ASSERT(aRequest->IsTextSource());
+    if (!JS::CompileOffThread(cx, options,
+                              aRequest->ScriptText().begin(),
+                              aRequest->ScriptText().length(),
+                              OffThreadScriptLoaderCallback,
+                              static_cast<void*>(runnable))) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
   }
 
   mDocument->BlockOnload();
 
   // Once the compilation is finished, an event would be added to the event loop
   // to call ScriptLoader::ProcessOffThreadRequest with the same request.
   aRequest->mProgress = ScriptLoadRequest::Progress::eCompiling;
 
@@ -1863,18 +1887,18 @@ ScriptLoader::GetScriptSource(ScriptLoad
     // XXX This is inefficient - GetText makes multiple
     // copies.
     aRequest->mElement->GetScriptText(inlineData);
     return SourceBufferHolder(inlineData.get(),
                               inlineData.Length(),
                               SourceBufferHolder::NoOwnership);
   }
 
-  return SourceBufferHolder(aRequest->mScriptText.begin(),
-                            aRequest->mScriptText.length(),
+  return SourceBufferHolder(aRequest->ScriptText().begin(),
+                            aRequest->ScriptText().length(),
                             SourceBufferHolder::NoOwnership);
 }
 
 nsresult
 ScriptLoader::ProcessRequest(ScriptLoadRequest* aRequest)
 {
   LOG(("ScriptLoadRequest (%p): Process request", aRequest));
 
@@ -1965,17 +1989,17 @@ ScriptLoader::ProcessRequest(ScriptLoadR
     // (disappearing window, some other error, ...). Finish the
     // request to avoid leaks in the JS engine.
     MOZ_ASSERT(!aRequest->IsModuleRequest());
     aRequest->MaybeCancelOffThreadScript();
   }
 
   // Free any source data, but keep the bytecode content as we might have to
   // save it later.
-  aRequest->mScriptText.clearAndFree();
+  aRequest->ClearScriptSource();
   if (aRequest->IsBytecode()) {
     // We received bytecode as input, thus we were decoding, and we will not be
     // encoding the bytecode once more. We can safely clear the content of this
     // buffer.
     aRequest->mScriptBytecode.clearAndFree();
   }
 
   return rv;
@@ -2102,16 +2126,17 @@ ScriptLoader::ShouldCacheBytecode(Script
   // Look at the preference to know which strategy (parameters) should be used
   // when the bytecode cache is enabled.
   int32_t strategy = nsContentUtils::BytecodeCacheStrategy();
 
   // List of parameters used by the strategies.
   bool hasSourceLengthMin = false;
   bool hasFetchCountMin = false;
   size_t sourceLengthMin = 100;
+  size_t binASTLengthMin = 70;
   int32_t fetchCountMin = 4;
 
   LOG(("ScriptLoadRequest (%p): Bytecode-cache: strategy = %d.", aRequest, strategy));
   switch (strategy) {
     case -2: {
       // Reader mode, keep requesting alternate data but no longer save it.
       LOG(("ScriptLoadRequest (%p): Bytecode-cache: Encoding disabled.", aRequest));
       return false;
@@ -2122,29 +2147,42 @@ ScriptLoader::ShouldCacheBytecode(Script
       hasFetchCountMin = false;
       break;
     }
     default:
     case 0: {
       hasSourceLengthMin = true;
       hasFetchCountMin = true;
       sourceLengthMin = 1024;
+      binASTLengthMin = 700;
       // If we were to optimize only for speed, without considering the impact
       // on memory, we should set this threshold to 2. (Bug 900784 comment 120)
       fetchCountMin = 4;
       break;
     }
   }
 
   // If the script is too small/large, do not attempt at creating a bytecode
   // cache for this script, as the overhead of parsing it might not be worth the
   // effort.
-  if (hasSourceLengthMin && aRequest->mScriptText.length() < sourceLengthMin) {
-    LOG(("ScriptLoadRequest (%p): Bytecode-cache: Script is too small.", aRequest));
-    return false;
+  if (hasSourceLengthMin) {
+    size_t sourceLength;
+    size_t minLength;
+    if (aRequest->IsTextSource()) {
+      sourceLength = aRequest->ScriptText().length();
+      minLength = sourceLengthMin;
+    } else {
+      MOZ_ASSERT(aRequest->IsBinASTSource());
+      sourceLength = aRequest->ScriptBinASTData().length();
+      minLength = binASTLengthMin;
+    }
+    if (sourceLength < minLength) {
+      LOG(("ScriptLoadRequest (%p): Bytecode-cache: Script is too small.", aRequest));
+      return false;
+    }
   }
 
   // Check that we loaded the cache entry a few times before attempting any
   // bytecode-cache optimization, such that we do not waste time on entry which
   // are going to be dropped soon.
   if (hasFetchCountMin) {
     int32_t fetchCount = 0;
     if (NS_FAILED(aRequest->mCacheInfo->GetCacheTokenFetchCount(&fetchCount))) {
@@ -2156,16 +2194,30 @@ ScriptLoader::ShouldCacheBytecode(Script
       return false;
     }
   }
 
   LOG(("ScriptLoadRequest (%p): Bytecode-cache: Trigger encoding.", aRequest));
   return true;
 }
 
+static bool
+ShouldRecordParseTimeTelemetry(ScriptLoadRequest* aRequest)
+{
+  static const size_t MinScriptChars = 1024;
+  static const size_t MinBinASTBytes = 700;
+
+  if (aRequest->IsTextSource()) {
+    return aRequest->ScriptText().length() >= MinScriptChars;
+  } else {
+    MOZ_ASSERT(aRequest->IsBinASTSource());
+    return aRequest->ScriptBinASTData().length() >= MinBinASTBytes;
+  }
+}
+
 nsresult
 ScriptLoader::EvaluateScript(ScriptLoadRequest* aRequest)
 {
   using namespace mozilla::Telemetry;
   MOZ_ASSERT(aRequest->IsReadyToRun());
 
   // We need a document to evaluate scripts.
   if (!mDocument) {
@@ -2207,17 +2259,17 @@ ScriptLoader::EvaluateScript(ScriptLoadR
 
   bool oldProcessingScriptTag = context->GetProcessingScriptTag();
   context->SetProcessingScriptTag(true);
   nsresult rv;
   {
     if (aRequest->IsModuleRequest()) {
       // When a module is already loaded, it is not feched a second time and the
       // mDataType of the request might remain set to DataType::Unknown.
-      MOZ_ASSERT(!aRequest->IsBytecode());
+      MOZ_ASSERT(aRequest->IsTextSource() || aRequest->IsUnknownDataType());
       LOG(("ScriptLoadRequest (%p): Evaluate Module", aRequest));
 
       // currentScript is set to null for modules.
       AutoCurrentScriptUpdater scriptUpdater(this, nullptr);
 
       EnsureModuleResolveHook(cx);
 
       ModuleLoadRequest* request = aRequest->AsModuleRequest();
@@ -2279,43 +2331,56 @@ ScriptLoader::EvaluateScript(ScriptLoadR
           MOZ_ASSERT(!aRequest->mCacheInfo);
         } else {
           MOZ_ASSERT(aRequest->IsSource());
           JS::Rooted<JSScript*> script(cx);
           bool encodeBytecode = ShouldCacheBytecode(aRequest);
 
           TimeStamp start;
           if (Telemetry::CanRecordExtended()) {
-            // Only record telemetry for scripts which are above the threshold.
-            if (aRequest->mCacheInfo && aRequest->mScriptText.length() >= 1024) {
-              start = TimeStamp::Now();
+            // Only record telemetry for scripts which are above a threshold.
+            if (aRequest->mCacheInfo && ShouldRecordParseTimeTelemetry(aRequest)) {
+                start = TimeStamp::Now();
             }
           }
 
           {
             nsJSUtils::ExecutionContext exec(cx, global);
             exec.SetEncodeBytecode(encodeBytecode);
             TRACE_FOR_TEST(aRequest->mElement, "scriptloader_execute");
             if (aRequest->mOffThreadToken) {
               // Off-main-thread parsing.
               LOG(("ScriptLoadRequest (%p): Join (off-thread parsing) and Execute",
                    aRequest));
-              rv = exec.JoinAndExec(&aRequest->mOffThreadToken, &script);
+              if (aRequest->IsBinASTSource()) {
+                rv = exec.DecodeBinASTJoinAndExec(&aRequest->mOffThreadToken, &script);
+              } else {
+                MOZ_ASSERT(aRequest->IsTextSource());
+                rv = exec.JoinAndExec(&aRequest->mOffThreadToken, &script);
+              }
               if (start) {
                 AccumulateTimeDelta(encodeBytecode
                                     ? DOM_SCRIPT_OFF_THREAD_PARSE_ENCODE_EXEC_MS
                                     : DOM_SCRIPT_OFF_THREAD_PARSE_EXEC_MS,
                                     start);
               }
             } else {
               // Main thread parsing (inline and small scripts)
               LOG(("ScriptLoadRequest (%p): Compile And Exec", aRequest));
-              nsAutoString inlineData;
-              SourceBufferHolder srcBuf = GetScriptSource(aRequest, inlineData);
-              rv = exec.CompileAndExec(options, srcBuf, &script);
+              if (aRequest->IsBinASTSource()) {
+                rv = exec.DecodeBinASTAndExec(options,
+                                              aRequest->ScriptBinASTData().begin(),
+                                              aRequest->ScriptBinASTData().length(),
+                                              &script);
+              } else {
+                MOZ_ASSERT(aRequest->IsTextSource());
+                nsAutoString inlineData;
+                SourceBufferHolder srcBuf = GetScriptSource(aRequest, inlineData);
+                rv = exec.CompileAndExec(options, srcBuf, &script);
+              }
               if (start) {
                 AccumulateTimeDelta(encodeBytecode
                                     ? DOM_SCRIPT_MAIN_THREAD_PARSE_ENCODE_EXEC_MS
                                     : DOM_SCRIPT_MAIN_THREAD_PARSE_EXEC_MS,
                                     start);
               }
             }
           }
--- a/dom/script/ScriptLoader.h
+++ b/dom/script/ScriptLoader.h
@@ -15,16 +15,17 @@
 #include "nsCycleCollectionParticipant.h"
 #include "nsTArray.h"
 #include "nsAutoPtr.h"
 #include "nsICacheInfoChannel.h"
 #include "nsIDocument.h"
 #include "nsIIncrementalStreamLoader.h"
 #include "nsURIHashKey.h"
 #include "mozilla/CORSMode.h"
+#include "mozilla/dom/DOMPrefs.h"
 #include "mozilla/dom/ScriptLoadRequest.h"
 #include "mozilla/dom/SRIMetadata.h"
 #include "mozilla/dom/SRICheck.h"
 #include "mozilla/MozPromise.h"
 #include "mozilla/net/ReferrerPolicy.h"
 #include "mozilla/Vector.h"
 
 class nsIURI;
@@ -393,16 +394,25 @@ private:
    * Abort the current stream, and re-start with a new load request from scratch
    * without requesting any alternate data. Returns NS_BINDING_RETARGETED on
    * success, as this error code is used to abort the input stream.
    */
   nsresult RestartLoad(ScriptLoadRequest* aRequest);
 
   void HandleLoadError(ScriptLoadRequest *aRequest, nsresult aResult);
 
+  static bool BinASTEncodingEnabled()
+  {
+#ifdef JS_BUILD_BINAST
+    return DOMPrefs::BinASTEncodingEnabled();
+#else
+    return false;
+#endif
+  }
+
   /**
    * Process any pending requests asynchronously (i.e. off an event) if there
    * are any. Note that this is a no-op if there aren't any currently pending
    * requests.
    *
    * This function is virtual to allow cross-library calls to SetEnabled()
    */
   virtual void ProcessPendingRequestsAsync();
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -4262,25 +4262,30 @@ JS::FinishOffThreadBinASTDecode(JSContex
     MOZ_ASSERT(cx);
     MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
     return HelperThreadState().finishBinASTDecodeTask(cx, token);
 }
 
 #endif /* JS_BUILD_BINAST */
 
 enum class OffThread {
-    Compile, Decode,
+    Compile, Decode, DecodeBinAST
 };
 
 static bool
 CanDoOffThread(JSContext* cx, const ReadOnlyCompileOptions& options, size_t length, OffThread what)
 {
     static const size_t TINY_LENGTH = 5 * 1000;
     static const size_t HUGE_SRC_LENGTH = 100 * 1000;
     static const size_t HUGE_BC_LENGTH = 367 * 1000;
+    static const size_t HUGE_BINAST_LENGTH = 70 * 1000;
+
+    // TODO: We can't decode BinAST off main thread until bug 1459555 is fixed.
+    if (what == OffThread::DecodeBinAST)
+        return false;
 
     // These are heuristics which the caller may choose to ignore (e.g., for
     // testing purposes).
     if (!options.forceAsync) {
         // Compiling off the main thread inolves creating a new Zone and other
         // significant overheads.  Don't bother if the script is tiny.
         if (length < TINY_LENGTH)
             return false;
@@ -4288,16 +4293,18 @@ CanDoOffThread(JSContext* cx, const Read
         // If the parsing task would have to wait for GC to complete, it'll probably
         // be faster to just start it synchronously on the main thread unless the
         // script is huge.
         if (OffThreadParsingMustWaitForGC(cx->runtime())) {
             if (what == OffThread::Compile && length < HUGE_SRC_LENGTH)
                 return false;
             if (what == OffThread::Decode && length < HUGE_BC_LENGTH)
                 return false;
+            if (what == OffThread::DecodeBinAST && length < HUGE_BINAST_LENGTH)
+                return false;
         }
     }
 
     return cx->runtime()->canUseParallelParsing() && CanUseExtraThreads();
 }
 
 JS_PUBLIC_API(bool)
 JS::CanCompileOffThread(JSContext* cx, const ReadOnlyCompileOptions& options, size_t length)
@@ -4306,16 +4313,24 @@ JS::CanCompileOffThread(JSContext* cx, c
 }
 
 JS_PUBLIC_API(bool)
 JS::CanDecodeOffThread(JSContext* cx, const ReadOnlyCompileOptions& options, size_t length)
 {
     return CanDoOffThread(cx, options, length, OffThread::Decode);
 }
 
+#ifdef JS_BUILD_BINAST
+JS_PUBLIC_API(bool)
+JS::CanDecodeBinASTOffThread(JSContext* cx, const ReadOnlyCompileOptions& options, size_t length)
+{
+    return CanDoOffThread(cx, options, length, OffThread::DecodeBinAST);
+}
+#endif
+
 JS_PUBLIC_API(bool)
 JS::CompileOffThread(JSContext* cx, const ReadOnlyCompileOptions& options,
                      const char16_t* chars, size_t length,
                      OffThreadCompileCallback callback, void* callbackData)
 {
     MOZ_ASSERT(CanCompileOffThread(cx, options, length));
     return StartOffThreadParseScript(cx, options, chars, length, callback, callbackData);
 }
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -4238,16 +4238,19 @@ extern JS_PUBLIC_API(JSScript*)
 DecodeBinAST(JSContext* cx, const ReadOnlyCompileOptions& options,
              FILE* file);
 
 extern JS_PUBLIC_API(JSScript*)
 DecodeBinAST(JSContext* cx, const ReadOnlyCompileOptions& options,
              const uint8_t* buf, size_t length);
 
 extern JS_PUBLIC_API(bool)
+CanDecodeBinASTOffThread(JSContext* cx, const ReadOnlyCompileOptions& options, size_t length);
+
+extern JS_PUBLIC_API(bool)
 DecodeBinASTOffThread(JSContext* cx, const ReadOnlyCompileOptions& options,
                       const uint8_t* buf, size_t length,
                       OffThreadCompileCallback callback, void* callbackData);
 
 extern JS_PUBLIC_API(JSScript*)
 FinishOffThreadBinASTDecode(JSContext* cx, OffThreadToken* token);
 
 } /* namespace JS */
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -253,16 +253,20 @@ pref("dom.script_loader.bytecode_cache.e
 //          time.
 //   *  0 : (default) The bytecode would be saved in order to minimize the
 //          page-load time.
 //
 // Other values might lead to experimental strategies. For more details, have a
 // look at: ScriptLoader::ShouldCacheBytecode function.
 pref("dom.script_loader.bytecode_cache.strategy", 0);
 
+#ifdef JS_BUILD_BINAST
+pref("dom.script_loader.binast_encoding.enabled", false);
+#endif
+
 // Fastback caching - if this pref is negative, then we calculate the number
 // of content viewers to cache based on the amount of available memory.
 pref("browser.sessionhistory.max_total_viewers", -1);
 
 pref("ui.use_native_colors", true);
 pref("ui.click_hold_context_menus", false);
 
 // Pop up context menu on mouseup instead of mousedown, if that's the OS default.
--- a/netwerk/mime/nsMimeTypes.h
+++ b/netwerk/mime/nsMimeTypes.h
@@ -70,16 +70,17 @@
 #define APPLICATION_XPINSTALL               "application/x-xpinstall"
 #define APPLICATION_XML                     "application/xml"
 #define APPLICATION_XHTML_XML               "application/xhtml+xml"
 #define APPLICATION_XSLT_XML                "application/xslt+xml"
 #define APPLICATION_MATHML_XML              "application/mathml+xml"
 #define APPLICATION_RDF_XML                 "application/rdf+xml"
 #define APPLICATION_WAPXHTML_XML            "application/vnd.wap.xhtml+xml"
 #define APPLICATION_PACKAGE                 "application/package"
+#define APPLICATION_JAVASCRIPT_BINAST       "application/javascript-binast"
 
 #define AUDIO_BASIC                         "audio/basic"
 #define AUDIO_OGG                           "audio/ogg"
 #define AUDIO_WAV                           "audio/x-wav"
 #define AUDIO_WEBM                          "audio/webm"
 #define AUDIO_MP3                           "audio/mpeg"
 #define AUDIO_MP4                           "audio/mp4"
 #define AUDIO_AMR                           "audio/amr"
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -500466,21 +500466,21 @@
    "4c9aabb4fac2a558b0828b50e2b06e01e43dd4b4",
    "support"
   ],
   "css/css-fonts/README": [
    "7134f9466636a2d729b84989a69b9c320925cfa7",
    "support"
   ],
   "css/css-fonts/alternates-order-ref.html": [
-   "5ca219e9f3b14418a44e771d6b00d487debe15af",
+   "9c5fe651a47243060ec7a30bc02cba7d85f853f2",
    "support"
   ],
   "css/css-fonts/alternates-order.html": [
-   "9232ad7768f5bea41d87469113b210b3c663750e",
+   "104ef4541a76b22dc574fc1a0dfbcb0fc822ef88",
    "reftest"
   ],
   "css/css-fonts/calc-in-font-variation-settings.html": [
    "ebd68854330dd96a6cfa31c273d9b6810442497d",
    "testharness"
   ],
   "css/css-fonts/first-available-font-001-ref.html": [
    "7f6bdc4dcb91cdf976631216a72ec0557234a6c2",
@@ -517218,17 +517218,17 @@
    "04a6a67837127ba47e757e5cc3a9ff91c1e01d9a",
    "reftest"
   ],
   "css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-028.html": [
    "cafd680e210a6d677bde00ae4c5f9263c1e7b48e",
    "reftest"
   ],
   "css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-029.html": [
-   "67161f08b8aec3fe9bd94e14cbe865dd4c9d3664",
+   "763518bc498fa2560e91eb6dfd13803a5dab2e17",
    "reftest"
   ],
   "css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-030.html": [
    "e5b82f3b41a856a34152af46c9cc659202c61843",
    "reftest"
   ],
   "css/css-shapes/shape-outside/supported-shapes/polygon/reference/shape-outside-polygon-007-ref.html": [
    "3ee945e85e3c2a9b502e93c04ed53966551e7df1",
@@ -543546,17 +543546,17 @@
    "cd3f0233cc0eaf9295e602ca25aef87fb68df851",
    "support"
   ],
   "css/mediaqueries/support/min-width-tables-001-iframe.html": [
    "29e7fb34c94e2e8411514d1e71d09aca2ddb642e",
    "support"
   ],
   "css/mediaqueries/test_media_queries.html": [
-   "234627714b5f304ad7efbeaf2dea3455125f8cc4",
+   "cff3585932589f611a7101329d3b5b6ca27820aa",
    "testharness"
   ],
   "css/mediaqueries/viewport-script-dynamic-ref.html": [
    "7d55c513e2de39c9b362fc864233a3008ca6ced2",
    "support"
   ],
   "css/mediaqueries/viewport-script-dynamic.html": [
    "1c2ba1a9116942599804ed29553e85628afadb04",
@@ -591654,17 +591654,17 @@
    "00559b2f12b8e2cebcddf1a74e5f1581e436b863",
    "testharness"
   ],
   "old-tests/submission/Microsoft/foreigncontent/foreign_content_015.html": [
    "3c582d9c4bd9b54ebb1fc16f28341e589dfe71c7",
    "testharness"
   ],
   "old-tests/submission/Microsoft/history/history_000.htm": [
-   "67c9d8a21aef80b124923effa8be40949d5e3e53",
+   "7f3698823c7c68617ce9f0fc9371158420e7ad0d",
    "testharness"
   ],
   "old-tests/submission/Microsoft/selection/RemoveElementContainingSelection.htm": [
    "bf01b165fa15b374f50f67055f85ce24a79138c9",
    "support"
   ],
   "old-tests/submission/Microsoft/selection/collapseToEnd.htm": [
    "7ab29052320bb2d05ac6bcd9e7282d939e0dc1b5",
@@ -613870,17 +613870,17 @@
    "1cc5702e0aee887d925d2bf3471ac759d7430874",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-removeTrack.https.html": [
    "561575bea206ec1c9572e1e5e6f97d1e0bebe2d1",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-setDescription-transceiver.html": [
-   "aab677c9196488544b30c7eecd180c3046290bb2",
+   "0f998108088cee211977870f9c64f2a89bef7bf0",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-setLocalDescription-answer.html": [
    "e215aa042c67a23ae776b83d662a035a22e03810",
    "testharness"
   ],
   "webrtc/RTCPeerConnection-setLocalDescription-offer.html": [
    "117fc91599d11b63f2d232a63bace8e367dbb72a",
--- a/testing/web-platform/mozilla/meta/MANIFEST.json
+++ b/testing/web-platform/mozilla/meta/MANIFEST.json
@@ -3,16 +3,41 @@
   "reftest": {},
   "reftest_node": {},
   "support": {
    "./placeholder": [
     [
      {}
     ]
    ],
+   "binast/large.binjs": [
+    [
+     {}
+    ]
+   ],
+   "binast/large.js": [
+    [
+     {}
+    ]
+   ],
+   "binast/serve.py": [
+    [
+     {}
+    ]
+   ],
+   "binast/small.binjs": [
+    [
+     {}
+    ]
+   ],
+   "binast/small.js": [
+    [
+     {}
+    ]
+   ],
    "dom/throttling/resources/test.html": [
     [
      {}
     ]
    ],
    "dom/throttling/resources/throttling.js": [
     [
      {}
@@ -416,16 +441,34 @@
   },
   "testharness": {
    "baselinecoverage/wpt_baselinecoverage.html": [
     [
      "/_mozilla/baselinecoverage/wpt_baselinecoverage.html",
      {}
     ]
    ],
+   "binast/insecure.html": [
+    [
+     "/_mozilla/binast/insecure.html",
+     {}
+    ]
+   ],
+   "binast/large.https.html": [
+    [
+     "/_mozilla/binast/large.https.html",
+     {}
+    ]
+   ],
+   "binast/small.https.html": [
+    [
+     "/_mozilla/binast/small.https.html",
+     {}
+    ]
+   ],
    "dom/classList.html": [
     [
      "/_mozilla/dom/classList.html",
      {}
     ]
    ],
    "dom/throttling/throttling-1.window.js": [
     [
@@ -997,16 +1040,48 @@
   "./placeholder": [
    "74e16eb87ecdfeb2dfc28f36e0c73a584abdf9c2",
    "support"
   ],
   "baselinecoverage/wpt_baselinecoverage.html": [
    "8deca29e7b82fc7ef4c11d2a16e8611dfb8cf487",
    "testharness"
   ],
+  "binast/insecure.html": [
+   "04b9d7807d0adf6a471d0621f18ddf20010fdac1",
+   "testharness"
+  ],
+  "binast/large.binjs": [
+   "b83da2863c47d8cedec22bf136fbea077ba8a86b",
+   "support"
+  ],
+  "binast/large.https.html": [
+   "3b27ede3f0666df43f752d8022c1e72d87f5b64f",
+   "testharness"
+  ],
+  "binast/large.js": [
+   "1477587b19402f93514b1099a559cc7a376d2ab7",
+   "support"
+  ],
+  "binast/serve.py": [
+   "2421d4ce3c58c8d42c440e2455fd95db898b2b91",
+   "support"
+  ],
+  "binast/small.binjs": [
+   "0a8c0455cc13c1e8fe36540d768f0fad2e31aa2f",
+   "support"
+  ],
+  "binast/small.https.html": [
+   "01ecd5cf1b3e7e5fa7bc53960d9defb80eb3eef7",
+   "testharness"
+  ],
+  "binast/small.js": [
+   "047cbda8fa0155dc777deecb7f98779e94d84b1a",
+   "support"
+  ],
   "dom/classList.html": [
    "17c65652a2943738a0cc5152aec737f243a02d3d",
    "testharness"
   ],
   "dom/throttling/resources/test.html": [
    "0898e3efa5dbcc74138bdf27ff2787d1f1a2d4d8",
    "support"
   ],
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/mozilla/meta/binast/__dir__.ini
@@ -0,0 +1,1 @@
+prefs: [dom.script_loader.binast_encoding.enabled:true]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/mozilla/meta/binast/large.https.html.ini
@@ -0,0 +1,4 @@
+[large.https.html]
+  [Check we can load BinAST over HTTPS]
+    expected:
+      if release_or_beta or os == "android" or (os == "win" and processor == "x86"): FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/mozilla/meta/binast/small.https.html.ini
@@ -0,0 +1,4 @@
+[small.https.html]
+  [Check we can load BinAST over HTTPS]
+    expected:
+      if release_or_beta or os == "android" or (os == "win" and processor == "x86"): FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/binast/insecure.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<title>Check we dont't load BinAST over an insecure connection</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+    setup({allow_uncaught_exception: true});
+
+    var hadError = false;
+    function setError() { hadError = true; }
+
+    window.addEventListener("error", setError);
+
+    const test_load = async_test("Check we can't load BinAST over HTTP");
+    window.addEventListener("load", test_load.step_func_done(ev => {
+      assert_equals(hadError, false, "Didn't expect an error event");
+      assert_equals(binASTLoaded, false, "Didn't expect to load BinAST version");
+    }));
+
+</script>
+<script src="./serve.py?name=small" onerror="setError()"></script>
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..202d7338774eb4ab676661259af1fa22295be541
GIT binary patch
literal 11478
zc${5Y54_~$RXv0dLI{u7$1r^?%fC{fwWYKoA|m2ISEO`{uuw!q{3erq6J92HB$>B6
zi-?F65fKrwA|fIpA|fIpRzyTZL_|bHL`1AuD^{$CwbqJw?)|=GcK!X9eQz_FeD}|}
z=bn4RDW`wpV_R^+X=e{MZyua|VKz#OCTnIVUhefi^zp1oE0b?&s;pSqw0*5g>pClo
z_neXyBVIXO@7<cE)q6i_s>}@Y^rLB#o60n0b>#oQ#4jE^s46p?Kj?#78eUH}r;}k?
z%`YE1sOz+9(h(oAB`Md^4?9CnDa$-H#r)2AOHyTP&HLt-%s;ze%Q<JC{)yAJ%w2lo
zQ?JL-wz3-4%fqY~Y&qxSr5UB8CAoBJIW3xC-Be3v$lKE@OBT$q6f63J#N@NJhCY~O
zGpw_s8E5q*OBT&=Nc&B0@63w68JfB=)hOGM^7niyPMd1hH%Y>WPEDTAcyE@{X?aqU
zPO@@Z=d&LFQQF&=Ov1ozEvrdSep^d(-oekaawRRYy6Fvul^L4}BeK97){4w*;7{6A
zu9YL+%LkWvCXeFXG_m53JYAp@qs-{zd26oL*Wzi~9$r>r9DiMxOXVahBz;TW<CX{Z
zm{B$|a>vC}o7DxqqaMe6bdvFeVL|I@$&$F36+EQ<>Y4<0(MF@JVl$lP5{WiCOw!_D
z$S~w(Ayy?elaU#Y%Sr;bl1^)W-SBILM0ng<xiA>gy(W$IS)*_;9L{1Mnij^{!)Gr@
zN_Y}IPYFc*g*m8JQn93}n+=l3Y=w_hQ{=$~P0-salQeQ$l9ZB|JY5@?*{E+H&ii+C
zapbSGcrq^aLPa<wGa6BqjVY#+<@RCeD3>DyIO|O1uu6Hc*W%0M0z}qLk))Ed5uu`>
z67UqMjjn{-H0h#VrKyLS;YcD<8bxqf_3U9|IxPT52ml6HLN$`a)=5??<oKjUyxyo5
zuQ96=KCs%*+DZ$>SWRM9EKBZ3S)$qRTgYS<K5D3V!?Ws!VHl|+MY<xK(04Ev{7bDA
zqq3OLwtk1{ELn13o|wMr>MkYfCE})%{0~P{Cb?!dR#_@NMYO5v%q(c_6q3%eG9x`+
zlSVpm&(**RMwDpUcnSW}I;-W#s?5t38o%I-iU&kXX85pdl<{J=VzRt%i$^0M@F)vg
zrR93MR+Z(}p8edRhqmzSz?jKKd!1*FB>%Al%FH%8pXFfsyunpVl2&Q^)L}_liLH5&
zxHnQxXsU8;EHIkZg4TYQ$$Z8v5mW1#1b!9?RZOKyhNE;WU#+T)cx>9RGy-3DzoM};
z$#{d9UL+DU5?+VR>4XVO^GR7J<0)gN7;fn%msCpza{gG>*l(TYHV}zWqe;YT8nx(P
zoh&%i>6eo6vLuuqh$qPgNmiZ;21XqOr*&j#QnoC|5v_^%E8m_93k~b(#PL%q=-OZn
z+)7CDU%oN1<wAzh>=4E<7nf7HHZj$l_-6gC6t%)g`RS@rAJ&*_qM|UXSx0Q9FxwSa
zY&J>CA{z;43Z6ZyYqqf5Ftt2!n;9zFHL49UK2Oq;EADx1h<mL?jjQIgYD-q`NJm0u
zl8RNrOOQbW1;q?u$|DNPT};9TOGZ3HxNTuTAQe(=>{uG5<&LbG?O-r0`+6GxX?u$0
z1>pfH|5W%(k{XdtGT}QmgXOlP)Z=Vz!%nqs@<rFGgbXYvHj7eMqcklP)!9f0ip8r>
z5gKi&cUp@DQjIiNlD2AEUP~C1wst5or2En8lt~cnAuVK@lSueAxxAJ&<JlxLEuGer
zE{du4+OjFPYw$_aL>z;EXiMp}1DQ)~qf9IsNQ~^Yp-X1Hu19A57P&lOscUilu4*;-
zNhuZ%mjwBBBYrAdq*cgom<6TiVq|~TN>8@YSLwXG33FkD@x(UP+9FAe%V$hak!qz)
z)<}@Oc|k8KyYfGe^)SrVD5l5`G%*s%Fp=yhWua)c&8ZfM*p$|`)EU}ZBW|BrDmN2O
zBPkxO;dD(|nKi%`$;zyzYtj_h*TiCJ9&#IbowsS>R-*;$qkKznubFY;W~pWR*?U^z
zUMp+c>0)tNSbj($C%&^|@E0R+-Z)n?lCGte<fX**F^)b;a!$`R;!G8Y_AUOpaW2;6
z34Ik1wnz566Z2V%EbA+O?YBKz9;bL03`<cmE9)k2RTLSzW7i@*SPi5%6Mm7{+t~F3
zMk>#icLwEx%hU2ebcV>9m9##%iy5C#3R@2Ia+^YHyAk1_k(mpNx(NrYlAcoMnkw~O
zs#gT60ywAha^iZKi&UX1s|5A71Dc&0ITfO)5{+_eK5I&@FF0^&;F7VDj}^V6R6=9Z
zKZ=>Gm1f+VJKNXxV0$hFUY#v#oLEV@7LNQQ8?_BhWuvKAty~mr4#ZyC8C#k@xNH8l
zD=j*gXR4V;D+(s-p>!tczZ<N&%q)j4x8hBSO4!S+Wh#2LaH<VzQIaNfcdA@dk6qIj
z#4NaDVyzh#{4f;|SevG`KNv1KVVzd<Sf_1nEf|#K<fF_=b*UhlM*f~UnXTmQn8Jz`
z@|@L3#!03m%Zj=1lw0&LIn|0T?Kd*&Btl@WYg<Kd@w8BRsG(VFTzgUYPbck&QIUq&
zgJRn_y1G)?Yqn>(@E4gfwi1;$8&!W@<6m@RNhLjl6GJK`Z5aDxnzvr@Cz(gfm*JBh
zECCgiFLzZ==Ts$VVLIL3%vx3ztS+?}llY8M;XKFOddxt5pVsed^E93b|In#&GE=;X
zD`x9f(TNRl5^4YMw>?}&RuvzbD{;vVA<_{=t`18fS^iy2^E{hc?WlvUjpUNcQkgH>
zT$AAmw%SUkN-#aw?;K&f>d{b(AgZ*}F?$r|B>ZxyZJTjOt$FiK&&UWIRp#s0*|(%S
zx(27U%4{@MD$Y=nx6|ud&8+AgEkjl!PZ8diDzREzz+wlQHhFrAUS>%MX|Z4;@wL(B
zQLD1ynG0?x)F3xj?rRB1A=0N*bZ%itLoVZ~&{f*76$u25RH4m8+JT#0(Gt|OvO?z)
zCR=c&s%FQIZn~BgMB(V9*5P|<ryKFkYBCa6K>}tim)jQ<t9npXTDbSx+Hnn{rq<Oc
z>7F3(b&9K4k}qqo=*{au$0|EFQ=X5OTCz<fk;O_W&R0gEFl|CX<DwG|rf)%}U23_-
zzSAyZzT>4dTNy9eXsU3wN83in{>4mW)+X^ZE2>P2OA)HKEV=Y(9onHlJXL0xuO$qd
z-N4PUMkjZ9tng!28!I>XU6p)Ux<fmxWz><~o)E>-3v><>rD?@rCW=&4KyJ!K*WvU~
zH_)qgV|XT_Y+I3L?C>UPv(*{YHr<F`*mkF;+98`+IC!UB#%v|aBrBr9<R?>>wj)8F
zO>{RxuhfT@uPRs6>uDsr4l}1BB~&{v*)CC8OpqsZ$H5PqMrqlJIAyccRsW9m<Y{Hw
zuj11>MqO<k>+qM)Lc86u#%3c0Rk^B7R#Zo+d@Z2qxe>x{XV}qLph~!IJdrLYO}xD?
z)ig~xYZMB2nI5$a>bcsrsFVt`D79W6xht)f!m6s3rZy6GXtj$k`Y{XmMaMHbPY$hc
z(*ZJFVdiD>7&xE%uHQPVonlXyyZT~A7Hx;p#Z0bOHnuaDZS&Mg)wDkkW34r5bRKAj
zI;BHPKkWvH$n91u>+N8pW~Z^zgt2U4>^g<FxyF^Qn`GLxIKoHuww<zMfU#p7fyG=6
z9a>Af$Z%~-$41+w%VNWvVJN7UoRcyP2uxYGhwh>-bn@%M(=r_Ep7Eg^+E(UU?N-ic
z7u(A68nu*@hEl$Z7RC8&-p=PEn^X{wpjgpn-MC>rYxf{p1k$wTn!^?>*+6D&T@Q_A
zU@4Rl$fqJub)70xH(@9G(N@<^OV)aFsM)24O%b^yx~ZknS$$d<m31Oaos<!c?<gfz
z3eQIKC7H0DHZ#%l8+W?w7O^tnNxu+?X*U@1c*V)0mNwaWL7|ft-GG~pmUINR3pfg+
zwN&Dv^G7Yaaatj2TsKMWQm{^RZDiYyYp`<SQac>aLV;VarAd9XbnvqB2D<1HB_Sb{
zL47QXg5{m^Co!N#5$@e-*}OxNXm{AEdM3h3WvOc;qI5OW!muJ!Mqix?l`2H0HOjwo
zl8d1vNY=PUmpt3XCRt6Jx|`Ug+Nq2SS+2LM5KWGX;bKK@Y;?9DXVo+9C-NXRl;xJE
zqz*|Tb8NQEVC#L+MKE>CJl#~z$-3#g@uM8-j9WLXjIL#_a2ObiXvsuo8M|sJM{e4&
zH>a$Gb_SW%ZuhC$OwVRp*2H8wtJ(F)$msG<2PTy-h0F~?-lFD}m76l7-WFcy>*jE!
za?^$^QnVpSw3eLcDywb|TIXD!2xql>i>B}TIU7TxN2r*St<5^E*JYpEDrm`Doyunx
zy*b$}sA+Z9?}iDTM^iJg)mG~(pyie*-FU5YkHjjLjx$lO*WJvLbYJAz6hW)HKF|`D
zmkV~dNS%W`bmP31TtipfsHsfBF2l8%4@;@i&otNM=9Wm^jFao8TilUU+M4V(KWW`-
zUn>yR_UcY5lQ+mzKn=68dtczHl$5$|s@0`|jo2t9IETG%*)HzalW{3cGo5v5lh;Cx
zUH#SL1-E1EcfzNdSyUOf-nNTDyF{KC`@TeyOWc*aa@M536BoNa?_OHisVfxL>N^iP
zl%;lKuR~e7^J}O{Z!UAY&ZNcGZi!~@t2+AX7ISsM4a`fL`cay!Dofguwrx+R5|M%3
zu#tA_z-~>;FPZlJnJ(#d<s?l~cSb73^Q@@b*L+02RJ`o51TGueI1Ag(bc$ja=1@p&
zl}SNYvV@S;LZ$kr(JHg67T0|AHAy6mQAazGQc^~iQhIZ-Y88<X+Q8Mh#&*i~mRJ^Y
zyIQ+*vTGw@0-cGZsfoI?Z=Lkbi`ZReT2353RTSxC`>l2sY7m}kQi++ARL?f5c5|PI
z2wu`X@37o4?b(f`G-Y>+TR>Q)-t{j_@VU*Eu!K^Z5uv2}sM>7Twy|y(Zmv+pUR4_X
zo!&sXH%;xf%<f+7o?h70(X(R4)%jEgzu3061tsw$bqgjts_L?Cr$cP7l($RGoED9y
zL_oQ&19oeV?h@S|T|ZCrwAbmb?scFv?ty#v<ityDbO+Sb3$C~K>@?rLY!oIG*~T(w
zh)1b)L9pF|u5d%JEdlC^LIo&2k{|7~=eC&bJCfy2LE9cph%FE1@3zyOK3^AIO4g{`
zC^c5@)Y^yYX6i^zSE6k(FuFQ2jHTYqTBCM}oXE?5J2Z{6x?45bS4wtYQMB4Cv0^sM
zb6wOcC!}_W*U4A*JWHL{>$Pt+Econ*E4(E@ZfqM+xmD`qzBbL1vRw?#>`*qYgbU`>
zr=w3h7fsz+6T7I<jkoaPv>ga~?Mg5gV5?s$dZ&e@0oBCIrsYWcme8$RbZtLv$ns{R
z%es3>wcM_xW;AUV21;drR4GSN=5$J$sz)B(wHTudJE0eSb7Cf0-L5r73Q)bN``tQ!
zv7Ohum5)jVUFL1j%uEbD(`GLl7WG6{3`sW~+7}4*io7GOIXg`3a?j*#?nhaX^}Cg-
zbXUEuVwUrX)O9Eb)K$-{(~5;%kdEeWcBT{2bfPIu_Xd~GXZEt>f^*K^v}yjoe7tew
zV(h{9d;mePd*JyV!dGHE5aLA-$9qUUe89s$;Upic5zY#+Uxd=bF){9raAJr<LR|0T
z&KSFUxIe;EF;+Yr9^zl{<2ZyLg!on59^vQkKiDTk7U1|0cZB$Eyzb+;0G|%<Sb)<*
z+#cgy0UinPgpXT891!6G55~hwyxqeKA$}8I3GrrxEXF+nE{Jh$jK>2!<6|1&^FBTs
z<8=>@M7YGmH9nq+F^h0Vge!d<5aY5KmxQ<>#vMLRj<Dk6rT{&Bk!QzvCB&H#E{*Z5
zj}JsR$iurLyzJrO03V3)T#QG3oamv9@LY%oV;mmgXYh7_eIpzbVG>~x_xd;~#5BUG
zJj=tEeLNiFEBrgeULLkYxXs6T9{v|kdKls}9)1mfj59sl<Ka~wr$=};#QPbG82bk}
zFv1ri+#TZD5T6clREU!Tyx?OWA3urG$DiY!5ZfYr&c|*ccJpx$O^opQ7}xoDe~g17
zY>M%FIEugWPk{SFm=MQCc+<m=;1&<BF^D1diEw#<dqaFA#xeXm#M3^`53oCP;UQu2
zX-j~Q_y}<uqw3*^2#5LjE8NSU0WQYTA#RRf0_+{)-9By$@luS>Mo2=O9AFmXI6C3u
zc^}urcsj(bF*egU58s7P;zl2*F?&8<Aeel7+Q-WwR((7aVUGw8hS=T5U*jz1(!;$T
z&JXcygogtB1UBHo05=BsF`Vz=mH>Z)dm`K(U`vSaz?}gek1>t08so|skHmP*!*Ae9
z4+9_X#REi$k82~OAvO~XG(5mV9{vk2d3ZU-9v=1xu_ea&G0uqb1$q_WVIP-zxGKgC
zgl32@MR+~JV|-GCU&5&&?)UL6IKsyYqwHbt05^H~eY{0DL^#~TD?Tptu}6TfMtIo6
zyFFabbOksq#DO8si%<u6F~Dc}C&d09Dr^dHkB^T;_+1>uvWs!FhsPMH0MCc`OoYcn
z9OvUjA7dQj;n5I%>>cBC5xx(ndpIq^em)-Y@#}b&)##&SFhe}g`V8<zCdtPROha56
zU@N{AcX^n4*ydwjCYwfvAOk!_6o)tkH~2Uq!Z+at7HfnL;*k*h`nWQ}jXcl8H)1az
z0}rS2!SvF@S6L_l-tzEAc!qTv;y>|1gf-k9;Iar;u^u8sI3d6l0gm&qghvV60Q-6P
z6!wpCsgIkPNFP`ExP{qZ_4>FY#)&abitz8aGs6B6{vJ<6I4{8F7<<O}M;t_NJ^Upm
z0nYGoBz_B5hZr!gF`oCZkB8?2oJ;tI_`HX`VqC;O0j{C1KAs40J<Hn1@jiYC?;?|U
zc#f%za5ew<*pC6HiA<7@Hv_!l;l3Cj!rmTM1H2L7xd?BwU_;y$;sR0;X_3M4@VNj#
zfZKh1(Zgl*h-F1A1o(`PXJcH*>W%RP;S%850O#N^;)%$M@w0fGxu9hM&Lby8SPgNE
zk9Q*68sJqAmop!vmH>yuc-zO{;_Glih$}oi#H@sPBg8-BA8;=DJH$l{W{e-kT|Q3r
z@MMTrV;n^^#8|+-9^UbBbby+47~?)>BgXX}4#qb6!B2eL%~%D<B785t9?vpIF}?wB
z`FM&Y8R3_4x{trZ>j5UTD#n+{Jt2;da951)$JH#i7*~*m1DqM-Fb~Iu_<8IZ;1Ys4
z!i;@|&4m0*u!MM<Wf&ugu@U?GNCIpk!+FSIB3$I*^$-WKGD6(v;oDHNq9Qc7Im8d*
zj0pS0I3mV1A+CvVU5M*QUon0GN5|NWos78k@Kd;h0A(;eycOUzG962sX!mfkk1u<;
znN%9!<QSWLyc*(0f;`66G(E(*0dDcJSBQ0-8{reUmEMLp!N=2dHNc)e?hA3bhp$BV
zQh)+aMfh)=8{@?YM+A5$z|#Q^@bD*icZ3@}oD}1h5I2!ZB0L%3Y}^&$Kz12&Mu;;4
z+)nZ#ctSkJWO{gtfBQHlz*R(Zh&ses9x@MCMEEjkz{gMHP9Gn|ui!u*htqSmWDge-
zDgh2-!)3^P-0tB-7AKP*;79SAk3H!C>pQ@Q@urUvPV#U;gf}9*6k+cO+wlT17~{4G
zH?hHmcsN3g^J4rCPG)iNX#t*$@eDymD0;Y&{f(b9aS@J>@u&Djyh?)QIV={^7azmU
z>f?+M|Aw1AJP_l604Y-&;obn>jQd%k<mmv92KZOJOz6e<ZJg=jFp{u`NsRl6b`M{J
zV_C2sHV60^E($SYZL+<wf@8cyJ`M1G5AVZO9u5hxn}@5}_Q^YBZwA@JCYl%Eaw3MG
zM!43)o)NBKifLVdLvfvlKg27fCPJCc`1lX}1@`t~SotCD^za^>#$FV|#Ha}%54T3R
zEW{BWo?roZcrd~s!f_N61W<_ABK!f~@$j`cE5OVA6X82?CQHi4qx8zhW>zITL5K%v
zG3$_F^>GyaV>yJlklwM`gt#)m_h7~r8RO6JG#e2ci;s`v7Pd(b-;QM*%eG4%W{350
eeSimiJRadRHZi6l4n)>@3%lu0KU%{1{{I1C<lg-N
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/binast/large.https.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<title>Check whether we can load large BinAST file</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+    setup({allow_uncaught_exception: true});
+
+    var hadError = false;
+    function setError() { hadError = true; }
+
+    window.addEventListener("error", setError);
+
+    const test_load = async_test("Check we can load BinAST over HTTPS");
+    window.addEventListener("load", test_load.step_func_done(ev => {
+      assert_equals(hadError, false, "Didn't expect an error event");
+      assert_equals(binASTLoaded, true, "Expected to load BinAST version");
+      assert_equals(wordCount, 878);
+    }));
+
+</script>
+<script src="./serve.py?name=large" async onerror="setError()"></script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/binast/large.js
@@ -0,0 +1,885 @@
+// Larger JS source to exercise off-thread parsing.
+
+// List of words to bulk up output file.
+const words = [
+    "A",
+    "a",
+    "aa",
+    "aal",
+    "aalii",
+    "aam",
+    "Aani",
+    "aardvark",
+    "aardwolf",
+    "Aaron",
+    "Aaronic",
+    "Aaronical",
+    "Aaronite",
+    "Aaronitic",
+    "Aaru",
+    "Ab",
+    "aba",
+    "Ababdeh",
+    "Ababua",
+    "abac",
+    "abaca",
+    "abacate",
+    "abacay",
+    "abacinate",
+    "abacination",
+    "abaciscus",
+    "abacist",
+    "aback",
+    "abactinal",
+    "abactinally",
+    "abaction",
+    "abactor",
+    "abaculus",
+    "abacus",
+    "Abadite",
+    "abaff",
+    "abaft",
+    "abaisance",
+    "abaiser",
+    "abaissed",
+    "abalienate",
+    "abalienation",
+    "abalone",
+    "Abama",
+    "abampere",
+    "abandon",
+    "abandonable",
+    "abandoned",
+    "abandonedly",
+    "abandonee",
+    "abandoner",
+    "abandonment",
+    "Abanic",
+    "Abantes",
+    "abaptiston",
+    "Abarambo",
+    "Abaris",
+    "abarthrosis",
+    "abarticular",
+    "abarticulation",
+    "abas",
+    "abase",
+    "abased",
+    "abasedly",
+    "abasedness",
+    "abasement",
+    "abaser",
+    "Abasgi",
+    "abash",
+    "abashed",
+    "abashedly",
+    "abashedness",
+    "abashless",
+    "abashlessly",
+    "abashment",
+    "abasia",
+    "abasic",
+    "abask",
+    "Abassin",
+    "abastardize",
+    "abatable",
+    "abate",
+    "abatement",
+    "abater",
+    "abatis",
+    "abatised",
+    "abaton",
+    "abator",
+    "abattoir",
+    "Abatua",
+    "abature",
+    "abave",
+    "abaxial",
+    "abaxile",
+    "abaze",
+    "abb",
+    "Abba",
+    "abbacomes",
+    "abbacy",
+    "Abbadide",
+    "abbas",
+    "abbasi",
+    "abbassi",
+    "Abbasside",
+    "abbatial",
+    "abbatical",
+    "abbess",
+    "abbey",
+    "abbeystede",
+    "Abbie",
+    "abbot",
+    "abbotcy",
+    "abbotnullius",
+    "abbotship",
+    "abbreviate",
+    "abbreviately",
+    "abbreviation",
+    "abbreviator",
+    "abbreviatory",
+    "abbreviature",
+    "Abby",
+    "abcoulomb",
+    "abdal",
+    "abdat",
+    "Abderian",
+    "Abderite",
+    "abdest",
+    "abdicable",
+    "abdicant",
+    "abdicate",
+    "abdication",
+    "abdicative",
+    "abdicator",
+    "Abdiel",
+    "abditive",
+    "abditory",
+    "abdomen",
+    "abdominal",
+    "Abdominales",
+    "abdominalian",
+    "abdominally",
+    "abdominoanterior",
+    "abdominocardiac",
+    "abdominocentesis",
+    "abdominocystic",
+    "abdominogenital",
+    "abdominohysterectomy",
+    "abdominohysterotomy",
+    "abdominoposterior",
+    "abdominoscope",
+    "abdominoscopy",
+    "abdominothoracic",
+    "abdominous",
+    "abdominovaginal",
+    "abdominovesical",
+    "abduce",
+    "abducens",
+    "abducent",
+    "abduct",
+    "abduction",
+    "abductor",
+    "Abe",
+    "abeam",
+    "abear",
+    "abearance",
+    "abecedarian",
+    "abecedarium",
+    "abecedary",
+    "abed",
+    "abeigh",
+    "Abel",
+    "abele",
+    "Abelia",
+    "Abelian",
+    "Abelicea",
+    "Abelite",
+    "abelite",
+    "Abelmoschus",
+    "abelmosk",
+    "Abelonian",
+    "abeltree",
+    "Abencerrages",
+    "abenteric",
+    "abepithymia",
+    "Aberdeen",
+    "aberdevine",
+    "Aberdonian",
+    "Aberia",
+    "aberrance",
+    "aberrancy",
+    "aberrant",
+    "aberrate",
+    "aberration",
+    "aberrational",
+    "aberrator",
+    "aberrometer",
+    "aberroscope",
+    "aberuncator",
+    "abet",
+    "abetment",
+    "abettal",
+    "abettor",
+    "abevacuation",
+    "abey",
+    "abeyance",
+    "abeyancy",
+    "abeyant",
+    "abfarad",
+    "abhenry",
+    "abhiseka",
+    "abhominable",
+    "abhor",
+    "abhorrence",
+    "abhorrency",
+    "abhorrent",
+    "abhorrently",
+    "abhorrer",
+    "abhorrible",
+    "abhorring",
+    "Abhorson",
+    "abidal",
+    "abidance",
+    "abide",
+    "abider",
+    "abidi",
+    "abiding",
+    "abidingly",
+    "abidingness",
+    "Abie",
+    "Abies",
+    "abietate",
+    "abietene",
+    "abietic",
+    "abietin",
+    "Abietineae",
+    "abietineous",
+    "abietinic",
+    "Abiezer",
+    "Abigail",
+    "abigail",
+    "abigailship",
+    "abigeat",
+    "abigeus",
+    "abilao",
+    "ability",
+    "abilla",
+    "abilo",
+    "abintestate",
+    "abiogenesis",
+    "abiogenesist",
+    "abiogenetic",
+    "abiogenetical",
+    "abiogenetically",
+    "abiogenist",
+    "abiogenous",
+    "abiogeny",
+    "abiological",
+    "abiologically",
+    "abiology",
+    "abiosis",
+    "abiotic",
+    "abiotrophic",
+    "abiotrophy",
+    "Abipon",
+    "abir",
+    "abirritant",
+    "abirritate",
+    "abirritation",
+    "abirritative",
+    "abiston",
+    "Abitibi",
+    "abiuret",
+    "abject",
+    "abjectedness",
+    "abjection",
+    "abjective",
+    "abjectly",
+    "abjectness",
+    "abjoint",
+    "abjudge",
+    "abjudicate",
+    "abjudication",
+    "abjunction",
+    "abjunctive",
+    "abjuration",
+    "abjuratory",
+    "abjure",
+    "abjurement",
+    "abjurer",
+    "abkar",
+    "abkari",
+    "Abkhas",
+    "Abkhasian",
+    "ablach",
+    "ablactate",
+    "ablactation",
+    "ablare",
+    "ablastemic",
+    "ablastous",
+    "ablate",
+    "ablation",
+    "ablatitious",
+    "ablatival",
+    "ablative",
+    "ablator",
+    "ablaut",
+    "ablaze",
+    "able",
+    "ableeze",
+    "ablegate",
+    "ableness",
+    "ablepharia",
+    "ablepharon",
+    "ablepharous",
+    "Ablepharus",
+    "ablepsia",
+    "ableptical",
+    "ableptically",
+    "abler",
+    "ablest",
+    "ablewhackets",
+    "ablins",
+    "abloom",
+    "ablow",
+    "ablude",
+    "abluent",
+    "ablush",
+    "ablution",
+    "ablutionary",
+    "abluvion",
+    "ably",
+    "abmho",
+    "Abnaki",
+    "abnegate",
+    "abnegation",
+    "abnegative",
+    "abnegator",
+    "Abner",
+    "abnerval",
+    "abnet",
+    "abneural",
+    "abnormal",
+    "abnormalism",
+    "abnormalist",
+    "abnormality",
+    "abnormalize",
+    "abnormally",
+    "abnormalness",
+    "abnormity",
+    "abnormous",
+    "abnumerable",
+    "Abo",
+    "aboard",
+    "Abobra",
+    "abode",
+    "abodement",
+    "abody",
+    "abohm",
+    "aboil",
+    "abolish",
+    "abolisher",
+    "abolishment",
+    "abolition",
+    "abolitionary",
+    "abolitionism",
+    "abolitionist",
+    "abolitionize",
+    "abolla",
+    "aboma",
+    "abomasum",
+    "abomasus",
+    "abominable",
+    "abominableness",
+    "abominably",
+    "abominate",
+    "abomination",
+    "abominator",
+    "abomine",
+    "Abongo",
+    "aboon",
+    "aborad",
+    "aboral",
+    "aborally",
+    "abord",
+    "aboriginal",
+    "aboriginality",
+    "aboriginally",
+    "aboriginary",
+    "aborigine",
+    "abort",
+    "aborted",
+    "aborticide",
+    "abortient",
+    "abortifacient",
+    "abortin",
+    "abortion",
+    "abortional",
+    "abortionist",
+    "abortive",
+    "abortively",
+    "abortiveness",
+    "abortus",
+    "abouchement",
+    "abound",
+    "abounder",
+    "abounding",
+    "aboundingly",
+    "about",
+    "abouts",
+    "above",
+    "aboveboard",
+    "abovedeck",
+    "aboveground",
+    "aboveproof",
+    "abovestairs",
+    "abox",
+    "abracadabra",
+    "abrachia",
+    "abradant",
+    "abrade",
+    "abrader",
+    "Abraham",
+    "Abrahamic",
+    "Abrahamidae",
+    "Abrahamite",
+    "Abrahamitic",
+    "abraid",
+    "Abram",
+    "Abramis",
+    "abranchial",
+    "abranchialism",
+    "abranchian",
+    "Abranchiata",
+    "abranchiate",
+    "abranchious",
+    "abrasax",
+    "abrase",
+    "abrash",
+    "abrasiometer",
+    "abrasion",
+    "abrasive",
+    "abrastol",
+    "abraum",
+    "abraxas",
+    "abreact",
+    "abreaction",
+    "abreast",
+    "abrenounce",
+    "abret",
+    "abrico",
+    "abridge",
+    "abridgeable",
+    "abridged",
+    "abridgedly",
+    "abridger",
+    "abridgment",
+    "abrim",
+    "abrin",
+    "abristle",
+    "abroach",
+    "abroad",
+    "Abrocoma",
+    "abrocome",
+    "abrogable",
+    "abrogate",
+    "abrogation",
+    "abrogative",
+    "abrogator",
+    "Abroma",
+    "Abronia",
+    "abrook",
+    "abrotanum",
+    "abrotine",
+    "abrupt",
+    "abruptedly",
+    "abruption",
+    "abruptly",
+    "abruptness",
+    "Abrus",
+    "Absalom",
+    "absampere",
+    "Absaroka",
+    "absarokite",
+    "abscess",
+    "abscessed",
+    "abscession",
+    "abscessroot",
+    "abscind",
+    "abscise",
+    "abscision",
+    "absciss",
+    "abscissa",
+    "abscissae",
+    "abscisse",
+    "abscission",
+    "absconce",
+    "abscond",
+    "absconded",
+    "abscondedly",
+    "abscondence",
+    "absconder",
+    "absconsa",
+    "abscoulomb",
+    "absence",
+    "absent",
+    "absentation",
+    "absentee",
+    "absenteeism",
+    "absenteeship",
+    "absenter",
+    "absently",
+    "absentment",
+    "absentmindedly",
+    "absentness",
+    "absfarad",
+    "abshenry",
+    "Absi",
+    "absinthe",
+    "absinthial",
+    "absinthian",
+    "absinthiate",
+    "absinthic",
+    "absinthin",
+    "absinthine",
+    "absinthism",
+    "absinthismic",
+    "absinthium",
+    "absinthol",
+    "absit",
+    "absmho",
+    "absohm",
+    "absolute",
+    "absolutely",
+    "absoluteness",
+    "absolution",
+    "absolutism",
+    "absolutist",
+    "absolutistic",
+    "absolutistically",
+    "absolutive",
+    "absolutization",
+    "absolutize",
+    "absolutory",
+    "absolvable",
+    "absolvatory",
+    "absolve",
+    "absolvent",
+    "absolver",
+    "absolvitor",
+    "absolvitory",
+    "absonant",
+    "absonous",
+    "absorb",
+    "absorbability",
+    "absorbable",
+    "absorbed",
+    "absorbedly",
+    "absorbedness",
+    "absorbefacient",
+    "absorbency",
+    "absorbent",
+    "absorber",
+    "absorbing",
+    "absorbingly",
+    "absorbition",
+    "absorpt",
+    "absorptance",
+    "absorptiometer",
+    "absorptiometric",
+    "absorption",
+    "absorptive",
+    "absorptively",
+    "absorptiveness",
+    "absorptivity",
+    "absquatulate",
+    "abstain",
+    "abstainer",
+    "abstainment",
+    "abstemious",
+    "abstemiously",
+    "abstemiousness",
+    "abstention",
+    "abstentionist",
+    "abstentious",
+    "absterge",
+    "abstergent",
+    "abstersion",
+    "abstersive",
+    "abstersiveness",
+    "abstinence",
+    "abstinency",
+    "abstinent",
+    "abstinential",
+    "abstinently",
+    "abstract",
+    "abstracted",
+    "abstractedly",
+    "abstractedness",
+    "abstracter",
+    "abstraction",
+    "abstractional",
+    "abstractionism",
+    "abstractionist",
+    "abstractitious",
+    "abstractive",
+    "abstractively",
+    "abstractiveness",
+    "abstractly",
+    "abstractness",
+    "abstractor",
+    "abstrahent",
+    "abstricted",
+    "abstriction",
+    "abstruse",
+    "abstrusely",
+    "abstruseness",
+    "abstrusion",
+    "abstrusity",
+    "absume",
+    "absumption",
+    "absurd",
+    "absurdity",
+    "absurdly",
+    "absurdness",
+    "absvolt",
+    "Absyrtus",
+    "abterminal",
+    "abthain",
+    "abthainrie",
+    "abthainry",
+    "abthanage",
+    "Abu",
+    "abu",
+    "abucco",
+    "abulia",
+    "abulic",
+    "abulomania",
+    "abuna",
+    "abundance",
+    "abundancy",
+    "abundant",
+    "Abundantia",
+    "abundantly",
+    "abura",
+    "aburabozu",
+    "aburban",
+    "aburst",
+    "aburton",
+    "abusable",
+    "abuse",
+    "abusedly",
+    "abusee",
+    "abuseful",
+    "abusefully",
+    "abusefulness",
+    "abuser",
+    "abusion",
+    "abusious",
+    "abusive",
+    "abusively",
+    "abusiveness",
+    "abut",
+    "Abuta",
+    "Abutilon",
+    "abutment",
+    "abuttal",
+    "abutter",
+    "abutting",
+    "abuzz",
+    "abvolt",
+    "abwab",
+    "aby",
+    "abysm",
+    "abysmal",
+    "abysmally",
+    "abyss",
+    "abyssal",
+    "Abyssinian",
+    "abyssobenthonic",
+    "abyssolith",
+    "abyssopelagic",
+    "acacatechin",
+    "acacatechol",
+    "acacetin",
+    "Acacia",
+    "Acacian",
+    "acaciin",
+    "acacin",
+    "academe",
+    "academial",
+    "academian",
+    "Academic",
+    "academic",
+    "academical",
+    "academically",
+    "academicals",
+    "academician",
+    "academicism",
+    "academism",
+    "academist",
+    "academite",
+    "academization",
+    "academize",
+    "Academus",
+    "academy",
+    "Acadia",
+    "acadialite",
+    "Acadian",
+    "Acadie",
+    "Acaena",
+    "acajou",
+    "acaleph",
+    "Acalepha",
+    "Acalephae",
+    "acalephan",
+    "acalephoid",
+    "acalycal",
+    "acalycine",
+    "acalycinous",
+    "acalyculate",
+    "Acalypha",
+    "Acalypterae",
+    "Acalyptrata",
+    "Acalyptratae",
+    "acalyptrate",
+    "Acamar",
+    "acampsia",
+    "acana",
+    "acanaceous",
+    "acanonical",
+    "acanth",
+    "acantha",
+    "Acanthaceae",
+    "acanthaceous",
+    "acanthad",
+    "Acantharia",
+    "Acanthia",
+    "acanthial",
+    "acanthin",
+    "acanthine",
+    "acanthion",
+    "acanthite",
+    "acanthocarpous",
+    "Acanthocephala",
+    "acanthocephalan",
+    "Acanthocephali",
+    "acanthocephalous",
+    "Acanthocereus",
+    "acanthocladous",
+    "Acanthodea",
+    "acanthodean",
+    "Acanthodei",
+    "Acanthodes",
+    "acanthodian",
+    "Acanthodidae",
+    "Acanthodii",
+    "Acanthodini",
+    "acanthoid",
+    "Acantholimon",
+    "acanthological",
+    "acanthology",
+    "acantholysis",
+    "acanthoma",
+    "Acanthomeridae",
+    "acanthon",
+    "Acanthopanax",
+    "Acanthophis",
+    "acanthophorous",
+    "acanthopod",
+    "acanthopodous",
+    "acanthopomatous",
+    "acanthopore",
+    "acanthopteran",
+    "Acanthopteri",
+    "acanthopterous",
+    "acanthopterygian",
+    "Acanthopterygii",
+    "acanthosis",
+    "acanthous",
+    "Acanthuridae",
+    "Acanthurus",
+    "acanthus",
+    "acapnia",
+    "acapnial",
+    "acapsular",
+    "acapu",
+    "acapulco",
+    "acara",
+    "Acarapis",
+    "acardia",
+    "acardiac",
+    "acari",
+    "acarian",
+    "acariasis",
+    "acaricidal",
+    "acaricide",
+    "acarid",
+    "Acarida",
+    "Acaridea",
+    "acaridean",
+    "acaridomatium",
+    "acariform",
+    "Acarina",
+    "acarine",
+    "acarinosis",
+    "acarocecidium",
+    "acarodermatitis",
+    "acaroid",
+    "acarol",
+    "acarologist",
+    "acarology",
+    "acarophilous",
+    "acarophobia",
+    "acarotoxic",
+    "acarpelous",
+    "acarpous",
+    "Acarus",
+    "Acastus",
+    "acatalectic",
+    "acatalepsia",
+    "acatalepsy",
+    "acataleptic",
+    "acatallactic",
+    "acatamathesia",
+    "acataphasia",
+    "acataposis",
+    "acatastasia",
+    "acatastatic",
+    "acate",
+    "acategorical",
+    "acatery",
+    "acatharsia",
+    "acatharsy",
+    "acatholic",
+    "acaudal",
+    "acaudate",
+    "acaulescent",
+    "acauline",
+    "acaulose",
+    "acaulous",
+    "acca",
+    "accede",
+    "accedence",
+    "acceder",
+    "accelerable",
+    "accelerando",
+    "accelerant",
+    "accelerate",
+    "accelerated",
+    "acceleratedly",
+    "acceleration",
+    "accelerative",
+    "accelerator",
+    "acceleratory",
+    "accelerograph",
+    "accelerometer",
+    "accend",
+    "accendibility",
+    "accendible",
+    "accension",
+    "accensor",
+    "accent",
+    "accentless",
+    "accentor",
+    "accentuable",
+    "accentual",
+    "accentuality",
+    "accentually",
+    "accentuate",
+    "accentuation",
+    "accentuator",
+    "accentus",
+    "accept",
+    "acceptability",
+    "acceptable",
+    "acceptableness",
+    "acceptably",
+    "acceptance"
+];
+var binASTLoaded = false
+var wordCount = words.length;
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/binast/serve.py
@@ -0,0 +1,35 @@
+# Serve BinAST encoded JS if the client requests it.
+
+import re
+import os.path
+import urlparse
+
+JavaScriptMimeType = "application/javascript"
+BinASTMimeType = "application/javascript-binast"
+
+SourceTextExtension = ".js"
+BinASTExtension = ".binjs"
+
+def clientAcceptsBinAST(request):
+    if "accept" not in request.headers:
+        return False
+
+    encodings = [s.strip().lower() for s in request.headers["accept"].split(",")]
+    return BinASTMimeType in encodings
+
+def main(request, response):
+    params = urlparse.parse_qs(request.url_parts[3])
+    name = params['name'][0]
+    if not re.match(r'\w+$', name):
+        raise Exception("Bad name parameter: " + name)
+
+    if clientAcceptsBinAST(request):
+        mimeType = BinASTMimeType
+        extension = BinASTExtension
+    else:
+        mimeType = JavaScriptMimeType
+        extension = SourceTextExtension
+
+    scriptDir = os.path.dirname(__file__)
+    contentPath = os.path.join(scriptDir, name + extension)
+    return [("Content-Type", mimeType)], open(contentPath, "rb")
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..307ad0a68b1d5aa27c4ab35f6bd628cb919959fa
GIT binary patch
literal 202
zc${6$u?oU46a~;2N)hP?I7%tb?oKI|QfWIR#icl;%~KwPq`U^vAN6}IE`oPH_ujl}
z&x-Y>6ip-K6%Dw6;dOi``->*jgM|}}R+A3sU7Dja=<d~*-4JLe<{2n}S+pAc1c@36
zr;z=(JsoNcK-%WsThOj}P6YG3toEvtRa>gxdlqd6bV7Bt*9J`d(A0ObD1IW1BgWYh
L&NyFlwpo1u6of<E
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/binast/small.https.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<title>Check whether we can load small BinAST file</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+    setup({allow_uncaught_exception: true});
+
+    var hadError = false;
+    function setError() { hadError = true; }
+
+    window.addEventListener("error", setError);
+
+    const test_load = async_test("Check we can load BinAST over HTTPS");
+    window.addEventListener("load", test_load.step_func_done(ev => {
+      assert_equals(hadError, false, "Didn't expect an error event");
+      assert_equals(binASTLoaded, true, "Expected to load BinAST version");
+    }));
+
+</script>
+<script src="./serve.py?name=small" onerror="setError()"></script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/binast/small.js
@@ -0,0 +1,2 @@
+// JS version of the script.  The BinAST version sets the flag to true.
+var binASTLoaded = false;