Merge mozilla-central to autoland. a=merge on a CLOSED TREE
authorDaniel Varga <dvarga@mozilla.com>
Fri, 11 Jan 2019 06:19:53 +0200
changeset 510544 9afb01b30049bad89a837b6889da359df70a2a61
parent 510543 897a97a4931de01e125e04a23a0139693c4ca163 (current diff)
parent 510482 6317367156ddbc537c700dbd9a8ebdf1e371ae8a (diff)
child 510545 7dcfa876f76cd181cb7f8d482770c09b32f6a849
push id10547
push userffxbld-merge
push dateMon, 21 Jan 2019 13:03:58 +0000
treeherdermozilla-beta@24ec1916bffe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone66.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to autoland. a=merge on a CLOSED TREE
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2420,16 +2420,17 @@ dependencies = [
  "bindgen 0.43.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "cssparser 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.19.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "fallible 0.0.1",
  "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "hashglobe 0.1.0",
+ "indexmap 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "itertools 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "malloc_size_of 0.0.1",
  "malloc_size_of_derive 0.0.1",
  "matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "new_debug_unreachable 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -1918,16 +1918,18 @@ bool GLContext::AssembleOffscreenFBs(con
   }
 
   return isComplete;
 }
 
 void GLContext::MarkDestroyed() {
   if (IsDestroyed()) return;
 
+  OnMarkDestroyed();
+
   // Null these before they're naturally nulled after dtor, as we want GLContext
   // to still be alive in *their* dtors.
   mScreen = nullptr;
   mBlitHelper = nullptr;
   mReadTexImageHelper = nullptr;
 
   mContextLost = true;
   mSymbols = {};
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -3317,16 +3317,19 @@ class GLContext : public GLLibraryLoader
   // Destructor
  public:
   virtual ~GLContext();
 
   // Mark this context as destroyed.  This will nullptr out all
   // the GL function pointers!
   void MarkDestroyed();
 
+ protected:
+  virtual void OnMarkDestroyed() {}
+
   // -----------------------------------------------------------------------------
   // Everything that isn't standard GL APIs
  protected:
   typedef gfx::SurfaceFormat SurfaceFormat;
 
  public:
   virtual bool Init() = 0;
 
--- a/gfx/gl/GLContextEGL.h
+++ b/gfx/gl/GLContextEGL.h
@@ -90,16 +90,18 @@ class GLContextEGL : public GLContext {
   static already_AddRefed<GLContextEGL> CreateEGLPBufferOffscreenContext(
       CreateContextFlags flags, const gfx::IntSize& size,
       const SurfaceCaps& minCaps, nsACString* const out_FailureId);
 
  protected:
   friend class GLContextProviderEGL;
   friend class GLContextEGLFactory;
 
+  virtual void OnMarkDestroyed() override;
+
  public:
   const EGLConfig mConfig;
 
  protected:
   const RefPtr<GLLibraryEGL> mEgl;
   EGLSurface mSurface;
   const EGLSurface mFallbackSurface;
 
--- a/gfx/gl/GLContextProviderEGL.cpp
+++ b/gfx/gl/GLContextProviderEGL.cpp
@@ -305,16 +305,23 @@ GLContextEGL::GLContextEGL(CreateContext
       mFallbackSurface(CreateFallbackSurface(config)),
       mContext(context) {
 #ifdef DEBUG
   printf_stderr("Initializing context %p surface %p on display %p\n", mContext,
                 mSurface, EGL_DISPLAY());
 #endif
 }
 
+void
+GLContextEGL::OnMarkDestroyed() {
+  if (mSurfaceOverride != EGL_NO_SURFACE) {
+    SetEGLSurfaceOverride(EGL_NO_SURFACE);
+  }
+}
+
 GLContextEGL::~GLContextEGL() {
   MarkDestroyed();
 
   // Wrapped context should not destroy eglContext/Surface
   if (!mOwnsContext) {
     return;
   }
 
--- a/gfx/gl/SharedSurfaceANGLE.cpp
+++ b/gfx/gl/SharedSurfaceANGLE.cpp
@@ -93,18 +93,20 @@ SharedSurface_ANGLEShareHandle::SharedSu
     : SharedSurface(SharedSurfaceType::EGLSurfaceANGLE, AttachmentType::Screen,
                     gl, size, hasAlpha, true),
       mEGL(egl),
       mPBuffer(pbuffer),
       mShareHandle(shareHandle),
       mKeyedMutex(keyedMutex) {}
 
 SharedSurface_ANGLEShareHandle::~SharedSurface_ANGLEShareHandle() {
-  if (GLContextEGL::Cast(mGL)->GetEGLSurfaceOverride() == mPBuffer) {
-    GLContextEGL::Cast(mGL)->SetEGLSurfaceOverride(EGL_NO_SURFACE);
+  GLContext* gl = mGL;
+
+  if (gl && GLContextEGL::Cast(gl)->GetEGLSurfaceOverride() == mPBuffer) {
+    GLContextEGL::Cast(gl)->SetEGLSurfaceOverride(EGL_NO_SURFACE);
   }
   mEGL->fDestroySurface(Display(), mPBuffer);
 }
 
 void SharedSurface_ANGLEShareHandle::LockProdImpl() {
   GLContextEGL::Cast(mGL)->SetEGLSurfaceOverride(mPBuffer);
 }
 
--- a/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp
@@ -384,16 +384,18 @@ void CrossProcessCompositorBridgeParent:
         MakeUnique<ContentBuildPayload>(aInfo.transactionStart(), endTime));
   }
 #endif
   Telemetry::Accumulate(
       Telemetry::CONTENT_FULL_PAINT_TIME,
       static_cast<uint32_t>(
           (endTime - aInfo.transactionStart()).ToMilliseconds()));
 
+  RegisterPayload(aLayerTree, aInfo.payload());
+
   aLayerTree->SetPendingTransactionId(
       aInfo.id(), aInfo.vsyncId(), aInfo.vsyncStart(), aInfo.refreshStart(),
       aInfo.transactionStart(), endTime, aInfo.url(), aInfo.fwdTime());
 }
 
 void CrossProcessCompositorBridgeParent::DidCompositeLocked(
     LayersId aId, const VsyncId& aVsyncId, TimeStamp& aCompositeStart,
     TimeStamp& aCompositeEnd) {
--- a/ipc/glue/GeckoChildProcessHost.cpp
+++ b/ipc/glue/GeckoChildProcessHost.cpp
@@ -28,26 +28,22 @@
 #include "nsAppDirectoryServiceDefs.h"
 #endif
 
 #include "nsExceptionHandler.h"
 
 #include "nsDirectoryServiceDefs.h"
 #include "nsIFile.h"
 #include "nsPrintfCString.h"
-#include "nsIObserverService.h"
 
+#include "mozilla/ClearOnShutdown.h"
 #include "mozilla/ipc/BrowserProcessSubThread.h"
 #include "mozilla/ipc/EnvironmentMap.h"
 #include "mozilla/Omnijar.h"
-#include "mozilla/RecordReplay.h"
 #include "mozilla/Scoped.h"
-#include "mozilla/Services.h"
-#include "mozilla/SharedThreadPool.h"
-#include "mozilla/StaticMutex.h"
 #include "mozilla/Telemetry.h"
 #include "ProtocolUtils.h"
 #include <sys/stat.h>
 
 #ifdef XP_WIN
 #include "nsIWinTaskbar.h"
 #include <stdlib.h>
 #define NS_TASKBAR_CONTRACTID "@mozilla.org/windows-taskbar;1"
@@ -66,17 +62,16 @@
 #include "nsTArray.h"
 #include "nsClassHashtable.h"
 #include "nsHashKeys.h"
 #include "nsNativeCharsetUtils.h"
 #include "nscore.h"  // for NS_FREE_PERMANENT_DATA
 #include "private/pprio.h"
 
 using mozilla::MonitorAutoLock;
-using mozilla::StaticMutexAutoLock;
 using mozilla::ipc::GeckoChildProcessHost;
 
 namespace mozilla {
 MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPRFileDesc, PRFileDesc,
                                           PR_Close)
 }
 
 using mozilla::ScopedPRFileDesc;
@@ -465,140 +460,36 @@ void GeckoChildProcessHost::GetChildLogN
     buffer.Append(origLogName);
   }
 
   // Append child-specific postfix to name
   buffer.AppendLiteral(".child-");
   buffer.AppendInt(mChildCounter);
 }
 
