Merge inbound to mozilla-central. a=merge
authorMargareta Eliza Balazs <ebalazs@mozilla.com>
Fri, 11 May 2018 12:36:02 +0300
changeset 794135 21f09d7e7214eaebf1e0980494159bd846e1bdd9
parent 794134 8e9a4a323f0cfce20c7fae76d15c252693a65536 (current diff)
parent 794031 4ef3bf9d981a0edf151dc1373ad2385db966a93f (diff)
child 794136 59a49b12b26846302393edfbd20b5e72ef6b1d85
child 794140 cdc6eac8c0ec18ebae6e75930b9f4d9877695427
child 794153 4b9f89889392e29203a61e0624d537aaa26f1897
child 794155 3620e7711bfcd62e462fabc7a841ca61b00020a9
child 794163 9604aeeb7725efda10cc6342d1ec792b6402d344
child 794164 649f4d58db4b8119be0f5a212ae94dbb6711b342
child 794165 9b5c0274098f115035d3bcd54e810b6490a225e5
child 794170 200c5e740cfb699f3043f7352730d158b671a04a
child 794172 5a5b249f677f326dfda37dc4905b05c03b51dbba
child 794179 0812efd2990f1115f135b6b128d111b1c61137e2
child 794181 5d0d6a248e8efd6bcbf7108fe441e0af839b635c
child 794182 b24fb29d4bdc064d2c18b181755ae75d4646c755
child 794184 a8c05534dc9e9c2160d51a440f3430f9f3a2fec9
child 794185 1fad96f0091b947e66eca57f5f85b2c867b90c2d
child 794187 1f7ccddc8a2f9ef1e69c5df109933f53dd18bf37
child 794191 4303d49c53931385892231969e40048f096b4d4c
child 794216 e38c102f0514716c1153a764d69e2ddb706772e6
child 794236 89c7f8a6e1bb77ceb242f05749c22d929ee86929
child 794314 d5cb9684452ee3f1a6fd84934ba4f41a4bac9028
child 794378 56548294792b4fc92a1561973f51dcb566b48701
child 794385 a5bb33ba481cbe8e7a56a110f427cbb0501145f8
child 794389 68815073f5c7943b9d137df7a70a7e9b543de9ea
child 794449 d405a421953fdef8511a992eeabfdf6cb0a3391f
child 794911 33078b1333a3e46157db5f6c4446ea8f92c9a4b1
child 794920 1476f11b5c546a84223a1aec15e0e91cf2c13534
child 795576 26cce42745a3c6d4cf5e5ceaeb86c4b5feca125a
child 795577 2c06d9a31bea0628002dffd5bed6ce484059b175
child 796242 961e2334543862ae2a88cddc04b06118fe364952
child 799158 4edfd405ca253a51818d7dcd9b3e1de1b2ff6a38
child 799209 585f5ecd3a883ab2f346dc5ece3b3c1d29d42bc3
child 799344 8101817574a22d100b213d987b648300df4e5ac3
child 799634 c703a501950e6908f0e15160e5e93f6f17d53706
push id109574
push userbmo:dharvey@mozilla.com
push dateFri, 11 May 2018 10:59:04 +0000
reviewersmerge
milestone62.0a1
Merge inbound to mozilla-central. a=merge
gfx/thebes/gfxPlatform.cpp
layout/painting/nsDisplayList.cpp
layout/style/ServoBindings.toml
layout/style/nsCSSProps.cpp
layout/style/nsStyleConsts.h
layout/style/nsStyleStruct.cpp
media/libyuv/libyuv/source/convert_argb.cc.orig
mobile/android/base/java/org/mozilla/gecko/CrashReporter.java
security/manager/ssl/nsNSSComponent.cpp
--- a/browser/extensions/webcompat/bootstrap.js
+++ b/browser/extensions/webcompat/bootstrap.js
@@ -71,17 +71,17 @@ this.startup = function({webExtension}) 
   Services.prefs.clearUserPref(INJECTIONS_ENABLE_PREF_NAME);
   Services.prefs.addObserver(INJECTIONS_ENABLE_PREF_NAME, InjectionsEnablePrefObserver);
 
   Services.prefs.clearUserPref(UA_ENABLE_PREF_NAME);
   Services.prefs.addObserver(UA_ENABLE_PREF_NAME, UAEnablePrefObserver);
 
   // Listen to the useragentoverrides-initialized notification we get and
   // initialize our overrider there. This is done to avoid slowing down the
-  // apparent startup proces, since we avoid loading anything before the first
+  // apparent startup process, since we avoid loading anything before the first
   // window is visible to the user. See bug 1371442 for details.
   let uaStartupObserver = {
     observe(aSubject, aTopic, aData) {
       if (aTopic !== UA_OVERRIDES_INIT_TOPIC) {
         return;
       }
 
       Services.obs.removeObserver(this, UA_OVERRIDES_INIT_TOPIC);
--- a/dom/payments/PaymentRequestManager.h
+++ b/dom/payments/PaymentRequestManager.h
@@ -18,17 +18,17 @@
 namespace mozilla {
 namespace dom {
 
 class PaymentRequestChild;
 class IPCPaymentActionRequest;
 
 /*
  *  PaymentRequestManager is a singleton used to manage the created PaymentRequests.
- *  It is also the communication agent to chrome proces.
+ *  It is also the communication agent to chrome process.
  */
 class PaymentRequestManager final
 {
 public:
   NS_INLINE_DECL_REFCOUNTING(PaymentRequestManager)
 
   static already_AddRefed<PaymentRequestManager> GetSingleton();
 
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -624,17 +624,17 @@ WebRenderDebugPrefChangeCallback(const c
 static uint32_t GetSkiaGlyphCacheSize()
 {
     // Only increase font cache size on non-android to save memory.
 #if !defined(MOZ_WIDGET_ANDROID)
     // 10mb as the default pref cache size on desktop due to talos perf tweaking.
     // Chromium uses 20mb and skia default uses 2mb.
     // We don't need to change the font cache count since we usually
     // cache thrash due to asian character sets in talos.
-    // Only increase memory on the content proces
+    // Only increase memory on the content process
     uint32_t cacheSize = gfxPrefs::SkiaContentFontCacheSize() * 1024 * 1024;
     if (mozilla::BrowserTabsRemoteAutostart()) {
       return XRE_IsContentProcess() ? cacheSize : kDefaultGlyphCacheSize;
     }
 
     return cacheSize;
 #else
     return kDefaultGlyphCacheSize;
--- a/ipc/glue/MessageChannel.cpp
+++ b/ipc/glue/MessageChannel.cpp
@@ -8,16 +8,17 @@
 #include "mozilla/ipc/MessageChannel.h"
 
 #include "mozilla/Assertions.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/ipc/ProtocolUtils.h"
 #include "mozilla/Logging.h"
 #include "mozilla/Move.h"
+#include "mozilla/ScopeExit.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/TimeStamp.h"
 #include "nsAppRunner.h"
 #include "mozilla/UniquePtr.h"
 #include "nsAutoPtr.h"
 #include "nsDebug.h"
 #include "nsISupportsImpl.h"
@@ -2691,26 +2692,36 @@ MessageChannel::CloseWithTimeout()
 }
 
 void
 MessageChannel::Close()
 {
     AssertWorkerThread();
 
     {
-        MonitorAutoLock lock(*mMonitor);
+        // We don't use MonitorAutoLock here as that causes some sort of
+        // deadlock in the error/timeout-with-a-listener state below when
+        // compiling an optimized msvc build.
+        mMonitor->Lock();
+
+        // Instead just use a ScopeExit to manage the unlock.
+        RefPtr<RefCountedMonitor> monitor(mMonitor);
+        auto exit = MakeScopeExit([m = Move(monitor)] () {
+          m->Unlock();
+        });
 
         if (ChannelError == mChannelState || ChannelTimeout == mChannelState) {
             // See bug 538586: if the listener gets deleted while the
             // IO thread's NotifyChannelError event is still enqueued
             // and subsequently deletes us, then the error event will
             // also be deleted and the listener will never be notified
             // of the channel error.
             if (mListener) {
-                MonitorAutoUnlock unlock(*mMonitor);
+                exit.release(); // Explicitly unlocking, clear scope exit.
+                mMonitor->Unlock();
                 NotifyMaybeChannelError();
             }
             return;
         }
 
         if (ChannelOpening == mChannelState) {
             // SynchronouslyClose() waits for an ack from the other side, so
             // the opening sequence should complete before this returns.
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -3206,32 +3206,34 @@ ICCall_ConstStringSplit::Compiler::gener
     {
         // Ensure that sep is a string.
         Address sepAddr(masm.getStackPointer(), ICStackValueOffset + SEP_DEPTH);
         ValueOperand sepVal = regs.takeAnyValue();
 
         masm.loadValue(sepAddr, sepVal);
         masm.branchTestString(Assembler::NotEqual, sepVal, &failureRestoreArgc);
 
-        Register sep = masm.extractString(sepVal, ExtractTemp0);
+        Register sep = sepVal.scratchReg();
+        masm.unboxString(sepVal, sep);
         masm.branchPtr(Assembler::NotEqual, Address(ICStubReg, offsetOfExpectedSep()),
                        sep, &failureRestoreArgc);
         regs.add(sepVal);
     }
 
     // Guard str.
     {
         // Ensure that str is a string.
         Address strAddr(masm.getStackPointer(), ICStackValueOffset + STR_DEPTH);
         ValueOperand strVal = regs.takeAnyValue();
 
         masm.loadValue(strAddr, strVal);
         masm.branchTestString(Assembler::NotEqual, strVal, &failureRestoreArgc);
 
-        Register str = masm.extractString(strVal, ExtractTemp0);
+        Register str = strVal.scratchReg();
+        masm.unboxString(strVal, str);
         masm.branchPtr(Assembler::NotEqual, Address(ICStubReg, offsetOfExpectedStr()),
                        str, &failureRestoreArgc);
         regs.add(strVal);
     }
 
     // Main stub body.
     {
         Register paramReg = regs.takeAny();
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -7740,26 +7740,30 @@ void
 CodeGenerator::visitCompareStrictS(LCompareStrictS* lir)
 {
     JSOp op = lir->mir()->jsop();
     MOZ_ASSERT(op == JSOP_STRICTEQ || op == JSOP_STRICTNE);
 
     const ValueOperand leftV = ToValue(lir, LCompareStrictS::Lhs);
     Register right = ToRegister(lir->right());
     Register output = ToRegister(lir->output());
-    Register tempToUnbox = ToTempUnboxRegister(lir->tempToUnbox());
 
     Label string, done;
 
     masm.branchTestString(Assembler::Equal, leftV, &string);
     masm.move32(Imm32(op == JSOP_STRICTNE), output);
     masm.jump(&done);
 
     masm.bind(&string);
-    Register left = masm.extractString(leftV, tempToUnbox);
+#ifdef JS_NUNBOX32
+    Register left = leftV.payloadReg();
+#else
+    Register left = ToTempUnboxRegister(lir->tempToUnbox());
+#endif
+    masm.unboxString(leftV, left);
     emitCompareS(lir, op, left, right, output);
 
     masm.bind(&done);
 }
 
 void
 CodeGenerator::visitCompareS(LCompareS* lir)
 {
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -1491,16 +1491,18 @@ MacroAssembler::initUnboxedObjectContent
         MOZ_ASSERT(*(list + 1) == -1);
     }
 }
 
 void
 MacroAssembler::compareStrings(JSOp op, Register left, Register right, Register result,
                                Label* fail)
 {
+    MOZ_ASSERT(left != result);
+    MOZ_ASSERT(right != result);
     MOZ_ASSERT(IsEqualityOp(op));
 
     Label done;
     Label notPointerEqual;
     // Fast path for identical strings.
     branchPtr(Assembler::NotEqual, left, right, &notPointerEqual);
     move32(Imm32(op == JSOP_EQ || op == JSOP_STRICTEQ), result);
     jump(&done);
--- a/js/src/jit/SharedIC.cpp
+++ b/js/src/jit/SharedIC.cpp
@@ -1480,32 +1480,39 @@ ICCompare_Fallback::Compiler::generateSt
 
 //
 // Compare_String
 //
 
 bool
 ICCompare_String::Compiler::generateStubCode(MacroAssembler& masm)
 {
-    Label failure;
+    Label failure, restore;
     masm.branchTestString(Assembler::NotEqual, R0, &failure);
     masm.branchTestString(Assembler::NotEqual, R1, &failure);
 
     MOZ_ASSERT(IsEqualityOp(op));
 
-    Register left = masm.extractString(R0, ExtractTemp0);
-    Register right = masm.extractString(R1, ExtractTemp1);
+    // left/right are part of R0/R1. Restore R0 and R1 in the failure case.
+    Register left = R0.scratchReg();
+    Register right = R1.scratchReg();
+    masm.unboxString(R0, left);
+    masm.unboxString(R1, right);
 
     AllocatableGeneralRegisterSet regs(availableGeneralRegs(2));
     Register scratchReg = regs.takeAny();
 
-    masm.compareStrings(op, left, right, scratchReg, &failure);
+    masm.compareStrings(op, left, right, scratchReg, &restore);
     masm.tagValue(JSVAL_TYPE_BOOLEAN, scratchReg, R0);
     EmitReturnFromIC(masm);
 
+    masm.bind(&restore);
+    masm.tagValue(JSVAL_TYPE_STRING, left, R0);
+    masm.tagValue(JSVAL_TYPE_STRING, right, R1);
+
     masm.bind(&failure);
     EmitStubGuardFailure(masm);
     return true;
 }
 
 //
 // Compare_Symbol
 //
--- a/js/src/jit/arm/MacroAssembler-arm.h
+++ b/js/src/jit/arm/MacroAssembler-arm.h
@@ -827,20 +827,16 @@ class MacroAssemblerARMCompat : public M
     // Extended unboxing API. If the payload is already in a register, returns
     // that register. Otherwise, provides a move to the given scratch register,
     // and returns that.
     Register extractObject(const Address& address, Register scratch);
     Register extractObject(const ValueOperand& value, Register scratch) {
         unboxNonDouble(value, value.payloadReg(), JSVAL_TYPE_OBJECT);
         return value.payloadReg();
     }
-    Register extractString(const ValueOperand& value, Register scratch) {
-        unboxNonDouble(value, value.payloadReg(), JSVAL_TYPE_STRING);
-        return value.payloadReg();
-    }
     Register extractSymbol(const ValueOperand& value, Register scratch) {
         unboxNonDouble(value, value.payloadReg(), JSVAL_TYPE_SYMBOL);
         return value.payloadReg();
     }
     Register extractInt32(const ValueOperand& value, Register scratch) {
         return value.payloadReg();
     }
     Register extractBoolean(const ValueOperand& value, Register scratch) {
--- a/js/src/jit/arm64/MacroAssembler-arm64.h
+++ b/js/src/jit/arm64/MacroAssembler-arm64.h
@@ -413,20 +413,16 @@ class MacroAssemblerCompat : public vixl
         loadPtr(address, scratch);
         unboxObject(scratch, scratch);
         return scratch;
     }
     Register extractObject(const ValueOperand& value, Register scratch) {
         unboxObject(value, scratch);
         return scratch;
     }
-    Register extractString(const ValueOperand& value, Register scratch) {
-        unboxString(value, scratch);
-        return scratch;
-    }
     Register extractSymbol(const ValueOperand& value, Register scratch) {
         unboxSymbol(value, scratch);
         return scratch;
     }
     Register extractInt32(const ValueOperand& value, Register scratch) {
         unboxInt32(value, scratch);
         return scratch;
     }
--- a/js/src/jit/none/MacroAssembler-none.h
+++ b/js/src/jit/none/MacroAssembler-none.h
@@ -329,17 +329,16 @@ class MacroAssemblerNone : public Assemb
     template <typename T> void unboxPrivate(T, Register) { MOZ_CRASH(); }
     void unboxValue(const ValueOperand&, AnyRegister, JSValueType) { MOZ_CRASH(); }
     void unboxNonDouble(const ValueOperand&, Register, JSValueType) { MOZ_CRASH();}
     void unboxNonDouble(const Address&, Register, JSValueType) { MOZ_CRASH();}
     void unboxGCThingForPreBarrierTrampoline(const Address&, Register) { MOZ_CRASH(); }
     void notBoolean(ValueOperand) { MOZ_CRASH(); }
     Register extractObject(Address, Register) { MOZ_CRASH(); }
     Register extractObject(ValueOperand, Register) { MOZ_CRASH(); }
-    Register extractString(ValueOperand, Register) { MOZ_CRASH(); }
     Register extractSymbol(ValueOperand, Register) { MOZ_CRASH(); }
     Register extractInt32(ValueOperand, Register) { MOZ_CRASH(); }
     Register extractBoolean(ValueOperand, Register) { MOZ_CRASH(); }
     template <typename T> Register extractTag(T, Register) { MOZ_CRASH(); }
 
     void convertFloat32ToInt32(FloatRegister, Register, Label*, bool v = true) { MOZ_CRASH(); }
     void convertDoubleToInt32(FloatRegister, Register, Label*, bool v = true) { MOZ_CRASH(); }
     void convertBoolToInt32(Register, Register) { MOZ_CRASH(); }
--- a/js/src/jit/x64/MacroAssembler-x64.h
+++ b/js/src/jit/x64/MacroAssembler-x64.h
@@ -880,21 +880,16 @@ class MacroAssemblerX64 : public MacroAs
         unboxObject(address, scratch);
         return scratch;
     }
     Register extractObject(const ValueOperand& value, Register scratch) {
         MOZ_ASSERT(scratch != ScratchReg);
         unboxObject(value, scratch);
         return scratch;
     }
-    Register extractString(const ValueOperand& value, Register scratch) {
-        MOZ_ASSERT(scratch != ScratchReg);
-        unboxString(value, scratch);
-        return scratch;
-    }
     Register extractSymbol(const ValueOperand& value, Register scratch) {
         MOZ_ASSERT(scratch != ScratchReg);
         unboxSymbol(value, scratch);
         return scratch;
     }
     Register extractInt32(const ValueOperand& value, Register scratch) {
         MOZ_ASSERT(scratch != ScratchReg);
         unboxInt32(value, scratch);
--- a/js/src/jit/x86/MacroAssembler-x86.h
+++ b/js/src/jit/x86/MacroAssembler-x86.h
@@ -832,20 +832,16 @@ class MacroAssemblerX86 : public MacroAs
     Register extractObject(const Address& address, Register dest) {
         unboxObject(address, dest);
         return dest;
     }
     Register extractObject(const ValueOperand& value, Register scratch) {
         unboxNonDouble(value, value.payloadReg(), JSVAL_TYPE_OBJECT, scratch);
         return value.payloadReg();
     }
-    Register extractString(const ValueOperand& value, Register scratch) {
-        unboxNonDouble(value, value.payloadReg(), JSVAL_TYPE_STRING, scratch);
-        return value.payloadReg();
-    }
     Register extractSymbol(const ValueOperand& value, Register scratch) {
         unboxNonDouble(value, value.payloadReg(), JSVAL_TYPE_SYMBOL, scratch);
         return value.payloadReg();
     }
     Register extractInt32(const ValueOperand& value, Register scratch) {
         return value.payloadReg();
     }
     Register extractBoolean(const ValueOperand& value, Register scratch) {
--- a/js/src/wasm/WasmCode.cpp
+++ b/js/src/wasm/WasmCode.cpp
@@ -35,28 +35,19 @@
 
 using namespace js;
 using namespace js::jit;
 using namespace js::wasm;
 using mozilla::BinarySearch;
 using mozilla::MakeEnumeratedRange;
 using mozilla::PodAssign;
 
-bool
-CodeSegment::registerInProcessMap()
-{
-    if (!RegisterCodeSegment(this))
-        return false;
-    registered_ = true;
-    return true;
-}
-
 CodeSegment::~CodeSegment()
 {
-    if (registered_)
+    if (unregisterOnDestroy_)
         UnregisterCodeSegment(this);
 }
 
 static uint32_t
 RoundupCodeLength(uint32_t codeLength)
 {
     // AllocateExecutableMemory() requires a multiple of ExecutableCodePageSize.
     return JS_ROUNDUP(codeLength, ExecutableCodePageSize);
@@ -92,27 +83,47 @@ CodeSegment::AllocateCodeBytes(uint32_t 
     memset(((uint8_t*)p) + codeLength, 0, roundedCodeLength - codeLength);
 
     // We account for the bytes allocated in WasmModuleObject::create, where we
     // have the necessary JSContext.
 
     return UniqueCodeBytes((uint8_t*)p, FreeCode(roundedCodeLength));
 }
 
+bool
+CodeSegment::initialize(const CodeTier& codeTier)
+{
+    MOZ_ASSERT(!initialized());
+    codeTier_ = &codeTier;
+    MOZ_ASSERT(initialized());
+
+    // In the case of tiering, RegisterCodeSegment() immediately makes this code
+    // segment live to access from other threads executing the containing
+    // module. So only call once the CodeSegment is fully initialized.
+    if (!RegisterCodeSegment(this))
+        return false;
+
+    // This bool is only used by the destructor which cannot be called racily
+    // and so it is not a problem to mutate it after RegisterCodeSegment().
+    MOZ_ASSERT(!unregisterOnDestroy_);
+    unregisterOnDestroy_ = true;
+    return true;
+}
+
 const Code&
 CodeSegment::code() const
 {
     MOZ_ASSERT(codeTier_);
     return codeTier_->code();
 }
 
 void
 CodeSegment::addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* code) const
 {
-    *code += RoundupCodeLength(length_);
+    *code += RoundupCodeLength(length());
 }
 
 void
 FreeCode::operator()(uint8_t* bytes)
 {
     MOZ_ASSERT(codeLength);
     MOZ_ASSERT(codeLength == RoundupCodeLength(codeLength));
 
@@ -256,155 +267,123 @@ SendCodeRangesToProfiler(const ModuleSeg
             continue;
         if (!name.append('\0'))
             return;
         vtune::MarkWasm(vtune::GenerateUniqueMethodID(), name.begin(), (void*)start, size);
 #endif
     }
 }
 
+ModuleSegment::ModuleSegment(Tier tier,
+                             UniqueCodeBytes codeBytes,
+                             uint32_t codeLength,
+                             const LinkDataTier& linkData)
+  : CodeSegment(Move(codeBytes), codeLength, CodeSegment::Kind::Module),
+    tier_(tier),
+    outOfBoundsCode_(base() + linkData.outOfBoundsOffset),
+    unalignedAccessCode_(base() + linkData.unalignedAccessOffset),
+    trapCode_(base() + linkData.trapOffset)
+{
+}
+
 /* static */ UniqueModuleSegment
-ModuleSegment::create(Tier tier,
-                      MacroAssembler& masm,
-                      const ShareableBytes& bytecode,
-                      const LinkDataTier& linkData,
-                      const Metadata& metadata,
-                      const CodeRangeVector& codeRanges)
+ModuleSegment::create(Tier tier, MacroAssembler& masm, const LinkDataTier& linkData)
 {
     uint32_t codeLength = masm.bytesNeeded();
 
     UniqueCodeBytes codeBytes = AllocateCodeBytes(codeLength);
     if (!codeBytes)
         return nullptr;
 
     // We'll flush the icache after static linking, in initialize().
     masm.executableCopy(codeBytes.get(), /* flushICache = */ false);
 
-    return create(tier, Move(codeBytes), codeLength, bytecode, linkData, metadata, codeRanges);
+    return js::MakeUnique<ModuleSegment>(tier, Move(codeBytes), codeLength, linkData);
 }
 
 /* static */ UniqueModuleSegment
-ModuleSegment::create(Tier tier,
-                      const Bytes& unlinkedBytes,
-                      const ShareableBytes& bytecode,
-                      const LinkDataTier& linkData,
-                      const Metadata& metadata,
-                      const CodeRangeVector& codeRanges)
+ModuleSegment::create(Tier tier, const Bytes& unlinkedBytes, const LinkDataTier& linkData)
 {
     uint32_t codeLength = unlinkedBytes.length();
 
     UniqueCodeBytes codeBytes = AllocateCodeBytes(codeLength);
     if (!codeBytes)
         return nullptr;
 
     memcpy(codeBytes.get(), unlinkedBytes.begin(), codeLength);
 
-    return create(tier, Move(codeBytes), codeLength, bytecode, linkData, metadata, codeRanges);
-}
-
-/* static */ UniqueModuleSegment
-ModuleSegment::create(Tier tier,
-                      UniqueCodeBytes codeBytes,
-                      uint32_t codeLength,
-                      const ShareableBytes& bytecode,
-                      const LinkDataTier& linkData,
-                      const Metadata& metadata,
-                      const CodeRangeVector& codeRanges)
-{
-    // These should always exist and should never be first in the code segment.
-
-    auto ms = js::MakeUnique<ModuleSegment>();
-    if (!ms)
-        return nullptr;
-
-    if (!ms->initialize(tier, Move(codeBytes), codeLength, bytecode, linkData, metadata, codeRanges))
-        return nullptr;
-
-    return UniqueModuleSegment(ms.release());
+    return js::MakeUnique<ModuleSegment>(tier, Move(codeBytes), codeLength, linkData);
 }
 
 bool
-ModuleSegment::initialize(Tier tier,
-                          UniqueCodeBytes codeBytes,
-                          uint32_t codeLength,
+ModuleSegment::initialize(const CodeTier& codeTier,
                           const ShareableBytes& bytecode,
                           const LinkDataTier& linkData,
                           const Metadata& metadata,
-                          const CodeRangeVector& codeRanges)
+                          const MetadataTier& metadataTier)
 {
-    MOZ_ASSERT(!bytes_);
-
-    tier_ = tier;
-    bytes_ = Move(codeBytes);
-    length_ = codeLength;
-    outOfBoundsCode_ = bytes_.get() + linkData.outOfBoundsOffset;
-    unalignedAccessCode_ = bytes_.get() + linkData.unalignedAccessOffset;
-    trapCode_ = bytes_.get() + linkData.trapOffset;
-
     if (!StaticallyLink(*this, linkData))
         return false;
 
-    ExecutableAllocator::cacheFlush(bytes_.get(), RoundupCodeLength(codeLength));
+    ExecutableAllocator::cacheFlush(base(), RoundupCodeLength(length()));
 
     // Reprotect the whole region to avoid having separate RW and RX mappings.
-    if (!ExecutableAllocator::makeExecutable(bytes_.get(), RoundupCodeLength(codeLength)))
+    if (!ExecutableAllocator::makeExecutable(base(), RoundupCodeLength(length())))
         return false;
 
-    if (!registerInProcessMap())
-        return false;
+    SendCodeRangesToProfiler(*this, bytecode.bytes, metadata, metadataTier.codeRanges);
 
-    SendCodeRangesToProfiler(*this, bytecode.bytes, metadata, codeRanges);
-
-    return true;
+    // See comments in CodeSegment::initialize() for why this must be last.
+    return CodeSegment::initialize(codeTier);
 }
 
 size_t
 ModuleSegment::serializedSize() const
 {
-    return sizeof(uint32_t) + length_;
+    return sizeof(uint32_t) + length();
 }
 
 void
 ModuleSegment::addSizeOfMisc(mozilla::MallocSizeOf mallocSizeOf, size_t* code, size_t* data) const
 {
     CodeSegment::addSizeOfMisc(mallocSizeOf, code);
     *data += mallocSizeOf(this);
 }
 
 uint8_t*
 ModuleSegment::serialize(uint8_t* cursor, const LinkDataTier& linkData) const
 {
     MOZ_ASSERT(tier() == Tier::Serialized);
 
-    cursor = WriteScalar<uint32_t>(cursor, length_);
-    uint8_t* base = cursor;
-    cursor = WriteBytes(cursor, bytes_.get(), length_);
-    StaticallyUnlink(base, linkData);
+    cursor = WriteScalar<uint32_t>(cursor, length());
+    uint8_t* serializedBase = cursor;
+    cursor = WriteBytes(cursor, base(), length());
+    StaticallyUnlink(serializedBase, linkData);
     return cursor;
 }
 
-const uint8_t*
-ModuleSegment::deserialize(const uint8_t* cursor, const ShareableBytes& bytecode,
-                           const LinkDataTier& linkData, const Metadata& metadata,
-                           const CodeRangeVector& codeRanges)
+/* static */ const uint8_t*
+ModuleSegment::deserialize(const uint8_t* cursor, const LinkDataTier& linkData,
+                           UniqueModuleSegment* segment)
 {
     uint32_t length;
     cursor = ReadScalar<uint32_t>(cursor, &length);
     if (!cursor)
         return nullptr;
 
     UniqueCodeBytes bytes = AllocateCodeBytes(length);
     if (!bytes)
         return nullptr;
 
     cursor = ReadBytes(cursor, bytes.get(), length);
     if (!cursor)
         return nullptr;
 
-    if (!initialize(Tier::Serialized, Move(bytes), length, bytecode, linkData, metadata, codeRanges))
+    *segment = js::MakeUnique<ModuleSegment>(Tier::Serialized, Move(bytes), length, linkData);
+    if (!*segment)
         return nullptr;
 
     return cursor;
 }
 
 const CodeRange*
 ModuleSegment::lookupRange(const void* pc) const
 {
@@ -555,46 +534,36 @@ MetadataTier::deserialize(const uint8_t*
     (cursor = trapSites.deserialize(cursor)) &&
     (cursor = DeserializeVector(cursor, &funcImports)) &&
     (cursor = DeserializeVector(cursor, &funcExports));
     debugTrapFarJumpOffsets.clear();
     debugFuncToCodeRange.clear();
     return cursor;
 }
 
-bool
-LazyStubSegment::initialize(UniqueCodeBytes codeBytes, size_t length)
-{
-    MOZ_ASSERT(bytes_ == nullptr);
-
-    bytes_ = Move(codeBytes);
-    length_ = length;
-
-    return registerInProcessMap();
-}
-
 UniqueLazyStubSegment
 LazyStubSegment::create(const CodeTier& codeTier, size_t length)
 {
     UniqueCodeBytes codeBytes = AllocateCodeBytes(length);
     if (!codeBytes)
         return nullptr;
 
-    auto segment = js::MakeUnique<LazyStubSegment>(codeTier);
-    if (!segment || !segment->initialize(Move(codeBytes), length))
+    auto segment = js::MakeUnique<LazyStubSegment>(Move(codeBytes), length);
+    if (!segment || !segment->initialize(codeTier))
         return nullptr;
+
     return segment;
 }
 
 bool
 LazyStubSegment::hasSpace(size_t bytes) const
 {
     MOZ_ASSERT(bytes % MPROTECT_PAGE_SIZE == 0);
-    return bytes <= length_ &&
-           usedBytes_ <= length_ - bytes;
+    return bytes <= length() &&
+           usedBytes_ <= length() - bytes;
 }
 
 bool
 LazyStubSegment::addStubs(size_t codeLength, const Uint32Vector& funcExportIndices,
                           const FuncExportVector& funcExports, const CodeRangeVector& codeRanges,
                           uint8_t** codePtr, size_t* indexFirstInsertedCodeRange)
 {
     MOZ_ASSERT(hasSpace(codeLength));
@@ -1016,34 +985,35 @@ CodeTier::serializedSize() const
 uint8_t*
 CodeTier::serialize(uint8_t* cursor, const LinkDataTier& linkData) const
 {
     cursor = metadata_->serialize(cursor);
     cursor = segment_->serialize(cursor, linkData);
     return cursor;
 }
 
-const uint8_t*
-CodeTier::deserialize(const uint8_t* cursor, const SharedBytes& bytecode, Metadata& metadata,
-                      const LinkDataTier& linkData)
+/* static */ const uint8_t*
+CodeTier::deserialize(const uint8_t* cursor, const LinkDataTier& linkData,
+                      UniqueCodeTier* codeTier)
 {
-    metadata_ = js::MakeUnique<MetadataTier>(Tier::Serialized);
-    if (!metadata_)
+    auto metadata = js::MakeUnique<MetadataTier>(Tier::Serialized);
+    if (!metadata)
         return nullptr;
-    cursor = metadata_->deserialize(cursor);
+    cursor = metadata->deserialize(cursor);
     if (!cursor)
         return nullptr;
 
-    auto segment = Move(js::MakeUnique<ModuleSegment>());
-    if (!segment)
-        return nullptr;
-    cursor = segment->deserialize(cursor, *bytecode, linkData, metadata, metadata_->codeRanges);
+    UniqueModuleSegment segment;
+    cursor = ModuleSegment::deserialize(cursor, linkData, &segment);
     if (!cursor)
         return nullptr;
-    segment_ = takeOwnership(Move(segment));
+
+    *codeTier = js::MakeUnique<CodeTier>(Move(metadata), Move(segment));
+    if (!*codeTier)
+        return nullptr;
 
     return cursor;
 }
 
 void
 CodeTier::addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* code, size_t* data) const
 {
     segment_->addSizeOfMisc(mallocSizeOf, code, data);
@@ -1103,43 +1073,57 @@ JumpTables::init(CompileMode mode, const
         if (cr.isFunction())
             setTieringEntry(cr.funcIndex(), codeBase + cr.funcTierEntry());
         else if (cr.isJitEntry())
             setJitEntry(cr.funcIndex(), codeBase + cr.begin());
     }
     return true;
 }
 
-Code::Code(UniqueCodeTier codeTier, const Metadata& metadata, JumpTables&& maybeJumpTables)
-  : tier1_(takeOwnership(Move(codeTier))),
+Code::Code(UniqueCodeTier tier1, const Metadata& metadata, JumpTables&& maybeJumpTables)
+  : tier1_(Move(tier1)),
     metadata_(&metadata),
     profilingLabels_(mutexid::WasmCodeProfilingLabels, CacheableCharsVector()),
     jumpTables_(Move(maybeJumpTables))
+{}
+
+bool
+Code::initialize(const ShareableBytes& bytecode, const LinkDataTier& linkData)
 {
+    MOZ_ASSERT(!initialized());
+
+    if (!tier1_->initialize(*this, bytecode, linkData, *metadata_))
+        return false;
+
+    MOZ_ASSERT(initialized());
+    return true;
 }
 
-Code::Code()
-  : profilingLabels_(mutexid::WasmCodeProfilingLabels, CacheableCharsVector())
-{
-}
-
-void
-Code::setTier2(UniqueCodeTier tier2) const
+bool
+Code::setTier2(UniqueCodeTier tier2, const ShareableBytes& bytecode,
+               const LinkDataTier& linkData) const
 {
     MOZ_RELEASE_ASSERT(!hasTier2());
     MOZ_RELEASE_ASSERT(tier2->tier() == Tier::Ion && tier1_->tier() == Tier::Baseline);
-    tier2_ = takeOwnership(Move(tier2));
+
+    if (!tier2->initialize(*this, bytecode, linkData, *metadata_))
+        return false;
+
+    tier2_ = Move(tier2);
+
+    return true;
 }
 
 void
 Code::commitTier2() const
 {
     MOZ_RELEASE_ASSERT(!hasTier2());
     MOZ_RELEASE_ASSERT(tier2_.get());
     hasTier2_ = true;
+    MOZ_ASSERT(hasTier2());
 }
 
 uint32_t
 Code::getFuncIndex(JSFunction* fun) const
 {
     if (fun->isAsmJSNative())
         return fun->asmJSFuncIndex();
     return jumpTables_.funcIndexFromJitEntry(fun->wasmJitEntry());
@@ -1175,24 +1159,30 @@ Code::bestTier() const
     return tier1_->tier();
 }
 
 const CodeTier&
 Code::codeTier(Tier tier) const
 {
     switch (tier) {
       case Tier::Baseline:
-        if (tier1_->tier() == Tier::Baseline)
+        if (tier1_->tier() == Tier::Baseline) {
+            MOZ_ASSERT(tier1_->initialized());
             return *tier1_;
+        }
         MOZ_CRASH("No code segment at this tier");
       case Tier::Ion:
-        if (tier1_->tier() == Tier::Ion)
+        if (tier1_->tier() == Tier::Ion) {
+            MOZ_ASSERT(tier1_->initialized());
             return *tier1_;
-        if (hasTier2())
+        }
+        if (tier2_) {
+            MOZ_ASSERT(tier2_->initialized());
             return *tier2_;
+        }
         MOZ_CRASH("No code segment at this tier");
       default:
         MOZ_CRASH();
     }
 }
 
 bool
 Code::containsCodePC(const void* pc) const
@@ -1365,48 +1355,71 @@ Code::addSizeOfMiscIfNotSeen(MallocSizeO
              metadata().sizeOfIncludingThisIfNotSeen(mallocSizeOf, seenMetadata) +
              profilingLabels_.lock()->sizeOfExcludingThis(mallocSizeOf) +
              jumpTables_.sizeOfMiscIncludingThis(mallocSizeOf);
 
     for (auto t : tiers())
         codeTier(t).addSizeOfMisc(mallocSizeOf, code, data);
 }
 
+bool
+CodeTier::initialize(const Code& code,
+                     const ShareableBytes& bytecode,
+                     const LinkDataTier& linkData,
+                     const Metadata& metadata)
+{
+    MOZ_ASSERT(!initialized());
+    code_ = &code;
+
+    MOZ_ASSERT(lazyStubs_.lock()->empty());
+
+    // See comments in CodeSegment::initialize() for why this must be last.
+    if (!segment_->initialize(*this, bytecode, linkData, metadata, *metadata_))
+        return false;
+
+    MOZ_ASSERT(initialized());
+    return true;
+}
+
 size_t
 Code::serializedSize() const
 {
     return metadata().serializedSize() +
            codeTier(Tier::Serialized).serializedSize();
 }
 
 uint8_t*
-Code::serialize(uint8_t* cursor, const LinkDataTier& linkDataTier) const
+Code::serialize(uint8_t* cursor, const LinkData& linkData) const
 {
     MOZ_RELEASE_ASSERT(!metadata().debugEnabled);
 
     cursor = metadata().serialize(cursor);
-    cursor = codeTier(Tier::Serialized).serialize(cursor, linkDataTier);
+    cursor = codeTier(Tier::Serialized).serialize(cursor, linkData.tier(Tier::Serialized));
     return cursor;
 }
 
-const uint8_t*
-Code::deserialize(const uint8_t* cursor, const SharedBytes& bytecode,
-                  const LinkDataTier& linkDataTier, Metadata& metadata)
+/* static */ const uint8_t*
+Code::deserialize(const uint8_t* cursor,
+                  const ShareableBytes& bytecode,
+                  const LinkData& linkData,
+                  Metadata& metadata,
+                  SharedCode* out)
 {
     cursor = metadata.deserialize(cursor);
     if (!cursor)
         return nullptr;
 
-    auto codeTier = js::MakeUnique<CodeTier>(Tier::Serialized);
-    if (!codeTier)
-        return nullptr;
-    cursor = codeTier->deserialize(cursor, bytecode, metadata, linkDataTier);
+    UniqueCodeTier codeTier;
+    cursor = CodeTier::deserialize(cursor, linkData.tier(Tier::Serialized), &codeTier);
     if (!cursor)
         return nullptr;
 
-    tier1_ = takeOwnership(Move(codeTier));
-    metadata_ = &metadata;
-
-    if (!jumpTables_.init(CompileMode::Once, tier1_->segment(), tier1_->metadata().codeRanges))
+    JumpTables jumpTables;
+    if (!jumpTables.init(CompileMode::Once, codeTier->segment(), codeTier->metadata().codeRanges))
         return nullptr;
 
+    MutableCode code = js_new<Code>(Move(codeTier), metadata, Move(jumpTables));
+    if (!code || !code->initialize(bytecode, linkData.tier(Tier::Serialized)))
+        return nullptr;
+
+    *out = code;
     return cursor;
 }
--- a/js/src/wasm/WasmCode.h
+++ b/js/src/wasm/WasmCode.h
@@ -29,16 +29,17 @@ namespace js {
 struct AsmJSMetadata;
 class WasmInstanceObject;
 
 namespace wasm {
 
 struct LinkDataTier;
 struct MetadataTier;
 struct Metadata;
+class LinkData;
 
 // ShareableBytes is a reference-counted Vector of bytes.
 
 struct ShareableBytes : ShareableBase<ShareableBytes>
 {
     // Vector is 'final', so instead make Vector a member and add boilerplate.
     Bytes bytes;
     ShareableBytes() = default;
@@ -76,40 +77,40 @@ class LazyStubSegment;
 // - LazyStubSegment, i.e. the code segment of entry stubs that are lazily
 // generated.
 
 class CodeSegment
 {
   protected:
     static UniqueCodeBytes AllocateCodeBytes(uint32_t codeLength);
 
-    UniqueCodeBytes bytes_;
-    uint32_t length_;
-
-    // A back reference to the owning code.
-    const CodeTier* codeTier_;
-
     enum class Kind {
         LazyStubs,
         Module
-    } kind_;
+    };
 
-    bool registerInProcessMap();
+    CodeSegment(UniqueCodeBytes bytes, uint32_t length, Kind kind)
+      : bytes_(Move(bytes)),
+        length_(length),
+        kind_(kind),
+        codeTier_(nullptr),
+        unregisterOnDestroy_(false)
+    {}
+
+    bool initialize(const CodeTier& codeTier);
 
   private:
-    bool registered_;
+    const UniqueCodeBytes bytes_;
+    const uint32_t        length_;
+    const Kind            kind_;
+    const CodeTier*       codeTier_;
+    bool                  unregisterOnDestroy_;
 
   public:
-    explicit CodeSegment(Kind kind = Kind::Module)
-      : length_(UINT32_MAX),
-        codeTier_(nullptr),
-        kind_(kind),
-        registered_(false)
-    {}
-
+    bool initialized() const { return !!codeTier_; }
     ~CodeSegment();
 
     bool isLazyStubs() const { return kind_ == Kind::LazyStubs; }
     bool isModule() const { return kind_ == Kind::Module; }
     const ModuleSegment* asModule() const {
         MOZ_ASSERT(isModule());
         return (ModuleSegment*) this;
     }
@@ -120,95 +121,66 @@ class CodeSegment
 
     uint8_t* base() const { return bytes_.get(); }
     uint32_t length() const { MOZ_ASSERT(length_ != UINT32_MAX); return length_; }
 
     bool containsCodePC(const void* pc) const {
         return pc >= base() && pc < (base() + length_);
     }
 
-    void initCodeTier(const CodeTier* codeTier) {
-        MOZ_ASSERT(!codeTier_);
-        codeTier_ = codeTier;
-    }
-    const CodeTier& codeTier() const { return *codeTier_; }
+    const CodeTier& codeTier() const { MOZ_ASSERT(initialized()); return *codeTier_; }
     const Code& code() const;
 
     void addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* code) const;
 };
 
 // A wasm ModuleSegment owns the allocated executable code for a wasm module.
 
 typedef UniquePtr<ModuleSegment> UniqueModuleSegment;
-typedef UniquePtr<const ModuleSegment> UniqueConstModuleSegment;
 
 class ModuleSegment : public CodeSegment
 {
-    Tier            tier_;
+    const Tier      tier_;
+    uint8_t* const  outOfBoundsCode_;
+    uint8_t* const  unalignedAccessCode_;
+    uint8_t* const  trapCode_;
 
-    // These are pointers into code for stubs used for signal-handler
-    // control-flow transfer.
-    uint8_t*        outOfBoundsCode_;
-    uint8_t*        unalignedAccessCode_;
-    uint8_t*        trapCode_;
+  public:
+    ModuleSegment(Tier tier,
+                  UniqueCodeBytes codeBytes,
+                  uint32_t codeLength,
+                  const LinkDataTier& linkData);
 
-    bool initialize(Tier tier,
-                    UniqueCodeBytes bytes,
-                    uint32_t codeLength,
+    static UniqueModuleSegment create(Tier tier,
+                                      jit::MacroAssembler& masm,
+                                      const LinkDataTier& linkData);
+    static UniqueModuleSegment create(Tier tier,
+                                      const Bytes& unlinkedBytes,
+                                      const LinkDataTier& linkData);
+
+    bool initialize(const CodeTier& codeTier,
                     const ShareableBytes& bytecode,
                     const LinkDataTier& linkData,
                     const Metadata& metadata,
-                    const CodeRangeVector& codeRanges);
-
-    static UniqueModuleSegment create(Tier tier,
-                                      UniqueCodeBytes bytes,
-                                      uint32_t codeLength,
-                                      const ShareableBytes& bytecode,
-                                      const LinkDataTier& linkData,
-                                      const Metadata& metadata,
-                                      const CodeRangeVector& codeRanges);
-  public:
-    ModuleSegment(const ModuleSegment&) = delete;
-    void operator=(const ModuleSegment&) = delete;
-
-    ModuleSegment()
-      : CodeSegment(),
-        tier_(Tier(-1)),
-        outOfBoundsCode_(nullptr),
-        unalignedAccessCode_(nullptr),
-        trapCode_(nullptr)
-    {}
-
-    static UniqueModuleSegment create(Tier tier,
-                                      jit::MacroAssembler& masm,
-                                      const ShareableBytes& bytecode,
-                                      const LinkDataTier& linkData,
-                                      const Metadata& metadata,
-                                      const CodeRangeVector& codeRanges);
-
-    static UniqueModuleSegment create(Tier tier,
-                                      const Bytes& unlinkedBytes,
-                                      const ShareableBytes& bytecode,
-                                      const LinkDataTier& linkData,
-                                      const Metadata& metadata,
-                                      const CodeRangeVector& codeRanges);
+                    const MetadataTier& metadataTier);
 
     Tier tier() const { return tier_; }
 
+    // Pointers to stubs to which PC is redirected from the signal-handler.
+
     uint8_t* outOfBoundsCode() const { return outOfBoundsCode_; }
     uint8_t* unalignedAccessCode() const { return unalignedAccessCode_; }
     uint8_t* trapCode() const { return trapCode_; }
 
     // Structured clone support:
 
     size_t serializedSize() const;
-    uint8_t* serialize(uint8_t* cursor, const LinkDataTier& linkDataTier) const;
-    const uint8_t* deserialize(const uint8_t* cursor, const ShareableBytes& bytecode,
-                               const LinkDataTier& linkDataTier, const Metadata& metadata,
-                               const CodeRangeVector& codeRanges);
+    uint8_t* serialize(uint8_t* cursor, const LinkDataTier& linkData) const;
+    static const uint8_t* deserialize(const uint8_t* cursor, const LinkDataTier& linkData,
+                                      UniqueModuleSegment* segment);
 
     const CodeRange* lookupRange(const void* pc) const;
 
     void addSizeOfMisc(mozilla::MallocSizeOf mallocSizeOf, size_t* code, size_t* data) const;
 };
 
 // A FuncExport represents a single function definition inside a wasm Module
 // that has been exported one or more times. A FuncExport represents an
@@ -522,50 +494,47 @@ using UniqueMetadataTier = UniquePtr<Met
 
 // LazyStubSegment is a code segment lazily generated for function entry stubs
 // (both interpreter and jit ones).
 //
 // Because a stub is usually small (a few KiB) and an executable code segment
 // isn't (64KiB), a given stub segment can contain entry stubs of many
 // functions.
 
+using UniqueLazyStubSegment = UniquePtr<LazyStubSegment>;
+using LazyStubSegmentVector = Vector<UniqueLazyStubSegment, 0, SystemAllocPolicy>;
+
 class LazyStubSegment : public CodeSegment
 {
     CodeRangeVector codeRanges_;
     size_t usedBytes_;
 
     static constexpr size_t MPROTECT_PAGE_SIZE = 4 * 1024;
 
-    bool initialize(UniqueCodeBytes codeBytes, size_t length);
-
   public:
-    explicit LazyStubSegment(const CodeTier& codeTier)
-      : CodeSegment(CodeSegment::Kind::LazyStubs),
+    LazyStubSegment(UniqueCodeBytes bytes, size_t length)
+      : CodeSegment(Move(bytes), length, CodeSegment::Kind::LazyStubs),
         usedBytes_(0)
-    {
-        initCodeTier(&codeTier);
-    }
+    {}
 
-    static UniquePtr<LazyStubSegment> create(const CodeTier& codeTier, size_t length);
+    static UniqueLazyStubSegment create(const CodeTier& codeTier, size_t codeLength);
+
     static size_t AlignBytesNeeded(size_t bytes) { return AlignBytes(bytes, MPROTECT_PAGE_SIZE); }
 
     bool hasSpace(size_t bytes) const;
     bool addStubs(size_t codeLength, const Uint32Vector& funcExportIndices,
                   const FuncExportVector& funcExports, const CodeRangeVector& codeRanges,
                   uint8_t** codePtr, size_t* indexFirstInsertedCodeRange);
 
     const CodeRangeVector& codeRanges() const { return codeRanges_; }
     const CodeRange* lookupRange(const void* pc) const;
 
     void addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* code, size_t* data) const;
 };
 
-using UniqueLazyStubSegment = UniquePtr<LazyStubSegment>;
-using LazyStubSegmentVector = Vector<UniqueLazyStubSegment, 0, SystemAllocPolicy>;
-
 // LazyFuncExport helps to efficiently lookup a CodeRange from a given function
 // index. It is inserted in a vector sorted by function index, to perform
 // binary search on it later.
 
 struct LazyFuncExport
 {
     size_t funcIndex;
     size_t lazyStubSegmentIndex;
@@ -618,80 +587,67 @@ class LazyStubTier
     void setJitEntries(const Maybe<size_t>& stubSegmentIndex, const Code& code);
 
     void addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* code, size_t* data) const;
 };
 
 // CodeTier contains all the data related to a given compilation tier. It is
 // built during module generation and then immutably stored in a Code.
 
+typedef UniquePtr<CodeTier> UniqueCodeTier;
+typedef UniquePtr<const CodeTier> UniqueConstCodeTier;
+
 class CodeTier
 {
-    const Tier                  tier_;
-    const Code*                 code_;
+    const Code* code_;
 
     // Serialized information.
-    UniqueMetadataTier          metadata_;
-    UniqueConstModuleSegment    segment_;
+    const UniqueMetadataTier metadata_;
+    const UniqueModuleSegment segment_;
 
     // Lazy stubs, not serialized.
     ExclusiveData<LazyStubTier> lazyStubs_;
 
-    UniqueConstModuleSegment takeOwnership(UniqueModuleSegment segment) const {
-        segment->initCodeTier(this);
-        return UniqueConstModuleSegment(segment.release());
-    }
-
     static const MutexId& mutexForTier(Tier tier) {
         if (tier == Tier::Baseline)
             return mutexid::WasmLazyStubsTier1;
         MOZ_ASSERT(tier == Tier::Ion);
         return mutexid::WasmLazyStubsTier2;
     }
 
   public:
-    explicit CodeTier(Tier tier)
-      : tier_(tier),
-        code_(nullptr),
-        metadata_(nullptr),
-        segment_(nullptr),
-        lazyStubs_(mutexForTier(tier))
+    CodeTier(UniqueMetadataTier metadata, UniqueModuleSegment segment)
+      : code_(nullptr),
+        metadata_(Move(metadata)),
+        segment_(Move(segment)),
+        lazyStubs_(mutexForTier(segment_->tier()))
     {}
 
-    CodeTier(Tier tier, UniqueMetadataTier metadata, UniqueModuleSegment segment)
-      : tier_(tier),
-        code_(nullptr),
-        metadata_(Move(metadata)),
-        segment_(takeOwnership(Move(segment))),
-        lazyStubs_(mutexForTier(tier))
-    {}
+    bool initialized() const { return !!code_ && segment_->initialized(); }
 
-    void initCode(const Code* code) {
-        MOZ_ASSERT(!code_);
-        code_ = code;
-    }
+    bool initialize(const Code& code,
+                    const ShareableBytes& bytecode,
+                    const LinkDataTier& linkData,
+                    const Metadata& metadata);
 
-    Tier tier() const { return tier_; }
+    Tier tier() const { return segment_->tier(); }
     const ExclusiveData<LazyStubTier>& lazyStubs() const { return lazyStubs_; }
     const MetadataTier& metadata() const { return *metadata_.get(); }
     const ModuleSegment& segment() const { return *segment_.get(); }
-    const Code& code() const { return *code_; }
+    const Code& code() const { MOZ_ASSERT(initialized()); return *code_; }
 
     const CodeRange* lookupRange(const void* pc) const;
 
     size_t serializedSize() const;
     uint8_t* serialize(uint8_t* cursor, const LinkDataTier& linkData) const;
-    const uint8_t* deserialize(const uint8_t* cursor, const SharedBytes& bytecode,
-                               Metadata& metadata, const LinkDataTier& linkData);
+    static const uint8_t* deserialize(const uint8_t* cursor, const LinkDataTier& linkData,
+                                      UniqueCodeTier* codeTier);
     void addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* code, size_t* data) const;
 };
 
-typedef UniquePtr<CodeTier> UniqueCodeTier;
-typedef UniquePtr<const CodeTier> UniqueConstCodeTier;
-
 // Jump tables to take tiering into account, when calling either from wasm to
 // wasm (through rabaldr) or from jit to wasm (jit entry).
 
 class JumpTables
 {
     using TablePointer = mozilla::UniquePtr<void*[], JS::FreePolicy>;
 
     CompileMode mode_;
@@ -738,42 +694,43 @@ class JumpTables
     }
 };
 
 // Code objects own executable code and the metadata that describe it. A single
 // Code object is normally shared between a module and all its instances.
 //
 // profilingLabels_ is lazily initialized, but behind a lock.
 
+typedef RefPtr<const Code> SharedCode;
+typedef RefPtr<Code> MutableCode;
+
 class Code : public ShareableBase<Code>
 {
-    UniqueConstCodeTier                 tier1_;
+    UniqueCodeTier                      tier1_;
     mutable UniqueConstCodeTier         tier2_; // Access only when hasTier2() is true
     mutable Atomic<bool>                hasTier2_;
     SharedMetadata                      metadata_;
     ExclusiveData<CacheableCharsVector> profilingLabels_;
     JumpTables                          jumpTables_;
 
-    UniqueConstCodeTier takeOwnership(UniqueCodeTier codeTier) const {
-        codeTier->initCode(this);
-        return UniqueConstCodeTier(codeTier.release());
-    }
+  public:
+    Code(UniqueCodeTier tier1, const Metadata& metadata, JumpTables&& maybeJumpTables);
+    bool initialized() const { return tier1_->initialized(); }
 
-  public:
-    Code();
-    Code(UniqueCodeTier tier, const Metadata& metadata, JumpTables&& maybeJumpTables);
+    bool initialize(const ShareableBytes& bytecode, const LinkDataTier& linkData);
 
     void setTieringEntry(size_t i, void* target) const { jumpTables_.setTieringEntry(i, target); }
     void** tieringJumpTable() const { return jumpTables_.tiering(); }
 
     void setJitEntry(size_t i, void* target) const { jumpTables_.setJitEntry(i, target); }
     void** getAddressOfJitEntry(size_t i) const { return jumpTables_.getAddressOfJitEntry(i); }
     uint32_t getFuncIndex(JSFunction* fun) const;
 
-    void setTier2(UniqueCodeTier tier2) const;
+    bool setTier2(UniqueCodeTier tier2, const ShareableBytes& bytecode,
+                  const LinkDataTier& linkData) const;
     void commitTier2() const;
 
     bool hasTier2() const { return hasTier2_; }
     Tiers tiers() const;
     bool hasTier(Tier t) const;
 
     Tier stableTier() const;    // This is stable during a run
     Tier bestTier() const;      // This may transition from Baseline -> Ion at any time
@@ -809,20 +766,20 @@ class Code : public ShareableBase<Code>
                                 size_t* code,
                                 size_t* data) const;
 
     // A Code object is serialized as the length and bytes of the machine code
     // after statically unlinking it; the Code is then later recreated from the
     // machine code and other parts.
 
     size_t serializedSize() const;
-    uint8_t* serialize(uint8_t* cursor, const LinkDataTier& linkDataTier) const;
-    const uint8_t* deserialize(const uint8_t* cursor, const SharedBytes& bytecode,
-                               const LinkDataTier& linkDataTier, Metadata& metadata);
+    uint8_t* serialize(uint8_t* cursor, const LinkData& linkData) const;
+    static const uint8_t* deserialize(const uint8_t* cursor,
+                                      const ShareableBytes& bytecode,
+                                      const LinkData& linkData,
+                                      Metadata& metadata,
+                                      SharedCode* code);
 };
 
-typedef RefPtr<const Code> SharedCode;
-typedef RefPtr<Code> MutableCode;
-
 } // namespace wasm
 } // namespace js
 
 #endif // wasm_code_h
--- a/js/src/wasm/WasmGenerator.cpp
+++ b/js/src/wasm/WasmGenerator.cpp
@@ -938,18 +938,17 @@ ModuleGenerator::finish(const ShareableB
     // All functions and stubs have been compiled, finish linking and metadata.
 
     if (!finishCode())
         return nullptr;
 
     if (!finishMetadata(bytecode))
         return nullptr;
 
-    return ModuleSegment::create(tier(), masm_, bytecode, *linkDataTier_, *metadata_,
-                                 metadataTier_->codeRanges);
+    return ModuleSegment::create(tier(), masm_, *linkDataTier_);
 }
 
 SharedModule
 ModuleGenerator::finishModule(const ShareableBytes& bytecode)
 {
     MOZ_ASSERT(mode() == CompileMode::Once || mode() == CompileMode::Tier1);
 
     UniqueModuleSegment moduleSegment = finish(bytecode);
@@ -967,22 +966,22 @@ ModuleGenerator::finishModule(const Shar
         if (!bytes.resize(masm_.bytesNeeded()))
             return nullptr;
         masm_.executableCopy(bytes.begin(), /* flushICache = */ false);
         maybeDebuggingBytes = js::MakeUnique<Bytes>(Move(bytes));
         if (!maybeDebuggingBytes)
             return nullptr;
     }
 
-    auto codeTier = js::MakeUnique<CodeTier>(tier(), Move(metadataTier_), Move(moduleSegment));
+    auto codeTier = js::MakeUnique<CodeTier>(Move(metadataTier_), Move(moduleSegment));
     if (!codeTier)
         return nullptr;
 
-    SharedCode code = js_new<Code>(Move(codeTier), *metadata_, Move(jumpTables));
-    if (!code)
+    MutableCode code = js_new<Code>(Move(codeTier), *metadata_, Move(jumpTables));
+    if (!code || !code->initialize(bytecode, *linkDataTier_))
         return nullptr;
 
     SharedModule module(js_new<Module>(Move(assumptions_),
                                        *code,
                                        Move(maybeDebuggingBytes),
                                        LinkData(Move(linkDataTier_)),
                                        Move(env_->imports),
                                        Move(env_->exports),
@@ -1007,17 +1006,17 @@ ModuleGenerator::finishTier2(Module& mod
 
     if (cancelled_ && *cancelled_)
         return false;
 
     UniqueModuleSegment moduleSegment = finish(module.bytecode());
     if (!moduleSegment)
         return false;
 
-    auto tier2 = js::MakeUnique<CodeTier>(tier(), Move(metadataTier_), Move(moduleSegment));
+    auto tier2 = js::MakeUnique<CodeTier>(Move(metadataTier_), Move(moduleSegment));
     if (!tier2)
         return false;
 
     if (MOZ_UNLIKELY(JitOptions.wasmDelayTier2)) {
         // Introduce an artificial delay when testing wasmDelayTier2, since we
         // want to exercise both tier1 and tier2 code in this case.
         std::this_thread::sleep_for(std::chrono::milliseconds(500));
     }
--- a/js/src/wasm/WasmModule.cpp
+++ b/js/src/wasm/WasmModule.cpp
@@ -105,73 +105,73 @@ LinkDataTier::deserialize(const uint8_t*
 size_t
 LinkDataTier::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
 {
     return internalLinks.sizeOfExcludingThis(mallocSizeOf) +
            symbolicLinks.sizeOfExcludingThis(mallocSizeOf);
 }
 
 void
-LinkData::setTier2(UniqueLinkDataTier linkData) const
+LinkData::setTier2(UniqueLinkDataTier tier) const
 {
-    MOZ_RELEASE_ASSERT(linkData->tier == Tier::Ion && linkData1_->tier == Tier::Baseline);
-    MOZ_RELEASE_ASSERT(!linkData2_.get());
-    linkData2_ = Move(linkData);
+    MOZ_RELEASE_ASSERT(tier->tier == Tier::Ion && tier1_->tier == Tier::Baseline);
+    MOZ_RELEASE_ASSERT(!tier2_.get());
+    tier2_ = Move(tier);
 }
 
 const LinkDataTier&
-LinkData::linkData(Tier tier) const
+LinkData::tier(Tier tier) const
 {
     switch (tier) {
       case Tier::Baseline:
-        if (linkData1_->tier == Tier::Baseline)
-            return *linkData1_;
+        if (tier1_->tier == Tier::Baseline)
+            return *tier1_;
         MOZ_CRASH("No linkData at this tier");
       case Tier::Ion:
-        if (linkData1_->tier == Tier::Ion)
-            return *linkData1_;
-        if (linkData2_)
-            return *linkData2_;
+        if (tier1_->tier == Tier::Ion)
+            return *tier1_;
+        if (tier2_)
+            return *tier2_;
         MOZ_CRASH("No linkData at this tier");
       default:
         MOZ_CRASH();
     }
 }
 
 size_t
 LinkData::serializedSize() const
 {
-    return linkData(Tier::Serialized).serializedSize();
+    return tier(Tier::Serialized).serializedSize();
 }
 
 uint8_t*
 LinkData::serialize(uint8_t* cursor) const
 {
-    cursor = linkData(Tier::Serialized).serialize(cursor);
+    cursor = tier(Tier::Serialized).serialize(cursor);
     return cursor;
 }
 
 const uint8_t*
 LinkData::deserialize(const uint8_t* cursor)
 {
-    MOZ_ASSERT(!linkData1_);
-    linkData1_ = js::MakeUnique<LinkDataTier>(Tier::Serialized);
-    if (!linkData1_)
+    MOZ_ASSERT(!tier1_);
+    tier1_ = js::MakeUnique<LinkDataTier>(Tier::Serialized);
+    if (!tier1_)
         return nullptr;
-    cursor = linkData1_->deserialize(cursor);
+    cursor = tier1_->deserialize(cursor);
     return cursor;
 }
 
 size_t
 LinkData::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
 {
     size_t sum = 0;
-    sum += linkData1_->sizeOfExcludingThis(mallocSizeOf);
-    if (linkData2_)
-        sum += linkData2_->sizeOfExcludingThis(mallocSizeOf);
+    sum += tier1_->sizeOfExcludingThis(mallocSizeOf);
+    if (tier2_)
+        sum += tier2_->sizeOfExcludingThis(mallocSizeOf);
     return sum;
 }
 
 class Module::Tier2GeneratorTaskImpl : public Tier2GeneratorTask
 {
     SharedModule            module_;
     SharedCompileArgs       compileArgs_;
     Atomic<bool>            cancelled_;
@@ -238,62 +238,67 @@ Module::notifyCompilationListeners()
         tiering.notify_all(/* inactive */);
     }
 
     for (RefPtr<JS::WasmModuleListener>& listener : listeners)
         listener->onCompilationComplete();
 }
 
 bool
-Module::finishTier2(UniqueLinkDataTier linkData2, UniqueCodeTier tier2, ModuleEnvironment* env2)
+Module::finishTier2(UniqueLinkDataTier linkData2, UniqueCodeTier tier2Arg, ModuleEnvironment* env2)
 {
-    MOZ_ASSERT(code().bestTier() == Tier::Baseline && tier2->tier() == Tier::Ion);
+    MOZ_ASSERT(code().bestTier() == Tier::Baseline && tier2Arg->tier() == Tier::Ion);
+
+    // Install the data in the data structures. They will not be visible
+    // until commitTier2().
 
+    if (!code().setTier2(Move(tier2Arg), *bytecode_, *linkData2))
+        return false;
+    linkData().setTier2(Move(linkData2));
+    for (uint32_t i = 0; i < elemSegments_.length(); i++)
+        elemSegments_[i].setTier2(Move(env2->elemSegments[i].elemCodeRangeIndices(Tier::Ion)));
+
+    // Before we can make tier-2 live, we need to compile tier2 versions of any
+    // extant tier1 lazy stubs (otherwise, tiering would break the assumption
+    // that any extant exported wasm function has had a lazy entry stub already
+    // compiled for it).
     {
         // We need to prevent new tier1 stubs generation until we've committed
         // the newer tier2 stubs, otherwise we might not generate one tier2
         // stub that has been generated for tier1 before we committed.
 
         const MetadataTier& metadataTier1 = metadata(Tier::Baseline);
 
         auto stubs1 = code().codeTier(Tier::Baseline).lazyStubs().lock();
-        auto stubs2 = tier2->lazyStubs().lock();
+        auto stubs2 = code().codeTier(Tier::Ion).lazyStubs().lock();
 
         MOZ_ASSERT(stubs2->empty());
 
         Uint32Vector funcExportIndices;
         for (size_t i = 0; i < metadataTier1.funcExports.length(); i++) {
             const FuncExport& fe = metadataTier1.funcExports[i];
             if (fe.hasEagerStubs())
                 continue;
             MOZ_ASSERT(!env2->isAsmJS(), "only wasm functions are lazily exported");
             if (!stubs1->hasStub(fe.funcIndex()))
                 continue;
             if (!funcExportIndices.emplaceBack(i))
                 return false;
         }
 
         HasGcTypes gcTypesEnabled = code().metadata().temporaryHasGcTypes;
+        const CodeTier& tier2 = code().codeTier(Tier::Ion);
 
         Maybe<size_t> stub2Index;
-        if (!stubs2->createTier2(gcTypesEnabled, funcExportIndices, *tier2, &stub2Index))
+        if (!stubs2->createTier2(gcTypesEnabled, funcExportIndices, tier2, &stub2Index))
             return false;
 
-        // Install the data in the data structures. They will not be visible
-        // yet.
+        // Now that we can't fail or otherwise abort tier2, make it live.
 
         MOZ_ASSERT(!code().hasTier2());
-        linkData().setTier2(Move(linkData2));
-        code().setTier2(Move(tier2));
-        for (uint32_t i = 0; i < elemSegments_.length(); i++)
-            elemSegments_[i].setTier2(Move(env2->elemSegments[i].elemCodeRangeIndices(Tier::Ion)));
-
-        // Now that all the code and metadata is valid, make tier 2 code
-        // visible and unblock anyone waiting on it.
-
         code().commitTier2();
 
         // Now tier2 is committed and we can update jump tables entries to
         // start making tier2 live.  Because lazy stubs are protected by a lock
         // and notifyCompilationListeners should be called without any lock
         // held, do it before.
 
         stubs2->setJitEntries(stub2Index, code());
@@ -413,17 +418,17 @@ Module::compiledSerialize(uint8_t* compi
 
     uint8_t* cursor = compiledBegin;
     cursor = assumptions_.serialize(cursor);
     cursor = linkData_.serialize(cursor);
     cursor = SerializeVector(cursor, imports_);
     cursor = SerializeVector(cursor, exports_);
     cursor = SerializePodVector(cursor, dataSegments_);
     cursor = SerializeVector(cursor, elemSegments_);
-    cursor = code_->serialize(cursor, linkData_.linkData(Tier::Serialized));
+    cursor = code_->serialize(cursor, linkData_);
     MOZ_RELEASE_ASSERT(cursor == compiledBegin + compiledSize);
 }
 
 /* static */ bool
 Module::assumptionsMatch(const Assumptions& current, const uint8_t* compiledBegin, size_t remain)
 {
     Assumptions cached;
     if (!cached.deserialize(compiledBegin, remain))
@@ -476,18 +481,18 @@ Module::deserialize(const uint8_t* bytec
     if (!cursor)
         return nullptr;
 
     ElemSegmentVector elemSegments;
     cursor = DeserializeVector(cursor, &elemSegments);
     if (!cursor)
         return nullptr;
 
-    MutableCode code = js_new<Code>();
-    cursor = code->deserialize(cursor, bytecode, linkData.linkData(Tier::Serialized), *metadata);
+    SharedCode code;
+    cursor = Code::deserialize(cursor, *bytecode, linkData, *metadata, &code);
     if (!cursor)
         return nullptr;
 
     MOZ_RELEASE_ASSERT(cursor == compiledBegin + compiledSize);
     MOZ_RELEASE_ASSERT(!!maybeMetadata == code->metadata().isAsmJS());
 
     return js_new<Module>(Move(assumptions),
                           *code,
@@ -1240,44 +1245,41 @@ Module::instantiate(JSContext* cx,
 
     if (metadata().debugEnabled) {
         // The first time through, use the pre-linked code in the module but
         // mark it as busy. Subsequently, instantiate the copy of the code
         // bytes that we keep around for debugging instead, because the debugger
         // may patch the pre-linked code at any time.
         if (!codeIsBusy_.compareExchange(false, true)) {
             Tier tier = Tier::Baseline;
-            auto segment = ModuleSegment::create(tier,
-                                                 *unlinkedCodeForDebugging_,
-                                                 *bytecode_,
-                                                 linkData(tier),
-                                                 metadata(),
-                                                 metadata(tier).codeRanges);
+            auto segment = ModuleSegment::create(tier, *unlinkedCodeForDebugging_, linkData(tier));
             if (!segment) {
                 ReportOutOfMemory(cx);
                 return false;
             }
 
             UniqueMetadataTier metadataTier = js::MakeUnique<MetadataTier>(tier);
             if (!metadataTier || !metadataTier->clone(metadata(tier)))
                 return false;
 
-            auto codeTier = js::MakeUnique<CodeTier>(tier, Move(metadataTier), Move(segment));
+            auto codeTier = js::MakeUnique<CodeTier>(Move(metadataTier), Move(segment));
             if (!codeTier)
                 return false;
 
             JumpTables jumpTables;
             if (!jumpTables.init(CompileMode::Once, codeTier->segment(), metadata(tier).codeRanges))
                 return false;
 
-            code = js_new<Code>(Move(codeTier), metadata(), Move(jumpTables));
-            if (!code) {
+            MutableCode debugCode = js_new<Code>(Move(codeTier), metadata(), Move(jumpTables));
+            if (!debugCode || !debugCode->initialize(*bytecode_, linkData(tier))) {
                 ReportOutOfMemory(cx);
                 return false;
             }
+
+            code = debugCode;
         }
     }
 
     // To support viewing the source of an instance (Instance::createText), the
     // instance must hold onto a ref of the bytecode (keeping it alive). This
     // wastes memory for most users, so we try to only save the source when a
     // developer actually cares: when the compartment is debuggable (which is
     // true when the web console is open), has code compiled with debug flag
--- a/js/src/wasm/WasmModule.h
+++ b/js/src/wasm/WasmModule.h
@@ -77,25 +77,25 @@ struct LinkDataTier : LinkDataTierCachea
 
     WASM_DECLARE_SERIALIZABLE(LinkData)
 };
 
 typedef UniquePtr<LinkDataTier> UniqueLinkDataTier;
 
 class LinkData
 {
-    UniqueLinkDataTier         linkData1_; // Always present
-    mutable UniqueLinkDataTier linkData2_; // Access only if hasTier2() is true
+    UniqueLinkDataTier         tier1_; // Always present
+    mutable UniqueLinkDataTier tier2_; // Access only if hasTier2() is true
 
   public:
     LinkData() {}
-    explicit LinkData(UniqueLinkDataTier linkData) : linkData1_(Move(linkData)) {}
+    explicit LinkData(UniqueLinkDataTier tier) : tier1_(Move(tier)) {}
 
     void setTier2(UniqueLinkDataTier linkData) const;
-    const LinkDataTier& linkData(Tier tier) const;
+    const LinkDataTier& tier(Tier tier) const;
 
     WASM_DECLARE_SERIALIZABLE(LinkData)
 };
 
 // Contains the locked tiering state of a Module: whether there is an active
 // background tier-2 compilation in progress and, if so, the list of listeners
 // waiting for the tier-2 compilation to complete.
 
@@ -187,17 +187,17 @@ class Module : public JS::WasmModule
     }
     ~Module() override { /* Note: can be called on any thread */ }
 
     const Code& code() const { return *code_; }
     const ModuleSegment& moduleSegment(Tier t) const { return code_->segment(t); }
     const Metadata& metadata() const { return code_->metadata(); }
     const MetadataTier& metadata(Tier t) const { return code_->metadata(t); }
     const LinkData& linkData() const { return linkData_; }
-    const LinkDataTier& linkData(Tier t) const { return linkData_.linkData(t); }
+    const LinkDataTier& linkData(Tier t) const { return linkData_.tier(t); }
     const ImportVector& imports() const { return imports_; }
     const ExportVector& exports() const { return exports_; }
     const ShareableBytes& bytecode() const { return *bytecode_; }
     uint32_t codeLength(Tier t) const { return code_->segment(t).length(); }
 
     // Instantiate this module with the given imports:
 
     bool instantiate(JSContext* cx,
--- a/js/src/wasm/WasmProcess.cpp
+++ b/js/src/wasm/WasmProcess.cpp
@@ -212,16 +212,17 @@ class ProcessCodeSegmentMap
     }
 };
 
 static ProcessCodeSegmentMap processCodeSegmentMap;
 
 bool
 wasm::RegisterCodeSegment(const CodeSegment* cs)
 {
+    MOZ_ASSERT(cs->codeTier().code().initialized());
     return processCodeSegmentMap.insert(cs);
 }
 
 void
 wasm::UnregisterCodeSegment(const CodeSegment* cs)
 {
     processCodeSegmentMap.remove(cs);
 }
--- a/layout/generic/nsCanvasFrame.cpp
+++ b/layout/generic/nsCanvasFrame.cpp
@@ -370,17 +370,17 @@ nsDisplayCanvasBackgroundImage::IsSingle
     return false;
 
 
   nsPresContext* presContext = mFrame->PresContext();
   uint32_t flags = aBuilder->GetBackgroundPaintFlags();
   nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
   const nsStyleImageLayers::Layer &layer = mBackgroundStyle->StyleBackground()->mImage.mLayers[mLayer];
 
-  if (layer.mAttachment != NS_STYLE_IMAGELAYER_ATTACHMENT_FIXED)
+  if (layer.mAttachment != StyleImageLayerAttachment::Fixed)
     return false;
 
    nsBackgroundLayerState state =
      nsCSSRendering::PrepareImageLayer(presContext, mFrame, flags,
                                        borderArea, aClipRect, layer);
 
 
   // We only care about images here, not gradients.
--- a/layout/painting/nsCSSRendering.cpp
+++ b/layout/painting/nsCSSRendering.cpp
@@ -2329,17 +2329,17 @@ nsCSSRendering::GetImageLayerClip(const 
     layerClip = haveRoundedCorners
                      ? StyleGeometryBox::MozAlmostPadding
                      : StyleGeometryBox::PaddingBox;
   }
 
   aClipState->mBGClipArea = clipBorderArea;
 
   if (aForFrame->IsScrollFrame() &&
-      NS_STYLE_IMAGELAYER_ATTACHMENT_LOCAL == aLayer.mAttachment) {
+      StyleImageLayerAttachment::Local == aLayer.mAttachment) {
     // As of this writing, this is still in discussion in the CSS Working Group
     // http://lists.w3.org/Archives/Public/www-style/2013Jul/0250.html
 
     // The rectangle for 'background-clip' scrolls with the content,
     // but the background is also clipped at a non-scrolling 'padding-box'
     // like the content. (See below.)
     // Therefore, only 'content-box' makes a difference here.
     if (layerClip == StyleGeometryBox::ContentBox) {
@@ -2942,17 +2942,17 @@ nsCSSRendering::ComputeImageLayerPositio
   }
 
   MOZ_ASSERT(!aForFrame->IsFrameOfType(nsIFrame::eSVG) ||
              aForFrame->IsSVGOuterSVGFrame());
 
   LayoutFrameType frameType = aForFrame->Type();
   nsIFrame* geometryFrame = aForFrame;
   if (MOZ_UNLIKELY(frameType == LayoutFrameType::Scroll &&
-                   NS_STYLE_IMAGELAYER_ATTACHMENT_LOCAL == aLayer.mAttachment)) {
+                   StyleImageLayerAttachment::Local == aLayer.mAttachment)) {
     nsIScrollableFrame* scrollableFrame = do_QueryFrame(aForFrame);
     positionArea = nsRect(
       scrollableFrame->GetScrolledFrame()->GetPosition()
         // For the dir=rtl case:
         + scrollableFrame->GetScrollRange().TopLeft(),
       scrollableFrame->GetScrolledRect().Size());
     // The ScrolledRect’s size does not include the borders or scrollbars,
     // reverse the handling of background-origin
@@ -3003,17 +3003,17 @@ nsCSSRendering::ComputeImageLayerPositio
       border += geometryFrame->GetUsedPadding();
       NS_ASSERTION(layerOrigin == StyleGeometryBox::ContentBox,
                    "unknown background-origin value");
     }
     positionArea.Deflate(border);
   }
 
   nsIFrame* attachedToFrame = aForFrame;
-  if (NS_STYLE_IMAGELAYER_ATTACHMENT_FIXED == aLayer.mAttachment) {
+  if (StyleImageLayerAttachment::Fixed == aLayer.mAttachment) {
     // If it's a fixed background attachment, then the image is placed
     // relative to the viewport, which is the area of the root frame
     // in a screen context or the page content frame in a print context.
     attachedToFrame = aPresContext->PresShell()->GetRootFrame();
     NS_ASSERTION(attachedToFrame, "no root frame");
     nsIFrame* pageContentFrame = nullptr;
     if (aPresContext->IsPaginated()) {
       pageContentFrame = nsLayoutUtils::GetClosestFrameOfType(
@@ -3278,17 +3278,17 @@ nsCSSRendering::PrepareImageLayer(nsPres
   if (aOutIsTransformedFixed) {
     *aOutIsTransformedFixed = transformedFixed;
   }
 
   // For background-attachment:fixed backgrounds, we'll override the area
   // where the background can be drawn to the viewport.
   nsRect bgClipRect = aBGClipRect;
 
-  if (NS_STYLE_IMAGELAYER_ATTACHMENT_FIXED == aLayer.mAttachment &&
+  if (StyleImageLayerAttachment::Fixed == aLayer.mAttachment &&
       !transformedFixed &&
       (aFlags & nsCSSRendering::PAINTBG_TO_WINDOW)) {
     bgClipRect = positionArea + aBorderArea.TopLeft();
   }
 
   StyleImageLayerRepeat repeatX = aLayer.mRepeat.mXRepeat;
   StyleImageLayerRepeat repeatY = aLayer.mRepeat.mYRepeat;
 
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -3496,17 +3496,17 @@ nsDisplayBackgroundImage::GetInitData(ns
     nsCSSRendering::PrepareImageLayer(presContext, aFrame, flags,
                                       aBackgroundRect, aBackgroundRect, layer,
                                       &isTransformedFixed);
 
   // background-attachment:fixed is treated as background-attachment:scroll
   // if it's affected by a transform.
   // See https://www.w3.org/Bugs/Public/show_bug.cgi?id=17521.
   bool shouldTreatAsFixed =
-    layer.mAttachment == NS_STYLE_IMAGELAYER_ATTACHMENT_FIXED && !isTransformedFixed;
+    layer.mAttachment == StyleImageLayerAttachment::Fixed && !isTransformedFixed;
 
   bool shouldFixToViewport = shouldTreatAsFixed && !layer.mImage.IsEmpty();
   bool isRasterImage = state.mImageRenderer.IsRasterImage();
   nsCOMPtr<imgIContainer> image;
   if (isRasterImage) {
     image = state.mImageRenderer.GetImage();
   }
   return InitData{
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -168,16 +168,17 @@ rusty-enums = [
     "mozilla::StyleStackSizing",
     "mozilla::StyleBorderImageRepeat",
     "mozilla::StyleBoxPack",
     "mozilla::StyleBoxOrient",
     "mozilla::StyleBoxAlign",
     "mozilla::StyleUserFocus",
     "mozilla::StyleUserSelect",
     "mozilla::StyleImageLayerRepeat",
+    "mozilla::StyleImageLayerAttachment",
     "mozilla::StyleBoxDecorationBreak",
     "mozilla::StyleRuleInclusion",
     "mozilla::StyleGridTrackBreadth",
     "mozilla::StyleOverscrollBehavior",
     "mozilla::StyleWhiteSpace",
     "nsStyleImageType",
     "nsStyleSVGPaintType",
     "nsStyleSVGFallbackType",
@@ -238,16 +239,17 @@ whitelist-types = [
     "mozilla::ServoTraversalFlags",
     "mozilla::StaticPrefs",
     "mozilla::StyleShapeRadius",
     "mozilla::StyleGrid.*",
     "mozilla::UpdateAnimationsTasks",
     "mozilla::LookAndFeel",
     "mozilla::gfx::Float",
     "mozilla::gfx::FontVariation",
+    "mozilla::StyleImageLayerAttachment",
     ".*ThreadSafe.*Holder",
     "AnonymousContent",
     "AudioContext",
     "CapturingContentInfo",
     "DefaultDelete",
     "DOMIntersectionObserverEntry",
     "Element",
     "FontFamilyList",
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -442,19 +442,19 @@ const KTableEntry nsCSSProps::kBackfaceV
 
 const KTableEntry nsCSSProps::kTransformStyleKTable[] = {
   { eCSSKeyword_flat, NS_STYLE_TRANSFORM_STYLE_FLAT },
   { eCSSKeyword_preserve_3d, NS_STYLE_TRANSFORM_STYLE_PRESERVE_3D },
   { eCSSKeyword_UNKNOWN, -1 }
 };
 
 const KTableEntry nsCSSProps::kImageLayerAttachmentKTable[] = {
-  { eCSSKeyword_fixed, NS_STYLE_IMAGELAYER_ATTACHMENT_FIXED },
-  { eCSSKeyword_scroll, NS_STYLE_IMAGELAYER_ATTACHMENT_SCROLL },
-  { eCSSKeyword_local, NS_STYLE_IMAGELAYER_ATTACHMENT_LOCAL },
+  { eCSSKeyword_fixed, StyleImageLayerAttachment::Fixed },
+  { eCSSKeyword_scroll, StyleImageLayerAttachment::Scroll },
+  { eCSSKeyword_local, StyleImageLayerAttachment::Local },
   { eCSSKeyword_UNKNOWN, -1 }
 };
 
 const KTableEntry nsCSSProps::kBackgroundOriginKTable[] = {
   { eCSSKeyword_border_box, StyleGeometryBox::BorderBox },
   { eCSSKeyword_padding_box, StyleGeometryBox::PaddingBox },
   { eCSSKeyword_content_box, StyleGeometryBox::ContentBox },
   { eCSSKeyword_UNKNOWN, -1 }
--- a/layout/style/nsStyleConsts.h
+++ b/layout/style/nsStyleConsts.h
@@ -249,19 +249,21 @@ enum class FillMode : uint8_t;
 // See nsStyleDisplay
 #define NS_STYLE_ANIMATION_ITERATION_COUNT_INFINITE 0
 
 // See nsStyleDisplay
 #define NS_STYLE_ANIMATION_PLAY_STATE_RUNNING     0
 #define NS_STYLE_ANIMATION_PLAY_STATE_PAUSED      1
 
 // See nsStyleImageLayers
-#define NS_STYLE_IMAGELAYER_ATTACHMENT_SCROLL        0
-#define NS_STYLE_IMAGELAYER_ATTACHMENT_FIXED         1
-#define NS_STYLE_IMAGELAYER_ATTACHMENT_LOCAL         2
+enum class StyleImageLayerAttachment : uint8_t {
+    Scroll,
+    Fixed,
+    Local
+};
 
 // A magic value that we use for our "pretend that background-clip is
 // 'padding' when we have a solid border" optimization.  This isn't
 // actually equal to NS_STYLE_IMAGELAYER_CLIP_PADDING because using that
 // causes antialiasing seams between the background and border.  This
 // is a backend-only value.
 #define NS_STYLE_IMAGELAYER_CLIP_MOZ_ALMOST_PADDING  127
 
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -3106,17 +3106,17 @@ nsStyleImageLayers::Size::operator==(con
   return mWidthType == aOther.mWidthType &&
          mHeightType == aOther.mHeightType &&
          (mWidthType != eLengthPercentage || mWidth == aOther.mWidth) &&
          (mHeightType != eLengthPercentage || mHeight == aOther.mHeight);
 }
 
 nsStyleImageLayers::Layer::Layer()
   : mClip(StyleGeometryBox::BorderBox)
-  , mAttachment(NS_STYLE_IMAGELAYER_ATTACHMENT_SCROLL)
+  , mAttachment(StyleImageLayerAttachment::Scroll)
   , mBlendMode(NS_STYLE_BLEND_NORMAL)
   , mComposite(NS_STYLE_MASK_COMPOSITE_ADD)
   , mMaskMode(NS_STYLE_MASK_MODE_MATCH_SOURCE)
 {
   mImage.SetNull();
   mSize.SetInitialValues();
 }
 
@@ -3345,17 +3345,17 @@ nsStyleBackground::CalcDifference(const 
   return hint;
 }
 
 bool
 nsStyleBackground::HasFixedBackground(nsIFrame* aFrame) const
 {
   NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, mImage) {
     const nsStyleImageLayers::Layer &layer = mImage.mLayers[i];
-    if (layer.mAttachment == NS_STYLE_IMAGELAYER_ATTACHMENT_FIXED &&
+    if (layer.mAttachment == StyleImageLayerAttachment::Fixed &&
         !layer.mImage.IsEmpty() &&
         !nsLayoutUtils::IsTransformed(aFrame)) {
       return true;
     }
   }
   return false;
 }
 
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -648,29 +648,31 @@ struct nsStyleImageLayers {
     }
     bool operator!=(const Repeat& aOther) const {
       return !(*this == aOther);
     }
   };
 
   struct Layer {
     typedef mozilla::StyleGeometryBox StyleGeometryBox;
+    typedef mozilla::StyleImageLayerAttachment StyleImageLayerAttachment;
 
     nsStyleImage  mImage;         // [reset]
     mozilla::Position mPosition;  // [reset]
     Size          mSize;          // [reset]
     StyleGeometryBox  mClip;      // [reset] See nsStyleConsts.h
     MOZ_INIT_OUTSIDE_CTOR
       StyleGeometryBox mOrigin;   // [reset] See nsStyleConsts.h
-    uint8_t       mAttachment;    // [reset] See nsStyleConsts.h
+    StyleImageLayerAttachment mAttachment;
+                                  // [reset] See nsStyleConsts.h
                                   // background-only property
                                   // This property is used for background layer
                                   // only. For a mask layer, it should always
                                   // be the initial value, which is
-                                  // NS_STYLE_IMAGELAYER_ATTACHMENT_SCROLL.
+                                  // StyleImageLayerAttachment::Scroll.
     uint8_t       mBlendMode;     // [reset] See nsStyleConsts.h
                                   // background-only property
                                   // This property is used for background layer
                                   // only. For a mask layer, it should always
                                   // be the initial value, which is
                                   // NS_STYLE_BLEND_NORMAL.
     uint8_t       mComposite;     // [reset] See nsStyleConsts.h
                                   // mask-only property
--- a/layout/style/nsStyleStructInlines.h
+++ b/layout/style/nsStyleStructInlines.h
@@ -261,16 +261,16 @@ nsStyleUserInterface::GetEffectivePointe
 }
 
 bool
 nsStyleBackground::HasLocalBackground() const
 {
   NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, mImage) {
     const nsStyleImageLayers::Layer& layer = mImage.mLayers[i];
     if (!layer.mImage.IsEmpty() &&
-        layer.mAttachment == NS_STYLE_IMAGELAYER_ATTACHMENT_LOCAL) {
+        layer.mAttachment == mozilla::StyleImageLayerAttachment::Local) {
       return true;
     }
   }
   return false;
 }
 
 #endif /* !defined(nsStyleStructInlines_h_) */
deleted file mode 100644
--- a/media/libyuv/libyuv/source/convert_argb.cc.orig
+++ /dev/null
@@ -1,2199 +0,0 @@
-/*
- *  Copyright 2011 The LibYuv Project Authors. All rights reserved.
- *
- *  Use of this source code is governed by a BSD-style license
- *  that can be found in the LICENSE file in the root of the source
- *  tree. An additional intellectual property rights grant can be found
- *  in the file PATENTS. All contributing project authors may
- *  be found in the AUTHORS file in the root of the source tree.
- */
-
-#include "libyuv/convert_argb.h"
-
-#include "libyuv/cpu_id.h"
-#ifdef HAVE_JPEG
-#include "libyuv/mjpeg_decoder.h"
-#endif
-#include "libyuv/planar_functions.h"  // For CopyPlane and ARGBShuffle.
-#include "libyuv/rotate_argb.h"
-#include "libyuv/row.h"
-#include "libyuv/video_common.h"
-
-#ifdef __cplusplus
-namespace libyuv {
-extern "C" {
-#endif
-
-// Copy ARGB with optional flipping
-LIBYUV_API
-int ARGBCopy(const uint8_t* src_argb,
-             int src_stride_argb,
-             uint8_t* dst_argb,
-             int dst_stride_argb,
-             int width,
-             int height) {
-  if (!src_argb || !dst_argb || width <= 0 || height == 0) {
-    return -1;
-  }
-  // Negative height means invert the image.
-  if (height < 0) {
-    height = -height;
-    src_argb = src_argb + (height - 1) * src_stride_argb;
-    src_stride_argb = -src_stride_argb;
-  }
-
-  CopyPlane(src_argb, src_stride_argb, dst_argb, dst_stride_argb, width * 4,
-            height);
-  return 0;
-}
-
-// Convert I420 to ARGB with matrix
-static int I420ToARGBMatrix(const uint8_t* src_y,
-                            int src_stride_y,
-                            const uint8_t* src_u,
-                            int src_stride_u,
-                            const uint8_t* src_v,
-                            int src_stride_v,
-                            uint8_t* dst_argb,
-                            int dst_stride_argb,
-                            const struct YuvConstants* yuvconstants,
-                            int width,
-                            int height) {
-  int y;
-  void (*I422ToARGBRow)(const uint8_t* y_buf, const uint8_t* u_buf,
-                        const uint8_t* v_buf, uint8_t* rgb_buf,
-                        const struct YuvConstants* yuvconstants, int width) =
-      I422ToARGBRow_C;
-  if (!src_y || !src_u || !src_v || !dst_argb || width <= 0 || height == 0) {
-    return -1;
-  }
-  // Negative height means invert the image.
-  if (height < 0) {
-    height = -height;
-    dst_argb = dst_argb + (height - 1) * dst_stride_argb;
-    dst_stride_argb = -dst_stride_argb;
-  }
-#if defined(HAS_I422TOARGBROW_SSSE3)
-  if (TestCpuFlag(kCpuHasSSSE3)) {
-    I422ToARGBRow = I422ToARGBRow_Any_SSSE3;
-    if (IS_ALIGNED(width, 8)) {
-      I422ToARGBRow = I422ToARGBRow_SSSE3;
-    }
-  }
-#endif
-#if defined(HAS_I422TOARGBROW_AVX2)
-  if (TestCpuFlag(kCpuHasAVX2)) {
-    I422ToARGBRow = I422ToARGBRow_Any_AVX2;
-    if (IS_ALIGNED(width, 16)) {
-      I422ToARGBRow = I422ToARGBRow_AVX2;
-    }
-  }
-#endif
-#if defined(HAS_I422TOARGBROW_NEON)
-  if (TestCpuFlag(kCpuHasNEON)) {
-    I422ToARGBRow = I422ToARGBRow_Any_NEON;
-    if (IS_ALIGNED(width, 8)) {
-      I422ToARGBRow = I422ToARGBRow_NEON;
-    }
-  }
-#endif
-#if defined(HAS_I422TOARGBROW_MSA)
-  if (TestCpuFlag(kCpuHasMSA)) {
-    I422ToARGBRow = I422ToARGBRow_Any_MSA;
-    if (IS_ALIGNED(width, 8)) {
-      I422ToARGBRow = I422ToARGBRow_MSA;
-    }
-  }
-#endif
-
-  for (y = 0; y < height; ++y) {
-    I422ToARGBRow(src_y, src_u, src_v, dst_argb, yuvconstants, width);
-    dst_argb += dst_stride_argb;
-    src_y += src_stride_y;
-    if (y & 1) {
-      src_u += src_stride_u;
-      src_v += src_stride_v;
-    }
-  }
-  return 0;
-}
-
-// Convert I420 to ARGB.
-LIBYUV_API
-int I420ToARGB(const uint8_t* src_y,
-               int src_stride_y,
-               const uint8_t* src_u,
-               int src_stride_u,
-               const uint8_t* src_v,
-               int src_stride_v,
-               uint8_t* dst_argb,
-               int dst_stride_argb,
-               int width,
-               int height) {
-  return I420ToARGBMatrix(src_y, src_stride_y, src_u, src_stride_u, src_v,
-                          src_stride_v, dst_argb, dst_stride_argb,
-                          &kYuvI601Constants, width, height);
-}
-
-// Convert I420 to ABGR.
-LIBYUV_API
-int I420ToABGR(const uint8_t* src_y,
-               int src_stride_y,
-               const uint8_t* src_u,
-               int src_stride_u,
-               const uint8_t* src_v,
-               int src_stride_v,
-               uint8_t* dst_abgr,
-               int dst_stride_abgr,
-               int width,
-               int height) {
-  return I420ToARGBMatrix(src_y, src_stride_y, src_v,
-                          src_stride_v,  // Swap U and V
-                          src_u, src_stride_u, dst_abgr, dst_stride_abgr,
-                          &kYvuI601Constants,  // Use Yvu matrix
-                          width, height);
-}
-
-// Convert J420 to ARGB.
-LIBYUV_API
-int J420ToARGB(const uint8_t* src_y,
-               int src_stride_y,
-               const uint8_t* src_u,
-               int src_stride_u,
-               const uint8_t* src_v,
-               int src_stride_v,
-               uint8_t* dst_argb,
-               int dst_stride_argb,
-               int width,
-               int height) {
-  return I420ToARGBMatrix(src_y, src_stride_y, src_u, src_stride_u, src_v,
-                          src_stride_v, dst_argb, dst_stride_argb,
-                          &kYuvJPEGConstants, width, height);
-}
-
-// Convert J420 to ABGR.
-LIBYUV_API
-int J420ToABGR(const uint8_t* src_y,
-               int src_stride_y,
-               const uint8_t* src_u,
-               int src_stride_u,
-               const uint8_t* src_v,
-               int src_stride_v,
-               uint8_t* dst_abgr,
-               int dst_stride_abgr,
-               int width,
-               int height) {
-  return I420ToARGBMatrix(src_y, src_stride_y, src_v,
-                          src_stride_v,  // Swap U and V
-                          src_u, src_stride_u, dst_abgr, dst_stride_abgr,
-                          &kYvuJPEGConstants,  // Use Yvu matrix
-                          width, height);
-}
-
-// Convert H420 to ARGB.
-LIBYUV_API
-int H420ToARGB(const uint8_t* src_y,
-               int src_stride_y,
-               const uint8_t* src_u,
-               int src_stride_u,
-               const uint8_t* src_v,
-               int src_stride_v,
-               uint8_t* dst_argb,
-               int dst_stride_argb,
-               int width,
-               int height) {
-  return I420ToARGBMatrix(src_y, src_stride_y, src_u, src_stride_u, src_v,
-                          src_stride_v, dst_argb, dst_stride_argb,
-                          &kYuvH709Constants, width, height);
-}
-
-// Convert H420 to ABGR.
-LIBYUV_API
-int H420ToABGR(const uint8_t* src_y,
-               int src_stride_y,
-               const uint8_t* src_u,
-               int src_stride_u,
-               const uint8_t* src_v,
-               int src_stride_v,
-               uint8_t* dst_abgr,
-               int dst_stride_abgr,
-               int width,
-               int height) {
-  return I420ToARGBMatrix(src_y, src_stride_y, src_v,
-                          src_stride_v,  // Swap U and V
-                          src_u, src_stride_u, dst_abgr, dst_stride_abgr,
-                          &kYvuH709Constants,  // Use Yvu matrix
-                          width, height);
-}
-
-// Convert I422 to ARGB with matrix
-static int I422ToARGBMatrix(const uint8_t* src_y,
-                            int src_stride_y,
-                            const uint8_t* src_u,
-                            int src_stride_u,
-                            const uint8_t* src_v,
-                            int src_stride_v,
-                            uint8_t* dst_argb,
-                            int dst_stride_argb,
-                            const struct YuvConstants* yuvconstants,
-                            int width,
-                            int height) {
-  int y;
-  void (*I422ToARGBRow)(const uint8_t* y_buf, const uint8_t* u_buf,
-                        const uint8_t* v_buf, uint8_t* rgb_buf,
-                        const struct YuvConstants* yuvconstants, int width) =
-      I422ToARGBRow_C;
-  if (!src_y || !src_u || !src_v || !dst_argb || width <= 0 || height == 0) {
-    return -1;
-  }
-  // Negative height means invert the image.
-  if (height < 0) {
-    height = -height;
-    dst_argb = dst_argb + (height - 1) * dst_stride_argb;
-    dst_stride_argb = -dst_stride_argb;
-  }
-  // Coalesce rows.
-  if (src_stride_y == width && src_stride_u * 2 == width &&
-      src_stride_v * 2 == width && dst_stride_argb == width * 4) {
-    width *= height;
-    height = 1;
-    src_stride_y = src_stride_u = src_stride_v = dst_stride_argb = 0;
-  }
-#if defined(HAS_I422TOARGBROW_SSSE3)
-  if (TestCpuFlag(kCpuHasSSSE3)) {
-    I422ToARGBRow = I422ToARGBRow_Any_SSSE3;
-    if (IS_ALIGNED(width, 8)) {
-      I422ToARGBRow = I422ToARGBRow_SSSE3;
-    }
-  }
-#endif
-#if defined(HAS_I422TOARGBROW_AVX2)
-  if (TestCpuFlag(kCpuHasAVX2)) {
-    I422ToARGBRow = I422ToARGBRow_Any_AVX2;
-    if (IS_ALIGNED(width, 16)) {
-      I422ToARGBRow = I422ToARGBRow_AVX2;
-    }
-  }
-#endif
-#if defined(HAS_I422TOARGBROW_NEON)
-  if (TestCpuFlag(kCpuHasNEON)) {
-    I422ToARGBRow = I422ToARGBRow_Any_NEON;
-    if (IS_ALIGNED(width, 8)) {
-      I422ToARGBRow = I422ToARGBRow_NEON;
-    }
-  }
-#endif
-#if defined(HAS_I422TOARGBROW_MSA)
-  if (TestCpuFlag(kCpuHasMSA)) {
-    I422ToARGBRow = I422ToARGBRow_Any_MSA;
-    if (IS_ALIGNED(width, 8)) {
-      I422ToARGBRow = I422ToARGBRow_MSA;
-    }
-  }
-#endif
-
-  for (y = 0; y < height; ++y) {
-    I422ToARGBRow(src_y, src_u, src_v, dst_argb, yuvconstants, width);
-    dst_argb += dst_stride_argb;
-    src_y += src_stride_y;
-    src_u += src_stride_u;
-    src_v += src_stride_v;
-  }
-  return 0;
-}
-
-// Convert I422 to ARGB.
-LIBYUV_API
-int I422ToARGB(const uint8_t* src_y,
-               int src_stride_y,
-               const uint8_t* src_u,
-               int src_stride_u,
-               const uint8_t* src_v,
-               int src_stride_v,
-               uint8_t* dst_argb,
-               int dst_stride_argb,
-               int width,
-               int height) {
-  return I422ToARGBMatrix(src_y, src_stride_y, src_u, src_stride_u, src_v,
-                          src_stride_v, dst_argb, dst_stride_argb,
-                          &kYuvI601Constants, width, height);
-}
-
-// Convert I422 to ABGR.
-LIBYUV_API
-int I422ToABGR(const uint8_t* src_y,
-               int src_stride_y,
-               const uint8_t* src_u,
-               int src_stride_u,
-               const uint8_t* src_v,
-               int src_stride_v,
-               uint8_t* dst_abgr,
-               int dst_stride_abgr,
-               int width,
-               int height) {
-  return I422ToARGBMatrix(src_y, src_stride_y, src_v,
-                          src_stride_v,  // Swap U and V
-                          src_u, src_stride_u, dst_abgr, dst_stride_abgr,
-                          &kYvuI601Constants,  // Use Yvu matrix
-                          width, height);
-}
-
-// Convert J422 to ARGB.
-LIBYUV_API
-int J422ToARGB(const uint8_t* src_y,
-               int src_stride_y,
-               const uint8_t* src_u,
-               int src_stride_u,
-               const uint8_t* src_v,
-               int src_stride_v,
-               uint8_t* dst_argb,
-               int dst_stride_argb,
-               int width,
-               int height) {
-  return I422ToARGBMatrix(src_y, src_stride_y, src_u, src_stride_u, src_v,
-                          src_stride_v, dst_argb, dst_stride_argb,
-                          &kYuvJPEGConstants, width, height);
-}
-
-// Convert J422 to ABGR.
-LIBYUV_API
-int J422ToABGR(const uint8_t* src_y,
-               int src_stride_y,
-               const uint8_t* src_u,
-               int src_stride_u,
-               const uint8_t* src_v,
-               int src_stride_v,
-               uint8_t* dst_abgr,
-               int dst_stride_abgr,
-               int width,
-               int height) {
-  return I422ToARGBMatrix(src_y, src_stride_y, src_v,
-                          src_stride_v,  // Swap U and V
-                          src_u, src_stride_u, dst_abgr, dst_stride_abgr,
-                          &kYvuJPEGConstants,  // Use Yvu matrix
-                          width, height);
-}
-
-// Convert H422 to ARGB.
-LIBYUV_API
-int H422ToARGB(const uint8_t* src_y,
-               int src_stride_y,
-               const uint8_t* src_u,
-               int src_stride_u,
-               const uint8_t* src_v,
-               int src_stride_v,
-               uint8_t* dst_argb,
-               int dst_stride_argb,
-               int width,
-               int height) {
-  return I422ToARGBMatrix(src_y, src_stride_y, src_u, src_stride_u, src_v,
-                          src_stride_v, dst_argb, dst_stride_argb,
-                          &kYuvH709Constants, width, height);
-}
-
-// Convert H422 to ABGR.
-LIBYUV_API
-int H422ToABGR(const uint8_t* src_y,
-               int src_stride_y,
-               const uint8_t* src_u,
-               int src_stride_u,
-               const uint8_t* src_v,
-               int src_stride_v,
-               uint8_t* dst_abgr,
-               int dst_stride_abgr,
-               int width,
-               int height) {
-  return I422ToARGBMatrix(src_y, src_stride_y, src_v,
-                          src_stride_v,  // Swap U and V
-                          src_u, src_stride_u, dst_abgr, dst_stride_abgr,
-                          &kYvuH709Constants,  // Use Yvu matrix
-                          width, height);
-}
-
-// Convert 10 bit YUV to ARGB with matrix
-// TODO(fbarchard): Consider passing scale multiplier to I210ToARGB to
-// multiply 10 bit yuv into high bits to allow any number of bits.
-static int I010ToAR30Matrix(const uint16_t* src_y,
-                            int src_stride_y,
-                            const uint16_t* src_u,
-                            int src_stride_u,
-                            const uint16_t* src_v,
-                            int src_stride_v,
-                            uint8_t* dst_ar30,
-                            int dst_stride_ar30,
-                            const struct YuvConstants* yuvconstants,
-                            int width,
-                            int height) {
-  int y;
-  void (*I210ToAR30Row)(const uint16_t* y_buf, const uint16_t* u_buf,
-                        const uint16_t* v_buf, uint8_t* rgb_buf,
-                        const struct YuvConstants* yuvconstants, int width) =
-      I210ToAR30Row_C;
-  if (!src_y || !src_u || !src_v || !dst_ar30 || width <= 0 || height == 0) {
-    return -1;
-  }
-  // Negative height means invert the image.
-  if (height < 0) {
-    height = -height;
-    dst_ar30 = dst_ar30 + (height - 1) * dst_stride_ar30;
-    dst_stride_ar30 = -dst_stride_ar30;
-  }
-#if defined(HAS_I210TOAR30ROW_SSSE3)
-  if (TestCpuFlag(kCpuHasSSSE3)) {
-    I210ToAR30Row = I210ToAR30Row_Any_SSSE3;
-    if (IS_ALIGNED(width, 8)) {
-      I210ToAR30Row = I210ToAR30Row_SSSE3;
-    }
-  }
-#endif
-#if defined(HAS_I210TOAR30ROW_AVX2)
-  if (TestCpuFlag(kCpuHasAVX2)) {
-    I210ToAR30Row = I210ToAR30Row_Any_AVX2;
-    if (IS_ALIGNED(width, 16)) {
-      I210ToAR30Row = I210ToAR30Row_AVX2;
-    }
-  }
-#endif
-  for (y = 0; y < height; ++y) {
-    I210ToAR30Row(src_y, src_u, src_v, dst_ar30, yuvconstants, width);
-    dst_ar30 += dst_stride_ar30;
-    src_y += src_stride_y;
-    if (y & 1) {
-      src_u += src_stride_u;
-      src_v += src_stride_v;
-    }
-  }
-  return 0;
-}
-
-// Convert I010 to AR30.
-LIBYUV_API
-int I010ToAR30(const uint16_t* src_y,
-               int src_stride_y,
-               const uint16_t* src_u,
-               int src_stride_u,
-               const uint16_t* src_v,
-               int src_stride_v,
-               uint8_t* dst_ar30,
-               int dst_stride_ar30,
-               int width,
-               int height) {
-  return I010ToAR30Matrix(src_y, src_stride_y, src_u, src_stride_u, src_v,
-                          src_stride_v, dst_ar30, dst_stride_ar30,
-                          &kYuvI601Constants, width, height);
-}
-
-// Convert H010 to AR30.
-LIBYUV_API
-int H010ToAR30(const uint16_t* src_y,
-               int src_stride_y,
-               const uint16_t* src_u,
-               int src_stride_u,
-               const uint16_t* src_v,
-               int src_stride_v,
-               uint8_t* dst_ar30,
-               int dst_stride_ar30,
-               int width,
-               int height) {
-  return I010ToAR30Matrix(src_y, src_stride_y, src_u, src_stride_u, src_v,
-                          src_stride_v, dst_ar30, dst_stride_ar30,
-                          &kYuvH709Constants, width, height);
-}
-
-// Convert I010 to AB30.
-LIBYUV_API
-int I010ToAB30(const uint16_t* src_y,
-               int src_stride_y,
-               const uint16_t* src_u,
-               int src_stride_u,
-               const uint16_t* src_v,
-               int src_stride_v,
-               uint8_t* dst_ab30,
-               int dst_stride_ab30,
-               int width,
-               int height) {
-  return I010ToAR30Matrix(src_y, src_stride_y, src_v, src_stride_v, src_u,
-                          src_stride_u, dst_ab30, dst_stride_ab30,
-                          &kYvuI601Constants, width, height);
-}
-
-// Convert H010 to AB30.
-LIBYUV_API
-int H010ToAB30(const uint16_t* src_y,
-               int src_stride_y,
-               const uint16_t* src_u,
-               int src_stride_u,
-               const uint16_t* src_v,
-               int src_stride_v,
-               uint8_t* dst_ab30,
-               int dst_stride_ab30,
-               int width,
-               int height) {
-  return I010ToAR30Matrix(src_y, src_stride_y, src_v, src_stride_v, src_u,
-                          src_stride_u, dst_ab30, dst_stride_ab30,
-                          &kYvuH709Constants, width, height);
-}
-
-// Convert 10 bit YUV to ARGB with matrix
-static int I010ToARGBMatrix(const uint16_t* src_y,
-                            int src_stride_y,
-                            const uint16_t* src_u,
-                            int src_stride_u,
-                            const uint16_t* src_v,
-                            int src_stride_v,
-                            uint8_t* dst_argb,
-                            int dst_stride_argb,
-                            const struct YuvConstants* yuvconstants,
-                            int width,
-                            int height) {
-  int y;
-  void (*I210ToARGBRow)(const uint16_t* y_buf, const uint16_t* u_buf,
-                        const uint16_t* v_buf, uint8_t* rgb_buf,
-                        const struct YuvConstants* yuvconstants, int width) =
-      I210ToARGBRow_C;
-  if (!src_y || !src_u || !src_v || !dst_argb || width <= 0 || height == 0) {
-    return -1;
-  }
-  // Negative height means invert the image.
-  if (height < 0) {
-    height = -height;
-    dst_argb = dst_argb + (height - 1) * dst_stride_argb;
-    dst_stride_argb = -dst_stride_argb;
-  }
-#if defined(HAS_I210TOARGBROW_SSSE3)
-  if (TestCpuFlag(kCpuHasSSSE3)) {
-    I210ToARGBRow = I210ToARGBRow_Any_SSSE3;
-    if (IS_ALIGNED(width, 8)) {
-      I210ToARGBRow = I210ToARGBRow_SSSE3;
-    }
-  }
-#endif
-#if defined(HAS_I210TOARGBROW_AVX2)
-  if (TestCpuFlag(kCpuHasAVX2)) {
-    I210ToARGBRow = I210ToARGBRow_Any_AVX2;
-    if (IS_ALIGNED(width, 16)) {
-      I210ToARGBRow = I210ToARGBRow_AVX2;
-    }
-  }
-#endif
-  for (y = 0; y < height; ++y) {
-    I210ToARGBRow(src_y, src_u, src_v, dst_argb, yuvconstants, width);
-    dst_argb += dst_stride_argb;
-    src_y += src_stride_y;
-    if (y & 1) {
-      src_u += src_stride_u;
-      src_v += src_stride_v;
-    }
-  }
-  return 0;
-}
-
-// Convert I010 to ARGB.
-LIBYUV_API
-int I010ToARGB(const uint16_t* src_y,
-               int src_stride_y,
-               const uint16_t* src_u,
-               int src_stride_u,
-               const uint16_t* src_v,
-               int src_stride_v,
-               uint8_t* dst_argb,
-               int dst_stride_argb,
-               int width,
-               int height) {
-  return I010ToARGBMatrix(src_y, src_stride_y, src_u, src_stride_u, src_v,
-                          src_stride_v, dst_argb, dst_stride_argb,
-                          &kYuvI601Constants, width, height);
-}
-
-// Convert I010 to ABGR.
-LIBYUV_API
-int I010ToABGR(const uint16_t* src_y,
-               int src_stride_y,
-               const uint16_t* src_u,
-               int src_stride_u,
-               const uint16_t* src_v,
-               int src_stride_v,
-               uint8_t* dst_abgr,
-               int dst_stride_abgr,
-               int width,
-               int height) {
-  return I010ToARGBMatrix(src_y, src_stride_y, src_v,
-                          src_stride_v,  // Swap U and V
-                          src_u, src_stride_u, dst_abgr, dst_stride_abgr,
-                          &kYvuI601Constants,  // Use Yvu matrix
-                          width, height);
-}
-
-// Convert H010 to ARGB.
-LIBYUV_API
-int H010ToARGB(const uint16_t* src_y,
-               int src_stride_y,
-               const uint16_t* src_u,
-               int src_stride_u,
-               const uint16_t* src_v,
-               int src_stride_v,
-               uint8_t* dst_argb,
-               int dst_stride_argb,
-               int width,
-               int height) {
-  return I010ToARGBMatrix(src_y, src_stride_y, src_u, src_stride_u, src_v,
-                          src_stride_v, dst_argb, dst_stride_argb,
-                          &kYuvH709Constants, width, height);
-}
-
-// Convert H010 to ABGR.
-LIBYUV_API
-int H010ToABGR(const uint16_t* src_y,
-               int src_stride_y,
-               const uint16_t* src_u,
-               int src_stride_u,
-               const uint16_t* src_v,
-               int src_stride_v,
-               uint8_t* dst_abgr,
-               int dst_stride_abgr,
-               int width,
-               int height) {
-  return I010ToARGBMatrix(src_y, src_stride_y, src_v,
-                          src_stride_v,  // Swap U and V
-                          src_u, src_stride_u, dst_abgr, dst_stride_abgr,
-                          &kYvuH709Constants,  // Use Yvu matrix
-                          width, height);
-}
-
-// Convert I444 to ARGB with matrix
-static int I444ToARGBMatrix(const uint8_t* src_y,
-                            int src_stride_y,
-                            const uint8_t* src_u,
-                            int src_stride_u,
-                            const uint8_t* src_v,
-                            int src_stride_v,
-                            uint8_t* dst_argb,
-                            int dst_stride_argb,
-                            const struct YuvConstants* yuvconstants,
-                            int width,
-                            int height) {
-  int y;
-  void (*I444ToARGBRow)(const uint8_t* y_buf, const uint8_t* u_buf,
-                        const uint8_t* v_buf, uint8_t* rgb_buf,
-                        const struct YuvConstants* yuvconstants, int width) =
-      I444ToARGBRow_C;
-  if (!src_y || !src_u || !src_v || !dst_argb || width <= 0 || height == 0) {
-    return -1;
-  }
-  // Negative height means invert the image.
-  if (height < 0) {
-    height = -height;
-    dst_argb = dst_argb + (height - 1) * dst_stride_argb;
-    dst_stride_argb = -dst_stride_argb;
-  }
-  // Coalesce rows.
-  if (src_stride_y == width && src_stride_u == width && src_stride_v == width &&
-      dst_stride_argb == width * 4) {
-    width *= height;
-    height = 1;
-    src_stride_y = src_stride_u = src_stride_v = dst_stride_argb = 0;
-  }
-#if defined(HAS_I444TOARGBROW_SSSE3)
-  if (TestCpuFlag(kCpuHasSSSE3)) {
-    I444ToARGBRow = I444ToARGBRow_Any_SSSE3;
-    if (IS_ALIGNED(width, 8)) {
-      I444ToARGBRow = I444ToARGBRow_SSSE3;
-    }
-  }
-#endif
-#if defined(HAS_I444TOARGBROW_AVX2)
-  if (TestCpuFlag(kCpuHasAVX2)) {
-    I444ToARGBRow = I444ToARGBRow_Any_AVX2;
-    if (IS_ALIGNED(width, 16)) {
-      I444ToARGBRow = I444ToARGBRow_AVX2;
-    }
-  }
-#endif
-#if defined(HAS_I444TOARGBROW_NEON)
-  if (TestCpuFlag(kCpuHasNEON)) {
-    I444ToARGBRow = I444ToARGBRow_Any_NEON;
-    if (IS_ALIGNED(width, 8)) {
-      I444ToARGBRow = I444ToARGBRow_NEON;
-    }
-  }
-#endif
-#if defined(HAS_I444TOARGBROW_MSA)
-  if (TestCpuFlag(kCpuHasMSA)) {
-    I444ToARGBRow = I444ToARGBRow_Any_MSA;
-    if (IS_ALIGNED(width, 8)) {
-      I444ToARGBRow = I444ToARGBRow_MSA;
-    }
-  }
-#endif
-
-  for (y = 0; y < height; ++y) {
-    I444ToARGBRow(src_y, src_u, src_v, dst_argb, yuvconstants, width);
-    dst_argb += dst_stride_argb;
-    src_y += src_stride_y;
-    src_u += src_stride_u;
-    src_v += src_stride_v;
-  }
-  return 0;
-}
-
-// Convert I444 to ARGB.
-LIBYUV_API
-int I444ToARGB(const uint8_t* src_y,
-               int src_stride_y,
-               const uint8_t* src_u,
-               int src_stride_u,
-               const uint8_t* src_v,
-               int src_stride_v,
-               uint8_t* dst_argb,
-               int dst_stride_argb,
-               int width,
-               int height) {
-  return I444ToARGBMatrix(src_y, src_stride_y, src_u, src_stride_u, src_v,
-                          src_stride_v, dst_argb, dst_stride_argb,
-                          &kYuvI601Constants, width, height);
-}
-
-// Convert I444 to ABGR.
-LIBYUV_API
-int I444ToABGR(const uint8_t* src_y,
-               int src_stride_y,
-               const uint8_t* src_u,
-               int src_stride_u,
-               const uint8_t* src_v,
-               int src_stride_v,
-               uint8_t* dst_abgr,
-               int dst_stride_abgr,
-               int width,
-               int height) {
-  return I444ToARGBMatrix(src_y, src_stride_y, src_v,
-                          src_stride_v,  // Swap U and V
-                          src_u, src_stride_u, dst_abgr, dst_stride_abgr,
-                          &kYvuI601Constants,  // Use Yvu matrix
-                          width, height);
-}
-
-// Convert J444 to ARGB.
-LIBYUV_API
-int J444ToARGB(const uint8_t* src_y,
-               int src_stride_y,
-               const uint8_t* src_u,
-               int src_stride_u,
-               const uint8_t* src_v,
-               int src_stride_v,
-               uint8_t* dst_argb,
-               int dst_stride_argb,
-               int width,
-               int height) {
-  return I444ToARGBMatrix(src_y, src_stride_y, src_u, src_stride_u, src_v,
-                          src_stride_v, dst_argb, dst_stride_argb,
-                          &kYuvJPEGConstants, width, height);
-}
-
-// Convert I420 with Alpha to preattenuated ARGB.
-static int I420AlphaToARGBMatrix(const uint8_t* src_y,
-                                 int src_stride_y,
-                                 const uint8_t* src_u,
-                                 int src_stride_u,
-                                 const uint8_t* src_v,
-                                 int src_stride_v,
-                                 const uint8_t* src_a,
-                                 int src_stride_a,
-                                 uint8_t* dst_argb,
-                                 int dst_stride_argb,
-                                 const struct YuvConstants* yuvconstants,
-                                 int width,
-                                 int height,
-                                 int attenuate) {
-  int y;
-  void (*I422AlphaToARGBRow)(const uint8_t* y_buf, const uint8_t* u_buf,
-                             const uint8_t* v_buf, const uint8_t* a_buf,
-                             uint8_t* dst_argb,
-                             const struct YuvConstants* yuvconstants,
-                             int width) = I422AlphaToARGBRow_C;
-  void (*ARGBAttenuateRow)(const uint8_t* src_argb, uint8_t* dst_argb,
-                           int width) = ARGBAttenuateRow_C;
-  if (!src_y || !src_u || !src_v || !dst_argb || width <= 0 || height == 0) {
-    return -1;
-  }
-  // Negative height means invert the image.
-  if (height < 0) {
-    height = -height;
-    dst_argb = dst_argb + (height - 1) * dst_stride_argb;
-    dst_stride_argb = -dst_stride_argb;
-  }
-#if defined(HAS_I422ALPHATOARGBROW_SSSE3)
-  if (TestCpuFlag(kCpuHasSSSE3)) {
-    I422AlphaToARGBRow = I422AlphaToARGBRow_Any_SSSE3;
-    if (IS_ALIGNED(width, 8)) {
-      I422AlphaToARGBRow = I422AlphaToARGBRow_SSSE3;
-    }
-  }
-#endif
-#if defined(HAS_I422ALPHATOARGBROW_AVX2)
-  if (TestCpuFlag(kCpuHasAVX2)) {
-    I422AlphaToARGBRow = I422AlphaToARGBRow_Any_AVX2;
-    if (IS_ALIGNED(width, 16)) {
-      I422AlphaToARGBRow = I422AlphaToARGBRow_AVX2;
-    }
-  }
-#endif
-#if defined(HAS_I422ALPHATOARGBROW_NEON)
-  if (TestCpuFlag(kCpuHasNEON)) {
-    I422AlphaToARGBRow = I422AlphaToARGBRow_Any_NEON;
-    if (IS_ALIGNED(width, 8)) {
-      I422AlphaToARGBRow = I422AlphaToARGBRow_NEON;
-    }
-  }
-#endif
-#if defined(HAS_I422ALPHATOARGBROW_MSA)
-  if (TestCpuFlag(kCpuHasMSA)) {
-    I422AlphaToARGBRow = I422AlphaToARGBRow_Any_MSA;
-    if (IS_ALIGNED(width, 8)) {
-      I422AlphaToARGBRow = I422AlphaToARGBRow_MSA;
-    }
-  }
-#endif
-#if defined(HAS_ARGBATTENUATEROW_SSSE3)
-  if (TestCpuFlag(kCpuHasSSSE3)) {
-    ARGBAttenuateRow = ARGBAttenuateRow_Any_SSSE3;
-    if (IS_ALIGNED(width, 4)) {
-      ARGBAttenuateRow = ARGBAttenuateRow_SSSE3;
-    }
-  }
-#endif
-#if defined(HAS_ARGBATTENUATEROW_AVX2)
-  if (TestCpuFlag(kCpuHasAVX2)) {
-    ARGBAttenuateRow = ARGBAttenuateRow_Any_AVX2;
-    if (IS_ALIGNED(width, 8)) {
-      ARGBAttenuateRow = ARGBAttenuateRow_AVX2;
-    }
-  }
-#endif
-#if defined(HAS_ARGBATTENUATEROW_NEON)
-  if (TestCpuFlag(kCpuHasNEON)) {
-    ARGBAttenuateRow = ARGBAttenuateRow_Any_NEON;
-    if (IS_ALIGNED(width, 8)) {
-      ARGBAttenuateRow = ARGBAttenuateRow_NEON;
-    }
-  }
-#endif
-#if defined(HAS_ARGBATTENUATEROW_MSA)
-  if (TestCpuFlag(kCpuHasMSA)) {
-    ARGBAttenuateRow = ARGBAttenuateRow_Any_MSA;
-    if (IS_ALIGNED(width, 8)) {
-      ARGBAttenuateRow = ARGBAttenuateRow_MSA;
-    }
-  }
-#endif
-
-  for (y = 0; y < height; ++y) {
-    I422AlphaToARGBRow(src_y, src_u, src_v, src_a, dst_argb, yuvconstants,
-                       width);
-    if (attenuate) {
-      ARGBAttenuateRow(dst_argb, dst_argb, width);
-    }
-    dst_argb += dst_stride_argb;
-    src_a += src_stride_a;
-    src_y += src_stride_y;
-    if (y & 1) {
-      src_u += src_stride_u;
-      src_v += src_stride_v;
-    }
-  }
-  return 0;
-}
-
-// Convert I420 with Alpha to ARGB.
-LIBYUV_API
-int I420AlphaToARGB(const uint8_t* src_y,
-                    int src_stride_y,
-                    const uint8_t* src_u,
-                    int src_stride_u,
-                    const uint8_t* src_v,
-                    int src_stride_v,
-                    const uint8_t* src_a,
-                    int src_stride_a,
-                    uint8_t* dst_argb,
-                    int dst_stride_argb,
-                    int width,
-                    int height,
-                    int attenuate) {
-  return I420AlphaToARGBMatrix(src_y, src_stride_y, src_u, src_stride_u, src_v,
-                               src_stride_v, src_a, src_stride_a, dst_argb,
-                               dst_stride_argb, &kYuvI601Constants, width,
-                               height, attenuate);
-}
-
-// Convert I420 with Alpha to ABGR.
-LIBYUV_API
-int I420AlphaToABGR(const uint8_t* src_y,
-                    int src_stride_y,
-                    const uint8_t* src_u,
-                    int src_stride_u,
-                    const uint8_t* src_v,
-                    int src_stride_v,
-                    const uint8_t* src_a,
-                    int src_stride_a,
-                    uint8_t* dst_abgr,
-                    int dst_stride_abgr,
-                    int width,
-                    int height,
-                    int attenuate) {
-  return I420AlphaToARGBMatrix(
-      src_y, src_stride_y, src_v, src_stride_v,  // Swap U and V
-      src_u, src_stride_u, src_a, src_stride_a, dst_abgr, dst_stride_abgr,
-      &kYvuI601Constants,  // Use Yvu matrix
-      width, height, attenuate);
-}
-
-// Convert I400 to ARGB.
-LIBYUV_API
-int I400ToARGB(const uint8_t* src_y,
-               int src_stride_y,
-               uint8_t* dst_argb,
-               int dst_stride_argb,
-               int width,
-               int height) {
-  int y;
-  void (*I400ToARGBRow)(const uint8_t* y_buf, uint8_t* rgb_buf, int width) =
-      I400ToARGBRow_C;
-  if (!src_y || !dst_argb || width <= 0 || height == 0) {
-    return -1;
-  }
-  // Negative height means invert the image.
-  if (height < 0) {
-    height = -height;
-    dst_argb = dst_argb + (height - 1) * dst_stride_argb;
-    dst_stride_argb = -dst_stride_argb;
-  }
-  // Coalesce rows.
-  if (src_stride_y == width && dst_stride_argb == width * 4) {
-    width *= height;
-    height = 1;
-    src_stride_y = dst_stride_argb = 0;
-  }
-#if defined(HAS_I400TOARGBROW_SSE2)
-  if (TestCpuFlag(kCpuHasSSE2)) {
-    I400ToARGBRow = I400ToARGBRow_Any_SSE2;
-    if (IS_ALIGNED(width, 8)) {
-      I400ToARGBRow = I400ToARGBRow_SSE2;
-    }
-  }
-#endif
-#if defined(HAS_I400TOARGBROW_AVX2)
-  if (TestCpuFlag(kCpuHasAVX2)) {
-    I400ToARGBRow = I400ToARGBRow_Any_AVX2;
-    if (IS_ALIGNED(width, 16)) {
-      I400ToARGBRow = I400ToARGBRow_AVX2;
-    }
-  }
-#endif
-#if defined(HAS_I400TOARGBROW_NEON)
-  if (TestCpuFlag(kCpuHasNEON)) {
-    I400ToARGBRow = I400ToARGBRow_Any_NEON;
-    if (IS_ALIGNED(width, 8)) {
-      I400ToARGBRow = I400ToARGBRow_NEON;
-    }
-  }
-#endif
-#if defined(HAS_I400TOARGBROW_MSA)
-  if (TestCpuFlag(kCpuHasMSA)) {
-    I400ToARGBRow = I400ToARGBRow_Any_MSA;
-    if (IS_ALIGNED(width, 16)) {
-      I400ToARGBRow = I400ToARGBRow_MSA;
-    }
-  }
-#endif
-
-  for (y = 0; y < height; ++y) {
-    I400ToARGBRow(src_y, dst_argb, width);
-    dst_argb += dst_stride_argb;
-    src_y += src_stride_y;
-  }
-  return 0;
-}
-
-// Convert J400 to ARGB.
-LIBYUV_API
-int J400ToARGB(const uint8_t* src_y,
-               int src_stride_y,
-               uint8_t* dst_argb,
-               int dst_stride_argb,
-               int width,
-               int height) {
-  int y;
-  void (*J400ToARGBRow)(const uint8_t* src_y, uint8_t* dst_argb, int width) =
-      J400ToARGBRow_C;
-  if (!src_y || !dst_argb || width <= 0 || height == 0) {
-    return -1;
-  }
-  // Negative height means invert the image.
-  if (height < 0) {
-    height = -height;
-    src_y = src_y + (height - 1) * src_stride_y;
-    src_stride_y = -src_stride_y;
-  }
-  // Coalesce rows.
-  if (src_stride_y == width && dst_stride_argb == width * 4) {
-    width *= height;
-    height = 1;
-    src_stride_y = dst_stride_argb = 0;
-  }
-#if defined(HAS_J400TOARGBROW_SSE2)
-  if (TestCpuFlag(kCpuHasSSE2)) {
-    J400ToARGBRow = J400ToARGBRow_Any_SSE2;
-    if (IS_ALIGNED(width, 8)) {
-      J400ToARGBRow = J400ToARGBRow_SSE2;
-    }
-  }
-#endif
-#if defined(HAS_J400TOARGBROW_AVX2)
-  if (TestCpuFlag(kCpuHasAVX2)) {
-    J400ToARGBRow = J400ToARGBRow_Any_AVX2;
-    if (IS_ALIGNED(width, 16)) {
-      J400ToARGBRow = J400ToARGBRow_AVX2;
-    }
-  }
-#endif
-#if defined(HAS_J400TOARGBROW_NEON)
-  if (TestCpuFlag(kCpuHasNEON)) {
-    J400ToARGBRow = J400ToARGBRow_Any_NEON;
-    if (IS_ALIGNED(width, 8)) {
-      J400ToARGBRow = J400ToARGBRow_NEON;
-    }
-  }
-#endif
-#if defined(HAS_J400TOARGBROW_MSA)
-  if (TestCpuFlag(kCpuHasMSA)) {
-    J400ToARGBRow = J400ToARGBRow_Any_MSA;
-    if (IS_ALIGNED(width, 16)) {
-      J400ToARGBRow = J400ToARGBRow_MSA;
-    }
-  }
-#endif
-  for (y = 0; y < height; ++y) {
-    J400ToARGBRow(src_y, dst_argb, width);
-    src_y += src_stride_y;
-    dst_argb += dst_stride_argb;
-  }
-  return 0;
-}
-
-// Shuffle table for converting BGRA to ARGB.
-static const uvec8 kShuffleMaskBGRAToARGB = {
-    3u, 2u, 1u, 0u, 7u, 6u, 5u, 4u, 11u, 10u, 9u, 8u, 15u, 14u, 13u, 12u};
-
-// Shuffle table for converting ABGR to ARGB.
-static const uvec8 kShuffleMaskABGRToARGB = {
-    2u, 1u, 0u, 3u, 6u, 5u, 4u, 7u, 10u, 9u, 8u, 11u, 14u, 13u, 12u, 15u};
-
-// Shuffle table for converting RGBA to ARGB.
-static const uvec8 kShuffleMaskRGBAToARGB = {
-    1u, 2u, 3u, 0u, 5u, 6u, 7u, 4u, 9u, 10u, 11u, 8u, 13u, 14u, 15u, 12u};
-
-// Convert BGRA to ARGB.
-LIBYUV_API
-int BGRAToARGB(const uint8_t* src_bgra,
-               int src_stride_bgra,
-               uint8_t* dst_argb,
-               int dst_stride_argb,
-               int width,
-               int height) {
-  return ARGBShuffle(src_bgra, src_stride_bgra, dst_argb, dst_stride_argb,
-                     (const uint8_t*)(&kShuffleMaskBGRAToARGB), width, height);
-}
-
-// Convert ARGB to BGRA (same as BGRAToARGB).
-LIBYUV_API
-int ARGBToBGRA(const uint8_t* src_bgra,
-               int src_stride_bgra,
-               uint8_t* dst_argb,
-               int dst_stride_argb,
-               int width,
-               int height) {
-  return ARGBShuffle(src_bgra, src_stride_bgra, dst_argb, dst_stride_argb,
-                     (const uint8_t*)(&kShuffleMaskBGRAToARGB), width, height);
-}
-
-// Convert ABGR to ARGB.
-LIBYUV_API
-int ABGRToARGB(const uint8_t* src_abgr,
-               int src_stride_abgr,
-               uint8_t* dst_argb,
-               int dst_stride_argb,
-               int width,
-               int height) {
-  return ARGBShuffle(src_abgr, src_stride_abgr, dst_argb, dst_stride_argb,
-                     (const uint8_t*)(&kShuffleMaskABGRToARGB), width, height);
-}
-
-// Convert ARGB to ABGR to (same as ABGRToARGB).
-LIBYUV_API
-int ARGBToABGR(const uint8_t* src_abgr,
-               int src_stride_abgr,
-               uint8_t* dst_argb,
-               int dst_stride_argb,
-               int width,
-               int height) {
-  return ARGBShuffle(src_abgr, src_stride_abgr, dst_argb, dst_stride_argb,
-                     (const uint8_t*)(&kShuffleMaskABGRToARGB), width, height);
-}
-
-// Convert RGBA to ARGB.
-LIBYUV_API
-int RGBAToARGB(const uint8_t* src_rgba,
-               int src_stride_rgba,
-               uint8_t* dst_argb,
-               int dst_stride_argb,
-               int width,
-               int height) {
-  return ARGBShuffle(src_rgba, src_stride_rgba, dst_argb, dst_stride_argb,
-                     (const uint8_t*)(&kShuffleMaskRGBAToARGB), width, height);
-}
-
-// Convert RGB24 to ARGB.
-LIBYUV_API
-int RGB24ToARGB(const uint8_t* src_rgb24,
-                int src_stride_rgb24,
-                uint8_t* dst_argb,
-                int dst_stride_argb,
-                int width,
-                int height) {
-  int y;
-  void (*RGB24ToARGBRow)(const uint8_t* src_rgb, uint8_t* dst_argb, int width) =
-      RGB24ToARGBRow_C;
-  if (!src_rgb24 || !dst_argb || width <= 0 || height == 0) {
-    return -1;
-  }
-  // Negative height means invert the image.
-  if (height < 0) {
-    height = -height;
-    src_rgb24 = src_rgb24 + (height - 1) * src_stride_rgb24;
-    src_stride_rgb24 = -src_stride_rgb24;
-  }
-  // Coalesce rows.
-  if (src_stride_rgb24 == width * 3 && dst_stride_argb == width * 4) {
-    width *= height;
-    height = 1;
-    src_stride_rgb24 = dst_stride_argb = 0;
-  }
-#if defined(HAS_RGB24TOARGBROW_SSSE3)
-  if (TestCpuFlag(kCpuHasSSSE3)) {
-    RGB24ToARGBRow = RGB24ToARGBRow_Any_SSSE3;
-    if (IS_ALIGNED(width, 16)) {
-      RGB24ToARGBRow = RGB24ToARGBRow_SSSE3;
-    }
-  }
-#endif
-#if defined(HAS_RGB24TOARGBROW_NEON)
-  if (TestCpuFlag(kCpuHasNEON)) {
-    RGB24ToARGBRow = RGB24ToARGBRow_Any_NEON;
-    if (IS_ALIGNED(width, 8)) {
-      RGB24ToARGBRow = RGB24ToARGBRow_NEON;
-    }
-  }
-#endif
-#if defined(HAS_RGB24TOARGBROW_MSA)
-  if (TestCpuFlag(kCpuHasMSA)) {
-    RGB24ToARGBRow = RGB24ToARGBRow_Any_MSA;
-    if (IS_ALIGNED(width, 16)) {
-      RGB24ToARGBRow = RGB24ToARGBRow_MSA;
-    }
-  }
-#endif
-
-  for (y = 0; y < height; ++y) {
-    RGB24ToARGBRow(src_rgb24, dst_argb, width);
-    src_rgb24 += src_stride_rgb24;
-    dst_argb += dst_stride_argb;
-  }
-  return 0;
-}
-
-// Convert RAW to ARGB.
-LIBYUV_API
-int RAWToARGB(const uint8_t* src_raw,
-              int src_stride_raw,
-              uint8_t* dst_argb,
-              int dst_stride_argb,
-              int width,
-              int height) {
-  int y;
-  void (*RAWToARGBRow)(const uint8_t* src_rgb, uint8_t* dst_argb, int width) =
-      RAWToARGBRow_C;
-  if (!src_raw || !dst_argb || width <= 0 || height == 0) {
-    return -1;
-  }
-  // Negative height means invert the image.
-  if (height < 0) {
-    height = -height;
-    src_raw = src_raw + (height - 1) * src_stride_raw;
-    src_stride_raw = -src_stride_raw;
-  }
-  // Coalesce rows.
-  if (src_stride_raw == width * 3 && dst_stride_argb == width * 4) {
-    width *= height;
-    height = 1;
-    src_stride_raw = dst_stride_argb = 0;
-  }
-#if defined(HAS_RAWTOARGBROW_SSSE3)
-  if (TestCpuFlag(kCpuHasSSSE3)) {
-    RAWToARGBRow = RAWToARGBRow_Any_SSSE3;
-    if (IS_ALIGNED(width, 16)) {
-      RAWToARGBRow = RAWToARGBRow_SSSE3;
-    }
-  }
-#endif
-#if defined(HAS_RAWTOARGBROW_NEON)
-  if (TestCpuFlag(kCpuHasNEON)) {
-    RAWToARGBRow = RAWToARGBRow_Any_NEON;
-    if (IS_ALIGNED(width, 8)) {
-      RAWToARGBRow = RAWToARGBRow_NEON;
-    }
-  }
-#endif
-#if defined(HAS_RAWTOARGBROW_MSA)
-  if (TestCpuFlag(kCpuHasMSA)) {
-    RAWToARGBRow = RAWToARGBRow_Any_MSA;
-    if (IS_ALIGNED(width, 16)) {
-      RAWToARGBRow = RAWToARGBRow_MSA;
-    }
-  }
-#endif
-
-  for (y = 0; y < height; ++y) {
-    RAWToARGBRow(src_raw, dst_argb, width);
-    src_raw += src_stride_raw;
-    dst_argb += dst_stride_argb;
-  }
-  return 0;
-}
-
-// Convert RGB565 to ARGB.
-LIBYUV_API
-int RGB565ToARGB(const uint8_t* src_rgb565,
-                 int src_stride_rgb565,
-                 uint8_t* dst_argb,
-                 int dst_stride_argb,
-                 int width,
-                 int height) {
-  int y;
-  void (*RGB565ToARGBRow)(const uint8_t* src_rgb565, uint8_t* dst_argb,
-                          int width) = RGB565ToARGBRow_C;
-  if (!src_rgb565 || !dst_argb || width <= 0 || height == 0) {
-    return -1;
-  }
-  // Negative height means invert the image.
-  if (height < 0) {
-    height = -height;
-    src_rgb565 = src_rgb565 + (height - 1) * src_stride_rgb565;
-    src_stride_rgb565 = -src_stride_rgb565;
-  }
-  // Coalesce rows.
-  if (src_stride_rgb565 == width * 2 && dst_stride_argb == width * 4) {
-    width *= height;
-    height = 1;
-    src_stride_rgb565 = dst_stride_argb = 0;
-  }
-#if defined(HAS_RGB565TOARGBROW_SSE2)
-  if (TestCpuFlag(kCpuHasSSE2)) {
-    RGB565ToARGBRow = RGB565ToARGBRow_Any_SSE2;
-    if (IS_ALIGNED(width, 8)) {
-      RGB565ToARGBRow = RGB565ToARGBRow_SSE2;
-    }
-  }
-#endif
-#if defined(HAS_RGB565TOARGBROW_AVX2)
-  if (TestCpuFlag(kCpuHasAVX2)) {
-    RGB565ToARGBRow = RGB565ToARGBRow_Any_AVX2;
-    if (IS_ALIGNED(width, 16)) {
-      RGB565ToARGBRow = RGB565ToARGBRow_AVX2;
-    }
-  }
-#endif
-#if defined(HAS_RGB565TOARGBROW_NEON)
-  if (TestCpuFlag(kCpuHasNEON)) {
-    RGB565ToARGBRow = RGB565ToARGBRow_Any_NEON;
-    if (IS_ALIGNED(width, 8)) {
-      RGB565ToARGBRow = RGB565ToARGBRow_NEON;
-    }
-  }
-#endif
-#if defined(HAS_RGB565TOARGBROW_MSA)
-  if (TestCpuFlag(kCpuHasMSA)) {
-    RGB565ToARGBRow = RGB565ToARGBRow_Any_MSA;
-    if (IS_ALIGNED(width, 16)) {
-      RGB565ToARGBRow = RGB565ToARGBRow_MSA;
-    }
-  }
-#endif
-
-  for (y = 0; y < height; ++y) {
-    RGB565ToARGBRow(src_rgb565, dst_argb, width);
-    src_rgb565 += src_stride_rgb565;
-    dst_argb += dst_stride_argb;
-  }
-  return 0;
-}
-
-// Convert ARGB1555 to ARGB.
-LIBYUV_API
-int ARGB1555ToARGB(const uint8_t* src_argb1555,
-                   int src_stride_argb1555,
-                   uint8_t* dst_argb,
-                   int dst_stride_argb,
-                   int width,
-                   int height) {
-  int y;
-  void (*ARGB1555ToARGBRow)(const uint8_t* src_argb1555, uint8_t* dst_argb,
-                            int width) = ARGB1555ToARGBRow_C;
-  if (!src_argb1555 || !dst_argb || width <= 0 || height == 0) {
-    return -1;
-  }
-  // Negative height means invert the image.
-  if (height < 0) {
-    height = -height;
-    src_argb1555 = src_argb1555 + (height - 1) * src_stride_argb1555;
-    src_stride_argb1555 = -src_stride_argb1555;
-  }
-  // Coalesce rows.
-  if (src_stride_argb1555 == width * 2 && dst_stride_argb == width * 4) {
-    width *= height;
-    height = 1;
-    src_stride_argb1555 = dst_stride_argb = 0;
-  }
-#if defined(HAS_ARGB1555TOARGBROW_SSE2)
-  if (TestCpuFlag(kCpuHasSSE2)) {
-    ARGB1555ToARGBRow = ARGB1555ToARGBRow_Any_SSE2;
-    if (IS_ALIGNED(width, 8)) {
-      ARGB1555ToARGBRow = ARGB1555ToARGBRow_SSE2;
-    }
-  }
-#endif
-#if defined(HAS_ARGB1555TOARGBROW_AVX2)
-  if (TestCpuFlag(kCpuHasAVX2)) {
-    ARGB1555ToARGBRow = ARGB1555ToARGBRow_Any_AVX2;
-    if (IS_ALIGNED(width, 16)) {
-      ARGB1555ToARGBRow = ARGB1555ToARGBRow_AVX2;
-    }
-  }
-#endif
-#if defined(HAS_ARGB1555TOARGBROW_NEON)
-  if (TestCpuFlag(kCpuHasNEON)) {
-    ARGB1555ToARGBRow = ARGB1555ToARGBRow_Any_NEON;
-    if (IS_ALIGNED(width, 8)) {
-      ARGB1555ToARGBRow = ARGB1555ToARGBRow_NEON;
-    }
-  }
-#endif
-#if defined(HAS_ARGB1555TOARGBROW_MSA)
-  if (TestCpuFlag(kCpuHasMSA)) {
-    ARGB1555ToARGBRow = ARGB1555ToARGBRow_Any_MSA;
-    if (IS_ALIGNED(width, 16)) {
-      ARGB1555ToARGBRow = ARGB1555ToARGBRow_MSA;
-    }
-  }
-#endif
-
-  for (y = 0; y < height; ++y) {
-    ARGB1555ToARGBRow(src_argb1555, dst_argb, width);
-    src_argb1555 += src_stride_argb1555;
-    dst_argb += dst_stride_argb;
-  }
-  return 0;
-}
-
-// Convert ARGB4444 to ARGB.
-LIBYUV_API
-int ARGB4444ToARGB(const uint8_t* src_argb4444,
-                   int src_stride_argb4444,
-                   uint8_t* dst_argb,
-                   int dst_stride_argb,
-                   int width,
-                   int height) {
-  int y;
-  void (*ARGB4444ToARGBRow)(const uint8_t* src_argb4444, uint8_t* dst_argb,
-                            int width) = ARGB4444ToARGBRow_C;
-  if (!src_argb4444 || !dst_argb || width <= 0 || height == 0) {
-    return -1;
-  }
-  // Negative height means invert the image.
-  if (height < 0) {
-    height = -height;
-    src_argb4444 = src_argb4444 + (height - 1) * src_stride_argb4444;
-    src_stride_argb4444 = -src_stride_argb4444;
-  }
-  // Coalesce rows.
-  if (src_stride_argb4444 == width * 2 && dst_stride_argb == width * 4) {
-    width *= height;
-    height = 1;
-    src_stride_argb4444 = dst_stride_argb = 0;
-  }
-#if defined(HAS_ARGB4444TOARGBROW_SSE2)
-  if (TestCpuFlag(kCpuHasSSE2)) {
-    ARGB4444ToARGBRow = ARGB4444ToARGBRow_Any_SSE2;
-    if (IS_ALIGNED(width, 8)) {
-      ARGB4444ToARGBRow = ARGB4444ToARGBRow_SSE2;
-    }
-  }
-#endif
-#if defined(HAS_ARGB4444TOARGBROW_AVX2)
-  if (TestCpuFlag(kCpuHasAVX2)) {
-    ARGB4444ToARGBRow = ARGB4444ToARGBRow_Any_AVX2;
-    if (IS_ALIGNED(width, 16)) {
-      ARGB4444ToARGBRow = ARGB4444ToARGBRow_AVX2;
-    }
-  }
-#endif
-#if defined(HAS_ARGB4444TOARGBROW_NEON)
-  if (TestCpuFlag(kCpuHasNEON)) {
-    ARGB4444ToARGBRow = ARGB4444ToARGBRow_Any_NEON;
-    if (IS_ALIGNED(width, 8)) {
-      ARGB4444ToARGBRow = ARGB4444ToARGBRow_NEON;
-    }
-  }
-#endif
-#if defined(HAS_ARGB4444TOARGBROW_MSA)
-  if (TestCpuFlag(kCpuHasMSA)) {
-    ARGB4444ToARGBRow = ARGB4444ToARGBRow_Any_MSA;
-    if (IS_ALIGNED(width, 16)) {
-      ARGB4444ToARGBRow = ARGB4444ToARGBRow_MSA;
-    }
-  }
-#endif
-
-  for (y = 0; y < height; ++y) {
-    ARGB4444ToARGBRow(src_argb4444, dst_argb, width);
-    src_argb4444 += src_stride_argb4444;
-    dst_argb += dst_stride_argb;
-  }
-  return 0;
-}
-
-// Convert AR30 to ARGB.
-LIBYUV_API
-int AR30ToARGB(const uint8_t* src_ar30,
-               int src_stride_ar30,
-               uint8_t* dst_argb,
-               int dst_stride_argb,
-               int width,
-               int height) {
-  int y;
-  if (!src_ar30 || !dst_argb || width <= 0 || height == 0) {
-    return -1;
-  }
-  // Negative height means invert the image.
-  if (height < 0) {
-    height = -height;
-    src_ar30 = src_ar30 + (height - 1) * src_stride_ar30;
-    src_stride_ar30 = -src_stride_ar30;
-  }
-  // Coalesce rows.
-  if (src_stride_ar30 == width * 4 && dst_stride_argb == width * 4) {
-    width *= height;
-    height = 1;
-    src_stride_ar30 = dst_stride_argb = 0;
-  }
-  for (y = 0; y < height; ++y) {
-    AR30ToARGBRow_C(src_ar30, dst_argb, width);
-    src_ar30 += src_stride_ar30;
-    dst_argb += dst_stride_argb;
-  }
-  return 0;
-}
-
-// Convert AR30 to ABGR.
-LIBYUV_API
-int AR30ToABGR(const uint8_t* src_ar30,
-               int src_stride_ar30,
-               uint8_t* dst_abgr,
-               int dst_stride_abgr,
-               int width,
-               int height) {
-  int y;
-  if (!src_ar30 || !dst_abgr || width <= 0 || height == 0) {
-    return -1;
-  }
-  // Negative height means invert the image.
-  if (height < 0) {
-    height = -height;
-    src_ar30 = src_ar30 + (height - 1) * src_stride_ar30;
-    src_stride_ar30 = -src_stride_ar30;
-  }
-  // Coalesce rows.
-  if (src_stride_ar30 == width * 4 && dst_stride_abgr == width * 4) {
-    width *= height;
-    height = 1;
-    src_stride_ar30 = dst_stride_abgr = 0;
-  }
-  for (y = 0; y < height; ++y) {
-    AR30ToABGRRow_C(src_ar30, dst_abgr, width);
-    src_ar30 += src_stride_ar30;
-    dst_abgr += dst_stride_abgr;
-  }
-  return 0;
-}
-
-// Convert AR30 to AB30.
-LIBYUV_API
-int AR30ToAB30(const uint8_t* src_ar30,
-               int src_stride_ar30,
-               uint8_t* dst_ab30,
-               int dst_stride_ab30,
-               int width,
-               int height) {
-  int y;
-  if (!src_ar30 || !dst_ab30 || width <= 0 || height == 0) {
-    return -1;
-  }
-  // Negative height means invert the image.
-  if (height < 0) {
-    height = -height;
-    src_ar30 = src_ar30 + (height - 1) * src_stride_ar30;
-    src_stride_ar30 = -src_stride_ar30;
-  }
-  // Coalesce rows.
-  if (src_stride_ar30 == width * 4 && dst_stride_ab30 == width * 4) {
-    width *= height;
-    height = 1;
-    src_stride_ar30 = dst_stride_ab30 = 0;
-  }
-  for (y = 0; y < height; ++y) {
-    AR30ToAB30Row_C(src_ar30, dst_ab30, width);
-    src_ar30 += src_stride_ar30;
-    dst_ab30 += dst_stride_ab30;
-  }
-  return 0;
-}
-
-// Convert NV12 to ARGB with matrix
-static int NV12ToARGBMatrix(const uint8_t* src_y,
-                            int src_stride_y,
-                            const uint8_t* src_uv,
-                            int src_stride_uv,
-                            uint8_t* dst_argb,
-                            int dst_stride_argb,
-                            const struct YuvConstants* yuvconstants,
-                            int width,
-                            int height) {
-  int y;
-  void (*NV12ToARGBRow)(
-      const uint8_t* y_buf, const uint8_t* uv_buf, uint8_t* rgb_buf,
-      const struct YuvConstants* yuvconstants, int width) = NV12ToARGBRow_C;
-  if (!src_y || !src_uv || !dst_argb || width <= 0 || height == 0) {
-    return -1;
-  }
-  // Negative height means invert the image.
-  if (height < 0) {
-    height = -height;
-    dst_argb = dst_argb + (height - 1) * dst_stride_argb;
-    dst_stride_argb = -dst_stride_argb;
-  }
-#if defined(HAS_NV12TOARGBROW_SSSE3)
-  if (TestCpuFlag(kCpuHasSSSE3)) {
-    NV12ToARGBRow = NV12ToARGBRow_Any_SSSE3;
-    if (IS_ALIGNED(width, 8)) {
-      NV12ToARGBRow = NV12ToARGBRow_SSSE3;
-    }
-  }
-#endif
-#if defined(HAS_NV12TOARGBROW_AVX2)
-  if (TestCpuFlag(kCpuHasAVX2)) {
-    NV12ToARGBRow = NV12ToARGBRow_Any_AVX2;
-    if (IS_ALIGNED(width, 16)) {
-      NV12ToARGBRow = NV12ToARGBRow_AVX2;
-    }
-  }
-#endif
-#if defined(HAS_NV12TOARGBROW_NEON)
-  if (TestCpuFlag(kCpuHasNEON)) {
-    NV12ToARGBRow = NV12ToARGBRow_Any_NEON;
-    if (IS_ALIGNED(width, 8)) {
-      NV12ToARGBRow = NV12ToARGBRow_NEON;
-    }
-  }
-#endif
-#if defined(HAS_NV12TOARGBROW_MSA)
-  if (TestCpuFlag(kCpuHasMSA)) {
-    NV12ToARGBRow = NV12ToARGBRow_Any_MSA;
-    if (IS_ALIGNED(width, 8)) {
-      NV12ToARGBRow = NV12ToARGBRow_MSA;
-    }
-  }
-#endif
-
-  for (y = 0; y < height; ++y) {
-    NV12ToARGBRow(src_y, src_uv, dst_argb, yuvconstants, width);
-    dst_argb += dst_stride_argb;
-    src_y += src_stride_y;
-    if (y & 1) {
-      src_uv += src_stride_uv;
-    }
-  }
-  return 0;
-}
-
-// Convert NV21 to ARGB with matrix
-static int NV21ToARGBMatrix(const uint8_t* src_y,
-                            int src_stride_y,
-                            const uint8_t* src_vu,
-                            int src_stride_vu,
-                            uint8_t* dst_argb,
-                            int dst_stride_argb,
-                            const struct YuvConstants* yuvconstants,
-                            int width,
-                            int height) {
-  int y;
-  void (*NV21ToARGBRow)(
-      const uint8_t* y_buf, const uint8_t* uv_buf, uint8_t* rgb_buf,
-      const struct YuvConstants* yuvconstants, int width) = NV21ToARGBRow_C;
-  if (!src_y || !src_vu || !dst_argb || width <= 0 || height == 0) {
-    return -1;
-  }
-  // Negative height means invert the image.
-  if (height < 0) {
-    height = -height;
-    dst_argb = dst_argb + (height - 1) * dst_stride_argb;
-    dst_stride_argb = -dst_stride_argb;
-  }
-#if defined(HAS_NV21TOARGBROW_SSSE3)
-  if (TestCpuFlag(kCpuHasSSSE3)) {
-    NV21ToARGBRow = NV21ToARGBRow_Any_SSSE3;
-    if (IS_ALIGNED(width, 8)) {
-      NV21ToARGBRow = NV21ToARGBRow_SSSE3;
-    }
-  }
-#endif
-#if defined(HAS_NV21TOARGBROW_AVX2)
-  if (TestCpuFlag(kCpuHasAVX2)) {
-    NV21ToARGBRow = NV21ToARGBRow_Any_AVX2;
-    if (IS_ALIGNED(width, 16)) {
-      NV21ToARGBRow = NV21ToARGBRow_AVX2;
-    }
-  }
-#endif
-#if defined(HAS_NV21TOARGBROW_NEON)
-  if (TestCpuFlag(kCpuHasNEON)) {
-    NV21ToARGBRow = NV21ToARGBRow_Any_NEON;
-    if (IS_ALIGNED(width, 8)) {
-      NV21ToARGBRow = NV21ToARGBRow_NEON;
-    }
-  }
-#endif
-#if defined(HAS_NV21TOARGBROW_MSA)
-  if (TestCpuFlag(kCpuHasMSA)) {
-    NV21ToARGBRow = NV21ToARGBRow_Any_MSA;
-    if (IS_ALIGNED(width, 8)) {
-      NV21ToARGBRow = NV21ToARGBRow_MSA;
-    }
-  }
-#endif
-
-  for (y = 0; y < height; ++y) {
-    NV21ToARGBRow(src_y, src_vu, dst_argb, yuvconstants, width);
-    dst_argb += dst_stride_argb;
-    src_y += src_stride_y;
-    if (y & 1) {
-      src_vu += src_stride_vu;
-    }
-  }
-  return 0;
-}
-
-// Convert NV12 to ARGB.
-LIBYUV_API
-int NV12ToARGB(const uint8_t* src_y,
-               int src_stride_y,
-               const uint8_t* src_uv,
-               int src_stride_uv,
-               uint8_t* dst_argb,
-               int dst_stride_argb,
-               int width,
-               int height) {
-  return NV12ToARGBMatrix(src_y, src_stride_y, src_uv, src_stride_uv, dst_argb,
-                          dst_stride_argb, &kYuvI601Constants, width, height);
-}
-
-// Convert NV21 to ARGB.
-LIBYUV_API
-int NV21ToARGB(const uint8_t* src_y,
-               int src_stride_y,
-               const uint8_t* src_vu,
-               int src_stride_vu,
-               uint8_t* dst_argb,
-               int dst_stride_argb,
-               int width,
-               int height) {
-  return NV21ToARGBMatrix(src_y, src_stride_y, src_vu, src_stride_vu, dst_argb,
-                          dst_stride_argb, &kYuvI601Constants, width, height);
-}
-
-// Convert NV12 to ABGR.
-// To output ABGR instead of ARGB swap the UV and use a mirrrored yuc matrix.
-// To swap the UV use NV12 instead of NV21.LIBYUV_API
-int NV12ToABGR(const uint8_t* src_y,
-               int src_stride_y,
-               const uint8_t* src_uv,
-               int src_stride_uv,
-               uint8_t* dst_abgr,
-               int dst_stride_abgr,
-               int width,
-               int height) {
-  return NV21ToARGBMatrix(src_y, src_stride_y, src_uv, src_stride_uv, dst_abgr,
-                          dst_stride_abgr, &kYvuI601Constants, width, height);
-}
-
-// Convert NV21 to ABGR.
-LIBYUV_API
-int NV21ToABGR(const uint8_t* src_y,
-               int src_stride_y,
-               const uint8_t* src_vu,
-               int src_stride_vu,
-               uint8_t* dst_abgr,
-               int dst_stride_abgr,
-               int width,
-               int height) {
-  return NV12ToARGBMatrix(src_y, src_stride_y, src_vu, src_stride_vu, dst_abgr,
-                          dst_stride_abgr, &kYvuI601Constants, width, height);
-}
-
-// TODO(fbarchard): Consider SSSE3 2 step conversion.
-// Convert NV12 to RGB24 with matrix
-static int NV12ToRGB24Matrix(const uint8_t* src_y,
-                             int src_stride_y,
-                             const uint8_t* src_uv,
-                             int src_stride_uv,
-                             uint8_t* dst_rgb24,
-                             int dst_stride_rgb24,
-                             const struct YuvConstants* yuvconstants,
-                             int width,
-                             int height) {
-  int y;
-  void (*NV12ToRGB24Row)(
-      const uint8_t* y_buf, const uint8_t* uv_buf, uint8_t* rgb_buf,
-      const struct YuvConstants* yuvconstants, int width) = NV12ToRGB24Row_C;
-  if (!src_y || !src_uv || !dst_rgb24 || width <= 0 || height == 0) {
-    return -1;
-  }
-  // Negative height means invert the image.
-  if (height < 0) {
-    height = -height;
-    dst_rgb24 = dst_rgb24 + (height - 1) * dst_stride_rgb24;
-    dst_stride_rgb24 = -dst_stride_rgb24;
-  }
-#if defined(HAS_NV12TORGB24ROW_NEON)
-  if (TestCpuFlag(kCpuHasNEON)) {
-    NV12ToRGB24Row = NV12ToRGB24Row_Any_NEON;
-    if (IS_ALIGNED(width, 8)) {
-      NV12ToRGB24Row = NV12ToRGB24Row_NEON;
-    }
-  }
-#endif
-
-  for (y = 0; y < height; ++y) {
-    NV12ToRGB24Row(src_y, src_uv, dst_rgb24, yuvconstants, width);
-    dst_rgb24 += dst_stride_rgb24;
-    src_y += src_stride_y;
-    if (y & 1) {
-      src_uv += src_stride_uv;
-    }
-  }
-  return 0;
-}
-
-// Convert NV21 to RGB24 with matrix
-static int NV21ToRGB24Matrix(const uint8_t* src_y,
-                             int src_stride_y,
-                             const uint8_t* src_vu,
-                             int src_stride_vu,
-                             uint8_t* dst_rgb24,
-                             int dst_stride_rgb24,
-                             const struct YuvConstants* yuvconstants,
-                             int width,
-                             int height) {
-  int y;
-  void (*NV21ToRGB24Row)(
-      const uint8_t* y_buf, const uint8_t* uv_buf, uint8_t* rgb_buf,
-      const struct YuvConstants* yuvconstants, int width) = NV21ToRGB24Row_C;
-  if (!src_y || !src_vu || !dst_rgb24 || width <= 0 || height == 0) {
-    return -1;
-  }
-  // Negative height means invert the image.
-  if (height < 0) {
-    height = -height;
-    dst_rgb24 = dst_rgb24 + (height - 1) * dst_stride_rgb24;
-    dst_stride_rgb24 = -dst_stride_rgb24;
-  }
-#if defined(HAS_NV21TORGB24ROW_NEON)
-  if (TestCpuFlag(kCpuHasNEON)) {
-    NV21ToRGB24Row = NV21ToRGB24Row_Any_NEON;
-    if (IS_ALIGNED(width, 8)) {
-      NV21ToRGB24Row = NV21ToRGB24Row_NEON;
-    }
-  }
-#endif
-
-  for (y = 0; y < height; ++y) {
-    NV21ToRGB24Row(src_y, src_vu, dst_rgb24, yuvconstants, width);
-    dst_rgb24 += dst_stride_rgb24;
-    src_y += src_stride_y;
-    if (y & 1) {
-      src_vu += src_stride_vu;
-    }
-  }
-  return 0;
-}
-
-// TODO(fbarchard): \(fbarchard): NV12ToRAW can be implemented by mirrored
-// matrix. Convert NV12 to RGB24.
-LIBYUV_API
-int NV12ToRGB24(const uint8_t* src_y,
-                int src_stride_y,
-                const uint8_t* src_uv,
-                int src_stride_uv,
-                uint8_t* dst_rgb24,
-                int dst_stride_rgb24,
-                int width,
-                int height) {
-  return NV12ToRGB24Matrix(src_y, src_stride_y, src_uv, src_stride_uv,
-                           dst_rgb24, dst_stride_rgb24, &kYuvI601Constants,
-                           width, height);
-}
-
-// Convert NV21 to RGB24.
-LIBYUV_API
-int NV21ToRGB24(const uint8_t* src_y,
-                int src_stride_y,
-                const uint8_t* src_vu,
-                int src_stride_vu,
-                uint8_t* dst_rgb24,
-                int dst_stride_rgb24,
-                int width,
-                int height) {
-  return NV21ToRGB24Matrix(src_y, src_stride_y, src_vu, src_stride_vu,
-                           dst_rgb24, dst_stride_rgb24, &kYuvI601Constants,
-                           width, height);
-}
-
-// Convert M420 to ARGB.
-LIBYUV_API
-int M420ToARGB(const uint8_t* src_m420,
-               int src_stride_m420,
-               uint8_t* dst_argb,
-               int dst_stride_argb,
-               int width,
-               int height) {
-  int y;
-  void (*NV12ToARGBRow)(
-      const uint8_t* y_buf, const uint8_t* uv_buf, uint8_t* rgb_buf,
-      const struct YuvConstants* yuvconstants, int width) = NV12ToARGBRow_C;
-  if (!src_m420 || !dst_argb || width <= 0 || height == 0) {
-    return -1;
-  }
-  // Negative height means invert the image.
-  if (height < 0) {
-    height = -height;
-    dst_argb = dst_argb + (height - 1) * dst_stride_argb;
-    dst_stride_argb = -dst_stride_argb;
-  }
-#if defined(HAS_NV12TOARGBROW_SSSE3)
-  if (TestCpuFlag(kCpuHasSSSE3)) {
-    NV12ToARGBRow = NV12ToARGBRow_Any_SSSE3;
-    if (IS_ALIGNED(width, 8)) {
-      NV12ToARGBRow = NV12ToARGBRow_SSSE3;
-    }
-  }
-#endif
-#if defined(HAS_NV12TOARGBROW_AVX2)
-  if (TestCpuFlag(kCpuHasAVX2)) {
-    NV12ToARGBRow = NV12ToARGBRow_Any_AVX2;
-    if (IS_ALIGNED(width, 16)) {
-      NV12ToARGBRow = NV12ToARGBRow_AVX2;
-    }
-  }
-#endif
-#if defined(HAS_NV12TOARGBROW_NEON)
-  if (TestCpuFlag(kCpuHasNEON)) {
-    NV12ToARGBRow = NV12ToARGBRow_Any_NEON;
-    if (IS_ALIGNED(width, 8)) {
-      NV12ToARGBRow = NV12ToARGBRow_NEON;
-    }
-  }
-#endif
-#if defined(HAS_NV12TOARGBROW_MSA)
-  if (TestCpuFlag(kCpuHasMSA)) {
-    NV12ToARGBRow = NV12ToARGBRow_Any_MSA;
-    if (IS_ALIGNED(width, 8)) {
-      NV12ToARGBRow = NV12ToARGBRow_MSA;
-    }
-  }
-#endif
-
-  for (y = 0; y < height - 1; y += 2) {
-    NV12ToARGBRow(src_m420, src_m420 + src_stride_m420 * 2, dst_argb,
-                  &kYuvI601Constants, width);
-    NV12ToARGBRow(src_m420 + src_stride_m420, src_m420 + src_stride_m420 * 2,
-                  dst_argb + dst_stride_argb, &kYuvI601Constants, width);
-    dst_argb += dst_stride_argb * 2;
-    src_m420 += src_stride_m420 * 3;
-  }
-  if (height & 1) {
-    NV12ToARGBRow(src_m420, src_m420 + src_stride_m420 * 2, dst_argb,
-                  &kYuvI601Constants, width);
-  }
-  return 0;
-}
-
-// Convert YUY2 to ARGB.
-LIBYUV_API
-int YUY2ToARGB(const uint8_t* src_yuy2,
-               int src_stride_yuy2,
-               uint8_t* dst_argb,
-               int dst_stride_argb,
-               int width,
-               int height) {
-  int y;
-  void (*YUY2ToARGBRow)(const uint8_t* src_yuy2, uint8_t* dst_argb,
-                        const struct YuvConstants* yuvconstants, int width) =
-      YUY2ToARGBRow_C;
-  if (!src_yuy2 || !dst_argb || width <= 0 || height == 0) {
-    return -1;
-  }
-  // Negative height means invert the image.
-  if (height < 0) {
-    height = -height;
-    src_yuy2 = src_yuy2 + (height - 1) * src_stride_yuy2;
-    src_stride_yuy2 = -src_stride_yuy2;
-  }
-  // Coalesce rows.
-  if (src_stride_yuy2 == width * 2 && dst_stride_argb == width * 4) {
-    width *= height;
-    height = 1;
-    src_stride_yuy2 = dst_stride_argb = 0;
-  }
-#if defined(HAS_YUY2TOARGBROW_SSSE3)
-  if (TestCpuFlag(kCpuHasSSSE3)) {
-    YUY2ToARGBRow = YUY2ToARGBRow_Any_SSSE3;
-    if (IS_ALIGNED(width, 16)) {
-      YUY2ToARGBRow = YUY2ToARGBRow_SSSE3;
-    }
-  }
-#endif
-#if defined(HAS_YUY2TOARGBROW_AVX2)
-  if (TestCpuFlag(kCpuHasAVX2)) {
-    YUY2ToARGBRow = YUY2ToARGBRow_Any_AVX2;
-    if (IS_ALIGNED(width, 32)) {
-      YUY2ToARGBRow = YUY2ToARGBRow_AVX2;
-    }
-  }
-#endif
-#if defined(HAS_YUY2TOARGBROW_NEON)
-  if (TestCpuFlag(kCpuHasNEON)) {
-    YUY2ToARGBRow = YUY2ToARGBRow_Any_NEON;
-    if (IS_ALIGNED(width, 8)) {
-      YUY2ToARGBRow = YUY2ToARGBRow_NEON;
-    }
-  }
-#endif
-#if defined(HAS_YUY2TOARGBROW_MSA)
-  if (TestCpuFlag(kCpuHasMSA)) {
-    YUY2ToARGBRow = YUY2ToARGBRow_Any_MSA;
-    if (IS_ALIGNED(width, 8)) {
-      YUY2ToARGBRow = YUY2ToARGBRow_MSA;
-    }
-  }
-#endif
-  for (y = 0; y < height; ++y) {
-    YUY2ToARGBRow(src_yuy2, dst_argb, &kYuvI601Constants, width);
-    src_yuy2 += src_stride_yuy2;
-    dst_argb += dst_stride_argb;
-  }
-  return 0;
-}
-
-// Convert UYVY to ARGB.
-LIBYUV_API
-int UYVYToARGB(const uint8_t* src_uyvy,
-               int src_stride_uyvy,
-               uint8_t* dst_argb,
-               int dst_stride_argb,
-               int width,
-               int height) {
-  int y;
-  void (*UYVYToARGBRow)(const uint8_t* src_uyvy, uint8_t* dst_argb,
-                        const struct YuvConstants* yuvconstants, int width) =
-      UYVYToARGBRow_C;
-  if (!src_uyvy || !dst_argb || width <= 0 || height == 0) {
-    return -1;
-  }
-  // Negative height means invert the image.
-  if (height < 0) {
-    height = -height;
-    src_uyvy = src_uyvy + (height - 1) * src_stride_uyvy;
-    src_stride_uyvy = -src_stride_uyvy;
-  }
-  // Coalesce rows.
-  if (src_stride_uyvy == width * 2 && dst_stride_argb == width * 4) {
-    width *= height;
-    height = 1;
-    src_stride_uyvy = dst_stride_argb = 0;
-  }
-#if defined(HAS_UYVYTOARGBROW_SSSE3)
-  if (TestCpuFlag(kCpuHasSSSE3)) {
-    UYVYToARGBRow = UYVYToARGBRow_Any_SSSE3;
-    if (IS_ALIGNED(width, 16)) {
-      UYVYToARGBRow = UYVYToARGBRow_SSSE3;
-    }
-  }
-#endif
-#if defined(HAS_UYVYTOARGBROW_AVX2)
-  if (TestCpuFlag(kCpuHasAVX2)) {
-    UYVYToARGBRow = UYVYToARGBRow_Any_AVX2;
-    if (IS_ALIGNED(width, 32)) {
-      UYVYToARGBRow = UYVYToARGBRow_AVX2;
-    }
-  }
-#endif
-#if defined(HAS_UYVYTOARGBROW_NEON)
-  if (TestCpuFlag(kCpuHasNEON)) {
-    UYVYToARGBRow = UYVYToARGBRow_Any_NEON;
-    if (IS_ALIGNED(width, 8)) {
-      UYVYToARGBRow = UYVYToARGBRow_NEON;
-    }
-  }
-#endif
-#if defined(HAS_UYVYTOARGBROW_MSA)
-  if (TestCpuFlag(kCpuHasMSA)) {
-    UYVYToARGBRow = UYVYToARGBRow_Any_MSA;
-    if (IS_ALIGNED(width, 8)) {
-      UYVYToARGBRow = UYVYToARGBRow_MSA;
-    }
-  }
-#endif
-  for (y = 0; y < height; ++y) {
-    UYVYToARGBRow(src_uyvy, dst_argb, &kYuvI601Constants, width);
-    src_uyvy += src_stride_uyvy;
-    dst_argb += dst_stride_argb;
-  }
-  return 0;
-}
-static void WeavePixels(const uint8_t* src_u,
-                        const uint8_t* src_v,
-                        int src_pixel_stride_uv,
-                        uint8_t* dst_uv,
-                        int width) {
-  int i;
-  for (i = 0; i < width; ++i) {
-    dst_uv[0] = *src_u;
-    dst_uv[1] = *src_v;
-    dst_uv += 2;
-    src_u += src_pixel_stride_uv;
-    src_v += src_pixel_stride_uv;
-  }
-}
-
-// Convert Android420 to ARGB.
-LIBYUV_API
-int Android420ToARGBMatrix(const uint8_t* src_y,
-                           int src_stride_y,
-                           const uint8_t* src_u,
-                           int src_stride_u,
-                           const uint8_t* src_v,
-                           int src_stride_v,
-                           int src_pixel_stride_uv,
-                           uint8_t* dst_argb,
-                           int dst_stride_argb,
-                           const struct YuvConstants* yuvconstants,
-                           int width,
-                           int height) {
-  int y;
-  uint8_t* dst_uv;
-  const ptrdiff_t vu_off = src_v - src_u;
-  int halfwidth = (width + 1) >> 1;
-  int halfheight = (height + 1) >> 1;
-  if (!src_y || !src_u || !src_v || !dst_argb || width <= 0 || height == 0) {
-    return -1;
-  }
-  // Negative height means invert the image.
-  if (height < 0) {
-    height = -height;
-    halfheight = (height + 1) >> 1;
-    dst_argb = dst_argb + (height - 1) * dst_stride_argb;
-    dst_stride_argb = -dst_stride_argb;
-  }
-
-  // I420
-  if (src_pixel_stride_uv == 1) {
-    return I420ToARGBMatrix(src_y, src_stride_y, src_u, src_stride_u, src_v,
-                            src_stride_v, dst_argb, dst_stride_argb,
-                            yuvconstants, width, height);
-    // NV21
-  }
-  if (src_pixel_stride_uv == 2 && vu_off == -1 &&
-      src_stride_u == src_stride_v) {
-    return NV21ToARGBMatrix(src_y, src_stride_y, src_v, src_stride_v, dst_argb,
-                            dst_stride_argb, yuvconstants, width, height);
-    // NV12
-  }
-  if (src_pixel_stride_uv == 2 && vu_off == 1 && src_stride_u == src_stride_v) {
-    return NV12ToARGBMatrix(src_y, src_stride_y, src_u, src_stride_u, dst_argb,
-                            dst_stride_argb, yuvconstants, width, height);
-  }
-
-  // General case fallback creates NV12
-  align_buffer_64(plane_uv, halfwidth * 2 * halfheight);
-  dst_uv = plane_uv;
-  for (y = 0; y < halfheight; ++y) {
-    WeavePixels(src_u, src_v, src_pixel_stride_uv, dst_uv, halfwidth);
-    src_u += src_stride_u;
-    src_v += src_stride_v;
-    dst_uv += halfwidth * 2;
-  }
-  NV12ToARGBMatrix(src_y, src_stride_y, plane_uv, halfwidth * 2, dst_argb,
-                   dst_stride_argb, yuvconstants, width, height);
-  free_aligned_buffer_64(plane_uv);
-  return 0;
-}
-
-// Convert Android420 to ARGB.
-LIBYUV_API
-int Android420ToARGB(const uint8_t* src_y,
-                     int src_stride_y,
-                     const uint8_t* src_u,
-                     int src_stride_u,
-                     const uint8_t* src_v,
-                     int src_stride_v,
-                     int src_pixel_stride_uv,
-                     uint8_t* dst_argb,
-                     int dst_stride_argb,
-                     int width,
-                     int height) {
-  return Android420ToARGBMatrix(src_y, src_stride_y, src_u, src_stride_u, src_v,
-                                src_stride_v, src_pixel_stride_uv, dst_argb,
-                                dst_stride_argb, &kYuvI601Constants, width,
-                                height);
-}
-
-// Convert Android420 to ABGR.
-LIBYUV_API
-int Android420ToABGR(const uint8_t* src_y,
-                     int src_stride_y,
-                     const uint8_t* src_u,
-                     int src_stride_u,
-                     const uint8_t* src_v,
-                     int src_stride_v,
-                     int src_pixel_stride_uv,
-                     uint8_t* dst_abgr,
-                     int dst_stride_abgr,
-                     int width,
-                     int height) {
-  return Android420ToARGBMatrix(src_y, src_stride_y, src_v, src_stride_v, src_u,
-                                src_stride_u, src_pixel_stride_uv, dst_abgr,
-                                dst_stride_abgr, &kYvuI601Constants, width,
-                                height);
-}
-
-#ifdef __cplusplus
-}  // extern "C"
-}  // namespace libyuv
-#endif
--- a/mobile/android/app/build.gradle
+++ b/mobile/android/app/build.gradle
@@ -117,17 +117,17 @@ android {
                 srcDir "${topsrcdir}/mobile/android/base/java"
                 srcDir "${topsrcdir}/mobile/android/services/src/main/java"
 
                 if (mozconfig.substs.MOZ_ANDROID_MLS_STUMBLER) {
                     srcDir "${topsrcdir}/mobile/android/stumbler/java"
                 }
 
                 if (!mozconfig.substs.MOZ_CRASHREPORTER) {
-                    exclude 'org/mozilla/gecko/CrashReporter.java'
+                    exclude 'org/mozilla/gecko/CrashReporterActivity.java'
                 }
 
                 if (!mozconfig.substs.MOZ_NATIVE_DEVICES) {
                     exclude 'org/mozilla/gecko/ChromeCastDisplay.java'
                     exclude 'org/mozilla/gecko/ChromeCastPlayer.java'
                     exclude 'org/mozilla/gecko/GeckoMediaPlayer.java'
                     exclude 'org/mozilla/gecko/GeckoPresentationDisplay.java'
                     exclude 'org/mozilla/gecko/MediaPlayerManager.java'
--- a/mobile/android/app/lint.xml
+++ b/mobile/android/app/lint.xml
@@ -59,17 +59,17 @@
         <ignore path="**/mobile/android/base/java/org/mozilla/gecko/util/ViewUtil.java"/>
         <ignore path="**/mobile/android/base/java/org/mozilla/gecko/IntentHelper.java"/>
         <ignore path="**/media/webrtc/trunk/webrtc/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioEffects.java"/>
         <ignore path="**/media/webrtc/trunk/webrtc/modules/audio_device/android/java/src/org/webrtc/voiceengine/WebRtcAudioRecord.java"/>
         <ignore path="src/photon/res/values/styles.xml"/>
     </issue>
 
     <!-- We fixed all "Registered" lint errors. However the current gradle plugin has a bug where
-         it ignores @SuppressLint annotations for this check. See CrashReporter class and
+         it ignores @SuppressLint annotations for this check. See CrashReporterActivity class and
          https://code.google.com/p/android/issues/detail?id=204846 -->
     <issue id="Registered" severity="warning" />
 
     <issue id="ObjectAnimatorBinding" severity="error">
         <!-- Two animated properties are provided by the underlying
              View implementation. -->
         <ignore path="**/mobile/android/base/java/org/mozilla/gecko/firstrun/FirstrunPager.java"/>
     </issue>
--- a/mobile/android/base/AndroidManifest.xml.in
+++ b/mobile/android/base/AndroidManifest.xml.in
@@ -267,17 +267,17 @@
                 <data android:scheme="moz-notification" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </receiver>
 
 #include ../services/manifests/FxAccountAndroidManifest_activities.xml.in
 
 #ifdef MOZ_CRASHREPORTER
-  <activity android:name="org.mozilla.gecko.CrashReporter"
+  <activity android:name="org.mozilla.gecko.CrashReporterActivity"
             android:process="@ANDROID_PACKAGE_NAME@.CrashReporter"
             android:label="@string/crash_reporter_title"
             android:icon="@drawable/crash_reporter"
             android:theme="@style/Gecko"
             android:exported="false"
             android:excludeFromRecents="true">
           <intent-filter>
             <action android:name="org.mozilla.gecko.reportCrash" />
deleted file mode 100644
--- a/mobile/android/base/java/org/mozilla/gecko/CrashReporter.java
+++ /dev/null
@@ -1,599 +0,0 @@
-/* -*- Mode: Java; tab-width: 20; indent-tabs-mode: nil; -*-
- * 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/. */
-
-package org.mozilla.gecko;
-
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.Map;
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.FileReader;
-import java.io.FileWriter;
-import java.io.InputStreamReader;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.net.HttpURLConnection;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.net.URLDecoder;
-import java.nio.channels.Channels;
-import java.nio.channels.FileChannel;
-import java.security.MessageDigest;
-import java.util.zip.GZIPOutputStream;
-
-import org.mozilla.gecko.AppConstants.Versions;
-import org.mozilla.gecko.GeckoProfile;
-import org.mozilla.gecko.mozglue.GeckoLoader;
-import org.mozilla.gecko.mozglue.MinidumpAnalyzer;
-import org.mozilla.gecko.telemetry.pingbuilders.TelemetryCrashPingBuilder;
-import org.mozilla.gecko.telemetry.TelemetryDispatcher;
-import org.mozilla.gecko.util.INIParser;
-import org.mozilla.gecko.util.INISection;
-import org.mozilla.gecko.util.ProxySelector;
-
-import android.annotation.SuppressLint;
-import android.app.AlertDialog;
-import android.app.ProgressDialog;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Handler;
-import android.support.v7.app.AppCompatActivity;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.View;
-import android.widget.CheckBox;
-import android.widget.CompoundButton;
-import android.widget.EditText;
-
-// Registered: This activity is only registered in the manifest if MOZ_CRASHREPORTER is set.
-// CutPasteId: This lint is not worth fixing.  To fix it, cache all the findViewById results.
-@SuppressLint("Registered,CutPasteId")
-public class CrashReporter extends AppCompatActivity
-{
-    private static final String LOGTAG = "GeckoCrashReporter";
-
-    private static final String PASSED_MINI_DUMP_KEY = "minidumpPath";
-    private static final String PASSED_MINI_DUMP_SUCCESS_KEY = "minidumpSuccess";
-    private static final String MINI_DUMP_PATH_KEY = "upload_file_minidump";
-    private static final String PAGE_URL_KEY = "URL";
-    private static final String NOTES_KEY = "Notes";
-    private static final String SERVER_URL_KEY = "ServerURL";
-
-    private static final String CRASH_REPORT_SUFFIX = "/mozilla/Crash Reports/";
-    private static final String PENDING_SUFFIX = CRASH_REPORT_SUFFIX + "pending";
-    private static final String SUBMITTED_SUFFIX = CRASH_REPORT_SUFFIX + "submitted";
-
-    private static final String PREFS_SEND_REPORT   = "sendReport";
-    private static final String PREFS_INCLUDE_URL   = "includeUrl";
-    private static final String PREFS_ALLOW_CONTACT = "allowContact";
-    private static final String PREFS_CONTACT_EMAIL = "contactEmail";
-
-    private Handler mHandler;
-    private ProgressDialog mProgressDialog;
-    private File mPendingMinidumpFile;
-    private File mPendingExtrasFile;
-    private HashMap<String, String> mExtrasStringMap;
-    private boolean mMinidumpSucceeded;
-
-    private boolean moveFile(File inFile, File outFile) {
-        Log.i(LOGTAG, "moving " + inFile + " to " + outFile);
-        if (inFile.renameTo(outFile))
-            return true;
-        try {
-            outFile.createNewFile();
-            Log.i(LOGTAG, "couldn't rename minidump file");
-            // so copy it instead
-            FileChannel inChannel = new FileInputStream(inFile).getChannel();
-            FileChannel outChannel = new FileOutputStream(outFile).getChannel();
-            long transferred = inChannel.transferTo(0, inChannel.size(), outChannel);
-            inChannel.close();
-            outChannel.close();
-
-            if (transferred > 0)
-                inFile.delete();
-        } catch (Exception e) {
-            Log.e(LOGTAG, "exception while copying minidump file: ", e);
-            return false;
-        }
-        return true;
-    }
-
-    private void doFinish() {
-        if (mHandler != null) {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    finish();
-                }
-            });
-        }
-    }
-
-    @Override
-    public void finish() {
-        try {
-            if (mProgressDialog.isShowing()) {
-                mProgressDialog.dismiss();
-            }
-        } catch (Exception e) {
-            Log.e(LOGTAG, "exception while closing progress dialog: ", e);
-        }
-        super.finish();
-    }
-
-    @Override
-    @SuppressLint("WrongThread") // We don't have a worker thread for the TelemetryDispatcher
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        // mHandler is created here so runnables can be run on the main thread
-        mHandler = new Handler();
-        setContentView(R.layout.crash_reporter);
-        mProgressDialog = new ProgressDialog(this);
-        mProgressDialog.setMessage(getString(R.string.sending_crash_report));
-
-        mMinidumpSucceeded = getIntent().getBooleanExtra(PASSED_MINI_DUMP_SUCCESS_KEY, false);
-        if (!mMinidumpSucceeded) {
-            Log.i(LOGTAG, "Failed to get minidump.");
-        }
-        String passedMinidumpPath = getIntent().getStringExtra(PASSED_MINI_DUMP_KEY);
-        File passedMinidumpFile = new File(passedMinidumpPath);
-        File pendingDir = new File(getFilesDir(), PENDING_SUFFIX);
-        pendingDir.mkdirs();
-        mPendingMinidumpFile = new File(pendingDir, passedMinidumpFile.getName());
-        moveFile(passedMinidumpFile, mPendingMinidumpFile);
-
-        File extrasFile = new File(passedMinidumpPath.replaceAll("\\.dmp", ".extra"));
-        mPendingExtrasFile = new File(pendingDir, extrasFile.getName());
-        moveFile(extrasFile, mPendingExtrasFile);
-
-        // Compute the minidump hash and generate the stack traces
-        computeMinidumpHash(mPendingExtrasFile, mPendingMinidumpFile);
-
-        try {
-            GeckoLoader.loadMozGlue(this);
-
-            if (!MinidumpAnalyzer.GenerateStacks(mPendingMinidumpFile.getPath(), /* fullStacks */ false)) {
-                Log.e(LOGTAG, "Could not generate stacks for this minidump: " + passedMinidumpPath);
-            }
-        } catch (UnsatisfiedLinkError e) {
-            Log.e(LOGTAG, "Could not load libmozglue.so, stacks for this crash won't be generated");
-        }
-
-        // Extract the annotations from the .extra file
-        mExtrasStringMap = new HashMap<String, String>();
-        readStringsFromFile(mPendingExtrasFile.getPath(), mExtrasStringMap);
-
-        try {
-            // Find the profile name and path. Since we don't have any other way of getting it within
-            // this context we extract it from the crash dump path.
-            final File profileDir = passedMinidumpFile.getParentFile().getParentFile();
-            final String profileName = getProfileName(profileDir);
-
-            if (profileName != null) {
-                // Extract the crash dump ID and telemetry client ID, we need profile access for the latter.
-                final String passedMinidumpName = passedMinidumpFile.getName();
-                // Strip the .dmp suffix from the minidump name to obtain the crash ID.
-                final String crashId = passedMinidumpName.substring(0, passedMinidumpName.length() - 4);
-                final GeckoProfile profile = GeckoProfile.get(this, profileName, profileDir);
-                final String clientId = profile.getClientId();
-
-                // Assemble and send the crash ping
-                final TelemetryCrashPingBuilder pingBuilder =
-                    new TelemetryCrashPingBuilder(crashId, clientId, mExtrasStringMap);
-                final TelemetryDispatcher dispatcher = new TelemetryDispatcher(profileDir.getPath(), profileName);
-                dispatcher.queuePingForUpload(this, pingBuilder);
-            }
-        } catch (GeckoProfileDirectories.NoMozillaDirectoryException | IOException e) {
-            Log.e(LOGTAG, "Cannot send the crash ping: ", e);
-        }
-
-        // Notify GeckoApp that we've crashed, so it can react appropriately during the next start.
-        try {
-            File crashFlag = new File(GeckoProfileDirectories.getMozillaDirectory(this), "CRASHED");
-            crashFlag.createNewFile();
-        } catch (GeckoProfileDirectories.NoMozillaDirectoryException | IOException e) {
-            Log.e(LOGTAG, "Cannot set crash flag: ", e);
-        }
-
-        final CheckBox allowContactCheckBox = (CheckBox) findViewById(R.id.allow_contact);
-        final CheckBox includeUrlCheckBox = (CheckBox) findViewById(R.id.include_url);
-        final CheckBox sendReportCheckBox = (CheckBox) findViewById(R.id.send_report);
-        final EditText commentsEditText = (EditText) findViewById(R.id.comment);
-        final EditText emailEditText = (EditText) findViewById(R.id.email);
-
-        // Load CrashReporter preferences to avoid redundant user input.
-        SharedPreferences prefs = GeckoSharedPrefs.forCrashReporter(this);
-        final boolean sendReport   = prefs.getBoolean(PREFS_SEND_REPORT, true);
-        final boolean includeUrl   = prefs.getBoolean(PREFS_INCLUDE_URL, false);
-        final boolean allowContact = prefs.getBoolean(PREFS_ALLOW_CONTACT, false);
-        final String contactEmail  = prefs.getString(PREFS_CONTACT_EMAIL, "");
-
-        allowContactCheckBox.setChecked(allowContact);
-        includeUrlCheckBox.setChecked(includeUrl);
-        sendReportCheckBox.setChecked(sendReport);
-        emailEditText.setText(contactEmail);
-
-        sendReportCheckBox.setOnCheckedChangeListener(new CheckBox.OnCheckedChangeListener() {
-            @Override
-            public void onCheckedChanged(CompoundButton checkbox, boolean isChecked) {
-                commentsEditText.setEnabled(isChecked);
-                commentsEditText.requestFocus();
-
-                includeUrlCheckBox.setEnabled(isChecked);
-                allowContactCheckBox.setEnabled(isChecked);
-                emailEditText.setEnabled(isChecked && allowContactCheckBox.isChecked());
-            }
-        });
-
-        allowContactCheckBox.setOnCheckedChangeListener(new CheckBox.OnCheckedChangeListener() {
-            @Override
-            public void onCheckedChanged(CompoundButton checkbox, boolean isChecked) {
-                // We need to check isEnabled() here because this listener is
-                // fired on rotation -- even when the checkbox is disabled.
-                emailEditText.setEnabled(checkbox.isEnabled() && isChecked);
-                emailEditText.requestFocus();
-            }
-        });
-
-        emailEditText.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                // Even if the email EditText is disabled, allow it to be
-                // clicked and focused.
-                if (sendReportCheckBox.isChecked() && !v.isEnabled()) {
-                    allowContactCheckBox.setChecked(true);
-                    v.setEnabled(true);
-                    v.requestFocus();
-                }
-            }
-        });
-    }
-
-    @Override
-    public void onBackPressed() {
-        AlertDialog.Builder builder = new AlertDialog.Builder(this);
-        builder.setMessage(R.string.crash_closing_alert);
-        builder.setNegativeButton(R.string.button_cancel, new DialogInterface.OnClickListener() {
-            @Override
-            public void onClick(DialogInterface dialog, int which) {
-                dialog.dismiss();
-            }
-        });
-        builder.setPositiveButton(R.string.button_ok, new DialogInterface.OnClickListener() {
-            @Override
-            public void onClick(DialogInterface dialog, int which) {
-                CrashReporter.this.finish();
-            }
-        });
-        builder.show();
-    }
-
-    private void backgroundSendReport() {
-        final CheckBox sendReportCheckbox = (CheckBox) findViewById(R.id.send_report);
-        if (!sendReportCheckbox.isChecked()) {
-            doFinish();
-            return;
-        }
-
-        // Persist settings to avoid redundant user input.
-        savePrefs();
-
-        mProgressDialog.show();
-        new Thread(new Runnable() {
-            @Override
-            public void run() {
-                sendReport(mPendingMinidumpFile, mExtrasStringMap, mPendingExtrasFile);
-            }
-        }, "CrashReporter Thread").start();
-    }
-
-    private void savePrefs() {
-        SharedPreferences.Editor editor = GeckoSharedPrefs.forCrashReporter(this).edit();
-
-        final boolean allowContact = ((CheckBox) findViewById(R.id.allow_contact)).isChecked();
-        final boolean includeUrl   = ((CheckBox) findViewById(R.id.include_url)).isChecked();
-        final boolean sendReport   = ((CheckBox) findViewById(R.id.send_report)).isChecked();
-        final String contactEmail  = ((EditText) findViewById(R.id.email)).getText().toString();
-
-        editor.putBoolean(PREFS_ALLOW_CONTACT, allowContact);
-        editor.putBoolean(PREFS_INCLUDE_URL, includeUrl);
-        editor.putBoolean(PREFS_SEND_REPORT, sendReport);
-        editor.putString(PREFS_CONTACT_EMAIL, contactEmail);
-
-        // A slight performance improvement via async apply() vs. blocking on commit().
-        editor.apply();
-    }
-
-    public void onCloseClick(View v) {  // bound via crash_reporter.xml
-        backgroundSendReport();
-    }
-
-    public void onRestartClick(View v) {  // bound via crash_reporter.xml
-        doRestart();
-        backgroundSendReport();
-    }
-
-    private String getProfileName(File profileDir) throws GeckoProfileDirectories.NoMozillaDirectoryException {
-        final File mozillaDir = GeckoProfileDirectories.getMozillaDirectory(this);
-        final INIParser parser = GeckoProfileDirectories.getProfilesINI(mozillaDir);
-        String profileName = null;
-
-        if (parser.getSections() != null) {
-            for (Enumeration<INISection> e = parser.getSections().elements(); e.hasMoreElements(); ) {
-                final INISection section = e.nextElement();
-                final String path = section.getStringProperty("Path");
-                final boolean isRelative = (section.getIntProperty("IsRelative") == 1);
-
-                if ((isRelative && path.equals(profileDir.getName())) ||
-                    path.equals(profileDir.getPath())) {
-                    profileName = section.getStringProperty("Name");
-                    break;
-                }
-            }
-        }
-
-        return profileName;
-    }
-
-
-    private void computeMinidumpHash(File extraFile, File minidump) {
-        try {
-            FileInputStream stream = new FileInputStream(minidump);
-            MessageDigest md = MessageDigest.getInstance("SHA-256");
-
-            try {
-                byte[] buffer = new byte[4096];
-                int readBytes;
-
-                while ((readBytes = stream.read(buffer)) != -1) {
-                    md.update(buffer, 0, readBytes);
-                }
-            } finally {
-              stream.close();
-            }
-
-            byte[] digest = md.digest();
-            StringBuilder hash = new StringBuilder(84);
-
-            hash.append("MinidumpSha256Hash=");
-
-            for (int i = 0; i < digest.length; i++) {
-              hash.append(Integer.toHexString((digest[i] & 0xf0) >> 4));
-              hash.append(Integer.toHexString(digest[i] & 0x0f));
-            }
-
-            hash.append('\n');
-
-            FileWriter writer = new FileWriter(extraFile, /* append */ true);
-
-            try {
-                writer.write(hash.toString());
-            } finally {
-                writer.close();
-            }
-        } catch (Exception e) {
-            Log.e(LOGTAG, "exception while computing the minidump hash: ", e);
-        }
-    }
-
-    private boolean readStringsFromFile(String filePath, Map<String, String> stringMap) {
-        try {
-            BufferedReader reader = new BufferedReader(new FileReader(filePath));
-            return readStringsFromReader(reader, stringMap);
-        } catch (Exception e) {
-            Log.e(LOGTAG, "exception while reading strings: ", e);
-            return false;
-        }
-    }
-
-    private boolean readStringsFromReader(BufferedReader reader, Map<String, String> stringMap) throws IOException {
-        String line;
-        while ((line = reader.readLine()) != null) {
-            int equalsPos = -1;
-            if ((equalsPos = line.indexOf('=')) != -1) {
-                String key = line.substring(0, equalsPos);
-                String val = unescape(line.substring(equalsPos + 1));
-                stringMap.put(key, val);
-            }
-        }
-        reader.close();
-        return true;
-    }
-
-    private String generateBoundary() {
-        // Generate some random numbers to fill out the boundary
-        int r0 = (int)(Integer.MAX_VALUE * Math.random());
-        int r1 = (int)(Integer.MAX_VALUE * Math.random());
-        return String.format("---------------------------%08X%08X", r0, r1);
-    }
-
-    private void sendPart(OutputStream os, String boundary, String name, String data) {
-        try {
-            os.write(("--" + boundary + "\r\n" +
-                      "Content-Disposition: form-data; name=\"" + name + "\"\r\n" +
-                      "\r\n" +
-                      data + "\r\n"
-                     ).getBytes());
-        } catch (Exception ex) {
-            Log.e(LOGTAG, "Exception when sending \"" + name + "\"", ex);
-        }
-    }
-
-    private void sendFile(OutputStream os, String boundary, String name, File file) throws IOException {
-        os.write(("--" + boundary + "\r\n" +
-                  "Content-Disposition: form-data; name=\"" + name + "\"; " +
-                  "filename=\"" + file.getName() + "\"\r\n" +
-                  "Content-Type: application/octet-stream\r\n" +
-                  "\r\n"
-                 ).getBytes());
-        FileChannel fc = new FileInputStream(file).getChannel();
-        fc.transferTo(0, fc.size(), Channels.newChannel(os));
-        fc.close();
-    }
-
-    private String readLogcat() {
-        final String crashReporterProc = " " + android.os.Process.myPid() + ' ';
-        BufferedReader br = null;
-        try {
-            // get at most the last 400 lines of logcat
-            Process proc = Runtime.getRuntime().exec(new String[] {
-                "logcat", "-v", "threadtime", "-t", "400", "-d", "*:D"
-            });
-            StringBuilder sb = new StringBuilder();
-            br = new BufferedReader(new InputStreamReader(proc.getInputStream()));
-            for (String s = br.readLine(); s != null; s = br.readLine()) {
-                if (s.contains(crashReporterProc)) {
-                    // Don't include logs from the crash reporter's process.
-                    break;
-                }
-                sb.append(s).append('\n');
-            }
-            return sb.toString();
-        } catch (Exception e) {
-            return "Unable to get logcat: " + e.toString();
-        } finally {
-            if (br != null) {
-                try {
-                    br.close();
-                } catch (Exception e) {
-                    // ignore
-                }
-            }
-        }
-    }
-
-    private void sendReport(File minidumpFile, Map<String, String> extras, File extrasFile) {
-        Log.i(LOGTAG, "sendReport: " + minidumpFile.getPath());
-        final CheckBox includeURLCheckbox = (CheckBox) findViewById(R.id.include_url);
-
-        String spec = extras.get(SERVER_URL_KEY);
-        if (spec == null) {
-            doFinish();
-            return;
-        }
-
-        Log.i(LOGTAG, "server url: " + spec);
-        try {
-            final URL url = new URL(URLDecoder.decode(spec, "UTF-8"));
-            final URI uri = new URI(url.getProtocol(), url.getUserInfo(),
-                                    url.getHost(), url.getPort(),
-                                    url.getPath(), url.getQuery(), url.getRef());
-            HttpURLConnection conn = (HttpURLConnection)ProxySelector.openConnectionWithProxy(uri);
-            conn.setRequestMethod("POST");
-            String boundary = generateBoundary();
-            conn.setDoOutput(true);
-            conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
-            conn.setRequestProperty("Content-Encoding", "gzip");
-
-            OutputStream os = new GZIPOutputStream(conn.getOutputStream());
-            for (String key : extras.keySet()) {
-                if (key.equals(PAGE_URL_KEY)) {
-                    if (includeURLCheckbox.isChecked())
-                        sendPart(os, boundary, key, extras.get(key));
-                } else if (!key.equals(SERVER_URL_KEY) && !key.equals(NOTES_KEY)) {
-                    sendPart(os, boundary, key, extras.get(key));
-                }
-            }
-
-            // Add some extra information to notes so its displayed by
-            // crash-stats.mozilla.org. Remove this when bug 607942 is fixed.
-            StringBuilder sb = new StringBuilder();
-            sb.append(extras.containsKey(NOTES_KEY) ? extras.get(NOTES_KEY) + "\n" : "");
-            sb.append(Build.MANUFACTURER).append(' ')
-              .append(Build.MODEL).append('\n')
-              .append(Build.FINGERPRINT);
-            sendPart(os, boundary, NOTES_KEY, sb.toString());
-
-            sendPart(os, boundary, "Android_Manufacturer", Build.MANUFACTURER);
-            sendPart(os, boundary, "Android_Model", Build.MODEL);
-            sendPart(os, boundary, "Android_Board", Build.BOARD);
-            sendPart(os, boundary, "Android_Brand", Build.BRAND);
-            sendPart(os, boundary, "Android_Device", Build.DEVICE);
-            sendPart(os, boundary, "Android_Display", Build.DISPLAY);
-            sendPart(os, boundary, "Android_Fingerprint", Build.FINGERPRINT);
-            sendPart(os, boundary, "Android_APP_ABI", AppConstants.MOZ_APP_ABI);
-            sendPart(os, boundary, "Android_CPU_ABI", Build.CPU_ABI);
-            sendPart(os, boundary, "Android_MIN_SDK", Integer.toString(AppConstants.Versions.MIN_SDK_VERSION));
-            sendPart(os, boundary, "Android_MAX_SDK", Integer.toString(AppConstants.Versions.MAX_SDK_VERSION));
-            try {
-                sendPart(os, boundary, "Android_CPU_ABI2", Build.CPU_ABI2);
-                sendPart(os, boundary, "Android_Hardware", Build.HARDWARE);
-            } catch (Exception ex) {
-                Log.e(LOGTAG, "Exception while sending SDK version 8 keys", ex);
-            }
-            sendPart(os, boundary, "Android_Version",  Build.VERSION.SDK_INT + " (" + Build.VERSION.CODENAME + ")");
-            if (Versions.feature16Plus && includeURLCheckbox.isChecked()) {
-                sendPart(os, boundary, "Android_Logcat", readLogcat());
-            }
-
-            String comment = ((EditText) findViewById(R.id.comment)).getText().toString();
-            if (!TextUtils.isEmpty(comment)) {
-                sendPart(os, boundary, "Comments", comment);
-            }
-
-            if (((CheckBox) findViewById(R.id.allow_contact)).isChecked()) {
-                String email = ((EditText) findViewById(R.id.email)).getText().toString();
-                sendPart(os, boundary, "Email", email);
-            }
-
-            sendPart(os, boundary, PASSED_MINI_DUMP_SUCCESS_KEY, mMinidumpSucceeded ? "True" : "False");
-            sendFile(os, boundary, MINI_DUMP_PATH_KEY, minidumpFile);
-            os.write(("\r\n--" + boundary + "--\r\n").getBytes());
-            os.flush();
-            os.close();
-            BufferedReader br = new BufferedReader(
-                new InputStreamReader(conn.getInputStream()));
-            HashMap<String, String>  responseMap = new HashMap<String, String>();
-            readStringsFromReader(br, responseMap);
-
-            if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
-                File submittedDir = new File(getFilesDir(),
-                                             SUBMITTED_SUFFIX);
-                submittedDir.mkdirs();
-                minidumpFile.delete();
-                extrasFile.delete();
-                String crashid = responseMap.get("CrashID");
-                File file = new File(submittedDir, crashid + ".txt");
-                FileOutputStream fos = new FileOutputStream(file);
-                fos.write("Crash ID: ".getBytes());
-                fos.write(crashid.getBytes());
-                fos.close();
-            } else {
-                Log.i(LOGTAG, "Received failure HTTP response code from server: " + conn.getResponseCode());
-            }
-        } catch (IOException e) {
-            Log.e(LOGTAG, "exception during send: ", e);
-        } catch (URISyntaxException e) {
-            Log.e(LOGTAG, "exception during new URI: ", e);
-        }
-
-        doFinish();
-    }
-
-    private void doRestart() {
-        try {
-            String action = "android.intent.action.MAIN";
-            Intent intent = new Intent(action);
-            intent.setClassName(AppConstants.ANDROID_PACKAGE_NAME,
-                                AppConstants.MOZ_ANDROID_BROWSER_INTENT_CLASS);
-            intent.putExtra("didRestart", true);
-            Log.i(LOGTAG, intent.toString());
-            startActivity(intent);
-        } catch (Exception e) {
-            Log.e(LOGTAG, "error while trying to restart", e);
-        }
-    }
-
-    private String unescape(String string) {
-        return string.replaceAll("\\\\\\\\", "\\").replaceAll("\\\\n", "\n").replaceAll("\\\\t", "\t");
-    }
-}
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/CrashReporterActivity.java
@@ -0,0 +1,598 @@
+/* -*- Mode: Java; tab-width: 20; indent-tabs-mode: nil; -*-
+ * 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/. */
+
+package org.mozilla.gecko;
+
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.nio.channels.Channels;
+import java.nio.channels.FileChannel;
+import java.security.MessageDigest;
+import java.util.zip.GZIPOutputStream;
+
+import org.mozilla.gecko.AppConstants.Versions;
+import org.mozilla.gecko.mozglue.GeckoLoader;
+import org.mozilla.gecko.mozglue.MinidumpAnalyzer;
+import org.mozilla.gecko.telemetry.pingbuilders.TelemetryCrashPingBuilder;
+import org.mozilla.gecko.telemetry.TelemetryDispatcher;
+import org.mozilla.gecko.util.INIParser;
+import org.mozilla.gecko.util.INISection;
+import org.mozilla.gecko.util.ProxySelector;
+
+import android.annotation.SuppressLint;
+import android.app.AlertDialog;
+import android.app.ProgressDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.v7.app.AppCompatActivity;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.EditText;
+
+// Registered: This activity is only registered in the manifest if MOZ_CRASHREPORTER is set.
+// CutPasteId: This lint is not worth fixing.  To fix it, cache all the findViewById results.
+@SuppressLint("Registered,CutPasteId")
+public class CrashReporterActivity extends AppCompatActivity
+{
+    private static final String LOGTAG = "GeckoCrashReporter";
+
+    private static final String PASSED_MINI_DUMP_KEY = "minidumpPath";
+    private static final String PASSED_MINI_DUMP_SUCCESS_KEY = "minidumpSuccess";
+    private static final String MINI_DUMP_PATH_KEY = "upload_file_minidump";
+    private static final String PAGE_URL_KEY = "URL";
+    private static final String NOTES_KEY = "Notes";
+    private static final String SERVER_URL_KEY = "ServerURL";
+
+    private static final String CRASH_REPORT_SUFFIX = "/mozilla/Crash Reports/";
+    private static final String PENDING_SUFFIX = CRASH_REPORT_SUFFIX + "pending";
+    private static final String SUBMITTED_SUFFIX = CRASH_REPORT_SUFFIX + "submitted";
+
+    private static final String PREFS_SEND_REPORT   = "sendReport";
+    private static final String PREFS_INCLUDE_URL   = "includeUrl";
+    private static final String PREFS_ALLOW_CONTACT = "allowContact";
+    private static final String PREFS_CONTACT_EMAIL = "contactEmail";
+
+    private Handler mHandler;
+    private ProgressDialog mProgressDialog;
+    private File mPendingMinidumpFile;
+    private File mPendingExtrasFile;
+    private HashMap<String, String> mExtrasStringMap;
+    private boolean mMinidumpSucceeded;
+
+    private boolean moveFile(File inFile, File outFile) {
+        Log.i(LOGTAG, "moving " + inFile + " to " + outFile);
+        if (inFile.renameTo(outFile))
+            return true;
+        try {
+            outFile.createNewFile();
+            Log.i(LOGTAG, "couldn't rename minidump file");
+            // so copy it instead
+            FileChannel inChannel = new FileInputStream(inFile).getChannel();
+            FileChannel outChannel = new FileOutputStream(outFile).getChannel();
+            long transferred = inChannel.transferTo(0, inChannel.size(), outChannel);
+            inChannel.close();
+            outChannel.close();
+
+            if (transferred > 0)
+                inFile.delete();
+        } catch (Exception e) {
+            Log.e(LOGTAG, "exception while copying minidump file: ", e);
+            return false;
+        }
+        return true;
+    }
+
+    private void doFinish() {
+        if (mHandler != null) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    finish();
+                }
+            });
+        }
+    }
+
+    @Override
+    public void finish() {
+        try {
+            if (mProgressDialog.isShowing()) {
+                mProgressDialog.dismiss();
+            }
+        } catch (Exception e) {
+            Log.e(LOGTAG, "exception while closing progress dialog: ", e);
+        }
+        super.finish();
+    }
+
+    @Override
+    @SuppressLint("WrongThread") // We don't have a worker thread for the TelemetryDispatcher
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        // mHandler is created here so runnables can be run on the main thread
+        mHandler = new Handler();
+        setContentView(R.layout.crash_reporter);
+        mProgressDialog = new ProgressDialog(this);
+        mProgressDialog.setMessage(getString(R.string.sending_crash_report));
+
+        mMinidumpSucceeded = getIntent().getBooleanExtra(PASSED_MINI_DUMP_SUCCESS_KEY, false);
+        if (!mMinidumpSucceeded) {
+            Log.i(LOGTAG, "Failed to get minidump.");
+        }
+        String passedMinidumpPath = getIntent().getStringExtra(PASSED_MINI_DUMP_KEY);
+        File passedMinidumpFile = new File(passedMinidumpPath);
+        File pendingDir = new File(getFilesDir(), PENDING_SUFFIX);
+        pendingDir.mkdirs();
+        mPendingMinidumpFile = new File(pendingDir, passedMinidumpFile.getName());
+        moveFile(passedMinidumpFile, mPendingMinidumpFile);
+
+        File extrasFile = new File(passedMinidumpPath.replaceAll("\\.dmp", ".extra"));
+        mPendingExtrasFile = new File(pendingDir, extrasFile.getName());
+        moveFile(extrasFile, mPendingExtrasFile);
+
+        // Compute the minidump hash and generate the stack traces
+        computeMinidumpHash(mPendingExtrasFile, mPendingMinidumpFile);
+
+        try {
+            GeckoLoader.loadMozGlue(this);
+
+            if (!MinidumpAnalyzer.GenerateStacks(mPendingMinidumpFile.getPath(), /* fullStacks */ false)) {
+                Log.e(LOGTAG, "Could not generate stacks for this minidump: " + passedMinidumpPath);
+            }
+        } catch (UnsatisfiedLinkError e) {
+            Log.e(LOGTAG, "Could not load libmozglue.so, stacks for this crash won't be generated");
+        }
+
+        // Extract the annotations from the .extra file
+        mExtrasStringMap = new HashMap<String, String>();
+        readStringsFromFile(mPendingExtrasFile.getPath(), mExtrasStringMap);
+
+        try {
+            // Find the profile name and path. Since we don't have any other way of getting it within
+            // this context we extract it from the crash dump path.
+            final File profileDir = passedMinidumpFile.getParentFile().getParentFile();
+            final String profileName = getProfileName(profileDir);
+
+            if (profileName != null) {
+                // Extract the crash dump ID and telemetry client ID, we need profile access for the latter.
+                final String passedMinidumpName = passedMinidumpFile.getName();
+                // Strip the .dmp suffix from the minidump name to obtain the crash ID.
+                final String crashId = passedMinidumpName.substring(0, passedMinidumpName.length() - 4);
+                final GeckoProfile profile = GeckoProfile.get(this, profileName, profileDir);
+                final String clientId = profile.getClientId();
+
+                // Assemble and send the crash ping
+                final TelemetryCrashPingBuilder pingBuilder =
+                    new TelemetryCrashPingBuilder(crashId, clientId, mExtrasStringMap);
+                final TelemetryDispatcher dispatcher = new TelemetryDispatcher(profileDir.getPath(), profileName);
+                dispatcher.queuePingForUpload(this, pingBuilder);
+            }
+        } catch (GeckoProfileDirectories.NoMozillaDirectoryException | IOException e) {
+            Log.e(LOGTAG, "Cannot send the crash ping: ", e);
+        }
+
+        // Notify GeckoApp that we've crashed, so it can react appropriately during the next start.
+        try {
+            File crashFlag = new File(GeckoProfileDirectories.getMozillaDirectory(this), "CRASHED");
+            crashFlag.createNewFile();
+        } catch (GeckoProfileDirectories.NoMozillaDirectoryException | IOException e) {
+            Log.e(LOGTAG, "Cannot set crash flag: ", e);
+        }
+
+        final CheckBox allowContactCheckBox = (CheckBox) findViewById(R.id.allow_contact);
+        final CheckBox includeUrlCheckBox = (CheckBox) findViewById(R.id.include_url);
+        final CheckBox sendReportCheckBox = (CheckBox) findViewById(R.id.send_report);
+        final EditText commentsEditText = (EditText) findViewById(R.id.comment);
+        final EditText emailEditText = (EditText) findViewById(R.id.email);
+
+        // Load CrashReporterActivity preferences to avoid redundant user input.
+        SharedPreferences prefs = GeckoSharedPrefs.forCrashReporter(this);
+        final boolean sendReport   = prefs.getBoolean(PREFS_SEND_REPORT, true);
+        final boolean includeUrl   = prefs.getBoolean(PREFS_INCLUDE_URL, false);
+        final boolean allowContact = prefs.getBoolean(PREFS_ALLOW_CONTACT, false);
+        final String contactEmail  = prefs.getString(PREFS_CONTACT_EMAIL, "");
+
+        allowContactCheckBox.setChecked(allowContact);
+        includeUrlCheckBox.setChecked(includeUrl);
+        sendReportCheckBox.setChecked(sendReport);
+        emailEditText.setText(contactEmail);
+
+        sendReportCheckBox.setOnCheckedChangeListener(new CheckBox.OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(CompoundButton checkbox, boolean isChecked) {
+                commentsEditText.setEnabled(isChecked);
+                commentsEditText.requestFocus();
+
+                includeUrlCheckBox.setEnabled(isChecked);
+                allowContactCheckBox.setEnabled(isChecked);
+                emailEditText.setEnabled(isChecked && allowContactCheckBox.isChecked());
+            }
+        });
+
+        allowContactCheckBox.setOnCheckedChangeListener(new CheckBox.OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(CompoundButton checkbox, boolean isChecked) {
+                // We need to check isEnabled() here because this listener is
+                // fired on rotation -- even when the checkbox is disabled.
+                emailEditText.setEnabled(checkbox.isEnabled() && isChecked);
+                emailEditText.requestFocus();
+            }
+        });
+
+        emailEditText.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                // Even if the email EditText is disabled, allow it to be
+                // clicked and focused.
+                if (sendReportCheckBox.isChecked() && !v.isEnabled()) {
+                    allowContactCheckBox.setChecked(true);
+                    v.setEnabled(true);
+                    v.requestFocus();
+                }
+            }
+        });
+    }
+
+    @Override
+    public void onBackPressed() {
+        AlertDialog.Builder builder = new AlertDialog.Builder(this);
+        builder.setMessage(R.string.crash_closing_alert);
+        builder.setNegativeButton(R.string.button_cancel, new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                dialog.dismiss();
+            }
+        });
+        builder.setPositiveButton(R.string.button_ok, new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                CrashReporterActivity.this.finish();
+            }
+        });
+        builder.show();
+    }
+
+    private void backgroundSendReport() {
+        final CheckBox sendReportCheckbox = (CheckBox) findViewById(R.id.send_report);
+        if (!sendReportCheckbox.isChecked()) {
+            doFinish();
+            return;
+        }
+
+        // Persist settings to avoid redundant user input.
+        savePrefs();
+
+        mProgressDialog.show();
+        new Thread(new Runnable() {
+            @Override
+            public void run() {
+                sendReport(mPendingMinidumpFile, mExtrasStringMap, mPendingExtrasFile);
+            }
+        }, "CrashReporterActivity Thread").start();
+    }
+
+    private void savePrefs() {
+        SharedPreferences.Editor editor = GeckoSharedPrefs.forCrashReporter(this).edit();
+
+        final boolean allowContact = ((CheckBox) findViewById(R.id.allow_contact)).isChecked();
+        final boolean includeUrl   = ((CheckBox) findViewById(R.id.include_url)).isChecked();
+        final boolean sendReport   = ((CheckBox) findViewById(R.id.send_report)).isChecked();
+        final String contactEmail  = ((EditText) findViewById(R.id.email)).getText().toString();
+
+        editor.putBoolean(PREFS_ALLOW_CONTACT, allowContact);
+        editor.putBoolean(PREFS_INCLUDE_URL, includeUrl);
+        editor.putBoolean(PREFS_SEND_REPORT, sendReport);
+        editor.putString(PREFS_CONTACT_EMAIL, contactEmail);
+
+        // A slight performance improvement via async apply() vs. blocking on commit().
+        editor.apply();
+    }
+
+    public void onCloseClick(View v) {  // bound via crash_reporter.xml
+        backgroundSendReport();
+    }
+
+    public void onRestartClick(View v) {  // bound via crash_reporter.xml
+        doRestart();
+        backgroundSendReport();
+    }
+
+    private String getProfileName(File profileDir) throws GeckoProfileDirectories.NoMozillaDirectoryException {
+        final File mozillaDir = GeckoProfileDirectories.getMozillaDirectory(this);
+        final INIParser parser = GeckoProfileDirectories.getProfilesINI(mozillaDir);
+        String profileName = null;
+
+        if (parser.getSections() != null) {
+            for (Enumeration<INISection> e = parser.getSections().elements(); e.hasMoreElements(); ) {
+                final INISection section = e.nextElement();
+                final String path = section.getStringProperty("Path");
+                final boolean isRelative = (section.getIntProperty("IsRelative") == 1);
+
+                if ((isRelative && path.equals(profileDir.getName())) ||
+                    path.equals(profileDir.getPath())) {
+                    profileName = section.getStringProperty("Name");
+                    break;
+                }
+            }
+        }
+
+        return profileName;
+    }
+
+
+    private void computeMinidumpHash(File extraFile, File minidump) {
+        try {
+            FileInputStream stream = new FileInputStream(minidump);
+            MessageDigest md = MessageDigest.getInstance("SHA-256");
+
+            try {
+                byte[] buffer = new byte[4096];
+                int readBytes;
+
+                while ((readBytes = stream.read(buffer)) != -1) {
+                    md.update(buffer, 0, readBytes);
+                }
+            } finally {
+              stream.close();
+            }
+
+            byte[] digest = md.digest();
+            StringBuilder hash = new StringBuilder(84);
+
+            hash.append("MinidumpSha256Hash=");
+
+            for (int i = 0; i < digest.length; i++) {
+              hash.append(Integer.toHexString((digest[i] & 0xf0) >> 4));
+              hash.append(Integer.toHexString(digest[i] & 0x0f));
+            }
+
+            hash.append('\n');
+
+            FileWriter writer = new FileWriter(extraFile, /* append */ true);
+
+            try {
+                writer.write(hash.toString());
+            } finally {
+                writer.close();
+            }
+        } catch (Exception e) {
+            Log.e(LOGTAG, "exception while computing the minidump hash: ", e);
+        }
+    }
+
+    private boolean readStringsFromFile(String filePath, Map<String, String> stringMap) {
+        try {
+            BufferedReader reader = new BufferedReader(new FileReader(filePath));
+            return readStringsFromReader(reader, stringMap);
+        } catch (Exception e) {
+            Log.e(LOGTAG, "exception while reading strings: ", e);
+            return false;
+        }
+    }
+
+    private boolean readStringsFromReader(BufferedReader reader, Map<String, String> stringMap) throws IOException {
+        String line;
+        while ((line = reader.readLine()) != null) {
+            int equalsPos = -1;
+            if ((equalsPos = line.indexOf('=')) != -1) {
+                String key = line.substring(0, equalsPos);
+                String val = unescape(line.substring(equalsPos + 1));
+                stringMap.put(key, val);
+            }
+        }
+        reader.close();
+        return true;
+    }
+
+    private String generateBoundary() {
+        // Generate some random numbers to fill out the boundary
+        int r0 = (int)(Integer.MAX_VALUE * Math.random());
+        int r1 = (int)(Integer.MAX_VALUE * Math.random());
+        return String.format("---------------------------%08X%08X", r0, r1);
+    }
+
+    private void sendPart(OutputStream os, String boundary, String name, String data) {
+        try {
+            os.write(("--" + boundary + "\r\n" +
+                      "Content-Disposition: form-data; name=\"" + name + "\"\r\n" +
+                      "\r\n" +
+                      data + "\r\n"
+                     ).getBytes());
+        } catch (Exception ex) {
+            Log.e(LOGTAG, "Exception when sending \"" + name + "\"", ex);
+        }
+    }
+
+    private void sendFile(OutputStream os, String boundary, String name, File file) throws IOException {
+        os.write(("--" + boundary + "\r\n" +
+                  "Content-Disposition: form-data; name=\"" + name + "\"; " +
+                  "filename=\"" + file.getName() + "\"\r\n" +
+                  "Content-Type: application/octet-stream\r\n" +
+                  "\r\n"
+                 ).getBytes());
+        FileChannel fc = new FileInputStream(file).getChannel();
+        fc.transferTo(0, fc.size(), Channels.newChannel(os));
+        fc.close();
+    }
+
+    private String readLogcat() {
+        final String crashReporterProc = " " + android.os.Process.myPid() + ' ';
+        BufferedReader br = null;
+        try {
+            // get at most the last 400 lines of logcat
+            Process proc = Runtime.getRuntime().exec(new String[] {
+                "logcat", "-v", "threadtime", "-t", "400", "-d", "*:D"
+            });
+            StringBuilder sb = new StringBuilder();
+            br = new BufferedReader(new InputStreamReader(proc.getInputStream()));
+            for (String s = br.readLine(); s != null; s = br.readLine()) {
+                if (s.contains(crashReporterProc)) {
+                    // Don't include logs from the crash reporter's process.
+                    break;
+                }
+                sb.append(s).append('\n');
+            }
+            return sb.toString();
+        } catch (Exception e) {
+            return "Unable to get logcat: " + e.toString();
+        } finally {
+            if (br != null) {
+                try {
+                    br.close();
+                } catch (Exception e) {
+                    // ignore
+                }
+            }
+        }
+    }
+
+    private void sendReport(File minidumpFile, Map<String, String> extras, File extrasFile) {
+        Log.i(LOGTAG, "sendReport: " + minidumpFile.getPath());
+        final CheckBox includeURLCheckbox = (CheckBox) findViewById(R.id.include_url);
+
+        String spec = extras.get(SERVER_URL_KEY);
+        if (spec == null) {
+            doFinish();
+            return;
+        }
+
+        Log.i(LOGTAG, "server url: " + spec);
+        try {
+            final URL url = new URL(URLDecoder.decode(spec, "UTF-8"));
+            final URI uri = new URI(url.getProtocol(), url.getUserInfo(),
+                                    url.getHost(), url.getPort(),
+                                    url.getPath(), url.getQuery(), url.getRef());
+            HttpURLConnection conn = (HttpURLConnection)ProxySelector.openConnectionWithProxy(uri);
+            conn.setRequestMethod("POST");
+            String boundary = generateBoundary();
+            conn.setDoOutput(true);
+            conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
+            conn.setRequestProperty("Content-Encoding", "gzip");
+
+            OutputStream os = new GZIPOutputStream(conn.getOutputStream());
+            for (String key : extras.keySet()) {
+                if (key.equals(PAGE_URL_KEY)) {
+                    if (includeURLCheckbox.isChecked())
+                        sendPart(os, boundary, key, extras.get(key));
+                } else if (!key.equals(SERVER_URL_KEY) && !key.equals(NOTES_KEY)) {
+                    sendPart(os, boundary, key, extras.get(key));
+                }
+            }
+
+            // Add some extra information to notes so its displayed by
+            // crash-stats.mozilla.org. Remove this when bug 607942 is fixed.
+            StringBuilder sb = new StringBuilder();
+            sb.append(extras.containsKey(NOTES_KEY) ? extras.get(NOTES_KEY) + "\n" : "");
+            sb.append(Build.MANUFACTURER).append(' ')
+              .append(Build.MODEL).append('\n')
+              .append(Build.FINGERPRINT);
+            sendPart(os, boundary, NOTES_KEY, sb.toString());
+
+            sendPart(os, boundary, "Android_Manufacturer", Build.MANUFACTURER);
+            sendPart(os, boundary, "Android_Model", Build.MODEL);
+            sendPart(os, boundary, "Android_Board", Build.BOARD);
+            sendPart(os, boundary, "Android_Brand", Build.BRAND);
+            sendPart(os, boundary, "Android_Device", Build.DEVICE);
+            sendPart(os, boundary, "Android_Display", Build.DISPLAY);
+            sendPart(os, boundary, "Android_Fingerprint", Build.FINGERPRINT);
+            sendPart(os, boundary, "Android_APP_ABI", AppConstants.MOZ_APP_ABI);
+            sendPart(os, boundary, "Android_CPU_ABI", Build.CPU_ABI);
+            sendPart(os, boundary, "Android_MIN_SDK", Integer.toString(AppConstants.Versions.MIN_SDK_VERSION));
+            sendPart(os, boundary, "Android_MAX_SDK", Integer.toString(AppConstants.Versions.MAX_SDK_VERSION));
+            try {
+                sendPart(os, boundary, "Android_CPU_ABI2", Build.CPU_ABI2);
+                sendPart(os, boundary, "Android_Hardware", Build.HARDWARE);
+            } catch (Exception ex) {
+                Log.e(LOGTAG, "Exception while sending SDK version 8 keys", ex);
+            }
+            sendPart(os, boundary, "Android_Version",  Build.VERSION.SDK_INT + " (" + Build.VERSION.CODENAME + ")");
+            if (Versions.feature16Plus && includeURLCheckbox.isChecked()) {
+                sendPart(os, boundary, "Android_Logcat", readLogcat());
+            }
+
+            String comment = ((EditText) findViewById(R.id.comment)).getText().toString();
+            if (!TextUtils.isEmpty(comment)) {
+                sendPart(os, boundary, "Comments", comment);
+            }
+
+            if (((CheckBox) findViewById(R.id.allow_contact)).isChecked()) {
+                String email = ((EditText) findViewById(R.id.email)).getText().toString();
+                sendPart(os, boundary, "Email", email);
+            }
+
+            sendPart(os, boundary, PASSED_MINI_DUMP_SUCCESS_KEY, mMinidumpSucceeded ? "True" : "False");
+            sendFile(os, boundary, MINI_DUMP_PATH_KEY, minidumpFile);
+            os.write(("\r\n--" + boundary + "--\r\n").getBytes());
+            os.flush();
+            os.close();
+            BufferedReader br = new BufferedReader(
+                new InputStreamReader(conn.getInputStream()));
+            HashMap<String, String>  responseMap = new HashMap<String, String>();
+            readStringsFromReader(br, responseMap);
+
+            if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
+                File submittedDir = new File(getFilesDir(),
+                                             SUBMITTED_SUFFIX);
+                submittedDir.mkdirs();
+                minidumpFile.delete();
+                extrasFile.delete();
+                String crashid = responseMap.get("CrashID");
+                File file = new File(submittedDir, crashid + ".txt");
+                FileOutputStream fos = new FileOutputStream(file);
+                fos.write("Crash ID: ".getBytes());
+                fos.write(crashid.getBytes());
+                fos.close();
+            } else {
+                Log.i(LOGTAG, "Received failure HTTP response code from server: " + conn.getResponseCode());
+            }
+        } catch (IOException e) {
+            Log.e(LOGTAG, "exception during send: ", e);
+        } catch (URISyntaxException e) {
+            Log.e(LOGTAG, "exception during new URI: ", e);
+        }
+
+        doFinish();
+    }
+
+    private void doRestart() {
+        try {
+            String action = "android.intent.action.MAIN";
+            Intent intent = new Intent(action);
+            intent.setClassName(AppConstants.ANDROID_PACKAGE_NAME,
+                                AppConstants.MOZ_ANDROID_BROWSER_INTENT_CLASS);
+            intent.putExtra("didRestart", true);
+            Log.i(LOGTAG, intent.toString());
+            startActivity(intent);
+        } catch (Exception e) {
+            Log.e(LOGTAG, "error while trying to restart", e);
+        }
+    }
+
+    private String unescape(String string) {
+        return string.replaceAll("\\\\\\\\", "\\").replaceAll("\\\\n", "\n").replaceAll("\\\\t", "\t");
+    }
+}
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
@@ -118,17 +118,16 @@ public abstract class GeckoApp extends G
                                           ViewTreeObserver.OnGlobalLayoutListener {
 
     private static final String LOGTAG = "GeckoApp";
     private static final long ONE_DAY_MS = TimeUnit.MILLISECONDS.convert(1, TimeUnit.DAYS);
 
     public static final String ACTION_ALERT_CALLBACK       = "org.mozilla.gecko.ALERT_CALLBACK";
     public static final String ACTION_HOMESCREEN_SHORTCUT  = "org.mozilla.gecko.BOOKMARK";
     public static final String ACTION_WEBAPP               = "org.mozilla.gecko.WEBAPP";
-    public static final String ACTION_DEBUG                = "org.mozilla.gecko.DEBUG";
     public static final String ACTION_LAUNCH_SETTINGS      = "org.mozilla.gecko.SETTINGS";
     public static final String ACTION_LOAD                 = "org.mozilla.gecko.LOAD";
     public static final String ACTION_INIT_PW              = "org.mozilla.gecko.INIT_PW";
     public static final String ACTION_SWITCH_TAB           = "org.mozilla.gecko.SWITCH_TAB";
     public static final String ACTION_SHUTDOWN             = "org.mozilla.gecko.SHUTDOWN";
 
     public static final String INTENT_REGISTER_STUMBLER_LISTENER = "org.mozilla.gecko.STUMBLER_REGISTER_LOCAL_LISTENER";
 
@@ -993,62 +992,59 @@ public abstract class GeckoApp extends G
         // restart, and will be propagated to Gecko accordingly, so there's
         // no need to touch that here.
         if (BrowserLocaleManager.getInstance().systemLocaleDidChange()) {
             Log.i(LOGTAG, "System locale changed. Restarting.");
             finishAndShutdown(/* restart */ true);
             return;
         }
 
+        // To prevent races, register startup events before launching the Gecko thread.
+        EventDispatcher.getInstance().registerGeckoThreadListener(this,
+                "Gecko:Ready",
+                null);
+
+        EventDispatcher.getInstance().registerUiThreadListener(this,
+                "Gecko:CorruptAPK",
+                "Update:Check",
+                "Update:Download",
+                "Update:Install",
+                null);
+
         if (sAlreadyLoaded) {
             // This happens when the GeckoApp activity is destroyed by Android
             // without killing the entire application (see Bug 769269).
             // In case we have multiple GeckoApp-based activities, this can
             // also happen if we're not the first activity to run within a session.
             mIsRestoringActivity = true;
             Telemetry.addToHistogram("FENNEC_RESTORING_ACTIVITY", 1);
-
         } else {
             final String action = intent.getAction();
             final String[] args = GeckoApplication.getDefaultGeckoArgs();
-            final int flags = ACTION_DEBUG.equals(action) ? GeckoThread.FLAG_DEBUGGING : 0;
 
             sAlreadyLoaded = true;
-            GeckoThread.initMainProcess(/* profile */ null, args,
-                                        intent.getExtras(), flags);
+            if (GeckoApplication.getRuntime() == null) {
+                GeckoApplication.createRuntime(this, intent);
+            }
 
             // Speculatively pre-fetch the profile in the background.
             ThreadUtils.postToBackgroundThread(new Runnable() {
                 @Override
                 public void run() {
                     getProfile();
                 }
             });
 
             final String uri = getURIFromIntent(intent);
             if (!TextUtils.isEmpty(uri)) {
                 // Start a speculative connection as soon as Gecko loads.
                 GeckoThread.speculativeConnect(uri);
             }
         }
 
-        // To prevent races, register startup events before launching the Gecko thread.
-        EventDispatcher.getInstance().registerGeckoThreadListener(this,
-            "Gecko:Ready",
-            null);
-
-        EventDispatcher.getInstance().registerUiThreadListener(this,
-            "Gecko:CorruptAPK",
-            "Update:Check",
-            "Update:Download",
-            "Update:Install",
-            null);
-
-        GeckoThread.launch();
-
         Bundle stateBundle = IntentUtils.getBundleExtraSafe(getIntent(), EXTRA_STATE_BUNDLE);
         if (stateBundle != null) {
             // Use the state bundle if it was given as an intent extra. This is
             // only intended to be used internally via Robocop, so a boolean
             // is read from a private shared pref to prevent other apps from
             // injecting states.
             final SharedPreferences prefs = getSharedPreferences();
             if (prefs.getBoolean(PREFS_ALLOW_STATE_BUNDLE, false)) {
@@ -1076,17 +1072,17 @@ public abstract class GeckoApp extends G
         session.getSettings().setString(GeckoSessionSettings.CHROME_URI,
                                         "chrome://browser/content/browser.xul");
         session.setContentDelegate(this);
 
         // If the view already has a session, we need to ensure it is closed.
         if (mLayerView.getSession() != null) {
             mLayerView.getSession().close();
         }
-        mLayerView.setSession(session, GeckoRuntime.getDefault(this));
+        mLayerView.setSession(session, GeckoApplication.getRuntime());
         mLayerView.setOverScrollMode(View.OVER_SCROLL_NEVER);
 
         getAppEventDispatcher().registerGeckoThreadListener(this,
             "Locale:Set",
             "PrivateBrowsing:Data",
             null);
 
         getAppEventDispatcher().registerUiThreadListener(this,
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java
@@ -8,20 +8,22 @@ import android.Manifest;
 import android.app.Activity;
 import android.app.Application;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.net.Uri;
+import android.os.Bundle;
 import android.os.Environment;
 import android.os.Process;
 import android.os.SystemClock;
 import android.provider.MediaStore;
+import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.design.widget.Snackbar;
 import android.text.TextUtils;
 import android.util.Base64;
 import android.util.Log;
 
 import com.squareup.leakcanary.LeakCanary;
 import com.squareup.leakcanary.RefWatcher;
@@ -33,43 +35,47 @@ import org.mozilla.gecko.db.LocalBrowser
 import org.mozilla.gecko.distribution.Distribution;
 import org.mozilla.gecko.home.HomePanelsManager;
 import org.mozilla.gecko.icons.IconCallback;
 import org.mozilla.gecko.icons.IconResponse;
 import org.mozilla.gecko.icons.Icons;
 import org.mozilla.gecko.lwt.LightweightTheme;
 import org.mozilla.gecko.mdns.MulticastDNSManager;
 import org.mozilla.gecko.media.AudioFocusAgent;
+import org.mozilla.gecko.mozglue.SafeIntent;
 import org.mozilla.gecko.notifications.NotificationClient;
 import org.mozilla.gecko.notifications.NotificationHelper;
 import org.mozilla.gecko.permissions.Permissions;
 import org.mozilla.gecko.preferences.DistroSharedPrefsImport;
 import org.mozilla.gecko.pwa.PwaUtils;
 import org.mozilla.gecko.telemetry.TelemetryBackgroundReceiver;
 import org.mozilla.gecko.util.ActivityResultHandler;
 import org.mozilla.gecko.util.BitmapUtils;
 import org.mozilla.gecko.util.BundleEventListener;
 import org.mozilla.gecko.util.EventCallback;
 import org.mozilla.gecko.util.GeckoBundle;
 import org.mozilla.gecko.util.HardwareUtils;
 import org.mozilla.gecko.util.PRNGFixes;
 import org.mozilla.gecko.util.ShortcutUtils;
 import org.mozilla.gecko.util.ThreadUtils;
+import org.mozilla.geckoview.GeckoRuntime;
+import org.mozilla.geckoview.GeckoRuntimeSettings;
 
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.InputStream;
 import java.io.IOException;
 import java.lang.reflect.Method;
 import java.net.URL;
 import java.util.UUID;
 
 public class GeckoApplication extends Application
                               implements HapticFeedbackDelegate {
     private static final String LOG_TAG = "GeckoApplication";
+    public static final String ACTION_DEBUG = "org.mozilla.gecko.DEBUG";
     private static final String MEDIA_DECODING_PROCESS_CRASH = "MEDIA_DECODING_PROCESS_CRASH";
 
     private boolean mInBackground;
     private boolean mPausedGecko;
     private boolean mIsInitialResume;
 
     private LightweightTheme mLightweightTheme;
 
@@ -108,17 +114,17 @@ public class GeckoApplication extends Ap
             // In un-official builds, we want to load Javascript resources fresh
             // with each build.  In official builds, the startup cache is purged by
             // the buildid mechanism, but most un-official builds don't bump the
             // buildid, so we purge here instead.
             Log.w(LOG_TAG, "STARTUP PERFORMANCE WARNING: un-official build: purging the " +
                            "startup (JavaScript) caches.");
             return new String[] { "-purgecaches" };
         }
-        return null;
+        return new String[0];
     }
 
     public static String getDefaultUAString() {
         return HardwareUtils.isTablet() ? AppConstants.USER_AGENT_FENNEC_TABLET :
                                           AppConstants.USER_AGENT_FENNEC_MOBILE;
     }
 
     public static void shutdown(final Intent restartIntent) {
@@ -205,16 +211,60 @@ public class GeckoApplication extends Ap
             GeckoThread.onResume();
             mPausedGecko = false;
             GeckoNetworkManager.getInstance().start(this);
         }
 
         mInBackground = false;
     }
 
+    private static GeckoRuntime sGeckoRuntime;
+    public static GeckoRuntime getRuntime() {
+        return sGeckoRuntime;
+    }
+
+    public static GeckoRuntime ensureRuntime(@NonNull Context context) {
+        if (sGeckoRuntime != null) {
+            return sGeckoRuntime;
+        }
+
+        return createRuntime(context, null);
+    }
+
+    private static GeckoRuntimeSettings.Builder createSettingsBuilder() {
+        return new GeckoRuntimeSettings.Builder()
+                .javaCrashReportingEnabled(true)
+                .nativeCrashReportingEnabled(true)
+                .arguments(getDefaultGeckoArgs());
+    }
+
+    public static GeckoRuntime createRuntime(@NonNull Context context,
+                                             @Nullable SafeIntent intent) {
+        if (sGeckoRuntime != null) {
+            throw new IllegalStateException("Already have a GeckoRuntime!");
+        }
+
+        if (context == null) {
+            throw new IllegalArgumentException("Context must not be null");
+        }
+
+        GeckoRuntimeSettings.Builder builder = createSettingsBuilder();
+        if (intent != null) {
+            builder.pauseForDebugger(ACTION_DEBUG.equals(intent.getAction()));
+
+            Bundle extras = intent.getExtras();
+            if (extras != null) {
+                builder.extras(extras);
+            }
+        }
+
+        sGeckoRuntime = GeckoRuntime.create(context, builder.build());
+        return sGeckoRuntime;
+    }
+
     @Override
     public void onCreate() {
         Log.i(LOG_TAG, "zerdatime " + SystemClock.elapsedRealtime() +
               " - application start");
 
         final Context oldContext = GeckoAppShell.getApplicationContext();
         if (oldContext instanceof GeckoApplication) {
             ((GeckoApplication) oldContext).onDestroy();
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoService.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoService.java
@@ -13,16 +13,17 @@ import android.content.Intent;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.Handler;
 import android.os.Looper;
 import android.util.Log;
 
 import java.io.File;
 
+import org.mozilla.gecko.mozglue.SafeIntent;
 import org.mozilla.gecko.util.BundleEventListener;
 import org.mozilla.gecko.util.GeckoBundle;
 import org.mozilla.gecko.util.EventCallback;
 
 public class GeckoService extends Service {
 
     private static final String LOGTAG = "GeckoService";
     private static final boolean DEBUG = false;
@@ -161,44 +162,45 @@ public class GeckoService extends Servic
 
         final String profileName = intent.getStringExtra(INTENT_PROFILE_NAME);
         final String profileDir = intent.getStringExtra(INTENT_PROFILE_DIR);
 
         if (profileName == null) {
             throw new IllegalArgumentException("Intent must specify profile.");
         }
 
-        if (!GeckoThread.initMainProcessWithProfile(
-                profileName, profileDir != null ? new File(profileDir) : null,
-                GeckoApplication.getDefaultGeckoArgs(), intent.getExtras())) {
-            Log.w(LOGTAG, "Ignoring due to profile mismatch: " +
-                          profileName + " [" + profileDir + ']');
+        if (GeckoApplication.getRuntime() != null) {
+            // Gecko has already been initialized, make sure it's using the
+            // expected profile.
+            return GeckoThread.canUseProfile(profileName,
+                    profileDir != null ? new File(profileDir) : null);
+        }
 
-            final GeckoProfile profile = GeckoThread.getActiveProfile();
-            if (profile != null) {
-                Log.w(LOGTAG, "Current profile is " + profile.getName() +
-                              " [" + profile.getDir().getAbsolutePath() + ']');
-            }
-            return false;
+        String args;
+        if (profileDir != null) {
+            args = "-profile " + profileDir;
+        } else {
+            args = "-P " + profileName;
         }
+
+        intent.putExtra(GeckoThread.EXTRA_ARGS, args);
+        GeckoApplication.createRuntime(this, new SafeIntent(intent));
         return true;
     }
 
     private int handleIntent(final Intent intent, final int startId) {
         if (DEBUG) {
             Log.d(LOGTAG, "Handling " + intent.getAction());
         }
 
         if (!initGecko(intent)) {
             stopSelf(startId);
             return Service.START_NOT_STICKY;
         }
 
-        GeckoThread.launch();
-
         switch (intent.getAction()) {
         case INTENT_ACTION_UPDATE_ADDONS:
             // Run the add-on update service. Because the service is automatically invoked
             // when loading Gecko, we don't have to do anything else here.
         case INTENT_ACTION_LOAD_LIBS:
             // Load libs only. Don't take any additional actions.
         case INTENT_ACTION_START_GECKO:
             // Load libs and start Gecko. Don't take any additional actions.
--- a/mobile/android/base/java/org/mozilla/gecko/RemotePresentationService.java
+++ b/mobile/android/base/java/org/mozilla/gecko/RemotePresentationService.java
@@ -127,17 +127,17 @@ class VirtualPresentation extends CastPr
 
         final GeckoSession session = new GeckoSession();
         session.getSettings().setString(GeckoSessionSettings.CHROME_URI,
                                         PRESENTATION_VIEW_URI + "#" + deviceId);
         session.getSettings().setInt(GeckoSessionSettings.SCREEN_ID, screenId);
 
         // Create new GeckoView
         view = new GeckoView(getContext());
-        view.setSession(session, GeckoRuntime.getDefault(getContext()));
+        view.setSession(session, GeckoApplication.ensureRuntime(getContext()));
         view.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
                                               LayoutParams.MATCH_PARENT));
 
         // Create new layout to put the GeckoView
         layout = new RelativeLayout(getContext());
         layout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
                                                 LayoutParams.MATCH_PARENT));
         layout.addView(view);
--- a/mobile/android/base/java/org/mozilla/gecko/customtabs/CustomTabsActivity.java
+++ b/mobile/android/base/java/org/mozilla/gecko/customtabs/CustomTabsActivity.java
@@ -32,16 +32,17 @@ import android.view.View;
 import android.widget.ProgressBar;
 
 import org.mozilla.gecko.ActivityHandlerHelper;
 import org.mozilla.gecko.BrowserApp;
 import org.mozilla.gecko.Clipboard;
 import org.mozilla.gecko.DoorHangerPopup;
 import org.mozilla.gecko.EventDispatcher;
 import org.mozilla.gecko.FormAssistPopup;
+import org.mozilla.gecko.GeckoApplication;
 import org.mozilla.gecko.GeckoSharedPrefs;
 import org.mozilla.gecko.preferences.GeckoPreferences;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.SnackbarBuilder;
 import org.mozilla.gecko.Telemetry;
 import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.menu.GeckoMenu;
 import org.mozilla.gecko.menu.GeckoMenuInflater;
@@ -126,17 +127,17 @@ public class CustomTabsActivity extends 
 
         final GeckoSessionSettings settings = new GeckoSessionSettings();
         settings.setBoolean(GeckoSessionSettings.USE_MULTIPROCESS, false);
         mGeckoSession = new GeckoSession(settings);
         mGeckoSession.setNavigationDelegate(this);
         mGeckoSession.setProgressDelegate(this);
         mGeckoSession.setContentDelegate(this);
 
-        mGeckoView.setSession(mGeckoSession, GeckoRuntime.getDefault(this));
+        mGeckoView.setSession(mGeckoSession, GeckoApplication.ensureRuntime(this));
 
         mPromptService = new PromptService(this, mGeckoView.getEventDispatcher());
         mDoorHangerPopup = new DoorHangerPopup(this, mGeckoView.getEventDispatcher());
 
         mFormAssistPopup = (FormAssistPopup) findViewById(R.id.form_assist_popup);
         mFormAssistPopup.create(mGeckoView);
 
         mTextSelection = TextSelection.Factory.create(mGeckoView, this);
--- a/mobile/android/base/java/org/mozilla/gecko/webapps/WebAppActivity.java
+++ b/mobile/android/base/java/org/mozilla/gecko/webapps/WebAppActivity.java
@@ -21,16 +21,17 @@ import android.view.Window;
 import android.view.WindowManager;
 import android.widget.Toast;
 
 import org.mozilla.gecko.ActivityHandlerHelper;
 import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.BrowserApp;
 import org.mozilla.gecko.DoorHangerPopup;
 import org.mozilla.gecko.FormAssistPopup;
+import org.mozilla.gecko.GeckoApplication;
 import org.mozilla.gecko.GeckoScreenOrientation;
 import org.mozilla.gecko.GeckoSharedPrefs;
 import org.mozilla.gecko.preferences.GeckoPreferences;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.customtabs.CustomTabsActivity;
 import org.mozilla.gecko.permissions.Permissions;
 import org.mozilla.gecko.prompts.PromptService;
 import org.mozilla.gecko.text.TextSelection;
@@ -92,17 +93,17 @@ public class WebAppActivity extends AppC
 
         super.onCreate(savedInstanceState);
         setContentView(R.layout.webapp_activity);
         mGeckoView = (GeckoView) findViewById(R.id.pwa_gecko_view);
 
         final GeckoSessionSettings settings = new GeckoSessionSettings();
         settings.setBoolean(GeckoSessionSettings.USE_MULTIPROCESS, false);
         mGeckoSession = new GeckoSession(settings);
-        mGeckoView.setSession(mGeckoSession, GeckoRuntime.getDefault(this));
+        mGeckoView.setSession(mGeckoSession, GeckoApplication.ensureRuntime(this));
 
         mGeckoSession.setNavigationDelegate(this);
         mGeckoSession.setContentDelegate(this);
         mGeckoSession.setProgressDelegate(new GeckoSession.ProgressDelegate() {
             @Override
             public void onPageStart(GeckoSession session, String url) {
 
             }
--- a/mobile/android/geckoview/build.gradle
+++ b/mobile/android/geckoview/build.gradle
@@ -134,16 +134,20 @@ android {
                     exclude 'com/google/android/exoplayer2/**'
                     exclude 'org/mozilla/gecko/media/GeckoHlsAudioRenderer.java'
                     exclude 'org/mozilla/gecko/media/GeckoHlsPlayer.java'
                     exclude 'org/mozilla/gecko/media/GeckoHlsRendererBase.java'
                     exclude 'org/mozilla/gecko/media/GeckoHlsVideoRenderer.java'
                     exclude 'org/mozilla/gecko/media/Utils.java'
                 }
 
+                if (!mozconfig.substs.MOZ_CRASHREPORTER) {
+                    exclude 'org/mozilla/gecko/CrashReporterService.java'
+                }
+
                 if (mozconfig.substs.MOZ_WEBRTC) {
                     srcDir "${topsrcdir}/media/webrtc/trunk/webrtc/base/java/src"
                     srcDir "${topsrcdir}/media/webrtc/trunk/webrtc/modules/audio_device/android/java/src"
                     srcDir "${topsrcdir}/media/webrtc/trunk/webrtc/modules/video_capture/android/java/src"
                 }
             }
 
             assets {
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/TestRunnerActivity.java
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/TestRunnerActivity.java
@@ -115,16 +115,20 @@ public class TestRunnerActivity extends 
             final GeckoRuntimeSettings.Builder runtimeSettingsBuilder =
                 new GeckoRuntimeSettings.Builder();
             runtimeSettingsBuilder.arguments(new String[] { "-purgecaches" });
             final Bundle extras = intent.getExtras();
             if (extras != null) {
                 runtimeSettingsBuilder.extras(extras);
             }
 
+            runtimeSettingsBuilder
+                    .nativeCrashReportingEnabled(true)
+                    .javaCrashReportingEnabled(true);
+
             sRuntime = GeckoRuntime.create(this, runtimeSettingsBuilder.build());
             sRuntime.setDelegate(new GeckoRuntime.Delegate() {
                 @Override
                 public void onShutdown() {
                     mKillProcessOnDestroy = true;
                     finish();
                 }
             });
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/rule/GeckoSessionTestRule.java
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/rule/GeckoSessionTestRule.java
@@ -842,17 +842,20 @@ public class GeckoSessionTestRule extend
         final Class<?>[] classes = CALLBACK_CLASSES.toArray(new Class<?>[CALLBACK_CLASSES.size()]);
         mCallbackProxy = Proxy.newProxyInstance(GeckoSession.class.getClassLoader(),
                                                 classes, recorder);
 
         if (sRuntime == null) {
             final GeckoRuntimeSettings.Builder runtimeSettingsBuilder =
                 new GeckoRuntimeSettings.Builder();
             runtimeSettingsBuilder.arguments(new String[] { "-purgecaches" })
-                                  .extras(InstrumentationRegistry.getArguments());
+                    .extras(InstrumentationRegistry.getArguments())
+                    .nativeCrashReportingEnabled(true)
+                    .javaCrashReportingEnabled(true);
+
             sRuntime = GeckoRuntime.create(
                 InstrumentationRegistry.getTargetContext(),
                 runtimeSettingsBuilder.build());
         }
 
         sRuntime.getSettings().setRemoteDebuggingEnabled(mWithDevTools);
 
         mMainSession = new GeckoSession(settings);
--- a/mobile/android/geckoview/src/main/AndroidManifest.xml
+++ b/mobile/android/geckoview/src/main/AndroidManifest.xml
@@ -1,73 +1,100 @@
+<?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="org.mozilla.geckoview">
+          package="org.mozilla.geckoview">
 
     <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
     <uses-permission android:name="android.permission.INTERNET"/>
     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
     <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/>
     <uses-permission android:name="com.android.launcher.permission.UNINSTALL_SHORTCUT"/>
-
     <uses-permission android:name="android.permission.WAKE_LOCK"/>
     <uses-permission android:name="android.permission.VIBRATE"/>
 
-    <uses-feature android:name="android.hardware.location" android:required="false"/>
-    <uses-feature android:name="android.hardware.location.gps" android:required="false"/>
+    <uses-feature
+            android:name="android.hardware.location"
+            android:required="false"/>
+    <uses-feature
+            android:name="android.hardware.location.gps"
+            android:required="false"/>
     <uses-feature android:name="android.hardware.touchscreen"/>
 
-    <uses-permission android:name="android.permission.CAMERA" />
-    <uses-feature android:name="android.hardware.camera" android:required="false"/>
-    <uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
+    <uses-permission android:name="android.permission.CAMERA"/>
+
+    <uses-feature
+            android:name="android.hardware.camera"
+            android:required="false"/>
+    <uses-feature
+            android:name="android.hardware.camera.autofocus"
+            android:required="false"/>
 
-    <!--#ifdef MOZ_WEBRTC-->
-    <!-- TODO preprocess AndroidManifest.xml so that we can
-         conditionally include WebRTC permissions based on MOZ_WEBRTC. -->
-    <uses-permission android:name="android.permission.RECORD_AUDIO"/>-->
-    <uses-feature android:name="android.hardware.audio.low_latency" android:required="false"/>-->
-    <uses-feature android:name="android.hardware.microphone" android:required="false"/>-->
-    <uses-feature android:name="android.hardware.camera.any" android:required="false"/>-->
-    <!--#endif-->
+    <!-- #ifdef MOZ_WEBRTC -->
+    <!--
+         TODO preprocess AndroidManifest.xml so that we can
+         conditionally include WebRTC permissions based on MOZ_WEBRTC.
+    -->
+    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
+    -->
+    <uses-feature
+            android:name="android.hardware.audio.low_latency"
+            android:required="false"/>
+    -->
+    <uses-feature
+            android:name="android.hardware.microphone"
+            android:required="false"/>
+    -->
+    <uses-feature
+            android:name="android.hardware.camera.any"
+            android:required="false"/>
+    -->
+    <!-- #endif -->
+
 
     <!-- App requires OpenGL ES 2.0 -->
-    <uses-feature android:glEsVersion="0x00020000" android:required="true" />
+    <uses-feature
+            android:glEsVersion="0x00020000"
+            android:required="true"/>
 
     <application>
+
         <!-- New child services must also be added to the Fennec AndroidManifest.xml.in -->
         <service
-            android:name="org.mozilla.gecko.media.MediaManager"
-            android:enabled="true"
-            android:exported="false"
-            android:process=":media"
-            android:isolatedProcess="false">
+                android:name="org.mozilla.gecko.media.MediaManager"
+                android:enabled="true"
+                android:exported="false"
+                android:isolatedProcess="false"
+                android:process=":media">
         </service>
-
         <service
-            android:name="org.mozilla.gecko.process.GeckoServiceChildProcess$geckomediaplugin"
-            android:enabled="true"
-            android:exported="false"
-            android:process=":geckomediaplugin"
-            android:isolatedProcess="false">
+                android:name="org.mozilla.gecko.process.GeckoServiceChildProcess$geckomediaplugin"
+                android:enabled="true"
+                android:exported="false"
+                android:isolatedProcess="false"
+                android:process=":geckomediaplugin">
         </service>
-
         <service
-            android:name="org.mozilla.gecko.process.GeckoServiceChildProcess$tab"
-            android:enabled="true"
-            android:exported="false"
-            android:process=":tab"
-            android:isolatedProcess="false">
+                android:name="org.mozilla.gecko.process.GeckoServiceChildProcess$tab"
+                android:enabled="true"
+                android:exported="false"
+                android:isolatedProcess="false"
+                android:process=":tab">
         </service>
-
         <service
-            android:name="org.mozilla.gecko.gfx.SurfaceAllocatorService"
-            android:enabled="true"
-            android:exported="false"
-            android:isolatedProcess="false">
+                android:name="org.mozilla.gecko.gfx.SurfaceAllocatorService"
+                android:enabled="true"
+                android:exported="false"
+                android:isolatedProcess="false">
         </service>
-   </application>
+        <service
+                android:name="org.mozilla.gecko.CrashReporterService"
+                android:exported="false"
+                android:process=":crashreporter">
+        </service>
+    </application>
 
-</manifest>
+</manifest>
\ No newline at end of file
--- a/mobile/android/geckoview/src/main/aidl/org/mozilla/gecko/process/IChildProcess.aidl
+++ b/mobile/android/geckoview/src/main/aidl/org/mozilla/gecko/process/IChildProcess.aidl
@@ -6,15 +6,15 @@ package org.mozilla.gecko.process;
 
 import org.mozilla.gecko.process.IProcessManager;
 
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
 
 interface IChildProcess {
     int getPid();
-    boolean start(in IProcessManager procMan, in String[] args, in Bundle extras,
+    boolean start(in IProcessManager procMan, in String[] args, in Bundle extras, int flags,
                   in ParcelFileDescriptor prefsPfd, in ParcelFileDescriptor ipcPfd,
                   in ParcelFileDescriptor crashReporterPfd,
                   in ParcelFileDescriptor crashAnnotationPfd);
 
     void crash();
 }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/CrashHandler.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/CrashHandler.java
@@ -292,26 +292,25 @@ public class CrashHandler implements Thr
      * @param extraFile Path for the crash extra file
      * @return Whether the crash reporter was successfully launched
      */
     protected boolean launchCrashReporter(final String dumpFile, final String extraFile) {
         try {
             final Context context = getAppContext();
             final String javaPkg = getJavaPackageName();
             final String pkg = getAppPackageName();
-            final String component = javaPkg + ".CrashReporter";
+            final String component = javaPkg + ".CrashReporterService";
             final String action = javaPkg + ".reportCrash";
             final ProcessBuilder pb;
 
             if (context != null) {
                 final Intent intent = new Intent(action);
                 intent.setComponent(new ComponentName(pkg, component));
                 intent.putExtra("minidumpPath", dumpFile);
-                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                context.startActivity(intent);
+                context.startService(intent);
                 return true;
             }
 
             if (Build.VERSION.SDK_INT < 17) {
                 pb = new ProcessBuilder(
                     "/system/bin/am", "start",
                     "-a", action,
                     "-n", pkg + '/' + component,
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/CrashReporterService.java
@@ -0,0 +1,373 @@
+package org.mozilla.gecko;
+
+import org.mozilla.gecko.mozglue.GeckoLoader;
+import org.mozilla.gecko.mozglue.MinidumpAnalyzer;
+import org.mozilla.gecko.util.INIParser;
+import org.mozilla.gecko.util.INISection;
+import org.mozilla.gecko.util.ProxySelector;
+
+import android.app.IntentService;
+import android.content.Intent;
+import android.os.Build;
+import android.util.Log;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.nio.channels.Channels;
+import java.nio.channels.FileChannel;
+import java.security.MessageDigest;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.zip.GZIPOutputStream;
+
+public class CrashReporterService extends IntentService {
+    private static final String LOGTAG = "CrashReporter";
+    private static final String ACTION_REPORT_CRASH = "org.mozilla.gecko.reportCrash";
+    private static final String PASSED_MINI_DUMP_KEY = "minidumpPath";
+    private static final String PASSED_MINI_DUMP_SUCCESS_KEY = "minidumpSuccess";
+    private static final String MINI_DUMP_PATH_KEY = "upload_file_minidump";
+    private static final String PAGE_URL_KEY = "URL";
+    private static final String NOTES_KEY = "Notes";
+    private static final String SERVER_URL_KEY = "ServerURL";
+
+    private static final String CRASH_REPORT_SUFFIX = "/mozilla/Crash Reports/";
+    private static final String PENDING_SUFFIX = CRASH_REPORT_SUFFIX + "pending";
+    private static final String SUBMITTED_SUFFIX = CRASH_REPORT_SUFFIX + "submitted";
+
+    private File mPendingMinidumpFile;
+    private File mPendingExtrasFile;
+    private HashMap<String, String> mExtrasStringMap;
+    private boolean mMinidumpSucceeded;
+
+    public CrashReporterService() {
+        super("CrashReporterService");
+    }
+
+    @Override
+    protected void onHandleIntent(Intent intent) {
+        if (intent == null || !intent.getAction().equals(ACTION_REPORT_CRASH)) {
+            Log.d(LOGTAG, "Invalid or unknown action");
+            return;
+        }
+
+        Class<?> reporterActivityCls = getFennecReporterActivity();
+        if (reporterActivityCls != null) {
+            intent.setClass(this, reporterActivityCls);
+            startActivity(intent);
+            return;
+        }
+
+        submitCrash(intent);
+    }
+
+    private Class<?> getFennecReporterActivity() {
+        try {
+            return Class.forName("org.mozilla.gecko.CrashReporterActivity");
+        } catch (ClassNotFoundException e) {
+            return null;
+        }
+    }
+
+    private boolean moveFile(File inFile, File outFile) {
+        Log.i(LOGTAG, "moving " + inFile + " to " + outFile);
+        if (inFile.renameTo(outFile))
+            return true;
+        try {
+            outFile.createNewFile();
+            Log.i(LOGTAG, "couldn't rename minidump file");
+            // so copy it instead
+            FileChannel inChannel = new FileInputStream(inFile).getChannel();
+            FileChannel outChannel = new FileOutputStream(outFile).getChannel();
+            long transferred = inChannel.transferTo(0, inChannel.size(), outChannel);
+            inChannel.close();
+            outChannel.close();
+
+            if (transferred > 0)
+                inFile.delete();
+        } catch (Exception e) {
+            Log.e(LOGTAG, "exception while copying minidump file: ", e);
+            return false;
+        }
+        return true;
+    }
+
+    private void submitCrash(Intent intent) {
+        mMinidumpSucceeded = intent.getBooleanExtra(PASSED_MINI_DUMP_SUCCESS_KEY, false);
+        if (!mMinidumpSucceeded) {
+            Log.i(LOGTAG, "Failed to get minidump.");
+        }
+        String passedMinidumpPath = intent.getStringExtra(PASSED_MINI_DUMP_KEY);
+        File passedMinidumpFile = new File(passedMinidumpPath);
+        File pendingDir = new File(getFilesDir(), PENDING_SUFFIX);
+        pendingDir.mkdirs();
+        mPendingMinidumpFile = new File(pendingDir, passedMinidumpFile.getName());
+        moveFile(passedMinidumpFile, mPendingMinidumpFile);
+
+        File extrasFile = new File(passedMinidumpPath.replaceAll("\\.dmp", ".extra"));
+        mPendingExtrasFile = new File(pendingDir, extrasFile.getName());
+        moveFile(extrasFile, mPendingExtrasFile);
+
+        // Compute the minidump hash and generate the stack traces
+        computeMinidumpHash(mPendingExtrasFile, mPendingMinidumpFile);
+
+        try {
+            GeckoLoader.loadMozGlue(this);
+
+            if (!MinidumpAnalyzer.GenerateStacks(mPendingMinidumpFile.getPath(), /* fullStacks */ false)) {
+                Log.e(LOGTAG, "Could not generate stacks for this minidump: " + passedMinidumpPath);
+            }
+        } catch (UnsatisfiedLinkError e) {
+            Log.e(LOGTAG, "Could not load libmozglue.so, stacks for this crash won't be generated");
+        }
+
+        // Extract the annotations from the .extra file
+        mExtrasStringMap = new HashMap<String, String>();
+        readStringsFromFile(mPendingExtrasFile.getPath(), mExtrasStringMap);
+
+        try {
+            // Find the profile name and path. Since we don't have any other way of getting it within
+            // this context we extract it from the crash dump path.
+            final File profileDir = passedMinidumpFile.getParentFile().getParentFile();
+            final String profileName = getProfileName(profileDir);
+
+            if (profileName != null) {
+                // Extract the crash dump ID and telemetry client ID, we need profile access for the latter.
+                final String passedMinidumpName = passedMinidumpFile.getName();
+                // Strip the .dmp suffix from the minidump name to obtain the crash ID.
+                final String crashId = passedMinidumpName.substring(0, passedMinidumpName.length() - 4);
+                final GeckoProfile profile = GeckoProfile.get(this, profileName, profileDir);
+                final String clientId = profile.getClientId();
+            }
+        } catch (GeckoProfileDirectories.NoMozillaDirectoryException | IOException e) {
+            Log.e(LOGTAG, "Cannot send the crash ping: ", e);
+        }
+
+        // Notify GeckoApp that we've crashed, so it can react appropriately during the next start.
+        try {
+            File crashFlag = new File(GeckoProfileDirectories.getMozillaDirectory(this), "CRASHED");
+            crashFlag.createNewFile();
+        } catch (GeckoProfileDirectories.NoMozillaDirectoryException | IOException e) {
+            Log.e(LOGTAG, "Cannot set crash flag: ", e);
+        }
+
+        sendReport(mPendingMinidumpFile, mExtrasStringMap, mPendingExtrasFile);
+    }
+
+
+    private String getProfileName(File profileDir) throws GeckoProfileDirectories.NoMozillaDirectoryException {
+        final File mozillaDir = GeckoProfileDirectories.getMozillaDirectory(this);
+        final INIParser parser = GeckoProfileDirectories.getProfilesINI(mozillaDir);
+        String profileName = null;
+
+        if (parser.getSections() != null) {
+            for (Enumeration<INISection> e = parser.getSections().elements(); e.hasMoreElements(); ) {
+                final INISection section = e.nextElement();
+                final String path = section.getStringProperty("Path");
+                final boolean isRelative = (section.getIntProperty("IsRelative") == 1);
+
+                if ((isRelative && path.equals(profileDir.getName())) ||
+                        path.equals(profileDir.getPath())) {
+                    profileName = section.getStringProperty("Name");
+                    break;
+                }
+            }
+        }
+
+        return profileName;
+    }
+
+
+    private void computeMinidumpHash(File extraFile, File minidump) {
+        try {
+            FileInputStream stream = new FileInputStream(minidump);
+            MessageDigest md = MessageDigest.getInstance("SHA-256");
+
+            try {
+                byte[] buffer = new byte[4096];
+                int readBytes;
+
+                while ((readBytes = stream.read(buffer)) != -1) {
+                    md.update(buffer, 0, readBytes);
+                }
+            } finally {
+                stream.close();
+            }
+
+            byte[] digest = md.digest();
+            StringBuilder hash = new StringBuilder(84);
+
+            hash.append("MinidumpSha256Hash=");
+
+            for (int i = 0; i < digest.length; i++) {
+                hash.append(Integer.toHexString((digest[i] & 0xf0) >> 4));
+                hash.append(Integer.toHexString(digest[i] & 0x0f));
+            }
+
+            hash.append('\n');
+
+            FileWriter writer = new FileWriter(extraFile, /* append */ true);
+
+            try {
+                writer.write(hash.toString());
+            } finally {
+                writer.close();
+            }
+        } catch (Exception e) {
+            Log.e(LOGTAG, "exception while computing the minidump hash: ", e);
+        }
+    }
+
+    private boolean readStringsFromFile(String filePath, Map<String, String> stringMap) {
+        try {
+            BufferedReader reader = new BufferedReader(new FileReader(filePath));
+            return readStringsFromReader(reader, stringMap);
+        } catch (Exception e) {
+            Log.e(LOGTAG, "exception while reading strings: ", e);
+            return false;
+        }
+    }
+
+    private boolean readStringsFromReader(BufferedReader reader, Map<String, String> stringMap) throws IOException {
+        String line;
+        while ((line = reader.readLine()) != null) {
+            int equalsPos = -1;
+            if ((equalsPos = line.indexOf('=')) != -1) {
+                String key = line.substring(0, equalsPos);
+                String val = unescape(line.substring(equalsPos + 1));
+                stringMap.put(key, val);
+            }
+        }
+        reader.close();
+        return true;
+    }
+
+    private String generateBoundary() {
+        // Generate some random numbers to fill out the boundary
+        int r0 = (int)(Integer.MAX_VALUE * Math.random());
+        int r1 = (int)(Integer.MAX_VALUE * Math.random());
+        return String.format("---------------------------%08X%08X", r0, r1);
+    }
+
+    private void sendPart(OutputStream os, String boundary, String name, String data) {
+        try {
+            os.write(("--" + boundary + "\r\n" +
+                    "Content-Disposition: form-data; name=\"" + name + "\"\r\n" +
+                    "\r\n" +
+                    data + "\r\n"
+            ).getBytes());
+        } catch (Exception ex) {
+            Log.e(LOGTAG, "Exception when sending \"" + name + "\"", ex);
+        }
+    }
+
+    private void sendFile(OutputStream os, String boundary, String name, File file) throws IOException {
+        os.write(("--" + boundary + "\r\n" +
+                "Content-Disposition: form-data; name=\"" + name + "\"; " +
+                "filename=\"" + file.getName() + "\"\r\n" +
+                "Content-Type: application/octet-stream\r\n" +
+                "\r\n"
+        ).getBytes());
+        FileChannel fc = new FileInputStream(file).getChannel();
+        fc.transferTo(0, fc.size(), Channels.newChannel(os));
+        fc.close();
+    }
+
+    private void sendReport(File minidumpFile, Map<String, String> extras, File extrasFile) {
+        Log.i(LOGTAG, "sendReport: " + minidumpFile.getPath());
+
+        String spec = extras.get(SERVER_URL_KEY);
+        if (spec == null) {
+            return;
+        }
+
+        try {
+            final URL url = new URL(URLDecoder.decode(spec, "UTF-8"));
+            final URI uri = new URI(url.getProtocol(), url.getUserInfo(),
+                    url.getHost(), url.getPort(),
+                    url.getPath(), url.getQuery(), url.getRef());
+            HttpURLConnection conn = (HttpURLConnection) ProxySelector.openConnectionWithProxy(uri);
+            conn.setRequestMethod("POST");
+            String boundary = generateBoundary();
+            conn.setDoOutput(true);
+            conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
+            conn.setRequestProperty("Content-Encoding", "gzip");
+
+            OutputStream os = new GZIPOutputStream(conn.getOutputStream());
+            for (String key : extras.keySet()) {
+                if (!key.equals(SERVER_URL_KEY) && !key.equals(NOTES_KEY)) {
+                    sendPart(os, boundary, key, extras.get(key));
+                }
+            }
+
+            StringBuilder sb = new StringBuilder();
+            sb.append(extras.containsKey(NOTES_KEY) ? extras.get(NOTES_KEY) + "\n" : "");
+            sb.append(Build.MANUFACTURER).append(' ')
+                    .append(Build.MODEL).append('\n')
+                    .append(Build.FINGERPRINT);
+            sendPart(os, boundary, NOTES_KEY, sb.toString());
+
+            sendPart(os, boundary, "Android_Manufacturer", Build.MANUFACTURER);
+            sendPart(os, boundary, "Android_Model", Build.MODEL);
+            sendPart(os, boundary, "Android_Board", Build.BOARD);
+            sendPart(os, boundary, "Android_Brand", Build.BRAND);
+            sendPart(os, boundary, "Android_Device", Build.DEVICE);
+            sendPart(os, boundary, "Android_Display", Build.DISPLAY);
+            sendPart(os, boundary, "Android_Fingerprint", Build.FINGERPRINT);
+            sendPart(os, boundary, "Android_CPU_ABI", Build.CPU_ABI);
+            try {
+                sendPart(os, boundary, "Android_CPU_ABI2", Build.CPU_ABI2);
+                sendPart(os, boundary, "Android_Hardware", Build.HARDWARE);
+            } catch (Exception ex) {
+                Log.e(LOGTAG, "Exception while sending SDK version 8 keys", ex);
+            }
+            sendPart(os, boundary, "Android_Version",  Build.VERSION.SDK_INT + " (" + Build.VERSION.CODENAME + ")");
+            sendPart(os, boundary, PASSED_MINI_DUMP_SUCCESS_KEY, mMinidumpSucceeded ? "True" : "False");
+            sendFile(os, boundary, MINI_DUMP_PATH_KEY, minidumpFile);
+            os.write(("\r\n--" + boundary + "--\r\n").getBytes());
+            os.flush();
+            os.close();
+            BufferedReader br = new BufferedReader(
+                    new InputStreamReader(conn.getInputStream()));
+            HashMap<String, String>  responseMap = new HashMap<String, String>();
+            readStringsFromReader(br, responseMap);
+
+            if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
+                File submittedDir = new File(getFilesDir(),
+                        SUBMITTED_SUFFIX);
+                submittedDir.mkdirs();
+                minidumpFile.delete();
+                extrasFile.delete();
+                String crashid = responseMap.get("CrashID");
+                File file = new File(submittedDir, crashid + ".txt");
+                FileOutputStream fos = new FileOutputStream(file);
+                fos.write("Crash ID: ".getBytes());
+                fos.write(crashid.getBytes());
+                fos.close();
+                Log.i(LOGTAG, "Successfully sent crash report: " + crashid);
+            } else {
+                Log.w(LOGTAG, "Received failure HTTP response code from server: " + conn.getResponseCode());
+            }
+        } catch (IOException e) {
+            Log.e(LOGTAG, "exception during send: ", e);
+        } catch (URISyntaxException e) {
+            Log.e(LOGTAG, "exception during new URI: ", e);
+        }
+    }
+
+    private String unescape(String string) {
+        return string.replaceAll("\\\\\\\\", "\\").replaceAll("\\\\n", "\n").replaceAll("\\\\t", "\t");
+    }
+}
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java
@@ -106,17 +106,17 @@ import android.widget.AbsoluteLayout;
 
 public class GeckoAppShell
 {
     private static final String LOGTAG = "GeckoAppShell";
 
     // We have static members only.
     private GeckoAppShell() { }
 
-    private static final CrashHandler CRASH_HANDLER = new CrashHandler() {
+    private static class GeckoCrashHandler extends CrashHandler {
         @Override
         protected String getAppPackageName() {
             final Context appContext = getAppContext();
             if (appContext == null) {
                 return "<unknown>";
             }
             return appContext.getPackageName();
         }
@@ -169,20 +169,28 @@ public class GeckoAppShell
                 // Only use Java crash reporter if enabled on official build.
                 return super.reportException(thread, exc);
             }
             return false;
         }
     };
 
     private static String sAppNotes;
+    private static CrashHandler sCrashHandler;
 
-    public static CrashHandler ensureCrashHandling() {
-        // Crash handling is automatically enabled when GeckoAppShell is loaded.
-        return CRASH_HANDLER;
+    public static synchronized CrashHandler ensureCrashHandling() {
+        if (sCrashHandler == null) {
+            sCrashHandler = new GeckoCrashHandler();
+        }
+
+        return sCrashHandler;
+    }
+
+    public static synchronized boolean isCrashHandlingEnabled() {
+        return sCrashHandler != null;
     }
 
     @WrapForJNI(exceptionMode = "ignore")
     /* package */ static synchronized String getAppNotes() {
         return sAppNotes;
     }
 
     public static synchronized void appendAppNotesToCrashReport(final String notes) {
@@ -275,18 +283,20 @@ public class GeckoAppShell
      */
 
     @WrapForJNI(exceptionMode = "ignore")
     private static String getExceptionStackTrace(Throwable e) {
         return CrashHandler.getExceptionStackTrace(CrashHandler.getRootException(e));
     }
 
     @WrapForJNI(exceptionMode = "ignore")
-    private static void handleUncaughtException(Throwable e) {
-        CRASH_HANDLER.uncaughtException(null, e);
+    private static synchronized void handleUncaughtException(Throwable e) {
+        if (sCrashHandler != null) {
+            sCrashHandler.uncaughtException(null, e);
+        }
     }
 
     private static float getLocationAccuracy(Location location) {
         float radius = location.getAccuracy();
         return (location.hasAccuracy() && radius > 0) ? radius : 1001;
     }
 
     // Permissions are explicitly checked when requesting content permission.
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoThread.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoThread.java
@@ -7,35 +7,36 @@ package org.mozilla.gecko;
 
 import org.mozilla.gecko.annotation.RobocopTarget;
 import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.mozglue.GeckoLoader;
 import org.mozilla.gecko.process.GeckoProcessManager;
 import org.mozilla.gecko.util.GeckoBundle;
 import org.mozilla.gecko.util.FileUtils;
 import org.mozilla.gecko.util.ThreadUtils;
+import org.mozilla.geckoview.BuildConfig;
 
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
 import android.os.MessageQueue;
 import android.os.SystemClock;
 import android.support.annotation.Nullable;
 import android.text.TextUtils;
 import android.util.Log;
 
 import java.io.File;
-import java.io.FilenameFilter;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 import java.util.Locale;
 import java.util.StringTokenizer;
 
 public class GeckoThread extends Thread {
     private static final String LOGTAG = "GeckoThread";
 
     public enum State implements NativeQueue.State {
         // After being loaded by class loader.
@@ -119,20 +120,22 @@ public class GeckoThread extends Thread 
     @WrapForJNI
     private static final ClassLoader clsLoader = GeckoThread.class.getClassLoader();
     @WrapForJNI
     private static MessageQueue msgQueue;
     @WrapForJNI
     private static int uiThreadId;
 
     // Main process parameters
-    public static final int FLAG_DEBUGGING = 1; // Debugging mode.
-    public static final int FLAG_PRELOAD_CHILD = 2; // Preload child during main thread start.
+    public static final int FLAG_DEBUGGING = 1 << 0; // Debugging mode.
+    public static final int FLAG_PRELOAD_CHILD = 1 << 1; // Preload child during main thread start.
+    public static final int FLAG_ENABLE_NATIVE_CRASHREPORTER = 1 << 2; // Enable native crash reporting
+    public static final int FLAG_ENABLE_JAVA_CRASHREPORTER = 1 << 3; // Enable java crash reporting
 
-    private static final String EXTRA_ARGS = "args";
+    /* package */ static final String EXTRA_ARGS = "args";
     private static final String EXTRA_PREFS_FD = "prefsFd";
     private static final String EXTRA_IPC_FD = "ipcFd";
     private static final String EXTRA_CRASH_FD = "crashFd";
     private static final String EXTRA_CRASH_ANNOTATION_FD = "crashAnnotationFd";
 
     private boolean mInitialized;
     private GeckoProfile mProfile;
     private String[] mArgs;
@@ -176,21 +179,24 @@ public class GeckoThread extends Thread 
     }
 
     public static boolean initMainProcess(final GeckoProfile profile, final String[] args,
                                           final Bundle extras, final int flags) {
         return INSTANCE.init(profile, args, extras, flags, /* fd */ -1,
                              /* fd */ -1, /* fd */ -1, /* fd */ -1);
     }
 
-    public static boolean initChildProcess(final String[] args, final Bundle extras,
-                                           final int prefsFd, final int ipcFd,
+    public static boolean initChildProcess(final String[] args,
+                                           final Bundle extras,
+                                           final int flags,
+                                           final int prefsFd,
+                                           final int ipcFd,
                                            final int crashFd,
                                            final int crashAnnotationFd) {
-        return INSTANCE.init(/* profile */ null, args, extras, /* flags */ 0,
+        return INSTANCE.init(/* profile */ null, args, extras, flags,
                              prefsFd, ipcFd, crashFd, crashAnnotationFd);
     }
 
     private static boolean canUseProfile(final Context context, final GeckoProfile profile,
                                          final String profileName, final File profileDir) {
         if (profileDir != null && !profileDir.isDirectory()) {
             return false;
         }
@@ -372,20 +378,50 @@ public class GeckoThread extends Thread 
         return mProfile;
     }
 
     public static @Nullable Bundle getActiveExtras() {
         synchronized (INSTANCE) {
             if (!INSTANCE.mInitialized) {
                 return null;
             }
-            return INSTANCE.mExtras;
+            return new Bundle(INSTANCE.mExtras);
+        }
+    }
+
+    public static int getActiveFlags() {
+        synchronized (INSTANCE) {
+            if (!INSTANCE.mInitialized) {
+                return 0;
+            }
+
+            return INSTANCE.mFlags;
         }
     }
 
+    private static ArrayList<String> getEnvFromExtras(final Bundle extras) {
+        if (extras == null) {
+            return new ArrayList<>();
+        }
+
+        ArrayList<String> result = new ArrayList<>();
+        if (extras != null) {
+            String env = extras.getString("env0");
+            for (int c = 1; env != null; c++) {
+                if (BuildConfig.DEBUG) {
+                    Log.d(LOGTAG, "env var: " + env);
+                }
+                result.add(env);
+                env = extras.getString("env" + c);
+            }
+        }
+
+        return result;
+    }
+
     @Override
     public void run() {
         Log.i(LOGTAG, "preparing to run Gecko");
 
         Looper.prepare();
         GeckoThread.msgQueue = Looper.myQueue();
         ThreadUtils.sGeckoThread = this;
         ThreadUtils.sGeckoHandler = new Handler();
@@ -437,17 +473,31 @@ public class GeckoThread extends Thread 
 
         final Context context = GeckoAppShell.getApplicationContext();
         final String[] args = isChildProcess() ? mArgs : getMainProcessArgs();
 
         if ((mFlags & FLAG_DEBUGGING) != 0) {
             Log.i(LOGTAG, "RunGecko - args = " + TextUtils.join(" ", args));
         }
 
-        GeckoLoader.setupGeckoEnvironment(context, context.getFilesDir().getPath(), mExtras);
+        final List<String> env = getEnvFromExtras(mExtras);
+
+        // In Gecko, the native crash reporter is enabled by default in opt builds, and
+        // disabled by default in debug builds.
+        if ((mFlags & FLAG_ENABLE_NATIVE_CRASHREPORTER) == 0 && !BuildConfig.DEBUG_BUILD) {
+            env.add(0, "MOZ_CRASHREPORTER_DISABLE=1");
+        } else if ((mFlags & FLAG_ENABLE_NATIVE_CRASHREPORTER) != 0 && BuildConfig.DEBUG_BUILD) {
+            env.add(0, "MOZ_CRASHREPORTER=1");
+        }
+
+        if ((mFlags & FLAG_ENABLE_JAVA_CRASHREPORTER) != 0) {
+            GeckoAppShell.ensureCrashHandling();
+        }
+
+        GeckoLoader.setupGeckoEnvironment(context, context.getFilesDir().getPath(), env);
 
         // And go.
         GeckoLoader.nativeRun(args,
                               mExtras.getInt(EXTRA_PREFS_FD, -1),
                               mExtras.getInt(EXTRA_IPC_FD, -1),
                               mExtras.getInt(EXTRA_CRASH_FD, -1),
                               mExtras.getInt(EXTRA_CRASH_ANNOTATION_FD, -1));
 
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/GeckoLoader.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/GeckoLoader.java
@@ -3,16 +3,17 @@
  * 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/. */
 
 package org.mozilla.gecko.mozglue;
 
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.InputStream;
+import java.util.Collection;
 import java.util.Locale;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
 
 import android.content.Context;
 import android.content.Intent;
 import android.os.Build;
 import android.os.Bundle;
@@ -87,27 +88,19 @@ public final class GeckoLoader {
         if (oldDir.exists()) {
             delTree(oldDir);
         }
         return tmpDir;
     }
 
     public synchronized static void setupGeckoEnvironment(final Context context,
                                                           final String profilePath,
-                                                          final Bundle extras) {
-        // if we have an intent (we're being launched by an activity)
-        // read in any environmental variables from it here
-        if (extras != null) {
-            String env = extras.getString("env0");
-            Log.d(LOGTAG, "Gecko environment env0: " + env);
-            for (int c = 1; env != null; c++) {
-                putenv(env);
-                env = extras.getString("env" + c);
-                Log.d(LOGTAG, "env" + c + ": " + env);
-            }
+                                                          final Collection<String> env) {
+        for (final String e : env) {
+            putenv(e);
         }
 
         try {
             final File dataDir = new File(context.getApplicationInfo().dataDir);
             putenv("MOZ_ANDROID_DATA_DIR=" + dataDir.getCanonicalPath());
         } catch (final java.io.IOException e) {
             Log.e(LOGTAG, "Failed to resolve app data directory");
         }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/process/GeckoProcessManager.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/process/GeckoProcessManager.java
@@ -176,16 +176,21 @@ public final class GeckoProcessManager e
 
     @WrapForJNI
     private static int start(final String type, final String[] args,
                              final int prefsFd, final int ipcFd,
                              final int crashFd, final int crashAnnotationFd) {
         return INSTANCE.start(type, args, prefsFd, ipcFd, crashFd, crashAnnotationFd, /* retry */ false);
     }
 
+    private int filterFlagsForChild(int flags) {
+        return flags & (GeckoThread.FLAG_ENABLE_JAVA_CRASHREPORTER |
+                GeckoThread.FLAG_ENABLE_NATIVE_CRASHREPORTER);
+    }
+
     private int start(final String type, final String[] args, final int prefsFd,
                       final int ipcFd, final int crashFd,
                       final int crashAnnotationFd, final boolean retry) {
         final ChildConnection connection = getConnection(type);
         final IChildProcess child = connection.bind();
         if (child == null) {
             return 0;
         }
@@ -200,19 +205,21 @@ public final class GeckoProcessManager e
             ipcPfd = ParcelFileDescriptor.fromFd(ipcFd);
             crashPfd = (crashFd >= 0) ? ParcelFileDescriptor.fromFd(crashFd) : null;
             crashAnnotationPfd = (crashAnnotationFd >= 0) ? ParcelFileDescriptor.fromFd(crashAnnotationFd) : null;
         } catch (final IOException e) {
             Log.e(LOGTAG, "Cannot create fd for " + type, e);
             return 0;
         }
 
+        final int flags = filterFlagsForChild(GeckoThread.getActiveFlags());
+
         boolean started = false;
         try {
-            started = child.start(this, args, extras, prefsPfd, ipcPfd, crashPfd,
+            started = child.start(this, args, extras, flags, prefsPfd, ipcPfd, crashPfd,
                                   crashAnnotationPfd);
         } catch (final RemoteException e) {
         }
 
         if (!started) {
             if (retry) {
                 Log.e(LOGTAG, "Cannot restart child " + type);
                 return 0;
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/process/GeckoServiceChildProcess.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/process/GeckoServiceChildProcess.java
@@ -19,19 +19,17 @@ import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
 import android.os.RemoteException;
 import android.util.Log;
 
 public class GeckoServiceChildProcess extends Service {
-
     private static final String LOGTAG = "GeckoServiceChildProcess";
-
     private static IProcessManager sProcessManager;
 
     @WrapForJNI(calledFrom = "gecko")
     private static IGeckoEditableParent getEditableParent(final long contentId,
                                                           final long tabId) {
         try {
             return sProcessManager.getEditableParent(contentId, tabId);
         } catch (final RemoteException e) {
@@ -39,17 +37,16 @@ public class GeckoServiceChildProcess ex
             return null;
         }
     }
 
     @Override
     public void onCreate() {
         super.onCreate();
 
-        GeckoAppShell.ensureCrashHandling();
         GeckoAppShell.setApplicationContext(getApplicationContext());
     }
 
     @Override
     public int onStartCommand(final Intent intent, final int flags, final int startId) {
         return Service.START_NOT_STICKY;
     }
 
@@ -58,16 +55,17 @@ public class GeckoServiceChildProcess ex
         public int getPid() {
             return Process.myPid();
         }
 
         @Override
         public boolean start(final IProcessManager procMan,
                              final String[] args,
                              final Bundle extras,
+                             final int flags,
                              final ParcelFileDescriptor prefsPfd,
                              final ParcelFileDescriptor ipcPfd,
                              final ParcelFileDescriptor crashReporterPfd,
                              final ParcelFileDescriptor crashAnnotationPfd) {
             synchronized (GeckoServiceChildProcess.class) {
                 if (sProcessManager != null) {
                     Log.e(LOGTAG, "Child process already started");
                     return false;
@@ -80,17 +78,17 @@ public class GeckoServiceChildProcess ex
             final int crashReporterFd = crashReporterPfd != null ?
                                         crashReporterPfd.detachFd() : -1;
             final int crashAnnotationFd = crashAnnotationPfd != null ?
                                           crashAnnotationPfd.detachFd() : -1;
 
             ThreadUtils.postToUiThread(new Runnable() {
                 @Override
                 public void run() {
-                    if (GeckoThread.initChildProcess(args, extras, prefsFd, ipcFd, crashReporterFd,
+                    if (GeckoThread.initChildProcess(args, extras, flags, prefsFd, ipcFd, crashReporterFd,
                                                      crashAnnotationFd)) {
                         GeckoThread.launch();
                     }
                 }
             });
             return true;
         }
 
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntime.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntime.java
@@ -79,19 +79,33 @@ public final class GeckoRuntime implemen
             }
         }
     };
 
     /* package */ boolean init(final @NonNull GeckoRuntimeSettings settings) {
         if (DEBUG) {
             Log.d(LOGTAG, "init");
         }
-        final int flags = settings.getUseContentProcessHint()
-                          ? GeckoThread.FLAG_PRELOAD_CHILD
-                          : 0;
+        int flags = 0;
+        if (settings.getUseContentProcessHint()) {
+            flags |= GeckoThread.FLAG_PRELOAD_CHILD;
+        }
+
+        if (settings.getNativeCrashReportingEnabled()) {
+            flags |= GeckoThread.FLAG_ENABLE_NATIVE_CRASHREPORTER;
+        }
+
+        if (settings.getJavaCrashReportingEnabled()) {
+            flags |= GeckoThread.FLAG_ENABLE_JAVA_CRASHREPORTER;
+        }
+
+        if (settings.getPauseForDebuggerEnabled()) {
+            flags |= GeckoThread.FLAG_DEBUGGING;
+        }
+
         if (GeckoThread.initMainProcess(/* profile */ null,
                                         settings.getArguments(),
                                         settings.getExtras(),
                                         flags)) {
             if (!GeckoThread.launch()) {
                 Log.d(LOGTAG, "init failed (GeckoThread already launched)");
                 return false;
             }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntimeSettings.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntimeSettings.java
@@ -35,17 +35,19 @@ public final class GeckoRuntimeSettings 
         public @NonNull GeckoRuntimeSettings build() {
             return new GeckoRuntimeSettings(mSettings);
         }
 
         /**
          * Set the content process hint flag.
          *
          * @param use If true, this will reload the content process for future use.
+         *            Default is false.
          * @return This Builder instance.
+
          */
         public @NonNull Builder useContentProcessHint(final boolean use) {
             mSettings.mUseContentProcess = use;
             return this;
         }
 
         /**
          * Set the custom Gecko process arguments.
@@ -74,16 +76,17 @@ public final class GeckoRuntimeSettings 
             mSettings.mExtras = extras;
             return this;
         }
 
         /**
          * Set whether JavaScript support should be enabled.
          *
          * @param flag A flag determining whether JavaScript should be enabled.
+         *             Default is true.
          * @return This Builder instance.
          */
         public @NonNull Builder javaScriptEnabled(final boolean flag) {
             mSettings.mJavaScript.set(flag);
             return this;
         }
 
         /**
@@ -96,22 +99,64 @@ public final class GeckoRuntimeSettings 
             mSettings.mRemoteDebugging.set(enabled);
             return this;
         }
 
         /**
          * Set whether support for web fonts should be enabled.
          *
          * @param flag A flag determining whether web fonts should be enabled.
+         *             Default is true.
          * @return This Builder instance.
          */
         public @NonNull Builder webFontsEnabled(final boolean flag) {
             mSettings.mWebFonts.set(flag);
             return this;
         }
+
+        /**
+         * Set whether crash reporting for native code should be enabled. This will cause
+         * a SIGSEGV handler to be installed, and any crash encountered there will be
+         * reported to Mozilla.
+         *
+         * @param enabled A flag determining whether native crash reporting should be enabled.
+         *                Defaults to false.
+         * @return This Builder.
+         */
+        public @NonNull Builder nativeCrashReportingEnabled(final boolean enabled) {
+            mSettings.mNativeCrashReporting = enabled;
+            return this;
+        }
+
+        /**
+         * Set whether crash reporting for Java code should be enabled. This will cause
+         * a default unhandled exception handler to be installed, and any exceptions encountered
+         * will automatically reported to Mozilla.
+         *
+         * @param enabled A flag determining whether Java crash reporting should be enabled.
+         *                Defaults to false.
+         * @return This Builder.
+         */
+        public @NonNull Builder javaCrashReportingEnabled(final boolean enabled) {
+            mSettings.mJavaCrashReporting = enabled;
+            return this;
+        }
+
+        /**
+         * Set whether there should be a pause during startup. This is useful if you need to
+         * wait for a debugger to attach.
+         *
+         * @param enabled A flag determining whether there will be a pause early in startup.
+         *                Defaults to false.
+         * @return This Builder.
+         */
+        public @NonNull Builder pauseForDebugger(boolean enabled) {
+            mSettings.mDebugPause = enabled;
+            return this;
+        }
     }
 
     /* package */ GeckoRuntime runtime;
     /* package */ boolean mUseContentProcess;
     /* package */ String[] mArgs;
     /* package */ Bundle mExtras;
     /* package */ int prefCount;
 
@@ -145,16 +190,19 @@ public final class GeckoRuntimeSettings 
     }
 
     /* package */ Pref<Boolean> mJavaScript = new Pref<Boolean>(
         "javascript.enabled", true);
     /* package */ Pref<Boolean> mRemoteDebugging = new Pref<Boolean>(
         "devtools.debugger.remote-enabled", false);
     /* package */ Pref<Boolean> mWebFonts = new Pref<Boolean>(
         "browser.display.use_document_fonts", true);
+    /* package */ boolean mNativeCrashReporting;
+    /* package */ boolean mJavaCrashReporting;
+    /* package */ boolean mDebugPause;
 
     private final Pref<?>[] mPrefs = new Pref<?>[] {
         mJavaScript, mRemoteDebugging, mWebFonts
     };
 
     /* package */ GeckoRuntimeSettings() {
         this(null);
     }
@@ -175,16 +223,19 @@ public final class GeckoRuntimeSettings 
         mExtras = new Bundle(settings.getExtras());
 
         for (int i = 0; i < mPrefs.length; i++) {
             // We know this is safe.
             @SuppressWarnings("unchecked")
             final Pref<Object> uncheckedPref = (Pref<Object>) mPrefs[i];
             uncheckedPref.set(settings.mPrefs[i].get());
         }
+
+        mNativeCrashReporting = settings.mNativeCrashReporting;
+        mJavaCrashReporting = settings.mJavaCrashReporting;
     }
 
     /* package */ void flush() {
         for (final Pref<?> pref: mPrefs) {
             pref.flush();
         }
     }
 
@@ -270,44 +321,77 @@ public final class GeckoRuntimeSettings 
      * @param flag A flag determining whether web fonts should be enabled.
      * @return This GeckoRuntimeSettings instance.
      */
     public @NonNull GeckoRuntimeSettings setWebFontsEnabled(final boolean flag) {
         mWebFonts.set(flag);
         return this;
     }
 
+    /**
+     * Get whether native crash reporting is enabled or not.
+     *
+     * @return True if native crash reporting is enabled.
+     */
+    public boolean getNativeCrashReportingEnabled() {
+        return mNativeCrashReporting;
+    }
+
+    /**
+     * Get whether Java crash reporting is enabled or not.
+     *
+     * @return True if Java crash reporting is enabled.
+     */
+    public boolean getJavaCrashReportingEnabled() {
+        return mJavaCrashReporting;
+    }
+
+    /**
+     * Gets whether the pause-for-debugger is enabled or not.
+     *
+     * @return True if the pause is enabled.
+     */
+    public boolean getPauseForDebuggerEnabled() { return mDebugPause; }
+
     @Override // Parcelable
     public int describeContents() {
         return 0;
     }
 
     @Override // Parcelable
     public void writeToParcel(Parcel out, int flags) {
-        out.writeByte((byte) (mUseContentProcess ? 1 : 0));
+        ParcelableUtils.writeBoolean(out, mUseContentProcess);
         out.writeStringArray(mArgs);
         mExtras.writeToParcel(out, flags);
 
         for (final Pref<?> pref : mPrefs) {
             out.writeValue(pref.get());
         }
+
+        ParcelableUtils.writeBoolean(out, mNativeCrashReporting);
+        ParcelableUtils.writeBoolean(out, mJavaCrashReporting);
+        ParcelableUtils.writeBoolean(out, mDebugPause);
     }
 
     // AIDL code may call readFromParcel even though it's not part of Parcelable.
     public void readFromParcel(final Parcel source) {
-        mUseContentProcess = source.readByte() == 1;
+        mUseContentProcess = ParcelableUtils.readBoolean(source);
         mArgs = source.createStringArray();
         mExtras.readFromParcel(source);
 
         for (final Pref<?> pref : mPrefs) {
             // We know this is safe.
             @SuppressWarnings("unchecked")
             final Pref<Object> uncheckedPref = (Pref<Object>) pref;
             uncheckedPref.set(source.readValue(getClass().getClassLoader()));
         }
+
+        mNativeCrashReporting = ParcelableUtils.readBoolean(source);
+        mJavaCrashReporting = ParcelableUtils.readBoolean(source);
+        mJavaCrashReporting = ParcelableUtils.readBoolean(source);
     }
 
     public static final Parcelable.Creator<GeckoRuntimeSettings> CREATOR
         = new Parcelable.Creator<GeckoRuntimeSettings>() {
         @Override
         public GeckoRuntimeSettings createFromParcel(final Parcel in) {
             final GeckoRuntimeSettings settings = new GeckoRuntimeSettings();
             settings.readFromParcel(in);
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/ParcelableUtils.java
@@ -0,0 +1,19 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * vim: ts=4 sw=4 expandtab:
+ * 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/. */
+
+package org.mozilla.geckoview;
+
+import android.os.Parcel;
+
+class ParcelableUtils {
+    public static void writeBoolean(Parcel out, boolean val) {
+        out.writeByte((byte) (val ? 1 : 0));
+    }
+
+    public static boolean readBoolean(Parcel source) {
+        return source.readByte() == 1;
+    }
+}
--- a/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java
+++ b/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java
@@ -62,17 +62,22 @@ public class GeckoViewActivity extends A
                 // each build.
                 runtimeSettingsBuilder.arguments(new String[] { "-purgecaches" });
             }
 
             final Bundle extras = getIntent().getExtras();
             if (extras != null) {
                 runtimeSettingsBuilder.extras(extras);
             }
-            runtimeSettingsBuilder.useContentProcessHint(useMultiprocess);
+
+            runtimeSettingsBuilder
+                    .useContentProcessHint(useMultiprocess)
+                    .nativeCrashReportingEnabled(true)
+                    .javaCrashReportingEnabled(true);
+
             sGeckoRuntime = GeckoRuntime.create(this, runtimeSettingsBuilder.build());
         }
 
         final GeckoSessionSettings sessionSettings = new GeckoSessionSettings();
         sessionSettings.setBoolean(GeckoSessionSettings.USE_MULTIPROCESS,
                                    useMultiprocess);
         mGeckoSession = new GeckoSession(sessionSettings);
 
--- a/mozglue/misc/ConditionVariable_windows.cpp
+++ b/mozglue/misc/ConditionVariable_windows.cpp
@@ -49,31 +49,31 @@ void
 mozilla::detail::ConditionVariableImpl::notify_all()
 {
   WakeAllConditionVariable(&platformData()->cv_);
 }
 
 void
 mozilla::detail::ConditionVariableImpl::wait(MutexImpl& lock)
 {
-  CRITICAL_SECTION* cs = &lock.platformData()->criticalSection;
-  bool r = SleepConditionVariableCS(&platformData()->cv_, cs, INFINITE);
+  SRWLOCK* srwlock = &lock.platformData()->lock;
+  bool r = SleepConditionVariableSRW(&platformData()->cv_, srwlock, INFINITE, 0);
   MOZ_RELEASE_ASSERT(r);
 }
 
 mozilla::CVStatus
 mozilla::detail::ConditionVariableImpl::wait_for(MutexImpl& lock,
                                                  const mozilla::TimeDuration& rel_time)
 {
   if (rel_time == mozilla::TimeDuration::Forever()) {
     wait(lock);
     return CVStatus::NoTimeout;
   }
 
-  CRITICAL_SECTION* cs = &lock.platformData()->criticalSection;
+  SRWLOCK* srwlock = &lock.platformData()->lock;
 
   // Note that DWORD is unsigned, so we have to be careful to clamp at 0. If
   // rel_time is Forever, then ToMilliseconds is +inf, which evaluates as
   // greater than UINT32_MAX, resulting in the correct INFINITE wait. We also
   // don't want to round sub-millisecond waits to 0, as that wastes energy (see
   // bug 1437167 comment 6), so we instead round submillisecond waits to 1ms.
   double msecd = rel_time.ToMilliseconds();
   DWORD msec;
@@ -84,17 +84,17 @@ mozilla::detail::ConditionVariableImpl::
   } else {
     msec = static_cast<DWORD>(msecd);
     // Round submillisecond waits to 1ms.
     if (msec == 0 && !rel_time.IsZero()) {
       msec = 1;
     }
   }
 
-  BOOL r = SleepConditionVariableCS(&platformData()->cv_, cs, msec);
+  BOOL r = SleepConditionVariableSRW(&platformData()->cv_, srwlock, msec, 0);
   if (r)
     return CVStatus::NoTimeout;
   MOZ_RELEASE_ASSERT(GetLastError() == ERROR_TIMEOUT);
   return CVStatus::Timeout;
 }
 
 mozilla::detail::ConditionVariableImpl::~ConditionVariableImpl()
 {
--- a/mozglue/misc/MutexPlatformData_windows.h
+++ b/mozglue/misc/MutexPlatformData_windows.h
@@ -8,12 +8,12 @@
 #define MutexPlatformData_windows_h
 
 #include <windows.h>
 
 #include "mozilla/PlatformMutex.h"
 
 struct mozilla::detail::MutexImpl::PlatformData
 {
-  CRITICAL_SECTION criticalSection;
+  SRWLOCK lock;
 };
 
 #endif // MutexPlatformData_windows_h
--- a/mozglue/misc/Mutex_windows.cpp
+++ b/mozglue/misc/Mutex_windows.cpp
@@ -9,48 +9,33 @@
 #include "mozilla/PlatformMutex.h"
 
 #include <windows.h>
 
 #include "MutexPlatformData_windows.h"
 
 mozilla::detail::MutexImpl::MutexImpl()
 {
-  // This number was adopted from NSPR.
-  const static DWORD LockSpinCount = 1500;
-
-#if defined(RELEASE_OR_BETA)
-  // Vista and later automatically allocate and subsequently leak a debug info
-  // object for each critical section that we allocate unless we tell the
-  // system not to do that.
-  DWORD flags = CRITICAL_SECTION_NO_DEBUG_INFO;
-#else
-  DWORD flags = 0;
-#endif // defined(RELEASE_OR_BETA)
-
-  BOOL r = InitializeCriticalSectionEx(&platformData()->criticalSection,
-                                       LockSpinCount, flags);
-  MOZ_RELEASE_ASSERT(r);
+  InitializeSRWLock(&platformData()->lock);
 }
 
 mozilla::detail::MutexImpl::~MutexImpl()
 {
-  DeleteCriticalSection(&platformData()->criticalSection);
 }
 
 void
 mozilla::detail::MutexImpl::lock()
 {
-  EnterCriticalSection(&platformData()->criticalSection);
+  AcquireSRWLockExclusive(&platformData()->lock);
 }
 
 void
 mozilla::detail::MutexImpl::unlock()
 {
-  LeaveCriticalSection(&platformData()->criticalSection);
+  ReleaseSRWLockExclusive(&platformData()->lock);
 }
 
 mozilla::detail::MutexImpl::PlatformData*
 mozilla::detail::MutexImpl::platformData()
 {
   static_assert(sizeof(platformData_) >= sizeof(PlatformData),
                 "platformData_ is too small");
   return reinterpret_cast<PlatformData*>(platformData_);
--- a/netwerk/dns/nsHostResolver.cpp
+++ b/netwerk/dns/nsHostResolver.cpp
@@ -293,16 +293,25 @@ nsHostRecord::ResolveComplete()
         } else {
             AccumulateCategorical(((mTrrDuration - mNativeDuration) > k50ms) ?
                                   Telemetry::LABELS_DNS_TRR_RACE::NativeFasterBy50 :
                                   Telemetry::LABELS_DNS_TRR_RACE::NativeFaster);
             LOG(("nsHostRecord::Complete %s Dns Race: NATIVE\n", host.get()));
         }
     }
 
+    if (mTRRUsed && mNativeUsed) {
+        // both were used, accumulate comparative success
+        AccumulateCategorical(mNativeSuccess && mTRRSuccess?
+                              Telemetry::LABELS_DNS_TRR_COMPARE::BothWorked :
+                              ((mNativeSuccess ? Telemetry::LABELS_DNS_TRR_COMPARE::NativeWorked :
+                                (mTRRSuccess ? Telemetry::LABELS_DNS_TRR_COMPARE::TRRWorked:
+                                 Telemetry::LABELS_DNS_TRR_COMPARE::BothFailed))));
+    }
+
     switch(mResolverMode) {
     case MODE_NATIVEONLY:
     case MODE_TRROFF:
         AccumulateCategorical(Telemetry::LABELS_DNS_LOOKUP_ALGORITHM::nativeOnly);
         break;
     case MODE_PARALLEL:
         AccumulateCategorical(Telemetry::LABELS_DNS_LOOKUP_ALGORITHM::trrRace);
         break;
@@ -1681,16 +1690,21 @@ nsHostResolver::CompleteLookup(nsHostRec
             RefPtr<nsHostRecord> head = mEvictionQ.popFirst();
             mRecordDB.Remove(*static_cast<nsHostKey *>(head.get()));
 
             if (!head->negative) {
                 // record the age of the entry upon eviction.
                 TimeDuration age = TimeStamp::NowLoRes() - head->mValidStart;
                 Telemetry::Accumulate(Telemetry::DNS_CLEANUP_AGE,
                                       static_cast<uint32_t>(age.ToSeconds() / 60));
+                if (head->CheckExpiration(TimeStamp::Now()) !=
+                    nsHostRecord::EXP_EXPIRED) {
+                  Telemetry::Accumulate(Telemetry::DNS_PREMATURE_EVICTION,
+                                        static_cast<uint32_t>(age.ToSeconds() / 60));
+                }
             }
         }
     }
 
 #ifdef DNSQUERY_AVAILABLE
     // Unless the result is from TRR, resolve again to get TTL
     bool fromTRR = false;
     {
--- a/netwerk/sctp/src/netinet/sctp_crc32.c
+++ b/netwerk/sctp/src/netinet/sctp_crc32.c
@@ -55,17 +55,17 @@
  *
  * Arguments:
  *
  *		p_running_crc - pointer to the initial or final remainder value
  *				used in CRC computations. It should be set to
  *				non-NULL if the mode argument is equal to CONT or END
  *		p_buf - the packet buffer where crc computations are being performed
  *		length - the length of p_buf in bytes
- *		init_bytes - the number of initial bytes that need to be procesed before
+ *		init_bytes - the number of initial bytes that need to be processed before
  *					 aligning p_buf to multiples of 4 bytes
  *		mode - can be any of the following: BEGIN, CONT, END, BODY, ALIGN
  *
  * Return value:
  *
  *		The computed CRC32c value
  */
 
--- a/security/certverifier/CertVerifier.h
+++ b/security/certverifier/CertVerifier.h
@@ -58,22 +58,26 @@ enum class SHA1ModeResult {
   SucceededWithImportedRoot = 2,
   SucceededWithImportedRootOrSHA1Before2016 = 3,
   SucceededWithSHA1 = 4,
   Failed = 5,
 };
 
 // Whether or not we are enforcing one of our CA distrust policies. For context,
 // see Bug 1437754 and Bug 1409257.
-enum class DistrustedCAPolicy : uint32_t {
-  Permit = 0,
-  DistrustSymantecRoots = 1,
-  DistrustSymantecRootsRegardlessOfDate = 2,
+enum DistrustedCAPolicy : uint32_t {
+  Permit = 0b0000,
+  DistrustSymantecRoots = 0b0001,
+  DistrustSymantecRootsRegardlessOfDate = 0b0010,
 };
 
+// Bitmask by nsNSSComponent to check for wholly-invalid values; be sure to
+// update this to account for new entries in DistrustedCAPolicy.
+const uint32_t DistrustedCAPolicyMaxAllowedValueMask = 0b0011;
+
 enum class NetscapeStepUpPolicy : uint32_t;
 
 class PinningTelemetryInfo
 {
 public:
   PinningTelemetryInfo() { Reset(); }
 
   // Should we accumulate pinning telemetry for the result?
--- a/security/certverifier/NSSCertDBTrustDomain.cpp
+++ b/security/certverifier/NSSCertDBTrustDomain.cpp
@@ -872,35 +872,36 @@ NSSCertDBTrustDomain::IsChainValid(const
   // See bug 1434300. If the root is a Symantec root, see if we distrust this
   // path. Since we already have the root available, we can check that cheaply
   // here before proceeding with the rest of the algorithm.
 
   // This algorithm only applies if we are verifying in the context of a TLS
   // handshake. To determine this, we check mHostname: If it isn't set, this is
   // not TLS, so don't run the algorithm.
   if (mHostname && CertDNIsInList(root.get(), RootSymantecDNs) &&
-      mDistrustedCAPolicy != DistrustedCAPolicy::Permit) {
+      ((mDistrustedCAPolicy & DistrustedCAPolicy::DistrustSymantecRoots) ||
+       (mDistrustedCAPolicy & DistrustedCAPolicy::DistrustSymantecRootsRegardlessOfDate))) {
 
     rootCert = nullptr; // Clear the state for Segment...
     nsCOMPtr<nsIX509CertList> intCerts;
     nsCOMPtr<nsIX509Cert> eeCert;
 
     nsrv = nssCertList->SegmentCertificateChain(rootCert, intCerts, eeCert);
     if (NS_FAILED(nsrv)) {
       // This chain is supposed to be complete, so this is an error.
       return Result::FATAL_ERROR_LIBRARY_FAILURE;
     }
 
     // PRTime is microseconds since the epoch, whereas JS time is milliseconds.
     // (new Date("2016-06-01T00:00:00Z")).getTime() * 1000
     static const PRTime JUNE_1_2016 = 1464739200000000;
 
-    PRTime permitAfterDate = 0; // 0 indicates there is no permitAfterDate
-    if (mDistrustedCAPolicy == DistrustedCAPolicy::DistrustSymantecRoots) {
-      permitAfterDate = JUNE_1_2016;
+    PRTime permitAfterDate = JUNE_1_2016;
+    if (mDistrustedCAPolicy & DistrustedCAPolicy::DistrustSymantecRootsRegardlessOfDate) {
+      permitAfterDate = 0; // 0 indicates there is no permitAfterDate
     }
 
     bool isDistrusted = false;
     nsrv = CheckForSymantecDistrust(intCerts, eeCert, permitAfterDate,
                                     RootAppleAndGoogleSPKIs, isDistrusted);
     if (NS_FAILED(nsrv)) {
       return Result::FATAL_ERROR_LIBRARY_FAILURE;
     }
--- a/security/manager/ssl/nsNSSComponent.cpp
+++ b/security/manager/ssl/nsNSSComponent.cpp
@@ -1683,24 +1683,20 @@ void nsNSSComponent::setValidationOption
   }
 
   DistrustedCAPolicy defaultCAPolicyMode =
     DistrustedCAPolicy::DistrustSymantecRoots;
   DistrustedCAPolicy distrustedCAPolicy =
     static_cast<DistrustedCAPolicy>
       (Preferences::GetUint("security.pki.distrust_ca_policy",
                             static_cast<uint32_t>(defaultCAPolicyMode)));
-  switch(distrustedCAPolicy) {
-    case DistrustedCAPolicy::Permit:
-    case DistrustedCAPolicy::DistrustSymantecRoots:
-    case DistrustedCAPolicy::DistrustSymantecRootsRegardlessOfDate:
-      break;
-    default:
-      distrustedCAPolicy = defaultCAPolicyMode;
-      break;
+  // If distrustedCAPolicy sets any bits larger than the maximum mask, fall back
+  // to the default.
+  if (distrustedCAPolicy & ~DistrustedCAPolicyMaxAllowedValueMask) {
+    distrustedCAPolicy = defaultCAPolicyMode;
   }
 
   CertVerifier::OcspDownloadConfig odc;
   CertVerifier::OcspStrictConfig osc;
   uint32_t certShortLifetimeInDays;
   TimeDuration softTimeout;
   TimeDuration hardTimeout;
 
--- a/security/manager/ssl/tests/unit/test_symantec_apple_google.js
+++ b/security/manager/ssl/tests/unit/test_symantec_apple_google.js
@@ -35,37 +35,37 @@ add_connection_test("symantec-not-whitel
                     PRErrorCodeSuccess, null, shouldBeImminentlyDistrusted);
 
 // Not whitelisted certs before the cutoff are to be distrusted
 add_connection_test("symantec-not-whitelisted-before-cutoff.example.com",
                     MOZILLA_PKIX_ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED,
                     null, null);
 
 // Enable the Firefox 63 total distrust; before or after cutoff should now all
-// behave the same.
+// behave the same. This will be made the default in Bug 1460062.
 add_test(function() {
   clearSessionCache();
   Services.prefs.setIntPref("security.pki.distrust_ca_policy",
-              /* DistrustedCAPolicy::DistrustSymantecRootsRegardlessOfDate */ 2);
+              /* DistrustedCAPolicy::DistrustSymantecRootsRegardlessOfDate */ 0b10);
   run_next_test();
 });
 
 add_connection_test("symantec-not-whitelisted-before-cutoff.example.com",
                     MOZILLA_PKIX_ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED,
                     null, null);
 
 add_connection_test("symantec-not-whitelisted-after-cutoff.example.com",
                     MOZILLA_PKIX_ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED,
                     null, null);
 
 // Disable the distrust, should be back to the console warning
 add_test(function() {
   clearSessionCache();
   Services.prefs.setIntPref("security.pki.distrust_ca_policy",
-                            /* DistrustedCAPolicy::Permit */ 0);
+                            /* DistrustedCAPolicy::Permit */ 0b00);
   run_next_test();
 });
 
 add_connection_test("symantec-not-whitelisted-before-cutoff.example.com",
                     PRErrorCodeSuccess, null, shouldBeImminentlyDistrusted);
 
 add_test(function() {
   clearSessionCache();
@@ -91,23 +91,38 @@ add_task(async function() {
   let whitelistedCert = constructCertFromFile("test_symantec_apple_google/real-googlecom.pem");
 
   // Since we don't want to actually try to fetch OCSP for this certificate,
   // (as an external fetch is bad in the tests), disable OCSP first.
   Services.prefs.setIntPref("security.OCSP.enabled", 0);
 
   // Try with the policy for 60
   Services.prefs.setIntPref("security.pki.distrust_ca_policy",
-                            /* DistrustedCAPolicy::DistrustSymantecRoots */ 1);
+                            /* DistrustedCAPolicy::DistrustSymantecRoots */ 0b01);
 
   // (new Date("2018-02-16")).getTime() / 1000
   const VALIDATION_TIME = 1518739200;
 
   await checkCertErrorGenericAtTime(certDB, whitelistedCert, PRErrorCodeSuccess,
                                     certificateUsageSSLServer, VALIDATION_TIME);
 
   // Try with the policy for 63
   Services.prefs.setIntPref("security.pki.distrust_ca_policy",
-                            /* DistrustedCAPolicy::DistrustSymantecRootsRegardlessOfDate */ 2);
+                            /* DistrustedCAPolicy::DistrustSymantecRootsRegardlessOfDate */ 0b10);
 
   await checkCertErrorGenericAtTime(certDB, whitelistedCert, PRErrorCodeSuccess,
                                     certificateUsageSSLServer, VALIDATION_TIME);
 });
+
+// Check invalid policy values; should default to current default
+add_test(function() {
+  clearSessionCache();
+  Services.prefs.setIntPref("security.pki.distrust_ca_policy",
+                            /* Larger than Max Value */ 0b1111);
+  run_next_test();
+});
+
+add_connection_test("symantec-not-whitelisted-before-cutoff.example.com",
+                    MOZILLA_PKIX_ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED,
+                    null, null);
+
+add_connection_test("symantec-not-whitelisted-after-cutoff.example.com",
+                    PRErrorCodeSuccess, null, shouldBeImminentlyDistrusted);
--- a/servo/components/style/properties/longhand/background.mako.rs
+++ b/servo/components/style/properties/longhand/background.mako.rs
@@ -49,17 +49,17 @@
     vector=True,
     spec="https://drafts.csswg.org/css-backgrounds/#the-background-repeat",
     flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
 )}
 
 ${helpers.single_keyword("background-attachment",
                          "scroll fixed" + (" local" if product == "gecko" else ""),
                          vector=True,
-                         gecko_constant_prefix="NS_STYLE_IMAGELAYER_ATTACHMENT",
+                         gecko_enum_prefix="StyleImageLayerAttachment",
                          spec="https://drafts.csswg.org/css-backgrounds/#the-background-attachment",
                          animation_value_type="discrete",
                          flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER")}
 
 ${helpers.single_keyword("background-clip",
                          "border-box padding-box content-box",
                          extra_gecko_values="text",
                          vector=True, extra_prefixes="webkit",
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -3226,16 +3226,27 @@
   "DNS_CLEANUP_AGE": {
     "record_in_processes": ["main"],
     "expires_in_version": "never",
     "kind": "exponential",
     "high": 1440,
     "n_buckets": 50,
     "description": "DNS Cache Entry Age at Removal Time (minutes)"
   },
+  "DNS_PREMATURE_EVICTION": {
+    "record_in_processes": ["main"],
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": 1440,
+    "releaseChannelCollection": "opt-out",
+    "alert_emails": ["necko@mozilla.com", "dstenberg@mozilla.com"],
+    "bug_numbers": [1460305],
+    "n_buckets": 50,
+    "description": "DNS Cache Entry Age at Removal Time of non-expired entries (minutes)"
+  },
   "DNS_LOOKUP_TIME": {
     "record_in_processes": ["main"],
     "expires_in_version": "never",
     "kind": "exponential",
     "high": 60000,
     "releaseChannelCollection": "opt-out",
     "alert_emails": ["necko@mozilla.com", "pmcmanus@mozilla.com"],
     "n_buckets": 50,
@@ -3267,16 +3278,25 @@
     "record_in_processes": ["main"],
     "alert_emails": ["necko@mozilla.com", "dstenberg@mozilla.com"],
     "expires_in_version": "never",
     "kind": "categorical",
     "labels": ["TRRFasterBy50", "TRRFaster", "NativeFaster", "NativeFasterBy50"],
     "bug_numbers": [1434852],
     "description": "DNS: TRR parallel resolve racing results"
   },
+  "DNS_TRR_COMPARE": {
+    "record_in_processes": ["main"],
+    "alert_emails": ["necko@mozilla.com", "dstenberg@mozilla.com"],
+    "expires_in_version": "never",
+    "kind": "categorical",
+    "labels": ["BothWorked", "NativeWorked", "TRRWorked", "BothFailed"],
+    "bug_numbers": [1460589],
+    "description": "DNS: success distribution when both native and TRR were used"
+  },
   "DNS_TRR_BLACKLISTED": {
     "record_in_processes": ["main"],
     "expires_in_version": "never",
     "kind": "boolean",
     "description": "DNS check for TRR was blocked by blacklist",
     "bug_numbers": [1434852],
     "alert_emails": ["necko@mozilla.com", "dstenberg@mozilla.com"]
   },
--- a/toolkit/crashreporter/nsExceptionHandler.cpp
+++ b/toolkit/crashreporter/nsExceptionHandler.cpp
@@ -835,27 +835,27 @@ LaunchCrashReporterActivity(XP_CHAR* aPr
 
   if (pid == -1)
     return false;
   else if (pid == 0) {
     // Invoke the reportCrash activity using am
     if (androidUserSerial) {
       Unused << execlp("/system/bin/am",
                        "/system/bin/am",
-                       "start",
+                       "startservice",
                        "--user", androidUserSerial,
                        "-a", "org.mozilla.gecko.reportCrash",
                        "-n", aProgramPath,
                        "--es", "minidumpPath", aMinidumpPath,
                        "--ez", "minidumpSuccess", aSucceeded ? "true" : "false",
                        (char*)0);
     } else {
       Unused << execlp("/system/bin/am",
                        "/system/bin/am",
-                       "start",
+                       "startservice",
                        "-a", "org.mozilla.gecko.reportCrash",
                        "-n", aProgramPath,
                        "--es", "minidumpPath", aMinidumpPath,
                        "--ez", "minidumpSuccess", aSucceeded ? "true" : "false",
                        (char*)0);
     }
     _exit(1);
 
@@ -1539,20 +1539,20 @@ nsresult SetExceptionHandler(nsIFile* aX
 #endif // XP_WIN32
 #else
   // On Android, we launch using the application package name instead of a
   // filename, so use the dynamically set MOZ_ANDROID_PACKAGE_NAME, or fall
   // back to the static ANDROID_PACKAGE_NAME.
   const char* androidPackageName = PR_GetEnv("MOZ_ANDROID_PACKAGE_NAME");
   if (androidPackageName != nullptr) {
     nsCString package(androidPackageName);
-    package.AppendLiteral("/org.mozilla.gecko.CrashReporter");
+    package.AppendLiteral("/org.mozilla.gecko.CrashReporterService");
     crashReporterPath = ToNewCString(package);
   } else {
-    nsCString package(ANDROID_PACKAGE_NAME "/org.mozilla.gecko.CrashReporter");
+    nsCString package(ANDROID_PACKAGE_NAME "/org.mozilla.gecko.CrashReporterService");
     crashReporterPath = ToNewCString(package);
   }
 #endif // !defined(MOZ_WIDGET_ANDROID)
 
   // get temp path to use for minidump path
 #if defined(XP_WIN32)
   nsString tempPath;
 #else
--- a/widget/android/nsAppShell.cpp
+++ b/widget/android/nsAppShell.cpp
@@ -402,16 +402,17 @@ nsAppShell::nsAppShell()
     {
         MutexAutoLock lock(*sAppShellLock);
         sAppShell = this;
     }
 
     if (!XRE_IsParentProcess()) {
         if (jni::IsAvailable()) {
             GeckoThreadSupport::Init();
+            GeckoAppShellSupport::Init();
 
             // Set the corresponding state in GeckoThread.
             java::GeckoThread::SetState(java::GeckoThread::State::RUNNING());
         }
         return;
     }
 
     if (jni::IsAvailable()) {
--- a/widget/cocoa/TextInputHandler.mm
+++ b/widget/cocoa/TextInputHandler.mm
@@ -3747,17 +3747,17 @@ IMEInputHandler::MaybeDispatchCurrentKey
   if (NS_WARN_IF(!nativeEvent) ||
       [nativeEvent type] != NSKeyDown) {
     return true;
   }
 
   MOZ_LOG(gLog, LogLevel::Info,
     ("%p IMEInputHandler::MaybeDispatchKeydownEvent, aIsProcessedByIME=%s "
      "currentKeyEvent={ mKeyEvent(%p)={ type=%s, keyCode=%s (0x%X) } }, "
-     "aIsProcesedBy=%s, IsDeadKeyComposing()=%s",
+     "aIsProcessedBy=%s, IsDeadKeyComposing()=%s",
      this, TrueOrFalse(aIsProcessedByIME), nativeEvent,
      GetNativeKeyEventType(nativeEvent),
      GetKeyNameForNativeKeyCode([nativeEvent keyCode]), [nativeEvent keyCode],
      TrueOrFalse(IsIMEComposing()), TrueOrFalse(IsDeadKeyComposing())));
 
   RefPtr<IMEInputHandler> kungFuDeathGrip(this);
   RefPtr<TextEventDispatcher> dispatcher(mDispatcher);
   nsresult rv = dispatcher->BeginNativeInputTransaction();