Merge inbound to mozilla-central. a=merge
authorBrindusan Cristian <cbrindusan@mozilla.com>
Tue, 26 Mar 2019 02:26:12 +0200
changeset 465967 55261bc2e4654da9be5d3f9bddd3dec6bd177eb9
parent 465966 4146cbd2103cb97b239033f215eb750f1ca880f1 (current diff)
parent 465932 482787f1c704049f287b922f6da05fedaec1ba1e (diff)
child 465968 3ab51758e9168a1c2e55949e06fdcc5cd80f221c
child 466003 6268d2aa99da8121c5e688fb08792eef51bafab4
child 466055 df256c98c3fdb56de9bc2900803a6c4d58671100
push id112547
push usercbrindusan@mozilla.com
push dateTue, 26 Mar 2019 00:30:34 +0000
treeherdermozilla-inbound@3ab51758e916 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone68.0a1
first release with
nightly linux32
55261bc2e465 / 68.0a1 / 20190326002640 / files
nightly linux64
55261bc2e465 / 68.0a1 / 20190326002640 / files
nightly mac
55261bc2e465 / 68.0a1 / 20190326002640 / files
nightly win32
55261bc2e465 / 68.0a1 / 20190326002640 / files
nightly win64
55261bc2e465 / 68.0a1 / 20190326002640 / 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
--- a/devtools/client/debugger/new/src/actions/navigation.js
+++ b/devtools/client/debugger/new/src/actions/navigation.js
@@ -36,16 +36,17 @@ export function willNavigate(event: Obje
   return function({ dispatch, getState, client, sourceMaps }: ThunkArgs) {
     sourceMaps.clearSourceMaps();
     clearWasmStates();
     clearDocuments();
     clearSymbols();
     clearASTs();
     clearScopes();
     clearSources();
+    client.detachWorkers();
     dispatch(navigate(event.url));
   };
 }
 
 export function navigate(url: string) {
   return async function({ dispatch, getState }: ThunkArgs) {
     sourceQueue.clear();
     const thread = getMainThread(getState());
--- a/devtools/client/debugger/new/src/actions/tests/navigation.spec.js
+++ b/devtools/client/debugger/new/src/actions/tests/navigation.spec.js
@@ -22,17 +22,18 @@ const {
   getFileSearchResults
 } = selectors;
 
 const threadClient = {
   sourceContents: async () => ({
     source: "function foo1() {\n  const foo = 5; return foo;\n}",
     contentType: "text/javascript"
   }),
-  getBreakpointPositions: async () => ({})
+  getBreakpointPositions: async () => ({}),
+  detachWorkers: () => {}
 };
 
 describe("navigation", () => {
   it("connect sets the debuggeeUrl", async () => {
     const { dispatch, getState } = createStore({
       fetchWorkers: () => Promise.resolve([]),
       getMainThread: () => "FakeThread"
     });
@@ -62,37 +63,37 @@ describe("navigation", () => {
 
     results = getTextSearchResults(getState());
     expect(results).toHaveLength(0);
     expect(getTextSearchQuery(getState())).toEqual("");
     expect(getTextSearchStatus(getState())).toEqual("INITIAL");
   });
 
   it("navigation removes activeSearch 'project' value", async () => {
-    const { dispatch, getState } = createStore();
+    const { dispatch, getState } = createStore(threadClient);
     dispatch(actions.setActiveSearch("project"));
     expect(getActiveSearch(getState())).toBe("project");
 
     await dispatch(actions.willNavigate("will-navigate"));
     expect(getActiveSearch(getState())).toBe(null);
   });
 
   it("navigation clears the file-search query", async () => {
-    const { dispatch, getState } = createStore();
+    const { dispatch, getState } = createStore(threadClient);
 
     dispatch(actions.setFileSearchQuery("foobar"));
     expect(getFileSearchQuery(getState())).toBe("foobar");
 
     await dispatch(actions.willNavigate("will-navigate"));
 
     expect(getFileSearchQuery(getState())).toBe("");
   });
 
   it("navigation clears the file-search results", async () => {
-    const { dispatch, getState } = createStore();
+    const { dispatch, getState } = createStore(threadClient);
 
     const searchResults = [{ line: 1, ch: 3 }, { line: 3, ch: 2 }];
     dispatch(actions.updateSearchResults(2, 3, searchResults));
     expect(getFileSearchResults(getState())).toEqual({
       count: 2,
       index: 2,
       matchIndex: 1,
       matches: searchResults
@@ -104,16 +105,16 @@ describe("navigation", () => {
       count: 0,
       index: -1,
       matchIndex: -1,
       matches: []
     });
   });
 
   it("navigation removes activeSearch 'file' value", async () => {
-    const { dispatch, getState } = createStore();
+    const { dispatch, getState } = createStore(threadClient);
     dispatch(actions.setActiveSearch("file"));
     expect(getActiveSearch(getState())).toBe("file");
 
     await dispatch(actions.willNavigate("will-navigate"));
     expect(getActiveSearch(getState())).toBe(null);
   });
 });
--- a/devtools/client/debugger/new/src/client/firefox/commands.js
+++ b/devtools/client/debugger/new/src/client/firefox/commands.js
@@ -182,16 +182,22 @@ function locationKey(location: Breakpoin
   const sourceId = location.sourceId || "";
   return `${(sourceUrl: any)}:${sourceId}:${line}:${(column: any)}`;
 }
 
 function waitForWorkers(shouldWait: boolean) {
   shouldWaitForWorkers = shouldWait;
 }
 
+function detachWorkers() {
+  for (const thread of listWorkerThreadClients()) {
+    thread.detach();
+  }
+}
+
 function maybeGenerateLogGroupId(options) {
   if (options.logValue && tabTarget.traits && tabTarget.traits.canRewind) {
     return { ...options, logGroupId: `logGroup-${Math.random()}` };
   }
   return options;
 }
 
 function maybeClearLogpoint(location: BreakpointLocation) {
@@ -480,12 +486,13 @@ const clientCommands = {
   pauseOnExceptions,
   fetchSources,
   registerSourceActor,
   fetchWorkers,
   getMainThread,
   sendPacket,
   setSkipPausing,
   setEventListenerBreakpoints,
-  waitForWorkers
+  waitForWorkers,
+  detachWorkers
 };
 
 export { setupCommands, clientCommands };
--- a/js/src/builtin/Promise.h
+++ b/js/src/builtin/Promise.h
@@ -474,16 +474,25 @@ class OffThreadPromiseRuntimeState;
 // requires.)
 //
 // OffThreadPromiseTasks are not limited to use with helper threads. For
 // example, a function returning a promise of the result of a network operation
 // could provide the code collecting the incoming data with an
 // OffThreadPromiseTask for the promise, and let the embedding's network I/O
 // threads call dispatchResolveAndDestroy.
 //
+// OffThreadPromiseTask may also be used purely on the main thread, as a way to
+// "queue a task" in HTML terms. Note that a "task" is not the same as a
+// "microtask" and there are separate queues for tasks and microtasks that are
+// drained at separate times in the browser. The task queue is implemented by
+// the browser's main event loop. The microtask queue is implemented
+// by JS::JobQueue, used for promises and gets drained before returning to
+// the event loop. Thus OffThreadPromiseTask can only be used when the spec
+// says "queue a task", as the WebAssembly APIs do.
+//
 // An OffThreadPromiseTask has a JSContext, and must be constructed and have its
 // 'init' method called on that JSContext's thread. Once initialized, its
 // dispatchResolveAndDestroy method may be called from any thread. This is the
 // only safe way to destruct an OffThreadPromiseTask; doing so ensures the
 // OffThreadPromiseTask's destructor will run on the JSContext's thread, either
 // from the event loop or during shutdown.
 //
 // OffThreadPromiseTask::dispatchResolveAndDestroy uses the
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/async-instantiate.js
@@ -0,0 +1,47 @@
+const { Module, Instance, Global, instantiate, instantiateStreaming } = WebAssembly;
+
+const g = new Global({value: "i32", mutable:true}, 0);
+
+const code = wasmTextToBinary(`(module
+    (global $g (import "" "g") (mut i32))
+    (func $start (set_global $g (i32.add (get_global $g) (i32.const 1))))
+    (start $start)
+)`);
+const module = new Module(code);
+
+const importObj = { '': { get g() { g.value++; return g } } };
+
+g.value = 0;
+new Instance(module, importObj);
+assertEq(g.value, 2);
+
+g.value = 0;
+instantiate(module, importObj).then(i => {
+  assertEq(i instanceof Instance, true);
+  assertEq(g.value, 2);
+  g.value++;
+});
+assertEq(g.value, 1);
+drainJobQueue();
+assertEq(g.value, 3);
+
+g.value = 0;
+instantiate(code, importObj).then(({module,instance}) => {
+    assertEq(module instanceof Module, true);
+    assertEq(instance instanceof Instance, true);
+    assertEq(g.value, 2); g.value++; }
+);
+drainJobQueue();
+assertEq(g.value, 3);
+
+if (wasmStreamingIsSupported()) {
+  g.value = 0;
+  instantiateStreaming(code, importObj).then(({module,instance}) => {
+      assertEq(module instanceof Module, true);
+      assertEq(instance instanceof Instance, true);
+      assertEq(g.value, 2);
+      g.value++;
+  });
+  drainJobQueue();
+  assertEq(g.value, 3);
+}
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2142,29 +2142,32 @@ namespace JS {
  *
  * After storeOptimizedEncoding() is called, on cache hit, the embedding
  * may call consumeOptimizedEncoding() instead of consumeChunk()/streamEnd().
  * The embedding must ensure that the GetOptimizedEncodingBuildId() (see
  * js/BuildId.h) at the time when an optimized encoding is created is the same
  * as when it is later consumed.
  */
 
+using OptimizedEncodingBytes = js::Vector<uint8_t, 0, js::SystemAllocPolicy>;
+using UniqueOptimizedEncodingBytes = js::UniquePtr<OptimizedEncodingBytes>;
+
 class OptimizedEncodingListener {
  protected:
   virtual ~OptimizedEncodingListener() {}
 
  public:
   // SpiderMonkey will hold an outstanding reference count as long as it holds
   // a pointer to OptimizedEncodingListener.
   virtual MozExternalRefCountType MOZ_XPCOM_ABI AddRef() = 0;
   virtual MozExternalRefCountType MOZ_XPCOM_ABI Release() = 0;
 
   // SpiderMonkey may optionally call storeOptimizedEncoding() after it has
   // finished processing a streamed resource.
-  virtual void storeOptimizedEncoding(const uint8_t* bytes, size_t length) = 0;
+  virtual void storeOptimizedEncoding(UniqueOptimizedEncodingBytes bytes) = 0;
 };
 
 class JS_PUBLIC_API StreamConsumer {
  protected:
   // AsyncStreamConsumers are created and destroyed by SpiderMonkey.
   StreamConsumer() = default;
   virtual ~StreamConsumer() = default;
 
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -6904,31 +6904,30 @@ class StreamCacheEntry : public AtomicRe
   }
   MozExternalRefCountType MOZ_XPCOM_ABI Release() override {
     AtomicBase::Release();
     return 0;  // unused
   }
 
   const Uint8Vector& bytes() const { return bytes_; }
 
-  void storeOptimizedEncoding(const uint8_t* srcBytes,
-                              size_t srcLength) override {
-    MOZ_ASSERT(srcLength > 0);
+  void storeOptimizedEncoding(JS::UniqueOptimizedEncodingBytes src) override {
+    MOZ_ASSERT(src->length() > 0);
 
     // Tolerate races since a single StreamCacheEntry object can be used as
     // the source of multiple streaming compilations.
     auto dstBytes = optimized_.lock();
     if (dstBytes->length() > 0) {
       return;
     }
 
-    if (!dstBytes->resize(srcLength)) {
+    if (!dstBytes->resize(src->length())) {
       return;
     }
-    memcpy(dstBytes->begin(), srcBytes, srcLength);
+    memcpy(dstBytes->begin(), src->begin(), src->length());
   }
 
   bool hasOptimizedEncoding() const { return !optimized_.lock()->empty(); }
   const Uint8Vector& optimizedEncoding() const {
     return optimized_.lock().get();
   }
 };
 
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -6774,31 +6774,30 @@ static bool CheckBuffer(JSContext* cx, c
   }
 
   MOZ_ASSERT(buffer->isPreparedForAsmJS());
   return true;
 }
 
 static bool GetImports(JSContext* cx, const AsmJSMetadata& metadata,
                        HandleValue globalVal, HandleValue importVal,
-                       MutableHandle<FunctionVector> funcImports,
-                       MutableHandleValVector valImports) {
+                       ImportValues* imports) {
   Rooted<FunctionVector> ffis(cx, FunctionVector(cx));
   if (!ffis.resize(metadata.numFFIs)) {
     return false;
   }
 
   for (const AsmJSGlobal& global : metadata.asmJSGlobals) {
     switch (global.which()) {
       case AsmJSGlobal::Variable: {
         Maybe<LitValPOD> litVal;
         if (!ValidateGlobalVariable(cx, global, importVal, &litVal)) {
           return false;
         }
-        if (!valImports.append(Val(litVal->asLitVal()))) {
+        if (!imports->globalValues.append(Val(litVal->asLitVal()))) {
           return false;
         }
         break;
       }
       case AsmJSGlobal::FFI:
         if (!ValidateFFI(cx, global, importVal, &ffis)) {
           return false;
         }
@@ -6818,17 +6817,17 @@ static bool GetImports(JSContext* cx, co
         if (!ValidateConstant(cx, global, globalVal)) {
           return false;
         }
         break;
     }
   }
 
   for (const AsmJSImport& import : metadata.asmJSImports) {
-    if (!funcImports.append(ffis[import.ffiIndex()])) {
+    if (!imports->funcs.append(ffis[import.ffiIndex()])) {
       return false;
     }
   }
 
   return true;
 }
 
 static bool TryInstantiate(JSContext* cx, CallArgs args, const Module& module,
@@ -6840,40 +6839,37 @@ static bool TryInstantiate(JSContext* cx
   HandleValue bufferVal = args.get(2);
 
   // Re-check HasCompilerSupport(cx) since this varies per-thread and
   // 'module' may have been produced on a parser thread.
   if (!HasCompilerSupport(cx)) {
     return LinkFail(cx, "no compiler support");
   }
 
-  RootedArrayBufferObjectMaybeShared buffer(cx);
-  RootedWasmMemoryObject memory(cx);
+  Rooted<ImportValues> imports(cx);
+
   if (module.metadata().usesMemory()) {
+    RootedArrayBufferObjectMaybeShared buffer(cx);
     if (!CheckBuffer(cx, metadata, bufferVal, &buffer)) {
       return false;
     }
 
-    memory = WasmMemoryObject::create(cx, buffer, nullptr);
-    if (!memory) {
-      return false;
-    }
-  }
-
-  RootedValVector valImports(cx);
-  Rooted<FunctionVector> funcs(cx, FunctionVector(cx));
-  if (!GetImports(cx, metadata, globalVal, importVal, &funcs, &valImports)) {
-    return false;
-  }
-
-  Rooted<WasmGlobalObjectVector> globalObjs(cx);
-  Rooted<WasmTableObjectVector> tables(cx);
-  if (!module.instantiate(cx, funcs, tables.get(), memory, valImports,
-                          globalObjs.get(), nullptr, instanceObj))
-    return false;
+    imports.get().memory = WasmMemoryObject::create(cx, buffer, nullptr);
+    if (!imports.get().memory) {
+      return false;
+    }
+  }
+
+  if (!GetImports(cx, metadata, globalVal, importVal, imports.address())) {
+    return false;
+  }
+
+  if (!module.instantiate(cx, imports.get(), nullptr, instanceObj)) {
+    return false;
+  }
 
   exportObj.set(&instanceObj->exportsObj());
   return true;
 }
 
 static bool HandleInstantiationFailure(JSContext* cx, CallArgs args,
                                        const AsmJSMetadata& metadata) {
   RootedAtom name(cx, args.callee().as<JSFunction>().explicitName());
--- a/js/src/wasm/WasmBuiltins.cpp
+++ b/js/src/wasm/WasmBuiltins.cpp
@@ -1218,17 +1218,17 @@ static Maybe<ABIFunctionType> ToBuiltinA
       default:
         return Nothing();
     }
   }
 
   return Some(ABIFunctionType(abiType));
 }
 
-void* wasm::MaybeGetBuiltinThunk(HandleFunction f, const FuncType& funcType) {
+void* wasm::MaybeGetBuiltinThunk(JSFunction* f, const FuncType& funcType) {
   MOZ_ASSERT(builtinThunks);
 
   if (!f->isNative() || !f->hasJitInfo() ||
       f->jitInfo()->type() != JSJitInfo::InlinableNative) {
     return nullptr;
   }
 
   Maybe<ABIFunctionType> abiType = ToBuiltinABIFunctionType(funcType);
--- a/js/src/wasm/WasmBuiltins.h
+++ b/js/src/wasm/WasmBuiltins.h
@@ -88,17 +88,17 @@ bool LookupBuiltinThunk(void* pc, const 
 // executable code has been released.
 
 bool EnsureBuiltinThunksInitialized();
 
 void* HandleThrow(JSContext* cx, WasmFrameIter& iter);
 
 void* SymbolicAddressTarget(SymbolicAddress sym);
 
-void* MaybeGetBuiltinThunk(HandleFunction f, const FuncType& funcType);
+void* MaybeGetBuiltinThunk(JSFunction* f, const FuncType& funcType);
 
 void ReleaseBuiltinThunks();
 
 void* AddressOf(SymbolicAddress imm, jit::ABIFunctionType* abiType);
 
 #ifdef WASM_CODEGEN_DEBUG
 void PrintI32(int32_t val);
 void PrintF32(float val);
--- a/js/src/wasm/WasmInstance.cpp
+++ b/js/src/wasm/WasmInstance.cpp
@@ -1073,18 +1073,18 @@ void CopyValPostBarriered(uint8_t* dst, 
     default: { MOZ_CRASH("unexpected Val type"); }
   }
 }
 
 Instance::Instance(JSContext* cx, Handle<WasmInstanceObject*> object,
                    SharedCode code, UniqueTlsData tlsDataIn,
                    HandleWasmMemoryObject memory, SharedTableVector&& tables,
                    StructTypeDescrVector&& structTypeDescrs,
-                   Handle<FunctionVector> funcImports,
-                   HandleValVector globalImportValues,
+                   const JSFunctionVector& funcImports,
+                   const ValVector& globalImportValues,
                    const WasmGlobalObjectVector& globalObjs,
                    UniqueDebugState maybeDebug)
     : realm_(cx->realm()),
       object_(object),
       jsJitArgsRectifier_(
           cx->runtime()->jitRuntime()->getArgumentsRectifier().value),
       jsJitExceptionHandler_(
           cx->runtime()->jitRuntime()->getExceptionTail().value),
@@ -1115,17 +1115,17 @@ Instance::Instance(JSContext* cx, Handle
   tlsData()->cx = cx;
   tlsData()->resetInterrupt(cx);
   tlsData()->jumpTable = code_->tieringJumpTable();
   tlsData()->addressOfNeedsIncrementalBarrier =
       (uint8_t*)cx->compartment()->zone()->addressOfNeedsIncrementalBarrier();
 
   Tier callerTier = code_->bestTier();
   for (size_t i = 0; i < metadata(callerTier).funcImports.length(); i++) {
-    HandleFunction f = funcImports[i];
+    JSFunction* f = funcImports[i];
     const FuncImport& fi = metadata(callerTier).funcImports[i];
     FuncImportTls& import = funcImportTls(fi);
     import.fun = f;
     if (!isAsmJS() && IsExportedWasmFunction(f)) {
       WasmInstanceObject* calleeInstanceObj =
           ExportedFunctionToInstanceObject(f);
       Instance& calleeInstance = calleeInstanceObj->instance();
       Tier calleeTier = calleeInstance.code().bestTier();
@@ -1166,17 +1166,17 @@ Instance::Instance(JSContext* cx, Handle
 
     uint8_t* globalAddr = globalData() + global.offset();
     switch (global.kind()) {
       case GlobalKind::Import: {
         size_t imported = global.importIndex();
         if (global.isIndirect()) {
           *(void**)globalAddr = globalObjs[imported]->cell();
         } else {
-          CopyValPostBarriered(globalAddr, globalImportValues[imported].get());
+          CopyValPostBarriered(globalAddr, globalImportValues[imported]);
         }
         break;
       }
       case GlobalKind::Variable: {
         const InitExpr& init = global.initExpr();
         switch (init.kind()) {
           case InitExpr::Kind::Constant: {
             if (global.isIndirect()) {
@@ -1188,18 +1188,17 @@ Instance::Instance(JSContext* cx, Handle
           }
           case InitExpr::Kind::GetGlobal: {
             const GlobalDesc& imported = metadata().globals[init.globalIndex()];
 
             // Global-ref initializers cannot reference mutable globals, so
             // the source global should never be indirect.
             MOZ_ASSERT(!imported.isIndirect());
 
-            RootedVal dest(cx,
-                           globalImportValues[imported.importIndex()].get());
+            RootedVal dest(cx, globalImportValues[imported.importIndex()]);
             if (global.isIndirect()) {
               void* address = globalObjs[i]->cell();
               *(void**)globalAddr = address;
               CopyValPostBarriered((uint8_t*)address, dest.get());
             } else {
               CopyValPostBarriered(globalAddr, dest.get());
             }
             break;
--- a/js/src/wasm/WasmInstance.h
+++ b/js/src/wasm/WasmInstance.h
@@ -69,18 +69,18 @@ class Instance {
 
   bool callImport(JSContext* cx, uint32_t funcImportIndex, unsigned argc,
                   const uint64_t* argv, MutableHandleValue rval);
 
  public:
   Instance(JSContext* cx, HandleWasmInstanceObject object, SharedCode code,
            UniqueTlsData tlsData, HandleWasmMemoryObject memory,
            SharedTableVector&& tables, StructTypeDescrVector&& structTypeDescrs,
-           Handle<FunctionVector> funcImports,
-           HandleValVector globalImportValues,
+           const JSFunctionVector& funcImports,
+           const ValVector& globalImportValues,
            const WasmGlobalObjectVector& globalObjs,
            UniqueDebugState maybeDebug);
   ~Instance();
   bool init(JSContext* cx, const DataSegmentVector& dataSegments,
             const ElemSegmentVector& elemSegments);
   void trace(JSTracer* trc);
 
   // Trace any GC roots on the stack, for the frame associated with |wfi|,
--- a/js/src/wasm/WasmJS.cpp
+++ b/js/src/wasm/WasmJS.cpp
@@ -236,34 +236,28 @@ static bool GetProperty(JSContext* cx, H
     return false;
   }
 
   RootedId id(cx, AtomToId(atom));
   return GetProperty(cx, obj, obj, id, v);
 }
 
 static bool GetImports(JSContext* cx, const Module& module,
-                       HandleObject importObj,
-                       MutableHandle<FunctionVector> funcImports,
-                       WasmTableObjectVector& tableImports,
-                       MutableHandleWasmMemoryObject memoryImport,
-                       WasmGlobalObjectVector& globalObjs,
-                       MutableHandleValVector globalImportValues) {
-  const ImportVector& imports = module.imports();
-  if (!imports.empty() && !importObj) {
+                       HandleObject importObj, ImportValues* imports) {
+  if (!module.imports().empty() && !importObj) {
     return ThrowBadImportArg(cx);
   }
 
   const Metadata& metadata = module.metadata();
 
   uint32_t globalIndex = 0;
   const GlobalDescVector& globals = metadata.globals;
   uint32_t tableIndex = 0;
   const TableDescVector& tables = metadata.tables;
-  for (const Import& import : imports) {
+  for (const Import& import : module.imports()) {
     RootedValue v(cx);
     if (!GetProperty(cx, importObj, import.module.get(), &v)) {
       return false;
     }
 
     if (!v.isObject()) {
       JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
                                JSMSG_WASM_BAD_IMPORT_FIELD,
@@ -277,17 +271,17 @@ static bool GetImports(JSContext* cx, co
     }
 
     switch (import.kind) {
       case DefinitionKind::Function: {
         if (!IsFunctionObject(v)) {
           return ThrowBadImportType(cx, import.field.get(), "Function");
         }
 
-        if (!funcImports.append(&v.toObject().as<JSFunction>())) {
+        if (!imports->funcs.append(&v.toObject().as<JSFunction>())) {
           return false;
         }
 
         break;
       }
       case DefinitionKind::Table: {
         const uint32_t index = tableIndex++;
         if (!v.isObject() || !v.toObject().is<WasmTableObject>()) {
@@ -296,28 +290,28 @@ static bool GetImports(JSContext* cx, co
 
         RootedWasmTableObject obj(cx, &v.toObject().as<WasmTableObject>());
         if (obj->table().kind() != tables[index].kind) {
           JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
                                    JSMSG_WASM_BAD_TBL_TYPE_LINK);
           return false;
         }
 
-        if (!tableImports.append(obj)) {
+        if (!imports->tables.append(obj)) {
           return false;
         }
         break;
       }
       case DefinitionKind::Memory: {
         if (!v.isObject() || !v.toObject().is<WasmMemoryObject>()) {
           return ThrowBadImportType(cx, import.field.get(), "Memory");
         }
 
-        MOZ_ASSERT(!memoryImport);
-        memoryImport.set(&v.toObject().as<WasmMemoryObject>());
+        MOZ_ASSERT(!imports->memory);
+        imports->memory = &v.toObject().as<WasmMemoryObject>();
         break;
       }
       case DefinitionKind::Global: {
         const uint32_t index = globalIndex++;
         const GlobalDesc& global = globals[index];
         MOZ_ASSERT(global.importIndex() == index);
 
         RootedVal val(cx);
@@ -330,21 +324,22 @@ static bool GetImports(JSContext* cx, co
             return false;
           }
           if (obj->type() != global.type()) {
             JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
                                      JSMSG_WASM_BAD_GLOB_TYPE_LINK);
             return false;
           }
 
-          if (globalObjs.length() <= index && !globalObjs.resize(index + 1)) {
+          if (imports->globalObjs.length() <= index &&
+              !imports->globalObjs.resize(index + 1)) {
             ReportOutOfMemory(cx);
             return false;
           }
-          globalObjs[index] = obj;
+          imports->globalObjs[index] = obj;
           obj->val(&val);
         } else {
           if (IsNumberType(global.type())) {
             if (!v.isNumber()) {
               return ThrowBadImportType(cx, import.field.get(), "Number");
             }
           } else {
             MOZ_ASSERT(global.type().isReference());
@@ -366,17 +361,17 @@ static bool GetImports(JSContext* cx, co
             return false;
           }
 
           if (!ToWebAssemblyValue(cx, global.type(), v, &val)) {
             return false;
           }
         }
 
-        if (!globalImportValues.append(val)) {
+        if (!imports->globalValues.append(val)) {
           return false;
         }
 
         break;
       }
     }
   }
 
@@ -445,45 +440,38 @@ bool wasm::Eval(JSContext* cx, Handle<Ty
       JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
                                JSMSG_WASM_COMPILE_ERROR, error.get());
       return false;
     }
     ReportOutOfMemory(cx);
     return false;
   }
 
-  Rooted<FunctionVector> funcs(cx, FunctionVector(cx));
-  Rooted<WasmTableObjectVector> tables(cx);
-  RootedWasmMemoryObject memory(cx);
-  Rooted<WasmGlobalObjectVector> globalObjs(cx);
-
-  RootedValVector globals(cx);
-  if (!GetImports(cx, *module, importObj, &funcs, tables.get(), &memory,
-                  globalObjs.get(), &globals)) {
+  Rooted<ImportValues> imports(cx);
+  if (!GetImports(cx, *module, importObj, imports.address())) {
     return false;
   }
 
-  return module->instantiate(cx, funcs, tables.get(), memory, globals,
-                             globalObjs.get(), nullptr, instanceObj);
+  return module->instantiate(cx, imports.get(), nullptr, instanceObj);
 }
 
 struct MOZ_STACK_CLASS SerializeListener : JS::OptimizedEncodingListener {
   // MOZ_STACK_CLASS means these can be nops.
   MozExternalRefCountType MOZ_XPCOM_ABI AddRef() override { return 0; }
   MozExternalRefCountType MOZ_XPCOM_ABI Release() override { return 0; }
 
   DebugOnly<bool> called = false;
   Bytes* serialized;
   explicit SerializeListener(Bytes* serialized) : serialized(serialized) {}
 
-  void storeOptimizedEncoding(const uint8_t* bytes, size_t length) override {
+  void storeOptimizedEncoding(JS::UniqueOptimizedEncodingBytes bytes) override {
     MOZ_ASSERT(!called);
     called = true;
-    if (serialized->resize(length)) {
-      memcpy(serialized->begin(), bytes, length);
+    if (serialized->resize(bytes->length())) {
+      memcpy(serialized->begin(), bytes->begin(), bytes->length());
     }
   }
 };
 
 bool wasm::CompileAndSerialize(const ShareableBytes& bytecode,
                                Bytes* serialized) {
   MutableCompileArgs compileArgs = js_new<CompileArgs>(ScriptedCaller());
   if (!compileArgs) {
@@ -1245,18 +1233,18 @@ void WasmInstanceObject::trace(JSTracer*
 }
 
 /* static */
 WasmInstanceObject* WasmInstanceObject::create(
     JSContext* cx, SharedCode code, const DataSegmentVector& dataSegments,
     const ElemSegmentVector& elemSegments, UniqueTlsData tlsData,
     HandleWasmMemoryObject memory, SharedTableVector&& tables,
     StructTypeDescrVector&& structTypeDescrs,
-    Handle<FunctionVector> funcImports, const GlobalDescVector& globals,
-    HandleValVector globalImportValues,
+    const JSFunctionVector& funcImports, const GlobalDescVector& globals,
+    const ValVector& globalImportValues,
     const WasmGlobalObjectVector& globalObjs, HandleObject proto,
     UniqueDebugState maybeDebug) {
   UniquePtr<ExportMap> exports = js::MakeUnique<ExportMap>();
   if (!exports) {
     ReportOutOfMemory(cx);
     return nullptr;
   }
 
@@ -1340,37 +1328,16 @@ static bool GetImportArg(JSContext* cx, 
     if (!callArgs[1].isObject()) {
       return ThrowBadImportArg(cx);
     }
     importObj.set(&callArgs[1].toObject());
   }
   return true;
 }
 
-static bool Instantiate(JSContext* cx, const Module& module,
-                        HandleObject importObj,
-                        MutableHandleWasmInstanceObject instanceObj) {
-  RootedObject instanceProto(
-      cx, &cx->global()->getPrototype(JSProto_WasmInstance).toObject());
-
-  Rooted<FunctionVector> funcs(cx, FunctionVector(cx));
-  Rooted<WasmTableObjectVector> tables(cx);
-  RootedWasmMemoryObject memory(cx);
-  Rooted<WasmGlobalObjectVector> globalObjs(cx);
-
-  RootedValVector globals(cx);
-  if (!GetImports(cx, module, importObj, &funcs, tables.get(), &memory,
-                  globalObjs.get(), &globals)) {
-    return false;
-  }
-
-  return module.instantiate(cx, funcs, tables.get(), memory, globals,
-                            globalObjs.get(), instanceProto, instanceObj);
-}
-
 /* static */
 bool WasmInstanceObject::construct(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 
   Log(cx, "sync new Instance() started");
 
   if (!ThrowIfNotConstructing(cx, args, "Instance")) {
     return false;
@@ -1387,18 +1354,26 @@ bool WasmInstanceObject::construct(JSCon
     return false;
   }
 
   RootedObject importObj(cx);
   if (!GetImportArg(cx, args, &importObj)) {
     return false;
   }
 
+  RootedObject instanceProto(
+      cx, &cx->global()->getPrototype(JSProto_WasmInstance).toObject());
+
+  Rooted<ImportValues> imports(cx);
+  if (!GetImports(cx, *module, importObj, imports.address())) {
+    return false;
+  }
+
   RootedWasmInstanceObject instanceObj(cx);
-  if (!Instantiate(cx, *module, importObj, &instanceObj)) {
+  if (!module->instantiate(cx, imports.get(), instanceProto, &instanceObj)) {
     return false;
   }
 
   Log(cx, "sync new Instance() succeeded");
 
   args.rval().setObject(*instanceObj);
   return true;
 }
@@ -2861,66 +2836,114 @@ static bool Reject(JSContext* cx, const 
   if (!errorObj) {
     return false;
   }
 
   RootedValue rejectionValue(cx, ObjectValue(*errorObj));
   return PromiseObject::reject(cx, promise, rejectionValue);
 }
 
-static bool Resolve(JSContext* cx, const Module& module,
-                    Handle<PromiseObject*> promise, bool instantiate,
-                    HandleObject importObj, const UniqueCharsVector& warnings,
-                    const char* methodSuffix = "") {
-  if (!ReportCompileWarnings(cx, warnings)) {
+enum class Ret { Pair, Instance };
+
+class AsyncInstantiateTask : public OffThreadPromiseTask {
+  SharedModule module_;
+  PersistentRooted<ImportValues> imports_;
+  Ret ret_;
+
+ public:
+  AsyncInstantiateTask(JSContext* cx, const Module& module, Ret ret,
+                       Handle<PromiseObject*> promise)
+      : OffThreadPromiseTask(cx, promise),
+        module_(&module),
+        imports_(cx),
+        ret_(ret) {}
+
+  ImportValues& imports() { return imports_.get(); }
+
+  bool resolve(JSContext* cx, Handle<PromiseObject*> promise) override {
+    RootedObject instanceProto(
+        cx, &cx->global()->getPrototype(JSProto_WasmInstance).toObject());
+
+    RootedWasmInstanceObject instanceObj(cx);
+    if (!module_->instantiate(cx, imports_.get(), instanceProto,
+                              &instanceObj)) {
+      return RejectWithPendingException(cx, promise);
+    }
+
+    RootedValue resolutionValue(cx);
+    if (ret_ == Ret::Instance) {
+      resolutionValue = ObjectValue(*instanceObj);
+    } else {
+      RootedObject resultObj(cx, JS_NewPlainObject(cx));
+      if (!resultObj) {
+        return RejectWithPendingException(cx, promise);
+      }
+
+      RootedObject moduleProto(
+          cx, &cx->global()->getPrototype(JSProto_WasmModule).toObject());
+      RootedObject moduleObj(
+          cx, WasmModuleObject::create(cx, *module_, moduleProto));
+      if (!moduleObj) {
+        return RejectWithPendingException(cx, promise);
+      }
+
+      RootedValue val(cx, ObjectValue(*moduleObj));
+      if (!JS_DefineProperty(cx, resultObj, "module", val, JSPROP_ENUMERATE)) {
+        return RejectWithPendingException(cx, promise);
+      }
+
+      val = ObjectValue(*instanceObj);
+      if (!JS_DefineProperty(cx, resultObj, "instance", val,
+                             JSPROP_ENUMERATE)) {
+        return RejectWithPendingException(cx, promise);
+      }
+
+      resolutionValue = ObjectValue(*resultObj);
+    }
+
+    if (!PromiseObject::resolve(cx, promise, resolutionValue)) {
+      return RejectWithPendingException(cx, promise);
+    }
+
+    Log(cx, "async instantiate succeeded");
+    return true;
+  }
+};
+
+static bool AsyncInstantiate(JSContext* cx, const Module& module,
+                             HandleObject importObj, Ret ret,
+                             Handle<PromiseObject*> promise) {
+  auto task = js::MakeUnique<AsyncInstantiateTask>(cx, module, ret, promise);
+  if (!task || !task->init(cx)) {
     return false;
   }
 
+  if (!GetImports(cx, module, importObj, &task->imports())) {
+    return RejectWithPendingException(cx, promise);
+  }
+
+  task.release()->dispatchResolveAndDestroy();
+  return true;
+}
+
+static bool ResolveCompile(JSContext* cx, const Module& module,
+                           Handle<PromiseObject*> promise) {
   RootedObject proto(
       cx, &cx->global()->getPrototype(JSProto_WasmModule).toObject());
   RootedObject moduleObj(cx, WasmModuleObject::create(cx, module, proto));
   if (!moduleObj) {
     return RejectWithPendingException(cx, promise);
   }
 
-  RootedValue resolutionValue(cx);
-  if (instantiate) {
-    RootedWasmInstanceObject instanceObj(cx);
-    if (!Instantiate(cx, module, importObj, &instanceObj)) {
-      return RejectWithPendingException(cx, promise);
-    }
-
-    RootedObject resultObj(cx, JS_NewPlainObject(cx));
-    if (!resultObj) {
-      return RejectWithPendingException(cx, promise);
-    }
-
-    RootedValue val(cx, ObjectValue(*moduleObj));
-    if (!JS_DefineProperty(cx, resultObj, "module", val, JSPROP_ENUMERATE)) {
-      return RejectWithPendingException(cx, promise);
-    }
-
-    val = ObjectValue(*instanceObj);
-    if (!JS_DefineProperty(cx, resultObj, "instance", val, JSPROP_ENUMERATE)) {
-      return RejectWithPendingException(cx, promise);
-    }
-
-    resolutionValue = ObjectValue(*resultObj);
-  } else {
-    MOZ_ASSERT(!importObj);
-    resolutionValue = ObjectValue(*moduleObj);
-  }
-
+  RootedValue resolutionValue(cx, ObjectValue(*moduleObj));
   if (!PromiseObject::resolve(cx, promise, resolutionValue)) {
     return RejectWithPendingException(cx, promise);
   }
 
-  Log(cx, "async %s%s() succeeded", (instantiate ? "instantiate" : "compile"),
-      methodSuffix);
-
+  Log(cx, "async compile succeeded");
   return true;
 }
 
 struct CompileBufferTask : PromiseHelperTask {
   MutableBytes bytecode;
   SharedCompileArgs compileArgs;
   UniqueChars error;
   UniqueCharsVector warnings;
@@ -2945,19 +2968,26 @@ struct CompileBufferTask : PromiseHelper
     return PromiseHelperTask::init(cx);
   }
 
   void execute() override {
     module = CompileBuffer(*compileArgs, *bytecode, &error, &warnings);
   }
 
   bool resolve(JSContext* cx, Handle<PromiseObject*> promise) override {
-    return module
-               ? Resolve(cx, *module, promise, instantiate, importObj, warnings)
-               : Reject(cx, *compileArgs, promise, error);
+    if (!module) {
+      return Reject(cx, *compileArgs, promise, error);
+    }
+    if (!ReportCompileWarnings(cx, warnings)) {
+      return false;
+    }
+    if (instantiate) {
+      return AsyncInstantiate(cx, *module, importObj, Ret::Pair, promise);
+    }
+    return ResolveCompile(cx, *module, promise);
   }
 };
 
 static bool RejectWithPendingException(JSContext* cx,
                                        Handle<PromiseObject*> promise,
                                        CallArgs& callArgs) {
   if (!RejectWithPendingException(cx, promise)) {
     return false;
@@ -3058,27 +3088,19 @@ static bool WebAssembly_instantiate(JSCo
   RootedObject firstArg(cx);
   RootedObject importObj(cx);
   if (!GetInstantiateArgs(cx, callArgs, &firstArg, &importObj)) {
     return RejectWithPendingException(cx, promise, callArgs);
   }
 
   const Module* module;
   if (IsModuleObject(firstArg, &module)) {
-    RootedWasmInstanceObject instanceObj(cx);
-    if (!Instantiate(cx, *module, importObj, &instanceObj)) {
-      return RejectWithPendingException(cx, promise, callArgs);
-    }
-
-    RootedValue resolutionValue(cx, ObjectValue(*instanceObj));
-    if (!PromiseObject::resolve(cx, promise, resolutionValue)) {
+    if (!AsyncInstantiate(cx, *module, importObj, Ret::Instance, promise)) {
       return false;
     }
-
-    Log(cx, "async instantiate() succeeded");
   } else {
     auto task = cx->make_unique<CompileBufferTask>(cx, promise, importObj);
     if (!task || !task->init(cx, "WebAssembly.instantiate")) {
       return false;
     }
 
     if (!GetBufferSource(cx, firstArg, JSMSG_WASM_BAD_BUF_MOD_ARG,
                          &task->bytecode)) {
@@ -3405,18 +3427,23 @@ class CompileStreamTask : public Promise
 
   // Called on a JS thread after streaming compilation completes/errors:
 
   bool resolve(JSContext* cx, Handle<PromiseObject*> promise) override {
     MOZ_ASSERT(streamState_.lock() == Closed);
 
     if (module_) {
       MOZ_ASSERT(!streamFailed_ && !streamError_ && !compileError_);
-      return Resolve(cx, *module_, promise, instantiate_, importObj_, warnings_,
-                     "Streaming");
+      if (!ReportCompileWarnings(cx, warnings_)) {
+        return false;
+      }
+      if (instantiate_) {
+        return AsyncInstantiate(cx, *module_, importObj_, Ret::Pair, promise);
+      }
+      return ResolveCompile(cx, *module_, promise);
     }
 
     if (streamError_) {
       return RejectWithStreamErrorNumber(cx, *streamError_, promise);
     }
 
     return Reject(cx, *compileArgs_, promise, compileError_);
   }
--- a/js/src/wasm/WasmJS.h
+++ b/js/src/wasm/WasmJS.h
@@ -240,18 +240,19 @@ class WasmInstanceObject : public Native
   static WasmInstanceObject* create(
       JSContext* cx, RefPtr<const wasm::Code> code,
       const wasm::DataSegmentVector& dataSegments,
       const wasm::ElemSegmentVector& elemSegments, wasm::UniqueTlsData tlsData,
       HandleWasmMemoryObject memory,
       Vector<RefPtr<wasm::Table>, 0, SystemAllocPolicy>&& tables,
       GCVector<HeapPtr<StructTypeDescr*>, 0, SystemAllocPolicy>&&
           structTypeDescrs,
-      Handle<FunctionVector> funcImports, const wasm::GlobalDescVector& globals,
-      wasm::HandleValVector globalImportValues,
+      const JSFunctionVector& funcImports,
+      const wasm::GlobalDescVector& globals,
+      const wasm::ValVector& globalImportValues,
       const WasmGlobalObjectVector& globalObjs, HandleObject proto,
       UniquePtr<wasm::DebugState> maybeDebug);
   void initExportsObj(JSObject& exportsObj);
 
   wasm::Instance& instance() const;
   JSObject& exportsObj() const;
 
   static bool getExportedFunction(JSContext* cx,
--- a/js/src/wasm/WasmModule.cpp
+++ b/js/src/wasm/WasmModule.cpp
@@ -305,24 +305,24 @@ MutableModule Module::deserialize(const 
 
   return js_new<Module>(*code, std::move(imports), std::move(exports),
                         std::move(dataSegments), std::move(elemSegments),
                         std::move(customSections));
 }
 
 void Module::serialize(const LinkData& linkData,
                        JS::OptimizedEncodingListener& listener) const {
-  Vector<uint8_t, 0, SystemAllocPolicy> bytes;
-  if (!bytes.resize(serializedSize(linkData))) {
+  auto bytes = MakeUnique<JS::OptimizedEncodingBytes>();
+  if (!bytes || !bytes->resize(serializedSize(linkData))) {
     return;
   }
 
-  serialize(linkData, bytes.begin(), bytes.length());
+  serialize(linkData, bytes->begin(), bytes->length());
 
-  listener.storeOptimizedEncoding(bytes.begin(), bytes.length());
+  listener.storeOptimizedEncoding(std::move(bytes));
 }
 
 /* virtual */
 JSObject* Module::createObject(JSContext* cx) {
   if (!GlobalObject::ensureConstructor(cx, cx->global(), JSProto_WebAssembly)) {
     return nullptr;
   }
 
@@ -555,32 +555,32 @@ bool Module::extractCode(JSContext* cx, 
   if (!JS_DefineProperty(cx, result, "segments", value, JSPROP_ENUMERATE)) {
     return false;
   }
 
   vp.setObject(*result);
   return true;
 }
 
-static uint32_t EvaluateInitExpr(HandleValVector globalImportValues,
+static uint32_t EvaluateInitExpr(const ValVector& globalImportValues,
                                  InitExpr initExpr) {
   switch (initExpr.kind()) {
     case InitExpr::Kind::Constant:
       return initExpr.val().i32();
     case InitExpr::Kind::GetGlobal:
-      return globalImportValues[initExpr.globalIndex()].get().i32();
+      return globalImportValues[initExpr.globalIndex()].i32();
   }
 
   MOZ_CRASH("bad initializer expression");
 }
 
 bool Module::initSegments(JSContext* cx, HandleWasmInstanceObject instanceObj,
-                          Handle<FunctionVector> funcImports,
+                          const JSFunctionVector& funcImports,
                           HandleWasmMemoryObject memoryObj,
-                          HandleValVector globalImportValues) const {
+                          const ValVector& globalImportValues) const {
   MOZ_ASSERT_IF(!memoryObj, dataSegments_.empty());
 
   Instance& instance = instanceObj->instance();
   const SharedTableVector& tables = instance.tables();
 
 #ifndef ENABLE_WASM_BULKMEM_OPS
   // Bulk memory changes the error checking behavior: we may write partial data.
 
@@ -702,31 +702,31 @@ static const Import& FindImportForFuncIm
       return import;
     }
     funcImportIndex--;
   }
   MOZ_CRASH("ran out of imports");
 }
 
 bool Module::instantiateFunctions(JSContext* cx,
-                                  Handle<FunctionVector> funcImports) const {
+                                  const JSFunctionVector& funcImports) const {
 #ifdef DEBUG
   for (auto t : code().tiers()) {
     MOZ_ASSERT(funcImports.length() == metadata(t).funcImports.length());
   }
 #endif
 
   if (metadata().isAsmJS()) {
     return true;
   }
 
   Tier tier = code().stableTier();
 
   for (size_t i = 0; i < metadata(tier).funcImports.length(); i++) {
-    HandleFunction f = funcImports[i];
+    JSFunction* f = funcImports[i];
     if (!IsExportedFunction(f) || ExportedFunctionToInstance(f).isAsmJS()) {
       continue;
     }
 
     uint32_t funcIndex = ExportedFunctionToFuncIndex(f);
     Instance& instance = ExportedFunctionToInstance(f);
     Tier otherTier = instance.code().stableTier();
 
@@ -899,17 +899,17 @@ bool Module::instantiateLocalTable(JSCon
     ReportOutOfMemory(cx);
     return false;
   }
 
   return true;
 }
 
 bool Module::instantiateTables(JSContext* cx,
-                               WasmTableObjectVector& tableImports,
+                               const WasmTableObjectVector& tableImports,
                                MutableHandle<WasmTableObjectVector> tableObjs,
                                SharedTableVector* tables) const {
   uint32_t tableIndex = 0;
   for (const TableDesc& td : metadata().tables) {
     if (tableIndex < tableImports.length()) {
       Rooted<WasmTableObject*> tableObj(cx, tableImports[tableIndex]);
       if (!instantiateImportedTable(cx, td, tableObj, &tableObjs.get(),
                                     tables)) {
@@ -920,17 +920,17 @@ bool Module::instantiateTables(JSContext
         return false;
       }
     }
     tableIndex++;
   }
   return true;
 }
 
-static void ExtractGlobalValue(HandleValVector globalImportValues,
+static void ExtractGlobalValue(const ValVector& globalImportValues,
                                uint32_t globalIndex, const GlobalDesc& global,
                                MutableHandleVal result) {
   switch (global.kind()) {
     case GlobalKind::Import: {
       result.set(Val(globalImportValues[globalIndex]));
       return;
     }
     case GlobalKind::Variable: {
@@ -949,17 +949,17 @@ static void ExtractGlobalValue(HandleVal
       result.set(Val(global.constantValue()));
       return;
     }
   }
   MOZ_CRASH("Not a global value");
 }
 
 static bool EnsureGlobalObject(JSContext* cx,
-                               HandleValVector globalImportValues,
+                               const ValVector& globalImportValues,
                                size_t globalIndex, const GlobalDesc& global,
                                WasmGlobalObjectVector& globalObjs) {
   if (globalIndex < globalObjs.length() && globalObjs[globalIndex]) {
     return true;
   }
 
   RootedVal val(cx);
   ExtractGlobalValue(globalImportValues, globalIndex, global, &val);
@@ -975,17 +975,17 @@ static bool EnsureGlobalObject(JSContext
     return false;
   }
 
   globalObjs[globalIndex] = go;
   return true;
 }
 
 bool Module::instantiateGlobals(JSContext* cx,
-                                HandleValVector globalImportValues,
+                                const ValVector& globalImportValues,
                                 WasmGlobalObjectVector& globalObjs) const {
   // If there are exported globals that aren't in globalObjs because they
   // originate in this module or because they were immutable imports that came
   // in as primitive values then we must create cells in the globalObjs for
   // them here, as WasmInstanceObject::create() and CreateExportObject() will
   // need the cells to exist.
 
   const GlobalDescVector& globals = metadata().globals;
@@ -1078,17 +1078,17 @@ SharedCode Module::getDebugEnabledCode()
     return nullptr;
   }
 
   return debugCode;
 }
 
 static bool GetFunctionExport(JSContext* cx,
                               HandleWasmInstanceObject instanceObj,
-                              Handle<FunctionVector> funcImports,
+                              const JSFunctionVector& funcImports,
                               const Export& exp, MutableHandleValue val) {
   if (exp.funcIndex() < funcImports.length() &&
       IsExportedWasmFunction(funcImports[exp.funcIndex()])) {
     val.setObject(*funcImports[exp.funcIndex()]);
     return true;
   }
 
   RootedFunction fun(cx);
@@ -1098,17 +1098,17 @@ static bool GetFunctionExport(JSContext*
   }
 
   val.setObject(*fun);
   return true;
 }
 
 static bool CreateExportObject(JSContext* cx,
                                HandleWasmInstanceObject instanceObj,
-                               Handle<FunctionVector> funcImports,
+                               const JSFunctionVector& funcImports,
                                const WasmTableObjectVector& tableObjs,
                                HandleWasmMemoryObject memoryObj,
                                const WasmGlobalObjectVector& globalObjs,
                                const ExportVector& exports) {
   const Instance& instance = instanceObj->instance();
   const Metadata& metadata = instance.metadata();
 
   if (metadata.isAsmJS() && exports.length() == 1 &&
@@ -1330,44 +1330,40 @@ bool Module::makeStructTypeDescrs(
       return false;
     }
   }
 
   return true;
 #endif
 }
 
-bool Module::instantiate(JSContext* cx, Handle<FunctionVector> funcImports,
-                         WasmTableObjectVector& tableImports,
-                         HandleWasmMemoryObject memoryImport,
-                         HandleValVector globalImportValues,
-                         WasmGlobalObjectVector& globalObjs,
+bool Module::instantiate(JSContext* cx, ImportValues& imports,
                          HandleObject instanceProto,
                          MutableHandleWasmInstanceObject instance) const {
   MOZ_RELEASE_ASSERT(cx->wasmHaveSignalHandlers);
 
-  if (!instantiateFunctions(cx, funcImports)) {
+  if (!instantiateFunctions(cx, imports.funcs)) {
     return false;
   }
 
-  RootedWasmMemoryObject memory(cx, memoryImport);
+  RootedWasmMemoryObject memory(cx, imports.memory);
   if (!instantiateMemory(cx, &memory)) {
     return false;
   }
 
   // Note that tableObjs is sparse: it will be null in slots that contain
   // tables that are neither exported nor imported.
 
   Rooted<WasmTableObjectVector> tableObjs(cx);
   SharedTableVector tables;
-  if (!instantiateTables(cx, tableImports, &tableObjs, &tables)) {
+  if (!instantiateTables(cx, imports.tables, &tableObjs, &tables)) {
     return false;
   }
 
-  if (!instantiateGlobals(cx, globalImportValues, globalObjs)) {
+  if (!instantiateGlobals(cx, imports.globalValues, imports.globalObjs)) {
     return false;
   }
 
   UniqueTlsData tlsData = CreateTlsData(metadata().globalDataLength);
   if (!tlsData) {
     ReportOutOfMemory(cx);
     return false;
   }
@@ -1393,42 +1389,43 @@ bool Module::instantiate(JSContext* cx, 
 
   Rooted<StructTypeDescrVector> structTypeDescrs(cx);
   if (!makeStructTypeDescrs(cx, &structTypeDescrs)) {
     return false;
   }
 
   instance.set(WasmInstanceObject::create(
       cx, code, dataSegments_, elemSegments_, std::move(tlsData), memory,
-      std::move(tables), std::move(structTypeDescrs.get()), funcImports,
-      metadata().globals, globalImportValues, globalObjs, instanceProto,
-      std::move(maybeDebug)));
+      std::move(tables), std::move(structTypeDescrs.get()), imports.funcs,
+      metadata().globals, imports.globalValues, imports.globalObjs,
+      instanceProto, std::move(maybeDebug)));
   if (!instance) {
     return false;
   }
 
-  if (!CreateExportObject(cx, instance, funcImports, tableObjs.get(), memory,
-                          globalObjs, exports_)) {
+  if (!CreateExportObject(cx, instance, imports.funcs, tableObjs.get(), memory,
+                          imports.globalObjs, exports_)) {
     return false;
   }
 
   // Register the instance with the Realm so that it can find out about global
   // events like profiling being enabled in the realm. Registration does not
   // require a fully-initialized instance and must precede initSegments as the
   // final pre-requisite for a live instance.
 
   if (!cx->realm()->wasm.registerInstance(cx, instance)) {
     return false;
   }
 
   // Perform initialization as the final step after the instance is fully
   // constructed since this can make the instance live to content (even if the
   // start function fails).
 
-  if (!initSegments(cx, instance, funcImports, memory, globalImportValues)) {
+  if (!initSegments(cx, instance, imports.funcs, memory,
+                    imports.globalValues)) {
     return false;
   }
 
   // Now that the instance is fully live and initialized, the start function.
   // Note that failure may cause instantiation to throw, but the instance may
   // still be live via edges created by initSegments or the start function.
 
   if (metadata().startFuncIndex) {
--- a/js/src/wasm/WasmModule.h
+++ b/js/src/wasm/WasmModule.h
@@ -29,16 +29,41 @@ namespace wasm {
 
 struct CompileArgs;
 
 // In the context of wasm, the OptimizedEncodingListener specifically is
 // listening for the completion of tier-2.
 
 typedef RefPtr<JS::OptimizedEncodingListener> Tier2Listener;
 
+// A struct containing the typed, imported values that are harvested from the
+// import object and passed to Module::instantiate(). This struct must be
+// stored in a (Persistent)Rooted, not in the heap due to its use of TraceRoot()
+// and complete lack of barriers.
+
+struct ImportValues {
+  JSFunctionVector funcs;
+  WasmTableObjectVector tables;
+  WasmMemoryObject* memory;
+  WasmGlobalObjectVector globalObjs;
+  ValVector globalValues;
+
+  ImportValues() : memory(nullptr) {}
+
+  void trace(JSTracer* trc) {
+    funcs.trace(trc);
+    tables.trace(trc);
+    if (memory) {
+      TraceRoot(trc, &memory, "import values memory");
+    }
+    globalObjs.trace(trc);
+    globalValues.trace(trc);
+  }
+};
+
 // Module represents a compiled wasm module and primarily provides three
 // operations: instantiation, tiered compilation, serialization. A Module can be
 // instantiated any number of times to produce new Instance objects. A Module
 // can have a single tier-2 task initiated to augment a Module's code with a
 // higher tier. A Module can  have its optimized code serialized at any point
 // where the LinkData is also available, which is primarily (1) at the end of
 // module generation, (2) at the end of tier-2 compilation.
 //
@@ -77,35 +102,36 @@ class Module : public JS::WasmModule {
   mutable Tier2Listener tier2Listener_;
 
   // This flag is only used for testing purposes and is cleared on success or
   // failure. The field is racily polled from various threads.
 
   mutable Atomic<bool> testingTier2Active_;
 
   bool instantiateFunctions(JSContext* cx,
-                            Handle<FunctionVector> funcImports) const;
+                            const JSFunctionVector& funcImports) const;
   bool instantiateMemory(JSContext* cx,
                          MutableHandleWasmMemoryObject memory) const;
   bool instantiateImportedTable(JSContext* cx, const TableDesc& td,
                                 Handle<WasmTableObject*> table,
                                 WasmTableObjectVector* tableObjs,
                                 SharedTableVector* tables) const;
   bool instantiateLocalTable(JSContext* cx, const TableDesc& td,
                              WasmTableObjectVector* tableObjs,
                              SharedTableVector* tables) const;
-  bool instantiateTables(JSContext* cx, WasmTableObjectVector& tableImports,
+  bool instantiateTables(JSContext* cx,
+                         const WasmTableObjectVector& tableImports,
                          MutableHandle<WasmTableObjectVector> tableObjs,
                          SharedTableVector* tables) const;
-  bool instantiateGlobals(JSContext* cx, HandleValVector globalImportValues,
+  bool instantiateGlobals(JSContext* cx, const ValVector& globalImportValues,
                           WasmGlobalObjectVector& globalObjs) const;
   bool initSegments(JSContext* cx, HandleWasmInstanceObject instance,
-                    Handle<FunctionVector> funcImports,
+                    const JSFunctionVector& funcImports,
                     HandleWasmMemoryObject memory,
-                    HandleValVector globalImportValues) const;
+                    const ValVector& globalImportValues) const;
   SharedCode getDebugEnabledCode() const;
   bool makeStructTypeDescrs(
       JSContext* cx,
       MutableHandle<StructTypeDescrVector> structTypeDescrs) const;
 
   class Tier2GeneratorTaskImpl;
 
  public:
@@ -139,21 +165,17 @@ class Module : public JS::WasmModule {
   const ExportVector& exports() const { return exports_; }
   const CustomSectionVector& customSections() const { return customSections_; }
   const Bytes& debugBytecode() const { return debugBytecode_->bytes; }
   uint32_t codeLength(Tier t) const { return code_->segment(t).length(); }
   const StructTypeVector& structTypes() const { return code_->structTypes(); }
 
   // Instantiate this module with the given imports:
 
-  bool instantiate(JSContext* cx, Handle<FunctionVector> funcImports,
-                   WasmTableObjectVector& tableImport,
-                   HandleWasmMemoryObject memoryImport,
-                   HandleValVector globalImportValues,
-                   WasmGlobalObjectVector& globalObjs,
+  bool instantiate(JSContext* cx, ImportValues& imports,
                    HandleObject instanceProto,
                    MutableHandleWasmInstanceObject instanceObj) const;
 
   // Tier-2 compilation may be initiated after the Module is constructed at
   // most once. When tier-2 compilation completes, ModuleGenerator calls
   // finishTier2() from a helper thread, passing tier-variant data which will
   // be installed and made visible.
 
--- a/js/src/wasm/WasmTypes.h
+++ b/js/src/wasm/WasmTypes.h
@@ -44,16 +44,18 @@ namespace js {
 
 namespace jit {
 struct BaselineScript;
 enum class RoundingMode;
 }  // namespace jit
 
 // This is a widespread header, so lets keep out the core wasm impl types.
 
+typedef GCVector<JSFunction*, 0, SystemAllocPolicy> JSFunctionVector;
+
 class WasmMemoryObject;
 typedef GCPtr<WasmMemoryObject*> GCPtrWasmMemoryObject;
 typedef Rooted<WasmMemoryObject*> RootedWasmMemoryObject;
 typedef Handle<WasmMemoryObject*> HandleWasmMemoryObject;
 typedef MutableHandle<WasmMemoryObject*> MutableHandleWasmMemoryObject;
 
 class WasmModuleObject;
 typedef Rooted<WasmModuleObject*> RootedWasmModuleObject;
@@ -743,21 +745,20 @@ class LitVal {
     return u.ref_;
   }
   AnyRef anyref() const {
     MOZ_ASSERT(type_ == ValType::AnyRef);
     return u.anyref_;
   }
 };
 
-// A Val is a LitVal that can contain pointers to JSObjects, thanks to their
-// trace implementation. Since a Val is able to store a pointer to a JSObject,
-// it needs to be traced during compilation in case the pointee is moved.
-// The classic shorthands for Rooted things are defined after this class, for
-// easier usage.
+// A Val is a LitVal that can contain (non-null) pointers to GC things. All Vals
+// must be stored in Rooteds so that their trace() methods are called during
+// stack marking. Vals do not implement barriers and thus may not be stored on
+// the heap.
 
 class MOZ_NON_PARAM Val : public LitVal {
  public:
   Val() : LitVal() {}
   explicit Val(const LitVal& val);
   explicit Val(uint32_t i32) : LitVal(i32) {}
   explicit Val(uint64_t i64) : LitVal(i64) {}
   explicit Val(float f32) : LitVal(f32) {}
@@ -768,20 +769,20 @@ class MOZ_NON_PARAM Val : public LitVal 
   }
   void trace(JSTracer* trc);
 };
 
 typedef Rooted<Val> RootedVal;
 typedef Handle<Val> HandleVal;
 typedef MutableHandle<Val> MutableHandleVal;
 
-typedef GCVector<Val, 0, SystemAllocPolicy> GCVectorVal;
-typedef Rooted<GCVectorVal> RootedValVector;
-typedef Handle<GCVectorVal> HandleValVector;
-typedef MutableHandle<GCVectorVal> MutableHandleValVector;
+typedef GCVector<Val, 0, SystemAllocPolicy> ValVector;
+typedef Rooted<ValVector> RootedValVector;
+typedef Handle<ValVector> HandleValVector;
+typedef MutableHandle<ValVector> MutableHandleValVector;
 
 // The FuncType class represents a WebAssembly function signature which takes a
 // list of value types and returns an expression type. The engine uses two
 // in-memory representations of the argument Vector's memory (when elements do
 // not fit inline): normal malloc allocation (via SystemAllocPolicy) and
 // allocation in a LifoAlloc (via LifoAllocPolicy). The former FuncType objects
 // can have any lifetime since they own the memory. The latter FuncType objects
 // must not outlive the associated LifoAlloc mark/release interval (which is
--- a/mobile/android/app/geckoview-prefs.js
+++ b/mobile/android/app/geckoview-prefs.js
@@ -37,8 +37,11 @@ pref("intl.locale.requested", "");
 
 // Enable Safe Browsing blocklist updates
 pref("browser.safebrowsing.features.phishing.update", true);
 pref("browser.safebrowsing.features.malware.update", true);
 
 // Enable Tracking Protection blocklist updates
 pref("browser.safebrowsing.features.trackingAnnotation.update", true);
 pref("browser.safebrowsing.features.trackingProtection.update", true);
+
+// Enable cryptomining protection blocklist updates
+pref("browser.safebrowsing.features.cryptomining.update", true);
--- a/mobile/android/components/geckoview/GeckoViewStartup.js
+++ b/mobile/android/components/geckoview/GeckoViewStartup.js
@@ -7,16 +7,17 @@ const {GeckoViewUtils} = ChromeUtils.imp
 
 XPCOMUtils.defineLazyModuleGetters(this, {
   ActorManagerParent: "resource://gre/modules/ActorManagerParent.jsm",
   EventDispatcher: "resource://gre/modules/Messaging.jsm",
   FileSource: "resource://gre/modules/L10nRegistry.jsm",
   GeckoViewTelemetryController: "resource://gre/modules/GeckoViewTelemetryController.jsm",
   L10nRegistry: "resource://gre/modules/L10nRegistry.jsm",
   Preferences: "resource://gre/modules/Preferences.jsm",
+  SafeBrowsing: "resource://gre/modules/SafeBrowsing.jsm",
   Services: "resource://gre/modules/Services.jsm",
 });
 
 const {debug, warn} = GeckoViewUtils.initLogging("Startup"); // eslint-disable-line no-unused-vars
 
 function GeckoViewStartup() {
 }
 
@@ -120,16 +121,20 @@ GeckoViewStartup.prototype = {
 
         // Initialize the default l10n resource sources for L10nRegistry.
         let locales = Services.locale.packagedLocales;
         const greSource = new FileSource("toolkit", locales, "resource://gre/localization/{locale}/");
         L10nRegistry.registerSource(greSource);
 
         ChromeUtils.import("resource://gre/modules/NotificationDB.jsm");
 
+        // Initialize safe browsing module. This is required for content
+        // blocking features and manages blocklist downloads and updates.
+        SafeBrowsing.init();
+
         // Listen for global EventDispatcher messages
         EventDispatcher.instance.registerListener(this,
           ["GeckoView:ResetUserPrefs",
            "GeckoView:SetDefaultPrefs",
            "GeckoView:SetLocale"]);
         break;
       }
     }
--- a/mobile/android/geckoview/api.txt
+++ b/mobile/android/geckoview/api.txt
@@ -38,19 +38,20 @@ package org.mozilla.geckoview {
     method public void removeDrawCallback(@android.support.annotation.NonNull java.lang.Runnable);
     method public void setClearColor(int);
     method public void setFirstPaintCallback(@android.support.annotation.Nullable java.lang.Runnable);
   }
 
   @android.support.annotation.AnyThread public class ContentBlocking {
     ctor public ContentBlocking();
     field public static final int AT_AD = 2;
-    field public static final int AT_ALL = 62;
+    field public static final int AT_ALL = 126;
     field public static final int AT_ANALYTIC = 4;
     field public static final int AT_CONTENT = 16;
+    field public static final int AT_CRYPTOMINING = 64;
     field public static final int AT_SOCIAL = 8;
     field public static final int AT_TEST = 32;
     field public static final int COOKIE_ACCEPT_ALL = 0;
     field public static final int COOKIE_ACCEPT_FIRST_PARTY = 1;
     field public static final int COOKIE_ACCEPT_NONE = 2;
     field public static final int COOKIE_ACCEPT_NON_TRACKERS = 4;
     field public static final int COOKIE_ACCEPT_VISITED = 3;
     field public static final int COOKIE_LIFETIME_DAYS = 3;
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/ContentBlocking.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/ContentBlocking.java
@@ -70,16 +70,22 @@ public class ContentBlocking {
                 return this;
             }
         }
 
         /* package */ final Pref<String> mAt = new Pref<String>(
             "urlclassifier.trackingTable",
             ContentBlocking.catToAtPref(
                 AT_TEST | AT_ANALYTIC | AT_SOCIAL | AT_AD));
+        /* package */ final Pref<Boolean> mCm = new Pref<Boolean>(
+            "privacy.trackingprotection.cryptomining.enabled", false);
+        /* package */ final Pref<String> mCmList = new Pref<String>(
+            "urlclassifier.features.cryptomining.blacklistTables",
+            ContentBlocking.catToCmListPref(NONE));
+
         /* package */ final Pref<Boolean> mSbMalware = new Pref<Boolean>(
             "browser.safebrowsing.malware.enabled", true);
         /* package */ final Pref<Boolean> mSbPhishing = new Pref<Boolean>(
             "browser.safebrowsing.phishing.enabled", true);
         /* package */ final Pref<Integer> mCookieBehavior = new Pref<Integer>(
             "network.cookie.cookieBehavior", COOKIE_ACCEPT_ALL);
         /* package */ final Pref<Integer> mCookieLifetime = new Pref<Integer>(
             "network.cookie.lifetimePolicy", COOKIE_LIFETIME_NORMAL);
@@ -125,28 +131,33 @@ public class ContentBlocking {
          *
          * @param cat The categories of resources that should be blocked.
          *            Use one or more of the
          *            {@link ContentBlocking#AT_AD AT_* or SB_*} flags.
          * @return This Settings instance.
          */
         public @NonNull Settings setCategories(final @Category int cat) {
             mAt.commit(ContentBlocking.catToAtPref(cat));
+
+            mCm.commit(ContentBlocking.catToCmPref(cat));
+            mCmList.commit(ContentBlocking.catToCmListPref(cat));
+
             mSbMalware.commit(ContentBlocking.catToSbMalware(cat));
             mSbPhishing.commit(ContentBlocking.catToSbPhishing(cat));
             return this;
         }
 
         /**
          * Get the set content blocking categories.
          *
          * @return The categories of resources to be blocked.
          */
         public @Category int getCategories() {
-            return ContentBlocking.listToCat(mAt.get())
+            return ContentBlocking.atListToCat(mAt.get())
+                   | ContentBlocking.cmListToCat(mCmList.get())
                    | ContentBlocking.sbMalwareToCat(mSbMalware.get())
                    | ContentBlocking.sbPhishingToCat(mSbPhishing.get());
         }
 
         /**
          * Get the assigned cookie storage behavior.
          *
          * @return The assigned behavior, as one of {@link #COOKIE_ACCEPT_ALL COOKIE_ACCEPT_*} flags.
@@ -190,17 +201,17 @@ public class ContentBlocking {
             return this;
         }
 
     }
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(flag = true,
             value = { NONE, AT_AD, AT_ANALYTIC, AT_SOCIAL, AT_CONTENT,
-                      AT_ALL, AT_TEST,
+                      AT_ALL, AT_TEST, AT_CRYPTOMINING,
                       SB_MALWARE, SB_UNWANTED,
                       SB_HARMFUL, SB_PHISHING })
     /* package */ @interface Category {}
 
     public static final int NONE = 0;
 
     // Anti-tracking
     /**
@@ -224,20 +235,26 @@ public class ContentBlocking {
     public static final int AT_CONTENT = 1 << 4;
 
     /**
      * Block Gecko test trackers (used for tests).
      */
     public static final int AT_TEST = 1 << 5;
 
     /**
+     * Block cryptocurrency miners.
+     */
+    public static final int AT_CRYPTOMINING = 1 << 6;
+
+    /**
      * Block all known trackers.
+     * Blocks all {@link #AT_AD AT_*} types.
      */
     public static final int AT_ALL =
-        AT_AD | AT_ANALYTIC | AT_SOCIAL | AT_CONTENT | AT_TEST;
+        AT_AD | AT_ANALYTIC | AT_SOCIAL | AT_CONTENT | AT_TEST | AT_CRYPTOMINING;
 
     // Safe browsing
     /**
      * Block malware sites.
      */
     public static final int SB_MALWARE = 1 << 10;
 
     /**
@@ -345,17 +362,18 @@ public class ContentBlocking {
                 @NonNull final GeckoBundle bundle) {
             final String uri = bundle.getString("uri");
             final String matchedList = bundle.getString("matchedList");
             final long error = bundle.getLong("error", 0L);
 
             @Category int cats = NONE;
 
             if (matchedList != null) {
-                cats = ContentBlocking.listToCat(matchedList);
+                cats = ContentBlocking.atListToCat(matchedList) |
+                       ContentBlocking.cmListToCat(matchedList);
             } else if (error != 0L) {
                 cats = ContentBlocking.errorToCat(error);
             }
 
             return new BlockEvent(uri, cats);
         }
     }
 
@@ -378,16 +396,18 @@ public class ContentBlocking {
     }
 
 
     private static final String TEST = "test-track-simple";
     private static final String AD = "ads-track-digest256";
     private static final String ANALYTIC = "analytics-track-digest256";
     private static final String SOCIAL = "social-track-digest256";
     private static final String CONTENT = "content-track-digest256";
+    private static final String CRYPTOMINING =
+        "base-cryptomining-track-digest256";
 
     /* package */ static @Category int sbMalwareToCat(final boolean enabled) {
         return enabled ? (SB_MALWARE | SB_UNWANTED | SB_HARMFUL)
                        : NONE;
     }
 
     /* package */ static @Category int sbPhishingToCat(final boolean enabled) {
         return enabled ? SB_PHISHING
@@ -422,17 +442,30 @@ public class ContentBlocking {
         }
         if (builder.length() == 0) {
             return "";
         }
         // Trim final ','.
         return builder.substring(0, builder.length() - 1);
     }
 
-    /* package */ static @Category int listToCat(final String list) {
+    /* package */ static boolean catToCmPref(@Category final int cat) {
+        return (cat & AT_CRYPTOMINING) != 0;
+    }
+
+    /* package */ static String catToCmListPref(@Category final int cat) {
+        StringBuilder builder = new StringBuilder();
+
+        if ((cat & AT_CRYPTOMINING) != 0) {
+            builder.append(CRYPTOMINING);
+        }
+        return builder.toString();
+    }
+
+    /* package */ static @Category int atListToCat(final String list) {
         int cat = 0;
         if (list.indexOf(TEST) != -1) {
             cat |= AT_TEST;
         }
         if (list.indexOf(AD) != -1) {
             cat |= AT_AD;
         }
         if (list.indexOf(ANALYTIC) != -1) {
@@ -442,16 +475,24 @@ public class ContentBlocking {
             cat |= AT_SOCIAL;
         }
         if (list.indexOf(CONTENT) != -1) {
             cat |= AT_CONTENT;
         }
         return cat;
     }
 
+    /* package */ static @Category int cmListToCat(final String list) {
+        int cat = 0;
+        if (list.indexOf(CRYPTOMINING) != -1) {
+            cat |= AT_CRYPTOMINING;
+        }
+        return cat;
+    }
+
     /* package */ static @Category int errorToCat(final long error) {
         // Match flags with XPCOM ErrorList.h.
         if (error == 0x805D001FL) {
             return SB_PHISHING;
         }
         if (error == 0x805D001EL) {
             return SB_MALWARE;
         }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/doc-files/CHANGELOG.md
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/doc-files/CHANGELOG.md
@@ -13,16 +13,20 @@ exclude: true
   configuration has changed.
 
 [68.1]: ../GeckoRuntime.html#configurationChanged
 
 - Added `onSessionStateChange` to [`ProgressDelegate`][68.2] and removed `saveState`.
 
 [68.2]: ../GeckoSession.ProgressDelegate.html
 
+- Added [`ContentBlocking#AT_CRYPTOMINING`][68.3] for cryptocurrency miner blocking.
+
+[68.3]: ../ContentBlocking.html#AT_CRYPTOMINING
+
 ## v67
 - Added [`setAutomaticFontSizeAdjustment`][67.2] to
   [`GeckoRuntimeSettings`][67.3] for automatically adjusting font size settings
   depending on the OS-level font size setting.
 
 [67.2]: ../GeckoRuntimeSettings.html#setAutomaticFontSizeAdjustment-boolean-
 [67.3]: ../GeckoRuntimeSettings.html
 
@@ -219,9 +223,9 @@ exclude: true
 [65.23]: ../GeckoSession.FinderResult.html
 
 - Update [`CrashReporter#sendCrashReport`][65.24] to return the crash ID as a
   [`GeckoResult<String>`][65.25].
 
 [65.24]: ../CrashReporter.html#sendCrashReport-android.content.Context-android.os.Bundle-java.lang.String-
 [65.25]: ../GeckoResult.html
 
-[api-version]: e8fa4ed799e78f64fddd3f2c463202942811de11
+[api-version]: e48935ac13c3a907d46d50bb2b00f5e84d30ef4b
--- a/mobile/android/modules/geckoview/GeckoViewSettings.jsm
+++ b/mobile/android/modules/geckoview/GeckoViewSettings.jsm
@@ -4,20 +4,16 @@
 
 "use strict";
 
 var EXPORTED_SYMBOLS = ["GeckoViewSettings"];
 
 const {GeckoViewModule} = ChromeUtils.import("resource://gre/modules/GeckoViewModule.jsm");
 const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
-XPCOMUtils.defineLazyModuleGetters(this, {
-  SafeBrowsing: "resource://gre/modules/SafeBrowsing.jsm",
-});
-
 XPCOMUtils.defineLazyGetter(
   this, "MOBILE_USER_AGENT",
   function() {
     return Cc["@mozilla.org/network/protocol;1?name=http"]
            .getService(Ci.nsIHttpProtocolHandler).userAgent;
   });
 
 XPCOMUtils.defineLazyGetter(
@@ -40,21 +36,19 @@ const USER_AGENT_MODE_DESKTOP = 1;
 const USER_AGENT_MODE_VR = 2;
 
 // Handles GeckoView settings including:
 // * multiprocess
 // * user agent override
 class GeckoViewSettings extends GeckoViewModule {
   onInit() {
     debug `onInit`;
-    this._useTrackingProtection = false;
     this._userAgentMode = USER_AGENT_MODE_MOBILE;
     this._userAgentOverride = null;
     // Required for safe browsing and tracking protection.
-    SafeBrowsing.init();
 
     this.registerListener([
       "GeckoView:GetUserAgent",
     ]);
   }
 
   onEvent(aEvent, aData, aCallback) {
     debug `onEvent ${aEvent} ${aData}`;