-namespace {
-// Windows needs a single dedicated thread for process launching,
-// because of thread-safety restrictions/assertions in the sandbox
-// code.  (This implementation isn't itself Windows-specific, so
-// the ifdef can be changed to test on other platforms.)
-#ifdef XP_WIN
-
-static mozilla::StaticMutex gIPCLaunchThreadMutex;
-static mozilla::StaticRefPtr<nsIThread> gIPCLaunchThread;
-
-class IPCLaunchThreadObserver final : public nsIObserver {
- public:
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSIOBSERVER
- protected:
-  virtual ~IPCLaunchThreadObserver() = default;
-};
-
-NS_IMPL_ISUPPORTS(IPCLaunchThreadObserver, nsIObserver, nsISupports)
-
-NS_IMETHODIMP
-IPCLaunchThreadObserver::Observe(nsISupports* aSubject, const char* aTopic,
-                                 const char16_t* aData) {
-  MOZ_RELEASE_ASSERT(strcmp(aTopic, "xpcom-shutdown-threads") == 0);
-  StaticMutexAutoLock lock(gIPCLaunchThreadMutex);
-
-  nsresult rv = NS_OK;
-  if (gIPCLaunchThread) {
-    rv = gIPCLaunchThread->Shutdown();
-    gIPCLaunchThread = nullptr;
-  }
-  mozilla::Unused << NS_WARN_IF(NS_FAILED(rv));
-  return rv;
-}
+bool GeckoChildProcessHost::RunPerformAsyncLaunch(
+    std::vector<std::string> aExtraOpts) {
+  InitializeChannel();
 
-static nsCOMPtr<nsIEventTarget> GetIPCLauncher() {
-  StaticMutexAutoLock lock(gIPCLaunchThreadMutex);
-  if (!gIPCLaunchThread) {
-    nsCOMPtr<nsIThread> thread;
-    nsresult rv = NS_NewNamedThread(NS_LITERAL_CSTRING("IPC Launch"),
-                                    getter_AddRefs(thread));
-    if (!NS_WARN_IF(NS_FAILED(rv))) {
-      NS_DispatchToMainThread(
-          NS_NewRunnableFunction("GeckoChildProcessHost::GetIPCLauncher", [] {
-            nsCOMPtr<nsIObserverService> obsService =
-                mozilla::services::GetObserverService();
-            nsCOMPtr<nsIObserver> obs = new IPCLaunchThreadObserver();
-            obsService->AddObserver(obs, "xpcom-shutdown-threads", false);
-          }));
-      gIPCLaunchThread = thread.forget();
-    }
-  }
-
-  nsCOMPtr<nsIEventTarget> thread = gIPCLaunchThread.get();
-  return thread;
-}
-
-#else  // XP_WIN
-
-// Non-Windows platforms can use an on-demand thread pool.
-
-static nsCOMPtr<nsIEventTarget> GetIPCLauncher() {
-  nsCOMPtr<nsIEventTarget> pool =
-      mozilla::SharedThreadPool::Get(NS_LITERAL_CSTRING("IPC Launch"));
-  return pool;
-}
-
-#endif  // XP_WIN
-}  // anonymous namespace
-
-void GeckoChildProcessHost::RunPerformAsyncLaunch(
-    std::vector<std::string> aExtraOpts) {
-  auto fail = [this] {
+  bool ok = PerformAsyncLaunch(aExtraOpts);
+  if (!ok) {
+    // WaitUntilConnected might be waiting for us to signal.
+    // If something failed let's set the error state and notify.
     MonitorAutoLock lock(mMonitor);
     mProcessState = PROCESS_ERROR;
     mHandlePromise->Reject(LaunchError{}, __func__);
     lock.Notify();
-  };
-
-  // This (probably?) needs to happen on the I/O thread.
-  InitializeChannel();
-
-  // But the rest of this doesn't, and shouldn't block IPC messages:
-  auto launchWrapper = [this, fail, aExtraOpts = std::move(aExtraOpts)]() {
-    bool ok = PerformAsyncLaunch(aExtraOpts);
-
-    if (!ok) {
-      // WaitUntilConnected might be waiting for us to signal.
-      // If something failed let's set the error state and notify.
-      fail();
-      CHROMIUM_LOG(ERROR) << "Failed to launch "
-                          << XRE_ChildProcessTypeToString(mProcessType)
-                          << " subprocess";
-      Telemetry::Accumulate(
-          Telemetry::SUBPROCESS_LAUNCH_FAILURE,
-          nsDependentCString(XRE_ChildProcessTypeToString(mProcessType)));
-    }
-  };
-
-  // The Web Replay middleman process launches the actual content
-  // processes, and doesn't initialize enough of XPCOM to use thread
-  // pools.
-  if (!mozilla::recordreplay::IsMiddleman()) {
-    auto launcher = GetIPCLauncher();
-    MOZ_DIAGNOSTIC_ASSERT(launcher != nullptr);
-    // Creating a thread pool shouldn't normally fail, but in case it
-    // does, use the fallback we already have for the middleman case.
-    if (launcher != nullptr) {
-      nsresult rv = launcher->Dispatch(
-          NS_NewRunnableFunction(
-              "ipc::GeckoChildProcessHost::PerformAsyncLaunch", launchWrapper),
-          NS_DISPATCH_NORMAL);
-      if (NS_FAILED(rv)) {
-        fail();
-        CHROMIUM_LOG(ERROR) << "Failed to dispatch launch task for "
-                            << XRE_ChildProcessTypeToString(mProcessType)
-                            << " process; launching during shutdown?";
-      }
-      return;
-    }
+    CHROMIUM_LOG(ERROR) << "Failed to launch "
+                        << XRE_ChildProcessTypeToString(mProcessType)
+                        << " subprocess";
+    Telemetry::Accumulate(
+        Telemetry::SUBPROCESS_LAUNCH_FAILURE,
+        nsDependentCString(XRE_ChildProcessTypeToString(mProcessType)));
   }
-
-  // Fall back to launching on the I/O thread.
-  launchWrapper();
+  return ok;
 }
 
 void
 #if defined(XP_WIN)
 AddAppDirToCommandLine(CommandLine& aCmdLine)
 #else
 AddAppDirToCommandLine(std::vector<std::string>& aCmdLine)
 #endif
@@ -834,31 +725,16 @@ bool GeckoChildProcessHost::PerformAsync
       childArgv.push_back("-appomni");
       childArgv.push_back(path.get());
     }
   }
 
   // Add the application directory path (-appdir path)
   AddAppDirToCommandLine(childArgv);
 
-  // Tmp dir that the GPU or RDD process should use for crash reports.
-  // This arg is always populated (but possibly with an empty value) for
-  // a GPU or RDD child process.
-  if (mProcessType == GeckoProcessType_GPU ||
-      mProcessType == GeckoProcessType_RDD ||
-      mProcessType == GeckoProcessType_VR) {
-    nsCOMPtr<nsIFile> file;
-    CrashReporter::GetChildProcessTmpDir(getter_AddRefs(file));
-    nsAutoCString path;
-    if (file) {
-      file->GetNativePath(path);
-    }
-    childArgv.push_back(path.get());
-  }
-
   childArgv.push_back(pidstring);
 
   if (!CrashReporter::IsDummy()) {
 #if defined(OS_LINUX) || defined(OS_BSD) || defined(OS_SOLARIS)
     int childCrashFd, childCrashRemapFd;
     if (!CrashReporter::CreateNotificationPipeForChild(&childCrashFd,
                                                        &childCrashRemapFd)) {
       return false;
@@ -1119,31 +995,16 @@ bool GeckoChildProcessHost::PerformAsync
 
   // XXX Command line params past this point are expected to be at
   // the end of the command line string, and in a specific order.
   // See XRE_InitChildProcess in nsEmbedFunction.
 
   // Win app model id
   cmdLine.AppendLooseValue(mGroupId.get());
 
-  // Tmp dir that the GPU or RDD process should use for crash reports.
-  // This arg is always populated (but possibly with an empty value) for
-  // a GPU or RDD child process.
-  if (mProcessType == GeckoProcessType_GPU ||
-      mProcessType == GeckoProcessType_RDD) {
-    nsCOMPtr<nsIFile> file;
-    CrashReporter::GetChildProcessTmpDir(getter_AddRefs(file));
-    nsString path;
-    if (file) {
-      MOZ_ALWAYS_SUCCEEDS(file->GetPath(path));
-    }
-    std::wstring wpath(path.get());
-    cmdLine.AppendLooseValue(wpath);
-  }
-
   // Process id
   cmdLine.AppendLooseValue(UTF8ToWide(pidstring));
 
   cmdLine.AppendLooseValue(
       UTF8ToWide(CrashReporter::GetChildNotificationPipe()));
 
   if (!CrashReporter::IsDummy()) {
     PROsfd h = PR_FileDesc2NativeHandle(crashAnnotationWritePipe);
@@ -1219,22 +1080,17 @@ bool GeckoChildProcessHost::PerformAsync
   ) {
     MOZ_CRASH("cannot open handle to child process");
   }
 
   CrashReporter::RegisterChildCrashAnnotationFileDescriptor(
       base::GetProcId(process), crashAnnotationReadPipe.forget());
 
   MonitorAutoLock lock(mMonitor);
-  // This runs on a launch thread, but the OnChannel{Connected,Error} callbacks
-  // run on the I/O thread, so it's possible that the state already advanced
-  // beyond PROCESS_CREATED.
-  if (mProcessState < PROCESS_CREATED) {
-    mProcessState = PROCESS_CREATED;
-  }
+  mProcessState = PROCESS_CREATED;
   mHandlePromise->Resolve(process, __func__);
   lock.Notify();
 
   mLaunchOptions = nullptr;
 
   Telemetry::AccumulateTimeDelta(Telemetry::CHILD_PROCESS_LAUNCH_MS, startTS);
 
   return true;
@@ -1249,16 +1105,17 @@ bool GeckoChildProcessHost::OpenPrivileg
   return base::OpenPrivilegedProcessHandle(aPid, &mChildProcessHandle);
 }
 
 void GeckoChildProcessHost::OnChannelConnected(int32_t peer_pid) {
   if (!OpenPrivilegedHandle(peer_pid)) {
     MOZ_CRASH("can't open handle to child process");
   }
   MonitorAutoLock lock(mMonitor);
+  MOZ_DIAGNOSTIC_ASSERT(mProcessState == PROCESS_CREATED);
   mProcessState = PROCESS_CONNECTED;
   lock.Notify();
 }
 
 void GeckoChildProcessHost::OnMessageReceived(IPC::Message&& aMsg) {
   // We never process messages ourself, just save them up for the next
   // listener.
   mQueue.push(std::move(aMsg));
@@ -1266,16 +1123,17 @@ void GeckoChildProcessHost::OnMessageRec
 
 void GeckoChildProcessHost::OnChannelError() {
   // Update the process state to an error state if we have a channel
   // error before we're connected. This fixes certain failures,
   // but does not address the full range of possible issues described
   // in the FIXME comment below.
   MonitorAutoLock lock(mMonitor);
   if (mProcessState < PROCESS_CONNECTED) {
+    MOZ_DIAGNOSTIC_ASSERT(mProcessState == PROCESS_CREATED);
     mProcessState = PROCESS_ERROR;
     lock.Notify();
   }
   // FIXME/bug 773925: save up this error for the next listener.
 }
 
 RefPtr<GeckoChildProcessHost::HandlePromise>
 GeckoChildProcessHost::WhenProcessHandleReady() {
--- a/ipc/glue/GeckoChildProcessHost.h
+++ b/ipc/glue/GeckoChildProcessHost.h
@@ -176,23 +176,23 @@ class GeckoChildProcessHost : public Chi
 #endif
   RefPtr<HandlePromise::Private> mHandlePromise;
 
   bool OpenPrivilegedHandle(base::ProcessId aPid);
 
  private:
   DISALLOW_EVIL_CONSTRUCTORS(GeckoChildProcessHost);
 
-  // Does the actual work for AsyncLaunch; run in a thread pool
-  // (or, on Windows, a dedicated thread).
+  // Does the actual work for AsyncLaunch, on the IO thread.
+  // (TODO, bug 1487287: move this to its own thread(s).)
   bool PerformAsyncLaunch(StringVector aExtraOpts);
 
-  // Called on the I/O thread; creates channel, dispatches
-  // PerformAsyncLaunch, and consolidates error handling.
-  void RunPerformAsyncLaunch(StringVector aExtraOpts);
+  // Also called on the I/O thread; creates channel, launches, and
+  // consolidates error handling.
+  bool RunPerformAsyncLaunch(StringVector aExtraOpts);
 
   enum class BinaryPathType { Self, PluginContainer };
 
   static BinaryPathType GetPathToBinary(FilePath& exePath,
                                         GeckoProcessType processType);
 
   // The buffer is passed to preserve its lifetime until we are done
   // with launching the sub-process.
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -12673,24 +12673,26 @@ void CodeGenerator::visitHasClass(LHasCl
 
   masm.loadObjClassUnsafe(lhs, output);
   masm.cmpPtrSet(Assembler::Equal, output, ImmPtr(ins->mir()->getClass()),
                  output);
 }
 
 void CodeGenerator::visitGuardToClass(LGuardToClass* ins) {
   Register lhs = ToRegister(ins->lhs());
-  Register output = ToRegister(ins->output());
   Register temp = ToRegister(ins->temp());
 
+  // branchTestObjClass may zero the object register on speculative paths
+  // (we should have a defineReuseInput allocation in this case).
+  Register spectreRegToZero = lhs;
+
   Label notEqual;
 
   masm.branchTestObjClass(Assembler::NotEqual, lhs, ins->mir()->getClass(),
-                          temp, output, &notEqual);
-  masm.mov(lhs, output);
+                          temp, spectreRegToZero, &notEqual);
 
   // Can't return null-return here, so bail.
   bailoutFrom(&notEqual, ins->snapshot());
 }
 
 typedef JSString* (*ObjectClassToStringFn)(JSContext*, HandleObject);
 static const VMFunction ObjectClassToStringInfo =
     FunctionInfo<ObjectClassToStringFn>(js::ObjectClassToString,
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -4203,19 +4203,19 @@ void LIRGenerator::visitHasClass(MHasCla
   MOZ_ASSERT(ins->type() == MIRType::Boolean);
   define(new (alloc()) LHasClass(useRegister(ins->object())), ins);
 }
 
 void LIRGenerator::visitGuardToClass(MGuardToClass* ins) {
   MOZ_ASSERT(ins->object()->type() == MIRType::Object);
   MOZ_ASSERT(ins->type() == MIRType::Object);
   LGuardToClass* lir =
-      new (alloc()) LGuardToClass(useRegister(ins->object()), temp());
+      new (alloc()) LGuardToClass(useRegisterAtStart(ins->object()), temp());
   assignSnapshot(lir, Bailout_TypeBarrierO);
-  define(lir, ins);
+  defineReuseInput(lir, ins, 0);
 }
 
 void LIRGenerator::visitObjectClassToString(MObjectClassToString* ins) {
   MOZ_ASSERT(ins->object()->type() == MIRType::Object);
   MOZ_ASSERT(ins->type() == MIRType::String);
   auto lir =
       new (alloc()) LObjectClassToString(useRegisterAtStart(ins->object()));
   defineReturn(lir, ins);
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -1172,17 +1172,18 @@ static void AllocateObjectBufferWithInit
   if (count <= 0 || uint32_t(count) >= INT32_MAX / obj->bytesPerElement()) {
     obj->setFixedSlot(TypedArrayObject::LENGTH_SLOT, Int32Value(0));
     return;
   }
 
   obj->setFixedSlot(TypedArrayObject::LENGTH_SLOT, Int32Value(count));
 
   size_t nbytes = count * obj->bytesPerElement();
-  MOZ_ASSERT((CheckedUint32(nbytes) + sizeof(Value)).isValid());
+  MOZ_ASSERT((CheckedUint32(nbytes) + sizeof(Value)).isValid(),
+             "JS_ROUNDUP must not overflow");
 
   nbytes = JS_ROUNDUP(nbytes, sizeof(Value));
   void* buf = cx->nursery().allocateZeroedBuffer(obj, nbytes,
                                                  js::ArrayBufferContentsArena);
   if (buf) {
     obj->initPrivate(buf);
   }
 }
--- a/js/src/jit/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -1280,28 +1280,16 @@ void CodeGenerator::visitTrunc(LTrunc* l
 void CodeGenerator::visitTruncF(LTruncF* lir) {
   FloatRegister input = ToFloatRegister(lir->input());
   Register output = ToRegister(lir->output());
   Label bail;
   masm.truncf(input, output, &bail);
   bailoutFrom(&bail, lir->snapshot());
 }
 
-void CodeGeneratorARM::emitRoundDouble(FloatRegister src, Register dest,
-                                       Label* fail) {
-  ScratchDoubleScope scratch(masm);
-  ScratchRegisterScope scratchReg(masm);
-
-  masm.ma_vcvt_F64_I32(src, scratch);
-  masm.ma_vxfer(scratch, dest);
-  masm.ma_cmp(dest, Imm32(0x7fffffff), scratchReg);
-  masm.ma_cmp(dest, Imm32(0x80000000), scratchReg, Assembler::NotEqual);
-  masm.ma_b(fail, Assembler::Equal);
-}
-
 void CodeGenerator::visitTruncateDToInt32(LTruncateDToInt32* ins) {
   emitTruncateDouble(ToFloatRegister(ins->input()), ToRegister(ins->output()),
                      ins->mir());
 }
 
 void CodeGenerator::visitTruncateFToInt32(LTruncateFToInt32* ins) {
   emitTruncateFloat32(ToFloatRegister(ins->input()), ToRegister(ins->output()),
                       ins->mir());
--- a/js/src/jit/arm/CodeGenerator-arm.h
+++ b/js/src/jit/arm/CodeGenerator-arm.h
@@ -63,18 +63,16 @@ class CodeGeneratorARM : public CodeGene
   }
 
   template <class T>
   void generateUDivModZeroCheck(Register rhs, Register output, Label* done,
                                 LSnapshot* snapshot, T* mir);
 
   bool generateOutOfLineCode();
 
-  void emitRoundDouble(FloatRegister src, Register dest, Label* fail);
-
   // Emits a branch that directs control flow to the true block if |cond| is
   // true, and the false block if |cond| is false.
   void emitBranch(Assembler::Condition cond, MBasicBlock* ifTrue,
                   MBasicBlock* ifFalse);
 
   void testNullEmitBranch(Assembler::Condition cond, const ValueOperand& value,
                           MBasicBlock* ifTrue, MBasicBlock* ifFalse) {
     cond = masm.testNull(cond, value);
--- a/js/src/jit/arm64/CodeGenerator-arm64.cpp
+++ b/js/src/jit/arm64/CodeGenerator-arm64.cpp
@@ -1051,17 +1051,17 @@ void CodeGenerator::visitRoundF(LRoundF*
     // If the result of the rounding was non-zero, return the output.
     // In the case of zero, the input may have been NaN or -0, which must bail.
     masm.branch32(Assembler::NotEqual, output, Imm32(0), &done);
     {
       // If input is NaN, comparisons set the C and V bits of the NZCV flags.
       masm.Fcmp(input32, 0.0f);
       bailoutIf(Assembler::Overflow, lir->snapshot());
 
-      // Move all 64 bits of the input into a scratch register to check for -0.
+      // Move all 32 bits of the input into a scratch register to check for -0.
       vixl::UseScratchRegisterScope temps(&masm.asVIXL());
       const ARMRegister scratchGPR32 = temps.AcquireW();
       masm.Fmov(scratchGPR32, input32);
       masm.Cmp(scratchGPR32, vixl::Operand(uint32_t(0x80000000)));
       bailoutIf(Assembler::Equal, lir->snapshot());
     }
 
     masm.jump(&done);
@@ -1093,37 +1093,106 @@ void CodeGenerator::visitRoundF(LRoundF*
 
     // If output is zero, then the actual result is -0. Bail.
     bailoutTest32(Assembler::Zero, output, output, lir->snapshot());
   }
 
   masm.bind(&done);
 }
 
-void CodeGenerator::visitTrunc(LTrunc* lir) { MOZ_CRASH("visitTrunc"); }
+void CodeGenerator::visitTrunc(LTrunc* lir) {
+  const FloatRegister input = ToFloatRegister(lir->input());
+  const ARMFPRegister input64(input, 64);
+  const Register output = ToRegister(lir->output());
+  const ARMRegister output32(output, 32);
+
+  Label done, zeroCase;
+
+  // Convert scalar to signed 32-bit fixed-point, rounding toward zero.
+  // In the case of overflow, the output is saturated.
+  // In the case of NaN and -0, the output is zero.
+  masm.Fcvtzs(output32, input64);
+
+  // If the output was zero, worry about special cases.
+  masm.branch32(Assembler::Equal, output, Imm32(0), &zeroCase);
+
+  // Bail on overflow cases.
+  bailoutCmp32(Assembler::Equal, output, Imm32(INT_MAX), lir->snapshot());
+  bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot());
+
+  // If the output was non-zero and wasn't saturated, just return it.
+  masm.jump(&done);
+
+  // Handle the case of a zero output:
+  // 1. The input may have been NaN, requiring a bail.
+  // 2. The input may have been in (-1,-0], requiring a bail.
+  {
+    masm.bind(&zeroCase);
+
+    // If input is a negative number that truncated to zero, the real
+    // output should be the non-integer -0.
+    // The use of "lt" instead of "lo" also catches unordered NaN input.
+    masm.Fcmp(input64, 0.0);
+    bailoutIf(vixl::lt, lir->snapshot());
+  }
+
+  masm.bind(&done);
+}
 
-void CodeGenerator::visitTruncF(LTruncF* lir) { MOZ_CRASH("visitTruncF"); }
+void CodeGenerator::visitTruncF(LTruncF* lir) {
+  const FloatRegister input = ToFloatRegister(lir->input());
+  const ARMFPRegister input32(input, 32);
+  const Register output = ToRegister(lir->output());
+  const ARMRegister output32(output, 32);
+
+  Label done, zeroCase;
+
+  // Convert scalar to signed 32-bit fixed-point, rounding toward zero.
+  // In the case of overflow, the output is saturated.
+  // In the case of NaN and -0, the output is zero.
+  masm.Fcvtzs(output32, input32);
+
+  // If the output was zero, worry about special cases.
+  masm.branch32(Assembler::Equal, output, Imm32(0), &zeroCase);
+
+  // Bail on overflow cases.
+  bailoutCmp32(Assembler::Equal, output, Imm32(INT_MAX), lir->snapshot());
+  bailoutCmp32(Assembler::Equal, output, Imm32(INT_MIN), lir->snapshot());
+
+  // If the output was non-zero and wasn't saturated, just return it.
+  masm.jump(&done);
+
+  // Handle the case of a zero output:
+  // 1. The input may have been NaN, requiring a bail.
+  // 2. The input may have been in (-1,-0], requiring a bail.
+  {
+    masm.bind(&zeroCase);
+
+    // If input is a negative number that truncated to zero, the real
+    // output should be the non-integer -0.
+    // The use of "lt" instead of "lo" also catches unordered NaN input.
+    masm.Fcmp(input32, 0.0f);
+    bailoutIf(vixl::lt, lir->snapshot());
+  }
+
+  masm.bind(&done);
+}
 
 void CodeGenerator::visitClzI(LClzI* lir) {
   ARMRegister input = toWRegister(lir->input());
   ARMRegister output = toWRegister(lir->output());
   masm.Clz(output, input);
 }
 
 void CodeGenerator::visitCtzI(LCtzI* lir) {
   Register input = ToRegister(lir->input());
   Register output = ToRegister(lir->output());
   masm.ctz32(input, output, /* knownNotZero = */ false);
 }
 
-void CodeGeneratorARM64::emitRoundDouble(FloatRegister src, Register dest,
-                                         Label* fail) {
-  MOZ_CRASH("CodeGeneratorARM64::emitRoundDouble");
-}
-
 void CodeGenerator::visitTruncateDToInt32(LTruncateDToInt32* ins) {
   emitTruncateDouble(ToFloatRegister(ins->input()), ToRegister(ins->output()),
                      ins->mir());
 }
 
 void CodeGenerator::visitTruncateFToInt32(LTruncateFToInt32* ins) {
   emitTruncateFloat32(ToFloatRegister(ins->input()), ToRegister(ins->output()),
                       ins->mir());
--- a/js/src/jit/arm64/CodeGenerator-arm64.h
+++ b/js/src/jit/arm64/CodeGenerator-arm64.h
@@ -55,18 +55,16 @@ class CodeGeneratorARM64 : public CodeGe
   }
   void bailoutIfFalseBool(Register reg, LSnapshot* snapshot) {
     masm.test32(reg, Imm32(0xFF));
     return bailoutIf(Assembler::Zero, snapshot);
   }
 
   bool generateOutOfLineCode();
 
-  void emitRoundDouble(FloatRegister src, Register dest, Label* fail);
-
   // Emits a branch that directs control flow to the true block if |cond| is
   // true, and the false block if |cond| is false.
   void emitBranch(Assembler::Condition cond, MBasicBlock* ifTrue,
                   MBasicBlock* ifFalse);
 
   void testNullEmitBranch(Assembler::Condition cond, const ValueOperand& value,
                           MBasicBlock* ifTrue, MBasicBlock* ifFalse) {
     cond = masm.testNull(cond, value);
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -3,16 +3,17 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "vm/TypedArrayObject-inl.h"
 #include "vm/TypedArrayObject.h"
 
 #include "mozilla/Alignment.h"
+#include "mozilla/CheckedInt.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/TextUtils.h"
 
 #include <string.h>
 #ifndef XP_WIN
 #include <sys/mman.h>
 #endif
@@ -50,16 +51,17 @@
 #include "vm/NativeObject-inl.h"
 #include "vm/Shape-inl.h"
 
 using namespace js;
 
 using JS::CanonicalizeNaN;
 using JS::ToInt32;
 using JS::ToUint32;
+using mozilla::CheckedUint32;
 using mozilla::IsAsciiDigit;
 
 /*
  * TypedArrayObject
  *
  * The non-templated base class for the specific typed implementations.
  * This class holds all the member variables that are used by
  * the subclasses.
@@ -154,19 +156,25 @@ void TypedArrayObject::finalize(FreeOp* 
     // Update the data slot pointer if it points to the old JSObject.
     if (oldObj->hasInlineElements()) {
       newObj->setInlineElements();
     }
 
     return 0;
   }
 
-  Nursery& nursery = obj->runtimeFromMainThread()->gc.nursery();
   void* buf = oldObj->elements();
 
+  // Discarded objects (which didn't have enough room for inner elements) don't
+  // have any data to move.
+  if (!buf) {
+    return 0;
+  }
+
+  Nursery& nursery = obj->runtimeFromMainThread()->gc.nursery();
   if (!nursery.isInside(buf)) {
     nursery.removeMallocedBuffer(buf);
     return 0;
   }
 
   // Determine if we can use inline data for the target array. If this is
   // possible, the nursery will have picked an allocation size that is large
   // enough.
@@ -185,16 +193,19 @@ void TypedArrayObject::finalize(FreeOp* 
     if (nbytes == 0) {
       uint8_t* output = newObj->fixedData(TypedArrayObject::FIXED_DATA_START);
       output[0] = ZeroLengthArrayData;
     }
 #endif
     newObj->setInlineElements();
   } else {
     MOZ_ASSERT(!oldObj->hasInlineElements());
+    MOZ_ASSERT((CheckedUint32(nbytes) + sizeof(Value)).isValid(),
+               "JS_ROUNDUP must not overflow");
+
     AutoEnterOOMUnsafeRegion oomUnsafe;
     nbytes = JS_ROUNDUP(nbytes, sizeof(Value));
     void* data = newObj->zone()->pod_malloc<uint8_t>(
         nbytes, js::ArrayBufferContentsArena);
     if (!data) {
       oomUnsafe.crash(
           "Failed to allocate typed array elements while tenuring.");
     }
@@ -477,25 +488,19 @@ class TypedArrayObjectTemplate : public 
 #ifdef DEBUG
     if (len == 0) {
       uint8_t* output = tarray->fixedData(TypedArrayObject::FIXED_DATA_START);
       output[0] = TypedArrayObject::ZeroLengthArrayData;
     }
 #endif
   }
 
-  static void initTypedArrayData(JSContext* cx, TypedArrayObject* tarray,
-                                 int32_t len, void* buf,
-                                 gc::AllocKind allocKind) {
+  static void initTypedArrayData(TypedArrayObject* tarray, int32_t len,
+                                 void* buf, gc::AllocKind allocKind) {
     if (buf) {
-#ifdef DEBUG
-      Nursery& nursery = cx->nursery();
-      MOZ_ASSERT_IF(!nursery.isInside(buf) && !tarray->hasInlineElements(),
-                    tarray->isTenured());
-#endif
       tarray->initPrivate(buf);
     } else {
       size_t nbytes = len * BYTES_PER_ELEMENT;
 #ifdef DEBUG
       constexpr size_t dataOffset = TypedArrayObject::dataOffset();
       constexpr size_t offset = dataOffset + sizeof(HeapSlot);
       MOZ_ASSERT(offset + nbytes <= GetGCKindBytes(allocKind));
 #endif
@@ -519,35 +524,40 @@ class TypedArrayObjectTemplate : public 
 
     AutoSetNewObjectMetadata metadata(cx);
 
     gc::AllocKind allocKind = !fitsInline ? gc::GetGCObjectKind(instanceClass())
                                           : AllocKindForLazyBuffer(nbytes);
     RootedObjectGroup group(cx, templateObj->group());
     MOZ_ASSERT(group->clasp() == instanceClass());
 
-    NewObjectKind newKind = TenuredObject;
-
-    UniquePtr<void, JS::FreePolicy> buf;
-    if (!fitsInline) {
-      MOZ_ASSERT(len > 0);
-      buf.reset(cx->pod_calloc<uint8_t>(nbytes, js::ArrayBufferContentsArena));
-      if (!buf) {
-        return nullptr;
-      }
-    }
-
     TypedArrayObject* obj =
-        NewObjectWithGroup<TypedArrayObject>(cx, group, allocKind, newKind);
+        NewObjectWithGroup<TypedArrayObject>(cx, group, allocKind);
     if (!obj) {
       return nullptr;
     }
 
     initTypedArraySlots(obj, len);
-    initTypedArrayData(cx, obj, len, buf.release(), allocKind);
+
+    void* buf = nullptr;
+    if (!fitsInline) {
+      MOZ_ASSERT(len > 0);
+      MOZ_ASSERT((CheckedUint32(nbytes) + sizeof(Value)).isValid(),
+                 "JS_ROUNDUP must not overflow");
+
+      nbytes = JS_ROUNDUP(nbytes, sizeof(Value));
+      buf = cx->nursery().allocateZeroedBuffer(obj, nbytes,
+                                               js::ArrayBufferContentsArena);
+      if (!buf) {
+        ReportOutOfMemory(cx);
+        return nullptr;
+      }
+    }
+
+    initTypedArrayData(obj, len, buf, allocKind);
 
     return obj;
   }
 
   // ES2018 draft rev 8340bf9a8427ea81bb0d1459471afbcc91d18add
   // 22.2.4.1 TypedArray ( )
   // 22.2.4.2 TypedArray ( length )
   // 22.2.4.3 TypedArray ( typedArray )
--- a/python/mozbuild/mozbuild/frontend/gyp_reader.py
+++ b/python/mozbuild/mozbuild/frontend/gyp_reader.py
@@ -244,16 +244,20 @@ def process_gyp_result(gyp_result, gyp_d
 
             defines = target_conf.get('defines', [])
             if bool(config.substs['_MSC_VER']) and no_chromium:
                 msvs_settings = gyp.msvs_emulation.MsvsSettings(spec, {})
                 defines.extend(msvs_settings.GetComputedDefines(c))
             for define in defines:
                 if '=' in define:
                     name, value = define.split('=', 1)
+                    # The NSS gyp file doesn't expose a way to override this
+                    # currently, so we do so here.
+                    if name == 'NSS_ALLOW_SSLKEYLOGFILE' and config.substs.get('RELEASE_OR_BETA', False):
+                        continue
                     context['DEFINES'][name] = value
                 else:
                     context['DEFINES'][define] = True
 
             product_dir_dist = '$PRODUCT_DIR/dist/'
             for include in target_conf.get('include_dirs', []):
                 if include.startswith(product_dir_dist):
                     # special-case includes of <(PRODUCT_DIR)/dist/ to match
--- a/servo/components/style/Cargo.toml
+++ b/servo/components/style/Cargo.toml
@@ -33,16 +33,17 @@ cssparser = "0.25"
 crossbeam-channel = { version = "0.3", optional = true }
 new_debug_unreachable = "1.0"
 encoding_rs = {version = "0.8", optional = true}
 euclid = "0.19"
 fallible = { path = "../fallible" }
 fxhash = "0.2"
 hashglobe = { path = "../hashglobe" }
 html5ever = {version = "0.22", optional = true}
+indexmap = "1.0"
 itertools = "0.7.6"
 itoa = "0.4"
 lazy_static = "1"
 log = "0.4"
 malloc_size_of = { path = "../malloc_size_of" }
 malloc_size_of_derive = { path = "../malloc_size_of_derive" }
 matches = "0.1"
 nsstring = {path = "../../../xpcom/rust/nsstring/", optional = true}
--- a/servo/components/style/custom_properties.rs
+++ b/servo/components/style/custom_properties.rs
@@ -3,27 +3,27 @@
  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
 
 //! Support for [custom properties for cascading variables][custom].
 //!
 //! [custom]: https://drafts.csswg.org/css-variables/
 
 use crate::hash::map::Entry;
 use crate::properties::{CSSWideKeyword, CustomDeclarationValue};
-use crate::selector_map::{PrecomputedHashMap, PrecomputedHashSet};
+use crate::selector_map::{PrecomputedHashMap, PrecomputedHashSet, PrecomputedHasher};
 use crate::Atom;
 use cssparser::{Delimiter, Parser, ParserInput, SourcePosition, Token, TokenSerializationType};
-use precomputed_hash::PrecomputedHash;
+use indexmap::IndexMap;
 use selectors::parser::SelectorParseErrorKind;
 use servo_arc::Arc;
 use smallvec::SmallVec;
-use std::borrow::{Borrow, Cow};
+use std::borrow::Cow;
 use std::cmp;
 use std::fmt::{self, Write};
-use std::hash::Hash;
+use std::hash::BuildHasherDefault;
 use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
 
 /// The environment from which to get `env` function values.
 ///
 /// TODO(emilio): If this becomes a bit more complex we should probably move it
 /// to the `media_queries` module, or something.
 #[derive(Debug, MallocSizeOf)]
 pub struct CssEnvironment;
@@ -126,149 +126,26 @@ impl ToCss for SpecifiedValue {
 ///
 /// A consistent ordering is required for CSSDeclaration objects in the
 /// DOM. CSSDeclarations expose property names as indexed properties, which
 /// need to be stable. So we keep an array of property names which order is
 /// determined on the order that they are added to the name-value map.
 ///
 /// The variable values are guaranteed to not have references to other
 /// properties.
-pub type CustomPropertiesMap = OrderedMap<Name, Arc<VariableValue>>;
+pub type CustomPropertiesMap =
+    IndexMap<Name, Arc<VariableValue>, BuildHasherDefault<PrecomputedHasher>>;
 
 /// Both specified and computed values are VariableValues, the difference is
 /// whether var() functions are expanded.
 pub type SpecifiedValue = VariableValue;
 /// Both specified and computed values are VariableValues, the difference is
 /// whether var() functions are expanded.
 pub type ComputedValue = VariableValue;
 
-/// A map that preserves order for the keys, and that is easily indexable.
-#[derive(Clone, Debug, Eq, PartialEq)]
-pub struct OrderedMap<K, V>
-where
-    K: PrecomputedHash + Hash + Eq + Clone,
-{
-    /// Key index.
-    index: Vec<K>,
-    /// Key-value map.
-    values: PrecomputedHashMap<K, V>,
-}
-
-impl<K, V> OrderedMap<K, V>
-where
-    K: Eq + PrecomputedHash + Hash + Clone,
-{
-    /// Creates a new ordered map.
-    pub fn new() -> Self {
-        OrderedMap {
-            index: Vec::new(),
-            values: PrecomputedHashMap::default(),
-        }
-    }
-
-    /// Insert a new key-value pair.
-    ///
-    /// TODO(emilio): Remove unused_mut when Gecko and Servo agree in whether
-    /// it's necessary.
-    #[allow(unused_mut)]
-    pub fn insert(&mut self, key: K, value: V) {
-        let OrderedMap {
-            ref mut index,
-            ref mut values,
-        } = *self;
-        match values.entry(key) {
-            Entry::Vacant(mut entry) => {
-                index.push(entry.key().clone());
-                entry.insert(value);
-            },
-            Entry::Occupied(mut entry) => {
-                entry.insert(value);
-            },
-        }
-    }
-
-    /// Get a value given its key.
-    pub fn get(&self, key: &K) -> Option<&V> {
-        let value = self.values.get(key);
-        debug_assert_eq!(value.is_some(), self.index.contains(key));
-        value
-    }
-
-    /// Get whether there's a value on the map for `key`.
-    pub fn contains_key(&self, key: &K) -> bool {
-        self.values.contains_key(key)
-    }
-
-    /// Get the key located at the given index.
-    pub fn get_key_at(&self, index: u32) -> Option<&K> {
-        self.index.get(index as usize)
-    }
-
-    /// Get an ordered map iterator.
-    pub fn iter<'a>(&'a self) -> OrderedMapIterator<'a, K, V> {
-        OrderedMapIterator {
-            inner: self,
-            pos: 0,
-        }
-    }
-
-    /// Get the count of items in the map.
-    pub fn len(&self) -> usize {
-        debug_assert_eq!(self.values.len(), self.index.len());
-        self.values.len()
-    }
-
-    /// Returns whether this map is empty.
-    pub fn is_empty(&self) -> bool {
-        self.len() == 0
-    }
-
-    /// Remove an item given its key.
-    fn remove<Q: ?Sized>(&mut self, key: &Q) -> Option<V>
-    where
-        K: Borrow<Q>,
-        Q: PrecomputedHash + Hash + Eq,
-    {
-        let index = self.index.iter().position(|k| k.borrow() == key)?;
-        self.index.remove(index);
-        self.values.remove(key)
-    }
-}
-
-/// An iterator for OrderedMap.
-///
-/// The iteration order is determined by the order that the values are
-/// added to the key-value map.
-pub struct OrderedMapIterator<'a, K, V>
-where
-    K: 'a + Eq + PrecomputedHash + Hash + Clone,
-    V: 'a,
-{
-    /// The OrderedMap itself.
-    inner: &'a OrderedMap<K, V>,
-    /// The position of the iterator.
-    pos: usize,
-}
-
-impl<'a, K, V> Iterator for OrderedMapIterator<'a, K, V>
-where
-    K: Eq + PrecomputedHash + Hash + Clone,
-{
-    type Item = (&'a K, &'a V);
-
-    fn next(&mut self) -> Option<Self::Item> {
-        let key = self.inner.index.get(self.pos)?;
-
-        self.pos += 1;
-        let value = &self.inner.values[key];
-
-        Some((key, value))
-    }
-}
-
 /// A struct holding information about the external references to that a custom
 /// property value may have.
 #[derive(Default)]
 struct VarOrEnvReferences {
     custom_property_references: PrecomputedHashSet<Name>,
     references_environment: bool,
 }
 
@@ -643,17 +520,17 @@ impl<'a> CustomPropertiesBuilder<'a> {
 
         if !self.value_may_affect_style(name, specified_value) {
             return;
         }
 
         if self.custom_properties.is_none() {
             self.custom_properties = Some(match self.inherited {
                 Some(inherited) => (**inherited).clone(),
-                None => CustomPropertiesMap::new(),
+                None => CustomPropertiesMap::default(),
             });
         }
 
         let map = self.custom_properties.as_mut().unwrap();
         match *specified_value {
             CustomDeclarationValue::Value(ref unparsed_value) => {
                 let has_references = !unparsed_value.references.is_empty();
                 self.may_have_cycles |= has_references;
@@ -928,17 +805,17 @@ fn substitute_all(custom_properties_map:
         }
 
         // All resolved, so return the signal value.
         None
     }
 
     // We have to clone the names so that we can mutably borrow the map
     // in the context we create for traversal.
-    let names = custom_properties_map.index.clone();
+    let names: Vec<_> = custom_properties_map.keys().cloned().collect();
     for name in names.into_iter() {
         let mut context = Context {
             count: 0,
             index_map: PrecomputedHashMap::default(),
             stack: SmallVec::new(),
             var_info: SmallVec::new(),
             map: custom_properties_map,
             environment,
@@ -1107,17 +984,17 @@ pub fn substitute<'i>(
     first_token_type: TokenSerializationType,
     computed_values_map: Option<&Arc<CustomPropertiesMap>>,
     env: &CssEnvironment,
 ) -> Result<String, ParseError<'i>> {
     let mut substituted = ComputedValue::empty();
     let mut input = ParserInput::new(input);
     let mut input = Parser::new(&mut input);
     let mut position = (input.position(), first_token_type);
-    let empty_map = CustomPropertiesMap::new();
+    let empty_map = CustomPropertiesMap::default();
     let custom_properties = match computed_values_map {
         Some(m) => &**m,
         None => &empty_map,
     };
     let last_token_type = substitute_block(
         &mut input,
         &mut position,
         &mut substituted,
--- a/servo/components/style/gecko_string_cache/mod.rs
+++ b/servo/components/style/gecko_string_cache/mod.rs
@@ -9,20 +9,20 @@
 #![allow(non_upper_case_globals)]
 
 //! A drop-in replacement for string_cache, but backed by Gecko `nsAtom`s.
 
 use crate::gecko_bindings::bindings::Gecko_AddRefAtom;
 use crate::gecko_bindings::bindings::Gecko_Atomize;
 use crate::gecko_bindings::bindings::Gecko_Atomize16;
 use crate::gecko_bindings::bindings::Gecko_ReleaseAtom;
-use crate::gecko_bindings::structs::{nsAtom, nsDynamicAtom, nsStaticAtom};
-use crate::gecko_bindings::structs::root::mozilla::detail::GkAtoms_Atoms_AtomsCount;
 use crate::gecko_bindings::structs::root::mozilla::detail::gGkAtoms;
 use crate::gecko_bindings::structs::root::mozilla::detail::kGkAtomsArrayOffset;
+use crate::gecko_bindings::structs::root::mozilla::detail::GkAtoms_Atoms_AtomsCount;
+use crate::gecko_bindings::structs::{nsAtom, nsDynamicAtom, nsStaticAtom};
 use nsstring::{nsAString, nsStr};
 use precomputed_hash::PrecomputedHash;
 use std::borrow::{Borrow, Cow};
 use std::char::{self, DecodeUtf16};
 use std::fmt::{self, Write};
 use std::hash::{Hash, Hasher};
 use std::iter::Cloned;
 use std::ops::Deref;
--- a/servo/components/style/lib.rs
+++ b/servo/components/style/lib.rs
@@ -43,16 +43,17 @@ extern crate fallible;
 extern crate fxhash;
 #[cfg(feature = "gecko")]
 #[macro_use]
 pub mod gecko_string_cache;
 extern crate hashglobe;
 #[cfg(feature = "servo")]
 #[macro_use]
 extern crate html5ever;
+extern crate indexmap;
 extern crate itertools;
 extern crate itoa;
 #[macro_use]
 extern crate lazy_static;
 #[macro_use]
 extern crate log;
 #[macro_use]
 extern crate malloc_size_of;
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -5787,18 +5787,18 @@ pub extern "C" fn Servo_GetCustomPropert
     index: u32,
     name: *mut nsAString,
 ) -> bool {
     let custom_properties = match computed_values.custom_properties() {
         Some(p) => p,
         None => return false,
     };
 
-    let property_name = match custom_properties.get_key_at(index) {
-        Some(n) => n,
+    let property_name = match custom_properties.get_index(index as usize) {
+        Some((key, _value)) => key,
         None => return false,
     };
 
     let name = unsafe { name.as_mut().unwrap() };
     name.assign(&*property_name.as_slice());
 
     true
 }
--- a/toolkit/crashreporter/nsDummyExceptionHandler.cpp
+++ b/toolkit/crashreporter/nsDummyExceptionHandler.cpp
@@ -162,18 +162,16 @@ bool AppendExtraData(const nsAString& id
 }
 
 bool AppendExtraData(nsIFile* extraFile, const AnnotationTable& data) {
   return false;
 }
 
 void OOPInit() {}
 
-void GetChildProcessTmpDir(nsIFile** aOutTmpDir) {}
-
 #if defined(XP_WIN) || defined(XP_MACOSX)
 const char* GetChildNotificationPipe() { return nullptr; }
 #endif
 
 #ifdef MOZ_CRASHREPORTER_INJECTOR
 void InjectCrashReporterIntoProcess(DWORD processID,
                                     InjectorCrashCallback* cb) {}
 
--- a/toolkit/crashreporter/nsExceptionHandler.cpp
+++ b/toolkit/crashreporter/nsExceptionHandler.cpp
@@ -244,21 +244,16 @@ static bool sIncludeContextHeap = false;
 
 // OOP crash reporting
 static CrashGenerationServer* crashServer;  // chrome process has this
 static StaticMutex processMapLock;
 static std::map<ProcessId, PRFileDesc*> processToCrashFd;
 
 static std::terminate_handler oldTerminateHandler = nullptr;
 
-#if (defined(XP_MACOSX) || defined(XP_WIN))
-// This field is valid in both chrome and content processes.
-static xpstring* childProcessTmpDir = nullptr;
-#endif
-
 #if defined(XP_WIN) || defined(XP_MACOSX)
 // If crash reporting is disabled, we hand out this "null" pipe to the
 // child process and don't attempt to connect to a parent server.
 static const char kNullNotifyPipe[] = "-";
 static char* childCrashNotifyPipe;
 
 #elif defined(XP_LINUX)
 static int serverSocketFd = -1;
@@ -3062,36 +3057,16 @@ void OOPInit() {
   if (OOPInitialized()) return;
 
   MOZ_ASSERT(NS_IsMainThread());
 
   MOZ_ASSERT(gExceptionHandler != nullptr,
              "attempt to initialize OOP crash reporter before in-process "
              "crashreporter!");
 
-#if (defined(XP_WIN) || defined(XP_MACOSX))
-  nsCOMPtr<nsIFile> tmpDir;
-#if defined(MOZ_CONTENT_SANDBOX)
-  nsresult rv = NS_GetSpecialDirectory(NS_APP_CONTENT_PROCESS_TEMP_DIR,
-                                       getter_AddRefs(tmpDir));
-  if (NS_FAILED(rv) && PR_GetEnv("XPCSHELL_TEST_PROFILE_DIR")) {
-    // Temporary hack for xpcshell, will be fixed in bug 1257098
-    rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tmpDir));
-  }
-  if (NS_SUCCEEDED(rv)) {
-    childProcessTmpDir = CreatePathFromFile(tmpDir);
-  }
-#else
-  if (NS_SUCCEEDED(
-          NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tmpDir)))) {
-    childProcessTmpDir = CreatePathFromFile(tmpDir);
-  }
-#endif  // defined(MOZ_CONTENT_SANDBOX)
-#endif  // (defined(XP_WIN) || defined(XP_MACOSX))
-
 #if defined(XP_WIN)
   childCrashNotifyPipe =
       mozilla::Smprintf("\\\\.\\pipe\\gecko-crash-server-pipe.%i",
                         static_cast<int>(::GetCurrentProcessId()))
           .release();
 
   const std::wstring dumpPath = gExceptionHandler->dump_path();
   crashServer = new CrashGenerationServer(
@@ -3170,25 +3145,16 @@ static void OOPDeinit() {
   pidToMinidump = nullptr;
 
 #if defined(XP_WIN) || defined(XP_MACOSX)
   free(childCrashNotifyPipe);
   childCrashNotifyPipe = nullptr;
 #endif
 }
 
-void GetChildProcessTmpDir(nsIFile** aOutTmpDir) {
-  MOZ_ASSERT(XRE_IsParentProcess());
-#if (defined(XP_MACOSX) || defined(XP_WIN))
-  if (childProcessTmpDir) {
-    CreateFileFromPath(*childProcessTmpDir, aOutTmpDir);
-  }
-#endif
-}
-
 #if defined(XP_WIN) || defined(XP_MACOSX)
 // Parent-side API for children
 const char* GetChildNotificationPipe() {
   if (!GetEnabled()) return kNullNotifyPipe;
 
   MOZ_ASSERT(OOPInitialized());
 
   return childCrashNotifyPipe;
--- a/toolkit/crashreporter/nsExceptionHandler.h
+++ b/toolkit/crashreporter/nsExceptionHandler.h
@@ -242,20 +242,16 @@ bool CreateMinidumpsAndPair(ProcessHandl
 // a minidump (|parentMinidump|).
 // The resulting dump will get the id of the parent and use the |name| as
 // an extension.
 bool CreateAdditionalChildMinidump(ProcessHandle childPid,
                                    ThreadId childBlamedThread,
                                    nsIFile* parentMinidump,
                                    const nsACString& name);
 
-// Parent-side API, returns the tmp dir for child processes to use, accounting
-// for sandbox considerations.
-void GetChildProcessTmpDir(nsIFile** aOutTmpDir);
-
 #if defined(XP_WIN32) || defined(XP_MACOSX)
 // Parent-side API for children
 const char* GetChildNotificationPipe();
 
 #ifdef MOZ_CRASHREPORTER_INJECTOR
 // Inject a crash report client into an arbitrary process, and inform the
 // callback object when it crashes. Parent process only.
 
--- a/toolkit/mozapps/update/nsUpdateService.js
+++ b/toolkit/mozapps/update/nsUpdateService.js
@@ -1228,23 +1228,31 @@ function UpdatePatch(patch) {
         break;
       case "finalURL":
       case "state":
       case "type":
       case "URL":
         this[attr.name] = attr.value;
         break;
       default:
-        // Set nsIPropertyBag properties that were read from the xml file.
-        this.setProperty(attr.name, attr.value);
+        if (!this._attrNames.includes(attr.name)) {
+          // Set nsIPropertyBag properties that were read from the xml file.
+          this.setProperty(attr.name, attr.value);
+        }
         break;
     }
   }
 }
 UpdatePatch.prototype = {
+  // nsIUpdatePatch attribute names used to prevent nsIWritablePropertyBag from
+  // over writing nsIUpdatePatch attributes.
+  _attrNames: [
+    "errorCode", "finalURL", "selected", "size", "state", "type", "URL",
+  ],
+
   /**
    * See nsIUpdateService.idl
    */
   serialize: function UpdatePatch_serialize(updates) {
     var patch = updates.createElementNS(URI_UPDATE_NS, "patch");
     patch.setAttribute("size", this.size);
     patch.setAttribute("type", this.type);
     patch.setAttribute("URL", this.URL);
@@ -1261,34 +1269,46 @@ UpdatePatch.prototype = {
     if (this.selected) {
       patch.setAttribute("selected", this.selected);
     }
     if (this.state != STATE_NONE) {
       patch.setAttribute("state", this.state);
     }
 
     for (let [name, value] of Object.entries(this._properties)) {
-      if (value.present) {
+      if (value.present && !this._attrNames.includes(name)) {
         patch.setAttribute(name, value.data);
       }
     }
     return patch;
   },
 
   /**
    * See nsIWritablePropertyBag.idl
    */
   setProperty: function UpdatePatch_setProperty(name, value) {
+    if (this._attrNames.includes(name)) {
+      throw Components.Exception(
+        "Illegal value '" + name + "' (attribute exists on nsIUpdatePatch) " +
+        "when calling method: [nsIWritablePropertyBag::setProperty]",
+        Cr.NS_ERROR_ILLEGAL_VALUE);
+    }
     this._properties[name] = { data: value, present: true };
   },
 
   /**
    * See nsIWritablePropertyBag.idl
    */
   deleteProperty: function UpdatePatch_deleteProperty(name) {
+    if (this._attrNames.includes(name)) {
+      throw Components.Exception(
+        "Illegal value '" + name + "' (attribute exists on nsIUpdatePatch) " +
+        "when calling method: [nsIWritablePropertyBag::deleteProperty]",
+        Cr.NS_ERROR_ILLEGAL_VALUE);
+    }
     if (name in this._properties) {
       this._properties[name].present = false;
     } else {
       throw Cr.NS_ERROR_FAILURE;
     }
   },
 
   /**
@@ -1303,17 +1323,17 @@ UpdatePatch.prototype = {
 
   * enumerate() {
     // An nsISupportsInterfacePointer is used so creating an array using
     // Array.from will retain the QueryInterface for nsIProperty.
     let ip = Cc["@mozilla.org/supports-interface-pointer;1"].
              createInstance(Ci.nsISupportsInterfacePointer);
     let qi = ChromeUtils.generateQI([Ci.nsIProperty]);
     for (let [name, value] of Object.entries(this._properties)) {
-      if (value.present) {
+      if (value.present && !this._attrNames.includes(name)) {
         // The nsIPropertyBag enumerator returns a nsISimpleEnumerator whose
         // elements are nsIProperty objects. Calling QueryInterface for
         // nsIProperty on the object doesn't return to the caller an object that
         // is already queried to nsIProperty but do it just in case it is fixed
         // at some point.
         ip.data = {name, value: value.data, QueryInterface: qi};
         yield ip.data.QueryInterface(Ci.nsIProperty);
       }
@@ -1322,16 +1342,22 @@ UpdatePatch.prototype = {
 
   /**
    * See nsIPropertyBag.idl
    *
    * Note: returns null instead of throwing when the property doesn't exist to
    *       simplify code and to silence warnings in debug builds.
    */
   getProperty: function UpdatePatch_getProperty(name) {
+    if (this._attrNames.includes(name)) {
+      throw Components.Exception(
+        "Illegal value '" + name + "' (attribute exists on nsIUpdatePatch) " +
+        "when calling method: [nsIWritablePropertyBag::getProperty]",
+        Cr.NS_ERROR_ILLEGAL_VALUE);
+    }
     if (name in this._properties && this._properties[name].present) {
       return this._properties[name].data;
     }
     return null;
   },
 
   QueryInterface: ChromeUtils.generateQI([Ci.nsIUpdatePatch,
                                           Ci.nsIPropertyBag,
@@ -1426,18 +1452,20 @@ function Update(update) {
         case "name":
         case "previousAppVersion":
         case "serviceURL":
         case "statusText":
         case "type":
           this[attr.name] = attr.value;
           break;
         default:
-          // Set nsIPropertyBag properties that were read from the xml file.
-          this.setProperty(attr.name, attr.value);
+          if (!this._attrNames.includes(attr.name)) {
+            // Set nsIPropertyBag properties that were read from the xml file.
+            this.setProperty(attr.name, attr.value);
+          }
           break;
       }
     }
   }
 
   if (!this.previousAppVersion) {
     this.previousAppVersion = Services.appinfo.version;
   }
@@ -1465,16 +1493,25 @@ function Update(update) {
     // "<App Name> <Update App Version>"
     let brandBundle = Services.strings.createBundle(URI_BRAND_PROPERTIES);
     let appName = brandBundle.GetStringFromName("brandShortName");
     this.name = gUpdateBundle.formatStringFromName("updateName",
                                                    [appName, this.displayVersion], 2);
   }
 }
 Update.prototype = {
+  // nsIUpdate attribute names used to prevent nsIWritablePropertyBag from over
+  // writing nsIUpdate attributes.
+  _attrNames: [
+    "appVersion", "buildID", "channel", "detailsURL", "displayVersion",
+    "elevationFailure", "errorCode", "installDate", "isCompleteUpdate", "name",
+    "previousAppVersion", "promptWaitTime", "serviceURL", "state", "statusText",
+    "type", "unsupported",
+  ],
+
   /**
    * See nsIUpdateService.idl
    */
   getPatchAt: function Update_getPatchAt(index) {
     return this._patches[index];
   },
 
   /**
@@ -1558,40 +1595,52 @@ Update.prototype = {
     if (this.unsupported) {
       update.setAttribute("unsupported", this.unsupported);
     }
     if (this.elevationFailure) {
       update.setAttribute("elevationFailure", this.elevationFailure);
     }
 
     for (let [name, value] of Object.entries(this._properties)) {
-      if (value.present) {
+      if (value.present && !this._attrNames.includes(name)) {
         update.setAttribute(name, value.data);
       }
     }
 
     for (let i = 0; i < this.patchCount; ++i) {
       update.appendChild(this.getPatchAt(i).serialize(updates));
     }
 
     updates.documentElement.appendChild(update);
     return update;
   },
 
   /**
    * See nsIWritablePropertyBag.idl
    */
   setProperty: function Update_setProperty(name, value) {
+    if (this._attrNames.includes(name)) {
+      throw Components.Exception(
+        "Illegal value '" + name + "' (attribute exists on nsIUpdate) " +
+        "when calling method: [nsIWritablePropertyBag::setProperty]",
+        Cr.NS_ERROR_ILLEGAL_VALUE);
+    }
     this._properties[name] = { data: value, present: true };
   },
 
   /**
    * See nsIWritablePropertyBag.idl
    */
   deleteProperty: function Update_deleteProperty(name) {
+    if (this._attrNames.includes(name)) {
+      throw Components.Exception(
+        "Illegal value '" + name + "' (attribute exists on nsIUpdate) " +
+        "when calling method: [nsIWritablePropertyBag::deleteProperty]",
+        Cr.NS_ERROR_ILLEGAL_VALUE);
+    }
     if (name in this._properties) {
       this._properties[name].present = false;
     } else {
       throw Cr.NS_ERROR_FAILURE;
     }
   },
 
   /**
@@ -1606,17 +1655,17 @@ Update.prototype = {
 
   * enumerate() {
     // An nsISupportsInterfacePointer is used so creating an array using
     // Array.from will retain the QueryInterface for nsIProperty.
     let ip = Cc["@mozilla.org/supports-interface-pointer;1"].
              createInstance(Ci.nsISupportsInterfacePointer);
     let qi = ChromeUtils.generateQI([Ci.nsIProperty]);
     for (let [name, value] of Object.entries(this._properties)) {
-      if (value.present) {
+      if (value.present && !this._attrNames.includes(name)) {
         // The nsIPropertyBag enumerator returns a nsISimpleEnumerator whose
         // elements are nsIProperty objects. Calling QueryInterface for
         // nsIProperty on the object doesn't return to the caller an object that
         // is already queried to nsIProperty but do it just in case it is fixed
         // at some point.
         ip.data = {name, value: value.data, QueryInterface: qi};
         yield ip.data.QueryInterface(Ci.nsIProperty);
       }
@@ -1624,16 +1673,22 @@ Update.prototype = {
   },
 
   /**
    * See nsIPropertyBag.idl
    * Note: returns null instead of throwing when the property doesn't exist to
    *       simplify code and to silence warnings in debug builds.
    */
   getProperty: function Update_getProperty(name) {
+    if (this._attrNames.includes(name)) {
+      throw Components.Exception(
+        "Illegal value '" + name + "' (attribute exists on nsIUpdate) " +
+        "when calling method: [nsIWritablePropertyBag::getProperty]",
+        Cr.NS_ERROR_ILLEGAL_VALUE);
+    }
     if (name in this._properties && this._properties[name].present) {
       return this._properties[name].data;
     }
     return null;
   },
 
   QueryInterface: ChromeUtils.generateQI([Ci.nsIUpdate,
                                           Ci.nsIPropertyBag,
--- a/toolkit/mozapps/update/tests/unit_aus_update/updateManagerXML.js
+++ b/toolkit/mozapps/update/tests/unit_aus_update/updateManagerXML.js
@@ -256,10 +256,61 @@ function run_test() {
                "the first property name" + MSG_SHOULD_EQUAL);
   Assert.equal(results[0].value, "custom3 patch value",
                "the first property value" + MSG_SHOULD_EQUAL);
   Assert.equal(results[1].name, "custom4_attr",
                "the second property name" + MSG_SHOULD_EQUAL);
   Assert.equal(results[1].value, "custom4 patch value",
                "the second property value" + MSG_SHOULD_EQUAL);
 
+  let attrNames = [
+    "appVersion", "buildID", "channel", "detailsURL", "displayVersion",
+    "elevationFailure", "errorCode", "installDate", "isCompleteUpdate", "name",
+    "previousAppVersion", "promptWaitTime", "serviceURL", "state", "statusText",
+    "type", "unsupported",
+  ];
+  checkIllegalProperties(update, attrNames);
+
+  attrNames = [
+    "errorCode", "finalURL", "selected", "size", "state", "type", "URL",
+  ];
+  checkIllegalProperties(patch, attrNames);
+
   executeSoon(doTestFinish);
 }
+
+function checkIllegalProperties(object, propertyNames) {
+  let objectName =
+    object instanceof Ci.nsIUpdate ? "nsIUpdate" : "nsIUpdatePatch";
+  propertyNames.forEach(function(name) {
+    // Check that calling getProperty, setProperty, and deleteProperty on an
+    // nsIUpdate attribute throws NS_ERROR_ILLEGAL_VALUE
+    let result = 0;
+    try {
+      object.getProperty(name);
+    } catch (e) {
+      result = e.result;
+    }
+    Assert.equal(result, Cr.NS_ERROR_ILLEGAL_VALUE,
+                 "calling getProperty using an " + objectName + " attribute " +
+                 "name should throw NS_ERROR_ILLEGAL_VALUE");
+
+    result = 0;
+    try {
+      object.setProperty(name, "value");
+    } catch (e) {
+      result = e.result;
+    }
+    Assert.equal(result, Cr.NS_ERROR_ILLEGAL_VALUE,
+                 "calling setProperty using an " + objectName + " attribute " +
+                 "name should throw NS_ERROR_ILLEGAL_VALUE");
+
+    result = 0;
+    try {
+      object.deleteProperty(name);
+    } catch (e) {
+      result = e.result;
+    }
+    Assert.equal(result, Cr.NS_ERROR_ILLEGAL_VALUE,
+                 "calling deleteProperty using an " + objectName + " attribute " +
+                 "name should throw NS_ERROR_ILLEGAL_VALUE");
+  });
+}
--- a/toolkit/xre/nsEmbedFunctions.cpp
+++ b/toolkit/xre/nsEmbedFunctions.cpp
@@ -589,32 +589,16 @@ nsresult XRE_InitChildProcess(int aArgc,
   const char* const parentPIDString = aArgv[aArgc - 1];
   MOZ_ASSERT(parentPIDString, "NULL parent PID");
   --aArgc;
 
   char* end = 0;
   base::ProcessId parentPID = strtol(parentPIDString, &end, 10);
   MOZ_ASSERT(!*end, "invalid parent PID");
 
-  nsCOMPtr<nsIFile> crashReportTmpDir;
-  if (XRE_GetProcessType() == GeckoProcessType_GPU ||
-      XRE_GetProcessType() == GeckoProcessType_RDD) {
-    aArgc--;
-    if (strlen(aArgv[aArgc])) {  // if it's empty, ignore it
-      nsresult rv =
-          XRE_GetFileFromPath(aArgv[aArgc], getter_AddRefs(crashReportTmpDir));
-      if (NS_FAILED(rv)) {
-        // If we don't have a valid tmp dir we can probably still run ok, but
-        // crash report .extra files might not get picked up by the parent
-        // process. Debug-assert because this shouldn't happen in practice.
-        MOZ_ASSERT(false, "GPU process started without valid tmp dir!");
-      }
-    }
-  }
-
   // While replaying, use the parent PID that existed while recording.
   parentPID = recordreplay::RecordReplayValue(parentPID);
 
 #ifdef XP_MACOSX
   mozilla::ipc::SharedMemoryBasic::SetupMachMemory(
       parentPID, ports_in_receiver, ports_in_sender, ports_out_sender,
       ports_out_receiver, true);
 #endif